From 53bd3a1c30da076d254fc2570c1b5dd46591037a Mon Sep 17 00:00:00 2001 From: Michael Manis Date: Mon, 18 Jun 2018 19:49:12 -0400 Subject: [PATCH 0001/2854] Add some default LengthLimit to OsuTextBox --- osu.Game/Graphics/UserInterface/OsuTextBox.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Graphics/UserInterface/OsuTextBox.cs b/osu.Game/Graphics/UserInterface/OsuTextBox.cs index 6021af2028..17a1be8879 100644 --- a/osu.Game/Graphics/UserInterface/OsuTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTextBox.cs @@ -32,6 +32,7 @@ namespace osu.Game.Graphics.UserInterface Height = 40; TextContainer.Height = 0.5f; CornerRadius = 5; + LengthLimit = 1000; Current.DisabledChanged += disabled => { From ffe82aad25659c6f113ac9db685bcca581b815ab Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 10 Jul 2018 21:57:09 +0200 Subject: [PATCH 0002/2854] Add basic quick exit functionality --- .../Input/Bindings/GlobalActionContainer.cs | 3 ++ osu.Game/Screens/Play/Player.cs | 13 +++++++++ osu.Game/Screens/Play/QuickExit.cs | 28 +++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 osu.Game/Screens/Play/QuickExit.cs diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index b21deff509..f4419cc6d0 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -49,6 +49,7 @@ namespace osu.Game.Input.Bindings { new KeyBinding(InputKey.Space, GlobalAction.SkipCutscene), new KeyBinding(InputKey.Tilde, GlobalAction.QuickRetry), + new KeyBinding(new[] { InputKey.Alt, InputKey.Tilde }, GlobalAction.QuickExit), new KeyBinding(new[] { InputKey.Control, InputKey.Plus }, GlobalAction.IncreaseScrollSpeed), new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed), }; @@ -83,6 +84,8 @@ namespace osu.Game.Input.Bindings SkipCutscene, [Description("Quick Retry (Hold)")] QuickRetry, + [Description("Quick Exit (Hold)")] + QuickExit, [Description("Take screenshot")] TakeScreenshot, diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b406bda411..84fcede6b5 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -71,6 +71,7 @@ namespace osu.Game.Screens.Play private APIAccess api; private SampleChannel sampleRestart; + private SampleChannel sampleExit; protected ScoreProcessor ScoreProcessor; protected RulesetContainer RulesetContainer; @@ -93,6 +94,7 @@ namespace osu.Game.Screens.Play return; sampleRestart = audio.Sample.Get(@"Gameplay/restart"); + sampleExit = audio.Sample.Get(@"UI/screen-back"); mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); userAudioOffset = config.GetBindable(OsuSetting.AudioOffset); @@ -224,6 +226,17 @@ namespace osu.Game.Screens.Play RulesetContainer?.Hide(); Restart(); }, + }, + new QuickExit + { + Action = () => + { + if (!IsCurrentScreen) return; + + sampleExit?.Play(); + ValidForResume = false; + Exit(); + } } }; diff --git a/osu.Game/Screens/Play/QuickExit.cs b/osu.Game/Screens/Play/QuickExit.cs new file mode 100644 index 0000000000..611b02c543 --- /dev/null +++ b/osu.Game/Screens/Play/QuickExit.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCEusing System; + +using osu.Framework.Input.Bindings; +using osu.Game.Input.Bindings; +using osu.Game.Overlays; + +namespace osu.Game.Screens.Play +{ + public class QuickExit : HoldToConfirmOverlay, IKeyBindingHandler + { + public bool OnPressed(GlobalAction action) + { + if (action != GlobalAction.QuickExit) return false; + + BeginConfirm(); + return true; + } + + public bool OnReleased(GlobalAction action) + { + if (action != GlobalAction.QuickExit) return false; + + AbortConfirm(); + return true; + } + } +} From 4c6286a3ca1929cbe5c96410ea8121da00f65a40 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 10 Jul 2018 22:16:08 +0200 Subject: [PATCH 0003/2854] Fix license header --- osu.Game/Screens/Play/QuickExit.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/QuickExit.cs b/osu.Game/Screens/Play/QuickExit.cs index 611b02c543..0c3908a44f 100644 --- a/osu.Game/Screens/Play/QuickExit.cs +++ b/osu.Game/Screens/Play/QuickExit.cs @@ -1,5 +1,5 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCEusing System; +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Input.Bindings; using osu.Game.Input.Bindings; From 4cc22387d4a3481972ae35688952a392a1fb79fb Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 11 Jul 2018 12:03:05 +0200 Subject: [PATCH 0004/2854] Avoid interversion key configuration conflicts --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index f4419cc6d0..56d8db72fe 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -84,8 +84,6 @@ namespace osu.Game.Input.Bindings SkipCutscene, [Description("Quick Retry (Hold)")] QuickRetry, - [Description("Quick Exit (Hold)")] - QuickExit, [Description("Take screenshot")] TakeScreenshot, @@ -103,5 +101,8 @@ namespace osu.Game.Input.Bindings [Description("Select")] Select, + + [Description("Quick Exit (Hold)")] + QuickExit, } } From fde9df39389e88368360ce46ad2e651ac459a778 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 11 Jul 2018 14:47:34 +0200 Subject: [PATCH 0005/2854] Same as #3006 + hide gameplay instantly --- osu.Game/Screens/Play/Player.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 84fcede6b5..e0d5fed212 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -235,6 +235,8 @@ namespace osu.Game.Screens.Play sampleExit?.Play(); ValidForResume = false; + RulesetContainer?.Hide(); + pauseContainer?.Hide(); Exit(); } } From 07183c0069691ee0c39af25e3535b6362af121bd Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 15 Jul 2018 00:52:15 +0200 Subject: [PATCH 0006/2854] Hide Content instead of particular overlays --- osu.Game/Screens/Play/Player.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e0d5fed212..c575824c1c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -235,8 +235,7 @@ namespace osu.Game.Screens.Play sampleExit?.Play(); ValidForResume = false; - RulesetContainer?.Hide(); - pauseContainer?.Hide(); + Content.Hide(); Exit(); } } From be977e25414cbd443f51f079347528d64b83de2d Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Mon, 16 Jul 2018 23:50:22 +0200 Subject: [PATCH 0007/2854] Header1 --- osu.Game.Tests/Visual/TestCaseChangelog.cs | 34 ++++ .../Visual/TestCaseTextBadgePair.cs | 76 +++++++ .../Overlays/Changelog/ChangelogHeader.cs | 190 ++++++++++++++++++ .../Overlays/Changelog/Header/LineBadge.cs | 35 ++++ .../Changelog/Header/TextBadgePair.cs | 113 +++++++++++ .../Changelog/Header/TextBadgePairListing.cs | 58 ++++++ .../Changelog/Header/TextBadgePairRelease.cs | 42 ++++ osu.Game/Overlays/ChangelogOverlay.cs | 87 ++++++++ 8 files changed, 635 insertions(+) create mode 100644 osu.Game.Tests/Visual/TestCaseChangelog.cs create mode 100644 osu.Game.Tests/Visual/TestCaseTextBadgePair.cs create mode 100644 osu.Game/Overlays/Changelog/ChangelogHeader.cs create mode 100644 osu.Game/Overlays/Changelog/Header/LineBadge.cs create mode 100644 osu.Game/Overlays/Changelog/Header/TextBadgePair.cs create mode 100644 osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs create mode 100644 osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs create mode 100644 osu.Game/Overlays/ChangelogOverlay.cs diff --git a/osu.Game.Tests/Visual/TestCaseChangelog.cs b/osu.Game.Tests/Visual/TestCaseChangelog.cs new file mode 100644 index 0000000000..688cf2b075 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseChangelog.cs @@ -0,0 +1,34 @@ +// Copyright(c) 2007-2018 ppy Pty Ltd. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + + +using NUnit.Framework; +using osu.Game.Overlays; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseChangelog : OsuTestCase + { + private ChangelogOverlay changelog; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Add(changelog = new ChangelogOverlay()); + changelog.ToggleVisibility(); + + //AddStep(@"toggle", changelog.ToggleVisibility); + AddStep(@"toggle text 1", () => changelog.header.ActivateRelease("Release 20180626.1")); + AddStep(@"toggle text 2", () => changelog.header.ActivateRelease("Lazer 2018.713.1")); + AddStep(@"toggle text 3", () => changelog.header.ActivateRelease("Beta 20180626")); + AddStep(@"go to listing", changelog.header.ActivateListing); + } + + public TestCaseChangelog() + { + + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs b/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs new file mode 100644 index 0000000000..effce3d471 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs @@ -0,0 +1,76 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Overlays.Changelog.Header; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseTextBadgePair : OsuTestCase + { + private readonly Container container; + private readonly Box bottomLine; + private readonly TextBadgePair textBadgePair; + + public TestCaseTextBadgePair() + { + + Add(container = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new OpenTK.Vector2(300, 40), + Children = new Drawable[] + { + new Box + { + Colour = Color4.Gray, + RelativeSizeAxes = Axes.Both, + }, + bottomLine = new Box // purple line + { + Colour = Color4.Purple, + RelativeSizeAxes = Axes.X, + Height = 3, + Anchor = Anchor.BottomLeft, + Origin = Anchor.CentreLeft, + }, + textBadgePair = new TextBadgePair(Color4.White, "testing") + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + } + }, + }); + + AddStep(@"deactivate", () => textBadgePair.Deactivate()); + AddStep(@"activate", () => textBadgePair.Activate()); + AddStep(@"purple text", () => textBadgePair.SetTextColor(Color4.Purple, 100)); + AddStep(@"white text", () => textBadgePair.SetTextColor(Color4.White, 100)); + AddStep(@"purple badge", () => textBadgePair.SetBadgeColor(Color4.Purple, 100)); + AddStep(@"white badge", () => textBadgePair.SetBadgeColor(Color4.White, 100)); + AddStep(@"hide text", () => textBadgePair.HideText(250)); + AddStep(@"show text", () => textBadgePair.ShowText(250)); + } + + //[BackgroundDependencyLoader] + //private void load(OsuColour colours) + //{ + + //} + + //private enum BreadcrumbTab + //{ + // Click, + // The, + // Circles, + //} + } +} diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs new file mode 100644 index 0000000000..c8fff5bd6b --- /dev/null +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -0,0 +1,190 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +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.Framework.Graphics.Textures; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.Changelog.Header; +using System; +using System.Collections.Generic; +using System.Text; + +namespace osu.Game.Overlays.Changelog +{ + public class ChangelogHeader : Container + { + private readonly Container coverContainer; + + private Color4 purple = new Color4(191, 4, 255, 255); + private readonly Sprite coverImage; + private readonly Sprite headerBadge; //50x50, margin-right: 20 + private readonly FillFlowContainer headerTextContainer; + private readonly OsuSpriteText title, titleStream; + private readonly TextBadgePairListing listing; + private readonly TextBadgePairRelease releaseStream; + private readonly FillFlowContainer breadcrumbContainer; + + private const float cover_height = 310; + private const float title_height = 50; + private const float icon_size = 50; + private const float icon_margin = 20; + private const float version_height = 40; + + public ChangelogHeader() + { + RelativeSizeAxes = Axes.X; + Height = cover_height + 5; // 5 is for the "badge" that sticks a bit out of the bottom + Masking = true; // is masking necessary? since I see it nearly everywhere + Children = new Drawable[] + { + coverContainer = new Container + { + RelativeSizeAxes = Axes.X, + Height = cover_height, + Children = new Drawable[] + { + coverImage = new Sprite + { + RelativeSizeAxes = Axes.Both, + Size = new OpenTK.Vector2(1), + }, + new Container // cover + { + RelativeSizeAxes = Axes.X, + Height = cover_height, + Children = new Drawable[] + { + new Container // this is the line badge-Changelog-Stream + { + Height = title_height, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Y = -version_height, + Children = new Drawable[] + { + new CircularContainer // a purple circle + { + X = icon_margin, + Masking = true, + BorderColour = purple, + BorderThickness = 3, + MaskingSmoothness = 1, + Size = new OpenTK.Vector2(50), + Children = new Drawable[] + { + headerBadge = new Sprite + { + Size = new OpenTK.Vector2(0.8f), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + }, + new Box // this ensures the purple circle doesn't disappear..? + { + RelativeSizeAxes = Axes.Both, + Size = new OpenTK.Vector2(1), + AlwaysPresent = true, + Colour = Color4.Transparent, + } + } + }, + headerTextContainer = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + X = icon_size + icon_margin * 2, + Children = new Drawable[] + { + title = new OsuSpriteText + { + Text = "Changelog ", + TextSize = 30, + }, + titleStream = new OsuSpriteText + { + Text = "Listing", + TextSize = 30, + Colour = purple, + }, + } + } + } + }, + breadcrumbContainer = new FillFlowContainer // Listing > Lazer 2018.713.1 + { + X = 2 * icon_margin + icon_size - 10, + Height = version_height, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + listing = new TextBadgePairListing(purple), + releaseStream = new TextBadgePairRelease(purple, "Lazer") + }, + }, + new Box // purple line + { + Colour = purple, + RelativeSizeAxes = Axes.X, + Height = 3, + Anchor = Anchor.BottomLeft, + Origin = Anchor.CentreLeft, + }, + } + } + } + } + }; + breadcrumbContainer.OnLoadComplete = d => + { + releaseStream.OnActivation = listing.Deactivate; + listing.OnActivation = releaseStream.Deactivate; + }; + listing.text.OnUpdate = d => + { + listing.lineBadge.ResizeWidthTo(listing.text.DrawWidth); + }; + } + + public void ListingActivation() + { + releaseStream.Deactivate(); + } + + public void ReleaseActivation() + { + listing.Deactivate(); + } + + public void ActivateRelease(string displayText) + { + releaseStream.Activate(displayText); + titleStream.Text = displayText; + } + + public void ActivateListing() + { + listing.Activate(); + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + // should be added to osu-resources? + // headerBadge.Texture = textures.Get(@"https://osu.ppy.sh/images/icons/changelog.svg"); // this is not working + headerBadge.Texture = textures.Get(@"https://i.imgur.com/HQM3Vhp.png"); + coverImage.Texture = textures.Get(@"https://osu.ppy.sh/images/headers/changelog.jpg"); + } + } +} diff --git a/osu.Game/Overlays/Changelog/Header/LineBadge.cs b/osu.Game/Overlays/Changelog/Header/LineBadge.cs new file mode 100644 index 0000000000..c3b4ecc187 --- /dev/null +++ b/osu.Game/Overlays/Changelog/Header/LineBadge.cs @@ -0,0 +1,35 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.Changelog.Header +{ + public class LineBadge : Circle + { + private const float transition_duration = 100; + private const float uncollapsed_height = 10; + public float UncollapsedHeight => uncollapsed_height; + public float TransitionDuration => transition_duration; + private bool isCollapsed; + public bool IsCollapsed + { + get { return isCollapsed; } + set + { + isCollapsed = value; + this.ResizeHeightTo(value ? 1 : 10, transition_duration); + } + } + + public LineBadge(bool startCollapsed = false) + { + IsCollapsed = startCollapsed; + Anchor = Anchor.BottomCentre; + Origin = Anchor.Centre; + } + } +} diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs new file mode 100644 index 0000000000..01326938eb --- /dev/null +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -0,0 +1,113 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Overlays.Changelog; +using System; + +namespace osu.Game.Overlays.Changelog.Header +{ + + public class TextBadgePair : ClickableContainer + { + // When in listing, "Listing" is white and doesn't change on mouseover + // when release stream is chosen, "Listing" turns purple, and lighter font + // on mouseover, the badge scales up + // Version name steals "Listing"'s styling + + public SpriteText text; + public LineBadge lineBadge; + + public Action OnActivation; + public Action OnDeactivation; + + public void SetTextColor(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) + { + text.FadeColour(newColour, duration, easing); + } + + public void SetBadgeColor(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) + { + lineBadge.FadeColour(newColour, duration, easing); + } + + public void HideText(double duration = 0, Easing easing = Easing.InOutCubic) + { + lineBadge.IsCollapsed = true; + text.MoveToY(20, duration, easing) + .FadeOut(duration, easing); + } + + public void ShowText(double duration = 0, Easing easing = Easing.InOutCubic) + { + lineBadge.IsCollapsed = false; + text.MoveToY(0, duration, easing) + .FadeIn(duration, easing) + .Finally(d => lineBadge.ResizeWidthTo(text.DrawWidth, 250)); + } + + public void ChangeText(double duration = 0, string displayText = null, Easing easing = Easing.InOutCubic) + { + lineBadge.IsCollapsed = true; + text.MoveToY(20, duration, easing) + .FadeOut(duration, easing) + .Finally(d => + { + lineBadge.ResizeWidthTo(0); + if (!string.IsNullOrEmpty(displayText)) text.Text = displayText; + text.MoveToY(0, duration, easing) + .FadeIn(duration, easing) + .OnComplete(dd => { + lineBadge.ResizeWidthTo(text.DrawWidth); + lineBadge.IsCollapsed = false; + }); + }); + } + + public TextBadgePair(ColourInfo badgeColour, string displayText = "Listing") + { + AutoSizeAxes = Axes.X; + RelativeSizeAxes = Axes.Y; + Children = new Drawable[] + { + text = new SpriteText + { + TextSize = 20, + Text = displayText, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AlwaysPresent = true, + Margin = new MarginPadding() + { + Top = 5, + Bottom = 15, + Left = 10, + Right = 10, + } + }, + lineBadge = new LineBadge + { + Colour = badgeColour, + } + }; + } + + public virtual void Deactivate() + { + lineBadge.IsCollapsed = true; + text.Font = "Exo2.0-Regular"; + } + + public virtual void Activate() + { + lineBadge.IsCollapsed = false; + text.Font = "Exo2.0-Bold"; + } + } +} diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs new file mode 100644 index 0000000000..569f5ff2f5 --- /dev/null +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs @@ -0,0 +1,58 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Input; +using System; + +namespace osu.Game.Overlays.Changelog.Header +{ + public class TextBadgePairListing : TextBadgePair + { + private TextBadgePairRelease releaseBadge; + private ColourInfo badgeColour; + + public TextBadgePairListing(ColourInfo badgeColour) : base(badgeColour, "Listing") + { + this.releaseBadge = releaseBadge; + this.badgeColour = badgeColour; + text.Font = "Exo2.0-Bold"; + } + + public override void Activate() + { + lineBadge.IsCollapsed = false; + text.Font = "Exo2.0-Bold"; + SetTextColor(Color4.White, 100); + OnActivation?.Invoke(); + } + + public override void Deactivate() + { + lineBadge.IsCollapsed = true; + //text.Font = "Exo2.0-Regular"; // commented out since it makes bad resize-jumping + SetTextColor(badgeColour, 100); + OnDeactivation?.Invoke(); + } + + protected override bool OnClick(InputState state) + { + Activate(); + return base.OnClick(state); + } + + protected override bool OnHover(InputState state) + { + lineBadge.ResizeHeightTo(lineBadge.UncollapsedHeight, lineBadge.TransitionDuration); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + if (lineBadge.IsCollapsed) lineBadge.ResizeHeightTo(1, lineBadge.TransitionDuration); + base.OnHoverLost(state); + } + } +} diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs new file mode 100644 index 0000000000..318e8a4c73 --- /dev/null +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Input; + +namespace osu.Game.Overlays.Changelog.Header +{ + public class TextBadgePairRelease : TextBadgePair + { + private TextBadgePairListing listingBadge; + + public TextBadgePairRelease(ColourInfo badgeColour, string displayText) : base(badgeColour, displayText) + { + this.listingBadge = listingBadge; + text.Font = "Exo2.0-Bold"; + text.Y = 20; + text.Alpha = 0; + } + + public void SetText(string displayText) + { + text.Text = displayText; + } + + public void Activate(string displayText = null) + { + ClearTransforms(); + if (text.IsPresent) ChangeText(250, displayText); + else ShowText(); + OnActivation?.Invoke(); + } + + public override void Deactivate() + { + FinishTransforms(true); + HideText(250); + OnDeactivation?.Invoke(); + } + } +} diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs new file mode 100644 index 0000000000..f6a138e603 --- /dev/null +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -0,0 +1,87 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.Changelog; +using System; +using System.Collections.Generic; +using System.Text; + +namespace osu.Game.Overlays +{ + public class ChangelogOverlay : WaveOverlayContainer + { + private readonly ScrollContainer scroll; + + public ChangelogHeader header; + + public ChangelogOverlay() + { + Waves.FirstWaveColour = OsuColour.Gray(0.4f); + Waves.SecondWaveColour = OsuColour.Gray(0.3f); + Waves.ThirdWaveColour = OsuColour.Gray(0.2f); + Waves.FourthWaveColour = OsuColour.Gray(0.1f); + + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; + RelativeSizeAxes = Axes.Both; + Width = 0.85f; + + Masking = true; + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0), + Type = EdgeEffectType.Shadow, + Radius = 3, + Offset = new Vector2(0f, 1f), + }; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new Color4(20, 18, 23, 255) + }, + scroll = new ScrollContainer + { + RelativeSizeAxes = Axes.Both, + ScrollbarVisible = false, + Child = new ReverseChildIDFillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + header = new ChangelogHeader(), + }, + }, + }, + }; + } + + // receive input outside our bounds so we can trigger a close event on ourselves. + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + + protected override void PopIn() + { + base.PopIn(); + FadeEdgeEffectTo(0.25f, WaveContainer.APPEAR_DURATION, Easing.In); + } + + protected override void PopOut() + { + base.PopOut(); + FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); + } + } +} From b4bb97fba89a577af78f38302e9d7f5925f6e820 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 17 Jul 2018 15:01:53 +0200 Subject: [PATCH 0008/2854] Header2 --- osu.Game.Tests/Visual/TestCaseChangelog.cs | 12 ++- .../Visual/TestCaseTextBadgePair.cs | 8 +- .../Input/Bindings/GlobalActionContainer.cs | 2 + osu.Game/OsuGame.cs | 11 ++- .../Overlays/Changelog/ChangelogHeader.cs | 81 +++++++++++-------- .../Overlays/Changelog/Header/LineBadge.cs | 5 ++ .../Changelog/Header/TextBadgePair.cs | 61 ++++++++------ .../Changelog/Header/TextBadgePairListing.cs | 14 ++-- .../Changelog/Header/TextBadgePairRelease.cs | 7 +- .../Toolbar/ToolbarChangelogButton.cs | 22 +++++ 10 files changed, 146 insertions(+), 77 deletions(-) create mode 100644 osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs diff --git a/osu.Game.Tests/Visual/TestCaseChangelog.cs b/osu.Game.Tests/Visual/TestCaseChangelog.cs index 688cf2b075..28c43b5153 100644 --- a/osu.Game.Tests/Visual/TestCaseChangelog.cs +++ b/osu.Game.Tests/Visual/TestCaseChangelog.cs @@ -17,13 +17,11 @@ namespace osu.Game.Tests.Visual base.LoadComplete(); Add(changelog = new ChangelogOverlay()); - changelog.ToggleVisibility(); - - //AddStep(@"toggle", changelog.ToggleVisibility); - AddStep(@"toggle text 1", () => changelog.header.ActivateRelease("Release 20180626.1")); - AddStep(@"toggle text 2", () => changelog.header.ActivateRelease("Lazer 2018.713.1")); - AddStep(@"toggle text 3", () => changelog.header.ActivateRelease("Beta 20180626")); - AddStep(@"go to listing", changelog.header.ActivateListing); + + AddStep(@"Show", changelog.Show); + AddStep(@"Stable Release Stream", () => changelog.header.ShowReleaseStream("Stable", "Stable 20180626.1")); + AddStep(@"Lazer Release Stream", () => changelog.header.ShowReleaseStream("Lazer", "Lazer 2018.713.1")); + AddStep(@"Listing", changelog.header.ActivateListing); } public TestCaseChangelog() diff --git a/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs b/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs index effce3d471..659bcb26ef 100644 --- a/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs +++ b/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs @@ -52,10 +52,10 @@ namespace osu.Game.Tests.Visual AddStep(@"deactivate", () => textBadgePair.Deactivate()); AddStep(@"activate", () => textBadgePair.Activate()); - AddStep(@"purple text", () => textBadgePair.SetTextColor(Color4.Purple, 100)); - AddStep(@"white text", () => textBadgePair.SetTextColor(Color4.White, 100)); - AddStep(@"purple badge", () => textBadgePair.SetBadgeColor(Color4.Purple, 100)); - AddStep(@"white badge", () => textBadgePair.SetBadgeColor(Color4.White, 100)); + AddStep(@"purple text", () => textBadgePair.SetTextColour(Color4.Purple, 100)); + AddStep(@"white text", () => textBadgePair.SetTextColour(Color4.White, 100)); + AddStep(@"purple badge", () => textBadgePair.SetBadgeColour(Color4.Purple, 100)); + AddStep(@"white badge", () => textBadgePair.SetBadgeColour(Color4.White, 100)); AddStep(@"hide text", () => textBadgePair.HideText(250)); AddStep(@"show text", () => textBadgePair.ShowText(250)); } diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index b21deff509..00857b9c9b 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -63,6 +63,8 @@ namespace osu.Game.Input.Bindings ToggleChat, [Description("Toggle social overlay")] ToggleSocial, + [Description("Toggle changelog")] + ToggleChangelog, [Description("Reset input settings")] ResetInputSettings, [Description("Toggle toolbar")] diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 557b6e4469..3eb4faf6aa 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -53,6 +53,8 @@ namespace osu.Game private DialogOverlay dialogOverlay; + private ChangelogOverlay changelog; + private DirectOverlay direct; private SocialOverlay social; @@ -110,6 +112,8 @@ namespace osu.Game public void ToggleDirect() => direct.ToggleVisibility(); + public void ToggleChangelog() => changelog.ToggleVisibility(); + /// /// Close all game-wide overlays. /// @@ -281,6 +285,7 @@ namespace osu.Game loadComponentSingleFile(screenshotManager, Add); //overlay elements + loadComponentSingleFile(changelog = new ChangelogOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(social = new SocialOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(chat = new ChatOverlay { Depth = -1 }, mainContent.Add); @@ -315,6 +320,7 @@ namespace osu.Game dependencies.Cache(settings); dependencies.Cache(onscreenDisplay); dependencies.Cache(social); + dependencies.Cache(changelog); dependencies.Cache(direct); dependencies.Cache(chat); dependencies.Cache(userProfile); @@ -349,7 +355,7 @@ namespace osu.Game } // ensure only one of these overlays are open at once. - var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct }; + var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct, changelog }; overlays.AddRange(singleDisplayOverlays); foreach (var overlay in singleDisplayOverlays) @@ -459,6 +465,9 @@ namespace osu.Game case GlobalAction.ToggleSocial: social.ToggleVisibility(); return true; + case GlobalAction.ToggleChangelog: + changelog.ToggleVisibility(); + return true; case GlobalAction.ResetInputSettings: var sensitivity = frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity); diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index c8fff5bd6b..09812f784b 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using OpenTK; using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; @@ -10,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; using osu.Game.Overlays.Changelog.Header; @@ -55,13 +57,16 @@ namespace osu.Game.Overlays.Changelog { RelativeSizeAxes = Axes.Both, Size = new OpenTK.Vector2(1), + FillMode = FillMode.Fill, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, }, - new Container // cover - { - RelativeSizeAxes = Axes.X, - Height = cover_height, - Children = new Drawable[] - { + //new Container + //{ + // RelativeSizeAxes = Axes.X, + // Height = cover_height, + // Children = new Drawable[] + // { new Container // this is the line badge-Changelog-Stream { Height = title_height, @@ -108,12 +113,14 @@ namespace osu.Game.Overlays.Changelog title = new OsuSpriteText { Text = "Changelog ", - TextSize = 30, + Font = @"Exo2.0-Light", + TextSize = 38, // web: 30 }, titleStream = new OsuSpriteText { Text = "Listing", - TextSize = 30, + TextSize = 38, // web: 30 + Font = @"Exo2.0-Light", Colour = purple, }, } @@ -122,7 +129,7 @@ namespace osu.Game.Overlays.Changelog }, breadcrumbContainer = new FillFlowContainer // Listing > Lazer 2018.713.1 { - X = 2 * icon_margin + icon_size - 10, + X = 2 * icon_margin + icon_size - 8, // for some reason off by 3px Height = version_height, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, @@ -130,6 +137,21 @@ namespace osu.Game.Overlays.Changelog Children = new Drawable[] { listing = new TextBadgePairListing(purple), + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(7), + Colour = OsuColour.FromHex(@"bf04ff"), + Icon = FontAwesome.fa_chevron_right, + Margin = new MarginPadding() + { + Top = 8, + Left = 5, + Right = 5, + Bottom = 15, + }, + }, releaseStream = new TextBadgePairRelease(purple, "Lazer") }, }, @@ -141,42 +163,37 @@ namespace osu.Game.Overlays.Changelog Anchor = Anchor.BottomLeft, Origin = Anchor.CentreLeft, }, - } - } + // } + //} } } }; - breadcrumbContainer.OnLoadComplete = d => + + // is this a bad way to do this? + OnLoadComplete = d => { releaseStream.OnActivation = listing.Deactivate; - listing.OnActivation = releaseStream.Deactivate; - }; - listing.text.OnUpdate = d => - { - listing.lineBadge.ResizeWidthTo(listing.text.DrawWidth); + listing.OnActivation = () => + { + releaseStream.Deactivate(); + ChangeHeaderText("Listing"); + }; }; } - - public void ListingActivation() + + public void ShowReleaseStream(string headerText, string breadcrumbText) { - releaseStream.Deactivate(); + releaseStream.Activate(breadcrumbText); + ChangeHeaderText(headerText); } - public void ReleaseActivation() + private void ChangeHeaderText(string headerText) { - listing.Deactivate(); + titleStream.Text = headerText; + titleStream.FlashColour(Color4.White, 500, Easing.OutQuad); } - public void ActivateRelease(string displayText) - { - releaseStream.Activate(displayText); - titleStream.Text = displayText; - } - - public void ActivateListing() - { - listing.Activate(); - } + public void ActivateListing() => listing.Activate(); [BackgroundDependencyLoader] private void load(TextureStore textures) diff --git a/osu.Game/Overlays/Changelog/Header/LineBadge.cs b/osu.Game/Overlays/Changelog/Header/LineBadge.cs index c3b4ecc187..f321939a96 100644 --- a/osu.Game/Overlays/Changelog/Header/LineBadge.cs +++ b/osu.Game/Overlays/Changelog/Header/LineBadge.cs @@ -30,6 +30,11 @@ namespace osu.Game.Overlays.Changelog.Header IsCollapsed = startCollapsed; Anchor = Anchor.BottomCentre; Origin = Anchor.Centre; + Margin = new MarginPadding() + { + Left = 10, + Right = 10, + }; } } } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index 01326938eb..5d1018e504 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -8,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; +using osu.Game.Graphics; using osu.Game.Overlays.Changelog; using System; @@ -16,23 +18,18 @@ namespace osu.Game.Overlays.Changelog.Header public class TextBadgePair : ClickableContainer { - // When in listing, "Listing" is white and doesn't change on mouseover - // when release stream is chosen, "Listing" turns purple, and lighter font - // on mouseover, the badge scales up - // Version name steals "Listing"'s styling - - public SpriteText text; - public LineBadge lineBadge; + protected SpriteText text; + protected LineBadge lineBadge; public Action OnActivation; public Action OnDeactivation; - public void SetTextColor(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) + public void SetTextColour(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) { text.FadeColour(newColour, duration, easing); } - public void SetBadgeColor(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) + public void SetBadgeColour(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) { lineBadge.FadeColour(newColour, duration, easing); } @@ -44,30 +41,42 @@ namespace osu.Game.Overlays.Changelog.Header .FadeOut(duration, easing); } - public void ShowText(double duration = 0, Easing easing = Easing.InOutCubic) + public void ShowText(double duration = 0, string displayText = null, Easing easing = Easing.InOutCubic) { - lineBadge.IsCollapsed = false; + if (!string.IsNullOrEmpty(displayText)) text.Text = displayText; text.MoveToY(0, duration, easing) .FadeIn(duration, easing) - .Finally(d => lineBadge.ResizeWidthTo(text.DrawWidth, 250)); + .Finally(d => { + // waiting until text is drawn to use its DrawWidth + UpdateBadgeWidth(); + lineBadge.IsCollapsed = false; + }); } + /// + /// The duration of popping in and popping out not combined. + /// Full change takes double this time. public void ChangeText(double duration = 0, string displayText = null, Easing easing = Easing.InOutCubic) { lineBadge.IsCollapsed = true; text.MoveToY(20, duration, easing) .FadeOut(duration, easing) - .Finally(d => - { - lineBadge.ResizeWidthTo(0); - if (!string.IsNullOrEmpty(displayText)) text.Text = displayText; - text.MoveToY(0, duration, easing) - .FadeIn(duration, easing) - .OnComplete(dd => { - lineBadge.ResizeWidthTo(text.DrawWidth); - lineBadge.IsCollapsed = false; - }); + .Then() + .MoveToY(0, duration, easing) + .FadeIn(duration, easing) + .OnComplete(dd => { + UpdateBadgeWidth(); + lineBadge.IsCollapsed = false; }); + + // since using .finally/.oncomplete after first fadeout made the badge + // not hide sometimes in visual tests(because FinishTransforms()/CancelTransforms() + // didn't apply to transforms that come after the .finally), I'm using a scheduler here + Scheduler.AddDelayed(() => + { + lineBadge.ResizeWidthTo(0); // resizes when not visible + if (!string.IsNullOrEmpty(displayText)) text.Text = displayText; + }, duration); } public TextBadgePair(ColourInfo badgeColour, string displayText = "Listing") @@ -78,10 +87,10 @@ namespace osu.Game.Overlays.Changelog.Header { text = new SpriteText { - TextSize = 20, + TextSize = 21, // web is 16, but here it looks too small? Text = displayText, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, AlwaysPresent = true, Margin = new MarginPadding() { @@ -109,5 +118,7 @@ namespace osu.Game.Overlays.Changelog.Header lineBadge.IsCollapsed = false; text.Font = "Exo2.0-Bold"; } + + public void UpdateBadgeWidth() => lineBadge.ResizeWidthTo(text.DrawWidth); } } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs index 569f5ff2f5..f89ed8362e 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs @@ -11,29 +11,33 @@ namespace osu.Game.Overlays.Changelog.Header { public class TextBadgePairListing : TextBadgePair { - private TextBadgePairRelease releaseBadge; private ColourInfo badgeColour; public TextBadgePairListing(ColourInfo badgeColour) : base(badgeColour, "Listing") { - this.releaseBadge = releaseBadge; this.badgeColour = badgeColour; text.Font = "Exo2.0-Bold"; + text.Anchor = Anchor.TopCentre; + text.Origin = Anchor.TopCentre; + + // this doesn't work without the scheduler + // (because the text isn't yet fully drawn when it's loaded?) + text.OnLoadComplete = d => Scheduler.Add(UpdateBadgeWidth); } public override void Activate() { lineBadge.IsCollapsed = false; text.Font = "Exo2.0-Bold"; - SetTextColor(Color4.White, 100); + SetTextColour(Color4.White, 100); OnActivation?.Invoke(); } public override void Deactivate() { lineBadge.IsCollapsed = true; - //text.Font = "Exo2.0-Regular"; // commented out since it makes bad resize-jumping - SetTextColor(badgeColour, 100); + text.Font = "Exo2.0-Regular"; // commented out since it makes bad resize-jumping + SetTextColour(badgeColour, 100); OnDeactivation?.Invoke(); } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs index 318e8a4c73..71e3b251c2 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs @@ -10,6 +10,7 @@ namespace osu.Game.Overlays.Changelog.Header public class TextBadgePairRelease : TextBadgePair { private TextBadgePairListing listingBadge; + private const float transition_duration = 125; public TextBadgePairRelease(ColourInfo badgeColour, string displayText) : base(badgeColour, displayText) { @@ -27,15 +28,15 @@ namespace osu.Game.Overlays.Changelog.Header public void Activate(string displayText = null) { ClearTransforms(); - if (text.IsPresent) ChangeText(250, displayText); - else ShowText(); + if (!lineBadge.IsCollapsed) ChangeText(transition_duration, displayText); + else ShowText(transition_duration, displayText); OnActivation?.Invoke(); } public override void Deactivate() { FinishTransforms(true); - HideText(250); + HideText(transition_duration); OnDeactivation?.Invoke(); } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs new file mode 100644 index 0000000000..db4fd4ba07 --- /dev/null +++ b/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs @@ -0,0 +1,22 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarChangelogButton : ToolbarOverlayToggleButton + { + public ToolbarChangelogButton() + { + SetIcon(FontAwesome.fa_list); + } + + [BackgroundDependencyLoader(true)] + private void load(ChangelogOverlay changelog) + { + StateContainer = changelog; + } + } +} From 3c1e445fdfddd13e64a71dc60739e0af5f3ea1b8 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 17 Jul 2018 18:32:11 +0200 Subject: [PATCH 0009/2854] Header3 --- .../Visual/TestCaseTextBadgePair.cs | 16 +- .../Overlays/Changelog/ChangelogHeader.cs | 197 +++++++++--------- .../Overlays/Changelog/Header/LineBadge.cs | 14 +- .../Changelog/Header/TextBadgePair.cs | 15 +- .../Changelog/Header/TextBadgePairListing.cs | 1 - .../Changelog/Header/TextBadgePairRelease.cs | 10 +- osu.Game/Overlays/ChangelogOverlay.cs | 17 +- 7 files changed, 129 insertions(+), 141 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs b/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs index 659bcb26ef..9a4e481ff8 100644 --- a/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs +++ b/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs @@ -3,11 +3,9 @@ using NUnit.Framework; using OpenTK.Graphics; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; using osu.Game.Overlays.Changelog.Header; namespace osu.Game.Tests.Visual @@ -58,19 +56,7 @@ namespace osu.Game.Tests.Visual AddStep(@"white badge", () => textBadgePair.SetBadgeColour(Color4.White, 100)); AddStep(@"hide text", () => textBadgePair.HideText(250)); AddStep(@"show text", () => textBadgePair.ShowText(250)); + AddStep(@"change text", () => textBadgePair.ChangeText(250)); } - - //[BackgroundDependencyLoader] - //private void load(OsuColour colours) - //{ - - //} - - //private enum BreadcrumbTab - //{ - // Click, - // The, - // Circles, - //} } } diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 09812f784b..cc07ffeb18 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -4,20 +4,14 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; 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.Framework.Graphics.Textures; using osu.Game.Graphics; -using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; using osu.Game.Overlays.Changelog.Header; -using System; -using System.Collections.Generic; -using System.Text; namespace osu.Game.Overlays.Changelog { @@ -25,11 +19,12 @@ namespace osu.Game.Overlays.Changelog { private readonly Container coverContainer; - private Color4 purple = new Color4(191, 4, 255, 255); + protected Color4 purple = new Color4(191, 4, 255, 255); private readonly Sprite coverImage; private readonly Sprite headerBadge; //50x50, margin-right: 20 private readonly FillFlowContainer headerTextContainer; private readonly OsuSpriteText title, titleStream; + private readonly SpriteIcon chevron; private readonly TextBadgePairListing listing; private readonly TextBadgePairRelease releaseStream; private readonly FillFlowContainer breadcrumbContainer; @@ -61,110 +56,117 @@ namespace osu.Game.Overlays.Changelog Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - //new Container - //{ - // RelativeSizeAxes = Axes.X, - // Height = cover_height, - // Children = new Drawable[] - // { - new Container // this is the line badge-Changelog-Stream + new Container // this is the line badge-Changelog-Stream + { + Height = title_height, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Y = -version_height, + Children = new Drawable[] + { + new CircularContainer // a purple circle { - Height = title_height, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Y = -version_height, + X = icon_margin, + Masking = true, + BorderColour = purple, + BorderThickness = 3, + MaskingSmoothness = 1, + Size = new OpenTK.Vector2(50), Children = new Drawable[] { - new CircularContainer // a purple circle + headerBadge = new Sprite { - X = icon_margin, - Masking = true, - BorderColour = purple, - BorderThickness = 3, - MaskingSmoothness = 1, - Size = new OpenTK.Vector2(50), - Children = new Drawable[] - { - headerBadge = new Sprite - { - Size = new OpenTK.Vector2(0.8f), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - }, - new Box // this ensures the purple circle doesn't disappear..? - { - RelativeSizeAxes = Axes.Both, - Size = new OpenTK.Vector2(1), - AlwaysPresent = true, - Colour = Color4.Transparent, - } - } + RelativeSizeAxes = Axes.Both, + Size = new OpenTK.Vector2(0.8f), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, }, - headerTextContainer = new FillFlowContainer + + // this box has 2 functions: + // - ensures the circle doesn't disappear on the X and Y edges + // - lessens the white "contamination" on the circle (due to smoothing) + new Box { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - X = icon_size + icon_margin * 2, - Children = new Drawable[] - { - title = new OsuSpriteText - { - Text = "Changelog ", - Font = @"Exo2.0-Light", - TextSize = 38, // web: 30 - }, - titleStream = new OsuSpriteText - { - Text = "Listing", - TextSize = 38, // web: 30 - Font = @"Exo2.0-Light", - Colour = purple, - }, - } + RelativeSizeAxes = Axes.Both, + Size = new OpenTK.Vector2(1), + Alpha = 0, + AlwaysPresent = true, + Colour = purple, } } }, - breadcrumbContainer = new FillFlowContainer // Listing > Lazer 2018.713.1 + headerTextContainer = new FillFlowContainer { - X = 2 * icon_margin + icon_size - 8, // for some reason off by 3px - Height = version_height, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, + AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + X = icon_size + icon_margin * 2, Children = new Drawable[] { - listing = new TextBadgePairListing(purple), - new SpriteIcon + title = new OsuSpriteText { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(7), - Colour = OsuColour.FromHex(@"bf04ff"), - Icon = FontAwesome.fa_chevron_right, - Margin = new MarginPadding() - { - Top = 8, - Left = 5, - Right = 5, - Bottom = 15, - }, + Text = "Changelog ", + Font = @"Exo2.0-Light", + TextSize = 38, // web: 30 + }, + titleStream = new OsuSpriteText + { + Text = "Listing", + TextSize = 38, // web: 30 + Font = @"Exo2.0-Light", + Colour = purple, + }, + } + } + } + }, + breadcrumbContainer = new FillFlowContainer // Listing > Lazer 2018.713.1 + { + X = 2 * icon_margin + icon_size - 8, + Height = version_height, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + listing = new TextBadgePairListing(purple), + new Container() // without a container, moving the chevron wont work + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding() + { + Top = 10, + Left = 7, + Right = 9, + Bottom = 15, + }, + Children = new Drawable[] + { + chevron = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(7), + Colour = purple, + Icon = FontAwesome.fa_chevron_right, + Alpha = 0, + X = -200, }, - releaseStream = new TextBadgePairRelease(purple, "Lazer") }, }, - new Box // purple line - { - Colour = purple, - RelativeSizeAxes = Axes.X, - Height = 3, - Anchor = Anchor.BottomLeft, - Origin = Anchor.CentreLeft, - }, - // } - //} + releaseStream = new TextBadgePairRelease(purple, "Lazer") + }, + }, + new Box // purple line + { + Colour = purple, + RelativeSizeAxes = Axes.X, + Height = 3, + Anchor = Anchor.BottomLeft, + Origin = Anchor.CentreLeft, + }, } } }; @@ -172,10 +174,15 @@ namespace osu.Game.Overlays.Changelog // is this a bad way to do this? OnLoadComplete = d => { - releaseStream.OnActivation = listing.Deactivate; + releaseStream.OnActivation = () => + { + listing.Deactivate(); + chevron.MoveToX(0, 100).FadeIn(100); + }; listing.OnActivation = () => { releaseStream.Deactivate(); + chevron.MoveToX(-20, 100).FadeOut(100); ChangeHeaderText("Listing"); }; }; diff --git a/osu.Game/Overlays/Changelog/Header/LineBadge.cs b/osu.Game/Overlays/Changelog/Header/LineBadge.cs index f321939a96..0846821c5c 100644 --- a/osu.Game/Overlays/Changelog/Header/LineBadge.cs +++ b/osu.Game/Overlays/Changelog/Header/LineBadge.cs @@ -3,8 +3,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Changelog.Header { @@ -12,24 +10,26 @@ namespace osu.Game.Overlays.Changelog.Header { private const float transition_duration = 100; private const float uncollapsed_height = 10; - public float UncollapsedHeight => uncollapsed_height; + public float TransitionDuration => transition_duration; - private bool isCollapsed; + public float UncollapsedHeight => uncollapsed_height; + protected bool isCollapsed; public bool IsCollapsed { get { return isCollapsed; } set { isCollapsed = value; - this.ResizeHeightTo(value ? 1 : 10, transition_duration); + this.ResizeHeightTo(value ? 1 : uncollapsed_height, transition_duration); } } - public LineBadge(bool startCollapsed = false) + public LineBadge() { - IsCollapsed = startCollapsed; Anchor = Anchor.BottomCentre; Origin = Anchor.Centre; + + // this margin prevents jumps when changing text's font weight Margin = new MarginPadding() { Left = 10, diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index 5d1018e504..24ce873a95 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -1,16 +1,10 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; -using OpenTK.Graphics; 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.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Overlays.Changelog; using System; namespace osu.Game.Overlays.Changelog.Header @@ -43,7 +37,11 @@ namespace osu.Game.Overlays.Changelog.Header public void ShowText(double duration = 0, string displayText = null, Easing easing = Easing.InOutCubic) { - if (!string.IsNullOrEmpty(displayText)) text.Text = displayText; + if (!string.IsNullOrEmpty(displayText)) + { + text.Text = displayText; + } + text.MoveToY(0, duration, easing) .FadeIn(duration, easing) .Finally(d => { @@ -74,7 +72,7 @@ namespace osu.Game.Overlays.Changelog.Header // didn't apply to transforms that come after the .finally), I'm using a scheduler here Scheduler.AddDelayed(() => { - lineBadge.ResizeWidthTo(0); // resizes when not visible + //lineBadge.ResizeWidthTo(0); // resizes when not visible if (!string.IsNullOrEmpty(displayText)) text.Text = displayText; }, duration); } @@ -91,7 +89,6 @@ namespace osu.Game.Overlays.Changelog.Header Text = displayText, Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, - AlwaysPresent = true, Margin = new MarginPadding() { Top = 5, diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs index f89ed8362e..4897aeedd1 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs @@ -5,7 +5,6 @@ using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Input; -using System; namespace osu.Game.Overlays.Changelog.Header { diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs index 71e3b251c2..f69c7dd6a7 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs @@ -1,9 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; -using osu.Framework.Input; namespace osu.Game.Overlays.Changelog.Header { @@ -27,15 +25,17 @@ namespace osu.Game.Overlays.Changelog.Header public void Activate(string displayText = null) { - ClearTransforms(); - if (!lineBadge.IsCollapsed) ChangeText(transition_duration, displayText); + //ClearTransforms(); + // not using if (!lineBadge.IsCollapsed) because the text sometimes gets reset + // when quickly switching release streams + if (text.IsPresent) ChangeText(transition_duration, displayText); else ShowText(transition_duration, displayText); OnActivation?.Invoke(); } public override void Deactivate() { - FinishTransforms(true); + //FinishTransforms(true); HideText(transition_duration); OnDeactivation?.Invoke(); } diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index f6a138e603..915d747f00 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -9,11 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osu.Game.Overlays.Changelog; -using System; -using System.Collections.Generic; -using System.Text; namespace osu.Game.Overlays { @@ -23,19 +19,22 @@ namespace osu.Game.Overlays public ChangelogHeader header; + protected Color4 purple = new Color4(191, 4, 255, 255); + public ChangelogOverlay() { - Waves.FirstWaveColour = OsuColour.Gray(0.4f); - Waves.SecondWaveColour = OsuColour.Gray(0.3f); - Waves.ThirdWaveColour = OsuColour.Gray(0.2f); - Waves.FourthWaveColour = OsuColour.Gray(0.1f); + // these possibly need adjusting? + Waves.FirstWaveColour = OsuColour.FromHex(@"bf04ff"); + Waves.SecondWaveColour = OsuColour.FromHex(@"8F03BF"); + Waves.ThirdWaveColour = OsuColour.FromHex(@"600280"); + Waves.FourthWaveColour = OsuColour.FromHex(@"300140"); Anchor = Anchor.TopCentre; Origin = Anchor.TopCentre; RelativeSizeAxes = Axes.Both; Width = 0.85f; - Masking = true; + EdgeEffect = new EdgeEffectParameters { Colour = Color4.Black.Opacity(0), From 523d47e8154e573cf7873ec0650dd7bcde0db379 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 18 Jul 2018 01:35:06 +0200 Subject: [PATCH 0010/2854] Streams1 --- osu.Game.Tests/Visual/TestCaseChangelog.cs | 12 +- osu.Game/Graphics/StreamColour.cs | 17 ++ .../Overlays/Changelog/ChangelogHeader.cs | 217 +++++++++--------- .../Overlays/Changelog/ChangelogStreams.cs | 81 +++++++ .../Overlays/Changelog/Header/LineBadge.cs | 14 +- .../Changelog/Header/TextBadgePair.cs | 7 +- .../Changelog/Header/TextBadgePairListing.cs | 2 +- .../Overlays/Changelog/ReleaseStreamInfo.cs | 25 ++ .../Overlays/Changelog/Streams/StreamBadge.cs | 128 +++++++++++ osu.Game/Overlays/ChangelogOverlay.cs | 20 +- 10 files changed, 397 insertions(+), 126 deletions(-) create mode 100644 osu.Game/Graphics/StreamColour.cs create mode 100644 osu.Game/Overlays/Changelog/ChangelogStreams.cs create mode 100644 osu.Game/Overlays/Changelog/ReleaseStreamInfo.cs create mode 100644 osu.Game/Overlays/Changelog/Streams/StreamBadge.cs diff --git a/osu.Game.Tests/Visual/TestCaseChangelog.cs b/osu.Game.Tests/Visual/TestCaseChangelog.cs index 28c43b5153..c9b8a4d652 100644 --- a/osu.Game.Tests/Visual/TestCaseChangelog.cs +++ b/osu.Game.Tests/Visual/TestCaseChangelog.cs @@ -11,16 +11,22 @@ namespace osu.Game.Tests.Visual public class TestCaseChangelog : OsuTestCase { private ChangelogOverlay changelog; + private int releaseStreamCount; + private int index; protected override void LoadComplete() { base.LoadComplete(); Add(changelog = new ChangelogOverlay()); - + + releaseStreamCount = changelog.streams.badgesContainer.Children.Count; + AddStep(@"Show", changelog.Show); - AddStep(@"Stable Release Stream", () => changelog.header.ShowReleaseStream("Stable", "Stable 20180626.1")); - AddStep(@"Lazer Release Stream", () => changelog.header.ShowReleaseStream("Lazer", "Lazer 2018.713.1")); + AddRepeatStep(@"Toggle Release Stream", () => { + changelog.streams.badgesContainer.Children[index].Activate(); + index = (index == releaseStreamCount - 1) ? 0 : index + 1; + }, releaseStreamCount); AddStep(@"Listing", changelog.header.ActivateListing); } diff --git a/osu.Game/Graphics/StreamColour.cs b/osu.Game/Graphics/StreamColour.cs new file mode 100644 index 0000000000..fbb272c526 --- /dev/null +++ b/osu.Game/Graphics/StreamColour.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; + +namespace osu.Game.Graphics +{ + public class StreamColour + { + public readonly Color4 Stable = new Color4(102, 204, 255, 255); + public readonly Color4 StableFallback = new Color4(34, 153, 187, 255); + public readonly Color4 Beta = new Color4(255, 221, 85, 255); + public readonly Color4 CuttingEdge = new Color4(238, 170, 0, 255); + public readonly Color4 Lazer = new Color4(237, 18, 33, 255); + public readonly Color4 Web = new Color4(136, 102, 238, 255); + } +} diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index cc07ffeb18..183a63575b 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -12,13 +12,12 @@ using osu.Framework.Graphics.Textures; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Overlays.Changelog.Header; +using System; namespace osu.Game.Overlays.Changelog { public class ChangelogHeader : Container { - private readonly Container coverContainer; - protected Color4 purple = new Color4(191, 4, 255, 255); private readonly Sprite coverImage; private readonly Sprite headerBadge; //50x50, margin-right: 20 @@ -29,6 +28,8 @@ namespace osu.Game.Overlays.Changelog private readonly TextBadgePairRelease releaseStream; private readonly FillFlowContainer breadcrumbContainer; + public Action OnListingActivated; + private const float cover_height = 310; private const float title_height = 50; private const float icon_size = 50; @@ -38,137 +39,128 @@ namespace osu.Game.Overlays.Changelog public ChangelogHeader() { RelativeSizeAxes = Axes.X; - Height = cover_height + 5; // 5 is for the "badge" that sticks a bit out of the bottom - Masking = true; // is masking necessary? since I see it nearly everywhere + Height = cover_height; Children = new Drawable[] { - coverContainer = new Container + coverImage = new Sprite { - RelativeSizeAxes = Axes.X, - Height = cover_height, + RelativeSizeAxes = Axes.Both, + Size = new OpenTK.Vector2(1), + FillMode = FillMode.Fill, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new Container // this is the line badge-Changelog-Stream + { + Height = title_height, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Y = -version_height, Children = new Drawable[] { - coverImage = new Sprite + new CircularContainer // a purple circle { - RelativeSizeAxes = Axes.Both, - Size = new OpenTK.Vector2(1), - FillMode = FillMode.Fill, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - new Container // this is the line badge-Changelog-Stream - { - Height = title_height, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Y = -version_height, + X = icon_margin, + Masking = true, + BorderColour = purple, + BorderThickness = 3, + MaskingSmoothness = 1, + Size = new OpenTK.Vector2(50), Children = new Drawable[] { - new CircularContainer // a purple circle + headerBadge = new Sprite { - X = icon_margin, - Masking = true, - BorderColour = purple, - BorderThickness = 3, - MaskingSmoothness = 1, - Size = new OpenTK.Vector2(50), - Children = new Drawable[] - { - headerBadge = new Sprite - { - RelativeSizeAxes = Axes.Both, - Size = new OpenTK.Vector2(0.8f), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - - // this box has 2 functions: - // - ensures the circle doesn't disappear on the X and Y edges - // - lessens the white "contamination" on the circle (due to smoothing) - new Box - { - RelativeSizeAxes = Axes.Both, - Size = new OpenTK.Vector2(1), - Alpha = 0, - AlwaysPresent = true, - Colour = purple, - } - } + RelativeSizeAxes = Axes.Both, + Size = new OpenTK.Vector2(0.8f), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, }, - headerTextContainer = new FillFlowContainer + + // this box has 2 functions: + // - ensures the circle doesn't disappear on the X and Y edges + // - lessens the white "contamination" on the circle (due to smoothing) + new Box { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - X = icon_size + icon_margin * 2, - Children = new Drawable[] - { - title = new OsuSpriteText - { - Text = "Changelog ", - Font = @"Exo2.0-Light", - TextSize = 38, // web: 30 - }, - titleStream = new OsuSpriteText - { - Text = "Listing", - TextSize = 38, // web: 30 - Font = @"Exo2.0-Light", - Colour = purple, - }, - } + RelativeSizeAxes = Axes.Both, + Size = new OpenTK.Vector2(1), + Alpha = 0, + AlwaysPresent = true, + Colour = purple, } } }, - breadcrumbContainer = new FillFlowContainer // Listing > Lazer 2018.713.1 + headerTextContainer = new FillFlowContainer { - X = 2 * icon_margin + icon_size - 8, - Height = version_height, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, + AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + X = icon_size + icon_margin * 2, Children = new Drawable[] { - listing = new TextBadgePairListing(purple), - new Container() // without a container, moving the chevron wont work + title = new OsuSpriteText { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding() - { - Top = 10, - Left = 7, - Right = 9, - Bottom = 15, - }, - Children = new Drawable[] - { - chevron = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(7), - Colour = purple, - Icon = FontAwesome.fa_chevron_right, - Alpha = 0, - X = -200, - }, - }, + Text = "Changelog ", + Font = @"Exo2.0-Light", + TextSize = 38, // web: 30 + }, + titleStream = new OsuSpriteText + { + Text = "Listing", + TextSize = 38, // web: 30 + Font = @"Exo2.0-Light", + Colour = purple, + }, + } + } + } + }, + breadcrumbContainer = new FillFlowContainer // Listing > Lazer 2018.713.1 + { + X = 2 * icon_margin + icon_size - 8, + Height = version_height, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + listing = new TextBadgePairListing(purple), + new Container() // without a container, moving the chevron wont work + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding() + { + Top = 10, + Left = 7, + Right = 9, + Bottom = 15, + }, + Children = new Drawable[] + { + chevron = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(7), + Colour = purple, + Icon = FontAwesome.fa_chevron_right, + Alpha = 0, + X = -200, }, - releaseStream = new TextBadgePairRelease(purple, "Lazer") }, }, - new Box // purple line - { - Colour = purple, - RelativeSizeAxes = Axes.X, - Height = 3, - Anchor = Anchor.BottomLeft, - Origin = Anchor.CentreLeft, - }, - } - } + releaseStream = new TextBadgePairRelease(purple, "Lazer") + }, + }, + new Box // purple line + { + Colour = purple, + RelativeSizeAxes = Axes.X, + Height = 3, + Anchor = Anchor.BottomLeft, + Origin = Anchor.CentreLeft, + }, }; // is this a bad way to do this? @@ -184,6 +176,7 @@ namespace osu.Game.Overlays.Changelog releaseStream.Deactivate(); chevron.MoveToX(-20, 100).FadeOut(100); ChangeHeaderText("Listing"); + OnListingActivated?.Invoke(); }; }; } @@ -201,7 +194,7 @@ namespace osu.Game.Overlays.Changelog } public void ActivateListing() => listing.Activate(); - + [BackgroundDependencyLoader] private void load(TextureStore textures) { diff --git a/osu.Game/Overlays/Changelog/ChangelogStreams.cs b/osu.Game/Overlays/Changelog/ChangelogStreams.cs new file mode 100644 index 0000000000..e7f7d44c80 --- /dev/null +++ b/osu.Game/Overlays/Changelog/ChangelogStreams.cs @@ -0,0 +1,81 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Overlays.Changelog.Streams; +using System; + +namespace osu.Game.Overlays.Changelog +{ + public class ChangelogStreams : Container + { + private const float containerHeight = 106.5f; + private const float containerTopBottomMargin = 20; + private const float containerSideMargin = 85; + + public Bindable SelectedRelease = new Bindable(); + + private readonly StreamColour streamColour; + public readonly FillFlowContainer badgesContainer; + + public ChangelogStreams() + { + streamColour = new StreamColour(); + Height = containerHeight; + RelativeSizeAxes = Axes.X; + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Size = new OpenTK.Vector2(1), + Colour = new Color4(32, 24, 35, 255), + }, + badgesContainer = new FillFlowContainer + { + Direction = FillDirection.Horizontal, + RelativeSizeAxes = Axes.Both, + Margin = new MarginPadding() + { + Top = containerTopBottomMargin, + Bottom = containerTopBottomMargin, + Left = containerSideMargin, + Right = containerSideMargin, + }, + Children = new[] + { + new StreamBadge(streamColour.Stable, "Stable", "20180626.1", 16370, true), + new StreamBadge(streamColour.Beta, "Beta", "20180626", 186), + new StreamBadge(streamColour.Lazer, "Lazer", "2018.713.1"), + }, + }, + }; + badgesContainer.OnLoadComplete = d => + { + foreach (StreamBadge streamBadge in badgesContainer.Children) + { + streamBadge.OnActivation = () => + { + SelectedRelease.Value = new ReleaseStreamInfo() + { + DisplayVersion = streamBadge.DisplayVersion, + IsFeatured = streamBadge.IsFeatured, + Name = streamBadge.Name, + Users = streamBadge.Users, + }; + foreach (StreamBadge item in badgesContainer.Children) + { + if (item.Name != streamBadge.Name) item.Deactivate(); + } + }; + } + }; + } + } +} diff --git a/osu.Game/Overlays/Changelog/Header/LineBadge.cs b/osu.Game/Overlays/Changelog/Header/LineBadge.cs index 0846821c5c..c33244d50b 100644 --- a/osu.Game/Overlays/Changelog/Header/LineBadge.cs +++ b/osu.Game/Overlays/Changelog/Header/LineBadge.cs @@ -8,11 +8,10 @@ namespace osu.Game.Overlays.Changelog.Header { public class LineBadge : Circle { - private const float transition_duration = 100; - private const float uncollapsed_height = 10; + public float TransitionDuration = 100; + public float UncollapsedHeight; + public float CollapsedHeight; - public float TransitionDuration => transition_duration; - public float UncollapsedHeight => uncollapsed_height; protected bool isCollapsed; public bool IsCollapsed { @@ -20,14 +19,17 @@ namespace osu.Game.Overlays.Changelog.Header set { isCollapsed = value; - this.ResizeHeightTo(value ? 1 : uncollapsed_height, transition_duration); + this.ResizeHeightTo(value ? CollapsedHeight : UncollapsedHeight, TransitionDuration); } } - public LineBadge() + public LineBadge(bool startCollapsed = true, float collapsedHeight = 1, float uncollapsedHeight = 10) { Anchor = Anchor.BottomCentre; Origin = Anchor.Centre; + CollapsedHeight = collapsedHeight; + UncollapsedHeight = uncollapsedHeight; + Height = startCollapsed ? CollapsedHeight : UncollapsedHeight; // this margin prevents jumps when changing text's font weight Margin = new MarginPadding() diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index 24ce873a95..32910e26c6 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -14,10 +14,11 @@ namespace osu.Game.Overlays.Changelog.Header { protected SpriteText text; protected LineBadge lineBadge; + protected bool startCollapsed; public Action OnActivation; public Action OnDeactivation; - + public void SetTextColour(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) { text.FadeColour(newColour, duration, easing); @@ -77,7 +78,7 @@ namespace osu.Game.Overlays.Changelog.Header }, duration); } - public TextBadgePair(ColourInfo badgeColour, string displayText = "Listing") + public TextBadgePair(ColourInfo badgeColour, string displayText = "Listing", bool startBadgeCollapsed = true) { AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; @@ -97,7 +98,7 @@ namespace osu.Game.Overlays.Changelog.Header Right = 10, } }, - lineBadge = new LineBadge + lineBadge = new LineBadge(startCollapsed) { Colour = badgeColour, } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs index 4897aeedd1..41d9a2a8ef 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Changelog.Header { private ColourInfo badgeColour; - public TextBadgePairListing(ColourInfo badgeColour) : base(badgeColour, "Listing") + public TextBadgePairListing(ColourInfo badgeColour) : base(badgeColour, "Listing", false) { this.badgeColour = badgeColour; text.Font = "Exo2.0-Bold"; diff --git a/osu.Game/Overlays/Changelog/ReleaseStreamInfo.cs b/osu.Game/Overlays/Changelog/ReleaseStreamInfo.cs new file mode 100644 index 0000000000..90b2ab6cfb --- /dev/null +++ b/osu.Game/Overlays/Changelog/ReleaseStreamInfo.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using Newtonsoft.Json; +using osu.Game.Database; +using osu.Game.IO.Serialization; +using osu.Game.Rulesets; + +namespace osu.Game.Overlays.Changelog +{ + [Serializable] + public class ReleaseStreamInfo : IJsonSerializable + { + public string Name; + public string DisplayVersion; + + public float Users; + + public bool IsFeatured; + } +} diff --git a/osu.Game/Overlays/Changelog/Streams/StreamBadge.cs b/osu.Game/Overlays/Changelog/Streams/StreamBadge.cs new file mode 100644 index 0000000000..e61fa22007 --- /dev/null +++ b/osu.Game/Overlays/Changelog/Streams/StreamBadge.cs @@ -0,0 +1,128 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +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.Framework.Input; +using System; + +namespace osu.Game.Overlays.Changelog.Streams +{ + public class StreamBadge : Container + { + private const float badgeHeight = 56.5f; + private const float badgeWidth = 100; + private const float badgeTopBottomMargin = 5; + private const float transition_duration = 100; + + public Action OnActivation; + + private bool isActive; + + private Header.LineBadge lineBadge; + + public string Name { get; private set; } + public string DisplayVersion { get; private set; } + public bool IsFeatured { get; private set; } + public float Users { get; private set; } + + public StreamBadge(ColourInfo colour, string streamName, string streamBuild, float onlineUsers = 0, bool isFeatured = false) + { + Name = streamName; + DisplayVersion = streamBuild; + IsFeatured = isFeatured; + Users = onlineUsers; + Height = badgeHeight; + Width = isFeatured ? badgeWidth * 2 : badgeWidth; + Margin = new MarginPadding(5); + isActive = true; + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new[] + { + new SpriteText + { + Text = streamName, + Font = @"Exo2.0-Bold", + TextSize = 16, + Margin = new MarginPadding + { + Top = 5, + } + }, + new SpriteText + { + Text = streamBuild, + Font = @"Exo2.0-Light", + TextSize = 21, + }, + new SpriteText + { + Text = onlineUsers > 0 ? + string.Join(" ", onlineUsers.ToString("N0"), "users online"): + null, + TextSize = 12, + Font = @"Exo2.0-Regular", + Colour = new Color4(203, 164, 218, 255), + }, + } + }, + lineBadge = new Header.LineBadge(false, 2, 4) + { + Anchor = Anchor.TopCentre, + Width = 1, + Colour = colour, + RelativeSizeAxes = Axes.X, + }, + }; + } + + public void Activate(bool withoutHeaderUpdate = false) + { + isActive = true; + this.FadeIn(transition_duration); + lineBadge.IsCollapsed = false; + if (!withoutHeaderUpdate) OnActivation?.Invoke(); + } + + public void Deactivate() + { + isActive = false; + this.FadeTo(0.5f, transition_duration); + lineBadge.IsCollapsed = true; + } + + protected override bool OnClick(InputState state) + { + Activate(); + return base.OnClick(state); + } + + protected override bool OnHover(InputState state) + { + this.FadeIn(transition_duration); + lineBadge.IsCollapsed = false; + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + if (!isActive) + { + this.FadeTo(0.5f, transition_duration); + lineBadge.IsCollapsed = true; + } + base.OnHoverLost(state); + } + } +} diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 915d747f00..e47e290075 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Overlays.Changelog; +using osu.Game.Overlays.Changelog.Streams; namespace osu.Game.Overlays { @@ -17,7 +18,8 @@ namespace osu.Game.Overlays { private readonly ScrollContainer scroll; - public ChangelogHeader header; + public readonly ChangelogHeader header; + public readonly ChangelogStreams streams; protected Color4 purple = new Color4(191, 4, 255, 255); @@ -62,10 +64,26 @@ namespace osu.Game.Overlays Children = new Drawable[] { header = new ChangelogHeader(), + streams = new ChangelogStreams(), }, }, }, }; + streams.SelectedRelease.ValueChanged += r => + { + if (streams.SelectedRelease != null) + header.ShowReleaseStream(r.Name, string.Join(" ", r.Name, r.DisplayVersion)); + }; + streams.badgesContainer.OnLoadComplete += d => + { + header.OnListingActivated += () => + { + foreach (StreamBadge item in streams.badgesContainer.Children) + { + item.Activate(true); + } + }; + }; } // receive input outside our bounds so we can trigger a close event on ourselves. From 6baa761b9ce852fa5f313b57a7f77dcd49e66aae Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 18 Jul 2018 03:26:08 +0200 Subject: [PATCH 0011/2854] Streams2 --- osu.Game.Tests/Visual/TestCaseChangelog.cs | 3 +- .../Visual/TestCaseTextBadgePair.cs | 62 ------------------- .../Overlays/Changelog/ChangelogStreams.cs | 29 ++++++++- .../Changelog/Header/TextBadgePair.cs | 3 +- .../Overlays/Changelog/Streams/StreamBadge.cs | 5 +- osu.Game/Overlays/ChangelogOverlay.cs | 3 +- 6 files changed, 35 insertions(+), 70 deletions(-) delete mode 100644 osu.Game.Tests/Visual/TestCaseTextBadgePair.cs diff --git a/osu.Game.Tests/Visual/TestCaseChangelog.cs b/osu.Game.Tests/Visual/TestCaseChangelog.cs index c9b8a4d652..afd06d8415 100644 --- a/osu.Game.Tests/Visual/TestCaseChangelog.cs +++ b/osu.Game.Tests/Visual/TestCaseChangelog.cs @@ -1,7 +1,6 @@ -// Copyright(c) 2007-2018 ppy Pty Ltd. +// Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - using NUnit.Framework; using osu.Game.Overlays; diff --git a/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs b/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs deleted file mode 100644 index 9a4e481ff8..0000000000 --- a/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Overlays.Changelog.Header; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseTextBadgePair : OsuTestCase - { - private readonly Container container; - private readonly Box bottomLine; - private readonly TextBadgePair textBadgePair; - - public TestCaseTextBadgePair() - { - - Add(container = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new OpenTK.Vector2(300, 40), - Children = new Drawable[] - { - new Box - { - Colour = Color4.Gray, - RelativeSizeAxes = Axes.Both, - }, - bottomLine = new Box // purple line - { - Colour = Color4.Purple, - RelativeSizeAxes = Axes.X, - Height = 3, - Anchor = Anchor.BottomLeft, - Origin = Anchor.CentreLeft, - }, - textBadgePair = new TextBadgePair(Color4.White, "testing") - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - } - }, - }); - - AddStep(@"deactivate", () => textBadgePair.Deactivate()); - AddStep(@"activate", () => textBadgePair.Activate()); - AddStep(@"purple text", () => textBadgePair.SetTextColour(Color4.Purple, 100)); - AddStep(@"white text", () => textBadgePair.SetTextColour(Color4.White, 100)); - AddStep(@"purple badge", () => textBadgePair.SetBadgeColour(Color4.Purple, 100)); - AddStep(@"white badge", () => textBadgePair.SetBadgeColour(Color4.White, 100)); - AddStep(@"hide text", () => textBadgePair.HideText(250)); - AddStep(@"show text", () => textBadgePair.ShowText(250)); - AddStep(@"change text", () => textBadgePair.ChangeText(250)); - } - } -} diff --git a/osu.Game/Overlays/Changelog/ChangelogStreams.cs b/osu.Game/Overlays/Changelog/ChangelogStreams.cs index e7f7d44c80..99c9c0accd 100644 --- a/osu.Game/Overlays/Changelog/ChangelogStreams.cs +++ b/osu.Game/Overlays/Changelog/ChangelogStreams.cs @@ -7,6 +7,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; using osu.Game.Graphics; using osu.Game.Overlays.Changelog.Streams; using System; @@ -23,7 +24,7 @@ namespace osu.Game.Overlays.Changelog private readonly StreamColour streamColour; public readonly FillFlowContainer badgesContainer; - + public ChangelogStreams() { streamColour = new StreamColour(); @@ -77,5 +78,31 @@ namespace osu.Game.Overlays.Changelog } }; } + + protected override bool OnHover(InputState state) + { + // is this nullreference-safe for badgesContainer? + foreach (StreamBadge streamBadge in badgesContainer.Children) + { + if (SelectedRelease.Value != null) + { + if (SelectedRelease.Value.Name != streamBadge.Name) + { + streamBadge.Deactivate(); + } + } + else streamBadge.Deactivate(); + } + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + if (SelectedRelease.Value == null) + { + foreach (StreamBadge streamBadge in badgesContainer.Children) streamBadge.Activate(true); + } + base.OnHoverLost(state); + } } } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index 32910e26c6..42f7bd601a 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -9,7 +9,6 @@ using System; namespace osu.Game.Overlays.Changelog.Header { - public class TextBadgePair : ClickableContainer { protected SpriteText text; @@ -18,7 +17,7 @@ namespace osu.Game.Overlays.Changelog.Header public Action OnActivation; public Action OnDeactivation; - + public void SetTextColour(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) { text.FadeColour(newColour, duration, easing); diff --git a/osu.Game/Overlays/Changelog/Streams/StreamBadge.cs b/osu.Game/Overlays/Changelog/Streams/StreamBadge.cs index e61fa22007..9b70c9ce7f 100644 --- a/osu.Game/Overlays/Changelog/Streams/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/Streams/StreamBadge.cs @@ -13,7 +13,7 @@ using System; namespace osu.Game.Overlays.Changelog.Streams { - public class StreamBadge : Container + public class StreamBadge : ClickableContainer { private const float badgeHeight = 56.5f; private const float badgeWidth = 100; @@ -57,7 +57,7 @@ namespace osu.Game.Overlays.Changelog.Streams TextSize = 16, Margin = new MarginPadding { - Top = 5, + Top = 7, } }, new SpriteText @@ -80,6 +80,7 @@ namespace osu.Game.Overlays.Changelog.Streams lineBadge = new Header.LineBadge(false, 2, 4) { Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, Width = 1, Colour = colour, RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index e47e290075..9d4ec2b806 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -71,13 +71,14 @@ namespace osu.Game.Overlays }; streams.SelectedRelease.ValueChanged += r => { - if (streams.SelectedRelease != null) + if (streams.SelectedRelease.Value != null) header.ShowReleaseStream(r.Name, string.Join(" ", r.Name, r.DisplayVersion)); }; streams.badgesContainer.OnLoadComplete += d => { header.OnListingActivated += () => { + streams.SelectedRelease.Value = null; foreach (StreamBadge item in streams.badgesContainer.Children) { item.Activate(true); From 837747e35f53a152b8f01e192fee6d06a30db688 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 18 Jul 2018 15:17:20 +0200 Subject: [PATCH 0012/2854] Streams3 --- osu.Game.Tests/Visual/TestCaseChangelog.cs | 40 +++++++++++++++++-- .../Overlays/Changelog/ChangelogHeader.cs | 4 +- .../Changelog/Header/TextBadgePair.cs | 28 ++++--------- .../Changelog/Header/TextBadgePairListing.cs | 7 ++++ .../Changelog/Header/TextBadgePairRelease.cs | 1 - osu.Game/Overlays/ChangelogOverlay.cs | 4 +- 6 files changed, 56 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChangelog.cs b/osu.Game.Tests/Visual/TestCaseChangelog.cs index afd06d8415..c7b9f3dd9e 100644 --- a/osu.Game.Tests/Visual/TestCaseChangelog.cs +++ b/osu.Game.Tests/Visual/TestCaseChangelog.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using NUnit.Framework; +using osu.Framework.Graphics.Containers; using osu.Game.Overlays; namespace osu.Game.Tests.Visual @@ -12,6 +13,7 @@ namespace osu.Game.Tests.Visual private ChangelogOverlay changelog; private int releaseStreamCount; private int index; + private void indexIncrement() => index = (index == releaseStreamCount - 1) ? 0 : index + 1; protected override void LoadComplete() { @@ -22,16 +24,46 @@ namespace osu.Game.Tests.Visual releaseStreamCount = changelog.streams.badgesContainer.Children.Count; AddStep(@"Show", changelog.Show); - AddRepeatStep(@"Toggle Release Stream", () => { + AddRepeatStep(@"Toggle Release Stream", () => + { changelog.streams.badgesContainer.Children[index].Activate(); - index = (index == releaseStreamCount - 1) ? 0 : index + 1; + indexIncrement(); }, releaseStreamCount); - AddStep(@"Listing", changelog.header.ActivateListing); + AddStep(@"Listing", changelog.ActivateListing); + AddStep(@"Hide", changelog.Hide); + AddWaitStep(4); + AddStep(@"Show with Release Stream", () => + { + changelog.streams.badgesContainer.Children[index].Activate(); + changelog.Show(); + indexIncrement(); + }); + AddWaitStep(4); + AddStep(@"Hide", changelog.Hide); + AddWaitStep(4); + AddStep(@"Show with listing", () => + { + // .maybe changelog should have a function that does header.ActivateListing() + changelog.ActivateListing(); + changelog.Show(); + }); + AddWaitStep(4); + AddStep(@"Hide", changelog.Hide); + AddWaitStep(4); + AddStep(@"Activate release", () => + { + changelog.streams.badgesContainer.Children[index].Activate(); + indexIncrement(); + }); + AddStep(@"Show with listing", () => + { + changelog.ActivateListing(); + changelog.Show(); + }); } public TestCaseChangelog() { - } } } diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 183a63575b..43349fb668 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -133,7 +133,9 @@ namespace osu.Game.Overlays.Changelog { Top = 10, Left = 7, - Right = 9, + // + chevron size, and account for gained space on left by + // listing's font draw width being smaller + Right = 18, Bottom = 15, }, Children = new Drawable[] diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index 42f7bd601a..6482a69bb9 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -37,18 +37,10 @@ namespace osu.Game.Overlays.Changelog.Header public void ShowText(double duration = 0, string displayText = null, Easing easing = Easing.InOutCubic) { - if (!string.IsNullOrEmpty(displayText)) - { - text.Text = displayText; - } - + lineBadge.IsCollapsed = false; + if (!string.IsNullOrEmpty(displayText)) text.Text = displayText; text.MoveToY(0, duration, easing) - .FadeIn(duration, easing) - .Finally(d => { - // waiting until text is drawn to use its DrawWidth - UpdateBadgeWidth(); - lineBadge.IsCollapsed = false; - }); + .FadeIn(duration, easing); } /// @@ -61,19 +53,15 @@ namespace osu.Game.Overlays.Changelog.Header .FadeOut(duration, easing) .Then() .MoveToY(0, duration, easing) - .FadeIn(duration, easing) - .OnComplete(dd => { - UpdateBadgeWidth(); - lineBadge.IsCollapsed = false; - }); + .FadeIn(duration, easing); // since using .finally/.oncomplete after first fadeout made the badge // not hide sometimes in visual tests(because FinishTransforms()/CancelTransforms() // didn't apply to transforms that come after the .finally), I'm using a scheduler here Scheduler.AddDelayed(() => { - //lineBadge.ResizeWidthTo(0); // resizes when not visible if (!string.IsNullOrEmpty(displayText)) text.Text = displayText; + lineBadge.IsCollapsed = false; }, duration); } @@ -93,13 +81,13 @@ namespace osu.Game.Overlays.Changelog.Header { Top = 5, Bottom = 15, - Left = 10, - Right = 10, } }, lineBadge = new LineBadge(startCollapsed) { + Width = 1, Colour = badgeColour, + RelativeSizeAxes = Axes.X, } }; } @@ -115,7 +103,5 @@ namespace osu.Game.Overlays.Changelog.Header lineBadge.IsCollapsed = false; text.Font = "Exo2.0-Bold"; } - - public void UpdateBadgeWidth() => lineBadge.ResizeWidthTo(text.DrawWidth); } } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs index 41d9a2a8ef..0e12e1a2c5 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs @@ -19,6 +19,11 @@ namespace osu.Game.Overlays.Changelog.Header text.Anchor = Anchor.TopCentre; text.Origin = Anchor.TopCentre; + // I'm using this for constant badge width here, so that the whole + // thing doesn't jump left/right when listing's size changes + // due to different font weight (and thus width) + lineBadge.RelativeSizeAxes = Axes.None; + // this doesn't work without the scheduler // (because the text isn't yet fully drawn when it's loaded?) text.OnLoadComplete = d => Scheduler.Add(UpdateBadgeWidth); @@ -57,5 +62,7 @@ namespace osu.Game.Overlays.Changelog.Header if (lineBadge.IsCollapsed) lineBadge.ResizeHeightTo(1, lineBadge.TransitionDuration); base.OnHoverLost(state); } + + public void UpdateBadgeWidth() => lineBadge.ResizeWidthTo(text.DrawWidth); } } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs index f69c7dd6a7..d2fed56214 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs @@ -12,7 +12,6 @@ namespace osu.Game.Overlays.Changelog.Header public TextBadgePairRelease(ColourInfo badgeColour, string displayText) : base(badgeColour, displayText) { - this.listingBadge = listingBadge; text.Font = "Exo2.0-Bold"; text.Y = 20; text.Alpha = 0; diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 9d4ec2b806..20061317d2 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays { private readonly ScrollContainer scroll; - public readonly ChangelogHeader header; + private ChangelogHeader header; public readonly ChangelogStreams streams; protected Color4 purple = new Color4(191, 4, 255, 255); @@ -87,6 +87,8 @@ namespace osu.Game.Overlays }; } + public void ActivateListing() => header.ActivateListing(); + // receive input outside our bounds so we can trigger a close event on ourselves. public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; From 11e0732a2733401f511a5aefbc88c45fa0a03fc8 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 18 Jul 2018 19:32:15 +0200 Subject: [PATCH 0013/2854] Refactor1, UX1 --- osu.Game.Tests/Visual/TestCaseChangelog.cs | 24 +++---- osu.Game/Graphics/StreamColour.cs | 12 ++-- .../Overlays/Changelog/ChangelogHeader.cs | 48 ++++++------- .../Overlays/Changelog/ChangelogStreams.cs | 44 ++++++------ .../Overlays/Changelog/Header/LineBadge.cs | 10 +-- .../Changelog/Header/TextBadgePair.cs | 68 ++++++++++++------- .../Changelog/Header/TextBadgePairListing.cs | 30 ++++---- .../Changelog/Header/TextBadgePairRelease.cs | 25 ++++--- .../Overlays/Changelog/ReleaseStreamInfo.cs | 6 -- .../Overlays/Changelog/Streams/StreamBadge.cs | 51 ++++++++------ osu.Game/Overlays/ChangelogOverlay.cs | 47 +++++++++---- 11 files changed, 205 insertions(+), 160 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChangelog.cs b/osu.Game.Tests/Visual/TestCaseChangelog.cs index c7b9f3dd9e..da568e0eb3 100644 --- a/osu.Game.Tests/Visual/TestCaseChangelog.cs +++ b/osu.Game.Tests/Visual/TestCaseChangelog.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using NUnit.Framework; -using osu.Framework.Graphics.Containers; using osu.Game.Overlays; namespace osu.Game.Tests.Visual @@ -13,7 +12,7 @@ namespace osu.Game.Tests.Visual private ChangelogOverlay changelog; private int releaseStreamCount; private int index; - private void indexIncrement() => index = (index == releaseStreamCount - 1) ? 0 : index + 1; + private void indexIncrement() => index = index == releaseStreamCount - 1 ? 0 : index + 1; protected override void LoadComplete() { @@ -21,38 +20,37 @@ namespace osu.Game.Tests.Visual Add(changelog = new ChangelogOverlay()); - releaseStreamCount = changelog.streams.badgesContainer.Children.Count; + releaseStreamCount = changelog.Streams.BadgesContainer.Children.Count; AddStep(@"Show", changelog.Show); AddRepeatStep(@"Toggle Release Stream", () => { - changelog.streams.badgesContainer.Children[index].Activate(); + changelog.Streams.BadgesContainer.Children[index].Activate(); indexIncrement(); }, releaseStreamCount); AddStep(@"Listing", changelog.ActivateListing); AddStep(@"Hide", changelog.Hide); - AddWaitStep(4); + AddWaitStep(3); AddStep(@"Show with Release Stream", () => { - changelog.streams.badgesContainer.Children[index].Activate(); + changelog.Streams.BadgesContainer.Children[index].Activate(); changelog.Show(); indexIncrement(); }); - AddWaitStep(4); + AddWaitStep(3); AddStep(@"Hide", changelog.Hide); - AddWaitStep(4); + AddWaitStep(3); AddStep(@"Show with listing", () => { - // .maybe changelog should have a function that does header.ActivateListing() changelog.ActivateListing(); changelog.Show(); }); AddWaitStep(4); AddStep(@"Hide", changelog.Hide); - AddWaitStep(4); + AddWaitStep(3); AddStep(@"Activate release", () => { - changelog.streams.badgesContainer.Children[index].Activate(); + changelog.Streams.BadgesContainer.Children[index].Activate(); indexIncrement(); }); AddStep(@"Show with listing", () => @@ -61,9 +59,5 @@ namespace osu.Game.Tests.Visual changelog.Show(); }); } - - public TestCaseChangelog() - { - } } } diff --git a/osu.Game/Graphics/StreamColour.cs b/osu.Game/Graphics/StreamColour.cs index fbb272c526..0da0201c06 100644 --- a/osu.Game/Graphics/StreamColour.cs +++ b/osu.Game/Graphics/StreamColour.cs @@ -7,11 +7,11 @@ namespace osu.Game.Graphics { public class StreamColour { - public readonly Color4 Stable = new Color4(102, 204, 255, 255); - public readonly Color4 StableFallback = new Color4(34, 153, 187, 255); - public readonly Color4 Beta = new Color4(255, 221, 85, 255); - public readonly Color4 CuttingEdge = new Color4(238, 170, 0, 255); - public readonly Color4 Lazer = new Color4(237, 18, 33, 255); - public readonly Color4 Web = new Color4(136, 102, 238, 255); + public static readonly Color4 STABLE = new Color4(102, 204, 255, 255); + public static readonly Color4 STABLEFALLBACK = new Color4(34, 153, 187, 255); + public static readonly Color4 BETA = new Color4(255, 221, 85, 255); + public static readonly Color4 CUTTINGEDGE = new Color4(238, 170, 0, 255); + public static readonly Color4 LAZER = new Color4(237, 18, 33, 255); + public static readonly Color4 WEB = new Color4(136, 102, 238, 255); } } diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 43349fb668..ebf666b1d8 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -18,15 +18,12 @@ namespace osu.Game.Overlays.Changelog { public class ChangelogHeader : Container { - protected Color4 purple = new Color4(191, 4, 255, 255); + protected Color4 Purple = new Color4(191, 4, 255, 255); private readonly Sprite coverImage; private readonly Sprite headerBadge; //50x50, margin-right: 20 - private readonly FillFlowContainer headerTextContainer; - private readonly OsuSpriteText title, titleStream; - private readonly SpriteIcon chevron; + private readonly OsuSpriteText titleStream; private readonly TextBadgePairListing listing; private readonly TextBadgePairRelease releaseStream; - private readonly FillFlowContainer breadcrumbContainer; public Action OnListingActivated; @@ -38,6 +35,7 @@ namespace osu.Game.Overlays.Changelog public ChangelogHeader() { + SpriteIcon chevron; // AppVeyor told me this should be a local variable..? RelativeSizeAxes = Axes.X; Height = cover_height; Children = new Drawable[] @@ -45,7 +43,7 @@ namespace osu.Game.Overlays.Changelog coverImage = new Sprite { RelativeSizeAxes = Axes.Both, - Size = new OpenTK.Vector2(1), + Size = new Vector2(1), FillMode = FillMode.Fill, Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -62,16 +60,16 @@ namespace osu.Game.Overlays.Changelog { X = icon_margin, Masking = true, - BorderColour = purple, + BorderColour = Purple, BorderThickness = 3, MaskingSmoothness = 1, - Size = new OpenTK.Vector2(50), + Size = new Vector2(50), Children = new Drawable[] { headerBadge = new Sprite { RelativeSizeAxes = Axes.Both, - Size = new OpenTK.Vector2(0.8f), + Size = new Vector2(0.8f), Anchor = Anchor.Centre, Origin = Anchor.Centre, }, @@ -82,14 +80,14 @@ namespace osu.Game.Overlays.Changelog new Box { RelativeSizeAxes = Axes.Both, - Size = new OpenTK.Vector2(1), + Size = new Vector2(1), Alpha = 0, AlwaysPresent = true, - Colour = purple, + Colour = Purple, } } }, - headerTextContainer = new FillFlowContainer + new FillFlowContainer { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, @@ -98,7 +96,7 @@ namespace osu.Game.Overlays.Changelog X = icon_size + icon_margin * 2, Children = new Drawable[] { - title = new OsuSpriteText + new OsuSpriteText { Text = "Changelog ", Font = @"Exo2.0-Light", @@ -109,13 +107,13 @@ namespace osu.Game.Overlays.Changelog Text = "Listing", TextSize = 38, // web: 30 Font = @"Exo2.0-Light", - Colour = purple, + Colour = Purple, }, } } } }, - breadcrumbContainer = new FillFlowContainer // Listing > Lazer 2018.713.1 + new FillFlowContainer // Listing > Lazer 2018.713.1 { X = 2 * icon_margin + icon_size - 8, Height = version_height, @@ -124,12 +122,12 @@ namespace osu.Game.Overlays.Changelog Direction = FillDirection.Horizontal, Children = new Drawable[] { - listing = new TextBadgePairListing(purple), - new Container() // without a container, moving the chevron wont work + listing = new TextBadgePairListing(Purple), + new Container // without a container, moving the chevron wont work { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Margin = new MarginPadding() + Margin = new MarginPadding { Top = 10, Left = 7, @@ -145,19 +143,19 @@ namespace osu.Game.Overlays.Changelog Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(7), - Colour = purple, + Colour = Purple, Icon = FontAwesome.fa_chevron_right, Alpha = 0, X = -200, }, }, }, - releaseStream = new TextBadgePairRelease(purple, "Lazer") + releaseStream = new TextBadgePairRelease(Purple, "Lazer") }, }, new Box // purple line { - Colour = purple, + Colour = Purple, RelativeSizeAxes = Axes.X, Height = 3, Anchor = Anchor.BottomLeft, @@ -177,7 +175,7 @@ namespace osu.Game.Overlays.Changelog { releaseStream.Deactivate(); chevron.MoveToX(-20, 100).FadeOut(100); - ChangeHeaderText("Listing"); + changeHeaderText("Listing"); OnListingActivated?.Invoke(); }; }; @@ -186,10 +184,10 @@ namespace osu.Game.Overlays.Changelog public void ShowReleaseStream(string headerText, string breadcrumbText) { releaseStream.Activate(breadcrumbText); - ChangeHeaderText(headerText); + changeHeaderText(headerText); } - private void ChangeHeaderText(string headerText) + private void changeHeaderText(string headerText) { titleStream.Text = headerText; titleStream.FlashColour(Color4.White, 500, Easing.OutQuad); @@ -197,6 +195,8 @@ namespace osu.Game.Overlays.Changelog public void ActivateListing() => listing.Activate(); + public bool IsListingActivated() => listing.IsActivated; + [BackgroundDependencyLoader] private void load(TextureStore textures) { diff --git a/osu.Game/Overlays/Changelog/ChangelogStreams.cs b/osu.Game/Overlays/Changelog/ChangelogStreams.cs index 99c9c0accd..f0848188c8 100644 --- a/osu.Game/Overlays/Changelog/ChangelogStreams.cs +++ b/osu.Game/Overlays/Changelog/ChangelogStreams.cs @@ -3,32 +3,28 @@ using OpenTK.Graphics; using osu.Framework.Configuration; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Game.Graphics; using osu.Game.Overlays.Changelog.Streams; -using System; namespace osu.Game.Overlays.Changelog { public class ChangelogStreams : Container { - private const float containerHeight = 106.5f; - private const float containerTopBottomMargin = 20; - private const float containerSideMargin = 85; + private const float container_height = 106.5f; + private const float container_margin_y = 20; + private const float container_margin_x = 85; public Bindable SelectedRelease = new Bindable(); - private readonly StreamColour streamColour; - public readonly FillFlowContainer badgesContainer; + public readonly FillFlowContainer BadgesContainer; public ChangelogStreams() { - streamColour = new StreamColour(); - Height = containerHeight; + Height = container_height; RelativeSizeAxes = Axes.X; Children = new Drawable[] { @@ -38,39 +34,39 @@ namespace osu.Game.Overlays.Changelog Size = new OpenTK.Vector2(1), Colour = new Color4(32, 24, 35, 255), }, - badgesContainer = new FillFlowContainer + BadgesContainer = new FillFlowContainer { Direction = FillDirection.Horizontal, RelativeSizeAxes = Axes.Both, - Margin = new MarginPadding() + Margin = new MarginPadding { - Top = containerTopBottomMargin, - Bottom = containerTopBottomMargin, - Left = containerSideMargin, - Right = containerSideMargin, + Top = container_margin_y, + Bottom = container_margin_y, + Left = container_margin_x, + Right = container_margin_x, }, Children = new[] { - new StreamBadge(streamColour.Stable, "Stable", "20180626.1", 16370, true), - new StreamBadge(streamColour.Beta, "Beta", "20180626", 186), - new StreamBadge(streamColour.Lazer, "Lazer", "2018.713.1"), + new StreamBadge(StreamColour.STABLE, "Stable", "20180626.1", 16370, true), + new StreamBadge(StreamColour.BETA, "Beta", "20180626", 186), + new StreamBadge(StreamColour.LAZER, "Lazer", "2018.713.1"), }, }, }; - badgesContainer.OnLoadComplete = d => + BadgesContainer.OnLoadComplete = d => { - foreach (StreamBadge streamBadge in badgesContainer.Children) + foreach (StreamBadge streamBadge in BadgesContainer.Children) { streamBadge.OnActivation = () => { - SelectedRelease.Value = new ReleaseStreamInfo() + SelectedRelease.Value = new ReleaseStreamInfo { DisplayVersion = streamBadge.DisplayVersion, IsFeatured = streamBadge.IsFeatured, Name = streamBadge.Name, Users = streamBadge.Users, }; - foreach (StreamBadge item in badgesContainer.Children) + foreach (StreamBadge item in BadgesContainer.Children) { if (item.Name != streamBadge.Name) item.Deactivate(); } @@ -82,7 +78,7 @@ namespace osu.Game.Overlays.Changelog protected override bool OnHover(InputState state) { // is this nullreference-safe for badgesContainer? - foreach (StreamBadge streamBadge in badgesContainer.Children) + foreach (StreamBadge streamBadge in BadgesContainer.Children) { if (SelectedRelease.Value != null) { @@ -100,7 +96,7 @@ namespace osu.Game.Overlays.Changelog { if (SelectedRelease.Value == null) { - foreach (StreamBadge streamBadge in badgesContainer.Children) streamBadge.Activate(true); + foreach (StreamBadge streamBadge in BadgesContainer.Children) streamBadge.Activate(true); } base.OnHoverLost(state); } diff --git a/osu.Game/Overlays/Changelog/Header/LineBadge.cs b/osu.Game/Overlays/Changelog/Header/LineBadge.cs index c33244d50b..93eca528c5 100644 --- a/osu.Game/Overlays/Changelog/Header/LineBadge.cs +++ b/osu.Game/Overlays/Changelog/Header/LineBadge.cs @@ -8,18 +8,20 @@ namespace osu.Game.Overlays.Changelog.Header { public class LineBadge : Circle { - public float TransitionDuration = 100; + public float TransitionDuration = 400; public float UncollapsedHeight; public float CollapsedHeight; - protected bool isCollapsed; + private bool isCollapsed; public bool IsCollapsed { get { return isCollapsed; } set { isCollapsed = value; - this.ResizeHeightTo(value ? CollapsedHeight : UncollapsedHeight, TransitionDuration); + this.ResizeHeightTo(value ? CollapsedHeight : UncollapsedHeight, + value ? TransitionDuration / 2f : TransitionDuration, + value ? Easing.Out : Easing.OutElastic); } } @@ -32,7 +34,7 @@ namespace osu.Game.Overlays.Changelog.Header Height = startCollapsed ? CollapsedHeight : UncollapsedHeight; // this margin prevents jumps when changing text's font weight - Margin = new MarginPadding() + Margin = new MarginPadding { Left = 10, Right = 10, diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index 6482a69bb9..a888c436cf 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -1,45 +1,51 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; using System; namespace osu.Game.Overlays.Changelog.Header { - public class TextBadgePair : ClickableContainer + public class TextBadgePair : Container { - protected SpriteText text; - protected LineBadge lineBadge; - protected bool startCollapsed; + protected SpriteText Text; + protected LineBadge LineBadge; + public bool IsActivated { get; protected set; } public Action OnActivation; public Action OnDeactivation; + private SampleChannel sampleHover; + protected SampleChannel SampleActivate; public void SetTextColour(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) { - text.FadeColour(newColour, duration, easing); + Text.FadeColour(newColour, duration, easing); } public void SetBadgeColour(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) { - lineBadge.FadeColour(newColour, duration, easing); + LineBadge.FadeColour(newColour, duration, easing); } public void HideText(double duration = 0, Easing easing = Easing.InOutCubic) { - lineBadge.IsCollapsed = true; - text.MoveToY(20, duration, easing) + LineBadge.IsCollapsed = true; + Text.MoveToY(20, duration, easing) .FadeOut(duration, easing); } public void ShowText(double duration = 0, string displayText = null, Easing easing = Easing.InOutCubic) { - lineBadge.IsCollapsed = false; - if (!string.IsNullOrEmpty(displayText)) text.Text = displayText; - text.MoveToY(0, duration, easing) + LineBadge.IsCollapsed = false; + if (!string.IsNullOrEmpty(displayText)) Text.Text = displayText; + Text.MoveToY(0, duration, easing) .FadeIn(duration, easing); } @@ -48,8 +54,8 @@ namespace osu.Game.Overlays.Changelog.Header /// Full change takes double this time. public void ChangeText(double duration = 0, string displayText = null, Easing easing = Easing.InOutCubic) { - lineBadge.IsCollapsed = true; - text.MoveToY(20, duration, easing) + LineBadge.IsCollapsed = true; + Text.MoveToY(20, duration, easing) .FadeOut(duration, easing) .Then() .MoveToY(0, duration, easing) @@ -60,30 +66,30 @@ namespace osu.Game.Overlays.Changelog.Header // didn't apply to transforms that come after the .finally), I'm using a scheduler here Scheduler.AddDelayed(() => { - if (!string.IsNullOrEmpty(displayText)) text.Text = displayText; - lineBadge.IsCollapsed = false; + if (!string.IsNullOrEmpty(displayText)) Text.Text = displayText; + LineBadge.IsCollapsed = false; }, duration); } - public TextBadgePair(ColourInfo badgeColour, string displayText = "Listing", bool startBadgeCollapsed = true) + public TextBadgePair(ColourInfo badgeColour, string displayText = "Listing", bool startCollapsed = true) { AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; Children = new Drawable[] { - text = new SpriteText + Text = new SpriteText { TextSize = 21, // web is 16, but here it looks too small? Text = displayText, Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, - Margin = new MarginPadding() + Margin = new MarginPadding { Top = 5, Bottom = 15, } }, - lineBadge = new LineBadge(startCollapsed) + LineBadge = new LineBadge(startCollapsed) { Width = 1, Colour = badgeColour, @@ -94,14 +100,30 @@ namespace osu.Game.Overlays.Changelog.Header public virtual void Deactivate() { - lineBadge.IsCollapsed = true; - text.Font = "Exo2.0-Regular"; + IsActivated = false; + LineBadge.IsCollapsed = true; + Text.Font = "Exo2.0-Regular"; } public virtual void Activate() { - lineBadge.IsCollapsed = false; - text.Font = "Exo2.0-Bold"; + IsActivated = true; + LineBadge.IsCollapsed = false; + Text.Font = "Exo2.0-Bold"; + SampleActivate?.Play(); + } + + protected override bool OnHover(InputState state) + { + if (!IsActivated) sampleHover?.Play(); + return base.OnHover(state); + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); + SampleActivate = audio.Sample.Get(@"UI/generic-select-soft"); } } } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs index 0e12e1a2c5..eaf7e1289b 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs @@ -10,37 +10,41 @@ namespace osu.Game.Overlays.Changelog.Header { public class TextBadgePairListing : TextBadgePair { - private ColourInfo badgeColour; + private readonly ColourInfo badgeColour; public TextBadgePairListing(ColourInfo badgeColour) : base(badgeColour, "Listing", false) { + IsActivated = true; this.badgeColour = badgeColour; - text.Font = "Exo2.0-Bold"; - text.Anchor = Anchor.TopCentre; - text.Origin = Anchor.TopCentre; + Text.Font = "Exo2.0-Bold"; + Text.Anchor = Anchor.TopCentre; + Text.Origin = Anchor.TopCentre; // I'm using this for constant badge width here, so that the whole // thing doesn't jump left/right when listing's size changes // due to different font weight (and thus width) - lineBadge.RelativeSizeAxes = Axes.None; + LineBadge.RelativeSizeAxes = Axes.None; // this doesn't work without the scheduler // (because the text isn't yet fully drawn when it's loaded?) - text.OnLoadComplete = d => Scheduler.Add(UpdateBadgeWidth); + Text.OnLoadComplete = d => Scheduler.Add(UpdateBadgeWidth); } public override void Activate() { - lineBadge.IsCollapsed = false; - text.Font = "Exo2.0-Bold"; + IsActivated = true; + LineBadge.IsCollapsed = false; + Text.Font = "Exo2.0-Bold"; SetTextColour(Color4.White, 100); + SampleActivate?.Play(); OnActivation?.Invoke(); } public override void Deactivate() { - lineBadge.IsCollapsed = true; - text.Font = "Exo2.0-Regular"; // commented out since it makes bad resize-jumping + IsActivated = false; + LineBadge.IsCollapsed = true; + Text.Font = "Exo2.0-Regular"; // commented out since it makes bad resize-jumping SetTextColour(badgeColour, 100); OnDeactivation?.Invoke(); } @@ -53,16 +57,16 @@ namespace osu.Game.Overlays.Changelog.Header protected override bool OnHover(InputState state) { - lineBadge.ResizeHeightTo(lineBadge.UncollapsedHeight, lineBadge.TransitionDuration); + LineBadge.ResizeHeightTo(LineBadge.UncollapsedHeight, LineBadge.TransitionDuration, Easing.OutElastic); return base.OnHover(state); } protected override void OnHoverLost(InputState state) { - if (lineBadge.IsCollapsed) lineBadge.ResizeHeightTo(1, lineBadge.TransitionDuration); + if (IsActivated == false) LineBadge.ResizeHeightTo(1, LineBadge.TransitionDuration, Easing.Out); base.OnHoverLost(state); } - public void UpdateBadgeWidth() => lineBadge.ResizeWidthTo(text.DrawWidth); + public void UpdateBadgeWidth() => LineBadge.ResizeWidthTo(Text.DrawWidth); } } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs index d2fed56214..2186043035 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs @@ -12,29 +12,34 @@ namespace osu.Game.Overlays.Changelog.Header public TextBadgePairRelease(ColourInfo badgeColour, string displayText) : base(badgeColour, displayText) { - text.Font = "Exo2.0-Bold"; - text.Y = 20; - text.Alpha = 0; + Text.Font = "Exo2.0-Bold"; + Text.Y = 20; + Text.Alpha = 0; } public void SetText(string displayText) { - text.Text = displayText; + Text.Text = displayText; } public void Activate(string displayText = null) { - //ClearTransforms(); - // not using if (!lineBadge.IsCollapsed) because the text sometimes gets reset - // when quickly switching release streams - if (text.IsPresent) ChangeText(transition_duration, displayText); - else ShowText(transition_duration, displayText); + if (IsActivated) + { + if (displayText != Text.Text) ChangeText(transition_duration, displayText); + } + else + { + ShowText(transition_duration, displayText); + IsActivated = true; + } + SampleActivate?.Play(); OnActivation?.Invoke(); } public override void Deactivate() { - //FinishTransforms(true); + IsActivated = false; HideText(transition_duration); OnDeactivation?.Invoke(); } diff --git a/osu.Game/Overlays/Changelog/ReleaseStreamInfo.cs b/osu.Game/Overlays/Changelog/ReleaseStreamInfo.cs index 90b2ab6cfb..2934ae8f94 100644 --- a/osu.Game/Overlays/Changelog/ReleaseStreamInfo.cs +++ b/osu.Game/Overlays/Changelog/ReleaseStreamInfo.cs @@ -2,13 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using Newtonsoft.Json; -using osu.Game.Database; using osu.Game.IO.Serialization; -using osu.Game.Rulesets; namespace osu.Game.Overlays.Changelog { diff --git a/osu.Game/Overlays/Changelog/Streams/StreamBadge.cs b/osu.Game/Overlays/Changelog/Streams/StreamBadge.cs index 9b70c9ce7f..8bd10dcbb8 100644 --- a/osu.Game/Overlays/Changelog/Streams/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/Streams/StreamBadge.cs @@ -1,12 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; 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.Framework.Input; using System; @@ -15,21 +16,20 @@ namespace osu.Game.Overlays.Changelog.Streams { public class StreamBadge : ClickableContainer { - private const float badgeHeight = 56.5f; - private const float badgeWidth = 100; - private const float badgeTopBottomMargin = 5; + private const float badge_height = 56.5f; + private const float badge_width = 100; private const float transition_duration = 100; public Action OnActivation; - private bool isActive; + private bool isActivated; - private Header.LineBadge lineBadge; - - public string Name { get; private set; } - public string DisplayVersion { get; private set; } - public bool IsFeatured { get; private set; } - public float Users { get; private set; } + private readonly Header.LineBadge lineBadge; + private SampleChannel sampleHover; + public readonly string Name; + public readonly string DisplayVersion; + public readonly bool IsFeatured; + public readonly float Users; public StreamBadge(ColourInfo colour, string streamName, string streamBuild, float onlineUsers = 0, bool isFeatured = false) { @@ -37,10 +37,10 @@ namespace osu.Game.Overlays.Changelog.Streams DisplayVersion = streamBuild; IsFeatured = isFeatured; Users = onlineUsers; - Height = badgeHeight; - Width = isFeatured ? badgeWidth * 2 : badgeWidth; + Height = badge_height; + Width = isFeatured ? badge_width * 2 : badge_width; Margin = new MarginPadding(5); - isActive = true; + isActivated = true; Children = new Drawable[] { new FillFlowContainer @@ -84,13 +84,14 @@ namespace osu.Game.Overlays.Changelog.Streams Width = 1, Colour = colour, RelativeSizeAxes = Axes.X, + TransitionDuration = 600, }, }; } public void Activate(bool withoutHeaderUpdate = false) { - isActive = true; + isActivated = true; this.FadeIn(transition_duration); lineBadge.IsCollapsed = false; if (!withoutHeaderUpdate) OnActivation?.Invoke(); @@ -98,9 +99,12 @@ namespace osu.Game.Overlays.Changelog.Streams public void Deactivate() { - isActive = false; - this.FadeTo(0.5f, transition_duration); - lineBadge.IsCollapsed = true; + isActivated = false; + if (!IsHovered) + { + this.FadeTo(0.5f, transition_duration); + lineBadge.IsCollapsed = true; + } } protected override bool OnClick(InputState state) @@ -111,6 +115,7 @@ namespace osu.Game.Overlays.Changelog.Streams protected override bool OnHover(InputState state) { + if (!isActivated) sampleHover?.Play(); this.FadeIn(transition_duration); lineBadge.IsCollapsed = false; return base.OnHover(state); @@ -118,12 +123,18 @@ namespace osu.Game.Overlays.Changelog.Streams protected override void OnHoverLost(InputState state) { - if (!isActive) + if (!isActivated) { this.FadeTo(0.5f, transition_duration); lineBadge.IsCollapsed = true; } base.OnHoverLost(state); } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); + } } } diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 20061317d2..f928f9ee58 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Input.Bindings; using osu.Game.Overlays.Changelog; using osu.Game.Overlays.Changelog.Streams; @@ -16,12 +17,10 @@ namespace osu.Game.Overlays { public class ChangelogOverlay : WaveOverlayContainer { - private readonly ScrollContainer scroll; + private readonly ChangelogHeader header; + public readonly ChangelogStreams Streams; - private ChangelogHeader header; - public readonly ChangelogStreams streams; - - protected Color4 purple = new Color4(191, 4, 255, 255); + protected readonly Color4 Purple = new Color4(191, 4, 255, 255); public ChangelogOverlay() { @@ -52,7 +51,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = new Color4(20, 18, 23, 255) }, - scroll = new ScrollContainer + new ScrollContainer { RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, @@ -64,25 +63,25 @@ namespace osu.Game.Overlays Children = new Drawable[] { header = new ChangelogHeader(), - streams = new ChangelogStreams(), + Streams = new ChangelogStreams(), }, }, }, }; - streams.SelectedRelease.ValueChanged += r => + Streams.SelectedRelease.ValueChanged += r => { - if (streams.SelectedRelease.Value != null) + if (Streams.SelectedRelease.Value != null) header.ShowReleaseStream(r.Name, string.Join(" ", r.Name, r.DisplayVersion)); }; - streams.badgesContainer.OnLoadComplete += d => + Streams.BadgesContainer.OnLoadComplete += d => { header.OnListingActivated += () => { - streams.SelectedRelease.Value = null; - foreach (StreamBadge item in streams.badgesContainer.Children) - { - item.Activate(true); - } + Streams.SelectedRelease.Value = null; + if (!Streams.IsHovered) + foreach (StreamBadge item in Streams.BadgesContainer.Children) item.Activate(true); + else + foreach (StreamBadge item in Streams.BadgesContainer.Children) item.Deactivate(); }; }; } @@ -92,6 +91,24 @@ namespace osu.Game.Overlays // receive input outside our bounds so we can trigger a close event on ourselves. public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + public override bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.Back: + if (header.IsListingActivated()) State = Visibility.Hidden; + + // the problem here is that when hovering over the builds' container + // and pressing back, they don't lower their opacity they're rehovered on + else header.ActivateListing(); + return true; + case GlobalAction.Select: + return true; + } + + return false; + } + protected override void PopIn() { base.PopIn(); From bcd132e87f99bacbe1d91f7e7e69abfe83371ddc Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Thu, 19 Jul 2018 19:07:24 +0200 Subject: [PATCH 0014/2854] API1 --- osu.Game.Tests/Visual/TestCaseChangelog.cs | 16 ++- osu.Game/Graphics/StreamColour.cs | 19 +++ .../UserInterface/TooltipIconButton.cs | 43 +++++++ .../GetChangelogLatestBuildsRequest.cs | 17 +++ .../API/Requests/GetChangelogRequest.cs | 13 +++ .../API/Requests/Responses/APIChangelog.cs | 54 +++++++++ osu.Game/Overlays/Changelog/ChangelogChart.cs | 36 ++++++ .../Overlays/Changelog/ChangelogContent.cs | 18 +++ .../Changelog/ChangelogContentGroup.cs | 109 ++++++++++++++++++ .../Overlays/Changelog/ChangelogHeader.cs | 10 +- .../Overlays/Changelog/ChangelogStreams.cs | 36 +++--- .../Overlays/Changelog/ReleaseStreamInfo.cs | 19 --- .../Changelog/{Streams => }/StreamBadge.cs | 29 ++--- osu.Game/Overlays/ChangelogOverlay.cs | 62 ++++++++-- 14 files changed, 399 insertions(+), 82 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/TooltipIconButton.cs create mode 100644 osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs create mode 100644 osu.Game/Online/API/Requests/GetChangelogRequest.cs create mode 100644 osu.Game/Online/API/Requests/Responses/APIChangelog.cs create mode 100644 osu.Game/Overlays/Changelog/ChangelogChart.cs create mode 100644 osu.Game/Overlays/Changelog/ChangelogContent.cs create mode 100644 osu.Game/Overlays/Changelog/ChangelogContentGroup.cs delete mode 100644 osu.Game/Overlays/Changelog/ReleaseStreamInfo.cs rename osu.Game/Overlays/Changelog/{Streams => }/StreamBadge.cs (81%) diff --git a/osu.Game.Tests/Visual/TestCaseChangelog.cs b/osu.Game.Tests/Visual/TestCaseChangelog.cs index da568e0eb3..894b117118 100644 --- a/osu.Game.Tests/Visual/TestCaseChangelog.cs +++ b/osu.Game.Tests/Visual/TestCaseChangelog.cs @@ -10,9 +10,9 @@ namespace osu.Game.Tests.Visual public class TestCaseChangelog : OsuTestCase { private ChangelogOverlay changelog; - private int releaseStreamCount; private int index; - private void indexIncrement() => index = index == releaseStreamCount - 1 ? 0 : index + 1; + private void indexIncrement() => index = index >= changelog.Streams.BadgesContainer.Children.Count - 1 ? 0 : index + 1; + private bool isLoaded => changelog.Streams.BadgesContainer.Children.Count > 0; protected override void LoadComplete() { @@ -20,20 +20,18 @@ namespace osu.Game.Tests.Visual Add(changelog = new ChangelogOverlay()); - releaseStreamCount = changelog.Streams.BadgesContainer.Children.Count; - AddStep(@"Show", changelog.Show); AddRepeatStep(@"Toggle Release Stream", () => { - changelog.Streams.BadgesContainer.Children[index].Activate(); + if (isLoaded) changelog.Streams.BadgesContainer.Children[index].Activate(); indexIncrement(); - }, releaseStreamCount); + }, 6); AddStep(@"Listing", changelog.ActivateListing); AddStep(@"Hide", changelog.Hide); AddWaitStep(3); AddStep(@"Show with Release Stream", () => { - changelog.Streams.BadgesContainer.Children[index].Activate(); + if (isLoaded) changelog.Streams.BadgesContainer.Children[index].Activate(); changelog.Show(); indexIncrement(); }); @@ -45,12 +43,12 @@ namespace osu.Game.Tests.Visual changelog.ActivateListing(); changelog.Show(); }); - AddWaitStep(4); + AddWaitStep(3); AddStep(@"Hide", changelog.Hide); AddWaitStep(3); AddStep(@"Activate release", () => { - changelog.Streams.BadgesContainer.Children[index].Activate(); + if (isLoaded) changelog.Streams.BadgesContainer.Children[index].Activate(); indexIncrement(); }); AddStep(@"Show with listing", () => diff --git a/osu.Game/Graphics/StreamColour.cs b/osu.Game/Graphics/StreamColour.cs index 0da0201c06..2a0d7fceab 100644 --- a/osu.Game/Graphics/StreamColour.cs +++ b/osu.Game/Graphics/StreamColour.cs @@ -2,6 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using OpenTK.Graphics; +using osu.Framework.Graphics.Colour; +using System.Collections.Generic; namespace osu.Game.Graphics { @@ -13,5 +15,22 @@ namespace osu.Game.Graphics public static readonly Color4 CUTTINGEDGE = new Color4(238, 170, 0, 255); public static readonly Color4 LAZER = new Color4(237, 18, 33, 255); public static readonly Color4 WEB = new Color4(136, 102, 238, 255); + + private static readonly Dictionary colours = new Dictionary + { + { "stable40", STABLE }, + { "stable", STABLEFALLBACK }, + { "beta40", BETA }, + { "cuttingedge", CUTTINGEDGE }, + { "lazer", LAZER }, + { "web", WEB }, + }; + + public static ColourInfo FromStreamName(string name) + { + if (colours.TryGetValue(name, out ColourInfo colour)) + return colour; + else return new Color4(255, 255, 255, 255); + } } } diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs new file mode 100644 index 0000000000..918e203bf4 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -0,0 +1,43 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Graphics.UserInterface +{ + public class TooltipIconButton : OsuClickableContainer, IHasTooltip + { + private readonly SpriteIcon icon; + + public FontAwesome Icon + { + get { return icon.Icon; } + set { icon.Icon = value; } + } + + public TooltipIconButton() + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + }, + icon = new SpriteIcon + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(18), + } + }; + } + + + public string TooltipText { get; set; } + } +} diff --git a/osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs b/osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs new file mode 100644 index 0000000000..845760f2e4 --- /dev/null +++ b/osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Online.API.Requests.Responses; +using System.Collections.Generic; + +namespace osu.Game.Online.API.Requests +{ + /// + /// Obviously a placeholder + /// + public class GetChangelogLatestBuildsRequest : APIRequest> + { + protected override string Uri => Target; + protected override string Target => @"https://api.myjson.com/bins/16waui"; + } +} diff --git a/osu.Game/Online/API/Requests/GetChangelogRequest.cs b/osu.Game/Online/API/Requests/GetChangelogRequest.cs new file mode 100644 index 0000000000..652e638ab8 --- /dev/null +++ b/osu.Game/Online/API/Requests/GetChangelogRequest.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Online.API.Requests.Responses; + +namespace osu.Game.Online.API.Requests +{ + public class GetChangelogRequest : APIRequest + { + protected override string Uri => Target; + protected override string Target => "https://api.myjson.com/bins/6zv2i"; + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelog.cs b/osu.Game/Online/API/Requests/Responses/APIChangelog.cs new file mode 100644 index 0000000000..f0dcafdc04 --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APIChangelog.cs @@ -0,0 +1,54 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Newtonsoft.Json; +using System; +using System.Collections.Generic; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APIChangelog + { + [JsonProperty("id")] + public long Id { get; set; } + + [JsonProperty("version")] + public string Version { get; set; } + + [JsonProperty("display_version")] + public string DisplayVersion { get; set; } + + [JsonProperty("users")] + public long Users { get; set; } + + [JsonProperty("is_featured")] + public bool IsFeatured { get; set; } + + [JsonProperty("created_at")] + public DateTimeOffset CreatedAt { get; set; } + + [JsonProperty("disqus_id")] + public string DisqusId { get; set; } + + [JsonProperty("disqus_title")] + public string DisqusTitle { get; set; } + + [JsonProperty("update_stream")] + public UpdateStream UpdateStream { get; set; } + + [JsonProperty("changelog_entries")] + public List ChangelogEntries { get; set; } + } + + public class UpdateStream + { + [JsonProperty("id")] + public long Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("display_name")] + public string DisplayName { get; set; } + } +} diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs new file mode 100644 index 0000000000..e885867c74 --- /dev/null +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -0,0 +1,36 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Changelog +{ + // maybe look to osu.Game.Screens.Play.SquareGraph for reference later + public class ChangelogChart : BufferedContainer + { + public ChangelogChart() + { + RelativeSizeAxes = Axes.X; + Height = 100; + Children = new Drawable[] + { + new Box + { + Colour = StreamColour.STABLE, + RelativeSizeAxes = Axes.Both, + }, + new SpriteText + { + Text = "Graph Placeholder", + TextSize = 28, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs new file mode 100644 index 0000000000..d10fe19942 --- /dev/null +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Overlays.Changelog +{ + public class ChangelogContent : FillFlowContainer + { + public ChangelogContent() + { + RelativeSizeAxes = Axes.X; + //AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + } + } +} diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs new file mode 100644 index 0000000000..951cebb941 --- /dev/null +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -0,0 +1,109 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests.Responses; + +namespace osu.Game.Overlays.Changelog +{ + public class ChangelogContentGroup : FillFlowContainer + { + // will porobably depend in some way on #1692 (https://github.com/ppy/osu-framework/pull/1692) + // need to keep in mind it looks different on Listing (one contains all builds from a date) + // and when a stream is selected (looks like now) + public ChangelogContentGroup(APIChangelog build) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + Padding = new MarginPadding + { + Left = 70, + Right = 70, + }; + Children = new Drawable[] + { + // build version, arrows + new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding + { + Top = 20, + }, + Children = new Drawable[] + { + new TooltipIconButton + { + Icon = FontAwesome.fa_chevron_left, + Size = new Vector2(24), + // how do we link to previous/next builds? + // I'm thinking some linked list, but how do we make that + // from the available API data + TooltipText = "Previous", + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding + { + Left = 40, + Right = 40, + }, + Children = new[] + { + new SpriteText + { + Text = build.UpdateStream.DisplayName, + TextSize = 28, // web: 24, + Font = @"Exo2.0-Medium", + }, + new SpriteText // a space... + { + Text = " ", + TextSize = 28, + }, + new SpriteText + { + Text = build.DisplayVersion, + TextSize = 28, // web: 24, + Colour = StreamColour.STABLE, + }, + } + }, + new TooltipIconButton + { + Icon = FontAwesome.fa_chevron_right, + Size = new Vector2(24), + TooltipText = "Next", + }, + } + }, + new SpriteText + { + // do we need .ToUniversalTime() here? + // also, this is a temporary solution to weekdays in >localized< date strings + Text = build.CreatedAt.Date.ToLongDateString().Replace(build.CreatedAt.ToString("dddd") + ", ", ""), + TextSize = 17, // web: 14, + Colour = OsuColour.FromHex(@"FD5"), + Font = @"Exo2.0-Medium", + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding + { + Top = 5, + }, + }, + }; + } + //public ChangelogContentGroup(DateTimeOffset date) { } + } +} diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index ebf666b1d8..8332aeeaa4 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Changelog.Header; using System; @@ -27,6 +28,8 @@ namespace osu.Game.Overlays.Changelog public Action OnListingActivated; + public APIChangelog ChangelogEntry; + private const float cover_height = 310; private const float title_height = 50; private const float icon_size = 50; @@ -181,10 +184,11 @@ namespace osu.Game.Overlays.Changelog }; } - public void ShowReleaseStream(string headerText, string breadcrumbText) + public void ShowReleaseStream() { - releaseStream.Activate(breadcrumbText); - changeHeaderText(headerText); + releaseStream.Activate(String.Join(" ", + ChangelogEntry.UpdateStream.DisplayName, ChangelogEntry.DisplayVersion)); + changeHeaderText(ChangelogEntry.UpdateStream.DisplayName); } private void changeHeaderText(string headerText) diff --git a/osu.Game/Overlays/Changelog/ChangelogStreams.cs b/osu.Game/Overlays/Changelog/ChangelogStreams.cs index f0848188c8..b857a23653 100644 --- a/osu.Game/Overlays/Changelog/ChangelogStreams.cs +++ b/osu.Game/Overlays/Changelog/ChangelogStreams.cs @@ -2,13 +2,12 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using OpenTK.Graphics; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Overlays.Changelog.Streams; +using osu.Game.Online.API.Requests.Responses; +using System; namespace osu.Game.Overlays.Changelog { @@ -17,8 +16,9 @@ namespace osu.Game.Overlays.Changelog private const float container_height = 106.5f; private const float container_margin_y = 20; private const float container_margin_x = 85; + public Action OnSelection; - public Bindable SelectedRelease = new Bindable(); + public APIChangelog SelectedRelease; public readonly FillFlowContainer BadgesContainer; @@ -45,31 +45,23 @@ namespace osu.Game.Overlays.Changelog Left = container_margin_x, Right = container_margin_x, }, - Children = new[] - { - new StreamBadge(StreamColour.STABLE, "Stable", "20180626.1", 16370, true), - new StreamBadge(StreamColour.BETA, "Beta", "20180626", 186), - new StreamBadge(StreamColour.LAZER, "Lazer", "2018.713.1"), - }, }, }; - BadgesContainer.OnLoadComplete = d => + // ok, so this is probably not the best. + // will need to reflect on this. + // do we need the changelog to be updateable? + BadgesContainer.OnUpdate = d => { foreach (StreamBadge streamBadge in BadgesContainer.Children) { streamBadge.OnActivation = () => { - SelectedRelease.Value = new ReleaseStreamInfo - { - DisplayVersion = streamBadge.DisplayVersion, - IsFeatured = streamBadge.IsFeatured, - Name = streamBadge.Name, - Users = streamBadge.Users, - }; + SelectedRelease = streamBadge.ChangelogEntry; foreach (StreamBadge item in BadgesContainer.Children) { - if (item.Name != streamBadge.Name) item.Deactivate(); + if (item.ChangelogEntry.Id != streamBadge.ChangelogEntry.Id) item.Deactivate(); } + OnSelection?.Invoke(); }; } }; @@ -80,9 +72,9 @@ namespace osu.Game.Overlays.Changelog // is this nullreference-safe for badgesContainer? foreach (StreamBadge streamBadge in BadgesContainer.Children) { - if (SelectedRelease.Value != null) + if (SelectedRelease != null) { - if (SelectedRelease.Value.Name != streamBadge.Name) + if (SelectedRelease.UpdateStream.Id != streamBadge.ChangelogEntry.Id) { streamBadge.Deactivate(); } @@ -94,7 +86,7 @@ namespace osu.Game.Overlays.Changelog protected override void OnHoverLost(InputState state) { - if (SelectedRelease.Value == null) + if (SelectedRelease == null) { foreach (StreamBadge streamBadge in BadgesContainer.Children) streamBadge.Activate(true); } diff --git a/osu.Game/Overlays/Changelog/ReleaseStreamInfo.cs b/osu.Game/Overlays/Changelog/ReleaseStreamInfo.cs deleted file mode 100644 index 2934ae8f94..0000000000 --- a/osu.Game/Overlays/Changelog/ReleaseStreamInfo.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.IO.Serialization; - -namespace osu.Game.Overlays.Changelog -{ - [Serializable] - public class ReleaseStreamInfo : IJsonSerializable - { - public string Name; - public string DisplayVersion; - - public float Users; - - public bool IsFeatured; - } -} diff --git a/osu.Game/Overlays/Changelog/Streams/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs similarity index 81% rename from osu.Game/Overlays/Changelog/Streams/StreamBadge.cs rename to osu.Game/Overlays/Changelog/StreamBadge.cs index 8bd10dcbb8..578a95f583 100644 --- a/osu.Game/Overlays/Changelog/Streams/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -6,13 +6,14 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Online.API.Requests.Responses; using System; -namespace osu.Game.Overlays.Changelog.Streams +namespace osu.Game.Overlays.Changelog { public class StreamBadge : ClickableContainer { @@ -26,19 +27,13 @@ namespace osu.Game.Overlays.Changelog.Streams private readonly Header.LineBadge lineBadge; private SampleChannel sampleHover; - public readonly string Name; - public readonly string DisplayVersion; - public readonly bool IsFeatured; - public readonly float Users; + public readonly APIChangelog ChangelogEntry; - public StreamBadge(ColourInfo colour, string streamName, string streamBuild, float onlineUsers = 0, bool isFeatured = false) + public StreamBadge(APIChangelog changelogEntry) { - Name = streamName; - DisplayVersion = streamBuild; - IsFeatured = isFeatured; - Users = onlineUsers; + ChangelogEntry = changelogEntry; Height = badge_height; - Width = isFeatured ? badge_width * 2 : badge_width; + Width = ChangelogEntry.IsFeatured ? badge_width * 2 : badge_width; Margin = new MarginPadding(5); isActivated = true; Children = new Drawable[] @@ -52,7 +47,7 @@ namespace osu.Game.Overlays.Changelog.Streams { new SpriteText { - Text = streamName, + Text = ChangelogEntry.UpdateStream.DisplayName, Font = @"Exo2.0-Bold", TextSize = 16, Margin = new MarginPadding @@ -62,14 +57,14 @@ namespace osu.Game.Overlays.Changelog.Streams }, new SpriteText { - Text = streamBuild, + Text = ChangelogEntry.DisplayVersion, Font = @"Exo2.0-Light", TextSize = 21, }, new SpriteText { - Text = onlineUsers > 0 ? - string.Join(" ", onlineUsers.ToString("N0"), "users online"): + Text = ChangelogEntry.Users > 0 ? + string.Join(" ", ChangelogEntry.Users.ToString("N0"), "users online"): null, TextSize = 12, Font = @"Exo2.0-Regular", @@ -82,7 +77,7 @@ namespace osu.Game.Overlays.Changelog.Streams Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Width = 1, - Colour = colour, + Colour = StreamColour.FromStreamName(ChangelogEntry.UpdateStream.Name), RelativeSizeAxes = Axes.X, TransitionDuration = 600, }, diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index f928f9ee58..259036537b 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -3,6 +3,7 @@ using OpenTK; using OpenTK.Graphics; +using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -10,8 +11,10 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Input.Bindings; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Changelog; -using osu.Game.Overlays.Changelog.Streams; namespace osu.Game.Overlays { @@ -19,6 +22,9 @@ namespace osu.Game.Overlays { private readonly ChangelogHeader header; public readonly ChangelogStreams Streams; + private APIChangelog changelogEntry; + + private APIAccess api; protected readonly Color4 Purple = new Color4(191, 4, 255, 255); @@ -36,6 +42,8 @@ namespace osu.Game.Overlays Width = 0.85f; Masking = true; + ChangelogContent content; // told by appveyor to conver to local variable.. + EdgeEffect = new EdgeEffectParameters { Colour = Color4.Black.Opacity(0), @@ -64,25 +72,36 @@ namespace osu.Game.Overlays { header = new ChangelogHeader(), Streams = new ChangelogStreams(), + new ChangelogChart(), + // will need to default to day-sorted content + content = new ChangelogContent(), }, }, }, }; - Streams.SelectedRelease.ValueChanged += r => + Streams.OnSelection = () => { - if (Streams.SelectedRelease.Value != null) - header.ShowReleaseStream(r.Name, string.Join(" ", r.Name, r.DisplayVersion)); + if (Streams.SelectedRelease != null) + { + header.ChangelogEntry = Streams.SelectedRelease; + } + header.ShowReleaseStream(); + content.Clear(); + content.Add(new ChangelogContentGroup(Streams.SelectedRelease)); }; Streams.BadgesContainer.OnLoadComplete += d => { - header.OnListingActivated += () => - { - Streams.SelectedRelease.Value = null; - if (!Streams.IsHovered) - foreach (StreamBadge item in Streams.BadgesContainer.Children) item.Activate(true); - else - foreach (StreamBadge item in Streams.BadgesContainer.Children) item.Deactivate(); - }; + FetchChangelog(); + }; + header.OnListingActivated += () => + { + Streams.SelectedRelease = null; + content.Clear(); + // should add listing to content here + if (!Streams.IsHovered) + foreach (StreamBadge item in Streams.BadgesContainer.Children) item.Activate(true); + else + foreach (StreamBadge item in Streams.BadgesContainer.Children) item.Deactivate(); }; } @@ -120,5 +139,24 @@ namespace osu.Game.Overlays base.PopOut(); FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); } + + [BackgroundDependencyLoader] + private void load(APIAccess api) + { + this.api = api; + } + + public void FetchChangelog() + { + var req = new GetChangelogLatestBuildsRequest(); + req.Success += res => + { + foreach (APIChangelog item in res) + { + Streams.BadgesContainer.Add(new StreamBadge(item)); + } + }; + api.Queue(req); + } } } From a9299423cd79a08c36b8e2a0054e8e1f8cca3482 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Thu, 19 Jul 2018 21:49:13 +0200 Subject: [PATCH 0015/2854] Refactor2 --- .../Input/Bindings/GlobalActionContainer.cs | 2 -- osu.Game/OsuGame.cs | 11 +------- osu.Game/Overlays/Changelog/ChangelogChart.cs | 1 + .../Overlays/Changelog/ChangelogContent.cs | 7 ++++- .../Changelog/ChangelogContentGroup.cs | 5 ++-- .../Overlays/Changelog/ChangelogHeader.cs | 9 ++---- .../Overlays/Changelog/ChangelogStreams.cs | 28 ++++++++++--------- .../Changelog/Header/TextBadgePair.cs | 10 ++----- .../Changelog/Header/TextBadgePairRelease.cs | 5 +--- osu.Game/Overlays/Changelog/StreamBadge.cs | 2 -- osu.Game/Overlays/ChangelogOverlay.cs | 8 ++---- .../Toolbar/ToolbarChangelogButton.cs | 22 --------------- 12 files changed, 36 insertions(+), 74 deletions(-) delete mode 100644 osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 00857b9c9b..b21deff509 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -63,8 +63,6 @@ namespace osu.Game.Input.Bindings ToggleChat, [Description("Toggle social overlay")] ToggleSocial, - [Description("Toggle changelog")] - ToggleChangelog, [Description("Reset input settings")] ResetInputSettings, [Description("Toggle toolbar")] diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 3eb4faf6aa..557b6e4469 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -53,8 +53,6 @@ namespace osu.Game private DialogOverlay dialogOverlay; - private ChangelogOverlay changelog; - private DirectOverlay direct; private SocialOverlay social; @@ -112,8 +110,6 @@ namespace osu.Game public void ToggleDirect() => direct.ToggleVisibility(); - public void ToggleChangelog() => changelog.ToggleVisibility(); - /// /// Close all game-wide overlays. /// @@ -285,7 +281,6 @@ namespace osu.Game loadComponentSingleFile(screenshotManager, Add); //overlay elements - loadComponentSingleFile(changelog = new ChangelogOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(social = new SocialOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(chat = new ChatOverlay { Depth = -1 }, mainContent.Add); @@ -320,7 +315,6 @@ namespace osu.Game dependencies.Cache(settings); dependencies.Cache(onscreenDisplay); dependencies.Cache(social); - dependencies.Cache(changelog); dependencies.Cache(direct); dependencies.Cache(chat); dependencies.Cache(userProfile); @@ -355,7 +349,7 @@ namespace osu.Game } // ensure only one of these overlays are open at once. - var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct, changelog }; + var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct }; overlays.AddRange(singleDisplayOverlays); foreach (var overlay in singleDisplayOverlays) @@ -465,9 +459,6 @@ namespace osu.Game case GlobalAction.ToggleSocial: social.ToggleVisibility(); return true; - case GlobalAction.ToggleChangelog: - changelog.ToggleVisibility(); - return true; case GlobalAction.ResetInputSettings: var sensitivity = frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity); diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index e885867c74..b113a509ec 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -10,6 +10,7 @@ using osu.Game.Graphics; namespace osu.Game.Overlays.Changelog { // maybe look to osu.Game.Screens.Play.SquareGraph for reference later + // placeholder json file: https://api.myjson.com/bins/10ye8a public class ChangelogChart : BufferedContainer { public ChangelogChart() diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index d10fe19942..325ec802d4 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -11,8 +11,13 @@ namespace osu.Game.Overlays.Changelog public ChangelogContent() { RelativeSizeAxes = Axes.X; - //AutoSizeAxes = Axes.Y; + AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; + Padding = new MarginPadding + { + Left = 70, + Right = 70, + }; } } } diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 951cebb941..9675cf91f5 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -75,7 +75,8 @@ namespace osu.Game.Overlays.Changelog { Text = build.DisplayVersion, TextSize = 28, // web: 24, - Colour = StreamColour.STABLE, + Font = @"Exo2.0-Light", + Colour = StreamColour.FromStreamName(build.UpdateStream.Name), }, } }, @@ -90,7 +91,7 @@ namespace osu.Game.Overlays.Changelog new SpriteText { // do we need .ToUniversalTime() here? - // also, this is a temporary solution to weekdays in >localized< date strings + // also, this should be a temporary solution to weekdays in >localized< date strings Text = build.CreatedAt.Date.ToLongDateString().Replace(build.CreatedAt.ToString("dddd") + ", ", ""), TextSize = 17, // web: 14, Colour = OsuColour.FromHex(@"FD5"), diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 8332aeeaa4..a95d66158b 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Changelog { protected Color4 Purple = new Color4(191, 4, 255, 255); private readonly Sprite coverImage; - private readonly Sprite headerBadge; //50x50, margin-right: 20 + private readonly Sprite headerBadge; private readonly OsuSpriteText titleStream; private readonly TextBadgePairListing listing; private readonly TextBadgePairRelease releaseStream; @@ -46,10 +46,7 @@ namespace osu.Game.Overlays.Changelog coverImage = new Sprite { RelativeSizeAxes = Axes.Both, - Size = new Vector2(1), FillMode = FillMode.Fill, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, }, new Container // this is the line badge-Changelog-Stream { @@ -79,11 +76,11 @@ namespace osu.Game.Overlays.Changelog // this box has 2 functions: // - ensures the circle doesn't disappear on the X and Y edges - // - lessens the white "contamination" on the circle (due to smoothing) + // - gets rid of the white "contamination" on the circle (due to smoothing) + // (https://i.imgur.com/SMuvWBZ.png) new Box { RelativeSizeAxes = Axes.Both, - Size = new Vector2(1), Alpha = 0, AlwaysPresent = true, Colour = Purple, diff --git a/osu.Game/Overlays/Changelog/ChangelogStreams.cs b/osu.Game/Overlays/Changelog/ChangelogStreams.cs index b857a23653..aded31cb47 100644 --- a/osu.Game/Overlays/Changelog/ChangelogStreams.cs +++ b/osu.Game/Overlays/Changelog/ChangelogStreams.cs @@ -14,42 +14,44 @@ namespace osu.Game.Overlays.Changelog public class ChangelogStreams : Container { private const float container_height = 106.5f; - private const float container_margin_y = 20; - private const float container_margin_x = 85; + private const float padding_y = 20; + private const float padding_x = 85; public Action OnSelection; public APIChangelog SelectedRelease; + // not using SelectedRelease as a Bindable and then using .OnValueChange instead of OnSelection + // because it doesn't "refresh" the selection if the same stream is chosen public readonly FillFlowContainer BadgesContainer; public ChangelogStreams() { - Height = container_height; + // this should actually be resizeable (https://streamable.com/yw2ug) + // if not, with small width:height ratio it cuts off right-most content RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, - Size = new OpenTK.Vector2(1), Colour = new Color4(32, 24, 35, 255), }, BadgesContainer = new FillFlowContainer { - Direction = FillDirection.Horizontal, - RelativeSizeAxes = Axes.Both, - Margin = new MarginPadding + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { - Top = container_margin_y, - Bottom = container_margin_y, - Left = container_margin_x, - Right = container_margin_x, + Top = padding_y, + Bottom = padding_y, + Left = padding_x, + Right = padding_x, }, }, }; // ok, so this is probably not the best. - // will need to reflect on this. - // do we need the changelog to be updateable? + // how else can this be done? BadgesContainer.OnUpdate = d => { foreach (StreamBadge streamBadge in BadgesContainer.Children) diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index a888c436cf..c14414c6c4 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -61,9 +61,8 @@ namespace osu.Game.Overlays.Changelog.Header .MoveToY(0, duration, easing) .FadeIn(duration, easing); - // since using .finally/.oncomplete after first fadeout made the badge - // not hide sometimes in visual tests(because FinishTransforms()/CancelTransforms() - // didn't apply to transforms that come after the .finally), I'm using a scheduler here + // since using .finally/.oncomplete after first fadeout made the badge not hide + // sometimes in visual tests (https://streamable.com/0qssq), I'm using a scheduler here Scheduler.AddDelayed(() => { if (!string.IsNullOrEmpty(displayText)) Text.Text = displayText; @@ -79,10 +78,8 @@ namespace osu.Game.Overlays.Changelog.Header { Text = new SpriteText { - TextSize = 21, // web is 16, but here it looks too small? + TextSize = 21, // web: 16, Text = displayText, - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, Margin = new MarginPadding { Top = 5, @@ -91,7 +88,6 @@ namespace osu.Game.Overlays.Changelog.Header }, LineBadge = new LineBadge(startCollapsed) { - Width = 1, Colour = badgeColour, RelativeSizeAxes = Axes.X, } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs index 2186043035..339f9f26d6 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs @@ -17,10 +17,7 @@ namespace osu.Game.Overlays.Changelog.Header Text.Alpha = 0; } - public void SetText(string displayText) - { - Text.Text = displayText; - } + public void SetText(string displayText) => Text.Text = displayText; public void Activate(string displayText = null) { diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index 578a95f583..4fa9b2de88 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -76,10 +76,8 @@ namespace osu.Game.Overlays.Changelog { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Width = 1, Colour = StreamColour.FromStreamName(ChangelogEntry.UpdateStream.Name), RelativeSizeAxes = Axes.X, - TransitionDuration = 600, }, }; } diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 259036537b..7723ab7659 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -79,6 +79,7 @@ namespace osu.Game.Overlays }, }, }; + OnLoadComplete += d => FetchChangelog(); // is i Streams.OnSelection = () => { if (Streams.SelectedRelease != null) @@ -86,13 +87,9 @@ namespace osu.Game.Overlays header.ChangelogEntry = Streams.SelectedRelease; } header.ShowReleaseStream(); - content.Clear(); + content.Clear(); // this should probably happen with some transition content.Add(new ChangelogContentGroup(Streams.SelectedRelease)); }; - Streams.BadgesContainer.OnLoadComplete += d => - { - FetchChangelog(); - }; header.OnListingActivated += () => { Streams.SelectedRelease = null; @@ -151,6 +148,7 @@ namespace osu.Game.Overlays var req = new GetChangelogLatestBuildsRequest(); req.Success += res => { + Streams.BadgesContainer.Clear(); foreach (APIChangelog item in res) { Streams.BadgesContainer.Add(new StreamBadge(item)); diff --git a/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs deleted file mode 100644 index db4fd4ba07..0000000000 --- a/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Toolbar -{ - public class ToolbarChangelogButton : ToolbarOverlayToggleButton - { - public ToolbarChangelogButton() - { - SetIcon(FontAwesome.fa_list); - } - - [BackgroundDependencyLoader(true)] - private void load(ChangelogOverlay changelog) - { - StateContainer = changelog; - } - } -} From a857999950aa380f1d0d15557dfc89c9d00b6a69 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Fri, 20 Jul 2018 00:52:50 +0200 Subject: [PATCH 0016/2854] Refactor3 --- osu.Game.Tests/Visual/TestCaseChangelog.cs | 21 +++++++++++++--- .../UserInterface/TooltipIconButton.cs | 25 +++++++++---------- .../API/Requests/Responses/APIChangelog.cs | 6 ----- .../Overlays/Changelog/ChangelogStreams.cs | 15 +++++------ .../Changelog/Header/TextBadgePair.cs | 6 +++-- .../Changelog/Header/TextBadgePairListing.cs | 3 ++- .../Changelog/Header/TextBadgePairRelease.cs | 7 +++--- osu.Game/Overlays/Changelog/StreamBadge.cs | 5 ++-- osu.Game/Overlays/ChangelogOverlay.cs | 19 ++++++-------- 9 files changed, 56 insertions(+), 51 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChangelog.cs b/osu.Game.Tests/Visual/TestCaseChangelog.cs index 894b117118..ea6aa8086f 100644 --- a/osu.Game.Tests/Visual/TestCaseChangelog.cs +++ b/osu.Game.Tests/Visual/TestCaseChangelog.cs @@ -23,7 +23,8 @@ namespace osu.Game.Tests.Visual AddStep(@"Show", changelog.Show); AddRepeatStep(@"Toggle Release Stream", () => { - if (isLoaded) changelog.Streams.BadgesContainer.Children[index].Activate(); + if (isLoaded) + changelog.Streams.BadgesContainer.Children[index].Activate(); indexIncrement(); }, 6); AddStep(@"Listing", changelog.ActivateListing); @@ -31,7 +32,8 @@ namespace osu.Game.Tests.Visual AddWaitStep(3); AddStep(@"Show with Release Stream", () => { - if (isLoaded) changelog.Streams.BadgesContainer.Children[index].Activate(); + if (isLoaded) + changelog.Streams.BadgesContainer.Children[index].Activate(); changelog.Show(); indexIncrement(); }); @@ -48,7 +50,8 @@ namespace osu.Game.Tests.Visual AddWaitStep(3); AddStep(@"Activate release", () => { - if (isLoaded) changelog.Streams.BadgesContainer.Children[index].Activate(); + if (isLoaded) + changelog.Streams.BadgesContainer.Children[index].Activate(); indexIncrement(); }); AddStep(@"Show with listing", () => @@ -56,6 +59,18 @@ namespace osu.Game.Tests.Visual changelog.ActivateListing(); changelog.Show(); }); + AddStep(@"Activate Release", () => + { + if (isLoaded) + changelog.Streams.BadgesContainer.Children[index].Activate(); + }); + AddStep(@"Activate Listing", changelog.ActivateListing); + AddStep(@"Activate Release", () => + { + if (isLoaded) + changelog.Streams.BadgesContainer.Children[index].Activate(); + indexIncrement(); + }); } } } diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs index 918e203bf4..7614c4510a 100644 --- a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -22,22 +22,21 @@ namespace osu.Game.Graphics.UserInterface public TooltipIconButton() { Children = new Drawable[] + { + new Box { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - }, - icon = new SpriteIcon - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Size = new Vector2(18), - } - }; + RelativeSizeAxes = Axes.Both, + Alpha = 0, + }, + icon = new SpriteIcon + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(18), + } + }; } - public string TooltipText { get; set; } } } diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelog.cs b/osu.Game/Online/API/Requests/Responses/APIChangelog.cs index f0dcafdc04..b133888056 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelog.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelog.cs @@ -27,12 +27,6 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty("created_at")] public DateTimeOffset CreatedAt { get; set; } - [JsonProperty("disqus_id")] - public string DisqusId { get; set; } - - [JsonProperty("disqus_title")] - public string DisqusTitle { get; set; } - [JsonProperty("update_stream")] public UpdateStream UpdateStream { get; set; } diff --git a/osu.Game/Overlays/Changelog/ChangelogStreams.cs b/osu.Game/Overlays/Changelog/ChangelogStreams.cs index aded31cb47..01c57b8214 100644 --- a/osu.Game/Overlays/Changelog/ChangelogStreams.cs +++ b/osu.Game/Overlays/Changelog/ChangelogStreams.cs @@ -60,9 +60,8 @@ namespace osu.Game.Overlays.Changelog { SelectedRelease = streamBadge.ChangelogEntry; foreach (StreamBadge item in BadgesContainer.Children) - { - if (item.ChangelogEntry.Id != streamBadge.ChangelogEntry.Id) item.Deactivate(); - } + if (item.ChangelogEntry.Id != streamBadge.ChangelogEntry.Id) + item.Deactivate(); OnSelection?.Invoke(); }; } @@ -77,11 +76,10 @@ namespace osu.Game.Overlays.Changelog if (SelectedRelease != null) { if (SelectedRelease.UpdateStream.Id != streamBadge.ChangelogEntry.Id) - { streamBadge.Deactivate(); - } } - else streamBadge.Deactivate(); + else + streamBadge.Deactivate(); } return base.OnHover(state); } @@ -89,9 +87,8 @@ namespace osu.Game.Overlays.Changelog protected override void OnHoverLost(InputState state) { if (SelectedRelease == null) - { - foreach (StreamBadge streamBadge in BadgesContainer.Children) streamBadge.Activate(true); - } + foreach (StreamBadge streamBadge in BadgesContainer.Children) + streamBadge.Activate(true); base.OnHoverLost(state); } } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index c14414c6c4..6256c52be2 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -44,7 +44,8 @@ namespace osu.Game.Overlays.Changelog.Header public void ShowText(double duration = 0, string displayText = null, Easing easing = Easing.InOutCubic) { LineBadge.IsCollapsed = false; - if (!string.IsNullOrEmpty(displayText)) Text.Text = displayText; + if (!string.IsNullOrEmpty(displayText)) + Text.Text = displayText; Text.MoveToY(0, duration, easing) .FadeIn(duration, easing); } @@ -111,7 +112,8 @@ namespace osu.Game.Overlays.Changelog.Header protected override bool OnHover(InputState state) { - if (!IsActivated) sampleHover?.Play(); + if (!IsActivated) + sampleHover?.Play(); return base.OnHover(state); } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs index eaf7e1289b..425b4b111a 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs @@ -63,7 +63,8 @@ namespace osu.Game.Overlays.Changelog.Header protected override void OnHoverLost(InputState state) { - if (IsActivated == false) LineBadge.ResizeHeightTo(1, LineBadge.TransitionDuration, Easing.Out); + if (!IsActivated) + LineBadge.ResizeHeightTo(1, LineBadge.TransitionDuration, Easing.Out); base.OnHoverLost(state); } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs index 339f9f26d6..32a76670f0 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs @@ -23,13 +23,12 @@ namespace osu.Game.Overlays.Changelog.Header { if (IsActivated) { - if (displayText != Text.Text) ChangeText(transition_duration, displayText); + if (displayText != Text.Text) + ChangeText(transition_duration, displayText); } else - { ShowText(transition_duration, displayText); - IsActivated = true; - } + IsActivated = true; SampleActivate?.Play(); OnActivation?.Invoke(); } diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index 4fa9b2de88..7e367a63fa 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.Changelog new SpriteText { Text = ChangelogEntry.Users > 0 ? - string.Join(" ", ChangelogEntry.Users.ToString("N0"), "users online"): + string.Format($"{ChangelogEntry.Users:N0} users online") : null, TextSize = 12, Font = @"Exo2.0-Regular", @@ -87,7 +87,8 @@ namespace osu.Game.Overlays.Changelog isActivated = true; this.FadeIn(transition_duration); lineBadge.IsCollapsed = false; - if (!withoutHeaderUpdate) OnActivation?.Invoke(); + if (!withoutHeaderUpdate) + OnActivation?.Invoke(); } public void Deactivate() diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 7723ab7659..081b501cad 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -96,9 +96,11 @@ namespace osu.Game.Overlays content.Clear(); // should add listing to content here if (!Streams.IsHovered) - foreach (StreamBadge item in Streams.BadgesContainer.Children) item.Activate(true); + foreach (StreamBadge item in Streams.BadgesContainer.Children) + item.Activate(true); else - foreach (StreamBadge item in Streams.BadgesContainer.Children) item.Deactivate(); + foreach (StreamBadge item in Streams.BadgesContainer.Children) + item.Deactivate(); }; } @@ -112,13 +114,10 @@ namespace osu.Game.Overlays switch (action) { case GlobalAction.Back: - if (header.IsListingActivated()) State = Visibility.Hidden; - - // the problem here is that when hovering over the builds' container - // and pressing back, they don't lower their opacity they're rehovered on - else header.ActivateListing(); - return true; - case GlobalAction.Select: + if (header.IsListingActivated()) + State = Visibility.Hidden; + else + header.ActivateListing(); return true; } @@ -150,9 +149,7 @@ namespace osu.Game.Overlays { Streams.BadgesContainer.Clear(); foreach (APIChangelog item in res) - { Streams.BadgesContainer.Add(new StreamBadge(item)); - } }; api.Queue(req); } From 1857c91647caa6acf62b35b07b207b1b7f20b0f4 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Fri, 20 Jul 2018 11:20:01 +0200 Subject: [PATCH 0017/2854] Expand APIChangelog; Normalize its line endings --- .../API/Requests/Responses/APIChangelog.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelog.cs b/osu.Game/Online/API/Requests/Responses/APIChangelog.cs index b133888056..f4e4760012 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelog.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelog.cs @@ -32,6 +32,78 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty("changelog_entries")] public List ChangelogEntries { get; set; } + + [JsonProperty("versions")] + public Versions Versions { get; set; } + } + + public class Versions + { + [JsonProperty("next")] + public APIChangelog Next { get; set; } + + [JsonProperty("previous")] + public APIChangelog Previous { get; set; } + } + + public class ChangelogEntry + { + [JsonProperty("id")] + public long Id { get; set; } + + [JsonProperty("repository")] + public string Repository { get; set; } + + [JsonProperty("github_pull_request_id")] + public long GithubPullRequestId { get; set; } + + [JsonProperty("github_url")] + public string GithubUrl { get; set; } + + [JsonProperty("url")] + public object Url { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("category")] + public string Category { get; set; } + + [JsonProperty("title")] + public string Title { get; set; } + + [JsonProperty("message_html")] + public string MessageHtml { get; set; } + + [JsonProperty("major")] + public bool Major { get; set; } + + [JsonProperty("created_at")] + public DateTimeOffset CreatedAt { get; set; } + + [JsonProperty("github_user")] + public GithubUser GithubUser { get; set; } + } + + public partial class GithubUser + { + [JsonProperty("id")] + public long Id { get; set; } + + [JsonProperty("display_name")] + public string DisplayName { get; set; } + + [JsonProperty("github_url")] + public string GithubUrl { get; set; } + + [JsonProperty("osu_username")] + public string OsuUsername { get; set; } + + [JsonProperty("user_id")] + public long? UserId { get; set; } + + [JsonProperty("user_url")] + public string UserUrl { get; set; } } public class UpdateStream From c7669b21282e7eecdb486f43a5fc3131633657af Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Fri, 20 Jul 2018 12:22:31 +0200 Subject: [PATCH 0018/2854] Add build-scoped requests; Add OnClick to TooltipIconButton; Actions on pressing previous/next in builds --- .../UserInterface/TooltipIconButton.cs | 9 +++++++++ .../API/Requests/GetChangelogBuildRequest.cs | 20 +++++++++++++++++++ .../Changelog/ChangelogContentGroup.cs | 8 ++++---- 3 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs index 7614c4510a..5617dc5b44 100644 --- a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -5,13 +5,16 @@ using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; using osu.Game.Graphics.Containers; +using System; namespace osu.Game.Graphics.UserInterface { public class TooltipIconButton : OsuClickableContainer, IHasTooltip { private readonly SpriteIcon icon; + public Action OnPressed; public FontAwesome Icon { @@ -37,6 +40,12 @@ namespace osu.Game.Graphics.UserInterface }; } + protected override bool OnClick(InputState state) + { + OnPressed?.Invoke(); + return base.OnClick(state); + } + public string TooltipText { get; set; } } } diff --git a/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs b/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs new file mode 100644 index 0000000000..64bc6b4b59 --- /dev/null +++ b/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Online.API.Requests.Responses; + +namespace osu.Game.Online.API.Requests +{ + public class GetChangelogBuildRequest : APIRequest + { + private string url; + /// This will need to be changed to "long Id" + /// Placeholder for testing + GetChangelogBuildRequest(string url) + { + this.url = url; + } + protected override string Uri => @""; + protected override string Target => url; + } +} diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 9675cf91f5..07577e18c1 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -8,12 +8,13 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; +using System; namespace osu.Game.Overlays.Changelog { public class ChangelogContentGroup : FillFlowContainer { - // will porobably depend in some way on #1692 (https://github.com/ppy/osu-framework/pull/1692) + public Action NextRequested, PreviousRequested; // need to keep in mind it looks different on Listing (one contains all builds from a date) // and when a stream is selected (looks like now) public ChangelogContentGroup(APIChangelog build) @@ -45,10 +46,8 @@ namespace osu.Game.Overlays.Changelog { Icon = FontAwesome.fa_chevron_left, Size = new Vector2(24), - // how do we link to previous/next builds? - // I'm thinking some linked list, but how do we make that - // from the available API data TooltipText = "Previous", + OnPressed = PreviousRequested, }, new FillFlowContainer { @@ -85,6 +84,7 @@ namespace osu.Game.Overlays.Changelog Icon = FontAwesome.fa_chevron_right, Size = new Vector2(24), TooltipText = "Next", + OnPressed = NextRequested, }, } }, From 1b3010a1b534a9675582343f82c991cf9a2e704f Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Fri, 20 Jul 2018 12:33:44 +0200 Subject: [PATCH 0019/2854] Remove accidental partial modifier in APIChangelog --- osu.Game/Online/API/Requests/Responses/APIChangelog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelog.cs b/osu.Game/Online/API/Requests/Responses/APIChangelog.cs index f4e4760012..02f9bd763d 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelog.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelog.cs @@ -85,7 +85,7 @@ namespace osu.Game.Online.API.Requests.Responses public GithubUser GithubUser { get; set; } } - public partial class GithubUser + public class GithubUser { [JsonProperty("id")] public long Id { get; set; } From 227394925a0603905989ba47f4e8a112d313ac3e Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Fri, 20 Jul 2018 13:51:31 +0200 Subject: [PATCH 0020/2854] Add neighboring builds handling; + provoke angry AppVeyor --- .../API/Requests/GetChangelogBuildRequest.cs | 16 +++--- .../Overlays/Changelog/ChangelogContent.cs | 54 +++++++++++++++++++ .../Changelog/ChangelogContentGroup.cs | 4 +- osu.Game/Overlays/Changelog/StreamBadge.cs | 2 +- osu.Game/Overlays/ChangelogOverlay.cs | 3 +- 5 files changed, 67 insertions(+), 12 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs b/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs index 64bc6b4b59..c4ddb9a0cc 100644 --- a/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs +++ b/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs @@ -7,14 +7,16 @@ namespace osu.Game.Online.API.Requests { public class GetChangelogBuildRequest : APIRequest { - private string url; - /// This will need to be changed to "long Id" - /// Placeholder for testing - GetChangelogBuildRequest(string url) + private readonly string name; + private readonly string version; + + public GetChangelogBuildRequest(string streamName, string buildVersion) { - this.url = url; + name = streamName; + version = buildVersion; } - protected override string Uri => @""; - protected override string Target => url; + + //protected override string Target => $@"changelog/{name}/{version}"; + protected override string Uri => @"https://api.myjson.com/bins/ya5q2"; // for testing } } diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 325ec802d4..9b35f51721 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -1,13 +1,20 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Changelog { public class ChangelogContent : FillFlowContainer { + private APIChangelog currentBuild; + private APIAccess api; + public ChangelogContent() { RelativeSizeAxes = Axes.X; @@ -19,5 +26,52 @@ namespace osu.Game.Overlays.Changelog Right = 70, }; } + + public override void Add(ChangelogContentGroup changelogContentGroup) + { + changelogContentGroup.PreviousRequested = ShowPrevious; + changelogContentGroup.NextRequested = ShowNext; + base.Add(changelogContentGroup); + } + + public void ShowBuild(APIChangelog changelog) + { + Clear(); + Add(new ChangelogContentGroup(changelog)); + FetchChangelogBuild(changelog); + } + + private void ShowNext() + { + if (currentBuild.Versions.Next != null) + { + Clear(); + Add(new ChangelogContentGroup(currentBuild.Versions.Next)); + FetchChangelogBuild(currentBuild.Versions.Next); + } + } + + private void ShowPrevious() + { + if (currentBuild.Versions.Previous != null) + { + Clear(); + Add(new ChangelogContentGroup(currentBuild.Versions.Previous)); + FetchChangelogBuild(currentBuild.Versions.Previous); + } + } + + [BackgroundDependencyLoader] + private void load(APIAccess api) + { + this.api = api; + } + + private void FetchChangelogBuild(APIChangelog build) + { + var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); + req.Success += res => currentBuild = res; + api.Queue(req); + } } } diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 07577e18c1..4826379682 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -47,7 +47,7 @@ namespace osu.Game.Overlays.Changelog Icon = FontAwesome.fa_chevron_left, Size = new Vector2(24), TooltipText = "Previous", - OnPressed = PreviousRequested, + OnPressed = () => PreviousRequested(), }, new FillFlowContainer { @@ -84,7 +84,7 @@ namespace osu.Game.Overlays.Changelog Icon = FontAwesome.fa_chevron_right, Size = new Vector2(24), TooltipText = "Next", - OnPressed = NextRequested, + OnPressed = () => NextRequested(), }, } }, diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index 7e367a63fa..ba98e0a4f7 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.Changelog new SpriteText { Text = ChangelogEntry.Users > 0 ? - string.Format($"{ChangelogEntry.Users:N0} users online") : + $"{ChangelogEntry.Users:N0} users online" : null, TextSize = 12, Font = @"Exo2.0-Regular", diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 081b501cad..417b4f7a30 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -87,8 +87,7 @@ namespace osu.Game.Overlays header.ChangelogEntry = Streams.SelectedRelease; } header.ShowReleaseStream(); - content.Clear(); // this should probably happen with some transition - content.Add(new ChangelogContentGroup(Streams.SelectedRelease)); + content.ShowBuild(Streams.SelectedRelease); }; header.OnListingActivated += () => { From 02a8fb2154e62a8566a0c3b8c3c1c710335a39a5 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Fri, 20 Jul 2018 15:48:20 +0200 Subject: [PATCH 0021/2854] Update comments; Improve neighboring builds handling; Apply fixes to things pointed out by AppVeyor --- .../UserInterface/TooltipIconButton.cs | 9 ------- .../API/Requests/GetChangelogBuildRequest.cs | 14 +++++----- .../GetChangelogLatestBuildsRequest.cs | 7 ++--- .../API/Requests/GetChangelogRequest.cs | 4 +-- .../Overlays/Changelog/ChangelogContent.cs | 26 ++++++++++++------- .../Changelog/ChangelogContentGroup.cs | 10 +++---- .../Overlays/Changelog/ChangelogHeader.cs | 8 +++--- .../Overlays/Changelog/ChangelogStreams.cs | 3 --- osu.Game/Overlays/ChangelogOverlay.cs | 5 ++-- 9 files changed, 37 insertions(+), 49 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs index 5617dc5b44..7614c4510a 100644 --- a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -5,16 +5,13 @@ using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; using osu.Game.Graphics.Containers; -using System; namespace osu.Game.Graphics.UserInterface { public class TooltipIconButton : OsuClickableContainer, IHasTooltip { private readonly SpriteIcon icon; - public Action OnPressed; public FontAwesome Icon { @@ -40,12 +37,6 @@ namespace osu.Game.Graphics.UserInterface }; } - protected override bool OnClick(InputState state) - { - OnPressed?.Invoke(); - return base.OnClick(state); - } - public string TooltipText { get; set; } } } diff --git a/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs b/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs index c4ddb9a0cc..5d4d3b860c 100644 --- a/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs +++ b/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs @@ -7,14 +7,14 @@ namespace osu.Game.Online.API.Requests { public class GetChangelogBuildRequest : APIRequest { - private readonly string name; - private readonly string version; + //private readonly string name; + //private readonly string version; - public GetChangelogBuildRequest(string streamName, string buildVersion) - { - name = streamName; - version = buildVersion; - } + //public GetChangelogBuildRequest(string streamName, string buildVersion) + //{ + // name = streamName; + // version = buildVersion; + //} //protected override string Target => $@"changelog/{name}/{version}"; protected override string Uri => @"https://api.myjson.com/bins/ya5q2"; // for testing diff --git a/osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs b/osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs index 845760f2e4..962c8eab15 100644 --- a/osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs +++ b/osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs @@ -6,12 +6,9 @@ using System.Collections.Generic; namespace osu.Game.Online.API.Requests { - /// - /// Obviously a placeholder - /// public class GetChangelogLatestBuildsRequest : APIRequest> { - protected override string Uri => Target; - protected override string Target => @"https://api.myjson.com/bins/16waui"; + //protected override string Target => @"changelog"; + protected override string Uri => @"https://api.myjson.com/bins/16waui"; // for testing } } diff --git a/osu.Game/Online/API/Requests/GetChangelogRequest.cs b/osu.Game/Online/API/Requests/GetChangelogRequest.cs index 652e638ab8..6ddff7052e 100644 --- a/osu.Game/Online/API/Requests/GetChangelogRequest.cs +++ b/osu.Game/Online/API/Requests/GetChangelogRequest.cs @@ -7,7 +7,7 @@ namespace osu.Game.Online.API.Requests { public class GetChangelogRequest : APIRequest { - protected override string Uri => Target; - protected override string Target => "https://api.myjson.com/bins/6zv2i"; + //protected override string Target => @"changelog"; + protected override string Uri => @"https://api.myjson.com/bins/6zv2i"; // for testing } } diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 9b35f51721..23a1c54b51 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -29,8 +29,11 @@ namespace osu.Game.Overlays.Changelog public override void Add(ChangelogContentGroup changelogContentGroup) { - changelogContentGroup.PreviousRequested = ShowPrevious; - changelogContentGroup.NextRequested = ShowNext; + if (changelogContentGroup != null) + { + changelogContentGroup.PreviousRequested = showPrevious; + changelogContentGroup.NextRequested = showNext; + } base.Add(changelogContentGroup); } @@ -38,26 +41,29 @@ namespace osu.Game.Overlays.Changelog { Clear(); Add(new ChangelogContentGroup(changelog)); - FetchChangelogBuild(changelog); + //fetchChangelogBuild(changelog); + fetchChangelogBuild(); } - private void ShowNext() + private void showNext() { if (currentBuild.Versions.Next != null) { Clear(); Add(new ChangelogContentGroup(currentBuild.Versions.Next)); - FetchChangelogBuild(currentBuild.Versions.Next); + //fetchChangelogBuild(currentBuild.Versions.Next); + fetchChangelogBuild(); } } - private void ShowPrevious() + private void showPrevious() { if (currentBuild.Versions.Previous != null) { Clear(); Add(new ChangelogContentGroup(currentBuild.Versions.Previous)); - FetchChangelogBuild(currentBuild.Versions.Previous); + //fetchChangelogBuild(currentBuild.Versions.Previous); + fetchChangelogBuild(); } } @@ -67,9 +73,11 @@ namespace osu.Game.Overlays.Changelog this.api = api; } - private void FetchChangelogBuild(APIChangelog build) + //private void fetchChangelogBuild(APIChangelog build) + private void fetchChangelogBuild() { - var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); + //var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); + var req = new GetChangelogBuildRequest(); req.Success += res => currentBuild = res; api.Queue(req); } diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 4826379682..91b6ba0134 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -15,8 +15,6 @@ namespace osu.Game.Overlays.Changelog public class ChangelogContentGroup : FillFlowContainer { public Action NextRequested, PreviousRequested; - // need to keep in mind it looks different on Listing (one contains all builds from a date) - // and when a stream is selected (looks like now) public ChangelogContentGroup(APIChangelog build) { RelativeSizeAxes = Axes.X; @@ -47,7 +45,7 @@ namespace osu.Game.Overlays.Changelog Icon = FontAwesome.fa_chevron_left, Size = new Vector2(24), TooltipText = "Previous", - OnPressed = () => PreviousRequested(), + Action = () => PreviousRequested(), }, new FillFlowContainer { @@ -65,7 +63,7 @@ namespace osu.Game.Overlays.Changelog TextSize = 28, // web: 24, Font = @"Exo2.0-Medium", }, - new SpriteText // a space... + new SpriteText { Text = " ", TextSize = 28, @@ -84,7 +82,7 @@ namespace osu.Game.Overlays.Changelog Icon = FontAwesome.fa_chevron_right, Size = new Vector2(24), TooltipText = "Next", - OnPressed = () => NextRequested(), + Action = () => NextRequested(), }, } }, @@ -105,6 +103,6 @@ namespace osu.Game.Overlays.Changelog }, }; } - //public ChangelogContentGroup(DateTimeOffset date) { } + //public ChangelogContentGroup() { } // for listing } } diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index a95d66158b..1c3f6f0853 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Changelog RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fill, }, - new Container // this is the line badge-Changelog-Stream + new Container { Height = title_height, Anchor = Anchor.BottomLeft, @@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Changelog Y = -version_height, Children = new Drawable[] { - new CircularContainer // a purple circle + new CircularContainer { X = icon_margin, Masking = true, @@ -131,8 +131,6 @@ namespace osu.Game.Overlays.Changelog { Top = 10, Left = 7, - // + chevron size, and account for gained space on left by - // listing's font draw width being smaller Right = 18, Bottom = 15, }, @@ -153,7 +151,7 @@ namespace osu.Game.Overlays.Changelog releaseStream = new TextBadgePairRelease(Purple, "Lazer") }, }, - new Box // purple line + new Box { Colour = Purple, RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/Changelog/ChangelogStreams.cs b/osu.Game/Overlays/Changelog/ChangelogStreams.cs index 01c57b8214..cc2e1f3f31 100644 --- a/osu.Game/Overlays/Changelog/ChangelogStreams.cs +++ b/osu.Game/Overlays/Changelog/ChangelogStreams.cs @@ -26,8 +26,6 @@ namespace osu.Game.Overlays.Changelog public ChangelogStreams() { - // this should actually be resizeable (https://streamable.com/yw2ug) - // if not, with small width:height ratio it cuts off right-most content RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Children = new Drawable[] @@ -70,7 +68,6 @@ namespace osu.Game.Overlays.Changelog protected override bool OnHover(InputState state) { - // is this nullreference-safe for badgesContainer? foreach (StreamBadge streamBadge in BadgesContainer.Children) { if (SelectedRelease != null) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 417b4f7a30..d98db9de94 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -42,7 +42,7 @@ namespace osu.Game.Overlays Width = 0.85f; Masking = true; - ChangelogContent content; // told by appveyor to conver to local variable.. + ChangelogContent content; // told by appveyor to convert to local variable.. EdgeEffect = new EdgeEffectParameters { @@ -73,13 +73,12 @@ namespace osu.Game.Overlays header = new ChangelogHeader(), Streams = new ChangelogStreams(), new ChangelogChart(), - // will need to default to day-sorted content content = new ChangelogContent(), }, }, }, }; - OnLoadComplete += d => FetchChangelog(); // is i + OnLoadComplete += d => FetchChangelog(); Streams.OnSelection = () => { if (Streams.SelectedRelease != null) From c50c946b352d130b10d378850cf1c2f2cdf8a03e Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Fri, 20 Jul 2018 17:24:21 +0200 Subject: [PATCH 0022/2854] Make chevrons updateable --- .../Overlays/Changelog/ChangelogContent.cs | 42 +++++++++---------- .../Changelog/ChangelogContentGroup.cs | 24 ++++++++++- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 23a1c54b51..16e9d1a074 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -14,6 +14,7 @@ namespace osu.Game.Overlays.Changelog { private APIChangelog currentBuild; private APIAccess api; + private ChangelogContentGroup changelogContentGroup; public ChangelogContent() { @@ -27,20 +28,19 @@ namespace osu.Game.Overlays.Changelog }; } - public override void Add(ChangelogContentGroup changelogContentGroup) + private void add(APIChangelog changelogBuild) { - if (changelogContentGroup != null) - { - changelogContentGroup.PreviousRequested = showPrevious; - changelogContentGroup.NextRequested = showNext; - } - base.Add(changelogContentGroup); + Add(changelogContentGroup = new ChangelogContentGroup(changelogBuild) + { + PreviousRequested = showPrevious, + NextRequested = showNext, + }); } public void ShowBuild(APIChangelog changelog) { Clear(); - Add(new ChangelogContentGroup(changelog)); + add(changelog); //fetchChangelogBuild(changelog); fetchChangelogBuild(); } @@ -48,23 +48,19 @@ namespace osu.Game.Overlays.Changelog private void showNext() { if (currentBuild.Versions.Next != null) - { - Clear(); - Add(new ChangelogContentGroup(currentBuild.Versions.Next)); - //fetchChangelogBuild(currentBuild.Versions.Next); - fetchChangelogBuild(); - } + ShowBuild(currentBuild.Versions.Next); } private void showPrevious() { if (currentBuild.Versions.Previous != null) - { - Clear(); - Add(new ChangelogContentGroup(currentBuild.Versions.Previous)); - //fetchChangelogBuild(currentBuild.Versions.Previous); - fetchChangelogBuild(); - } + ShowBuild(currentBuild.Versions.Previous); + } + + private void updateChevronTooltips() + { + changelogContentGroup.UpdateChevronTooltips(currentBuild.Versions.Previous?.DisplayVersion, + currentBuild.Versions.Next?.DisplayVersion); } [BackgroundDependencyLoader] @@ -78,7 +74,11 @@ namespace osu.Game.Overlays.Changelog { //var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); var req = new GetChangelogBuildRequest(); - req.Success += res => currentBuild = res; + req.Success += res => + { + currentBuild = res; + updateChevronTooltips(); + }; api.Queue(req); } } diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 91b6ba0134..5b22b360e5 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -14,6 +14,8 @@ namespace osu.Game.Overlays.Changelog { public class ChangelogContentGroup : FillFlowContainer { + private readonly TooltipIconButton chevronPrevious, chevronNext; + public Action NextRequested, PreviousRequested; public ChangelogContentGroup(APIChangelog build) { @@ -40,7 +42,7 @@ namespace osu.Game.Overlays.Changelog }, Children = new Drawable[] { - new TooltipIconButton + chevronPrevious = new TooltipIconButton { Icon = FontAwesome.fa_chevron_left, Size = new Vector2(24), @@ -77,7 +79,7 @@ namespace osu.Game.Overlays.Changelog }, } }, - new TooltipIconButton + chevronNext = new TooltipIconButton { Icon = FontAwesome.fa_chevron_right, Size = new Vector2(24), @@ -103,6 +105,24 @@ namespace osu.Game.Overlays.Changelog }, }; } + + public void UpdateChevronTooltips(string previousVersion, string nextVersion) + { + if (string.IsNullOrEmpty(previousVersion)) + chevronPrevious.IsEnabled = false; + else + { + chevronPrevious.TooltipText = previousVersion; + chevronPrevious.IsEnabled = true; + } + if (string.IsNullOrEmpty(nextVersion)) + chevronNext.IsEnabled = false; + else + { + chevronNext.TooltipText = nextVersion; + chevronNext.IsEnabled = true; + } + } //public ChangelogContentGroup() { } // for listing } } From b049ffa11d0c1b879ab36d18b1cb31de72cdb447 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Fri, 20 Jul 2018 18:23:25 +0200 Subject: [PATCH 0023/2854] Improve fake-api --- .../API/Requests/GetChangelogBuildRequest.cs | 18 +++++++++--------- .../GetChangelogLatestBuildsRequest.cs | 4 ++-- .../Online/API/Requests/GetChangelogRequest.cs | 4 ++-- .../Overlays/Changelog/ChangelogContent.cs | 9 +++------ .../Changelog/ChangelogContentGroup.cs | 14 ++------------ 5 files changed, 18 insertions(+), 31 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs b/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs index 5d4d3b860c..2338b90865 100644 --- a/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs +++ b/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs @@ -7,16 +7,16 @@ namespace osu.Game.Online.API.Requests { public class GetChangelogBuildRequest : APIRequest { - //private readonly string name; - //private readonly string version; + private readonly string name; + private readonly string version; - //public GetChangelogBuildRequest(string streamName, string buildVersion) - //{ - // name = streamName; - // version = buildVersion; - //} + public GetChangelogBuildRequest(string streamName, string buildVersion) + { + name = streamName; + version = buildVersion; + } - //protected override string Target => $@"changelog/{name}/{version}"; - protected override string Uri => @"https://api.myjson.com/bins/ya5q2"; // for testing + protected override string Target => $@"changelog/{name}/{version}"; + protected override string Uri => $@"https://houtarouoreki.github.io/fake-api/{Target}"; // for testing } } diff --git a/osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs b/osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs index 962c8eab15..7940fd8ff5 100644 --- a/osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs +++ b/osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs @@ -8,7 +8,7 @@ namespace osu.Game.Online.API.Requests { public class GetChangelogLatestBuildsRequest : APIRequest> { - //protected override string Target => @"changelog"; - protected override string Uri => @"https://api.myjson.com/bins/16waui"; // for testing + protected override string Target => @"changelog/latest-builds"; + protected override string Uri => $@"https://houtarouoreki.github.io/fake-api/{Target}"; // for testing } } diff --git a/osu.Game/Online/API/Requests/GetChangelogRequest.cs b/osu.Game/Online/API/Requests/GetChangelogRequest.cs index 6ddff7052e..2f2e0ffe67 100644 --- a/osu.Game/Online/API/Requests/GetChangelogRequest.cs +++ b/osu.Game/Online/API/Requests/GetChangelogRequest.cs @@ -7,7 +7,7 @@ namespace osu.Game.Online.API.Requests { public class GetChangelogRequest : APIRequest { - //protected override string Target => @"changelog"; - protected override string Uri => @"https://api.myjson.com/bins/6zv2i"; // for testing + protected override string Target => @"changelog"; + protected override string Uri => $@"https://houtarouoreki.github.io/fake-api/{Target}"; // for testing } } diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 16e9d1a074..1f89fba9e1 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -41,8 +41,7 @@ namespace osu.Game.Overlays.Changelog { Clear(); add(changelog); - //fetchChangelogBuild(changelog); - fetchChangelogBuild(); + fetchChangelogBuild(changelog); } private void showNext() @@ -69,11 +68,9 @@ namespace osu.Game.Overlays.Changelog this.api = api; } - //private void fetchChangelogBuild(APIChangelog build) - private void fetchChangelogBuild() + private void fetchChangelogBuild(APIChangelog build) { - //var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); - var req = new GetChangelogBuildRequest(); + var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); req.Success += res => { currentBuild = res; diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 5b22b360e5..3b67aa36ec 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -108,20 +108,10 @@ namespace osu.Game.Overlays.Changelog public void UpdateChevronTooltips(string previousVersion, string nextVersion) { - if (string.IsNullOrEmpty(previousVersion)) - chevronPrevious.IsEnabled = false; - else - { + if (!string.IsNullOrEmpty(previousVersion)) chevronPrevious.TooltipText = previousVersion; - chevronPrevious.IsEnabled = true; - } - if (string.IsNullOrEmpty(nextVersion)) - chevronNext.IsEnabled = false; - else - { + if (!string.IsNullOrEmpty(nextVersion)) chevronNext.TooltipText = nextVersion; - chevronNext.IsEnabled = true; - } } //public ChangelogContentGroup() { } // for listing } From 835a8137151e27ddaeb2ab87466ec447ae615d40 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Fri, 20 Jul 2018 19:35:38 +0200 Subject: [PATCH 0024/2854] Improvements to TooltipIconButton Disable sounds when disabled; Remove default tooltip texts --- .../UserInterface/TooltipIconButton.cs | 52 ++++++++++++++++++- .../Changelog/ChangelogContentGroup.cs | 10 +++- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs index 7614c4510a..68413e7460 100644 --- a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -2,16 +2,38 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Containers; +using osu.Framework.Input; +using System; namespace osu.Game.Graphics.UserInterface { - public class TooltipIconButton : OsuClickableContainer, IHasTooltip + // not inheriting osuclickablecontainer/osuhovercontainer + // because click/hover sounds cannot be disabled, and they make + // double sounds when reappearing under the cursor + public class TooltipIconButton : ClickableContainer, IHasTooltip { private readonly SpriteIcon icon; + private SampleChannel sampleClick; + private SampleChannel sampleHover; + public Action Action; + + private bool isEnabled; + public bool IsEnabled + { + get { return isEnabled; } + set + { + isEnabled = value; + icon.Alpha = value ? 1 : 0.5f; + } + } public FontAwesome Icon { @@ -21,6 +43,7 @@ namespace osu.Game.Graphics.UserInterface public TooltipIconButton() { + isEnabled = true; Children = new Drawable[] { new Box @@ -33,10 +56,35 @@ namespace osu.Game.Graphics.UserInterface Origin = Anchor.Centre, Anchor = Anchor.Centre, Size = new Vector2(18), + Alpha = 0.5f, } }; } + protected override bool OnClick(InputState state) + { + if (isEnabled) + { + Action?.Invoke(); + sampleClick?.Play(); + } + return base.OnClick(state); + } + + protected override bool OnHover(InputState state) + { + if (isEnabled) + sampleHover?.Play(); + return base.OnHover(state); + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleClick = audio.Sample.Get(@"UI/generic-select-soft"); + sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); + } + public string TooltipText { get; set; } } } diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 3b67aa36ec..3ec11beb35 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -44,9 +44,9 @@ namespace osu.Game.Overlays.Changelog { chevronPrevious = new TooltipIconButton { + IsEnabled = false, Icon = FontAwesome.fa_chevron_left, Size = new Vector2(24), - TooltipText = "Previous", Action = () => PreviousRequested(), }, new FillFlowContainer @@ -81,9 +81,9 @@ namespace osu.Game.Overlays.Changelog }, chevronNext = new TooltipIconButton { + IsEnabled = false, Icon = FontAwesome.fa_chevron_right, Size = new Vector2(24), - TooltipText = "Next", Action = () => NextRequested(), }, } @@ -109,9 +109,15 @@ namespace osu.Game.Overlays.Changelog public void UpdateChevronTooltips(string previousVersion, string nextVersion) { if (!string.IsNullOrEmpty(previousVersion)) + { chevronPrevious.TooltipText = previousVersion; + chevronPrevious.IsEnabled = true; + } if (!string.IsNullOrEmpty(nextVersion)) + { chevronNext.TooltipText = nextVersion; + chevronNext.IsEnabled = true; + } } //public ChangelogContentGroup() { } // for listing } From 709872d688fa0fe04ada59dea451fb99de54751f Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Fri, 20 Jul 2018 22:11:51 +0200 Subject: [PATCH 0025/2854] Improve showing up builds --- .../UserInterface/TooltipIconButton.cs | 9 ++--- .../Overlays/Changelog/ChangelogContent.cs | 33 ++++++++++++------- osu.Game/Overlays/ChangelogOverlay.cs | 5 +++ 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs index 68413e7460..8b85c8c0ec 100644 --- a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -20,7 +20,6 @@ namespace osu.Game.Graphics.UserInterface public class TooltipIconButton : ClickableContainer, IHasTooltip { private readonly SpriteIcon icon; - private SampleChannel sampleClick; private SampleChannel sampleHover; public Action Action; @@ -55,8 +54,8 @@ namespace osu.Game.Graphics.UserInterface { Origin = Anchor.Centre, Anchor = Anchor.Centre, - Size = new Vector2(18), - Alpha = 0.5f, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.8f), } }; } @@ -64,10 +63,7 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnClick(InputState state) { if (isEnabled) - { Action?.Invoke(); - sampleClick?.Play(); - } return base.OnClick(state); } @@ -81,7 +77,6 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(AudioManager audio) { - sampleClick = audio.Sample.Get(@"UI/generic-select-soft"); sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); } diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 1f89fba9e1..8795c040ef 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -7,12 +7,14 @@ using osu.Framework.Graphics.Containers; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; +using System; namespace osu.Game.Overlays.Changelog { public class ChangelogContent : FillFlowContainer { - private APIChangelog currentBuild; + public APIChangelog CurrentBuild { get; private set; } + public Action OnBuildChanged; private APIAccess api; private ChangelogContentGroup changelogContentGroup; @@ -31,35 +33,42 @@ namespace osu.Game.Overlays.Changelog private void add(APIChangelog changelogBuild) { Add(changelogContentGroup = new ChangelogContentGroup(changelogBuild) - { - PreviousRequested = showPrevious, - NextRequested = showNext, - }); + { + PreviousRequested = showPrevious, + NextRequested = showNext, + }); } public void ShowBuild(APIChangelog changelog) { Clear(); add(changelog); + CurrentBuild = changelog; fetchChangelogBuild(changelog); } + private void showBuild(APIChangelog changelog) + { + ShowBuild(changelog); + OnBuildChanged(); + } + private void showNext() { - if (currentBuild.Versions.Next != null) - ShowBuild(currentBuild.Versions.Next); + if (CurrentBuild.Versions.Next != null) + showBuild(CurrentBuild.Versions.Next); } private void showPrevious() { - if (currentBuild.Versions.Previous != null) - ShowBuild(currentBuild.Versions.Previous); + if (CurrentBuild.Versions.Previous != null) + showBuild(CurrentBuild.Versions.Previous); } private void updateChevronTooltips() { - changelogContentGroup.UpdateChevronTooltips(currentBuild.Versions.Previous?.DisplayVersion, - currentBuild.Versions.Next?.DisplayVersion); + changelogContentGroup.UpdateChevronTooltips(CurrentBuild.Versions.Previous?.DisplayVersion, + CurrentBuild.Versions.Next?.DisplayVersion); } [BackgroundDependencyLoader] @@ -73,7 +82,7 @@ namespace osu.Game.Overlays.Changelog var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); req.Success += res => { - currentBuild = res; + CurrentBuild = res; updateChevronTooltips(); }; api.Queue(req); diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index d98db9de94..5932bbb9c8 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -100,6 +100,11 @@ namespace osu.Game.Overlays foreach (StreamBadge item in Streams.BadgesContainer.Children) item.Deactivate(); }; + content.OnBuildChanged = () => + { + header.ChangelogEntry = content.CurrentBuild; + header.ShowReleaseStream(); + }; } public void ActivateListing() => header.ActivateListing(); From c9f3e72b7a868082fc0d7ac2b97e1ec413a29bfe Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Fri, 20 Jul 2018 22:13:10 +0200 Subject: [PATCH 0026/2854] Unify build div's dimming mechanics with osu-web --- osu.Game/Overlays/Changelog/ChangelogStreams.cs | 12 +++++++++--- osu.Game/Overlays/Changelog/StreamBadge.cs | 14 ++++++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogStreams.cs b/osu.Game/Overlays/Changelog/ChangelogStreams.cs index cc2e1f3f31..1e6e9e1e51 100644 --- a/osu.Game/Overlays/Changelog/ChangelogStreams.cs +++ b/osu.Game/Overlays/Changelog/ChangelogStreams.cs @@ -72,8 +72,10 @@ namespace osu.Game.Overlays.Changelog { if (SelectedRelease != null) { - if (SelectedRelease.UpdateStream.Id != streamBadge.ChangelogEntry.Id) + if (SelectedRelease.UpdateStream.Id != streamBadge.ChangelogEntry.UpdateStream.Id) streamBadge.Deactivate(); + else + streamBadge.EnableDim(); } else streamBadge.Deactivate(); @@ -83,9 +85,13 @@ namespace osu.Game.Overlays.Changelog protected override void OnHoverLost(InputState state) { - if (SelectedRelease == null) - foreach (StreamBadge streamBadge in BadgesContainer.Children) + foreach (StreamBadge streamBadge in BadgesContainer.Children) + { + if (SelectedRelease == null) streamBadge.Activate(true); + else if (streamBadge.ChangelogEntry.UpdateStream.Id == SelectedRelease.UpdateStream.Id) + streamBadge.DisableDim(); + } base.OnHoverLost(state); } } diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index ba98e0a4f7..388245db1a 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -28,6 +28,7 @@ namespace osu.Game.Overlays.Changelog private readonly Header.LineBadge lineBadge; private SampleChannel sampleHover; public readonly APIChangelog ChangelogEntry; + private readonly FillFlowContainer Text; public StreamBadge(APIChangelog changelogEntry) { @@ -38,7 +39,7 @@ namespace osu.Game.Overlays.Changelog isActivated = true; Children = new Drawable[] { - new FillFlowContainer + Text = new FillFlowContainer { AutoSizeAxes = Axes.X, RelativeSizeAxes = Axes.Y, @@ -86,6 +87,7 @@ namespace osu.Game.Overlays.Changelog { isActivated = true; this.FadeIn(transition_duration); + Text.FadeIn(transition_duration); lineBadge.IsCollapsed = false; if (!withoutHeaderUpdate) OnActivation?.Invoke(); @@ -94,6 +96,7 @@ namespace osu.Game.Overlays.Changelog public void Deactivate() { isActivated = false; + DisableDim(); if (!IsHovered) { this.FadeTo(0.5f, transition_duration); @@ -109,7 +112,8 @@ namespace osu.Game.Overlays.Changelog protected override bool OnHover(InputState state) { - if (!isActivated) sampleHover?.Play(); + sampleHover?.Play(); + DisableDim(); this.FadeIn(transition_duration); lineBadge.IsCollapsed = false; return base.OnHover(state); @@ -122,9 +126,15 @@ namespace osu.Game.Overlays.Changelog this.FadeTo(0.5f, transition_duration); lineBadge.IsCollapsed = true; } + else + EnableDim(); base.OnHoverLost(state); } + public void EnableDim() => Text.FadeTo(0.5f, transition_duration); + + public void DisableDim() => Text.FadeIn(transition_duration); + [BackgroundDependencyLoader] private void load(AudioManager audio) { From 311546423863a2442f658ce2f959735168f4076d Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Fri, 20 Jul 2018 22:16:16 +0200 Subject: [PATCH 0027/2854] Remove unnecessary statement; Uncapitalize private member; --- osu.Game/Overlays/Changelog/StreamBadge.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index 388245db1a..80b2fb2a03 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Changelog private readonly Header.LineBadge lineBadge; private SampleChannel sampleHover; public readonly APIChangelog ChangelogEntry; - private readonly FillFlowContainer Text; + private readonly FillFlowContainer text; public StreamBadge(APIChangelog changelogEntry) { @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Changelog isActivated = true; Children = new Drawable[] { - Text = new FillFlowContainer + text = new FillFlowContainer { AutoSizeAxes = Axes.X, RelativeSizeAxes = Axes.Y, @@ -87,7 +87,6 @@ namespace osu.Game.Overlays.Changelog { isActivated = true; this.FadeIn(transition_duration); - Text.FadeIn(transition_duration); lineBadge.IsCollapsed = false; if (!withoutHeaderUpdate) OnActivation?.Invoke(); @@ -131,9 +130,9 @@ namespace osu.Game.Overlays.Changelog base.OnHoverLost(state); } - public void EnableDim() => Text.FadeTo(0.5f, transition_duration); + public void EnableDim() => text.FadeTo(0.5f, transition_duration); - public void DisableDim() => Text.FadeIn(transition_duration); + public void DisableDim() => text.FadeIn(transition_duration); [BackgroundDependencyLoader] private void load(AudioManager audio) From c36a303b363bc6f6eba0fd07a118a2483c38a2cd Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Fri, 20 Jul 2018 23:14:05 +0200 Subject: [PATCH 0028/2854] Fix ChangelogEntries being a list of objects --- osu.Game/Online/API/Requests/Responses/APIChangelog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelog.cs b/osu.Game/Online/API/Requests/Responses/APIChangelog.cs index 02f9bd763d..4ba7077863 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelog.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelog.cs @@ -31,7 +31,7 @@ namespace osu.Game.Online.API.Requests.Responses public UpdateStream UpdateStream { get; set; } [JsonProperty("changelog_entries")] - public List ChangelogEntries { get; set; } + public List ChangelogEntries { get; set; } [JsonProperty("versions")] public Versions Versions { get; set; } From 8d4de68c39e246ed01586a6e5a112d02a55048f6 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Fri, 20 Jul 2018 23:57:46 +0200 Subject: [PATCH 0029/2854] Nullables in APIChangelog; Primitive changelog entries display --- .../API/Requests/Responses/APIChangelog.cs | 14 ++++++------ .../Overlays/Changelog/ChangelogContent.cs | 1 + .../Changelog/ChangelogContentGroup.cs | 22 ++++++++++++++++++- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelog.cs b/osu.Game/Online/API/Requests/Responses/APIChangelog.cs index 4ba7077863..9d3357b071 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelog.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelog.cs @@ -25,7 +25,7 @@ namespace osu.Game.Online.API.Requests.Responses public bool IsFeatured { get; set; } [JsonProperty("created_at")] - public DateTimeOffset CreatedAt { get; set; } + public DateTimeOffset? CreatedAt { get; set; } [JsonProperty("update_stream")] public UpdateStream UpdateStream { get; set; } @@ -49,19 +49,19 @@ namespace osu.Game.Online.API.Requests.Responses public class ChangelogEntry { [JsonProperty("id")] - public long Id { get; set; } + public long? Id { get; set; } [JsonProperty("repository")] public string Repository { get; set; } [JsonProperty("github_pull_request_id")] - public long GithubPullRequestId { get; set; } + public long? GithubPullRequestId { get; set; } [JsonProperty("github_url")] public string GithubUrl { get; set; } [JsonProperty("url")] - public object Url { get; set; } + public string Url { get; set; } [JsonProperty("type")] public string Type { get; set; } @@ -76,10 +76,10 @@ namespace osu.Game.Online.API.Requests.Responses public string MessageHtml { get; set; } [JsonProperty("major")] - public bool Major { get; set; } + public bool? Major { get; set; } [JsonProperty("created_at")] - public DateTimeOffset CreatedAt { get; set; } + public DateTimeOffset? CreatedAt { get; set; } [JsonProperty("github_user")] public GithubUser GithubUser { get; set; } @@ -88,7 +88,7 @@ namespace osu.Game.Online.API.Requests.Responses public class GithubUser { [JsonProperty("id")] - public long Id { get; set; } + public long? Id { get; set; } [JsonProperty("display_name")] public string DisplayName { get; set; } diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 8795c040ef..1bae32b4ef 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -83,6 +83,7 @@ namespace osu.Game.Overlays.Changelog req.Success += res => { CurrentBuild = res; + changelogContentGroup.GenerateText(CurrentBuild.ChangelogEntries); updateChevronTooltips(); }; api.Queue(req); diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 3ec11beb35..bc6428450e 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -9,6 +9,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; using System; +using System.Collections.Generic; namespace osu.Game.Overlays.Changelog { @@ -17,6 +18,8 @@ namespace osu.Game.Overlays.Changelog private readonly TooltipIconButton chevronPrevious, chevronNext; public Action NextRequested, PreviousRequested; + public readonly TextFlowContainer ChangelogEntries; + public ChangelogContentGroup(APIChangelog build) { RelativeSizeAxes = Axes.X; @@ -92,7 +95,8 @@ namespace osu.Game.Overlays.Changelog { // do we need .ToUniversalTime() here? // also, this should be a temporary solution to weekdays in >localized< date strings - Text = build.CreatedAt.Date.ToLongDateString().Replace(build.CreatedAt.ToString("dddd") + ", ", ""), + Text = build.CreatedAt.HasValue ? build.CreatedAt.Value.Date.ToLongDateString() + .Replace(build.CreatedAt.Value.ToString("dddd") + ", ", "") : null, TextSize = 17, // web: 14, Colour = OsuColour.FromHex(@"FD5"), Font = @"Exo2.0-Medium", @@ -103,6 +107,11 @@ namespace osu.Game.Overlays.Changelog Top = 5, }, }, + ChangelogEntries = new TextFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, }; } @@ -119,6 +128,17 @@ namespace osu.Game.Overlays.Changelog chevronNext.IsEnabled = true; } } + + public void GenerateText(List changelogEntries) + { + foreach (ChangelogEntry entry in changelogEntries) + { + ChangelogEntries.AddParagraph(entry.Type); + ChangelogEntries.AddParagraph(entry.Title); + ChangelogEntries.AddText($"({entry.Repository}#{entry.GithubPullRequestId})"); + ChangelogEntries.AddText($"by {entry.GithubUser.DisplayName}"); + } + } //public ChangelogContentGroup() { } // for listing } } From 1492d5cb29d26876c3d3d7d6a25cbcfc5920c66f Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sat, 21 Jul 2018 02:58:53 +0200 Subject: [PATCH 0030/2854] Improve primitive changelog entry formatting --- .../Changelog/ChangelogContentGroup.cs | 65 +++++++++++++++---- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index bc6428450e..9393849d93 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using OpenTK; +using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -18,18 +19,13 @@ namespace osu.Game.Overlays.Changelog private readonly TooltipIconButton chevronPrevious, chevronNext; public Action NextRequested, PreviousRequested; - public readonly TextFlowContainer ChangelogEntries; + public readonly FillFlowContainer ChangelogEntries; public ChangelogContentGroup(APIChangelog build) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; - Padding = new MarginPadding - { - Left = 70, - Right = 70, - }; Children = new Drawable[] { // build version, arrows @@ -95,8 +91,7 @@ namespace osu.Game.Overlays.Changelog { // do we need .ToUniversalTime() here? // also, this should be a temporary solution to weekdays in >localized< date strings - Text = build.CreatedAt.HasValue ? build.CreatedAt.Value.Date.ToLongDateString() - .Replace(build.CreatedAt.Value.ToString("dddd") + ", ", "") : null, + Text = build.CreatedAt.Value.Date.ToLongDateString().Replace(build.CreatedAt.Value.ToString("dddd") + ", ", ""), TextSize = 17, // web: 14, Colour = OsuColour.FromHex(@"FD5"), Font = @"Exo2.0-Medium", @@ -107,10 +102,11 @@ namespace osu.Game.Overlays.Changelog Top = 5, }, }, - ChangelogEntries = new TextFlowContainer + ChangelogEntries = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, }, }; } @@ -133,10 +129,53 @@ namespace osu.Game.Overlays.Changelog { foreach (ChangelogEntry entry in changelogEntries) { - ChangelogEntries.AddParagraph(entry.Type); - ChangelogEntries.AddParagraph(entry.Title); - ChangelogEntries.AddText($"({entry.Repository}#{entry.GithubPullRequestId})"); - ChangelogEntries.AddText($"by {entry.GithubUser.DisplayName}"); + // textflowcontainer is unusable for formatting text + // this has to be a placeholder before we get a + // proper markdown/html formatting.. + // it can't handle overflowing properly + ChangelogEntries.Add(new SpriteText + { + Text = entry.Category, + TextSize = 24, // web: 18, + Font = @"Exo2.0-Bold", + Margin = new MarginPadding { Top = 35, Bottom = 15, }, + }); + ChangelogEntries.Add(new FillFlowContainer + { + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopRight, + Icon = FontAwesome.fa_check, + Size = new Vector2(14), + Margin = new MarginPadding { Top = 2, Right = 4 }, + }, + new TextFlowContainer(t => t.TextSize = 18) + { + Text = entry.Title, + AutoSizeAxes = Axes.Both, + }, + new SpriteText + { + Text = !string.IsNullOrEmpty(entry.Repository) ? + $" ({entry.Repository.Substring(4)}#{entry.GithubPullRequestId})" : + null, + TextSize = 18, + Colour = new Color4(153, 238, 255, 255), + }, + new SpriteText + { + Text = $" by {entry.GithubUser.DisplayName}", + TextSize = 14, // web: 12; + Margin = new MarginPadding { Top = 4, Left = 10, }, + }, + } + }); } } //public ChangelogContentGroup() { } // for listing From ce0029eabf68455d5a24ad6fdb5546512553abf1 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sat, 21 Jul 2018 03:19:30 +0200 Subject: [PATCH 0031/2854] Make build's creation date not nullable --- osu.Game/Online/API/Requests/Responses/APIChangelog.cs | 2 +- osu.Game/Overlays/Changelog/ChangelogContentGroup.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelog.cs b/osu.Game/Online/API/Requests/Responses/APIChangelog.cs index 9d3357b071..da5437515c 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelog.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelog.cs @@ -25,7 +25,7 @@ namespace osu.Game.Online.API.Requests.Responses public bool IsFeatured { get; set; } [JsonProperty("created_at")] - public DateTimeOffset? CreatedAt { get; set; } + public DateTimeOffset CreatedAt { get; set; } [JsonProperty("update_stream")] public UpdateStream UpdateStream { get; set; } diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 9393849d93..7c770dd153 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -91,7 +91,7 @@ namespace osu.Game.Overlays.Changelog { // do we need .ToUniversalTime() here? // also, this should be a temporary solution to weekdays in >localized< date strings - Text = build.CreatedAt.Value.Date.ToLongDateString().Replace(build.CreatedAt.Value.ToString("dddd") + ", ", ""), + Text = build.CreatedAt.Date.ToLongDateString().Replace(build.CreatedAt.ToString("dddd") + ", ", ""), TextSize = 17, // web: 14, Colour = OsuColour.FromHex(@"FD5"), Font = @"Exo2.0-Medium", From 50c4f6ff9521efeddeadd1db9ef59cef95a3e774 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sat, 21 Jul 2018 06:37:42 +0200 Subject: [PATCH 0032/2854] Prevent scrolling to top on build change; Add bottom margin --- osu.Game/Overlays/Changelog/ChangelogContent.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 1bae32b4ef..363b251383 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -25,24 +25,22 @@ namespace osu.Game.Overlays.Changelog Direction = FillDirection.Vertical; Padding = new MarginPadding { - Left = 70, - Right = 70, + Horizontal = 70, + Bottom = 100, }; } private void add(APIChangelog changelogBuild) { - Add(changelogContentGroup = new ChangelogContentGroup(changelogBuild) + Child = changelogContentGroup = new ChangelogContentGroup(changelogBuild) { PreviousRequested = showPrevious, NextRequested = showNext, - }); + }; } public void ShowBuild(APIChangelog changelog) { - Clear(); - add(changelog); CurrentBuild = changelog; fetchChangelogBuild(changelog); } @@ -83,6 +81,7 @@ namespace osu.Game.Overlays.Changelog req.Success += res => { CurrentBuild = res; + add(CurrentBuild); changelogContentGroup.GenerateText(CurrentBuild.ChangelogEntries); updateChevronTooltips(); }; From eca5fb6f052f4906233e1e10f7960133703e0035 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sat, 21 Jul 2018 06:38:02 +0200 Subject: [PATCH 0033/2854] Introduce better formatting to changelog entries; Sort by category --- .../Changelog/ChangelogContentGroup.cs | 79 ++++++++++--------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 7c770dd153..4a6eb27cc7 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; using System; @@ -17,6 +18,8 @@ namespace osu.Game.Overlays.Changelog public class ChangelogContentGroup : FillFlowContainer { private readonly TooltipIconButton chevronPrevious, chevronNext; + private readonly SortedDictionary> categories = + new SortedDictionary>(); public Action NextRequested, PreviousRequested; public readonly FillFlowContainer ChangelogEntries; @@ -97,10 +100,7 @@ namespace osu.Game.Overlays.Changelog Font = @"Exo2.0-Medium", Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Margin = new MarginPadding - { - Top = 5, - }, + Margin = new MarginPadding{ Top = 5, } }, ChangelogEntries = new FillFlowContainer { @@ -127,7 +127,16 @@ namespace osu.Game.Overlays.Changelog public void GenerateText(List changelogEntries) { + // sort entries by category foreach (ChangelogEntry entry in changelogEntries) + { + if (!categories.ContainsKey(entry.Category)) + categories.Add(entry.Category, new List { entry }); + else + categories[entry.Category].Add(entry); + } + + foreach (KeyValuePair> category in categories) { // textflowcontainer is unusable for formatting text // this has to be a placeholder before we get a @@ -135,47 +144,43 @@ namespace osu.Game.Overlays.Changelog // it can't handle overflowing properly ChangelogEntries.Add(new SpriteText { - Text = entry.Category, + Text = category.Key, TextSize = 24, // web: 18, Font = @"Exo2.0-Bold", Margin = new MarginPadding { Top = 35, Bottom = 15, }, }); - ChangelogEntries.Add(new FillFlowContainer + foreach (ChangelogEntry entry in category.Value) { - Direction = FillDirection.Full, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] + OsuTextFlowContainer title; + + ChangelogEntries.Add(title = new OsuTextFlowContainer { - new SpriteIcon + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + LineSpacing = 0.25f, + }); + title.AddIcon(FontAwesome.fa_check, t => { t.TextSize = 12; t.Padding = new MarginPadding { Left = -17, Right = 5 }; }); + title.AddText(entry.Title, t => { t.TextSize = 18; }); //t.Padding = new MarginPadding(10); }); + if (!string.IsNullOrEmpty(entry.Repository)) + { + title.AddText($" ({entry.Repository.Substring(4)}#{entry.GithubPullRequestId})", t => { - Anchor = Anchor.TopLeft, - Origin = Anchor.TopRight, - Icon = FontAwesome.fa_check, - Size = new Vector2(14), - Margin = new MarginPadding { Top = 2, Right = 4 }, - }, - new TextFlowContainer(t => t.TextSize = 18) - { - Text = entry.Title, - AutoSizeAxes = Axes.Both, - }, - new SpriteText - { - Text = !string.IsNullOrEmpty(entry.Repository) ? - $" ({entry.Repository.Substring(4)}#{entry.GithubPullRequestId})" : - null, - TextSize = 18, - Colour = new Color4(153, 238, 255, 255), - }, - new SpriteText - { - Text = $" by {entry.GithubUser.DisplayName}", - TextSize = 14, // web: 12; - Margin = new MarginPadding { Top = 4, Left = 10, }, - }, + t.TextSize = 18; + t.Colour = Color4.SkyBlue; + }); } - }); + title.AddText($" by {entry.GithubUser.DisplayName}", t => t.TextSize = 14); //web: 12; + ChangelogEntries.Add(new SpriteText + { + TextSize = 14, // web: 12, + Colour = new Color4(235, 184, 254, 255), + Text = $"{entry.MessageHtml?.Replace("

", "").Replace("

", "")}\n", + Margin = new MarginPadding { Bottom = 10, }, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }); + } } } //public ChangelogContentGroup() { } // for listing From 78f0f37ee65c7c4666b668c25064daf14b63d6a9 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sat, 21 Jul 2018 07:16:22 +0200 Subject: [PATCH 0034/2854] Change graph's colour on stream selection --- osu.Game/Overlays/Changelog/ChangelogChart.cs | 10 +++++++++- osu.Game/Overlays/ChangelogOverlay.cs | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index b113a509ec..18c74f4b51 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Changelog { @@ -13,13 +14,15 @@ namespace osu.Game.Overlays.Changelog // placeholder json file: https://api.myjson.com/bins/10ye8a public class ChangelogChart : BufferedContainer { + private Box background; + public ChangelogChart() { RelativeSizeAxes = Axes.X; Height = 100; Children = new Drawable[] { - new Box + background = new Box { Colour = StreamColour.STABLE, RelativeSizeAxes = Axes.Both, @@ -33,5 +36,10 @@ namespace osu.Game.Overlays.Changelog }, }; } + + public void ShowChart(APIChangelog releaseStream) + { + background.Colour = StreamColour.FromStreamName(releaseStream.UpdateStream.Name); + } } } diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 5932bbb9c8..f2df54ac99 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -22,6 +22,7 @@ namespace osu.Game.Overlays { private readonly ChangelogHeader header; public readonly ChangelogStreams Streams; + private readonly ChangelogChart chart; private APIChangelog changelogEntry; private APIAccess api; @@ -72,7 +73,7 @@ namespace osu.Game.Overlays { header = new ChangelogHeader(), Streams = new ChangelogStreams(), - new ChangelogChart(), + chart = new ChangelogChart(), content = new ChangelogContent(), }, }, @@ -87,6 +88,7 @@ namespace osu.Game.Overlays } header.ShowReleaseStream(); content.ShowBuild(Streams.SelectedRelease); + chart.ShowChart(Streams.SelectedRelease); }; header.OnListingActivated += () => { From 22af3cd923a9e16e5a4643a511086b9d0b925fa5 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sat, 21 Jul 2018 09:37:58 +0200 Subject: [PATCH 0035/2854] Handle possible null reference; --- osu.Game/Graphics/StreamColour.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/StreamColour.cs b/osu.Game/Graphics/StreamColour.cs index 2a0d7fceab..3ee31c90e6 100644 --- a/osu.Game/Graphics/StreamColour.cs +++ b/osu.Game/Graphics/StreamColour.cs @@ -28,9 +28,10 @@ namespace osu.Game.Graphics public static ColourInfo FromStreamName(string name) { - if (colours.TryGetValue(name, out ColourInfo colour)) - return colour; - else return new Color4(255, 255, 255, 255); + if (!string.IsNullOrEmpty(name)) + if (colours.TryGetValue(name, out ColourInfo colour)) + return colour; + return new Color4(255, 255, 255, 255); } } } From e2af2b0409fe91d7239f69e2fb1e7e77df8ecc32 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sat, 21 Jul 2018 09:39:54 +0200 Subject: [PATCH 0036/2854] Change chart's colour depending on release stream; Show whether it's populated Fix chart requests --- .../API/Requests/GetChangelogChartRequest.cs | 21 +++++++++ .../Requests/Responses/APIChangelogChart.cs | 34 ++++++++++++++ osu.Game/Overlays/Changelog/ChangelogChart.cs | 44 +++++++++++++++++-- 3 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Online/API/Requests/GetChangelogChartRequest.cs create mode 100644 osu.Game/Online/API/Requests/Responses/APIChangelogChart.cs diff --git a/osu.Game/Online/API/Requests/GetChangelogChartRequest.cs b/osu.Game/Online/API/Requests/GetChangelogChartRequest.cs new file mode 100644 index 0000000000..4a9568af0b --- /dev/null +++ b/osu.Game/Online/API/Requests/GetChangelogChartRequest.cs @@ -0,0 +1,21 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Online.API.Requests.Responses; +using System.Collections.Generic; + +namespace osu.Game.Online.API.Requests +{ + public class GetChangelogChartRequest : APIRequest + { + private readonly string updateStream; + + public GetChangelogChartRequest() => updateStream = null; + + public GetChangelogChartRequest(string updateStreamName) => updateStream = updateStreamName; + + protected override string Target => $@"changelog/{(!string.IsNullOrEmpty(updateStream) ? + updateStream + "/" : "")}chart-config"; + protected override string Uri => $@"https://houtarouoreki.github.io/fake-api/{Target}"; // for testing + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelogChart.cs b/osu.Game/Online/API/Requests/Responses/APIChangelogChart.cs new file mode 100644 index 0000000000..aa8c462018 --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APIChangelogChart.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Newtonsoft.Json; +using osu.Framework.Lists; +using System; +using System.Collections.Generic; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APIChangelogChart + { + [JsonProperty("build_history")] + public List BuildHistory { get; set; } + + [JsonProperty("order")] + public List Order { get; set; } + + [JsonProperty("stream_name")] + public string StreamName { get; set; } + } + + public class BuildHistory + { + [JsonProperty("created_at")] + public DateTimeOffset CreatedAt { get; set; } + + [JsonProperty("user_count")] + public long UserCount { get; set; } + + [JsonProperty("label")] + public string Label { get; set; } + } +} diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index 18c74f4b51..cb2d60c649 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -1,11 +1,15 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using OpenTK.Graphics; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Changelog @@ -15,6 +19,8 @@ namespace osu.Game.Overlays.Changelog public class ChangelogChart : BufferedContainer { private Box background; + private SpriteText text; + private APIAccess api; public ChangelogChart() { @@ -27,7 +33,7 @@ namespace osu.Game.Overlays.Changelog Colour = StreamColour.STABLE, RelativeSizeAxes = Axes.Both, }, - new SpriteText + text = new SpriteText { Text = "Graph Placeholder", TextSize = 28, @@ -37,9 +43,41 @@ namespace osu.Game.Overlays.Changelog }; } - public void ShowChart(APIChangelog releaseStream) + public void ShowChart(APIChangelog releaseStream) => fetchAndShowChangelogChart(releaseStream); + + private bool isEmpty(APIChangelogChart changelogChart) { - background.Colour = StreamColour.FromStreamName(releaseStream.UpdateStream.Name); + if (changelogChart != null) + foreach (BuildHistory buildHistory in changelogChart.BuildHistory) + if (buildHistory.UserCount > 0) return false; + return true; + } + + private void showChart(APIChangelogChart chartInfo, string updateStreamName) + { + if (!isEmpty(chartInfo)) + { + background.Colour = StreamColour.FromStreamName(updateStreamName); + text.Text = "Graph placeholder\n(chart is not empty)"; + } + else + { + background.Colour = Color4.Black; + text.Text = "Graph placeholder\n(chart is empty)"; + } + } + + [BackgroundDependencyLoader] + private void load(APIAccess api) + { + this.api = api; + } + + private void fetchAndShowChangelogChart(APIChangelog build) + { + var req = new GetChangelogChartRequest(build.UpdateStream.Name); + req.Success += res => showChart(res, build.UpdateStream.Name); + api.Queue(req); } } } From ba0430752caafec4abfabea92a9b6fe5822ac45a Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sat, 21 Jul 2018 09:45:14 +0200 Subject: [PATCH 0037/2854] Check wether a chart is populated; Fixes to graph's colour setting --- .../API/Requests/GetChangelogChartRequest.cs | 21 +++++++++ .../Requests/Responses/APIChangelogChart.cs | 34 ++++++++++++++ osu.Game/Overlays/Changelog/ChangelogChart.cs | 44 +++++++++++++++++-- 3 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Online/API/Requests/GetChangelogChartRequest.cs create mode 100644 osu.Game/Online/API/Requests/Responses/APIChangelogChart.cs diff --git a/osu.Game/Online/API/Requests/GetChangelogChartRequest.cs b/osu.Game/Online/API/Requests/GetChangelogChartRequest.cs new file mode 100644 index 0000000000..4a9568af0b --- /dev/null +++ b/osu.Game/Online/API/Requests/GetChangelogChartRequest.cs @@ -0,0 +1,21 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Online.API.Requests.Responses; +using System.Collections.Generic; + +namespace osu.Game.Online.API.Requests +{ + public class GetChangelogChartRequest : APIRequest + { + private readonly string updateStream; + + public GetChangelogChartRequest() => updateStream = null; + + public GetChangelogChartRequest(string updateStreamName) => updateStream = updateStreamName; + + protected override string Target => $@"changelog/{(!string.IsNullOrEmpty(updateStream) ? + updateStream + "/" : "")}chart-config"; + protected override string Uri => $@"https://houtarouoreki.github.io/fake-api/{Target}"; // for testing + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelogChart.cs b/osu.Game/Online/API/Requests/Responses/APIChangelogChart.cs new file mode 100644 index 0000000000..aa8c462018 --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APIChangelogChart.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Newtonsoft.Json; +using osu.Framework.Lists; +using System; +using System.Collections.Generic; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APIChangelogChart + { + [JsonProperty("build_history")] + public List BuildHistory { get; set; } + + [JsonProperty("order")] + public List Order { get; set; } + + [JsonProperty("stream_name")] + public string StreamName { get; set; } + } + + public class BuildHistory + { + [JsonProperty("created_at")] + public DateTimeOffset CreatedAt { get; set; } + + [JsonProperty("user_count")] + public long UserCount { get; set; } + + [JsonProperty("label")] + public string Label { get; set; } + } +} diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index 18c74f4b51..cb2d60c649 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -1,11 +1,15 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using OpenTK.Graphics; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Changelog @@ -15,6 +19,8 @@ namespace osu.Game.Overlays.Changelog public class ChangelogChart : BufferedContainer { private Box background; + private SpriteText text; + private APIAccess api; public ChangelogChart() { @@ -27,7 +33,7 @@ namespace osu.Game.Overlays.Changelog Colour = StreamColour.STABLE, RelativeSizeAxes = Axes.Both, }, - new SpriteText + text = new SpriteText { Text = "Graph Placeholder", TextSize = 28, @@ -37,9 +43,41 @@ namespace osu.Game.Overlays.Changelog }; } - public void ShowChart(APIChangelog releaseStream) + public void ShowChart(APIChangelog releaseStream) => fetchAndShowChangelogChart(releaseStream); + + private bool isEmpty(APIChangelogChart changelogChart) { - background.Colour = StreamColour.FromStreamName(releaseStream.UpdateStream.Name); + if (changelogChart != null) + foreach (BuildHistory buildHistory in changelogChart.BuildHistory) + if (buildHistory.UserCount > 0) return false; + return true; + } + + private void showChart(APIChangelogChart chartInfo, string updateStreamName) + { + if (!isEmpty(chartInfo)) + { + background.Colour = StreamColour.FromStreamName(updateStreamName); + text.Text = "Graph placeholder\n(chart is not empty)"; + } + else + { + background.Colour = Color4.Black; + text.Text = "Graph placeholder\n(chart is empty)"; + } + } + + [BackgroundDependencyLoader] + private void load(APIAccess api) + { + this.api = api; + } + + private void fetchAndShowChangelogChart(APIChangelog build) + { + var req = new GetChangelogChartRequest(build.UpdateStream.Name); + req.Success += res => showChart(res, build.UpdateStream.Name); + api.Queue(req); } } } From b5207d65f7ab6fceb76c9ac423f63d4e999818f7 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sat, 21 Jul 2018 10:05:12 +0200 Subject: [PATCH 0038/2854] Show listing graph; Slight refactor --- osu.Game/Graphics/StreamColour.cs | 2 +- .../API/Requests/GetChangelogChartRequest.cs | 1 - .../Requests/Responses/APIChangelogChart.cs | 1 - osu.Game/Overlays/Changelog/ChangelogChart.cs | 21 ++++++++++++++++--- osu.Game/Overlays/ChangelogOverlay.cs | 2 ++ 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/StreamColour.cs b/osu.Game/Graphics/StreamColour.cs index 3ee31c90e6..71626a0e3a 100644 --- a/osu.Game/Graphics/StreamColour.cs +++ b/osu.Game/Graphics/StreamColour.cs @@ -31,7 +31,7 @@ namespace osu.Game.Graphics if (!string.IsNullOrEmpty(name)) if (colours.TryGetValue(name, out ColourInfo colour)) return colour; - return new Color4(255, 255, 255, 255); + return new Color4(0, 0, 0, 255); } } } diff --git a/osu.Game/Online/API/Requests/GetChangelogChartRequest.cs b/osu.Game/Online/API/Requests/GetChangelogChartRequest.cs index 4a9568af0b..1e131fb91f 100644 --- a/osu.Game/Online/API/Requests/GetChangelogChartRequest.cs +++ b/osu.Game/Online/API/Requests/GetChangelogChartRequest.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Online.API.Requests.Responses; -using System.Collections.Generic; namespace osu.Game.Online.API.Requests { diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelogChart.cs b/osu.Game/Online/API/Requests/Responses/APIChangelogChart.cs index aa8c462018..3509ff7de1 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelogChart.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelogChart.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using Newtonsoft.Json; -using osu.Framework.Lists; using System; using System.Collections.Generic; diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index cb2d60c649..b8df166a19 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -18,8 +18,8 @@ namespace osu.Game.Overlays.Changelog // placeholder json file: https://api.myjson.com/bins/10ye8a public class ChangelogChart : BufferedContainer { - private Box background; - private SpriteText text; + private readonly Box background; + private readonly SpriteText text; private APIAccess api; public ChangelogChart() @@ -43,6 +43,14 @@ namespace osu.Game.Overlays.Changelog }; } + /// + /// Draw the graph with all builds + /// + public void ShowChart() => fetchAndShowChangelogChart(); + + /// + /// Draw the graph for a specific build + /// public void ShowChart(APIChangelog releaseStream) => fetchAndShowChangelogChart(releaseStream); private bool isEmpty(APIChangelogChart changelogChart) @@ -53,7 +61,7 @@ namespace osu.Game.Overlays.Changelog return true; } - private void showChart(APIChangelogChart chartInfo, string updateStreamName) + private void showChart(APIChangelogChart chartInfo, string updateStreamName = null) { if (!isEmpty(chartInfo)) { @@ -79,5 +87,12 @@ namespace osu.Game.Overlays.Changelog req.Success += res => showChart(res, build.UpdateStream.Name); api.Queue(req); } + + private void fetchAndShowChangelogChart() + { + var req = new GetChangelogChartRequest(); + req.Success += res => showChart(res); + api.Queue(req); + } } } diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index f2df54ac99..ce2878858f 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -101,6 +101,7 @@ namespace osu.Game.Overlays else foreach (StreamBadge item in Streams.BadgesContainer.Children) item.Deactivate(); + chart.ShowChart(); }; content.OnBuildChanged = () => { @@ -155,6 +156,7 @@ namespace osu.Game.Overlays Streams.BadgesContainer.Clear(); foreach (APIChangelog item in res) Streams.BadgesContainer.Add(new StreamBadge(item)); + chart.ShowChart(); }; api.Queue(req); } From 8f43b883b1509fa46b74ae8c094e022cbfa29893 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sat, 21 Jul 2018 23:00:05 +0200 Subject: [PATCH 0039/2854] Set the right colour for background --- osu.Game/Overlays/ChangelogOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index ce2878858f..cfa669534d 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -58,7 +58,7 @@ namespace osu.Game.Overlays new Box { RelativeSizeAxes = Axes.Both, - Colour = new Color4(20, 18, 23, 255) + Colour = new Color4(49, 36, 54, 255), }, new ScrollContainer { From 8e7efafba31193350088a0f0374060dd8eebad53 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sat, 21 Jul 2018 23:10:42 +0200 Subject: [PATCH 0040/2854] Lower height of the cover image, Adjust height of the purple line --- osu.Game/Overlays/Changelog/ChangelogHeader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 1c3f6f0853..a744fc3c97 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Changelog public APIChangelog ChangelogEntry; - private const float cover_height = 310; + private const float cover_height = 280; private const float title_height = 50; private const float icon_size = 50; private const float icon_margin = 20; @@ -155,7 +155,7 @@ namespace osu.Game.Overlays.Changelog { Colour = Purple, RelativeSizeAxes = Axes.X, - Height = 3, + Height = 2, Anchor = Anchor.BottomLeft, Origin = Anchor.CentreLeft, }, From 80808bddbf049e66e5b38c7e1c899a935d5b857c Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 05:28:43 +0200 Subject: [PATCH 0041/2854] Add changelog listing --- .../API/Requests/GetChangelogRequest.cs | 2 +- .../Overlays/Changelog/ChangelogContent.cs | 66 ++++++++++++++++-- .../Changelog/ChangelogContentGroup.cs | 69 +++++++++++++++++-- osu.Game/Overlays/ChangelogOverlay.cs | 3 +- 4 files changed, 124 insertions(+), 16 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetChangelogRequest.cs b/osu.Game/Online/API/Requests/GetChangelogRequest.cs index 2f2e0ffe67..ec8536c607 100644 --- a/osu.Game/Online/API/Requests/GetChangelogRequest.cs +++ b/osu.Game/Online/API/Requests/GetChangelogRequest.cs @@ -8,6 +8,6 @@ namespace osu.Game.Online.API.Requests public class GetChangelogRequest : APIRequest { protected override string Target => @"changelog"; - protected override string Uri => $@"https://houtarouoreki.github.io/fake-api/{Target}"; // for testing + protected override string Uri => $@"https://houtarouoreki.github.io/fake-api/{Target}/index"; // for testing } } diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 363b251383..a4797d6f7c 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -1,9 +1,11 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -11,7 +13,7 @@ using System; namespace osu.Game.Overlays.Changelog { - public class ChangelogContent : FillFlowContainer + public class ChangelogContent : FillFlowContainer { public APIChangelog CurrentBuild { get; private set; } public Action OnBuildChanged; @@ -23,11 +25,52 @@ namespace osu.Game.Overlays.Changelog RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; - Padding = new MarginPadding + Padding = new MarginPadding{ Bottom = 100, }; + } + + private void add(APIChangelog[] changelog) + { + DateTime currentDate = new DateTime(); + + Clear(); + + foreach (APIChangelog build in changelog) { - Horizontal = 70, - Bottom = 100, - }; + if (build.CreatedAt.Date != currentDate) + { + if (Children.Count != 0) + { + Add(new Box + { + RelativeSizeAxes = Axes.X, + Height = 2, + Colour = new Color4(17, 17, 17, 255), + Margin = new MarginPadding { Top = 30, }, + }); + } + Add(changelogContentGroup = new ChangelogContentGroup(build, true) + { + BuildRequested = () => showBuild(build), + }); + changelogContentGroup.GenerateText(build.ChangelogEntries); + currentDate = build.CreatedAt.Date; + } + else + { + changelogContentGroup.Add(new Box + { + RelativeSizeAxes = Axes.X, + Height = 1, + Colour = new Color4(32, 24, 35, 255), + Margin = new MarginPadding { Top = 30, }, + }); + Add(changelogContentGroup = new ChangelogContentGroup(build, false) + { + BuildRequested = () => ShowBuild(build), + }); + changelogContentGroup.GenerateText(build.ChangelogEntries); + } + } } private void add(APIChangelog changelogBuild) @@ -41,10 +84,12 @@ namespace osu.Game.Overlays.Changelog public void ShowBuild(APIChangelog changelog) { + fetchAndShowChangelogBuild(changelog); CurrentBuild = changelog; - fetchChangelogBuild(changelog); } + public void ShowListing() => fetchAndShowChangelog(); + private void showBuild(APIChangelog changelog) { ShowBuild(changelog); @@ -75,7 +120,14 @@ namespace osu.Game.Overlays.Changelog this.api = api; } - private void fetchChangelogBuild(APIChangelog build) + private void fetchAndShowChangelog() + { + var req = new GetChangelogRequest(); + req.Success += res => add(res); + api.Queue(req); + } + + private void fetchAndShowChangelogBuild(APIChangelog build) { var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); req.Success += res => diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 4a6eb27cc7..0fc4c31bbe 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Changelog private readonly SortedDictionary> categories = new SortedDictionary>(); - public Action NextRequested, PreviousRequested; + public Action NextRequested, PreviousRequested, BuildRequested; public readonly FillFlowContainer ChangelogEntries; public ChangelogContentGroup(APIChangelog build) @@ -29,6 +29,7 @@ namespace osu.Game.Overlays.Changelog RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; + Padding = new MarginPadding { Horizontal = 70 }; Children = new Drawable[] { // build version, arrows @@ -111,6 +112,67 @@ namespace osu.Game.Overlays.Changelog }; } + public ChangelogContentGroup(APIChangelog build, bool newDate = false) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + Padding = new MarginPadding { Horizontal = 70 }; + Children = new Drawable[] + { + new SpriteText + { + // do we need .ToUniversalTime() here? + // also, this should be a temporary solution to weekdays in >localized< date strings + Text = build.CreatedAt.Date.ToLongDateString().Replace(build.CreatedAt.ToString("dddd") + ", ", ""), + TextSize = 28, // web: 24, + Colour = OsuColour.FromHex(@"FD5"), + Font = @"Exo2.0-Light", + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding{ Top = 20, }, + Alpha = newDate ? 1 : 0, + }, + new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding{ Top = 20, }, + Spacing = new Vector2(5), + Children = new Drawable[] + { + new SpriteText + { + Text = build.UpdateStream.DisplayName, + TextSize = 20, // web: 18, + Font = @"Exo2.0-Medium", + }, + new SpriteText + { + Text = build.DisplayVersion, + TextSize = 20, // web: 18, + Font = @"Exo2.0-Light", + Colour = StreamColour.FromStreamName(build.UpdateStream.Name), + }, + new ClickableText + { + Text = " ok ", + TextSize = 20, + Action = BuildRequested, + }, + } + }, + ChangelogEntries = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + }, + }; + } + public void UpdateChevronTooltips(string previousVersion, string nextVersion) { if (!string.IsNullOrEmpty(previousVersion)) @@ -138,10 +200,6 @@ namespace osu.Game.Overlays.Changelog foreach (KeyValuePair> category in categories) { - // textflowcontainer is unusable for formatting text - // this has to be a placeholder before we get a - // proper markdown/html formatting.. - // it can't handle overflowing properly ChangelogEntries.Add(new SpriteText { Text = category.Key, @@ -183,6 +241,5 @@ namespace osu.Game.Overlays.Changelog } } } - //public ChangelogContentGroup() { } // for listing } } diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index cfa669534d..0a2c5e9b6f 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -93,8 +93,7 @@ namespace osu.Game.Overlays header.OnListingActivated += () => { Streams.SelectedRelease = null; - content.Clear(); - // should add listing to content here + content.ShowListing(); if (!Streams.IsHovered) foreach (StreamBadge item in Streams.BadgesContainer.Children) item.Activate(true); From c5bdfb885787cb6350bfa8f87d79c0ec5b7a1c8a Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 05:34:55 +0200 Subject: [PATCH 0042/2854] Use ClickableText for navigating to builds; Fix typo --- osu.Game/Overlays/Changelog/ChangelogContent.cs | 12 +++++++++--- osu.Game/Overlays/Changelog/ChangelogContentGroup.cs | 9 ++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index a4797d6f7c..36f9d3fec6 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -66,7 +66,7 @@ namespace osu.Game.Overlays.Changelog }); Add(changelogContentGroup = new ChangelogContentGroup(build, false) { - BuildRequested = () => ShowBuild(build), + BuildRequested = () => showBuild(build), }); changelogContentGroup.GenerateText(build.ChangelogEntries); } @@ -82,20 +82,26 @@ namespace osu.Game.Overlays.Changelog }; } + /// + /// Doesn't send back that the build has changed + /// public void ShowBuild(APIChangelog changelog) { fetchAndShowChangelogBuild(changelog); CurrentBuild = changelog; } - public void ShowListing() => fetchAndShowChangelog(); - + /// + /// Sends back that the build has changed + /// private void showBuild(APIChangelog changelog) { ShowBuild(changelog); OnBuildChanged(); } + public void ShowListing() => fetchAndShowChangelog(); + private void showNext() { if (CurrentBuild.Versions.Next != null) diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 0fc4c31bbe..36127d585e 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -149,18 +149,13 @@ namespace osu.Game.Overlays.Changelog TextSize = 20, // web: 18, Font = @"Exo2.0-Medium", }, - new SpriteText + new ClickableText { Text = build.DisplayVersion, TextSize = 20, // web: 18, Font = @"Exo2.0-Light", Colour = StreamColour.FromStreamName(build.UpdateStream.Name), - }, - new ClickableText - { - Text = " ok ", - TextSize = 20, - Action = BuildRequested, + Action = () => BuildRequested(), }, } }, From 2d36062159d8b3e5e77740eed2ec3c1305c2b60f Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 06:01:04 +0200 Subject: [PATCH 0043/2854] Add ClickableText --- .../Graphics/UserInterface/ClickableText.cs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 osu.Game/Graphics/UserInterface/ClickableText.cs diff --git a/osu.Game/Graphics/UserInterface/ClickableText.cs b/osu.Game/Graphics/UserInterface/ClickableText.cs new file mode 100644 index 0000000000..8d0e98d687 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/ClickableText.cs @@ -0,0 +1,64 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using System; + +namespace osu.Game.Graphics.UserInterface +{ + public class ClickableText : SpriteText, IHasTooltip + { + private bool isEnabled; + private SampleChannel sampleHover; + private SampleChannel sampleClick; + + public Action Action; + + public bool IsEnabled + { + get { return isEnabled; } + set + { + isEnabled = value; + Alpha = value ? 1 : 0.5f; + } + } + + public ClickableText() => isEnabled = true; + + public override bool HandleMouseInput => true; + + protected override bool OnHover(InputState state) + { + if (isEnabled) + sampleHover?.Play(); + return base.OnHover(state); + } + + protected override bool OnClick(InputState state) + { + if (isEnabled) + { + sampleClick?.Play(); + Action?.Invoke(); + } + return base.OnClick(state); + } + + public string TooltipText { get; set; } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); + } + } +} From 9586ef7b0a28d160d13fb6c7989b8c79f010d103 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 08:28:12 +0200 Subject: [PATCH 0044/2854] ClickableText changes --- .../Visual/TestCaseClickableText.cs | 70 +++++++++++++++++++ .../Graphics/UserInterface/ClickableText.cs | 55 ++++++++++++--- 2 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestCaseClickableText.cs diff --git a/osu.Game.Tests/Visual/TestCaseClickableText.cs b/osu.Game.Tests/Visual/TestCaseClickableText.cs new file mode 100644 index 0000000000..8bf58d8bf4 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseClickableText.cs @@ -0,0 +1,70 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using System; +using System.Collections.Generic; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseClickableText : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { + typeof(ClickableText), + typeof(FillFlowContainer) + }; + + public TestCaseClickableText() + { + ClickableText text; + + Add(new FillFlowContainer + { + Spacing = new Vector2(10), + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new[] + { + new ClickableText + { + Text = "Default", + }, + new ClickableText + { + IsEnabled = false, + Text = "Disabled", + }, + new ClickableText + { + Text = "Without sounds", + IsMuted = true, + }, + new ClickableText + { + Text = "Without click sounds", + IsClickMuted = true, + }, + new ClickableText + { + Text = "Without hover sounds", + IsHoverMuted = true, + }, + text = new ClickableText + { + Text = "Disables after click (Action)", + }, + new ClickableText + { + Text = "Has tooltip", + TooltipText = "Yep", + }, + }, + }); + text.Action = () => text.IsEnabled = false; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/ClickableText.cs b/osu.Game/Graphics/UserInterface/ClickableText.cs index 8d0e98d687..a97569c4c1 100644 --- a/osu.Game/Graphics/UserInterface/ClickableText.cs +++ b/osu.Game/Graphics/UserInterface/ClickableText.cs @@ -1,44 +1,81 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input; +using osu.Game.Graphics.Sprites; using System; namespace osu.Game.Graphics.UserInterface -{ - public class ClickableText : SpriteText, IHasTooltip +{/// +/// +/// + public class ClickableText : OsuSpriteText, IHasTooltip { private bool isEnabled; + private bool isMuted; private SampleChannel sampleHover; private SampleChannel sampleClick; + /// + /// An action that can be set to execute after click. + /// public Action Action; + /// + /// If set to true, a sound will be played on click. + /// + public bool IsClickMuted; + + /// + /// If set to true, a sound will be played on hover. + /// + public bool IsHoverMuted; + + /// + /// If disabled, no sounds will be played and wont execute. + /// True by default. + /// public bool IsEnabled { get { return isEnabled; } set { isEnabled = value; - Alpha = value ? 1 : 0.5f; + this.FadeTo(value ? 1 : 0.5f, 250); } } + /// + /// Whether to play sounds on hover and click. Automatically sets + /// and to the same value.> + /// + public bool IsMuted { + get { return isMuted; } + set + { + IsHoverMuted = value; + IsClickMuted = value; + isMuted = value; + } + } + + /// + /// A text with sounds on hover and click, + /// an action that can be set to execute on click, + /// and a tooltip. + /// public ClickableText() => isEnabled = true; public override bool HandleMouseInput => true; protected override bool OnHover(InputState state) { - if (isEnabled) + if (isEnabled && !IsHoverMuted) sampleHover?.Play(); return base.OnHover(state); } @@ -47,7 +84,8 @@ namespace osu.Game.Graphics.UserInterface { if (isEnabled) { - sampleClick?.Play(); + if (!IsClickMuted) + sampleClick?.Play(); Action?.Invoke(); } return base.OnClick(state); @@ -58,6 +96,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(AudioManager audio) { + sampleClick = audio.Sample.Get(@"UI/generic-select-soft"); sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); } } From 2982fe35870d275d0fdbd93cd11c99fc92ad1130 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 09:19:26 +0200 Subject: [PATCH 0045/2854] Compress Test Case; Remove empty summary --- .../Visual/TestCaseClickableText.cs | 57 +++---------------- .../Graphics/UserInterface/ClickableText.cs | 4 +- 2 files changed, 10 insertions(+), 51 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseClickableText.cs b/osu.Game.Tests/Visual/TestCaseClickableText.cs index 8bf58d8bf4..fb0edf2989 100644 --- a/osu.Game.Tests/Visual/TestCaseClickableText.cs +++ b/osu.Game.Tests/Visual/TestCaseClickableText.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; -using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; using System; @@ -12,57 +10,20 @@ namespace osu.Game.Tests.Visual { public class TestCaseClickableText : OsuTestCase { - public override IReadOnlyList RequiredTypes => new[] { - typeof(ClickableText), - typeof(FillFlowContainer) - }; + public override IReadOnlyList RequiredTypes => new[] { typeof(ClickableText) }; public TestCaseClickableText() { ClickableText text; - Add(new FillFlowContainer - { - Spacing = new Vector2(10), - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Children = new[] - { - new ClickableText - { - Text = "Default", - }, - new ClickableText - { - IsEnabled = false, - Text = "Disabled", - }, - new ClickableText - { - Text = "Without sounds", - IsMuted = true, - }, - new ClickableText - { - Text = "Without click sounds", - IsClickMuted = true, - }, - new ClickableText - { - Text = "Without hover sounds", - IsHoverMuted = true, - }, - text = new ClickableText - { - Text = "Disables after click (Action)", - }, - new ClickableText - { - Text = "Has tooltip", - TooltipText = "Yep", - }, - }, + AddRange(new[] { + new ClickableText{ Text = "Default", }, + new ClickableText{ IsEnabled = false, Text = "Disabled", }, + new ClickableText{ Text = "Without sounds", IsMuted = true, }, + new ClickableText{ Text = "Without click sounds", IsClickMuted = true, }, + new ClickableText{ Text = "Without hover sounds", IsHoverMuted = true, }, + text = new ClickableText{ Text = "Disables after click (Action)", }, + new ClickableText{ Text = "Has tooltip", TooltipText = "Yep", } }); text.Action = () => text.IsEnabled = false; } diff --git a/osu.Game/Graphics/UserInterface/ClickableText.cs b/osu.Game/Graphics/UserInterface/ClickableText.cs index a97569c4c1..f2f9f12228 100644 --- a/osu.Game/Graphics/UserInterface/ClickableText.cs +++ b/osu.Game/Graphics/UserInterface/ClickableText.cs @@ -11,9 +11,7 @@ using osu.Game.Graphics.Sprites; using System; namespace osu.Game.Graphics.UserInterface -{/// -/// -/// +{ public class ClickableText : OsuSpriteText, IHasTooltip { private bool isEnabled; From 2ea5a97784b7c46f4a34e28572ea5662e7829b58 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 09:31:51 +0200 Subject: [PATCH 0046/2854] Fix ClickableText test case --- .../Visual/TestCaseClickableText.cs | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseClickableText.cs b/osu.Game.Tests/Visual/TestCaseClickableText.cs index fb0edf2989..32167bc00a 100644 --- a/osu.Game.Tests/Visual/TestCaseClickableText.cs +++ b/osu.Game.Tests/Visual/TestCaseClickableText.cs @@ -10,20 +10,25 @@ namespace osu.Game.Tests.Visual { public class TestCaseClickableText : OsuTestCase { - public override IReadOnlyList RequiredTypes => new[] { typeof(ClickableText) }; + public override IReadOnlyList RequiredTypes => new[] { typeof(ClickableText), typeof(FillFlowContainer) }; public TestCaseClickableText() { ClickableText text; - AddRange(new[] { - new ClickableText{ Text = "Default", }, - new ClickableText{ IsEnabled = false, Text = "Disabled", }, - new ClickableText{ Text = "Without sounds", IsMuted = true, }, - new ClickableText{ Text = "Without click sounds", IsClickMuted = true, }, - new ClickableText{ Text = "Without hover sounds", IsHoverMuted = true, }, - text = new ClickableText{ Text = "Disables after click (Action)", }, - new ClickableText{ Text = "Has tooltip", TooltipText = "Yep", } + Add(new FillFlowContainer + { + Direction = FillDirection.Vertical, + Children = new[] + { + new ClickableText{ Text = "Default", }, + new ClickableText{ IsEnabled = false, Text = "Disabled", }, + new ClickableText{ Text = "Without sounds", IsMuted = true, }, + new ClickableText{ Text = "Without click sounds", IsClickMuted = true, }, + new ClickableText{ Text = "Without hover sounds", IsHoverMuted = true, }, + text = new ClickableText{ Text = "Disables after click (Action)", }, + new ClickableText{ Text = "Has tooltip", TooltipText = "Yep", } + } }); text.Action = () => text.IsEnabled = false; } From 8de65066dd0f70717ce1bb63ebac7fedd28a439f Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 10:28:53 +0200 Subject: [PATCH 0047/2854] Use shorter expression --- osu.Game/Overlays/Changelog/ChangelogContent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 36f9d3fec6..f2f2c1b73d 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -129,7 +129,7 @@ namespace osu.Game.Overlays.Changelog private void fetchAndShowChangelog() { var req = new GetChangelogRequest(); - req.Success += res => add(res); + req.Success += add; api.Queue(req); } From 7e327fd084ddc6e2b0df9c4349e27d7a69d3cb59 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 12:02:45 +0200 Subject: [PATCH 0048/2854] Use using --- osu.Game.Tests/Visual/TestCaseClickableText.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseClickableText.cs b/osu.Game.Tests/Visual/TestCaseClickableText.cs index 32167bc00a..10b5e1b112 100644 --- a/osu.Game.Tests/Visual/TestCaseClickableText.cs +++ b/osu.Game.Tests/Visual/TestCaseClickableText.cs @@ -12,11 +12,11 @@ namespace osu.Game.Tests.Visual { public override IReadOnlyList RequiredTypes => new[] { typeof(ClickableText), typeof(FillFlowContainer) }; - public TestCaseClickableText() - { - ClickableText text; + ClickableText text; - Add(new FillFlowContainer + protected override void LoadComplete() + { + using (var fillFlowContainer = new FillFlowContainer { Direction = FillDirection.Vertical, Children = new[] @@ -29,8 +29,12 @@ namespace osu.Game.Tests.Visual text = new ClickableText{ Text = "Disables after click (Action)", }, new ClickableText{ Text = "Has tooltip", TooltipText = "Yep", } } - }); + }) + { + Add(fillFlowContainer); + } text.Action = () => text.IsEnabled = false; + base.LoadComplete(); } } } From 59cbc58edd93a04d9ed559e10f88e0a6dff7a09b Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 13:04:39 +0200 Subject: [PATCH 0049/2854] Show changelog by default --- osu.Game/Overlays/ChangelogOverlay.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 0a2c5e9b6f..0af9f57c03 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -74,12 +74,16 @@ namespace osu.Game.Overlays header = new ChangelogHeader(), Streams = new ChangelogStreams(), chart = new ChangelogChart(), - content = new ChangelogContent(), + content = new ChangelogContent() }, }, }, }; - OnLoadComplete += d => FetchChangelog(); + OnLoadComplete += d => + { + FetchChangelog(); + content.ShowListing(); + }; Streams.OnSelection = () => { if (Streams.SelectedRelease != null) From 2b7f657f2c579df5788d1d5789f9952855a8a822 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 14:44:11 +0200 Subject: [PATCH 0050/2854] Place HTML messages in a TextFlowContainer Add missing accessibility modifier in ClickableText test case and fix disposal --- .../Visual/TestCaseClickableText.cs | 31 ++++++------------- .../Changelog/ChangelogContentGroup.cs | 15 +++++---- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseClickableText.cs b/osu.Game.Tests/Visual/TestCaseClickableText.cs index 10b5e1b112..15401724d1 100644 --- a/osu.Game.Tests/Visual/TestCaseClickableText.cs +++ b/osu.Game.Tests/Visual/TestCaseClickableText.cs @@ -12,29 +12,18 @@ namespace osu.Game.Tests.Visual { public override IReadOnlyList RequiredTypes => new[] { typeof(ClickableText), typeof(FillFlowContainer) }; - ClickableText text; - - protected override void LoadComplete() + public TestCaseClickableText() => Child = new FillFlowContainer { - using (var fillFlowContainer = new FillFlowContainer + Children = new[] { - Direction = FillDirection.Vertical, - Children = new[] - { - new ClickableText{ Text = "Default", }, - new ClickableText{ IsEnabled = false, Text = "Disabled", }, - new ClickableText{ Text = "Without sounds", IsMuted = true, }, - new ClickableText{ Text = "Without click sounds", IsClickMuted = true, }, - new ClickableText{ Text = "Without hover sounds", IsHoverMuted = true, }, - text = new ClickableText{ Text = "Disables after click (Action)", }, - new ClickableText{ Text = "Has tooltip", TooltipText = "Yep", } - } - }) - { - Add(fillFlowContainer); + new ClickableText { Text = "Default", }, + new ClickableText { IsEnabled = false, Text = "Disabled", }, + new ClickableText { Text = "Without sounds", IsMuted = true, }, + new ClickableText { Text = "Without click sounds", IsClickMuted = true, }, + new ClickableText { Text = "Without hover sounds", IsHoverMuted = true, }, + new ClickableText { Text = "Disables after click (Action)", }, + new ClickableText { Text = "Has tooltip", TooltipText = "Yep", }, } - text.Action = () => text.IsEnabled = false; - base.LoadComplete(); - } + }; } } diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 36127d585e..504b3f78eb 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -156,6 +156,7 @@ namespace osu.Game.Overlays.Changelog Font = @"Exo2.0-Light", Colour = StreamColour.FromStreamName(build.UpdateStream.Name), Action = () => BuildRequested(), + IsClickMuted = true, }, } }, @@ -211,7 +212,7 @@ namespace osu.Game.Overlays.Changelog Direction = FillDirection.Full, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - LineSpacing = 0.25f, + Margin = new MarginPadding{ Vertical = 5, }, }); title.AddIcon(FontAwesome.fa_check, t => { t.TextSize = 12; t.Padding = new MarginPadding { Left = -17, Right = 5 }; }); title.AddText(entry.Title, t => { t.TextSize = 18; }); //t.Padding = new MarginPadding(10); }); @@ -224,15 +225,17 @@ namespace osu.Game.Overlays.Changelog }); } title.AddText($" by {entry.GithubUser.DisplayName}", t => t.TextSize = 14); //web: 12; - ChangelogEntries.Add(new SpriteText + TextFlowContainer messageContainer; + ChangelogEntries.Add(messageContainer = new OsuTextFlowContainer { - TextSize = 14, // web: 12, - Colour = new Color4(235, 184, 254, 255), - Text = $"{entry.MessageHtml?.Replace("

", "").Replace("

", "")}\n", - Margin = new MarginPadding { Bottom = 10, }, AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, }); + messageContainer.AddText($"{entry.MessageHtml?.Replace("

", "").Replace("

", "")}\n", t => + { + t.TextSize = 14; // web: 12, + t.Colour = new Color4(235, 184, 254, 255); + }); } } } From 421c95156b420c5eda20807419c19393646cc9ab Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 15:16:42 +0200 Subject: [PATCH 0051/2854] Fix ClickableText test case --- osu.Game.Tests/Visual/TestCaseClickableText.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseClickableText.cs b/osu.Game.Tests/Visual/TestCaseClickableText.cs index 15401724d1..60ee764cfc 100644 --- a/osu.Game.Tests/Visual/TestCaseClickableText.cs +++ b/osu.Game.Tests/Visual/TestCaseClickableText.cs @@ -12,6 +12,7 @@ namespace osu.Game.Tests.Visual { public override IReadOnlyList RequiredTypes => new[] { typeof(ClickableText), typeof(FillFlowContainer) }; + private readonly ClickableText text; public TestCaseClickableText() => Child = new FillFlowContainer { Children = new[] @@ -21,7 +22,7 @@ namespace osu.Game.Tests.Visual new ClickableText { Text = "Without sounds", IsMuted = true, }, new ClickableText { Text = "Without click sounds", IsClickMuted = true, }, new ClickableText { Text = "Without hover sounds", IsHoverMuted = true, }, - new ClickableText { Text = "Disables after click (Action)", }, + text = new ClickableText { Text = "Disables after click (Action)", Action = () => text.IsEnabled = false }, new ClickableText { Text = "Has tooltip", TooltipText = "Yep", }, } }; From b3f789e19a0953e4f6a8596a9acfa2d283f11638 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 18:35:29 +0200 Subject: [PATCH 0052/2854] Rewrite ChangelogOverlay.cs --- osu.Game/Overlays/ChangelogOverlay.cs | 88 +++++++++++---------------- 1 file changed, 37 insertions(+), 51 deletions(-) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 0af9f57c03..4359741354 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -13,7 +13,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Input.Bindings; using osu.Game.Online.API; using osu.Game.Online.API.Requests; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Changelog; namespace osu.Game.Overlays @@ -21,13 +20,15 @@ namespace osu.Game.Overlays public class ChangelogOverlay : WaveOverlayContainer { private readonly ChangelogHeader header; - public readonly ChangelogStreams Streams; + private readonly ChangelogBadges badges; private readonly ChangelogChart chart; - private APIChangelog changelogEntry; + private readonly ChangelogContent content; + + private readonly Color4 purple = new Color4(191, 4, 255, 255); private APIAccess api; - protected readonly Color4 Purple = new Color4(191, 4, 255, 255); + private bool isAtListing; public ChangelogOverlay() { @@ -43,8 +44,6 @@ namespace osu.Game.Overlays Width = 0.85f; Masking = true; - ChangelogContent content; // told by appveyor to convert to local variable.. - EdgeEffect = new EdgeEffectParameters { Colour = Color4.Black.Opacity(0), @@ -72,49 +71,22 @@ namespace osu.Game.Overlays Children = new Drawable[] { header = new ChangelogHeader(), - Streams = new ChangelogStreams(), + badges = new ChangelogBadges(), chart = new ChangelogChart(), content = new ChangelogContent() }, }, }, }; - OnLoadComplete += d => - { - FetchChangelog(); - content.ShowListing(); - }; - Streams.OnSelection = () => - { - if (Streams.SelectedRelease != null) - { - header.ChangelogEntry = Streams.SelectedRelease; - } - header.ShowReleaseStream(); - content.ShowBuild(Streams.SelectedRelease); - chart.ShowChart(Streams.SelectedRelease); - }; - header.OnListingActivated += () => - { - Streams.SelectedRelease = null; - content.ShowListing(); - if (!Streams.IsHovered) - foreach (StreamBadge item in Streams.BadgesContainer.Children) - item.Activate(true); - else - foreach (StreamBadge item in Streams.BadgesContainer.Children) - item.Deactivate(); - chart.ShowChart(); - }; - content.OnBuildChanged = () => - { - header.ChangelogEntry = content.CurrentBuild; - header.ShowReleaseStream(); - }; + // content.ShowListing(); + // if (!Streams.IsHovered) + // foreach (StreamBadge item in Streams.BadgesContainer.Children) + // item.Activate(true); + // else + // foreach (StreamBadge item in Streams.BadgesContainer.Children) + // item.Deactivate(); } - public void ActivateListing() => header.ActivateListing(); - // receive input outside our bounds so we can trigger a close event on ourselves. public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; @@ -123,10 +95,10 @@ namespace osu.Game.Overlays switch (action) { case GlobalAction.Back: - if (header.IsListingActivated()) + if (isAtListing) State = Visibility.Hidden; else - header.ActivateListing(); + FetchAndShowListing(); return true; } @@ -151,16 +123,30 @@ namespace osu.Game.Overlays this.api = api; } - public void FetchChangelog() + /// + /// Fetches and shows changelog listing. + /// + public void FetchAndShowListing() { var req = new GetChangelogLatestBuildsRequest(); - req.Success += res => - { - Streams.BadgesContainer.Clear(); - foreach (APIChangelog item in res) - Streams.BadgesContainer.Add(new StreamBadge(item)); - chart.ShowChart(); - }; + header.ShowListing(); + badges.SelectNone(); + chart.ShowAllUpdateStreams(); + req.Success += content.ShowListing; + api.Queue(req); + } + + /// + /// Fetches and shows a specific build from a specific update stream. + /// + /// If true, will select fetched build's update stream badge. + public void FetchAndShowBuild(string updateStream, string version) + { + var req = new GetChangelogBuildRequest(updateStream, version); + header.ShowBuild(updateStream, version); + badges.SelectBadge(updateStream); + chart.ShowUpdateStream(updateStream); + req.Success += content.ShowBuild; api.Queue(req); } } From 797abd558f38647e042d283138c234addf1a6c87 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 18:44:45 +0200 Subject: [PATCH 0053/2854] Rewrite ChangelogHeader.cs --- .../Overlays/Changelog/ChangelogHeader.cs | 42 +++++-------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index a744fc3c97..6a7ccacdc3 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -26,9 +26,7 @@ namespace osu.Game.Overlays.Changelog private readonly TextBadgePairListing listing; private readonly TextBadgePairRelease releaseStream; - public Action OnListingActivated; - - public APIChangelog ChangelogEntry; + public Action ListingActivated; private const float cover_height = 280; private const float title_height = 50; @@ -160,41 +158,21 @@ namespace osu.Game.Overlays.Changelog Origin = Anchor.CentreLeft, }, }; - - // is this a bad way to do this? - OnLoadComplete = d => - { - releaseStream.OnActivation = () => - { - listing.Deactivate(); - chevron.MoveToX(0, 100).FadeIn(100); - }; - listing.OnActivation = () => - { - releaseStream.Deactivate(); - chevron.MoveToX(-20, 100).FadeOut(100); - changeHeaderText("Listing"); - OnListingActivated?.Invoke(); - }; - }; } - public void ShowReleaseStream() + public void ShowBuild(string updateStream, string version) { - releaseStream.Activate(String.Join(" ", - ChangelogEntry.UpdateStream.DisplayName, ChangelogEntry.DisplayVersion)); - changeHeaderText(ChangelogEntry.UpdateStream.DisplayName); - } - - private void changeHeaderText(string headerText) - { - titleStream.Text = headerText; + listing.Deactivate(); + releaseStream.Activate($"{updateStream} {version}"); + titleStream.Text = updateStream; titleStream.FlashColour(Color4.White, 500, Easing.OutQuad); } - public void ActivateListing() => listing.Activate(); - - public bool IsListingActivated() => listing.IsActivated; + public void ShowListing() + { + releaseStream.Deactivate(); + listing.Activate(); + } [BackgroundDependencyLoader] private void load(TextureStore textures) From a8bbcca0e06de761ddce0eac18024e00001df466 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 19:51:31 +0200 Subject: [PATCH 0054/2854] Rewrite ChangelogBadges --- .../Overlays/Changelog/ChangelogBadges.cs | 130 ++++++++++++++++++ osu.Game/Overlays/Changelog/StreamBadge.cs | 8 +- 2 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Overlays/Changelog/ChangelogBadges.cs diff --git a/osu.Game/Overlays/Changelog/ChangelogBadges.cs b/osu.Game/Overlays/Changelog/ChangelogBadges.cs new file mode 100644 index 0000000000..8bd52e76bc --- /dev/null +++ b/osu.Game/Overlays/Changelog/ChangelogBadges.cs @@ -0,0 +1,130 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Online.API.Requests.Responses; +using System; +using System.Collections.Generic; + +namespace osu.Game.Overlays.Changelog +{ + public class ChangelogBadges : Container + { + private const float container_height = 106.5f; + private const float padding_y = 20; + private const float padding_x = 85; + + public delegate void SelectionHandler(string updateStream, string version, EventArgs args); + + public event SelectionHandler Selected; + + private readonly FillFlowContainer badgesContainer; + + public ChangelogBadges() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new Color4(32, 24, 35, 255), + }, + badgesContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Top = padding_y, + Bottom = padding_y, + Left = padding_x, + Right = padding_x, + }, + }, + }; + //foreach (StreamBadge streamBadge in BadgesContainer.Children) + //{ + // streamBadge.OnActivation = () => + // { + // SelectedRelease = streamBadge.ChangelogEntry; + // foreach (StreamBadge item in BadgesContainer.Children) + // if (item.ChangelogEntry.Id != streamBadge.ChangelogEntry.Id) + // item.Deactivate(); + // OnSelection?.Invoke(); + // }; + //} + } + + public void Populate(List latestBuilds) + { + foreach (APIChangelog updateStream in latestBuilds) + { + var streamBadge = new StreamBadge(updateStream); + streamBadge.Selected += OnBadgeSelected; + badgesContainer.Add(streamBadge); + } + } + + public void SelectNone() + { + foreach (StreamBadge streamBadge in badgesContainer) + streamBadge.Deactivate(); + } + + public void SelectUpdateStream(string updateStream) + { + foreach (StreamBadge streamBadge in badgesContainer) + if (streamBadge.ChangelogEntry.UpdateStream.Name == updateStream) + { + streamBadge.Activate(); + return; + } + } + + private void OnBadgeSelected(StreamBadge source, EventArgs args) + { + OnSelected(source); + } + + protected virtual void OnSelected(StreamBadge source) + { + if (Selected != null) + Selected(source.ChangelogEntry.UpdateStream.Name, source.ChangelogEntry.Version, EventArgs.Empty); + } + + //protected override bool OnHover(InputState state) + //{ + // foreach (StreamBadge streamBadge in BadgesContainer.Children) + // { + // if (SelectedRelease != null) + // { + // if (SelectedRelease.UpdateStream.Id != streamBadge.ChangelogEntry.UpdateStream.Id) + // streamBadge.Deactivate(); + // else + // streamBadge.EnableDim(); + // } + // else + // streamBadge.Deactivate(); + // } + // return base.OnHover(state); + //} + + //protected override void OnHoverLost(InputState state) + //{ + // foreach (StreamBadge streamBadge in BadgesContainer.Children) + // { + // if (SelectedRelease == null) + // streamBadge.Activate(true); + // else if (streamBadge.ChangelogEntry.UpdateStream.Id == SelectedRelease.UpdateStream.Id) + // streamBadge.DisableDim(); + // } + // base.OnHoverLost(state); + //} + } +} diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index 80b2fb2a03..8294e6c403 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -21,7 +21,9 @@ namespace osu.Game.Overlays.Changelog private const float badge_width = 100; private const float transition_duration = 100; - public Action OnActivation; + public delegate void SelectedHandler(StreamBadge source, EventArgs args); + + public event SelectedHandler Selected; private bool isActivated; @@ -88,8 +90,8 @@ namespace osu.Game.Overlays.Changelog isActivated = true; this.FadeIn(transition_duration); lineBadge.IsCollapsed = false; - if (!withoutHeaderUpdate) - OnActivation?.Invoke(); + if (!withoutHeaderUpdate && Selected != null) + Selected(this, EventArgs.Empty); } public void Deactivate() From eca6265186d7da9e14eafe9dab47b32a6ff5c6ff Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 20:14:28 +0200 Subject: [PATCH 0055/2854] Rewrite ChangelogContent.cs --- .../Overlays/Changelog/ChangelogContent.cs | 94 +++---------------- 1 file changed, 15 insertions(+), 79 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index f2f2c1b73d..d06ef32420 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -15,11 +15,13 @@ namespace osu.Game.Overlays.Changelog { public class ChangelogContent : FillFlowContainer { - public APIChangelog CurrentBuild { get; private set; } - public Action OnBuildChanged; private APIAccess api; private ChangelogContentGroup changelogContentGroup; + public delegate void BuildSelectedEventHandler(string updateStream, string version, EventArgs args); + + public event BuildSelectedEventHandler BuildSelected; + public ChangelogContent() { RelativeSizeAxes = Axes.X; @@ -48,10 +50,9 @@ namespace osu.Game.Overlays.Changelog Margin = new MarginPadding { Top = 30, }, }); } - Add(changelogContentGroup = new ChangelogContentGroup(build, true) - { - BuildRequested = () => showBuild(build), - }); + // watch out for this? + Add(changelogContentGroup = new ChangelogContentGroup(build, true)); + changelogContentGroup.BuildSelected += OnBuildSelected; changelogContentGroup.GenerateText(build.ChangelogEntries); currentDate = build.CreatedAt.Date; } @@ -64,86 +65,21 @@ namespace osu.Game.Overlays.Changelog Colour = new Color4(32, 24, 35, 255), Margin = new MarginPadding { Top = 30, }, }); - Add(changelogContentGroup = new ChangelogContentGroup(build, false) - { - BuildRequested = () => showBuild(build), - }); + Add(changelogContentGroup = new ChangelogContentGroup(build, false)); + changelogContentGroup.BuildSelected += OnBuildSelected; changelogContentGroup.GenerateText(build.ChangelogEntries); } } } + private void OnBuildSelected(string updateStream, string version, EventArgs args) + { + throw new NotImplementedException(); + } + private void add(APIChangelog changelogBuild) { - Child = changelogContentGroup = new ChangelogContentGroup(changelogBuild) - { - PreviousRequested = showPrevious, - NextRequested = showNext, - }; - } - - /// - /// Doesn't send back that the build has changed - /// - public void ShowBuild(APIChangelog changelog) - { - fetchAndShowChangelogBuild(changelog); - CurrentBuild = changelog; - } - - /// - /// Sends back that the build has changed - /// - private void showBuild(APIChangelog changelog) - { - ShowBuild(changelog); - OnBuildChanged(); - } - - public void ShowListing() => fetchAndShowChangelog(); - - private void showNext() - { - if (CurrentBuild.Versions.Next != null) - showBuild(CurrentBuild.Versions.Next); - } - - private void showPrevious() - { - if (CurrentBuild.Versions.Previous != null) - showBuild(CurrentBuild.Versions.Previous); - } - - private void updateChevronTooltips() - { - changelogContentGroup.UpdateChevronTooltips(CurrentBuild.Versions.Previous?.DisplayVersion, - CurrentBuild.Versions.Next?.DisplayVersion); - } - - [BackgroundDependencyLoader] - private void load(APIAccess api) - { - this.api = api; - } - - private void fetchAndShowChangelog() - { - var req = new GetChangelogRequest(); - req.Success += add; - api.Queue(req); - } - - private void fetchAndShowChangelogBuild(APIChangelog build) - { - var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); - req.Success += res => - { - CurrentBuild = res; - add(CurrentBuild); - changelogContentGroup.GenerateText(CurrentBuild.ChangelogEntries); - updateChevronTooltips(); - }; - api.Queue(req); + Child = changelogContentGroup = new ChangelogContentGroup(changelogBuild); } } } From 6683d4cabeac6446c1b402129716f2ece6bafe8c Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 20:14:58 +0200 Subject: [PATCH 0056/2854] Add event handling in content groups --- .../Changelog/ChangelogContentGroup.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 504b3f78eb..0efa8b70c6 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -21,7 +21,10 @@ namespace osu.Game.Overlays.Changelog private readonly SortedDictionary> categories = new SortedDictionary>(); - public Action NextRequested, PreviousRequested, BuildRequested; + public delegate void BuildSelectedEventHandler(string updateStream, string version, EventArgs args); + + public event BuildSelectedEventHandler BuildSelected; + public readonly FillFlowContainer ChangelogEntries; public ChangelogContentGroup(APIChangelog build) @@ -50,7 +53,8 @@ namespace osu.Game.Overlays.Changelog IsEnabled = false, Icon = FontAwesome.fa_chevron_left, Size = new Vector2(24), - Action = () => PreviousRequested(), + Action = () => OnBuildSelected(build.Versions.Previous.UpdateStream.Name, + build.Versions.Previous.Version), }, new FillFlowContainer { @@ -87,7 +91,8 @@ namespace osu.Game.Overlays.Changelog IsEnabled = false, Icon = FontAwesome.fa_chevron_right, Size = new Vector2(24), - Action = () => NextRequested(), + Action = () => OnBuildSelected(build.Versions.Next.UpdateStream.Name, + build.Versions.Next.Version), }, } }, @@ -155,7 +160,7 @@ namespace osu.Game.Overlays.Changelog TextSize = 20, // web: 18, Font = @"Exo2.0-Light", Colour = StreamColour.FromStreamName(build.UpdateStream.Name), - Action = () => BuildRequested(), + Action = () => OnBuildSelected(build.UpdateStream.Name, build.Version), IsClickMuted = true, }, } @@ -183,6 +188,12 @@ namespace osu.Game.Overlays.Changelog } } + protected virtual void OnBuildSelected(string updateStream, string version) + { + if (BuildSelected != null) + BuildSelected(updateStream, version, EventArgs.Empty); + } + public void GenerateText(List changelogEntries) { // sort entries by category From 23309b3b0045abe3fef10c93869761b97a57f48c Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 20:27:20 +0200 Subject: [PATCH 0057/2854] Strip test case --- osu.Game.Tests/Visual/TestCaseChangelog.cs | 55 ---------------------- 1 file changed, 55 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChangelog.cs b/osu.Game.Tests/Visual/TestCaseChangelog.cs index ea6aa8086f..0710f7a254 100644 --- a/osu.Game.Tests/Visual/TestCaseChangelog.cs +++ b/osu.Game.Tests/Visual/TestCaseChangelog.cs @@ -10,67 +10,12 @@ namespace osu.Game.Tests.Visual public class TestCaseChangelog : OsuTestCase { private ChangelogOverlay changelog; - private int index; - private void indexIncrement() => index = index >= changelog.Streams.BadgesContainer.Children.Count - 1 ? 0 : index + 1; - private bool isLoaded => changelog.Streams.BadgesContainer.Children.Count > 0; protected override void LoadComplete() { base.LoadComplete(); Add(changelog = new ChangelogOverlay()); - - AddStep(@"Show", changelog.Show); - AddRepeatStep(@"Toggle Release Stream", () => - { - if (isLoaded) - changelog.Streams.BadgesContainer.Children[index].Activate(); - indexIncrement(); - }, 6); - AddStep(@"Listing", changelog.ActivateListing); - AddStep(@"Hide", changelog.Hide); - AddWaitStep(3); - AddStep(@"Show with Release Stream", () => - { - if (isLoaded) - changelog.Streams.BadgesContainer.Children[index].Activate(); - changelog.Show(); - indexIncrement(); - }); - AddWaitStep(3); - AddStep(@"Hide", changelog.Hide); - AddWaitStep(3); - AddStep(@"Show with listing", () => - { - changelog.ActivateListing(); - changelog.Show(); - }); - AddWaitStep(3); - AddStep(@"Hide", changelog.Hide); - AddWaitStep(3); - AddStep(@"Activate release", () => - { - if (isLoaded) - changelog.Streams.BadgesContainer.Children[index].Activate(); - indexIncrement(); - }); - AddStep(@"Show with listing", () => - { - changelog.ActivateListing(); - changelog.Show(); - }); - AddStep(@"Activate Release", () => - { - if (isLoaded) - changelog.Streams.BadgesContainer.Children[index].Activate(); - }); - AddStep(@"Activate Listing", changelog.ActivateListing); - AddStep(@"Activate Release", () => - { - if (isLoaded) - changelog.Streams.BadgesContainer.Children[index].Activate(); - indexIncrement(); - }); } } } From 61a8b98d32fe7a3295354ac6c1fc2787b14feec6 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 20:27:50 +0200 Subject: [PATCH 0058/2854] Rewrite improvements --- osu.Game/Overlays/Changelog/ChangelogChart.cs | 14 +-- .../Overlays/Changelog/ChangelogContent.cs | 20 ++-- .../Overlays/Changelog/ChangelogStreams.cs | 98 ------------------- osu.Game/Overlays/ChangelogOverlay.cs | 10 +- 4 files changed, 24 insertions(+), 118 deletions(-) delete mode 100644 osu.Game/Overlays/Changelog/ChangelogStreams.cs diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index b8df166a19..91e2aa5e49 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -43,15 +43,9 @@ namespace osu.Game.Overlays.Changelog }; } - /// - /// Draw the graph with all builds - /// - public void ShowChart() => fetchAndShowChangelogChart(); - /// /// Draw the graph for a specific build /// - public void ShowChart(APIChangelog releaseStream) => fetchAndShowChangelogChart(releaseStream); private bool isEmpty(APIChangelogChart changelogChart) { @@ -81,14 +75,14 @@ namespace osu.Game.Overlays.Changelog this.api = api; } - private void fetchAndShowChangelogChart(APIChangelog build) + public void ShowUpdateStream(string updateStream) { - var req = new GetChangelogChartRequest(build.UpdateStream.Name); - req.Success += res => showChart(res, build.UpdateStream.Name); + var req = new GetChangelogChartRequest(updateStream); + req.Success += res => showChart(res, updateStream); api.Queue(req); } - private void fetchAndShowChangelogChart() + public void ShowAllUpdateStreams() { var req = new GetChangelogChartRequest(); req.Success += res => showChart(res); diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index d06ef32420..5eb19119ec 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -10,6 +10,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using System; +using System.Collections.Generic; namespace osu.Game.Overlays.Changelog { @@ -30,7 +31,7 @@ namespace osu.Game.Overlays.Changelog Padding = new MarginPadding{ Bottom = 100, }; } - private void add(APIChangelog[] changelog) + public void ShowListing(List changelog) { DateTime currentDate = new DateTime(); @@ -52,7 +53,7 @@ namespace osu.Game.Overlays.Changelog } // watch out for this? Add(changelogContentGroup = new ChangelogContentGroup(build, true)); - changelogContentGroup.BuildSelected += OnBuildSelected; + changelogContentGroup.BuildSelected += onBuildSelected; changelogContentGroup.GenerateText(build.ChangelogEntries); currentDate = build.CreatedAt.Date; } @@ -66,20 +67,21 @@ namespace osu.Game.Overlays.Changelog Margin = new MarginPadding { Top = 30, }, }); Add(changelogContentGroup = new ChangelogContentGroup(build, false)); - changelogContentGroup.BuildSelected += OnBuildSelected; + changelogContentGroup.BuildSelected += onBuildSelected; changelogContentGroup.GenerateText(build.ChangelogEntries); } } } - private void OnBuildSelected(string updateStream, string version, EventArgs args) - { - throw new NotImplementedException(); - } - - private void add(APIChangelog changelogBuild) + public void ShowBuild(APIChangelog changelogBuild) { Child = changelogContentGroup = new ChangelogContentGroup(changelogBuild); } + + protected virtual void onBuildSelected(string updateStream, string version, EventArgs args) + { + if (BuildSelected != null) + BuildSelected(updateStream, version, EventArgs.Empty); + } } } diff --git a/osu.Game/Overlays/Changelog/ChangelogStreams.cs b/osu.Game/Overlays/Changelog/ChangelogStreams.cs deleted file mode 100644 index 1e6e9e1e51..0000000000 --- a/osu.Game/Overlays/Changelog/ChangelogStreams.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Online.API.Requests.Responses; -using System; - -namespace osu.Game.Overlays.Changelog -{ - public class ChangelogStreams : Container - { - private const float container_height = 106.5f; - private const float padding_y = 20; - private const float padding_x = 85; - public Action OnSelection; - - public APIChangelog SelectedRelease; - // not using SelectedRelease as a Bindable and then using .OnValueChange instead of OnSelection - // because it doesn't "refresh" the selection if the same stream is chosen - - public readonly FillFlowContainer BadgesContainer; - - public ChangelogStreams() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = new Color4(32, 24, 35, 255), - }, - BadgesContainer = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding - { - Top = padding_y, - Bottom = padding_y, - Left = padding_x, - Right = padding_x, - }, - }, - }; - // ok, so this is probably not the best. - // how else can this be done? - BadgesContainer.OnUpdate = d => - { - foreach (StreamBadge streamBadge in BadgesContainer.Children) - { - streamBadge.OnActivation = () => - { - SelectedRelease = streamBadge.ChangelogEntry; - foreach (StreamBadge item in BadgesContainer.Children) - if (item.ChangelogEntry.Id != streamBadge.ChangelogEntry.Id) - item.Deactivate(); - OnSelection?.Invoke(); - }; - } - }; - } - - protected override bool OnHover(InputState state) - { - foreach (StreamBadge streamBadge in BadgesContainer.Children) - { - if (SelectedRelease != null) - { - if (SelectedRelease.UpdateStream.Id != streamBadge.ChangelogEntry.UpdateStream.Id) - streamBadge.Deactivate(); - else - streamBadge.EnableDim(); - } - else - streamBadge.Deactivate(); - } - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - foreach (StreamBadge streamBadge in BadgesContainer.Children) - { - if (SelectedRelease == null) - streamBadge.Activate(true); - else if (streamBadge.ChangelogEntry.UpdateStream.Id == SelectedRelease.UpdateStream.Id) - streamBadge.DisableDim(); - } - base.OnHoverLost(state); - } - } -} diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 4359741354..2ae5bbdd78 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -14,6 +14,7 @@ using osu.Game.Input.Bindings; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Changelog; +using System; namespace osu.Game.Overlays { @@ -78,6 +79,8 @@ namespace osu.Game.Overlays }, }, }; + + badges.Selected += onBuildSelected; // content.ShowListing(); // if (!Streams.IsHovered) // foreach (StreamBadge item in Streams.BadgesContainer.Children) @@ -117,6 +120,11 @@ namespace osu.Game.Overlays FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); } + private void onBuildSelected(string updateStream, string version, EventArgs e) + { + FetchAndShowBuild(updateStream, version); + } + [BackgroundDependencyLoader] private void load(APIAccess api) { @@ -144,7 +152,7 @@ namespace osu.Game.Overlays { var req = new GetChangelogBuildRequest(updateStream, version); header.ShowBuild(updateStream, version); - badges.SelectBadge(updateStream); + badges.SelectUpdateStream(updateStream); chart.ShowUpdateStream(updateStream); req.Success += content.ShowBuild; api.Queue(req); From 24abec43c1ff780fd017321434b9504649e34f6b Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 21:00:29 +0200 Subject: [PATCH 0059/2854] Show on test case --- osu.Game.Tests/Visual/TestCaseChangelog.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/TestCaseChangelog.cs b/osu.Game.Tests/Visual/TestCaseChangelog.cs index 0710f7a254..da260c239b 100644 --- a/osu.Game.Tests/Visual/TestCaseChangelog.cs +++ b/osu.Game.Tests/Visual/TestCaseChangelog.cs @@ -16,6 +16,7 @@ namespace osu.Game.Tests.Visual base.LoadComplete(); Add(changelog = new ChangelogOverlay()); + AddStep(@"Show", changelog.Show); } } } From 51eec0dca44e11e7a9c1ae29c4d1ce451c5db325 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 22:13:14 +0200 Subject: [PATCH 0060/2854] Further rewrite --- .../Overlays/Changelog/ChangelogBadges.cs | 79 +++++++++++-------- .../Overlays/Changelog/ChangelogContent.cs | 3 +- .../Overlays/Changelog/ChangelogHeader.cs | 6 +- osu.Game/Overlays/Changelog/StreamBadge.cs | 6 +- osu.Game/Overlays/ChangelogOverlay.cs | 30 ++++--- 5 files changed, 71 insertions(+), 53 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogBadges.cs b/osu.Game/Overlays/Changelog/ChangelogBadges.cs index 8bd52e76bc..53f6a75bbc 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBadges.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBadges.cs @@ -23,6 +23,7 @@ namespace osu.Game.Overlays.Changelog public event SelectionHandler Selected; private readonly FillFlowContainer badgesContainer; + private long selectedStreamId = -1; public ChangelogBadges() { @@ -66,29 +67,43 @@ namespace osu.Game.Overlays.Changelog foreach (APIChangelog updateStream in latestBuilds) { var streamBadge = new StreamBadge(updateStream); - streamBadge.Selected += OnBadgeSelected; + streamBadge.Selected += onBadgeSelected; badgesContainer.Add(streamBadge); } } public void SelectNone() { - foreach (StreamBadge streamBadge in badgesContainer) - streamBadge.Deactivate(); + selectedStreamId = -1; + if (badgesContainer != null) + { + foreach (StreamBadge streamBadge in badgesContainer) + { + if (!IsHovered) + streamBadge.Activate(); + else + streamBadge.Deactivate(); + } + } } public void SelectUpdateStream(string updateStream) { foreach (StreamBadge streamBadge in badgesContainer) + { if (streamBadge.ChangelogEntry.UpdateStream.Name == updateStream) { + selectedStreamId = streamBadge.ChangelogEntry.UpdateStream.Id; streamBadge.Activate(); - return; } + else + streamBadge.Deactivate(); + } } - private void OnBadgeSelected(StreamBadge source, EventArgs args) + private void onBadgeSelected(StreamBadge source, EventArgs args) { + selectedStreamId = source.ChangelogEntry.UpdateStream.Id; OnSelected(source); } @@ -98,33 +113,33 @@ namespace osu.Game.Overlays.Changelog Selected(source.ChangelogEntry.UpdateStream.Name, source.ChangelogEntry.Version, EventArgs.Empty); } - //protected override bool OnHover(InputState state) - //{ - // foreach (StreamBadge streamBadge in BadgesContainer.Children) - // { - // if (SelectedRelease != null) - // { - // if (SelectedRelease.UpdateStream.Id != streamBadge.ChangelogEntry.UpdateStream.Id) - // streamBadge.Deactivate(); - // else - // streamBadge.EnableDim(); - // } - // else - // streamBadge.Deactivate(); - // } - // return base.OnHover(state); - //} + protected override bool OnHover(InputState state) + { + foreach (StreamBadge streamBadge in badgesContainer.Children) + { + if (selectedStreamId < 0) + { + if (selectedStreamId != streamBadge.ChangelogEntry.UpdateStream.Id) + streamBadge.Deactivate(); + else + streamBadge.EnableDim(); + } + else + streamBadge.Deactivate(); + } + return base.OnHover(state); + } - //protected override void OnHoverLost(InputState state) - //{ - // foreach (StreamBadge streamBadge in BadgesContainer.Children) - // { - // if (SelectedRelease == null) - // streamBadge.Activate(true); - // else if (streamBadge.ChangelogEntry.UpdateStream.Id == SelectedRelease.UpdateStream.Id) - // streamBadge.DisableDim(); - // } - // base.OnHoverLost(state); - //} + protected override void OnHoverLost(InputState state) + { + foreach (StreamBadge streamBadge in badgesContainer.Children) + { + if (selectedStreamId < 0) + streamBadge.Activate(true); + else if (streamBadge.ChangelogEntry.UpdateStream.Id == selectedStreamId) + streamBadge.DisableDim(); + } + base.OnHoverLost(state); + } } } diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 5eb19119ec..3b45e4af87 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -10,7 +10,6 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using System; -using System.Collections.Generic; namespace osu.Game.Overlays.Changelog { @@ -31,7 +30,7 @@ namespace osu.Game.Overlays.Changelog Padding = new MarginPadding{ Bottom = 100, }; } - public void ShowListing(List changelog) + public void ShowListing(APIChangelog[] changelog) { DateTime currentDate = new DateTime(); diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 6a7ccacdc3..68875ef126 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -160,11 +160,11 @@ namespace osu.Game.Overlays.Changelog }; } - public void ShowBuild(string updateStream, string version) + public void ShowBuild(string displayName, string displayVersion) { listing.Deactivate(); - releaseStream.Activate($"{updateStream} {version}"); - titleStream.Text = updateStream; + releaseStream.Activate($"{displayName} {displayVersion}"); + titleStream.Text = displayName; titleStream.FlashColour(Color4.White, 500, Easing.OutQuad); } diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index 8294e6c403..f4ae2a0cb2 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -85,12 +85,12 @@ namespace osu.Game.Overlays.Changelog }; } - public void Activate(bool withoutHeaderUpdate = false) + public void Activate(bool withoutFiringUpdates = true) { isActivated = true; this.FadeIn(transition_duration); lineBadge.IsCollapsed = false; - if (!withoutHeaderUpdate && Selected != null) + if (!withoutFiringUpdates && Selected != null) Selected(this, EventArgs.Empty); } @@ -107,7 +107,7 @@ namespace osu.Game.Overlays.Changelog protected override bool OnClick(InputState state) { - Activate(); + Activate(false); return base.OnClick(state); } diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 2ae5bbdd78..d9a139c6ee 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -79,15 +79,8 @@ namespace osu.Game.Overlays }, }, }; - badges.Selected += onBuildSelected; - // content.ShowListing(); - // if (!Streams.IsHovered) - // foreach (StreamBadge item in Streams.BadgesContainer.Children) - // item.Activate(true); - // else - // foreach (StreamBadge item in Streams.BadgesContainer.Children) - // item.Deactivate(); + header.ListingActivated += FetchAndShowListing; } // receive input outside our bounds so we can trigger a close event on ourselves. @@ -131,12 +124,22 @@ namespace osu.Game.Overlays this.api = api; } + protected override void LoadComplete() + { + var req = new GetChangelogLatestBuildsRequest(); + req.Success += badges.Populate; + api.Queue(req); + FetchAndShowListing(); + base.LoadComplete(); + } + /// /// Fetches and shows changelog listing. /// public void FetchAndShowListing() { - var req = new GetChangelogLatestBuildsRequest(); + isAtListing = true; + var req = new GetChangelogRequest(); header.ShowListing(); badges.SelectNone(); chart.ShowAllUpdateStreams(); @@ -147,14 +150,15 @@ namespace osu.Game.Overlays /// /// Fetches and shows a specific build from a specific update stream. /// - /// If true, will select fetched build's update stream badge. - public void FetchAndShowBuild(string updateStream, string version) + public void FetchAndShowBuild(string updateStream, string version, bool sentByBadges = false) { + isAtListing = false; var req = new GetChangelogBuildRequest(updateStream, version); - header.ShowBuild(updateStream, version); - badges.SelectUpdateStream(updateStream); + if (!sentByBadges) + badges.SelectUpdateStream(updateStream); chart.ShowUpdateStream(updateStream); req.Success += content.ShowBuild; + req.Success += res => header.ShowBuild(res.UpdateStream.DisplayName, res.DisplayVersion); api.Queue(req); } } From 391da478813c0020166009fcc91451ba43a2fef3 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Sun, 22 Jul 2018 22:31:24 +0200 Subject: [PATCH 0061/2854] Refactor the rewrite --- osu.Game/Overlays/Changelog/ChangelogBadges.cs | 5 ++--- osu.Game/Overlays/Changelog/ChangelogContent.cs | 11 ++++------- osu.Game/Overlays/Changelog/ChangelogContentGroup.cs | 3 +-- osu.Game/Overlays/Changelog/ChangelogHeader.cs | 5 +++-- osu.Game/Overlays/Changelog/StreamBadge.cs | 4 ++-- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogBadges.cs b/osu.Game/Overlays/Changelog/ChangelogBadges.cs index 53f6a75bbc..757f0e0727 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBadges.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBadges.cs @@ -109,8 +109,7 @@ namespace osu.Game.Overlays.Changelog protected virtual void OnSelected(StreamBadge source) { - if (Selected != null) - Selected(source.ChangelogEntry.UpdateStream.Name, source.ChangelogEntry.Version, EventArgs.Empty); + Selected?.Invoke(source.ChangelogEntry.UpdateStream.Name, source.ChangelogEntry.Version, EventArgs.Empty); } protected override bool OnHover(InputState state) @@ -135,7 +134,7 @@ namespace osu.Game.Overlays.Changelog foreach (StreamBadge streamBadge in badgesContainer.Children) { if (selectedStreamId < 0) - streamBadge.Activate(true); + streamBadge.Activate(); else if (streamBadge.ChangelogEntry.UpdateStream.Id == selectedStreamId) streamBadge.DisableDim(); } diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 3b45e4af87..f6e90b83a3 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -2,12 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using OpenTK.Graphics; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using System; @@ -52,7 +50,7 @@ namespace osu.Game.Overlays.Changelog } // watch out for this? Add(changelogContentGroup = new ChangelogContentGroup(build, true)); - changelogContentGroup.BuildSelected += onBuildSelected; + changelogContentGroup.BuildSelected += OnBuildSelected; changelogContentGroup.GenerateText(build.ChangelogEntries); currentDate = build.CreatedAt.Date; } @@ -66,7 +64,7 @@ namespace osu.Game.Overlays.Changelog Margin = new MarginPadding { Top = 30, }, }); Add(changelogContentGroup = new ChangelogContentGroup(build, false)); - changelogContentGroup.BuildSelected += onBuildSelected; + changelogContentGroup.BuildSelected += OnBuildSelected; changelogContentGroup.GenerateText(build.ChangelogEntries); } } @@ -77,10 +75,9 @@ namespace osu.Game.Overlays.Changelog Child = changelogContentGroup = new ChangelogContentGroup(changelogBuild); } - protected virtual void onBuildSelected(string updateStream, string version, EventArgs args) + protected virtual void OnBuildSelected(string updateStream, string version, EventArgs args) { - if (BuildSelected != null) - BuildSelected(updateStream, version, EventArgs.Empty); + BuildSelected?.Invoke(updateStream, version, EventArgs.Empty); } } } diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 0efa8b70c6..aabc390d49 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -190,8 +190,7 @@ namespace osu.Game.Overlays.Changelog protected virtual void OnBuildSelected(string updateStream, string version) { - if (BuildSelected != null) - BuildSelected(updateStream, version, EventArgs.Empty); + BuildSelected?.Invoke(updateStream, version, EventArgs.Empty); } public void GenerateText(List changelogEntries) diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 68875ef126..a4be3bdf7f 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Changelog.Header; using System; @@ -24,6 +23,7 @@ namespace osu.Game.Overlays.Changelog private readonly Sprite headerBadge; private readonly OsuSpriteText titleStream; private readonly TextBadgePairListing listing; + private readonly SpriteIcon chevron; private readonly TextBadgePairRelease releaseStream; public Action ListingActivated; @@ -36,7 +36,6 @@ namespace osu.Game.Overlays.Changelog public ChangelogHeader() { - SpriteIcon chevron; // AppVeyor told me this should be a local variable..? RelativeSizeAxes = Axes.X; Height = cover_height; Children = new Drawable[] @@ -166,12 +165,14 @@ namespace osu.Game.Overlays.Changelog releaseStream.Activate($"{displayName} {displayVersion}"); titleStream.Text = displayName; titleStream.FlashColour(Color4.White, 500, Easing.OutQuad); + chevron.MoveToX(0, 100).FadeIn(100); } public void ShowListing() { releaseStream.Deactivate(); listing.Activate(); + chevron.MoveToX(-20, 100).FadeOut(100); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index f4ae2a0cb2..2a55b9a4b9 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -90,8 +90,8 @@ namespace osu.Game.Overlays.Changelog isActivated = true; this.FadeIn(transition_duration); lineBadge.IsCollapsed = false; - if (!withoutFiringUpdates && Selected != null) - Selected(this, EventArgs.Empty); + if (!withoutFiringUpdates) + Selected?.Invoke(this, EventArgs.Empty); } public void Deactivate() From 4cc5a657f3343ad226c4fe749f066bc8110886f3 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Mon, 23 Jul 2018 10:05:19 +0200 Subject: [PATCH 0062/2854] Follow up framework changes --- osu.Game/Graphics/UserInterface/ClickableText.cs | 2 +- osu.Game/Graphics/UserInterface/TooltipIconButton.cs | 2 +- osu.Game/Overlays/Changelog/ChangelogBadges.cs | 2 +- osu.Game/Overlays/Changelog/Header/TextBadgePair.cs | 2 +- osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs | 2 +- osu.Game/Overlays/Changelog/StreamBadge.cs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ClickableText.cs b/osu.Game/Graphics/UserInterface/ClickableText.cs index f2f9f12228..c27bc56238 100644 --- a/osu.Game/Graphics/UserInterface/ClickableText.cs +++ b/osu.Game/Graphics/UserInterface/ClickableText.cs @@ -6,7 +6,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; -using osu.Framework.Input; +using osu.Framework.Input.States; using osu.Game.Graphics.Sprites; using System; diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs index 8b85c8c0ec..7f6641817a 100644 --- a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; +using osu.Framework.Input.States; using System; namespace osu.Game.Graphics.UserInterface diff --git a/osu.Game/Overlays/Changelog/ChangelogBadges.cs b/osu.Game/Overlays/Changelog/ChangelogBadges.cs index 757f0e0727..0cfd220601 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBadges.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBadges.cs @@ -5,7 +5,7 @@ using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; +using osu.Framework.Input.States; using osu.Game.Online.API.Requests.Responses; using System; using System.Collections.Generic; diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index 6256c52be2..c01501c308 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; +using osu.Framework.Input.States; using System; namespace osu.Game.Overlays.Changelog.Header diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs index 425b4b111a..36db135675 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs @@ -4,7 +4,7 @@ using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; -using osu.Framework.Input; +using osu.Framework.Input.States; namespace osu.Game.Overlays.Changelog.Header { diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index 2a55b9a4b9..44fdeb80a7 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -8,7 +8,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; +using osu.Framework.Input.States; using osu.Game.Graphics; using osu.Game.Online.API.Requests.Responses; using System; From e27292fef8bd11c6ab7b672278eb513db05fb684 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Mon, 23 Jul 2018 17:49:42 +0200 Subject: [PATCH 0063/2854] Rewrite LineBadge.cs + update all its references --- osu.Game.Tests/Visual/TestCaseLineBadge.cs | 51 +++++++++++ osu.Game/Graphics/UserInterface/LineBadge.cs | 84 +++++++++++++++++++ .../Overlays/Changelog/Header/LineBadge.cs | 44 ---------- .../Changelog/Header/TextBadgePair.cs | 17 ++-- .../Changelog/Header/TextBadgePairListing.cs | 8 +- osu.Game/Overlays/Changelog/StreamBadge.cs | 19 +++-- 6 files changed, 160 insertions(+), 63 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestCaseLineBadge.cs create mode 100644 osu.Game/Graphics/UserInterface/LineBadge.cs delete mode 100644 osu.Game/Overlays/Changelog/Header/LineBadge.cs diff --git a/osu.Game.Tests/Visual/TestCaseLineBadge.cs b/osu.Game.Tests/Visual/TestCaseLineBadge.cs new file mode 100644 index 0000000000..304b0f9518 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseLineBadge.cs @@ -0,0 +1,51 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseLineBadge : OsuTestCase + { + public TestCaseLineBadge() + { + Container containerHorizontal; + LineBadge lineBadge; + + Add(containerHorizontal = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Gray, + Alpha = 0.5f, + RelativeSizeAxes = Axes.Both, + }, + lineBadge = new LineBadge + { + Anchor = Anchor.Centre, + UncollapsedSize = 10, + CollapsedSize = 2, + Colour = Color4.DeepSkyBlue, + } + } + }); + + AddStep(@"", () => { }); + AddStep(@"Collapse", () => lineBadge.Collapse()); + AddStep(@"Uncollapse", () => lineBadge.Uncollapse()); + AddSliderStep(@"Resize container", 1, 300, 150, value => containerHorizontal.ResizeTo(value)); + AddStep(@"Horizontal", () => lineBadge.IsHorizontal = true); + AddStep(@"Anchor top", () => lineBadge.Anchor = Anchor.TopCentre); + AddStep(@"Vertical", () => lineBadge.IsHorizontal = false); + AddStep(@"Anchor left", () => lineBadge.Anchor = Anchor.CentreLeft); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/LineBadge.cs b/osu.Game/Graphics/UserInterface/LineBadge.cs new file mode 100644 index 0000000000..91dd2add1f --- /dev/null +++ b/osu.Game/Graphics/UserInterface/LineBadge.cs @@ -0,0 +1,84 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Graphics.UserInterface +{ + public class LineBadge : Circle + { + public float UncollapsedSize; + public float CollapsedSize; + + public bool IsCollapsed { get; private set; } + private bool isHorizontal; + + /// + /// Automatically sets the RelativeSizeAxes and switches X and Y components when changed. + /// + public bool IsHorizontal + { + get { return isHorizontal; } + set + { + if (value == isHorizontal) + return; + if (IsLoaded) + { + FinishTransforms(); + var height = Height; + var width = Width; + RelativeSizeAxes = value ? Axes.X : Axes.Y; + Width = height; + Height = width; + } + else + RelativeSizeAxes = value ? Axes.X : Axes.Y; + isHorizontal = value; + } + } + + /// + /// A simple rounded expandable line. Set its + /// property to the center of the edge it's meant stick with. By default, + /// takes up the full parent's axis defined by . + /// + /// Whether to initialize with the + /// or the + public LineBadge(bool startCollapsed = true) + { + IsCollapsed = startCollapsed; + RelativeSizeAxes = Axes.X; + isHorizontal = true; + Origin = Anchor.Centre; + } + + protected override void LoadComplete() + { + if (isHorizontal) + Height = IsCollapsed ? CollapsedSize : UncollapsedSize; + else + Width = IsCollapsed ? CollapsedSize : UncollapsedSize; + base.LoadComplete(); + } + + public void Collapse(float transitionDuration = 400, Easing easing = Easing.Out) + { + IsCollapsed = true; + if (IsHorizontal) + this.ResizeHeightTo(CollapsedSize, transitionDuration, easing); + else + this.ResizeWidthTo(CollapsedSize, transitionDuration, easing); + } + + public void Uncollapse(float transitionDuration = 400, Easing easing = Easing.OutElastic) + { + IsCollapsed = false; + if (IsHorizontal) + this.ResizeHeightTo(UncollapsedSize, transitionDuration, easing); + else + this.ResizeWidthTo(UncollapsedSize, transitionDuration, easing); + } + } +} diff --git a/osu.Game/Overlays/Changelog/Header/LineBadge.cs b/osu.Game/Overlays/Changelog/Header/LineBadge.cs deleted file mode 100644 index 93eca528c5..0000000000 --- a/osu.Game/Overlays/Changelog/Header/LineBadge.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Overlays.Changelog.Header -{ - public class LineBadge : Circle - { - public float TransitionDuration = 400; - public float UncollapsedHeight; - public float CollapsedHeight; - - private bool isCollapsed; - public bool IsCollapsed - { - get { return isCollapsed; } - set - { - isCollapsed = value; - this.ResizeHeightTo(value ? CollapsedHeight : UncollapsedHeight, - value ? TransitionDuration / 2f : TransitionDuration, - value ? Easing.Out : Easing.OutElastic); - } - } - - public LineBadge(bool startCollapsed = true, float collapsedHeight = 1, float uncollapsedHeight = 10) - { - Anchor = Anchor.BottomCentre; - Origin = Anchor.Centre; - CollapsedHeight = collapsedHeight; - UncollapsedHeight = uncollapsedHeight; - Height = startCollapsed ? CollapsedHeight : UncollapsedHeight; - - // this margin prevents jumps when changing text's font weight - Margin = new MarginPadding - { - Left = 10, - Right = 10, - }; - } - } -} diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index c01501c308..6ff945469c 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.States; +using osu.Game.Graphics.UserInterface; using System; namespace osu.Game.Overlays.Changelog.Header @@ -36,14 +37,14 @@ namespace osu.Game.Overlays.Changelog.Header public void HideText(double duration = 0, Easing easing = Easing.InOutCubic) { - LineBadge.IsCollapsed = true; + LineBadge.Collapse(); Text.MoveToY(20, duration, easing) .FadeOut(duration, easing); } public void ShowText(double duration = 0, string displayText = null, Easing easing = Easing.InOutCubic) { - LineBadge.IsCollapsed = false; + LineBadge.Uncollapse(); if (!string.IsNullOrEmpty(displayText)) Text.Text = displayText; Text.MoveToY(0, duration, easing) @@ -55,7 +56,7 @@ namespace osu.Game.Overlays.Changelog.Header /// Full change takes double this time. public void ChangeText(double duration = 0, string displayText = null, Easing easing = Easing.InOutCubic) { - LineBadge.IsCollapsed = true; + LineBadge.Collapse(); Text.MoveToY(20, duration, easing) .FadeOut(duration, easing) .Then() @@ -67,7 +68,7 @@ namespace osu.Game.Overlays.Changelog.Header Scheduler.AddDelayed(() => { if (!string.IsNullOrEmpty(displayText)) Text.Text = displayText; - LineBadge.IsCollapsed = false; + LineBadge.Uncollapse(); }, duration); } @@ -89,8 +90,10 @@ namespace osu.Game.Overlays.Changelog.Header }, LineBadge = new LineBadge(startCollapsed) { + CollapsedSize = 2, + UncollapsedSize = 10, Colour = badgeColour, - RelativeSizeAxes = Axes.X, + Anchor = Anchor.BottomCentre, } }; } @@ -98,14 +101,14 @@ namespace osu.Game.Overlays.Changelog.Header public virtual void Deactivate() { IsActivated = false; - LineBadge.IsCollapsed = true; + LineBadge.Collapse(); Text.Font = "Exo2.0-Regular"; } public virtual void Activate() { IsActivated = true; - LineBadge.IsCollapsed = false; + LineBadge.Uncollapse(); Text.Font = "Exo2.0-Bold"; SampleActivate?.Play(); } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs index 36db135675..2e9152b256 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Changelog.Header public override void Activate() { IsActivated = true; - LineBadge.IsCollapsed = false; + LineBadge.Uncollapse(); Text.Font = "Exo2.0-Bold"; SetTextColour(Color4.White, 100); SampleActivate?.Play(); @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.Changelog.Header public override void Deactivate() { IsActivated = false; - LineBadge.IsCollapsed = true; + LineBadge.Collapse(); Text.Font = "Exo2.0-Regular"; // commented out since it makes bad resize-jumping SetTextColour(badgeColour, 100); OnDeactivation?.Invoke(); @@ -57,14 +57,14 @@ namespace osu.Game.Overlays.Changelog.Header protected override bool OnHover(InputState state) { - LineBadge.ResizeHeightTo(LineBadge.UncollapsedHeight, LineBadge.TransitionDuration, Easing.OutElastic); + LineBadge.Uncollapse(); return base.OnHover(state); } protected override void OnHoverLost(InputState state) { if (!IsActivated) - LineBadge.ResizeHeightTo(1, LineBadge.TransitionDuration, Easing.Out); + LineBadge.Collapse(); base.OnHoverLost(state); } diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index 44fdeb80a7..59ce97bcc7 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.States; using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; using System; @@ -27,7 +28,7 @@ namespace osu.Game.Overlays.Changelog private bool isActivated; - private readonly Header.LineBadge lineBadge; + private readonly LineBadge lineBadge; private SampleChannel sampleHover; public readonly APIChangelog ChangelogEntry; private readonly FillFlowContainer text; @@ -75,21 +76,23 @@ namespace osu.Game.Overlays.Changelog }, } }, - lineBadge = new Header.LineBadge(false, 2, 4) + lineBadge = new LineBadge(false) { Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, Colour = StreamColour.FromStreamName(ChangelogEntry.UpdateStream.Name), - RelativeSizeAxes = Axes.X, + UncollapsedSize = 4, + CollapsedSize = 2, }, }; } + /// In case we don't want to + /// fire the event. public void Activate(bool withoutFiringUpdates = true) { isActivated = true; this.FadeIn(transition_duration); - lineBadge.IsCollapsed = false; + lineBadge.Uncollapse(); if (!withoutFiringUpdates) Selected?.Invoke(this, EventArgs.Empty); } @@ -101,7 +104,7 @@ namespace osu.Game.Overlays.Changelog if (!IsHovered) { this.FadeTo(0.5f, transition_duration); - lineBadge.IsCollapsed = true; + lineBadge.Collapse(200); } } @@ -116,7 +119,7 @@ namespace osu.Game.Overlays.Changelog sampleHover?.Play(); DisableDim(); this.FadeIn(transition_duration); - lineBadge.IsCollapsed = false; + lineBadge.Uncollapse(); return base.OnHover(state); } @@ -125,7 +128,7 @@ namespace osu.Game.Overlays.Changelog if (!isActivated) { this.FadeTo(0.5f, transition_duration); - lineBadge.IsCollapsed = true; + lineBadge.Collapse(200); } else EnableDim(); From 554c56d51f8dd1eb7786581ce8079e88c2161c07 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Mon, 23 Jul 2018 18:17:50 +0200 Subject: [PATCH 0064/2854] Change order in TextBadgePair.cs --- .../Changelog/Header/TextBadgePair.cs | 94 +++++++++---------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index 6ff945469c..ec99604df5 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -25,53 +25,6 @@ namespace osu.Game.Overlays.Changelog.Header private SampleChannel sampleHover; protected SampleChannel SampleActivate; - public void SetTextColour(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) - { - Text.FadeColour(newColour, duration, easing); - } - - public void SetBadgeColour(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) - { - LineBadge.FadeColour(newColour, duration, easing); - } - - public void HideText(double duration = 0, Easing easing = Easing.InOutCubic) - { - LineBadge.Collapse(); - Text.MoveToY(20, duration, easing) - .FadeOut(duration, easing); - } - - public void ShowText(double duration = 0, string displayText = null, Easing easing = Easing.InOutCubic) - { - LineBadge.Uncollapse(); - if (!string.IsNullOrEmpty(displayText)) - Text.Text = displayText; - Text.MoveToY(0, duration, easing) - .FadeIn(duration, easing); - } - - /// - /// The duration of popping in and popping out not combined. - /// Full change takes double this time. - public void ChangeText(double duration = 0, string displayText = null, Easing easing = Easing.InOutCubic) - { - LineBadge.Collapse(); - Text.MoveToY(20, duration, easing) - .FadeOut(duration, easing) - .Then() - .MoveToY(0, duration, easing) - .FadeIn(duration, easing); - - // since using .finally/.oncomplete after first fadeout made the badge not hide - // sometimes in visual tests (https://streamable.com/0qssq), I'm using a scheduler here - Scheduler.AddDelayed(() => - { - if (!string.IsNullOrEmpty(displayText)) Text.Text = displayText; - LineBadge.Uncollapse(); - }, duration); - } - public TextBadgePair(ColourInfo badgeColour, string displayText = "Listing", bool startCollapsed = true) { AutoSizeAxes = Axes.X; @@ -98,6 +51,27 @@ namespace osu.Game.Overlays.Changelog.Header }; } + /// + /// The duration of popping in and popping out not combined. + /// Full change takes double this time. + public void ChangeText(double duration = 0, string displayText = null, Easing easing = Easing.InOutCubic) + { + LineBadge.Collapse(); + Text.MoveToY(20, duration, easing) + .FadeOut(duration, easing) + .Then() + .MoveToY(0, duration, easing) + .FadeIn(duration, easing); + + // since using .finally/.oncomplete after first fadeout made the badge not hide + // sometimes in visual tests (https://streamable.com/0qssq), I'm using a scheduler here + Scheduler.AddDelayed(() => + { + if (!string.IsNullOrEmpty(displayText)) Text.Text = displayText; + LineBadge.Uncollapse(); + }, duration); + } + public virtual void Deactivate() { IsActivated = false; @@ -113,6 +87,32 @@ namespace osu.Game.Overlays.Changelog.Header SampleActivate?.Play(); } + public void SetTextColour(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) + { + Text.FadeColour(newColour, duration, easing); + } + + public void SetBadgeColour(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) + { + LineBadge.FadeColour(newColour, duration, easing); + } + + public void HideText(double duration = 0, Easing easing = Easing.InOutCubic) + { + LineBadge.Collapse(); + Text.MoveToY(20, duration, easing) + .FadeOut(duration, easing); + } + + public void ShowText(double duration = 0, string displayText = null, Easing easing = Easing.InOutCubic) + { + LineBadge.Uncollapse(); + if (!string.IsNullOrEmpty(displayText)) + Text.Text = displayText; + Text.MoveToY(0, duration, easing) + .FadeIn(duration, easing); + } + protected override bool OnHover(InputState state) { if (!IsActivated) From f685c5ba5852355b3b2419051b3b61dd7ab8c000 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Mon, 23 Jul 2018 18:38:20 +0200 Subject: [PATCH 0065/2854] Fix typos; remove outdated comments; minor order changes --- osu.Game.Tests/Visual/TestCaseLineBadge.cs | 6 +++--- osu.Game/Graphics/UserInterface/LineBadge.cs | 2 +- osu.Game/Graphics/UserInterface/TooltipIconButton.cs | 3 ++- osu.Game/Overlays/Changelog/ChangelogChart.cs | 5 ----- osu.Game/Overlays/Changelog/ChangelogContent.cs | 9 +++++---- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseLineBadge.cs b/osu.Game.Tests/Visual/TestCaseLineBadge.cs index 304b0f9518..d80ef0131b 100644 --- a/osu.Game.Tests/Visual/TestCaseLineBadge.cs +++ b/osu.Game.Tests/Visual/TestCaseLineBadge.cs @@ -13,10 +13,10 @@ namespace osu.Game.Tests.Visual { public TestCaseLineBadge() { - Container containerHorizontal; + Container container; LineBadge lineBadge; - Add(containerHorizontal = new Container + Add(container = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual AddStep(@"", () => { }); AddStep(@"Collapse", () => lineBadge.Collapse()); AddStep(@"Uncollapse", () => lineBadge.Uncollapse()); - AddSliderStep(@"Resize container", 1, 300, 150, value => containerHorizontal.ResizeTo(value)); + AddSliderStep(@"Resize container", 1, 300, 150, value => container.ResizeTo(value)); AddStep(@"Horizontal", () => lineBadge.IsHorizontal = true); AddStep(@"Anchor top", () => lineBadge.Anchor = Anchor.TopCentre); AddStep(@"Vertical", () => lineBadge.IsHorizontal = false); diff --git a/osu.Game/Graphics/UserInterface/LineBadge.cs b/osu.Game/Graphics/UserInterface/LineBadge.cs index 91dd2add1f..0283559aee 100644 --- a/osu.Game/Graphics/UserInterface/LineBadge.cs +++ b/osu.Game/Graphics/UserInterface/LineBadge.cs @@ -45,7 +45,7 @@ namespace osu.Game.Graphics.UserInterface /// takes up the full parent's axis defined by . /// /// Whether to initialize with the - /// or the + /// or the . public LineBadge(bool startCollapsed = true) { IsCollapsed = startCollapsed; diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs index 7f6641817a..1dfec5cb80 100644 --- a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -21,6 +21,7 @@ namespace osu.Game.Graphics.UserInterface { private readonly SpriteIcon icon; private SampleChannel sampleHover; + public Action Action; private bool isEnabled; @@ -30,7 +31,7 @@ namespace osu.Game.Graphics.UserInterface set { isEnabled = value; - icon.Alpha = value ? 1 : 0.5f; + icon.FadeTo(value ? 1 : 0.5f, 250); } } diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index 91e2aa5e49..667f206250 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -15,7 +15,6 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Changelog { // maybe look to osu.Game.Screens.Play.SquareGraph for reference later - // placeholder json file: https://api.myjson.com/bins/10ye8a public class ChangelogChart : BufferedContainer { private readonly Box background; @@ -43,10 +42,6 @@ namespace osu.Game.Overlays.Changelog }; } - /// - /// Draw the graph for a specific build - /// - private bool isEmpty(APIChangelogChart changelogChart) { if (changelogChart != null) diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index f6e90b83a3..9d81fcb30f 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -31,7 +31,6 @@ namespace osu.Game.Overlays.Changelog public void ShowListing(APIChangelog[] changelog) { DateTime currentDate = new DateTime(); - Clear(); foreach (APIChangelog build in changelog) @@ -48,10 +47,10 @@ namespace osu.Game.Overlays.Changelog Margin = new MarginPadding { Top = 30, }, }); } - // watch out for this? - Add(changelogContentGroup = new ChangelogContentGroup(build, true)); + changelogContentGroup = new ChangelogContentGroup(build, true); changelogContentGroup.BuildSelected += OnBuildSelected; changelogContentGroup.GenerateText(build.ChangelogEntries); + Add(changelogContentGroup); currentDate = build.CreatedAt.Date; } else @@ -63,9 +62,11 @@ namespace osu.Game.Overlays.Changelog Colour = new Color4(32, 24, 35, 255), Margin = new MarginPadding { Top = 30, }, }); - Add(changelogContentGroup = new ChangelogContentGroup(build, false)); + + changelogContentGroup = new ChangelogContentGroup(build, false); changelogContentGroup.BuildSelected += OnBuildSelected; changelogContentGroup.GenerateText(build.ChangelogEntries); + Add(changelogContentGroup); } } } From f526d6696970fa3fa7c430982ed597d0d8a69bd1 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Mon, 23 Jul 2018 19:40:58 +0200 Subject: [PATCH 0066/2854] Fix showing builds --- osu.Game/Overlays/Changelog/ChangelogContent.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 9d81fcb30f..4f336dc48b 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -74,6 +74,9 @@ namespace osu.Game.Overlays.Changelog public void ShowBuild(APIChangelog changelogBuild) { Child = changelogContentGroup = new ChangelogContentGroup(changelogBuild); + changelogContentGroup.GenerateText(changelogBuild.ChangelogEntries); + changelogContentGroup.UpdateChevronTooltips(changelogBuild.Versions.Previous?.DisplayVersion, + changelogBuild.Versions.Next?.DisplayVersion); } protected virtual void OnBuildSelected(string updateStream, string version, EventArgs args) From 6cd6ca432c0d43cca651ea5ffe26da3a05eb3806 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Mon, 23 Jul 2018 20:36:24 +0200 Subject: [PATCH 0067/2854] Fix links not working due to rewrite regression --- osu.Game/Overlays/Changelog/ChangelogContent.cs | 1 + osu.Game/Overlays/Changelog/ChangelogHeader.cs | 12 ++++++++++-- .../Overlays/Changelog/Header/TextBadgePair.cs | 17 +++++++++++++++-- .../Changelog/Header/TextBadgePairListing.cs | 6 +++--- .../Changelog/Header/TextBadgePairRelease.cs | 2 -- osu.Game/Overlays/ChangelogOverlay.cs | 6 +++++- 6 files changed, 34 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 4f336dc48b..3b64003104 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -77,6 +77,7 @@ namespace osu.Game.Overlays.Changelog changelogContentGroup.GenerateText(changelogBuild.ChangelogEntries); changelogContentGroup.UpdateChevronTooltips(changelogBuild.Versions.Previous?.DisplayVersion, changelogBuild.Versions.Next?.DisplayVersion); + changelogContentGroup.BuildSelected += OnBuildSelected; } protected virtual void OnBuildSelected(string updateStream, string version, EventArgs args) diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index a4be3bdf7f..8617aff6a0 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -26,7 +26,9 @@ namespace osu.Game.Overlays.Changelog private readonly SpriteIcon chevron; private readonly TextBadgePairRelease releaseStream; - public Action ListingActivated; + public delegate void ListingSelectedEventHandler(); + + public event ListingSelectedEventHandler ListingSelected; private const float cover_height = 280; private const float title_height = 50; @@ -127,7 +129,7 @@ namespace osu.Game.Overlays.Changelog Margin = new MarginPadding { Top = 10, - Left = 7, + Left = 15, Right = 18, Bottom = 15, }, @@ -157,6 +159,7 @@ namespace osu.Game.Overlays.Changelog Origin = Anchor.CentreLeft, }, }; + listing.Activated += OnListingSelected; } public void ShowBuild(string displayName, string displayVersion) @@ -175,6 +178,11 @@ namespace osu.Game.Overlays.Changelog chevron.MoveToX(-20, 100).FadeOut(100); } + protected virtual void OnListingSelected(object source, EventArgs e) + { + ListingSelected?.Invoke(); + } + [BackgroundDependencyLoader] private void load(TextureStore textures) { diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index ec99604df5..732edc2a58 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -20,8 +20,10 @@ namespace osu.Game.Overlays.Changelog.Header protected LineBadge LineBadge; public bool IsActivated { get; protected set; } - public Action OnActivation; - public Action OnDeactivation; + public delegate void ActivatedEventHandler(object source, EventArgs args); + + public event ActivatedEventHandler Activated; + private SampleChannel sampleHover; protected SampleChannel SampleActivate; @@ -120,6 +122,17 @@ namespace osu.Game.Overlays.Changelog.Header return base.OnHover(state); } + protected override bool OnClick(InputState state) + { + OnActivated(); + return base.OnClick(state); + } + + protected virtual void OnActivated() + { + Activated?.Invoke(this, EventArgs.Empty); + } + [BackgroundDependencyLoader] private void load(AudioManager audio) { diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs index 2e9152b256..3876e8a226 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs @@ -32,21 +32,21 @@ namespace osu.Game.Overlays.Changelog.Header public override void Activate() { + if (IsActivated) + return; IsActivated = true; LineBadge.Uncollapse(); Text.Font = "Exo2.0-Bold"; SetTextColour(Color4.White, 100); SampleActivate?.Play(); - OnActivation?.Invoke(); } public override void Deactivate() { IsActivated = false; LineBadge.Collapse(); - Text.Font = "Exo2.0-Regular"; // commented out since it makes bad resize-jumping + Text.Font = "Exo2.0-Regular"; SetTextColour(badgeColour, 100); - OnDeactivation?.Invoke(); } protected override bool OnClick(InputState state) diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs index 32a76670f0..7f60dbabbd 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs @@ -30,14 +30,12 @@ namespace osu.Game.Overlays.Changelog.Header ShowText(transition_duration, displayText); IsActivated = true; SampleActivate?.Play(); - OnActivation?.Invoke(); } public override void Deactivate() { IsActivated = false; HideText(transition_duration); - OnDeactivation?.Invoke(); } } } diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index d9a139c6ee..a4e3e8a91c 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -79,8 +79,9 @@ namespace osu.Game.Overlays }, }, }; + header.ListingSelected += FetchAndShowListing; badges.Selected += onBuildSelected; - header.ListingActivated += FetchAndShowListing; + content.BuildSelected += onBuildSelected; } // receive input outside our bounds so we can trigger a close event on ourselves. @@ -152,6 +153,9 @@ namespace osu.Game.Overlays /// public void FetchAndShowBuild(string updateStream, string version, bool sentByBadges = false) { + //// I should probably change this to take APIChangelog as an argument, + //// instantly update the header and badge, and if it doesn't contain the + //// needed info, just subscribe to when the info will be available isAtListing = false; var req = new GetChangelogBuildRequest(updateStream, version); if (!sentByBadges) From 7c6be4a07538077759630b6e2e2069f400423ebf Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Mon, 23 Jul 2018 21:38:14 +0200 Subject: [PATCH 0068/2854] Handle around an APIChangelog instead of just Name and Version: Makes showing up the header immediately possible --- .../Overlays/Changelog/ChangelogBadges.cs | 15 ++---------- .../Overlays/Changelog/ChangelogContent.cs | 6 ++--- .../Changelog/ChangelogContentGroup.cs | 14 +++++------ osu.Game/Overlays/ChangelogOverlay.cs | 24 +++++++++++-------- 4 files changed, 25 insertions(+), 34 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogBadges.cs b/osu.Game/Overlays/Changelog/ChangelogBadges.cs index 0cfd220601..6f088677d9 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBadges.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBadges.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Changelog private const float padding_y = 20; private const float padding_x = 85; - public delegate void SelectionHandler(string updateStream, string version, EventArgs args); + public delegate void SelectionHandler(APIChangelog releaseStream, EventArgs args); public event SelectionHandler Selected; @@ -49,17 +49,6 @@ namespace osu.Game.Overlays.Changelog }, }, }; - //foreach (StreamBadge streamBadge in BadgesContainer.Children) - //{ - // streamBadge.OnActivation = () => - // { - // SelectedRelease = streamBadge.ChangelogEntry; - // foreach (StreamBadge item in BadgesContainer.Children) - // if (item.ChangelogEntry.Id != streamBadge.ChangelogEntry.Id) - // item.Deactivate(); - // OnSelection?.Invoke(); - // }; - //} } public void Populate(List latestBuilds) @@ -109,7 +98,7 @@ namespace osu.Game.Overlays.Changelog protected virtual void OnSelected(StreamBadge source) { - Selected?.Invoke(source.ChangelogEntry.UpdateStream.Name, source.ChangelogEntry.Version, EventArgs.Empty); + Selected?.Invoke(source.ChangelogEntry, EventArgs.Empty); } protected override bool OnHover(InputState state) diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 3b64003104..7e92ab4c26 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Changelog private APIAccess api; private ChangelogContentGroup changelogContentGroup; - public delegate void BuildSelectedEventHandler(string updateStream, string version, EventArgs args); + public delegate void BuildSelectedEventHandler(APIChangelog build, EventArgs args); public event BuildSelectedEventHandler BuildSelected; @@ -80,9 +80,9 @@ namespace osu.Game.Overlays.Changelog changelogContentGroup.BuildSelected += OnBuildSelected; } - protected virtual void OnBuildSelected(string updateStream, string version, EventArgs args) + protected virtual void OnBuildSelected(APIChangelog build, EventArgs args) { - BuildSelected?.Invoke(updateStream, version, EventArgs.Empty); + BuildSelected?.Invoke(build, EventArgs.Empty); } } } diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index aabc390d49..444e81a75d 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Changelog private readonly SortedDictionary> categories = new SortedDictionary>(); - public delegate void BuildSelectedEventHandler(string updateStream, string version, EventArgs args); + public delegate void BuildSelectedEventHandler(APIChangelog build, EventArgs args); public event BuildSelectedEventHandler BuildSelected; @@ -53,8 +53,7 @@ namespace osu.Game.Overlays.Changelog IsEnabled = false, Icon = FontAwesome.fa_chevron_left, Size = new Vector2(24), - Action = () => OnBuildSelected(build.Versions.Previous.UpdateStream.Name, - build.Versions.Previous.Version), + Action = () => OnBuildSelected(build.Versions.Previous), }, new FillFlowContainer { @@ -91,8 +90,7 @@ namespace osu.Game.Overlays.Changelog IsEnabled = false, Icon = FontAwesome.fa_chevron_right, Size = new Vector2(24), - Action = () => OnBuildSelected(build.Versions.Next.UpdateStream.Name, - build.Versions.Next.Version), + Action = () => OnBuildSelected(build.Versions.Next), }, } }, @@ -160,7 +158,7 @@ namespace osu.Game.Overlays.Changelog TextSize = 20, // web: 18, Font = @"Exo2.0-Light", Colour = StreamColour.FromStreamName(build.UpdateStream.Name), - Action = () => OnBuildSelected(build.UpdateStream.Name, build.Version), + Action = () => OnBuildSelected(build), IsClickMuted = true, }, } @@ -188,9 +186,9 @@ namespace osu.Game.Overlays.Changelog } } - protected virtual void OnBuildSelected(string updateStream, string version) + protected virtual void OnBuildSelected(APIChangelog build) { - BuildSelected?.Invoke(updateStream, version, EventArgs.Empty); + BuildSelected?.Invoke(build, EventArgs.Empty); } public void GenerateText(List changelogEntries) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index a4e3e8a91c..fd0515a0b3 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Input.Bindings; using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Changelog; using System; @@ -114,9 +115,9 @@ namespace osu.Game.Overlays FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); } - private void onBuildSelected(string updateStream, string version, EventArgs e) + private void onBuildSelected(APIChangelog build, EventArgs e) { - FetchAndShowBuild(updateStream, version); + FetchAndShowBuild(build); } [BackgroundDependencyLoader] @@ -151,18 +152,21 @@ namespace osu.Game.Overlays /// /// Fetches and shows a specific build from a specific update stream. /// - public void FetchAndShowBuild(string updateStream, string version, bool sentByBadges = false) + public void FetchAndShowBuild(APIChangelog build, bool sentByBadges = false) { - //// I should probably change this to take APIChangelog as an argument, - //// instantly update the header and badge, and if it doesn't contain the - //// needed info, just subscribe to when the info will be available isAtListing = false; - var req = new GetChangelogBuildRequest(updateStream, version); + var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); + + if (build.UpdateStream.DisplayName != null && build.DisplayVersion != null) + header.ShowBuild(build.UpdateStream.DisplayName, build.DisplayVersion); + else + req.Success += res => header.ShowBuild(res.UpdateStream.DisplayName, res.DisplayVersion); + if (!sentByBadges) - badges.SelectUpdateStream(updateStream); - chart.ShowUpdateStream(updateStream); + badges.SelectUpdateStream(build.UpdateStream.Name); + + chart.ShowUpdateStream(build.UpdateStream.Name); req.Success += content.ShowBuild; - req.Success += res => header.ShowBuild(res.UpdateStream.DisplayName, res.DisplayVersion); api.Queue(req); } } From 054f578bc8c3cf76055fca4d9e1a976609e677c6 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Mon, 23 Jul 2018 23:15:14 +0200 Subject: [PATCH 0069/2854] Update few links not working; Reasses sample playing; Slight renames --- .../UserInterface/TooltipIconButton.cs | 5 +++++ .../Overlays/Changelog/ChangelogBadges.cs | 12 +++++----- .../Changelog/ChangelogContentGroup.cs | 1 - .../Overlays/Changelog/ChangelogHeader.cs | 8 +++++++ .../Changelog/Header/TextBadgePair.cs | 6 ++--- .../Changelog/Header/TextBadgePairListing.cs | 1 - .../Changelog/Header/TextBadgePairRelease.cs | 1 - osu.Game/Overlays/Changelog/StreamBadge.cs | 22 +++++++++++-------- osu.Game/Overlays/ChangelogOverlay.cs | 10 ++++++++- 9 files changed, 44 insertions(+), 22 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs index 1dfec5cb80..ecae010889 100644 --- a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -21,6 +21,7 @@ namespace osu.Game.Graphics.UserInterface { private readonly SpriteIcon icon; private SampleChannel sampleHover; + private SampleChannel sampleClick; public Action Action; @@ -64,7 +65,10 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnClick(InputState state) { if (isEnabled) + { + sampleClick?.Play(); Action?.Invoke(); + } return base.OnClick(state); } @@ -79,6 +83,7 @@ namespace osu.Game.Graphics.UserInterface private void load(AudioManager audio) { sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); + sampleClick = audio.Sample.Get(@"UI/generic-select-soft"); } public string TooltipText { get; set; } diff --git a/osu.Game/Overlays/Changelog/ChangelogBadges.cs b/osu.Game/Overlays/Changelog/ChangelogBadges.cs index 6f088677d9..3d1762e9f6 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBadges.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBadges.cs @@ -80,9 +80,9 @@ namespace osu.Game.Overlays.Changelog { foreach (StreamBadge streamBadge in badgesContainer) { - if (streamBadge.ChangelogEntry.UpdateStream.Name == updateStream) + if (streamBadge.LatestBuild.UpdateStream.Name == updateStream) { - selectedStreamId = streamBadge.ChangelogEntry.UpdateStream.Id; + selectedStreamId = streamBadge.LatestBuild.UpdateStream.Id; streamBadge.Activate(); } else @@ -92,13 +92,13 @@ namespace osu.Game.Overlays.Changelog private void onBadgeSelected(StreamBadge source, EventArgs args) { - selectedStreamId = source.ChangelogEntry.UpdateStream.Id; + selectedStreamId = source.LatestBuild.UpdateStream.Id; OnSelected(source); } protected virtual void OnSelected(StreamBadge source) { - Selected?.Invoke(source.ChangelogEntry, EventArgs.Empty); + Selected?.Invoke(source.LatestBuild, EventArgs.Empty); } protected override bool OnHover(InputState state) @@ -107,7 +107,7 @@ namespace osu.Game.Overlays.Changelog { if (selectedStreamId < 0) { - if (selectedStreamId != streamBadge.ChangelogEntry.UpdateStream.Id) + if (selectedStreamId != streamBadge.LatestBuild.UpdateStream.Id) streamBadge.Deactivate(); else streamBadge.EnableDim(); @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.Changelog { if (selectedStreamId < 0) streamBadge.Activate(); - else if (streamBadge.ChangelogEntry.UpdateStream.Id == selectedStreamId) + else if (streamBadge.LatestBuild.UpdateStream.Id == selectedStreamId) streamBadge.DisableDim(); } base.OnHoverLost(state); diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 444e81a75d..763be79a43 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -159,7 +159,6 @@ namespace osu.Game.Overlays.Changelog Font = @"Exo2.0-Light", Colour = StreamColour.FromStreamName(build.UpdateStream.Name), Action = () => OnBuildSelected(build), - IsClickMuted = true, }, } }, diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 8617aff6a0..37b9c0a047 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -160,6 +160,7 @@ namespace osu.Game.Overlays.Changelog }, }; listing.Activated += OnListingSelected; + releaseStream.Activated += OnReleaseSelected; } public void ShowBuild(string displayName, string displayVersion) @@ -175,6 +176,8 @@ namespace osu.Game.Overlays.Changelog { releaseStream.Deactivate(); listing.Activate(); + titleStream.Text = "Listing"; + titleStream.FlashColour(Color4.White, 500, Easing.OutQuad); chevron.MoveToX(-20, 100).FadeOut(100); } @@ -183,6 +186,11 @@ namespace osu.Game.Overlays.Changelog ListingSelected?.Invoke(); } + protected virtual void OnReleaseSelected(object source, EventArgs e) + { + titleStream.FlashColour(Color4.White, 500, Easing.OutQuad); + } + [BackgroundDependencyLoader] private void load(TextureStore textures) { diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index 732edc2a58..c8801771d3 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Changelog.Header public event ActivatedEventHandler Activated; private SampleChannel sampleHover; - protected SampleChannel SampleActivate; + private SampleChannel sampleActivate; public TextBadgePair(ColourInfo badgeColour, string displayText = "Listing", bool startCollapsed = true) { @@ -86,7 +86,6 @@ namespace osu.Game.Overlays.Changelog.Header IsActivated = true; LineBadge.Uncollapse(); Text.Font = "Exo2.0-Bold"; - SampleActivate?.Play(); } public void SetTextColour(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) @@ -125,6 +124,7 @@ namespace osu.Game.Overlays.Changelog.Header protected override bool OnClick(InputState state) { OnActivated(); + sampleActivate?.Play(); return base.OnClick(state); } @@ -137,7 +137,7 @@ namespace osu.Game.Overlays.Changelog.Header private void load(AudioManager audio) { sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); - SampleActivate = audio.Sample.Get(@"UI/generic-select-soft"); + sampleActivate = audio.Sample.Get(@"UI/generic-select-soft"); } } } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs index 3876e8a226..31a06ce67d 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs @@ -38,7 +38,6 @@ namespace osu.Game.Overlays.Changelog.Header LineBadge.Uncollapse(); Text.Font = "Exo2.0-Bold"; SetTextColour(Color4.White, 100); - SampleActivate?.Play(); } public override void Deactivate() diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs index 7f60dbabbd..97ce799509 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairRelease.cs @@ -29,7 +29,6 @@ namespace osu.Game.Overlays.Changelog.Header else ShowText(transition_duration, displayText); IsActivated = true; - SampleActivate?.Play(); } public override void Deactivate() diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index 59ce97bcc7..2a82620995 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -29,15 +29,17 @@ namespace osu.Game.Overlays.Changelog private bool isActivated; private readonly LineBadge lineBadge; + private SampleChannel sampleClick; private SampleChannel sampleHover; - public readonly APIChangelog ChangelogEntry; + + public readonly APIChangelog LatestBuild; private readonly FillFlowContainer text; - public StreamBadge(APIChangelog changelogEntry) + public StreamBadge(APIChangelog latestBuild) { - ChangelogEntry = changelogEntry; + this.LatestBuild = latestBuild; Height = badge_height; - Width = ChangelogEntry.IsFeatured ? badge_width * 2 : badge_width; + base.Width = this.LatestBuild.IsFeatured ? badge_width * 2 : badge_width; Margin = new MarginPadding(5); isActivated = true; Children = new Drawable[] @@ -51,7 +53,7 @@ namespace osu.Game.Overlays.Changelog { new SpriteText { - Text = ChangelogEntry.UpdateStream.DisplayName, + Text = this.LatestBuild.UpdateStream.DisplayName, Font = @"Exo2.0-Bold", TextSize = 16, Margin = new MarginPadding @@ -61,14 +63,14 @@ namespace osu.Game.Overlays.Changelog }, new SpriteText { - Text = ChangelogEntry.DisplayVersion, + Text = this.LatestBuild.DisplayVersion, Font = @"Exo2.0-Light", TextSize = 21, }, new SpriteText { - Text = ChangelogEntry.Users > 0 ? - $"{ChangelogEntry.Users:N0} users online" : + Text = this.LatestBuild.Users > 0 ? + $"{this.LatestBuild.Users:N0} users online" : null, TextSize = 12, Font = @"Exo2.0-Regular", @@ -79,7 +81,7 @@ namespace osu.Game.Overlays.Changelog lineBadge = new LineBadge(false) { Anchor = Anchor.TopCentre, - Colour = StreamColour.FromStreamName(ChangelogEntry.UpdateStream.Name), + Colour = StreamColour.FromStreamName(this.LatestBuild.UpdateStream.Name), UncollapsedSize = 4, CollapsedSize = 2, }, @@ -111,6 +113,7 @@ namespace osu.Game.Overlays.Changelog protected override bool OnClick(InputState state) { Activate(false); + sampleClick?.Play(); return base.OnClick(state); } @@ -142,6 +145,7 @@ namespace osu.Game.Overlays.Changelog [BackgroundDependencyLoader] private void load(AudioManager audio) { + sampleClick = audio.Sample.Get(@"UI/generic-select-soft"); sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); } } diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index fd0515a0b3..4a133870f7 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -4,6 +4,8 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -26,6 +28,8 @@ namespace osu.Game.Overlays private readonly ChangelogChart chart; private readonly ChangelogContent content; + private SampleChannel sampleBack; + private readonly Color4 purple = new Color4(191, 4, 255, 255); private APIAccess api; @@ -96,7 +100,10 @@ namespace osu.Game.Overlays if (isAtListing) State = Visibility.Hidden; else + { FetchAndShowListing(); + sampleBack?.Play(); + } return true; } @@ -121,9 +128,10 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(APIAccess api) + private void load(APIAccess api, AudioManager audio) { this.api = api; + sampleBack = audio.Sample.Get(@"UI/generic-select-soft"); // @"UI/screen-back" feels non-fitting here } protected override void LoadComplete() From 9a84747fe678cfe9dff3f1751992458acfa42391 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Mon, 23 Jul 2018 23:24:34 +0200 Subject: [PATCH 0070/2854] Fix reversed if/else statement causing inproper behavior for badges --- osu.Game/Overlays/Changelog/ChangelogBadges.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogBadges.cs b/osu.Game/Overlays/Changelog/ChangelogBadges.cs index 3d1762e9f6..1a5e4f7d18 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBadges.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBadges.cs @@ -105,7 +105,7 @@ namespace osu.Game.Overlays.Changelog { foreach (StreamBadge streamBadge in badgesContainer.Children) { - if (selectedStreamId < 0) + if (selectedStreamId >= 0) { if (selectedStreamId != streamBadge.LatestBuild.UpdateStream.Id) streamBadge.Deactivate(); From 9e9d7a855d5c9b266f294635f5ce5d317d96a046 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 24 Jul 2018 04:03:56 +0200 Subject: [PATCH 0071/2854] Remove reduntant .base and .this --- osu.Game/Overlays/Changelog/StreamBadge.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index 2a82620995..8c881348c7 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -37,9 +37,9 @@ namespace osu.Game.Overlays.Changelog public StreamBadge(APIChangelog latestBuild) { - this.LatestBuild = latestBuild; + LatestBuild = latestBuild; Height = badge_height; - base.Width = this.LatestBuild.IsFeatured ? badge_width * 2 : badge_width; + Width = LatestBuild.IsFeatured ? badge_width * 2 : badge_width; Margin = new MarginPadding(5); isActivated = true; Children = new Drawable[] @@ -53,7 +53,7 @@ namespace osu.Game.Overlays.Changelog { new SpriteText { - Text = this.LatestBuild.UpdateStream.DisplayName, + Text = LatestBuild.UpdateStream.DisplayName, Font = @"Exo2.0-Bold", TextSize = 16, Margin = new MarginPadding @@ -63,14 +63,14 @@ namespace osu.Game.Overlays.Changelog }, new SpriteText { - Text = this.LatestBuild.DisplayVersion, + Text = LatestBuild.DisplayVersion, Font = @"Exo2.0-Light", TextSize = 21, }, new SpriteText { - Text = this.LatestBuild.Users > 0 ? - $"{this.LatestBuild.Users:N0} users online" : + Text = LatestBuild.Users > 0 ? + $"{LatestBuild.Users:N0} users online" : null, TextSize = 12, Font = @"Exo2.0-Regular", @@ -81,7 +81,7 @@ namespace osu.Game.Overlays.Changelog lineBadge = new LineBadge(false) { Anchor = Anchor.TopCentre, - Colour = StreamColour.FromStreamName(this.LatestBuild.UpdateStream.Name), + Colour = StreamColour.FromStreamName(LatestBuild.UpdateStream.Name), UncollapsedSize = 4, CollapsedSize = 2, }, From 24bb44a152d42fb67eb6acf03d86dfcb011a597a Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 24 Jul 2018 04:04:18 +0200 Subject: [PATCH 0072/2854] Add TextBadgePair test case --- .../Visual/TestCaseTextBadgePair.cs | 50 +++++++++++++++++++ .../Changelog/Header/TextBadgePair.cs | 6 ++- 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/TestCaseTextBadgePair.cs diff --git a/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs b/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs new file mode 100644 index 0000000000..c61213ce3c --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs @@ -0,0 +1,50 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Overlays.Changelog.Header; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseTextBadgePair : OsuTestCase + { + public TestCaseTextBadgePair() + { + Container container; + TextBadgePair textBadgePair; + + Add(container = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 250, + Height = 50, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Gray, + Alpha = 0.5f, + RelativeSizeAxes = Axes.Both, + }, + textBadgePair = new TextBadgePair(Color4.DeepSkyBlue, "Test") + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } + }); + + AddStep(@"Deactivate", textBadgePair.Deactivate); + AddStep(@"Activate", textBadgePair.Activate); + AddStep(@"Hide text", () => textBadgePair.HideText(200)); + AddStep(@"Show text", () => textBadgePair.ShowText(200)); + AddStep(@"Different text", () => textBadgePair.ChangeText(200, "This one's a little bit wider")); + AddWaitStep(1); + AddStep(@"Different text", () => textBadgePair.ChangeText(200, "Ok?..")); + } + } +} diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index c8801771d3..201524cc2f 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -37,6 +37,8 @@ namespace osu.Game.Overlays.Changelog.Header { TextSize = 21, // web: 16, Text = displayText, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Margin = new MarginPadding { Top = 5, @@ -49,6 +51,7 @@ namespace osu.Game.Overlays.Changelog.Header UncollapsedSize = 10, Colour = badgeColour, Anchor = Anchor.BottomCentre, + Origin = Anchor.Centre, } }; } @@ -69,7 +72,8 @@ namespace osu.Game.Overlays.Changelog.Header // sometimes in visual tests (https://streamable.com/0qssq), I'm using a scheduler here Scheduler.AddDelayed(() => { - if (!string.IsNullOrEmpty(displayText)) Text.Text = displayText; + if (!string.IsNullOrEmpty(displayText)) + Text.Text = displayText; LineBadge.Uncollapse(); }, duration); } From fa6074925e8bf58d010b9d3a5459277643560dc1 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 24 Jul 2018 17:02:24 +0200 Subject: [PATCH 0073/2854] Remove unused variable --- osu.Game.Tests/Visual/TestCaseTextBadgePair.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs b/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs index c61213ce3c..6d2fe20f2b 100644 --- a/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs +++ b/osu.Game.Tests/Visual/TestCaseTextBadgePair.cs @@ -13,10 +13,9 @@ namespace osu.Game.Tests.Visual { public TestCaseTextBadgePair() { - Container container; TextBadgePair textBadgePair; - Add(container = new Container + Add(new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 175c20a1043b101cb69667c66d050bcc3fbb2f21 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 24 Jul 2018 18:41:47 +0200 Subject: [PATCH 0074/2854] Slight reorder --- osu.Game/Overlays/ChangelogOverlay.cs | 69 +++++++++++++-------------- 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 4a133870f7..08bcaff58b 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -28,14 +28,17 @@ namespace osu.Game.Overlays private readonly ChangelogChart chart; private readonly ChangelogContent content; - private SampleChannel sampleBack; - private readonly Color4 purple = new Color4(191, 4, 255, 255); + private SampleChannel sampleBack; + private APIAccess api; private bool isAtListing; + // receive input outside our bounds so we can trigger a close event on ourselves. + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + public ChangelogOverlay() { // these possibly need adjusting? @@ -89,8 +92,33 @@ namespace osu.Game.Overlays content.BuildSelected += onBuildSelected; } - // receive input outside our bounds so we can trigger a close event on ourselves. - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + [BackgroundDependencyLoader] + private void load(APIAccess api, AudioManager audio) + { + this.api = api; + sampleBack = audio.Sample.Get(@"UI/generic-select-soft"); // @"UI/screen-back" feels non-fitting here + } + + protected override void LoadComplete() + { + var req = new GetChangelogLatestBuildsRequest(); + req.Success += badges.Populate; + api.Queue(req); + FetchAndShowListing(); + base.LoadComplete(); + } + + protected override void PopIn() + { + base.PopIn(); + FadeEdgeEffectTo(0.25f, WaveContainer.APPEAR_DURATION, Easing.In); + } + + protected override void PopOut() + { + base.PopOut(); + FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); + } public override bool OnPressed(GlobalAction action) { @@ -110,38 +138,7 @@ namespace osu.Game.Overlays return false; } - protected override void PopIn() - { - base.PopIn(); - FadeEdgeEffectTo(0.25f, WaveContainer.APPEAR_DURATION, Easing.In); - } - - protected override void PopOut() - { - base.PopOut(); - FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); - } - - private void onBuildSelected(APIChangelog build, EventArgs e) - { - FetchAndShowBuild(build); - } - - [BackgroundDependencyLoader] - private void load(APIAccess api, AudioManager audio) - { - this.api = api; - sampleBack = audio.Sample.Get(@"UI/generic-select-soft"); // @"UI/screen-back" feels non-fitting here - } - - protected override void LoadComplete() - { - var req = new GetChangelogLatestBuildsRequest(); - req.Success += badges.Populate; - api.Queue(req); - FetchAndShowListing(); - base.LoadComplete(); - } + private void onBuildSelected(APIChangelog build, EventArgs e) => FetchAndShowBuild(build); /// /// Fetches and shows changelog listing. From 255c252f6aa3be47b6fdd8331e930fe5f46d65cb Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 24 Jul 2018 18:47:05 +0200 Subject: [PATCH 0075/2854] Shorten MarginPaddings in ChangelogContentGroup --- .../Changelog/ChangelogContentGroup.cs | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 763be79a43..f768b06da7 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -42,10 +42,7 @@ namespace osu.Game.Overlays.Changelog Origin = Anchor.TopCentre, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Margin = new MarginPadding - { - Top = 20, - }, + Margin = new MarginPadding { Top = 20 }, Children = new Drawable[] { chevronPrevious = new TooltipIconButton @@ -58,11 +55,7 @@ namespace osu.Game.Overlays.Changelog new FillFlowContainer { AutoSizeAxes = Axes.Both, - Margin = new MarginPadding - { - Left = 40, - Right = 40, - }, + Margin = new MarginPadding { Horizontal = 40 }, Children = new[] { new SpriteText @@ -104,7 +97,7 @@ namespace osu.Game.Overlays.Changelog Font = @"Exo2.0-Medium", Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Margin = new MarginPadding{ Top = 5, } + Margin = new MarginPadding { Top = 5 } }, ChangelogEntries = new FillFlowContainer { @@ -133,7 +126,7 @@ namespace osu.Game.Overlays.Changelog Font = @"Exo2.0-Light", Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Margin = new MarginPadding{ Top = 20, }, + Margin = new MarginPadding { Top = 20 }, Alpha = newDate ? 1 : 0, }, new FillFlowContainer @@ -142,7 +135,7 @@ namespace osu.Game.Overlays.Changelog Origin = Anchor.TopCentre, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Margin = new MarginPadding{ Top = 20, }, + Margin = new MarginPadding { Top = 20 }, Spacing = new Vector2(5), Children = new Drawable[] { @@ -208,7 +201,7 @@ namespace osu.Game.Overlays.Changelog Text = category.Key, TextSize = 24, // web: 18, Font = @"Exo2.0-Bold", - Margin = new MarginPadding { Top = 35, Bottom = 15, }, + Margin = new MarginPadding { Top = 35, Bottom = 15 }, }); foreach (ChangelogEntry entry in category.Value) { @@ -219,10 +212,14 @@ namespace osu.Game.Overlays.Changelog Direction = FillDirection.Full, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Margin = new MarginPadding{ Vertical = 5, }, + Margin = new MarginPadding { Vertical = 5 }, }); - title.AddIcon(FontAwesome.fa_check, t => { t.TextSize = 12; t.Padding = new MarginPadding { Left = -17, Right = 5 }; }); - title.AddText(entry.Title, t => { t.TextSize = 18; }); //t.Padding = new MarginPadding(10); }); + title.AddIcon(FontAwesome.fa_check, t => + { + t.TextSize = 12; + t.Padding = new MarginPadding { Left = -17, Right = 5 }; + }); + title.AddText(entry.Title, t => { t.TextSize = 18; }); if (!string.IsNullOrEmpty(entry.Repository)) { title.AddText($" ({entry.Repository.Substring(4)}#{entry.GithubPullRequestId})", t => From 225ff35907432cd9787c77cc8ff5ee9b69c7fe71 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 24 Jul 2018 18:55:50 +0200 Subject: [PATCH 0076/2854] XML for TooltipIconButton.cs --- osu.Game/Graphics/UserInterface/TooltipIconButton.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs index ecae010889..65ab5de036 100644 --- a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -23,9 +23,16 @@ namespace osu.Game.Graphics.UserInterface private SampleChannel sampleHover; private SampleChannel sampleClick; + /// + /// The action to fire upon click, if is set to true. + /// public Action Action; private bool isEnabled; + + /// + /// If set to true, upon click the will execute. It wont otherwise. + /// public bool IsEnabled { get { return isEnabled; } @@ -42,6 +49,9 @@ namespace osu.Game.Graphics.UserInterface set { icon.Icon = value; } } + /// + /// A simple icon that has an action upon click and can be disabled. + /// public TooltipIconButton() { isEnabled = true; From 16d3ca20731ea986382f4583c454b0f3cf2674b4 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 24 Jul 2018 19:01:24 +0200 Subject: [PATCH 0077/2854] Move breadcrumb to the right position --- osu.Game/Overlays/Changelog/ChangelogHeader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 37b9c0a047..035574bd1c 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -114,7 +114,7 @@ namespace osu.Game.Overlays.Changelog }, new FillFlowContainer // Listing > Lazer 2018.713.1 { - X = 2 * icon_margin + icon_size - 8, + X = 2 * icon_margin + icon_size, Height = version_height, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, From dfe4153c95b0cbfc56603741bb231b569d776a54 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 24 Jul 2018 19:34:40 +0200 Subject: [PATCH 0078/2854] Prevent duplicated fetching for listing and builds: - listing when already at it; - builds by immediately disabling links to them (chevrons and links in listing) --- .../Overlays/Changelog/ChangelogContentGroup.cs | 16 +++++++++++++--- osu.Game/Overlays/ChangelogOverlay.cs | 6 ++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index f768b06da7..44d5e33c56 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -50,7 +50,11 @@ namespace osu.Game.Overlays.Changelog IsEnabled = false, Icon = FontAwesome.fa_chevron_left, Size = new Vector2(24), - Action = () => OnBuildSelected(build.Versions.Previous), + Action = () => + { + OnBuildSelected(build.Versions.Previous); + chevronPrevious.IsEnabled = false; + }, }, new FillFlowContainer { @@ -83,7 +87,11 @@ namespace osu.Game.Overlays.Changelog IsEnabled = false, Icon = FontAwesome.fa_chevron_right, Size = new Vector2(24), - Action = () => OnBuildSelected(build.Versions.Next), + Action = () => + { + OnBuildSelected(build.Versions.Next); + chevronNext.IsEnabled = false; + }, }, } }, @@ -110,6 +118,7 @@ namespace osu.Game.Overlays.Changelog public ChangelogContentGroup(APIChangelog build, bool newDate = false) { + ClickableText clickableText; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; @@ -145,7 +154,7 @@ namespace osu.Game.Overlays.Changelog TextSize = 20, // web: 18, Font = @"Exo2.0-Medium", }, - new ClickableText + clickableText = new ClickableText { Text = build.DisplayVersion, TextSize = 20, // web: 18, @@ -162,6 +171,7 @@ namespace osu.Game.Overlays.Changelog Direction = FillDirection.Vertical, }, }; + clickableText.Action += () => clickableText.IsEnabled = false; } public void UpdateChevronTooltips(string previousVersion, string nextVersion) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 08bcaff58b..ad8d60d6ca 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -141,13 +141,15 @@ namespace osu.Game.Overlays private void onBuildSelected(APIChangelog build, EventArgs e) => FetchAndShowBuild(build); /// - /// Fetches and shows changelog listing. + /// If we're not already at it, fetches and shows changelog listing. /// public void FetchAndShowListing() { + header.ShowListing(); + if (isAtListing) + return; isAtListing = true; var req = new GetChangelogRequest(); - header.ShowListing(); badges.SelectNone(); chart.ShowAllUpdateStreams(); req.Success += content.ShowListing; From 3b362881853195936282bc8ade46b5b9fd94ae0c Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 24 Jul 2018 20:13:53 +0200 Subject: [PATCH 0079/2854] Rename TestCaseChangelog to TestCaseChangelogOverlay --- .../{TestCaseChangelog.cs => TestCaseChangelogOverlay.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Tests/Visual/{TestCaseChangelog.cs => TestCaseChangelogOverlay.cs} (90%) diff --git a/osu.Game.Tests/Visual/TestCaseChangelog.cs b/osu.Game.Tests/Visual/TestCaseChangelogOverlay.cs similarity index 90% rename from osu.Game.Tests/Visual/TestCaseChangelog.cs rename to osu.Game.Tests/Visual/TestCaseChangelogOverlay.cs index da260c239b..53143bd924 100644 --- a/osu.Game.Tests/Visual/TestCaseChangelog.cs +++ b/osu.Game.Tests/Visual/TestCaseChangelogOverlay.cs @@ -7,7 +7,7 @@ using osu.Game.Overlays; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseChangelog : OsuTestCase + public class TestCaseChangelogOverlay : OsuTestCase { private ChangelogOverlay changelog; From 8e412fe4037f78d5db5f4bea73a233a1646bb80f Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 24 Jul 2018 20:57:09 +0200 Subject: [PATCH 0080/2854] Add scrolling to the previous position when on "Back" --- osu.Game/Overlays/ChangelogOverlay.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index ad8d60d6ca..965d6fcf49 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -28,6 +28,8 @@ namespace osu.Game.Overlays private readonly ChangelogChart chart; private readonly ChangelogContent content; + private readonly ScrollContainer scroll; + private readonly Color4 purple = new Color4(191, 4, 255, 255); private SampleChannel sampleBack; @@ -35,6 +37,7 @@ namespace osu.Game.Overlays private APIAccess api; private bool isAtListing; + private float savedScrollPosition; // receive input outside our bounds so we can trigger a close event on ourselves. public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; @@ -68,7 +71,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = new Color4(49, 36, 54, 255), }, - new ScrollContainer + scroll = new ScrollContainer { RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, @@ -152,7 +155,11 @@ namespace osu.Game.Overlays var req = new GetChangelogRequest(); badges.SelectNone(); chart.ShowAllUpdateStreams(); - req.Success += content.ShowListing; + req.Success += listing => + { + content.ShowListing(listing); + scroll.ScrollTo(savedScrollPosition); + }; api.Queue(req); } @@ -173,7 +180,13 @@ namespace osu.Game.Overlays badges.SelectUpdateStream(build.UpdateStream.Name); chart.ShowUpdateStream(build.UpdateStream.Name); - req.Success += content.ShowBuild; + req.Success += APIChangelog => + { + savedScrollPosition = scroll.Current; + content.ShowBuild(APIChangelog); + if (scroll.Current > scroll.GetChildPosInContent(content)) + scroll.ScrollTo(content); + }; api.Queue(req); } } From 08a291f0d45e6dd507986dc2f97e0e4deec685c4 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 24 Jul 2018 21:19:29 +0200 Subject: [PATCH 0081/2854] Improve scroll handling --- osu.Game/Overlays/ChangelogOverlay.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 965d6fcf49..3e52e66e35 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -168,7 +168,6 @@ namespace osu.Game.Overlays /// public void FetchAndShowBuild(APIChangelog build, bool sentByBadges = false) { - isAtListing = false; var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); if (build.UpdateStream.DisplayName != null && build.DisplayVersion != null) @@ -182,10 +181,12 @@ namespace osu.Game.Overlays chart.ShowUpdateStream(build.UpdateStream.Name); req.Success += APIChangelog => { - savedScrollPosition = scroll.Current; content.ShowBuild(APIChangelog); if (scroll.Current > scroll.GetChildPosInContent(content)) scroll.ScrollTo(content); + if (isAtListing) + savedScrollPosition = scroll.Current; + isAtListing = false; }; api.Queue(req); } From 43a7b3a82510a073fe4aab2da4cce8b5fd001dfa Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 24 Jul 2018 21:42:25 +0200 Subject: [PATCH 0082/2854] Fetch listing only once; Reenable disabled links as they wont be regenerated --- .../Changelog/ChangelogContentGroup.cs | 8 ++++- osu.Game/Overlays/ChangelogOverlay.cs | 36 ++++++++++++------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 44d5e33c56..88be51bb47 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -171,7 +171,13 @@ namespace osu.Game.Overlays.Changelog Direction = FillDirection.Vertical, }, }; - clickableText.Action += () => clickableText.IsEnabled = false; + + // we may not want double clicks to make it double the work + clickableText.Action += () => + { + clickableText.IsEnabled = false; + Scheduler.AddDelayed(() => clickableText.IsEnabled = true, 2000); + }; } public void UpdateChevronTooltips(string previousVersion, string nextVersion) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 3e52e66e35..ccf2884422 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -26,6 +26,7 @@ namespace osu.Game.Overlays private readonly ChangelogHeader header; private readonly ChangelogBadges badges; private readonly ChangelogChart chart; + private readonly ChangelogContent listing; private readonly ChangelogContent content; private readonly ScrollContainer scroll; @@ -85,13 +86,15 @@ namespace osu.Game.Overlays header = new ChangelogHeader(), badges = new ChangelogBadges(), chart = new ChangelogChart(), + listing = new ChangelogContent(), content = new ChangelogContent() }, }, }, }; - header.ListingSelected += FetchAndShowListing; + header.ListingSelected += ShowListing; badges.Selected += onBuildSelected; + listing.BuildSelected += onBuildSelected; content.BuildSelected += onBuildSelected; } @@ -107,7 +110,7 @@ namespace osu.Game.Overlays var req = new GetChangelogLatestBuildsRequest(); req.Success += badges.Populate; api.Queue(req); - FetchAndShowListing(); + fetchListing(); base.LoadComplete(); } @@ -132,7 +135,7 @@ namespace osu.Game.Overlays State = Visibility.Hidden; else { - FetchAndShowListing(); + ShowListing(); sampleBack?.Play(); } return true; @@ -143,10 +146,7 @@ namespace osu.Game.Overlays private void onBuildSelected(APIChangelog build, EventArgs e) => FetchAndShowBuild(build); - /// - /// If we're not already at it, fetches and shows changelog listing. - /// - public void FetchAndShowListing() + private void fetchListing() { header.ShowListing(); if (isAtListing) @@ -155,14 +155,24 @@ namespace osu.Game.Overlays var req = new GetChangelogRequest(); badges.SelectNone(); chart.ShowAllUpdateStreams(); - req.Success += listing => - { - content.ShowListing(listing); - scroll.ScrollTo(savedScrollPosition); - }; + req.Success += listing.ShowListing; api.Queue(req); } + public void ShowListing() + { + header.ShowListing(); + if (isAtListing) + return; + isAtListing = true; + content.Hide(); + listing.Show(); + badges.SelectNone(); + chart.ShowAllUpdateStreams(); + listing.Show(); + scroll.ScrollTo(savedScrollPosition); + } + /// /// Fetches and shows a specific build from a specific update stream. /// @@ -181,6 +191,8 @@ namespace osu.Game.Overlays chart.ShowUpdateStream(build.UpdateStream.Name); req.Success += APIChangelog => { + listing.Hide(); + content.Show(); content.ShowBuild(APIChangelog); if (scroll.Current > scroll.GetChildPosInContent(content)) scroll.ScrollTo(content); From 74540bba83dd62594a6c1447ac36adfe18baef64 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 24 Jul 2018 21:59:09 +0200 Subject: [PATCH 0083/2854] Non-breaking space for "by ..." Doesn't work for now(?) --- osu.Game/Overlays/Changelog/ChangelogContentGroup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 88be51bb47..23e05d8065 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -244,7 +244,7 @@ namespace osu.Game.Overlays.Changelog t.Colour = Color4.SkyBlue; }); } - title.AddText($" by {entry.GithubUser.DisplayName}", t => t.TextSize = 14); //web: 12; + title.AddText($" by\u00A0{entry.GithubUser.DisplayName}", t => t.TextSize = 14); //web: 12; TextFlowContainer messageContainer; ChangelogEntries.Add(messageContainer = new OsuTextFlowContainer { From d2b2c4c19b3ef6778d73f8867b99c0be9966bc97 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Tue, 24 Jul 2018 23:25:57 +0200 Subject: [PATCH 0084/2854] Make PR-link surrounding parenthesis white --- .../Overlays/Changelog/ChangelogContentGroup.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 23e05d8065..821f6aadc4 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -221,15 +221,13 @@ namespace osu.Game.Overlays.Changelog }); foreach (ChangelogEntry entry in category.Value) { - OsuTextFlowContainer title; - - ChangelogEntries.Add(title = new OsuTextFlowContainer + OsuTextFlowContainer title = new OsuTextFlowContainer { Direction = FillDirection.Full, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Margin = new MarginPadding { Vertical = 5 }, - }); + }; title.AddIcon(FontAwesome.fa_check, t => { t.TextSize = 12; @@ -238,24 +236,27 @@ namespace osu.Game.Overlays.Changelog title.AddText(entry.Title, t => { t.TextSize = 18; }); if (!string.IsNullOrEmpty(entry.Repository)) { - title.AddText($" ({entry.Repository.Substring(4)}#{entry.GithubPullRequestId})", t => + title.AddText(" (", t => t.TextSize = 18); + title.AddText($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", t => { t.TextSize = 18; t.Colour = Color4.SkyBlue; }); + title.AddText(")", t => t.TextSize = 18); } title.AddText($" by\u00A0{entry.GithubUser.DisplayName}", t => t.TextSize = 14); //web: 12; - TextFlowContainer messageContainer; - ChangelogEntries.Add(messageContainer = new OsuTextFlowContainer + ChangelogEntries.Add(title); + TextFlowContainer messageContainer = new TextFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, - }); + }; messageContainer.AddText($"{entry.MessageHtml?.Replace("

", "").Replace("

", "")}\n", t => { t.TextSize = 14; // web: 12, t.Colour = new Color4(235, 184, 254, 255); }); + ChangelogEntries.Add(messageContainer); } } } From a00f4e81788fe1dd49f139d509e13a12ff848458 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 25 Jul 2018 00:12:05 +0200 Subject: [PATCH 0085/2854] Make Back action go to the top of listing before exiting the changelog screen --- osu.Game/Overlays/ChangelogOverlay.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index ccf2884422..fc21ac64ac 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -132,7 +132,15 @@ namespace osu.Game.Overlays { case GlobalAction.Back: if (isAtListing) - State = Visibility.Hidden; + { + if (scroll.Current > scroll.GetChildPosInContent(listing)) + { + scroll.ScrollTo(0); + sampleBack?.Play(); + } + else + State = Visibility.Hidden; + } else { ShowListing(); From cd79c2cc45c70cc59a07e42b5257a7799075086e Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 25 Jul 2018 00:59:21 +0200 Subject: [PATCH 0086/2854] Hide graph when it's unavailable; Scroll change --- osu.Game/Overlays/Changelog/ChangelogChart.cs | 40 +++++++++++-------- osu.Game/Overlays/ChangelogOverlay.cs | 6 +-- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index 667f206250..3313e4adcc 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -17,6 +17,10 @@ namespace osu.Game.Overlays.Changelog // maybe look to osu.Game.Screens.Play.SquareGraph for reference later public class ChangelogChart : BufferedContainer { + private const float height = 100; + private const float transition_duration = 300; + + private readonly Container container; private readonly Box background; private readonly SpriteText text; private APIAccess api; @@ -24,20 +28,25 @@ namespace osu.Game.Overlays.Changelog public ChangelogChart() { RelativeSizeAxes = Axes.X; - Height = 100; - Children = new Drawable[] + AutoSizeAxes = Axes.Y; + Child = container = new Container { - background = new Box + RelativeSizeAxes = Axes.X, + Height = height, + Children = new Drawable[] { - Colour = StreamColour.STABLE, - RelativeSizeAxes = Axes.Both, - }, - text = new SpriteText - { - Text = "Graph Placeholder", - TextSize = 28, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + background = new Box + { + Colour = StreamColour.STABLE, + RelativeSizeAxes = Axes.Both, + }, + text = new SpriteText + { + Text = "Graph Placeholder", + TextSize = 28, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, }, }; } @@ -55,13 +64,10 @@ namespace osu.Game.Overlays.Changelog if (!isEmpty(chartInfo)) { background.Colour = StreamColour.FromStreamName(updateStreamName); - text.Text = "Graph placeholder\n(chart is not empty)"; + container.MoveToY(0, transition_duration, Easing.InOutQuad).FadeIn(transition_duration); } else - { - background.Colour = Color4.Black; - text.Text = "Graph placeholder\n(chart is empty)"; - } + container.MoveToY(-height, transition_duration, Easing.InOutQuad).FadeOut(transition_duration); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index fc21ac64ac..a0d0726c01 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -197,13 +197,13 @@ namespace osu.Game.Overlays badges.SelectUpdateStream(build.UpdateStream.Name); chart.ShowUpdateStream(build.UpdateStream.Name); - req.Success += APIChangelog => + req.Success += apiChangelog => { listing.Hide(); content.Show(); - content.ShowBuild(APIChangelog); + content.ShowBuild(apiChangelog); if (scroll.Current > scroll.GetChildPosInContent(content)) - scroll.ScrollTo(content); + scroll.ScrollTo(chart); if (isAtListing) savedScrollPosition = scroll.Current; isAtListing = false; From 05a59f352547a716c149e70b198f111d4a1923da Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 25 Jul 2018 03:54:16 +0200 Subject: [PATCH 0087/2854] Add links to GitHub --- .../Changelog/ChangelogContentGroup.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 821f6aadc4..07e695b439 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -221,7 +221,7 @@ namespace osu.Game.Overlays.Changelog }); foreach (ChangelogEntry entry in category.Value) { - OsuTextFlowContainer title = new OsuTextFlowContainer + LinkFlowContainer title = new LinkFlowContainer { Direction = FillDirection.Full, RelativeSizeAxes = Axes.X, @@ -237,14 +237,18 @@ namespace osu.Game.Overlays.Changelog if (!string.IsNullOrEmpty(entry.Repository)) { title.AddText(" (", t => t.TextSize = 18); - title.AddText($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", t => - { - t.TextSize = 18; - t.Colour = Color4.SkyBlue; - }); + title.AddLink($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", + entry.GithubUrl, Online.Chat.LinkAction.External, null, + null, t => { t.TextSize = 18; }); title.AddText(")", t => t.TextSize = 18); } - title.AddText($" by\u00A0{entry.GithubUser.DisplayName}", t => t.TextSize = 14); //web: 12; + title.AddText(" by ", t => t.TextSize = 14); + if (entry.GithubUser.GithubUrl != null) + title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, + Online.Chat.LinkAction.External, null, null, + t => t.TextSize = 14); + else + title.AddText(entry.GithubUser.DisplayName, t => t.TextSize = 14); //web: 12; ChangelogEntries.Add(title); TextFlowContainer messageContainer = new TextFlowContainer { @@ -255,6 +259,7 @@ namespace osu.Game.Overlays.Changelog { t.TextSize = 14; // web: 12, t.Colour = new Color4(235, 184, 254, 255); + t = new ClickableText(); }); ChangelogEntries.Add(messageContainer); } From c8f36a0c66626103df5611b143b1bfd19448f8c4 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 25 Jul 2018 03:58:08 +0200 Subject: [PATCH 0088/2854] Add links to GitHub; Remove reduntant using directive and variable --- osu.Game/Overlays/Changelog/ChangelogChart.cs | 4 +--- osu.Game/Overlays/Changelog/ChangelogContentGroup.cs | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index 3313e4adcc..c88eae98a4 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -22,7 +21,6 @@ namespace osu.Game.Overlays.Changelog private readonly Container container; private readonly Box background; - private readonly SpriteText text; private APIAccess api; public ChangelogChart() @@ -40,7 +38,7 @@ namespace osu.Game.Overlays.Changelog Colour = StreamColour.STABLE, RelativeSizeAxes = Axes.Both, }, - text = new SpriteText + new SpriteText { Text = "Graph Placeholder", TextSize = 28, diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 07e695b439..1346689cb7 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -259,7 +259,6 @@ namespace osu.Game.Overlays.Changelog { t.TextSize = 14; // web: 12, t.Colour = new Color4(235, 184, 254, 255); - t = new ClickableText(); }); ChangelogEntries.Add(messageContainer); } From 2906f4b401fcbc69828cf02ef1d06f00c4d4d21c Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 25 Jul 2018 14:31:41 +0200 Subject: [PATCH 0089/2854] Change initial chart colour --- .../Visual/TestCaseChangelogOverlay.cs | 20 +++++++++++++++++++ osu.Game/Overlays/Changelog/ChangelogChart.cs | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseChangelogOverlay.cs b/osu.Game.Tests/Visual/TestCaseChangelogOverlay.cs index 53143bd924..d64f1c00f7 100644 --- a/osu.Game.Tests/Visual/TestCaseChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseChangelogOverlay.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using NUnit.Framework; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; namespace osu.Game.Tests.Visual @@ -17,6 +18,25 @@ namespace osu.Game.Tests.Visual Add(changelog = new ChangelogOverlay()); AddStep(@"Show", changelog.Show); + AddStep(@"Hide", changelog.Hide); + AddWaitStep(3); + AddStep(@"Show with Lazer 2018.712.0", () => + { + changelog.FetchAndShowBuild(new APIChangelog + { + Version = "2018.712.0", + UpdateStream = new UpdateStream { Name = "lazer" }, + }); + changelog.Show(); + }); + AddWaitStep(3); + AddStep(@"Hide", changelog.Hide); + AddWaitStep(3); + AddStep(@"Show with listing", () => + { + changelog.ShowListing(); + changelog.Show(); + }); } } } diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index c88eae98a4..369982a2ac 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Changelog { background = new Box { - Colour = StreamColour.STABLE, + Colour = OsuColour.Gray(0), RelativeSizeAxes = Axes.Both, }, new SpriteText @@ -44,6 +44,7 @@ namespace osu.Game.Overlays.Changelog TextSize = 28, Anchor = Anchor.Centre, Origin = Anchor.Centre, + Colour = OsuColour.Gray(1), }, }, }; From a7a6e52c16a1f2e9dde1c397e090e0cd4b765a83 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 25 Jul 2018 14:58:18 +0200 Subject: [PATCH 0090/2854] Bring StreamBadge visual proportions closer to the upstream design --- osu.Game/Overlays/Changelog/StreamBadge.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index 8c881348c7..fc12d58f7a 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Changelog { public class StreamBadge : ClickableContainer { - private const float badge_height = 56.5f; + private const float badge_height = 66.5f; private const float badge_width = 100; private const float transition_duration = 100; @@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Changelog LatestBuild = latestBuild; Height = badge_height; Width = LatestBuild.IsFeatured ? badge_width * 2 : badge_width; - Margin = new MarginPadding(5); + Padding = new MarginPadding(5); isActivated = true; Children = new Drawable[] { @@ -55,24 +55,21 @@ namespace osu.Game.Overlays.Changelog { Text = LatestBuild.UpdateStream.DisplayName, Font = @"Exo2.0-Bold", - TextSize = 16, - Margin = new MarginPadding - { - Top = 7, - } + TextSize = 14, // web: 12, + Margin = new MarginPadding { Top = 6, }, }, new SpriteText { Text = LatestBuild.DisplayVersion, Font = @"Exo2.0-Light", - TextSize = 21, + TextSize = 20, // web: 16, }, new SpriteText { Text = LatestBuild.Users > 0 ? $"{LatestBuild.Users:N0} users online" : null, - TextSize = 12, + TextSize = 12, // web: 10, Font = @"Exo2.0-Regular", Colour = new Color4(203, 164, 218, 255), }, From 3e6968b57ebf6baf488f0e7bf893f57370bf1030 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 25 Jul 2018 17:04:36 +0200 Subject: [PATCH 0091/2854] Unify MarginPadding --- osu.Game/Overlays/Changelog/ChangelogBadges.cs | 12 +++--------- osu.Game/Overlays/Changelog/ChangelogContent.cs | 6 +++--- osu.Game/Overlays/Changelog/ChangelogContentGroup.cs | 2 +- osu.Game/Overlays/Changelog/Header/TextBadgePair.cs | 6 +----- osu.Game/Overlays/Changelog/StreamBadge.cs | 2 +- 5 files changed, 9 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogBadges.cs b/osu.Game/Overlays/Changelog/ChangelogBadges.cs index 1a5e4f7d18..365d808108 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBadges.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBadges.cs @@ -15,8 +15,8 @@ namespace osu.Game.Overlays.Changelog public class ChangelogBadges : Container { private const float container_height = 106.5f; - private const float padding_y = 20; - private const float padding_x = 85; + private const float vertical_padding = 20; + private const float horizontal_padding = 85; public delegate void SelectionHandler(APIChangelog releaseStream, EventArgs args); @@ -40,13 +40,7 @@ namespace osu.Game.Overlays.Changelog { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding - { - Top = padding_y, - Bottom = padding_y, - Left = padding_x, - Right = padding_x, - }, + Padding = new MarginPadding { Vertical = vertical_padding, Horizontal = horizontal_padding }, }, }; } diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 7e92ab4c26..588a61f678 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Changelog RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; - Padding = new MarginPadding{ Bottom = 100, }; + Padding = new MarginPadding{ Bottom = 100 }; } public void ShowListing(APIChangelog[] changelog) @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.Changelog RelativeSizeAxes = Axes.X, Height = 2, Colour = new Color4(17, 17, 17, 255), - Margin = new MarginPadding { Top = 30, }, + Margin = new MarginPadding { Top = 30 }, }); } changelogContentGroup = new ChangelogContentGroup(build, true); @@ -60,7 +60,7 @@ namespace osu.Game.Overlays.Changelog RelativeSizeAxes = Axes.X, Height = 1, Colour = new Color4(32, 24, 35, 255), - Margin = new MarginPadding { Top = 30, }, + Margin = new MarginPadding { Top = 30 }, }); changelogContentGroup = new ChangelogContentGroup(build, false); diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 1346689cb7..8768f3623b 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -105,7 +105,7 @@ namespace osu.Game.Overlays.Changelog Font = @"Exo2.0-Medium", Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Margin = new MarginPadding { Top = 5 } + Margin = new MarginPadding { Top = 5 }, }, ChangelogEntries = new FillFlowContainer { diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index 201524cc2f..dd4ca1ce0b 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -39,11 +39,7 @@ namespace osu.Game.Overlays.Changelog.Header Text = displayText, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Margin = new MarginPadding - { - Top = 5, - Bottom = 15, - } + Margin = new MarginPadding { Top = 5, Bottom = 15 }, }, LineBadge = new LineBadge(startCollapsed) { diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index fc12d58f7a..ad5a858545 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Changelog Text = LatestBuild.UpdateStream.DisplayName, Font = @"Exo2.0-Bold", TextSize = 14, // web: 12, - Margin = new MarginPadding { Top = 6, }, + Margin = new MarginPadding { Top = 6 }, }, new SpriteText { From dcb5eb1767c3772a033172524f81f0bbb45ab153 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 25 Jul 2018 17:05:29 +0200 Subject: [PATCH 0092/2854] Remove reduntant comment --- osu.Game/Overlays/Changelog/ChangelogChart.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index 369982a2ac..7880ad8021 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -13,7 +13,6 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Changelog { - // maybe look to osu.Game.Screens.Play.SquareGraph for reference later public class ChangelogChart : BufferedContainer { private const float height = 100; From fb8ea770aef05d199d49cfdaed7c5692dd73dd97 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 25 Jul 2018 18:55:02 +0200 Subject: [PATCH 0093/2854] Make ChangelogChart's child buffered --- osu.Game/Overlays/Changelog/ChangelogChart.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index 7880ad8021..ee41b8085b 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -13,12 +13,13 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Changelog { - public class ChangelogChart : BufferedContainer + public class ChangelogChart : Container { private const float height = 100; private const float transition_duration = 300; - private readonly Container container; + // why make the child buffered? https://streamable.com/swbdj + private readonly BufferedContainer container; private readonly Box background; private APIAccess api; @@ -26,7 +27,7 @@ namespace osu.Game.Overlays.Changelog { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - Child = container = new Container + Child = container = new BufferedContainer { RelativeSizeAxes = Axes.X, Height = height, From 81786c4763a3ccf68f66ac80f380b60243fad7cf Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 25 Jul 2018 20:01:24 +0200 Subject: [PATCH 0094/2854] Move statement to a new line --- osu.Game/Overlays/Changelog/ChangelogChart.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index ee41b8085b..d965651e27 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -54,7 +54,8 @@ namespace osu.Game.Overlays.Changelog { if (changelogChart != null) foreach (BuildHistory buildHistory in changelogChart.BuildHistory) - if (buildHistory.UserCount > 0) return false; + if (buildHistory.UserCount > 0) + return false; return true; } From e1a24f55cfc84cb0aef8be7784dae73c36f753ff Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 25 Jul 2018 20:31:26 +0200 Subject: [PATCH 0095/2854] Update comments --- osu.Game/Graphics/UserInterface/ClickableText.cs | 4 ++++ osu.Game/Graphics/UserInterface/TooltipIconButton.cs | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ClickableText.cs b/osu.Game/Graphics/UserInterface/ClickableText.cs index c27bc56238..159a2c3377 100644 --- a/osu.Game/Graphics/UserInterface/ClickableText.cs +++ b/osu.Game/Graphics/UserInterface/ClickableText.cs @@ -12,6 +12,10 @@ using System; namespace osu.Game.Graphics.UserInterface { + // created a new class instead of using a Link in + // some kind of textflowcontainer because they aren't + // capable of having delegates/actions on click + // and (probably) can't be disabled public class ClickableText : OsuSpriteText, IHasTooltip { private bool isEnabled; diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs index 65ab5de036..19c5421f6a 100644 --- a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -15,8 +15,7 @@ using System; namespace osu.Game.Graphics.UserInterface { // not inheriting osuclickablecontainer/osuhovercontainer - // because click/hover sounds cannot be disabled, and they make - // double sounds when reappearing under the cursor + // because click/hover sounds cannot be disabled public class TooltipIconButton : ClickableContainer, IHasTooltip { private readonly SpriteIcon icon; From 6e6e43e8dffade2c4c1a551448c3a8827b17b3ae Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Wed, 25 Jul 2018 21:38:32 +0200 Subject: [PATCH 0096/2854] ClickableText changes colour on hover --- .../Graphics/UserInterface/ClickableText.cs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/ClickableText.cs b/osu.Game/Graphics/UserInterface/ClickableText.cs index 159a2c3377..739c5067d2 100644 --- a/osu.Game/Graphics/UserInterface/ClickableText.cs +++ b/osu.Game/Graphics/UserInterface/ClickableText.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -20,9 +21,13 @@ namespace osu.Game.Graphics.UserInterface { private bool isEnabled; private bool isMuted; + private SampleChannel sampleHover; private SampleChannel sampleClick; + protected Color4 HoverColour; + protected Color4 IdleColour; + /// /// An action that can be set to execute after click. /// @@ -78,10 +83,19 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(InputState state) { if (isEnabled && !IsHoverMuted) + { + this.FadeColour(HoverColour, 500, Easing.OutQuint); sampleHover?.Play(); + } return base.OnHover(state); } + protected override void OnHoverLost(InputState state) + { + this.FadeColour(IdleColour, 500, Easing.OutQuint); + base.OnHoverLost(state); + } + protected override bool OnClick(InputState state) { if (isEnabled) @@ -93,13 +107,20 @@ namespace osu.Game.Graphics.UserInterface return base.OnClick(state); } + protected override void LoadComplete() + { + IdleColour = Colour; + base.LoadComplete(); + } + public string TooltipText { get; set; } [BackgroundDependencyLoader] - private void load(AudioManager audio) + private void load(AudioManager audio, OsuColour colours) { sampleClick = audio.Sample.Get(@"UI/generic-select-soft"); sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); + HoverColour = colours.Yellow; } } } From 93dbc55738957e7561a2cf1f128dd804e30b825e Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Thu, 26 Jul 2018 10:47:43 +0200 Subject: [PATCH 0097/2854] Fix wrong conditional expression --- osu.Game/Graphics/UserInterface/ClickableText.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ClickableText.cs b/osu.Game/Graphics/UserInterface/ClickableText.cs index 739c5067d2..e0121ae28c 100644 --- a/osu.Game/Graphics/UserInterface/ClickableText.cs +++ b/osu.Game/Graphics/UserInterface/ClickableText.cs @@ -82,10 +82,11 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(InputState state) { - if (isEnabled && !IsHoverMuted) + if (isEnabled) { this.FadeColour(HoverColour, 500, Easing.OutQuint); - sampleHover?.Play(); + if (!IsHoverMuted) + sampleHover?.Play(); } return base.OnHover(state); } From a2959e9171114496ec61b56f982caefae22d7e26 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Thu, 26 Jul 2018 13:43:47 +0200 Subject: [PATCH 0098/2854] Remove default value without use --- osu.Game/Overlays/Changelog/ChangelogContentGroup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 8768f3623b..3e0023ea9a 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -116,7 +116,7 @@ namespace osu.Game.Overlays.Changelog }; } - public ChangelogContentGroup(APIChangelog build, bool newDate = false) + public ChangelogContentGroup(APIChangelog build, bool newDate) { ClickableText clickableText; RelativeSizeAxes = Axes.X; From 0f263e2ccabfd9bb751148527c860baf0261d62f Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Thu, 26 Jul 2018 14:19:58 +0200 Subject: [PATCH 0099/2854] Delete ClickableText class, use OsuHoverContainer instead --- .../Visual/TestCaseClickableText.cs | 30 ----- .../Graphics/UserInterface/ClickableText.cs | 127 ------------------ .../Changelog/ChangelogContentGroup.cs | 49 ++++--- 3 files changed, 29 insertions(+), 177 deletions(-) delete mode 100644 osu.Game.Tests/Visual/TestCaseClickableText.cs delete mode 100644 osu.Game/Graphics/UserInterface/ClickableText.cs diff --git a/osu.Game.Tests/Visual/TestCaseClickableText.cs b/osu.Game.Tests/Visual/TestCaseClickableText.cs deleted file mode 100644 index 60ee764cfc..0000000000 --- a/osu.Game.Tests/Visual/TestCaseClickableText.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using System; -using System.Collections.Generic; - -namespace osu.Game.Tests.Visual -{ - public class TestCaseClickableText : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(ClickableText), typeof(FillFlowContainer) }; - - private readonly ClickableText text; - public TestCaseClickableText() => Child = new FillFlowContainer - { - Children = new[] - { - new ClickableText { Text = "Default", }, - new ClickableText { IsEnabled = false, Text = "Disabled", }, - new ClickableText { Text = "Without sounds", IsMuted = true, }, - new ClickableText { Text = "Without click sounds", IsClickMuted = true, }, - new ClickableText { Text = "Without hover sounds", IsHoverMuted = true, }, - text = new ClickableText { Text = "Disables after click (Action)", Action = () => text.IsEnabled = false }, - new ClickableText { Text = "Has tooltip", TooltipText = "Yep", }, - } - }; - } -} diff --git a/osu.Game/Graphics/UserInterface/ClickableText.cs b/osu.Game/Graphics/UserInterface/ClickableText.cs deleted file mode 100644 index e0121ae28c..0000000000 --- a/osu.Game/Graphics/UserInterface/ClickableText.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Input.States; -using osu.Game.Graphics.Sprites; -using System; - -namespace osu.Game.Graphics.UserInterface -{ - // created a new class instead of using a Link in - // some kind of textflowcontainer because they aren't - // capable of having delegates/actions on click - // and (probably) can't be disabled - public class ClickableText : OsuSpriteText, IHasTooltip - { - private bool isEnabled; - private bool isMuted; - - private SampleChannel sampleHover; - private SampleChannel sampleClick; - - protected Color4 HoverColour; - protected Color4 IdleColour; - - /// - /// An action that can be set to execute after click. - /// - public Action Action; - - /// - /// If set to true, a sound will be played on click. - /// - public bool IsClickMuted; - - /// - /// If set to true, a sound will be played on hover. - /// - public bool IsHoverMuted; - - /// - /// If disabled, no sounds will be played and wont execute. - /// True by default. - /// - public bool IsEnabled - { - get { return isEnabled; } - set - { - isEnabled = value; - this.FadeTo(value ? 1 : 0.5f, 250); - } - } - - /// - /// Whether to play sounds on hover and click. Automatically sets - /// and to the same value.> - /// - public bool IsMuted { - get { return isMuted; } - set - { - IsHoverMuted = value; - IsClickMuted = value; - isMuted = value; - } - } - - /// - /// A text with sounds on hover and click, - /// an action that can be set to execute on click, - /// and a tooltip. - /// - public ClickableText() => isEnabled = true; - - public override bool HandleMouseInput => true; - - protected override bool OnHover(InputState state) - { - if (isEnabled) - { - this.FadeColour(HoverColour, 500, Easing.OutQuint); - if (!IsHoverMuted) - sampleHover?.Play(); - } - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - this.FadeColour(IdleColour, 500, Easing.OutQuint); - base.OnHoverLost(state); - } - - protected override bool OnClick(InputState state) - { - if (isEnabled) - { - if (!IsClickMuted) - sampleClick?.Play(); - Action?.Invoke(); - } - return base.OnClick(state); - } - - protected override void LoadComplete() - { - IdleColour = Colour; - base.LoadComplete(); - } - - public string TooltipText { get; set; } - - [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuColour colours) - { - sampleClick = audio.Sample.Get(@"UI/generic-select-soft"); - sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); - HoverColour = colours.Yellow; - } - } -} diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 3e0023ea9a..3208f424ce 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -118,7 +118,7 @@ namespace osu.Game.Overlays.Changelog public ChangelogContentGroup(APIChangelog build, bool newDate) { - ClickableText clickableText; + OsuHoverContainer clickableBuildText; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; @@ -138,29 +138,33 @@ namespace osu.Game.Overlays.Changelog Margin = new MarginPadding { Top = 20 }, Alpha = newDate ? 1 : 0, }, - new FillFlowContainer + clickableBuildText = new OsuHoverContainer { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, Margin = new MarginPadding { Top = 20 }, - Spacing = new Vector2(5), - Children = new Drawable[] + Action = () => OnBuildSelected(build), + Child = new FillFlowContainer { - new SpriteText + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), + AutoSizeAxes = Axes.Both, + Children = new Drawable[] { - Text = build.UpdateStream.DisplayName, - TextSize = 20, // web: 18, - Font = @"Exo2.0-Medium", - }, - clickableText = new ClickableText - { - Text = build.DisplayVersion, - TextSize = 20, // web: 18, - Font = @"Exo2.0-Light", - Colour = StreamColour.FromStreamName(build.UpdateStream.Name), - Action = () => OnBuildSelected(build), + new SpriteText + { + Text = build.UpdateStream.DisplayName, + TextSize = 20, // web: 18, + Font = @"Exo2.0-Medium", + }, + new SpriteText + { + Text = build.DisplayVersion, + TextSize = 20, // web: 18, + Font = @"Exo2.0-Light", + Colour = StreamColour.FromStreamName(build.UpdateStream.Name), + }, }, } }, @@ -173,10 +177,15 @@ namespace osu.Game.Overlays.Changelog }; // we may not want double clicks to make it double the work - clickableText.Action += () => + // can be clicked again only after a delay + clickableBuildText.Action += () => { - clickableText.IsEnabled = false; - Scheduler.AddDelayed(() => clickableText.IsEnabled = true, 2000); + clickableBuildText.Action = null; + clickableBuildText.FadeTo(0.5f, 500); + Scheduler.AddDelayed(() => { + clickableBuildText.Action = () => OnBuildSelected(build); + clickableBuildText.FadeIn(500); + }, 2000); }; } From 2d8277f4137868e91d620e24d881fc4c63c092f5 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Thu, 26 Jul 2018 17:02:51 +0200 Subject: [PATCH 0100/2854] Plotting the chart 1 --- osu.Game/Overlays/Changelog/ChangelogChart.cs | 54 ++++++++++++++++--- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index d965651e27..0141a3b3e1 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -3,13 +3,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.Framework.Logging; using osu.Game.Graphics; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; +using System; namespace osu.Game.Overlays.Changelog { @@ -19,7 +22,7 @@ namespace osu.Game.Overlays.Changelog private const float transition_duration = 300; // why make the child buffered? https://streamable.com/swbdj - private readonly BufferedContainer container; + private readonly Container container; private readonly Box background; private APIAccess api; @@ -27,17 +30,17 @@ namespace osu.Game.Overlays.Changelog { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - Child = container = new BufferedContainer + Child = container = new Container { RelativeSizeAxes = Axes.X, Height = height, Children = new Drawable[] { - background = new Box - { - Colour = OsuColour.Gray(0), - RelativeSizeAxes = Axes.Both, - }, + //background = new Box + //{ + // Colour = OsuColour.Gray(0), + // RelativeSizeAxes = Axes.Both, + //}, new SpriteText { Text = "Graph Placeholder", @@ -63,8 +66,8 @@ namespace osu.Game.Overlays.Changelog { if (!isEmpty(chartInfo)) { - background.Colour = StreamColour.FromStreamName(updateStreamName); container.MoveToY(0, transition_duration, Easing.InOutQuad).FadeIn(transition_duration); + plotChart(chartInfo, StreamColour.FromStreamName(updateStreamName)); } else container.MoveToY(-height, transition_duration, Easing.InOutQuad).FadeOut(transition_duration); @@ -89,5 +92,40 @@ namespace osu.Game.Overlays.Changelog req.Success += res => showChart(res); api.Queue(req); } + + // this could probably be combined with isEmpty, todo + private float getMaxUserCount(APIChangelogChart changelogChartInfo) + { + var maxUserCount = 0l; + foreach (BuildHistory build in changelogChartInfo.BuildHistory) + { + if (build.UserCount > maxUserCount) + maxUserCount = build.UserCount; + } + return maxUserCount; + } + + private void plotChart(APIChangelogChart changelogChartInfo, ColourInfo colour) + { + var maxUserCount = getMaxUserCount(changelogChartInfo); + var currentPos = 0f; + + container.Clear(); + + foreach (BuildHistory build in changelogChartInfo.BuildHistory) + { + container.Add(new Box + { + RelativeSizeAxes = Axes.Y, + Width = Math.Max(container.DrawWidth / changelogChartInfo.BuildHistory.Count, 2), + Height = build.UserCount / maxUserCount, + X = currentPos, + Colour = colour, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + }); + currentPos += container.DrawWidth / changelogChartInfo.BuildHistory.Count; + } + } } } From 516ba10dadbe321a19ae285392fa9719d468ccb5 Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Thu, 26 Jul 2018 17:26:42 +0200 Subject: [PATCH 0101/2854] Plotting the chart 2 --- osu.Game/Overlays/Changelog/ChangelogChart.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index 0141a3b3e1..730c0e2ad7 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Logging; using osu.Game.Graphics; using osu.Game.Online.API; using osu.Game.Online.API.Requests; @@ -30,6 +29,7 @@ namespace osu.Game.Overlays.Changelog { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + Masking = true; Child = container = new Container { RelativeSizeAxes = Axes.X, @@ -117,12 +117,13 @@ namespace osu.Game.Overlays.Changelog container.Add(new Box { RelativeSizeAxes = Axes.Y, - Width = Math.Max(container.DrawWidth / changelogChartInfo.BuildHistory.Count, 2), + Width = Math.Max(container.DrawWidth / changelogChartInfo.BuildHistory.Count, 1), Height = build.UserCount / maxUserCount, X = currentPos, Colour = colour, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, + Blending = BlendingMode.None, }); currentPos += container.DrawWidth / changelogChartInfo.BuildHistory.Count; } From 46134ed63d7bc012b67701a15dcc3b5155de14fd Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Thu, 26 Jul 2018 23:53:12 +0200 Subject: [PATCH 0102/2854] Add "ok" chart plotting; Add more keys to StreamColour .FromStreamName function --- osu.Game/Graphics/StreamColour.cs | 5 ++ osu.Game/Overlays/Changelog/ChangelogChart.cs | 86 ++++++++++++------- 2 files changed, 60 insertions(+), 31 deletions(-) diff --git a/osu.Game/Graphics/StreamColour.cs b/osu.Game/Graphics/StreamColour.cs index 71626a0e3a..9149c29b0a 100644 --- a/osu.Game/Graphics/StreamColour.cs +++ b/osu.Game/Graphics/StreamColour.cs @@ -19,10 +19,15 @@ namespace osu.Game.Graphics private static readonly Dictionary colours = new Dictionary { { "stable40", STABLE }, + { "Stable", STABLE }, { "stable", STABLEFALLBACK }, + { "Stable Fallback", STABLEFALLBACK }, { "beta40", BETA }, + { "Beta", BETA }, { "cuttingedge", CUTTINGEDGE }, + { "Cutting Edge", CUTTINGEDGE }, { "lazer", LAZER }, + { "Lazer", LAZER }, { "web", WEB }, }; diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs index 730c0e2ad7..57b3022724 100644 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ b/osu.Game/Overlays/Changelog/ChangelogChart.cs @@ -8,10 +8,12 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using System; +using System.Collections.Generic; namespace osu.Game.Overlays.Changelog { @@ -29,27 +31,10 @@ namespace osu.Game.Overlays.Changelog { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - Masking = true; Child = container = new Container { RelativeSizeAxes = Axes.X, Height = height, - Children = new Drawable[] - { - //background = new Box - //{ - // Colour = OsuColour.Gray(0), - // RelativeSizeAxes = Axes.Both, - //}, - new SpriteText - { - Text = "Graph Placeholder", - TextSize = 28, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Colour = OsuColour.Gray(1), - }, - }, }; } @@ -67,7 +52,10 @@ namespace osu.Game.Overlays.Changelog if (!isEmpty(chartInfo)) { container.MoveToY(0, transition_duration, Easing.InOutQuad).FadeIn(transition_duration); - plotChart(chartInfo, StreamColour.FromStreamName(updateStreamName)); + if (string.IsNullOrEmpty(updateStreamName)) + plotCharts(chartInfo); + else + plotChart(chartInfo, StreamColour.FromStreamName(updateStreamName)); } else container.MoveToY(-height, transition_duration, Easing.InOutQuad).FadeOut(transition_duration); @@ -105,27 +93,63 @@ namespace osu.Game.Overlays.Changelog return maxUserCount; } + private List clearUpDips(List buildHistories, float maxUserCount) + { + var buildHistory = new List(); + foreach (BuildHistory build in buildHistories) + { + if (build.UserCount / maxUserCount > 0.2f) + buildHistory.Add(build.UserCount); + } + return buildHistory; + } + private void plotChart(APIChangelogChart changelogChartInfo, ColourInfo colour) { var maxUserCount = getMaxUserCount(changelogChartInfo); - var currentPos = 0f; - container.Clear(); + container.Child = new BarGraph + { + Colour = colour, + Values = clearUpDips(changelogChartInfo.BuildHistory, maxUserCount), + RelativeSizeAxes = Axes.Both, + Direction = BarDirection.BottomToTop, + }; + } + + private void plotCharts(APIChangelogChart changelogChartInfo) + { + var maxUserCount = getMaxUserCount(changelogChartInfo); + + var releaseStreams = new Dictionary>(changelogChartInfo.Order.Count); + var highestUserCounts = new Dictionary(changelogChartInfo.Order.Count); + + foreach (string updateStream in changelogChartInfo.Order) + { + releaseStreams.Add(updateStream, new List()); + highestUserCounts.Add(updateStream, 0); + } foreach (BuildHistory build in changelogChartInfo.BuildHistory) { - container.Add(new Box + releaseStreams[build.Label].Add(build.UserCount); + if (highestUserCounts[build.Label] < build.UserCount) + highestUserCounts[build.Label] = build.UserCount; + } + + container.Clear(); + + foreach (KeyValuePair> releaseStream in releaseStreams) + { + var barGraph = new BarGraph { - RelativeSizeAxes = Axes.Y, - Width = Math.Max(container.DrawWidth / changelogChartInfo.BuildHistory.Count, 1), - Height = build.UserCount / maxUserCount, - X = currentPos, - Colour = colour, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Blending = BlendingMode.None, - }); - currentPos += container.DrawWidth / changelogChartInfo.BuildHistory.Count; + Colour = StreamColour.FromStreamName(releaseStream.Key), + Values = releaseStream.Value, + RelativeSizeAxes = Axes.Both, + Direction = BarDirection.BottomToTop, + //Height = highestUserCounts[releaseStream.Key] / maxUserCount, + }; + container.Add(barGraph); } } } From fba817b4e110f7c44f30dd79139c3ede7c8f39df Mon Sep 17 00:00:00 2001 From: HoutarouOreki Date: Fri, 27 Jul 2018 00:34:44 +0200 Subject: [PATCH 0103/2854] Resign ChangelogChart and disable its references --- osu.Game/Overlays/Changelog/ChangelogChart.cs | 156 ------------------ osu.Game/Overlays/ChangelogOverlay.cs | 20 ++- 2 files changed, 12 insertions(+), 164 deletions(-) delete mode 100644 osu.Game/Overlays/Changelog/ChangelogChart.cs diff --git a/osu.Game/Overlays/Changelog/ChangelogChart.cs b/osu.Game/Overlays/Changelog/ChangelogChart.cs deleted file mode 100644 index 57b3022724..0000000000 --- a/osu.Game/Overlays/Changelog/ChangelogChart.cs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -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.UserInterface; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Online.API.Requests.Responses; -using System; -using System.Collections.Generic; - -namespace osu.Game.Overlays.Changelog -{ - public class ChangelogChart : Container - { - private const float height = 100; - private const float transition_duration = 300; - - // why make the child buffered? https://streamable.com/swbdj - private readonly Container container; - private readonly Box background; - private APIAccess api; - - public ChangelogChart() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Child = container = new Container - { - RelativeSizeAxes = Axes.X, - Height = height, - }; - } - - private bool isEmpty(APIChangelogChart changelogChart) - { - if (changelogChart != null) - foreach (BuildHistory buildHistory in changelogChart.BuildHistory) - if (buildHistory.UserCount > 0) - return false; - return true; - } - - private void showChart(APIChangelogChart chartInfo, string updateStreamName = null) - { - if (!isEmpty(chartInfo)) - { - container.MoveToY(0, transition_duration, Easing.InOutQuad).FadeIn(transition_duration); - if (string.IsNullOrEmpty(updateStreamName)) - plotCharts(chartInfo); - else - plotChart(chartInfo, StreamColour.FromStreamName(updateStreamName)); - } - else - container.MoveToY(-height, transition_duration, Easing.InOutQuad).FadeOut(transition_duration); - } - - [BackgroundDependencyLoader] - private void load(APIAccess api) - { - this.api = api; - } - - public void ShowUpdateStream(string updateStream) - { - var req = new GetChangelogChartRequest(updateStream); - req.Success += res => showChart(res, updateStream); - api.Queue(req); - } - - public void ShowAllUpdateStreams() - { - var req = new GetChangelogChartRequest(); - req.Success += res => showChart(res); - api.Queue(req); - } - - // this could probably be combined with isEmpty, todo - private float getMaxUserCount(APIChangelogChart changelogChartInfo) - { - var maxUserCount = 0l; - foreach (BuildHistory build in changelogChartInfo.BuildHistory) - { - if (build.UserCount > maxUserCount) - maxUserCount = build.UserCount; - } - return maxUserCount; - } - - private List clearUpDips(List buildHistories, float maxUserCount) - { - var buildHistory = new List(); - foreach (BuildHistory build in buildHistories) - { - if (build.UserCount / maxUserCount > 0.2f) - buildHistory.Add(build.UserCount); - } - return buildHistory; - } - - private void plotChart(APIChangelogChart changelogChartInfo, ColourInfo colour) - { - var maxUserCount = getMaxUserCount(changelogChartInfo); - - container.Child = new BarGraph - { - Colour = colour, - Values = clearUpDips(changelogChartInfo.BuildHistory, maxUserCount), - RelativeSizeAxes = Axes.Both, - Direction = BarDirection.BottomToTop, - }; - } - - private void plotCharts(APIChangelogChart changelogChartInfo) - { - var maxUserCount = getMaxUserCount(changelogChartInfo); - - var releaseStreams = new Dictionary>(changelogChartInfo.Order.Count); - var highestUserCounts = new Dictionary(changelogChartInfo.Order.Count); - - foreach (string updateStream in changelogChartInfo.Order) - { - releaseStreams.Add(updateStream, new List()); - highestUserCounts.Add(updateStream, 0); - } - - foreach (BuildHistory build in changelogChartInfo.BuildHistory) - { - releaseStreams[build.Label].Add(build.UserCount); - if (highestUserCounts[build.Label] < build.UserCount) - highestUserCounts[build.Label] = build.UserCount; - } - - container.Clear(); - - foreach (KeyValuePair> releaseStream in releaseStreams) - { - var barGraph = new BarGraph - { - Colour = StreamColour.FromStreamName(releaseStream.Key), - Values = releaseStream.Value, - RelativeSizeAxes = Axes.Both, - Direction = BarDirection.BottomToTop, - //Height = highestUserCounts[releaseStream.Key] / maxUserCount, - }; - container.Add(barGraph); - } - } - } -} diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index a0d0726c01..a14fa77da3 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays { private readonly ChangelogHeader header; private readonly ChangelogBadges badges; - private readonly ChangelogChart chart; + //private readonly ChangelogChart chart; private readonly ChangelogContent listing; private readonly ChangelogContent content; @@ -85,7 +85,7 @@ namespace osu.Game.Overlays { header = new ChangelogHeader(), badges = new ChangelogBadges(), - chart = new ChangelogChart(), + //chart = new ChangelogChart(), listing = new ChangelogContent(), content = new ChangelogContent() }, @@ -162,7 +162,6 @@ namespace osu.Game.Overlays isAtListing = true; var req = new GetChangelogRequest(); badges.SelectNone(); - chart.ShowAllUpdateStreams(); req.Success += listing.ShowListing; api.Queue(req); } @@ -176,7 +175,7 @@ namespace osu.Game.Overlays content.Hide(); listing.Show(); badges.SelectNone(); - chart.ShowAllUpdateStreams(); + //chart.ShowAllUpdateStreams(); listing.Show(); scroll.ScrollTo(savedScrollPosition); } @@ -184,7 +183,12 @@ namespace osu.Game.Overlays /// /// Fetches and shows a specific build from a specific update stream. /// - public void FetchAndShowBuild(APIChangelog build, bool sentByBadges = false) + /// Must contain at least and + /// . If and + /// are specified, the header will instantly display them. + /// Whether to update badges. Should be set to false in case + /// the function is called by selecting a badge, to avoid an infinite loop. + public void FetchAndShowBuild(APIChangelog build, bool updateBadges = true) { var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); @@ -193,17 +197,17 @@ namespace osu.Game.Overlays else req.Success += res => header.ShowBuild(res.UpdateStream.DisplayName, res.DisplayVersion); - if (!sentByBadges) + if (updateBadges) badges.SelectUpdateStream(build.UpdateStream.Name); - chart.ShowUpdateStream(build.UpdateStream.Name); + //chart.ShowUpdateStream(build.UpdateStream.Name); req.Success += apiChangelog => { listing.Hide(); content.Show(); content.ShowBuild(apiChangelog); if (scroll.Current > scroll.GetChildPosInContent(content)) - scroll.ScrollTo(chart); + scroll.ScrollTo(content); if (isAtListing) savedScrollPosition = scroll.Current; isAtListing = false; From 6a5908801403a25db3864730b2fc615c1d44b1bf Mon Sep 17 00:00:00 2001 From: Houtarou Oreki Date: Wed, 22 Aug 2018 13:43:39 +0200 Subject: [PATCH 0104/2854] Move xml docs from ctors onto classes --- osu.Game/Graphics/UserInterface/LineBadge.cs | 10 +++++----- osu.Game/Graphics/UserInterface/TooltipIconButton.cs | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/LineBadge.cs b/osu.Game/Graphics/UserInterface/LineBadge.cs index 0283559aee..aaf225eb4c 100644 --- a/osu.Game/Graphics/UserInterface/LineBadge.cs +++ b/osu.Game/Graphics/UserInterface/LineBadge.cs @@ -6,6 +6,11 @@ using osu.Framework.Graphics.Shapes; namespace osu.Game.Graphics.UserInterface { + /// + /// A simple rounded expandable line. Set its + /// property to the center of the edge it's meant stick with. By default, + /// takes up the full parent's axis defined by . + /// public class LineBadge : Circle { public float UncollapsedSize; @@ -39,11 +44,6 @@ namespace osu.Game.Graphics.UserInterface } } - /// - /// A simple rounded expandable line. Set its - /// property to the center of the edge it's meant stick with. By default, - /// takes up the full parent's axis defined by . - /// /// Whether to initialize with the /// or the . public LineBadge(bool startCollapsed = true) diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs index 19c5421f6a..86fdd32bd2 100644 --- a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -16,6 +16,9 @@ namespace osu.Game.Graphics.UserInterface { // not inheriting osuclickablecontainer/osuhovercontainer // because click/hover sounds cannot be disabled + /// + /// An icon with an action upon click that can be disabled. + /// public class TooltipIconButton : ClickableContainer, IHasTooltip { private readonly SpriteIcon icon; @@ -48,9 +51,6 @@ namespace osu.Game.Graphics.UserInterface set { icon.Icon = value; } } - /// - /// A simple icon that has an action upon click and can be disabled. - /// public TooltipIconButton() { isEnabled = true; From 164a1a8e6e25797493c7ff44154ce90e6e776774 Mon Sep 17 00:00:00 2001 From: Houtarou Oreki Date: Wed, 22 Aug 2018 13:47:09 +0200 Subject: [PATCH 0105/2854] Remove unnecessary inheritance --- osu.Game/Graphics/UserInterface/TooltipIconButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs index 86fdd32bd2..ffd7153570 100644 --- a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -19,7 +19,7 @@ namespace osu.Game.Graphics.UserInterface /// /// An icon with an action upon click that can be disabled. /// - public class TooltipIconButton : ClickableContainer, IHasTooltip + public class TooltipIconButton : Container, IHasTooltip { private readonly SpriteIcon icon; private SampleChannel sampleHover; From a817164cb2cf339261f4b86cf339b4b7f159a3af Mon Sep 17 00:00:00 2001 From: Houtarou Oreki Date: Wed, 22 Aug 2018 13:49:08 +0200 Subject: [PATCH 0106/2854] Update comments --- osu.Game/Graphics/UserInterface/LineBadge.cs | 2 +- osu.Game/Graphics/UserInterface/TooltipIconButton.cs | 6 ++---- osu.Game/Overlays/Changelog/ChangelogContentGroup.cs | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/LineBadge.cs b/osu.Game/Graphics/UserInterface/LineBadge.cs index aaf225eb4c..53f057f4b3 100644 --- a/osu.Game/Graphics/UserInterface/LineBadge.cs +++ b/osu.Game/Graphics/UserInterface/LineBadge.cs @@ -20,7 +20,7 @@ namespace osu.Game.Graphics.UserInterface private bool isHorizontal; /// - /// Automatically sets the RelativeSizeAxes and switches X and Y components when changed. + /// Automatically sets the RelativeSizeAxes and switches X and Y size components when changed. /// public bool IsHorizontal { diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs index ffd7153570..f26df3f8c1 100644 --- a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -14,8 +14,6 @@ using System; namespace osu.Game.Graphics.UserInterface { - // not inheriting osuclickablecontainer/osuhovercontainer - // because click/hover sounds cannot be disabled /// /// An icon with an action upon click that can be disabled. /// @@ -26,14 +24,14 @@ namespace osu.Game.Graphics.UserInterface private SampleChannel sampleClick; /// - /// The action to fire upon click, if is set to true. + /// The action to fire upon click if is set to true. /// public Action Action; private bool isEnabled; /// - /// If set to true, upon click the will execute. It wont otherwise. + /// If set to true, upon click the will execute. /// public bool IsEnabled { diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 3208f424ce..c349f44d37 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -35,7 +35,6 @@ namespace osu.Game.Overlays.Changelog Padding = new MarginPadding { Horizontal = 70 }; Children = new Drawable[] { - // build version, arrows new FillFlowContainer { Anchor = Anchor.TopCentre, @@ -176,7 +175,7 @@ namespace osu.Game.Overlays.Changelog }, }; - // we may not want double clicks to make it double the work + // we may not want double clicks, // can be clicked again only after a delay clickableBuildText.Action += () => { From b72d44b1e805c5b142bac0543126ffae6df94119 Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Wed, 5 Sep 2018 22:54:07 +0200 Subject: [PATCH 0107/2854] Added OsuModDeflate class and adjusted OsuRuleset.cs --- osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs | 44 +++++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 5 +++ 2 files changed, 49 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs new file mode 100644 index 0000000000..ec80537a52 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModDeflate : Mod, IApplicableToDrawableHitObjects + { + public override string Name => "Deflate"; + public override string ShortenedName => "DF"; + public override FontAwesome Icon => FontAwesome.fa_compress; + public override ModType Type => ModType.Fun; + public override string Description => "Become one with the approach circle..."; + public override double ScoreMultiplier => 1; + + public void ApplyToDrawableHitObjects(IEnumerable drawables) + { + foreach (var drawable in drawables) + drawable.ApplyCustomUpdateState += drawableOnApplyCustomUpdateState; + } + + protected void drawableOnApplyCustomUpdateState(DrawableHitObject drawable, ArmedState state) + { + if (!(drawable is DrawableHitCircle d)) + return; + + d.ApproachCircle.Hide(); + var h = d.HitObject; + + using (d.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) + { + var origScale = d.Scale; + d.ScaleTo(1.1f); + d.ScaleTo(origScale, h.TimePreempt); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index fa6e9a018a..461f7188c9 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -117,6 +117,11 @@ namespace osu.Game.Rulesets.Osu new OsuModRelax(), new OsuModAutopilot(), }; + case ModType.Fun: + return new Mod[] + { + new OsuModDeflate(), + }; default: return new Mod[] { }; } From dbc2eb4771f985b9e6e87b0925bf62f897555272 Mon Sep 17 00:00:00 2001 From: Kyle Chang Date: Tue, 18 Sep 2018 23:15:36 -0400 Subject: [PATCH 0108/2854] Create ZoomIn Mod --- osu.Game.Rulesets.Osu/Mods/OsuModZoomIn.cs | 55 ++++++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 1 + 2 files changed, 56 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModZoomIn.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModZoomIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModZoomIn.cs new file mode 100644 index 0000000000..b531b9b3d0 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModZoomIn.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModZoomIn : Mod, IApplicableToDrawableHitObjects + { + public override string Name => "Zoom In"; + public override string ShortenedName => "ZI"; + public override FontAwesome Icon => FontAwesome.fa_dot_circle_o; + public override ModType Type => ModType.Fun; + public override string Description => "Circles zoom in. No approach circles."; + public override double ScoreMultiplier => 1; + + public void ApplyToDrawableHitObjects(IEnumerable drawables) + { + foreach (var drawable in drawables) + { + drawable.ApplyCustomUpdateState += ApplyBounceState; + } + } + + protected void ApplyBounceState(DrawableHitObject drawable, ArmedState state) + { + if (!(drawable is DrawableOsuHitObject d)) + return; + + var h = (OsuHitObject)drawable.HitObject; + + double appearTime = h.StartTime - h.TimePreempt; + double moveDuration = h.TimePreempt; + + using (drawable.BeginAbsoluteSequence(appearTime, true)) + { + var origScale = drawable.Scale; + + drawable + .ScaleTo(0.0f) + .ScaleTo(origScale, moveDuration, Easing.InOutSine); + } + + // Hide approach circle + (drawable as DrawableHitCircle)?.ApproachCircle.Hide(); + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 6736d10dab..029ca7d43e 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -121,6 +121,7 @@ namespace osu.Game.Rulesets.Osu return new Mod[] { new OsuModTransform(), new OsuModWiggle(), + new OsuModZoomIn(), }; default: return new Mod[] { }; From 1e3599bddef4004561454f049b18439c607f93c8 Mon Sep 17 00:00:00 2001 From: Kyle Chang Date: Wed, 19 Sep 2018 23:56:25 -0400 Subject: [PATCH 0109/2854] Disable fade in --- osu.Game.Rulesets.Osu/Mods/OsuModZoomIn.cs | 51 ++++++++++++++++------ 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModZoomIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModZoomIn.cs index b531b9b3d0..8e73ece12c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModZoomIn.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModZoomIn.cs @@ -2,13 +2,13 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using System.Linq; using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; -using OpenTK; namespace osu.Game.Rulesets.Osu.Mods { @@ -31,25 +31,48 @@ namespace osu.Game.Rulesets.Osu.Mods protected void ApplyBounceState(DrawableHitObject drawable, ArmedState state) { - if (!(drawable is DrawableOsuHitObject d)) - return; + if (!(drawable is DrawableOsuHitObject)) return; + if (state != ArmedState.Idle) return; var h = (OsuHitObject)drawable.HitObject; + var appearTime = h.StartTime - h.TimePreempt; + var moveDuration = h.TimePreempt; - double appearTime = h.StartTime - h.TimePreempt; - double moveDuration = h.TimePreempt; - - using (drawable.BeginAbsoluteSequence(appearTime, true)) + switch (drawable) { - var origScale = drawable.Scale; + case DrawableHitCircle circle: + foreach (var t in circle.Transforms.Where(t => t.TargetMember == "Alpha")) + circle.RemoveTransform(t); + using (circle.BeginAbsoluteSequence(appearTime, true)) + { + var origScale = drawable.Scale; - drawable - .ScaleTo(0.0f) - .ScaleTo(origScale, moveDuration, Easing.InOutSine); + circle + .ScaleTo(0) + .ScaleTo(origScale, moveDuration, Easing.OutSine) + .FadeTo(1); + } + + circle.ApproachCircle.Hide(); + + break; + + case DrawableSlider slider: + foreach (var t in slider.Transforms.Where(t => t.TargetMember == "Alpha")) + slider.RemoveTransform(t); + + using (slider.BeginAbsoluteSequence(appearTime, true)) + { + var origScale = slider.Scale; + + slider + .ScaleTo(0) + .ScaleTo(origScale, moveDuration, Easing.OutSine) + .FadeTo(1); + } + + break; } - - // Hide approach circle - (drawable as DrawableHitCircle)?.ApproachCircle.Hide(); } } } From 51d26fb648727a7db020cdd1367dbd86e13209ee Mon Sep 17 00:00:00 2001 From: Kyle Chang Date: Thu, 20 Sep 2018 19:06:37 -0400 Subject: [PATCH 0110/2854] Now spin in mod; circles spin in --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 1 + .../Mods/{OsuModZoomIn.cs => OsuModSpinIn.cs} | 44 +++++++++++++------ osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- 3 files changed, 32 insertions(+), 15 deletions(-) rename osu.Game.Rulesets.Osu/Mods/{OsuModZoomIn.cs => OsuModSpinIn.cs} (54%) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 4eff2a55c8..b3fb1b57a6 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Description => @"Play with no approach circles and fading circles/sliders."; public override double ScoreMultiplier => 1.06; + public override Type[] IncompatibleMods => new[] { typeof(OsuModSpinIn) }; private const double fade_in_duration_multiplier = 0.4; private const double fade_out_duration_multiplier = 0.3; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModZoomIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs similarity index 54% rename from osu.Game.Rulesets.Osu/Mods/OsuModZoomIn.cs rename to osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs index 8e73ece12c..3bb7d5933c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModZoomIn.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs @@ -1,35 +1,43 @@ // Copyright (c) 2007-2018 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.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; +using OpenTK; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModZoomIn : Mod, IApplicableToDrawableHitObjects + public class OsuModSpinIn : Mod, IApplicableToDrawableHitObjects { - public override string Name => "Zoom In"; - public override string ShortenedName => "ZI"; - public override FontAwesome Icon => FontAwesome.fa_dot_circle_o; + public override string Name => "Spin In"; + public override string ShortenedName => "SI"; + public override FontAwesome Icon => FontAwesome.fa_rotate_right; public override ModType Type => ModType.Fun; - public override string Description => "Circles zoom in. No approach circles."; + public override string Description => "Circle spin in. No approach circles."; public override double ScoreMultiplier => 1; + public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden) }; + + private const int rotate_offset = 360; + private const float rotate_starting_width = 2.5f; + public void ApplyToDrawableHitObjects(IEnumerable drawables) { foreach (var drawable in drawables) { - drawable.ApplyCustomUpdateState += ApplyBounceState; + drawable.ApplyCustomUpdateState += ApplyZoomState; } } - protected void ApplyBounceState(DrawableHitObject drawable, ArmedState state) + protected void ApplyZoomState(DrawableHitObject drawable, ArmedState state) { if (!(drawable is DrawableOsuHitObject)) return; if (state != ArmedState.Idle) return; @@ -41,15 +49,21 @@ namespace osu.Game.Rulesets.Osu.Mods switch (drawable) { case DrawableHitCircle circle: - foreach (var t in circle.Transforms.Where(t => t.TargetMember == "Alpha")) - circle.RemoveTransform(t); + // Disable Fade + circle.Transforms + .Where(t => t.TargetMember == "Alpha") + .ForEach(t => circle.RemoveTransform(t)); + using (circle.BeginAbsoluteSequence(appearTime, true)) { var origScale = drawable.Scale; + var origRotate = circle.Rotation; circle - .ScaleTo(0) - .ScaleTo(origScale, moveDuration, Easing.OutSine) + .RotateTo(origRotate+rotate_offset) + .RotateTo(origRotate, moveDuration) + .ScaleTo(origScale * new Vector2(rotate_starting_width, 0)) + .ScaleTo(origScale, moveDuration) .FadeTo(1); } @@ -58,8 +72,10 @@ namespace osu.Game.Rulesets.Osu.Mods break; case DrawableSlider slider: - foreach (var t in slider.Transforms.Where(t => t.TargetMember == "Alpha")) - slider.RemoveTransform(t); + // Disable fade + slider.Transforms + .Where(t => t.TargetMember == "Alpha") + .ForEach(t => slider.RemoveTransform(t)); using (slider.BeginAbsoluteSequence(appearTime, true)) { @@ -67,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Mods slider .ScaleTo(0) - .ScaleTo(origScale, moveDuration, Easing.OutSine) + .ScaleTo(origScale, moveDuration) .FadeTo(1); } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 029ca7d43e..cebd9f1321 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Osu return new Mod[] { new OsuModTransform(), new OsuModWiggle(), - new OsuModZoomIn(), + new OsuModSpinIn(), }; default: return new Mod[] { }; From 58e989fb9d925b7b1d160f05be813ffbe90c27b1 Mon Sep 17 00:00:00 2001 From: Kyle Chang Date: Thu, 20 Sep 2018 21:08:31 -0400 Subject: [PATCH 0111/2854] Fix judgement backgrounds not appearing properly --- .../Objects/Drawables/DrawableOsuHitObject.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 10cd246172..5503a61b3b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -37,8 +37,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { double transformTime = HitObject.StartTime - HitObject.TimePreempt; - base.ApplyTransformsAt(transformTime, true); - base.ClearTransformsAfter(transformTime, true); + if (state == ArmedState.Idle) + { + base.ApplyTransformsAt(transformTime, true); + base.ClearTransformsAfter(transformTime, true); + } using (BeginAbsoluteSequence(transformTime, true)) { From ece4da0435e988c968bd4d917c7115fc39a2e032 Mon Sep 17 00:00:00 2001 From: Kyle Chang Date: Thu, 20 Sep 2018 23:49:15 -0400 Subject: [PATCH 0112/2854] Revert "Fix judgement backgrounds not appearing properly" This reverts commit 58e989f --- .../Objects/Drawables/DrawableOsuHitObject.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 5503a61b3b..10cd246172 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -37,11 +37,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { double transformTime = HitObject.StartTime - HitObject.TimePreempt; - if (state == ArmedState.Idle) - { - base.ApplyTransformsAt(transformTime, true); - base.ClearTransformsAfter(transformTime, true); - } + base.ApplyTransformsAt(transformTime, true); + base.ClearTransformsAfter(transformTime, true); using (BeginAbsoluteSequence(transformTime, true)) { From bfa430ad8ca04f683bc7ee7363a58660dd16d210 Mon Sep 17 00:00:00 2001 From: Kyle Chang Date: Thu, 20 Sep 2018 23:51:43 -0400 Subject: [PATCH 0113/2854] Fix judgement backgrounds without breaking other things --- osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs index 3bb7d5933c..e597567336 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string ShortenedName => "SI"; public override FontAwesome Icon => FontAwesome.fa_rotate_right; public override ModType Type => ModType.Fun; - public override string Description => "Circle spin in. No approach circles."; + public override string Description => "Circles spin in. No approach circles."; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden) }; @@ -33,6 +33,7 @@ namespace osu.Game.Rulesets.Osu.Mods { foreach (var drawable in drawables) { + // Need to add custom update in order to disable fade drawable.ApplyCustomUpdateState += ApplyZoomState; } } @@ -43,8 +44,9 @@ namespace osu.Game.Rulesets.Osu.Mods if (state != ArmedState.Idle) return; var h = (OsuHitObject)drawable.HitObject; - var appearTime = h.StartTime - h.TimePreempt; - var moveDuration = h.TimePreempt; + + var appearTime = h.StartTime - h.TimePreempt + 1; + var moveDuration = h.TimePreempt - 1; switch (drawable) { @@ -63,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Mods .RotateTo(origRotate+rotate_offset) .RotateTo(origRotate, moveDuration) .ScaleTo(origScale * new Vector2(rotate_starting_width, 0)) - .ScaleTo(origScale, moveDuration) + .ScaleTo(origScale, moveDuration, Easing.InQuad) .FadeTo(1); } From 3cb33e53cc7df760a7295a20c74dfddb863cceb3 Mon Sep 17 00:00:00 2001 From: Kyle Chang Date: Fri, 21 Sep 2018 00:07:09 -0400 Subject: [PATCH 0114/2854] Hid approach circles after rewinding replays --- osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs index e597567336..348093ec9b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs @@ -69,7 +69,10 @@ namespace osu.Game.Rulesets.Osu.Mods .FadeTo(1); } - circle.ApproachCircle.Hide(); + using (circle.ApproachCircle.BeginAbsoluteSequence(appearTime, true)) + { + circle.ApproachCircle.Hide(); + } break; From 51dcfeee9249ad68500fa97940e3d5edbb46aa71 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 25 Aug 2018 21:40:40 +0900 Subject: [PATCH 0115/2854] Move existing tournament resources to new project --- osu.Game.Tournament.Tests/.vscode/launch.json | 31 ++++++++++++ osu.Game.Tournament.Tests/.vscode/tasks.json | 47 +++++++++++++++++++ .../TestCaseDrawings.cs | 31 ++++++------ .../osu.Game.Tournament.Tests.csproj | 16 +++++++ .../Properties/AssemblyInfo.cs | 11 +++++ .../Components/DrawingsConfigManager.cs | 2 +- .../Drawings/Components}/DrawingsTeam.cs | 20 +++++--- .../Screens/Drawings/Components}/Group.cs | 13 +++-- .../Drawings/Components}/GroupContainer.cs | 5 +- .../Screens/Drawings/Components}/ITeamList.cs | 4 +- .../Components}/ScrollingTeamContainer.cs | 23 ++++----- .../Components}/StorageBackedTeamList.cs | 20 +++----- .../Components/VisualiserContainer.cs | 6 +-- .../Screens/Drawings/DrawingsScreen.cs | 20 ++++---- .../osu.Game.Tournament.csproj | 13 +++++ osu.Game/Screens/Menu/MainMenu.cs | 15 ------ osu.Game/Tests/Visual/OsuTestCase.cs | 2 +- osu.sln | 12 +++++ 18 files changed, 202 insertions(+), 89 deletions(-) create mode 100644 osu.Game.Tournament.Tests/.vscode/launch.json create mode 100644 osu.Game.Tournament.Tests/.vscode/tasks.json rename {osu.Game.Tests/Visual => osu.Game.Tournament.Tests}/TestCaseDrawings.cs (76%) create mode 100644 osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj create mode 100644 osu.Game.Tournament/Properties/AssemblyInfo.cs rename {osu.Game/Screens/Tournament => osu.Game.Tournament/Screens/Drawings}/Components/DrawingsConfigManager.cs (92%) rename {osu.Game/Screens/Tournament/Teams => osu.Game.Tournament/Screens/Drawings/Components}/DrawingsTeam.cs (65%) rename {osu.Game/Screens/Tournament => osu.Game.Tournament/Screens/Drawings/Components}/Group.cs (95%) rename {osu.Game/Screens/Tournament => osu.Game.Tournament/Screens/Drawings/Components}/GroupContainer.cs (95%) rename {osu.Game/Screens/Tournament/Teams => osu.Game.Tournament/Screens/Drawings/Components}/ITeamList.cs (68%) rename {osu.Game/Screens/Tournament => osu.Game.Tournament/Screens/Drawings/Components}/ScrollingTeamContainer.cs (94%) rename {osu.Game/Screens/Tournament/Teams => osu.Game.Tournament/Screens/Drawings/Components}/StorageBackedTeamList.cs (73%) rename {osu.Game/Screens/Tournament => osu.Game.Tournament/Screens/Drawings}/Components/VisualiserContainer.cs (98%) rename osu.Game/Screens/Tournament/Drawings.cs => osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs (96%) create mode 100644 osu.Game.Tournament/osu.Game.Tournament.csproj diff --git a/osu.Game.Tournament.Tests/.vscode/launch.json b/osu.Game.Tournament.Tests/.vscode/launch.json new file mode 100644 index 0000000000..0204158347 --- /dev/null +++ b/osu.Game.Tournament.Tests/.vscode/launch.json @@ -0,0 +1,31 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "VisualTests (Debug)", + "type": "coreclr", + "request": "launch", + "program": "dotnet", + "args": [ + "${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Tournament.Tests.dll" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Debug)", + "env": {}, + "console": "internalConsole" + }, + { + "name": "VisualTests (Release)", + "type": "coreclr", + "request": "launch", + "program": "dotnet", + "args": [ + "${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Tournament.Tests.dll" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Release)", + "env": {}, + "console": "internalConsole" + } + ] +} \ No newline at end of file diff --git a/osu.Game.Tournament.Tests/.vscode/tasks.json b/osu.Game.Tournament.Tests/.vscode/tasks.json new file mode 100644 index 0000000000..37f2f32874 --- /dev/null +++ b/osu.Game.Tournament.Tests/.vscode/tasks.json @@ -0,0 +1,47 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Build (Debug)", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "--no-restore", + "osu.Game.Tournament.Tests.csproj", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Build (Release)", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "--no-restore", + "osu.Game.Tournament.Tests.csproj", + "/p:Configuration=Release", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Restore", + "type": "shell", + "command": "dotnet", + "args": [ + "restore" + ], + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Tests/Visual/TestCaseDrawings.cs b/osu.Game.Tournament.Tests/TestCaseDrawings.cs similarity index 76% rename from osu.Game.Tests/Visual/TestCaseDrawings.cs rename to osu.Game.Tournament.Tests/TestCaseDrawings.cs index a6a3ef6747..029668df73 100644 --- a/osu.Game.Tests/Visual/TestCaseDrawings.cs +++ b/osu.Game.Tournament.Tests/TestCaseDrawings.cs @@ -2,18 +2,17 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; -using System.ComponentModel; -using osu.Game.Screens.Tournament; -using osu.Game.Screens.Tournament.Teams; +using osu.Game.Tests.Visual; +using osu.Game.Tournament.Screens.Drawings; +using osu.Game.Tournament.Screens.Drawings.Components; -namespace osu.Game.Tests.Visual +namespace osu.Game.Tournament.Tests { - [Description("for tournament use")] public class TestCaseDrawings : OsuTestCase { public TestCaseDrawings() { - Add(new Drawings + Add(new DrawingsScreen { TeamList = new TestTeamList(), }); @@ -21,57 +20,57 @@ namespace osu.Game.Tests.Visual private class TestTeamList : ITeamList { - public IEnumerable Teams { get; } = new[] + public IEnumerable Teams { get; } = new[] { - new DrawingsTeam + new TournamentTeam { FlagName = "GB", FullName = "United Kingdom", Acronym = "UK" }, - new DrawingsTeam + new TournamentTeam { FlagName = "FR", FullName = "France", Acronym = "FRA" }, - new DrawingsTeam + new TournamentTeam { FlagName = "CN", FullName = "China", Acronym = "CHN" }, - new DrawingsTeam + new TournamentTeam { FlagName = "AU", FullName = "Australia", Acronym = "AUS" }, - new DrawingsTeam + new TournamentTeam { FlagName = "JP", FullName = "Japan", Acronym = "JPN" }, - new DrawingsTeam + new TournamentTeam { FlagName = "RO", FullName = "Romania", Acronym = "ROM" }, - new DrawingsTeam + new TournamentTeam { FlagName = "IT", FullName = "Italy", Acronym = "PIZZA" }, - new DrawingsTeam + new TournamentTeam { FlagName = "VE", FullName = "Venezuela", Acronym = "VNZ" }, - new DrawingsTeam + new TournamentTeam { FlagName = "US", FullName = "United States of America", diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj new file mode 100644 index 0000000000..2b67016f7a --- /dev/null +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -0,0 +1,16 @@ + + + + + + + + + + WinExe + netcoreapp2.1 + + + + + \ No newline at end of file diff --git a/osu.Game.Tournament/Properties/AssemblyInfo.cs b/osu.Game.Tournament/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..4955391097 --- /dev/null +++ b/osu.Game.Tournament/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Runtime.CompilerServices; + +// We publish our internal attributes to other sub-projects of the framework. +// Note, that we omit visual tests as they are meant to test the framework +// behavior "in the wild". + +[assembly: InternalsVisibleTo("osu.Game.Tournament.Tests")] +[assembly: InternalsVisibleTo("osu.Game.Tournament.Tests.Dynamic")] diff --git a/osu.Game/Screens/Tournament/Components/DrawingsConfigManager.cs b/osu.Game.Tournament/Screens/Drawings/Components/DrawingsConfigManager.cs similarity index 92% rename from osu.Game/Screens/Tournament/Components/DrawingsConfigManager.cs rename to osu.Game.Tournament/Screens/Drawings/Components/DrawingsConfigManager.cs index 49fb39c6da..ac3dbf8a11 100644 --- a/osu.Game/Screens/Tournament/Components/DrawingsConfigManager.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/DrawingsConfigManager.cs @@ -4,7 +4,7 @@ using osu.Framework.Configuration; using osu.Framework.Platform; -namespace osu.Game.Screens.Tournament.Components +namespace osu.Game.Tournament.Screens.Drawings.Components { public class DrawingsConfigManager : IniConfigManager { diff --git a/osu.Game/Screens/Tournament/Teams/DrawingsTeam.cs b/osu.Game.Tournament/Screens/Drawings/Components/DrawingsTeam.cs similarity index 65% rename from osu.Game/Screens/Tournament/Teams/DrawingsTeam.cs rename to osu.Game.Tournament/Screens/Drawings/Components/DrawingsTeam.cs index 0926ed2748..7e182d1b62 100644 --- a/osu.Game/Screens/Tournament/Teams/DrawingsTeam.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/DrawingsTeam.cs @@ -1,23 +1,29 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -namespace osu.Game.Screens.Tournament.Teams +namespace osu.Game.Tournament.Screens.Drawings.Components { - public class DrawingsTeam + public class TournamentTeam { /// /// The name of this team. /// public string FullName; - /// - /// Short acronym which appears in the group boxes post-selection. - /// - public string Acronym; - /// /// Name of the file containing the flag. /// public string FlagName; + + private string acronym; + + /// + /// Short acronym which appears in the group boxes post-selection. + /// + public string Acronym + { + get { return acronym ?? FullName.Substring(0, 3); } + set { acronym = value; } + } } } diff --git a/osu.Game/Screens/Tournament/Group.cs b/osu.Game.Tournament/Screens/Drawings/Components/Group.cs similarity index 95% rename from osu.Game/Screens/Tournament/Group.cs rename to osu.Game.Tournament/Screens/Drawings/Components/Group.cs index 6845d8fc48..01f13857fc 100644 --- a/osu.Game/Screens/Tournament/Group.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/Group.cs @@ -7,15 +7,14 @@ using System.Text; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Graphics.Sprites; using OpenTK; using OpenTK.Graphics; -using osu.Game.Screens.Tournament.Teams; -using osu.Framework.Graphics.Shapes; -namespace osu.Game.Screens.Tournament +namespace osu.Game.Tournament.Screens.Drawings.Components { public class Group : Container { @@ -73,7 +72,7 @@ namespace osu.Game.Screens.Tournament }; } - public void AddTeam(DrawingsTeam team) + public void AddTeam(TournamentTeam team) { GroupTeam gt = new GroupTeam(team); @@ -91,7 +90,7 @@ namespace osu.Game.Screens.Tournament return allTeams.Any(t => t.Team.FullName == fullName); } - public bool RemoveTeam(DrawingsTeam team) + public bool RemoveTeam(TournamentTeam team) { allTeams.RemoveAll(gt => gt.Team == team); @@ -122,12 +121,12 @@ namespace osu.Game.Screens.Tournament private class GroupTeam : Container { - public readonly DrawingsTeam Team; + public readonly TournamentTeam Team; private readonly FillFlowContainer innerContainer; private readonly Sprite flagSprite; - public GroupTeam(DrawingsTeam team) + public GroupTeam(TournamentTeam team) { Team = team; diff --git a/osu.Game/Screens/Tournament/GroupContainer.cs b/osu.Game.Tournament/Screens/Drawings/Components/GroupContainer.cs similarity index 95% rename from osu.Game/Screens/Tournament/GroupContainer.cs rename to osu.Game.Tournament/Screens/Drawings/Components/GroupContainer.cs index c717a7401f..2914d28a00 100644 --- a/osu.Game/Screens/Tournament/GroupContainer.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/GroupContainer.cs @@ -8,9 +8,8 @@ using System.Text; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using OpenTK; -using osu.Game.Screens.Tournament.Teams; -namespace osu.Game.Screens.Tournament +namespace osu.Game.Tournament.Screens.Drawings.Components { public class GroupContainer : Container { @@ -64,7 +63,7 @@ namespace osu.Game.Screens.Tournament } } - public void AddTeam(DrawingsTeam team) + public void AddTeam(TournamentTeam team) { if (groups[currentGroup].TeamsCount == maxTeams) return; diff --git a/osu.Game/Screens/Tournament/Teams/ITeamList.cs b/osu.Game.Tournament/Screens/Drawings/Components/ITeamList.cs similarity index 68% rename from osu.Game/Screens/Tournament/Teams/ITeamList.cs rename to osu.Game.Tournament/Screens/Drawings/Components/ITeamList.cs index 728c702173..37b321c85b 100644 --- a/osu.Game/Screens/Tournament/Teams/ITeamList.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/ITeamList.cs @@ -3,10 +3,10 @@ using System.Collections.Generic; -namespace osu.Game.Screens.Tournament.Teams +namespace osu.Game.Tournament.Screens.Drawings.Components { public interface ITeamList { - IEnumerable Teams { get; } + IEnumerable Teams { get; } } } diff --git a/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs b/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs similarity index 94% rename from osu.Game/Screens/Tournament/ScrollingTeamContainer.cs rename to osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs index d1c7e0fced..ec2e8afcb6 100644 --- a/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs @@ -13,18 +13,17 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Threading; -using osu.Game.Screens.Tournament.Teams; using OpenTK; using OpenTK.Graphics; -namespace osu.Game.Screens.Tournament +namespace osu.Game.Tournament.Screens.Drawings.Components { public class ScrollingTeamContainer : Container { public event Action OnScrollStarted; - public event Action OnSelected; + public event Action OnSelected; - private readonly List availableTeams = new List(); + private readonly List availableTeams = new List(); private readonly Container tracker; @@ -84,6 +83,7 @@ namespace osu.Game.Screens.Tournament } private ScrollState _scrollState; + private ScrollState scrollState { get { return _scrollState; } @@ -166,7 +166,7 @@ namespace osu.Game.Screens.Tournament } } - public void AddTeam(DrawingsTeam team) + public void AddTeam(TournamentTeam team) { if (availableTeams.Contains(team)) return; @@ -177,12 +177,12 @@ namespace osu.Game.Screens.Tournament scrollState = ScrollState.Idle; } - public void AddTeams(IEnumerable teams) + public void AddTeams(IEnumerable teams) { if (teams == null) return; - foreach (DrawingsTeam t in teams) + foreach (TournamentTeam t in teams) AddTeam(t); } @@ -193,7 +193,7 @@ namespace osu.Game.Screens.Tournament scrollState = ScrollState.Idle; } - public void RemoveTeam(DrawingsTeam team) + public void RemoveTeam(TournamentTeam team) { availableTeams.Remove(team); @@ -278,7 +278,7 @@ namespace osu.Game.Screens.Tournament private void addFlags() { - foreach (DrawingsTeam t in availableTeams) + foreach (TournamentTeam t in availableTeams) { Add(new ScrollingTeam(t) { @@ -320,12 +320,13 @@ namespace osu.Game.Screens.Tournament public const float WIDTH = 58; public const float HEIGHT = 41; - public DrawingsTeam Team; + public TournamentTeam Team; private readonly Sprite flagSprite; private readonly Box outline; private bool selected; + public bool Selected { get { return selected; } @@ -341,7 +342,7 @@ namespace osu.Game.Screens.Tournament } } - public ScrollingTeam(DrawingsTeam team) + public ScrollingTeam(TournamentTeam team) { Team = team; diff --git a/osu.Game/Screens/Tournament/Teams/StorageBackedTeamList.cs b/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs similarity index 73% rename from osu.Game/Screens/Tournament/Teams/StorageBackedTeamList.cs rename to osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs index bb112c7e2d..296a23339e 100644 --- a/osu.Game/Screens/Tournament/Teams/StorageBackedTeamList.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs @@ -7,7 +7,7 @@ using System.IO; using osu.Framework.Logging; using osu.Framework.Platform; -namespace osu.Game.Screens.Tournament.Teams +namespace osu.Game.Tournament.Screens.Drawings.Components { public class StorageBackedTeamList : ITeamList { @@ -20,11 +20,11 @@ namespace osu.Game.Screens.Tournament.Teams this.storage = storage; } - public IEnumerable Teams + public IEnumerable Teams { get { - var teams = new List(); + var teams = new List(); try { @@ -47,17 +47,11 @@ namespace osu.Game.Screens.Tournament.Teams continue; } - string flagName = split[0].Trim(); - string teamName = split[1].Trim(); - - string acronym = split.Length >= 3 ? split[2].Trim() : teamName; - acronym = acronym.Substring(0, Math.Min(3, acronym.Length)); - - teams.Add(new DrawingsTeam + teams.Add(new TournamentTeam { - FlagName = flagName, - FullName = teamName, - Acronym = acronym + FlagName = split[0].Trim(), + FullName = split[1].Trim(), + Acronym = split.Length >= 3 ? split[2].Trim() : null }); } } diff --git a/osu.Game/Screens/Tournament/Components/VisualiserContainer.cs b/osu.Game.Tournament/Screens/Drawings/Components/VisualiserContainer.cs similarity index 98% rename from osu.Game/Screens/Tournament/Components/VisualiserContainer.cs rename to osu.Game.Tournament/Screens/Drawings/Components/VisualiserContainer.cs index 1453d4e78f..6581dbd328 100644 --- a/osu.Game/Screens/Tournament/Components/VisualiserContainer.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/VisualiserContainer.cs @@ -1,16 +1,16 @@ // Copyright (c) 2007-2018 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.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.MathUtils; -using System.Collections.Generic; -using System.Linq; -namespace osu.Game.Screens.Tournament.Components +namespace osu.Game.Tournament.Screens.Drawings.Components { public class VisualiserContainer : Container { diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs similarity index 96% rename from osu.Game/Screens/Tournament/Drawings.cs rename to osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index 4e2109afd0..e3853dca6f 100644 --- a/osu.Game/Screens/Tournament/Drawings.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -9,23 +9,23 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Screens; using osu.Game.Screens.Backgrounds; -using osu.Game.Screens.Tournament.Components; -using osu.Game.Screens.Tournament.Teams; +using osu.Game.Tournament.Screens.Drawings.Components; using OpenTK; using OpenTK.Graphics; -using osu.Framework.IO.Stores; -using osu.Framework.Graphics.Shapes; -namespace osu.Game.Screens.Tournament +namespace osu.Game.Tournament.Screens.Drawings { - public class Drawings : OsuScreen + public class DrawingsScreen : OsuScreen { private const string results_filename = "drawings_results.txt"; @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Tournament private GroupContainer groupsContainer; private OsuSpriteText fullTeamNameText; - private readonly List allTeams = new List(); + private readonly List allTeams = new List(); private DrawingsConfigManager drawingsConfig; @@ -253,7 +253,7 @@ namespace osu.Game.Screens.Tournament reset(true); } - private void onTeamSelected(DrawingsTeam team) + private void onTeamSelected(TournamentTeam team) { groupsContainer.AddTeam(team); @@ -290,7 +290,7 @@ namespace osu.Game.Screens.Tournament teamsContainer.ClearTeams(); allTeams.Clear(); - foreach (DrawingsTeam t in TeamList.Teams) + foreach (TournamentTeam t in TeamList.Teams) { if (groupsContainer.ContainsTeam(t.FullName)) continue; @@ -327,7 +327,7 @@ namespace osu.Game.Screens.Tournament continue; // ReSharper disable once AccessToModifiedClosure - DrawingsTeam teamToAdd = allTeams.FirstOrDefault(t => t.FullName == line); + TournamentTeam teamToAdd = allTeams.FirstOrDefault(t => t.FullName == line); if (teamToAdd == null) continue; diff --git a/osu.Game.Tournament/osu.Game.Tournament.csproj b/osu.Game.Tournament/osu.Game.Tournament.csproj new file mode 100644 index 0000000000..8adff80820 --- /dev/null +++ b/osu.Game.Tournament/osu.Game.Tournament.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + Library + AnyCPU + true + tools for tournaments. + + + + + \ No newline at end of file diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 2dd6e1d7e1..042fa4a07b 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -3,11 +3,8 @@ using OpenTK; using OpenTK.Graphics; -using OpenTK.Input; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -18,7 +15,6 @@ using osu.Game.Screens.Direct; using osu.Game.Screens.Edit; using osu.Game.Screens.Multi; using osu.Game.Screens.Select; -using osu.Game.Screens.Tournament; namespace osu.Game.Screens.Menu { @@ -199,16 +195,5 @@ namespace osu.Game.Screens.Menu Content.FadeOut(3000); return base.OnExiting(next); } - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - if (!args.Repeat && state.Keyboard.ControlPressed && state.Keyboard.ShiftPressed && args.Key == Key.D) - { - Push(new Drawings()); - return true; - } - - return base.OnKeyDown(state, args); - } } } diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index 67a13bd850..dccf852000 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual { beatmap.SetAudioManager(audioManager); - Ruleset.Value = rulesets.AvailableRulesets.First(); + Ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(); } protected override void Dispose(bool isDisposing) diff --git a/osu.sln b/osu.sln index bf1b6d60e1..f6ed7a5c42 100644 --- a/osu.sln +++ b/osu.sln @@ -27,6 +27,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Taiko.Tes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Osu.Tests", "osu.Game.Rulesets.Osu.Tests\osu.Game.Rulesets.Osu.Tests.csproj", "{6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tournament", "osu.Game.Tournament\osu.Game.Tournament.csproj", "{5672CA4D-1B37-425B-A118-A8DA26E78938}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tournament.Tests", "osu.Game.Tournament.Tests\osu.Game.Tournament.Tests.csproj", "{5789E78D-38F9-4072-AB7B-978F34B2C17F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -81,6 +85,14 @@ Global {6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}.Release|Any CPU.Build.0 = Release|Any CPU + {5672CA4D-1B37-425B-A118-A8DA26E78938}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5672CA4D-1B37-425B-A118-A8DA26E78938}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5672CA4D-1B37-425B-A118-A8DA26E78938}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5672CA4D-1B37-425B-A118-A8DA26E78938}.Release|Any CPU.Build.0 = Release|Any CPU + {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 40b01ec35a4337bae9c9f662759ab69287179ff4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Aug 2018 01:14:18 +0900 Subject: [PATCH 0116/2854] Add basic match pairing component for ladder display --- .../TestCaseMatchPairings.cs | 61 +++++++++ .../Ladder/Components/DrawableMatchPairing.cs | 45 ++++++ .../Ladder/Components/DrawableMatchTeam.cs | 129 ++++++++++++++++++ .../Components/DrawableTournamentTeam.cs | 45 ++++++ .../Screens/Ladder/Components/MatchPairing.cs | 31 +++++ 5 files changed, 311 insertions(+) create mode 100644 osu.Game.Tournament.Tests/TestCaseMatchPairings.cs create mode 100644 osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs create mode 100644 osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs create mode 100644 osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentTeam.cs create mode 100644 osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs diff --git a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs new file mode 100644 index 0000000000..77cf3c97f1 --- /dev/null +++ b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs @@ -0,0 +1,61 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Tests.Visual; +using osu.Game.Tournament.Screens.Drawings.Components; +using osu.Game.Tournament.Screens.Ladder.Components; + +namespace osu.Game.Tournament.Tests +{ + public class TestCaseMatchPairings : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(MatchPairing), + typeof(DrawableMatchPairing), + typeof(DrawableMatchTeam), + typeof(DrawableTournamentTeam), + }; + + public TestCaseMatchPairings() + { + var pairing1 = new MatchPairing( + new TournamentTeam { FlagName = "AU", FullName = "Australia", }, + new TournamentTeam { FlagName = "JP", FullName = "Japan", Acronym = "JPN" }) + { + Team1Score = { Value = 8 }, + Team2Score = { Value = 6 }, + }; + + var pairing2 = new MatchPairing( + new TournamentTeam + { + FlagName = "RO", + FullName = "Romania", + } + ); + + Child = new FillFlowContainer + { + Children = new Drawable[] + { + new DrawableMatchPairing(pairing1), + new DrawableMatchPairing(pairing2), + new DrawableMatchPairing(new MatchPairing()) + } + }; + + AddStep("mark complete", () => pairing1.Completed.Value = true); + AddRepeatStep("change scores", () => pairing1.Team2Score.Value++, 5); + AddStep("mark complete", () => pairing1.Completed.Value = true); + + AddStep("add new team", () => pairing2.Team2.Value = + new TournamentTeam { FlagName = "PT", FullName = "Portugal" } + ); + } + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs new file mode 100644 index 0000000000..281208cdcf --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -0,0 +1,45 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + public class DrawableMatchPairing : CompositeDrawable + { + private readonly MatchPairing pairing; + private readonly FillFlowContainer flow; + + public DrawableMatchPairing(MatchPairing pairing) + { + this.pairing = pairing; + + AutoSizeAxes = Axes.Both; + + Margin = new MarginPadding(5); + + InternalChild = flow = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + }; + + pairing.Team1.BindValueChanged(_ => updateTeams()); + pairing.Team2.BindValueChanged(_ => updateTeams()); + + updateTeams(); + } + + private void updateTeams() + { + // todo: teams may need to be bindable for transitions at a later point. + + flow.Children = new[] + { + new DrawableMatchTeam(pairing.Team1, pairing), + new DrawableMatchTeam(pairing.Team2, pairing) + }; + } + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs new file mode 100644 index 0000000000..9d8ea00e31 --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -0,0 +1,129 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Tournament.Screens.Drawings.Components; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + public class DrawableMatchTeam : DrawableTournamentTeam + { + private OsuSpriteText scoreText; + private Box background; + + private readonly Bindable score = new Bindable(); + private readonly BindableBool completed = new BindableBool(); + + private Color4 colourWinner; + private Color4 colourNormal; + + private readonly Func isWinner; + + public DrawableMatchTeam(TournamentTeam team, MatchPairing pairing) + : base(team) + { + Size = new Vector2(150, 40); + + Masking = true; + CornerRadius = 5; + + Flag.Scale = new Vector2(0.9f); + Flag.Anchor = Flag.Origin = Anchor.CentreLeft; + + AcronymText.Anchor = AcronymText.Origin = Anchor.CentreLeft; + AcronymText.Padding = new MarginPadding { Left = 50 }; + AcronymText.TextSize = 24; + + if (pairing != null) + { + completed.BindTo(pairing.Completed); + + if (team == pairing.Team1.Value) + { + score.BindTo(pairing.Team1Score); + isWinner = () => pairing.Team1Score.Value > pairing.Team2Score.Value; + } + else + { + score.BindTo(pairing.Team2Score); + isWinner = () => pairing.Team2Score.Value > pairing.Team1Score.Value; + } + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + colourWinner = colours.BlueDarker; + colourNormal = OsuColour.Gray(0.2f); + + InternalChildren = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new Container + { + Padding = new MarginPadding(5), + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + AcronymText, + Flag, + new Container + { + Masking = true, + CornerRadius = 5, + Width = 0.3f, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + Colour = OsuColour.Gray(0.1f), + Alpha = 0.8f, + RelativeSizeAxes = Axes.Both, + }, + scoreText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + TextSize = 20, + } + } + } + } + } + }; + + completed.BindValueChanged(_ => updateWinStyle()); + + score.BindValueChanged(val => + { + scoreText.Text = val?.ToString() ?? string.Empty; + updateWinStyle(); + }, true); + } + + private void updateWinStyle() + { + bool winner = completed && isWinner?.Invoke() == true; + + background.FadeColour(winner ? colourWinner : colourNormal, winner ? 500 : 0, Easing.OutQuint); + + scoreText.Font = AcronymText.Font = winner ? "Exo2.0-Bold" : "Exo2.0-Regular"; + } + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentTeam.cs new file mode 100644 index 0000000000..dc5e2c9007 --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentTeam.cs @@ -0,0 +1,45 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Graphics.Sprites; +using osu.Game.Tournament.Screens.Drawings.Components; + +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + public abstract class DrawableTournamentTeam : CompositeDrawable + { + public readonly TournamentTeam Team; + + protected readonly Sprite Flag; + protected readonly OsuSpriteText AcronymText; + + protected DrawableTournamentTeam(TournamentTeam team) + { + Team = team; + + Flag = new Sprite + { + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit + }; + + AcronymText = new OsuSpriteText + { + Text = team?.Acronym.ToUpperInvariant() ?? string.Empty, + Font = @"Exo2.0-Regular" + }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + if (Team != null) + Flag.Texture = textures.Get($@"Flags/{Team.FlagName}"); + } + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs new file mode 100644 index 0000000000..b8d8b77d74 --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Game.Tournament.Screens.Drawings.Components; + +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + /// + /// A collection of two teams competing in a head-to-head match. + /// + public class MatchPairing + { + public Bindable Team1 = new Bindable(); + public Bindable Team1Score = new Bindable(); + + public Bindable Team2 = new Bindable(); + public Bindable Team2Score = new Bindable(); + + public Bindable Completed = new Bindable(); + + public MatchPairing(TournamentTeam team1 = null, TournamentTeam team2 = null) + { + Team1.Value = team1; + Team2.Value = team2; + + Team1Score.ValueChanged += _ => Completed.Value = false; + Team2Score.ValueChanged += _ => Completed.Value = false; + } + } +} From 3d3a7f714dc338b2d5ccf0a2eb55b563d5bb8fed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Aug 2018 01:24:19 +0900 Subject: [PATCH 0117/2854] Move and use shared components --- osu.Game.Tournament.Tests/TestCaseDrawings.cs | 1 + .../TestCaseMatchPairings.cs | 2 +- .../Components/DrawableTournamentTeam.cs | 3 +- .../TournamentTeam.cs} | 2 +- .../Screens/Drawings/Components/Group.cs | 49 ++++++------------- .../Drawings/Components/GroupContainer.cs | 1 + .../Screens/Drawings/Components/ITeamList.cs | 1 + .../Components/ScrollingTeamContainer.cs | 32 ++++-------- .../Components/StorageBackedTeamList.cs | 1 + .../Screens/Drawings/DrawingsScreen.cs | 1 + .../Ladder/Components/DrawableMatchTeam.cs | 2 +- .../Screens/Ladder/Components/MatchPairing.cs | 2 +- 12 files changed, 35 insertions(+), 62 deletions(-) rename osu.Game.Tournament/{Screens/Ladder => }/Components/DrawableTournamentTeam.cs (91%) rename osu.Game.Tournament/{Screens/Drawings/Components/DrawingsTeam.cs => Components/TournamentTeam.cs} (92%) diff --git a/osu.Game.Tournament.Tests/TestCaseDrawings.cs b/osu.Game.Tournament.Tests/TestCaseDrawings.cs index 029668df73..511724e51a 100644 --- a/osu.Game.Tournament.Tests/TestCaseDrawings.cs +++ b/osu.Game.Tournament.Tests/TestCaseDrawings.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using osu.Game.Tests.Visual; +using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Drawings; using osu.Game.Tournament.Screens.Drawings.Components; diff --git a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs index 77cf3c97f1..d5835896a4 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Tests.Visual; -using osu.Game.Tournament.Screens.Drawings.Components; +using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament.Tests diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentTeam.cs b/osu.Game.Tournament/Components/DrawableTournamentTeam.cs similarity index 91% rename from osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentTeam.cs rename to osu.Game.Tournament/Components/DrawableTournamentTeam.cs index dc5e2c9007..ec60d24c2c 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentTeam.cs +++ b/osu.Game.Tournament/Components/DrawableTournamentTeam.cs @@ -7,9 +7,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Graphics.Sprites; -using osu.Game.Tournament.Screens.Drawings.Components; -namespace osu.Game.Tournament.Screens.Ladder.Components +namespace osu.Game.Tournament.Components { public abstract class DrawableTournamentTeam : CompositeDrawable { diff --git a/osu.Game.Tournament/Screens/Drawings/Components/DrawingsTeam.cs b/osu.Game.Tournament/Components/TournamentTeam.cs similarity index 92% rename from osu.Game.Tournament/Screens/Drawings/Components/DrawingsTeam.cs rename to osu.Game.Tournament/Components/TournamentTeam.cs index 7e182d1b62..03d787af76 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/DrawingsTeam.cs +++ b/osu.Game.Tournament/Components/TournamentTeam.cs @@ -1,7 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -namespace osu.Game.Tournament.Screens.Drawings.Components +namespace osu.Game.Tournament.Components { public class TournamentTeam { diff --git a/osu.Game.Tournament/Screens/Drawings/Components/Group.cs b/osu.Game.Tournament/Screens/Drawings/Components/Group.cs index 01f13857fc..1600a19be8 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/Group.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/Group.cs @@ -4,13 +4,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using osu.Game.Graphics.Sprites; +using osu.Game.Tournament.Components; using OpenTK; using OpenTK.Graphics; @@ -119,21 +117,26 @@ namespace osu.Game.Tournament.Screens.Drawings.Components return sb.ToString(); } - private class GroupTeam : Container + private class GroupTeam : DrawableTournamentTeam { - public readonly TournamentTeam Team; - private readonly FillFlowContainer innerContainer; - private readonly Sprite flagSprite; - public GroupTeam(TournamentTeam team) + public GroupTeam(TournamentTeam team) : base(team) { - Team = team; - Width = 36; AutoSizeAxes = Axes.Y; - Children = new Drawable[] + + Flag.Anchor = Anchor.TopCentre; + Flag.Origin = Anchor.TopCentre; + + AcronymText.Anchor = Anchor.TopCentre; + AcronymText.Origin = Anchor.TopCentre; + AcronymText.Text = team.Acronym.ToUpperInvariant(); + AcronymText.TextSize = 10f; + AcronymText.Font = @"Exo2.0-Bold"; + + InternalChildren = new Drawable[] { innerContainer = new FillFlowContainer { @@ -148,22 +151,8 @@ namespace osu.Game.Tournament.Screens.Drawings.Components Children = new Drawable[] { - flagSprite = new Sprite - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - FillMode = FillMode.Fit - }, - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - - Text = team.Acronym.ToUpperInvariant(), - TextSize = 10f, - Font = @"Exo2.0-Bold" - } + Flag, + AcronymText } } }; @@ -175,12 +164,6 @@ namespace osu.Game.Tournament.Screens.Drawings.Components innerContainer.ScaleTo(1.5f); innerContainer.ScaleTo(1f, 200); } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - flagSprite.Texture = textures.Get($@"Flags/{Team.FlagName}"); - } } } } diff --git a/osu.Game.Tournament/Screens/Drawings/Components/GroupContainer.cs b/osu.Game.Tournament/Screens/Drawings/Components/GroupContainer.cs index 2914d28a00..236d19a3ad 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/GroupContainer.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/GroupContainer.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Text; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Tournament.Components; using OpenTK; namespace osu.Game.Tournament.Screens.Drawings.Components diff --git a/osu.Game.Tournament/Screens/Drawings/Components/ITeamList.cs b/osu.Game.Tournament/Screens/Drawings/Components/ITeamList.cs index 37b321c85b..614507dc80 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/ITeamList.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/ITeamList.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Game.Tournament.Components; namespace osu.Game.Tournament.Screens.Drawings.Components { diff --git a/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs b/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs index ec2e8afcb6..816a3ef958 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs @@ -5,14 +5,12 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -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.Framework.Graphics.Textures; using osu.Framework.Threading; +using osu.Game.Tournament.Components; using OpenTK; using OpenTK.Graphics; @@ -315,14 +313,11 @@ namespace osu.Game.Tournament.Screens.Drawings.Components Scrolling } - public class ScrollingTeam : Container + public class ScrollingTeam : DrawableTournamentTeam { public const float WIDTH = 58; public const float HEIGHT = 41; - public TournamentTeam Team; - - private readonly Sprite flagSprite; private readonly Box outline; private bool selected; @@ -343,9 +338,8 @@ namespace osu.Game.Tournament.Screens.Drawings.Components } public ScrollingTeam(TournamentTeam team) + : base(team) { - Team = team; - Anchor = Anchor.CentreLeft; Origin = Anchor.CentreLeft; @@ -355,28 +349,20 @@ namespace osu.Game.Tournament.Screens.Drawings.Components Alpha = 0; - Children = new Drawable[] + Flag.Anchor = Anchor.Centre; + Flag.Origin = Anchor.Centre; + Flag.Scale = new Vector2(0.9f); + + InternalChildren = new Drawable[] { outline = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0 }, - flagSprite = new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - - Size = new Vector2(WIDTH, HEIGHT) - new Vector2(8) - } + Flag }; } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - flagSprite.Texture = textures.Get($@"Flags/{Team.FlagName}"); - } } } } diff --git a/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs b/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs index 296a23339e..44ee9d64ca 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using osu.Framework.Logging; using osu.Framework.Platform; +using osu.Game.Tournament.Components; namespace osu.Game.Tournament.Screens.Drawings.Components { diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index e3853dca6f..8e6738d8d6 100644 --- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -19,6 +19,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Screens; using osu.Game.Screens.Backgrounds; +using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Drawings.Components; using OpenTK; using OpenTK.Graphics; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index 9d8ea00e31..1a190b4cc7 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Tournament.Screens.Drawings.Components; +using osu.Game.Tournament.Components; using OpenTK; using OpenTK.Graphics; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index b8d8b77d74..26beb5c598 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Configuration; -using osu.Game.Tournament.Screens.Drawings.Components; +using osu.Game.Tournament.Components; namespace osu.Game.Tournament.Screens.Ladder.Components { From 9fbbede027f76880e4649631f189a5c08eb03d8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Aug 2018 01:30:33 +0900 Subject: [PATCH 0118/2854] Fix spacing --- .../Screens/Ladder/Components/DrawableMatchPairing.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 281208cdcf..dd98e92ca3 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using OpenTK; namespace osu.Game.Tournament.Screens.Ladder.Components { @@ -23,6 +24,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { AutoSizeAxes = Axes.Both, Direction = FillDirection.Vertical, + Spacing = new Vector2(2) }; pairing.Team1.BindValueChanged(_ => updateTeams()); From bfc5ccd6d0a39cd3c0ea4898b7c23fa9962fd24c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Aug 2018 18:37:46 +0900 Subject: [PATCH 0119/2854] Add winners and progressions --- .../TestCaseMatchPairings.cs | 47 ++++++- .../Ladder/Components/DrawableMatchPairing.cs | 117 ++++++++++++++++-- .../Screens/Ladder/Components/MatchPairing.cs | 8 ++ 3 files changed, 158 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs index d5835896a4..a1ab2c7a48 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs @@ -23,6 +23,9 @@ namespace osu.Game.Tournament.Tests public TestCaseMatchPairings() { + FillFlowContainer level1; + FillFlowContainer level2; + var pairing1 = new MatchPairing( new TournamentTeam { FlagName = "AU", FullName = "Australia", }, new TournamentTeam { FlagName = "JP", FullName = "Japan", Acronym = "JPN" }) @@ -41,21 +44,53 @@ namespace osu.Game.Tournament.Tests Child = new FillFlowContainer { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, Children = new Drawable[] { - new DrawableMatchPairing(pairing1), - new DrawableMatchPairing(pairing2), - new DrawableMatchPairing(new MatchPairing()) + level1 = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new[] + { + new DrawableMatchPairing(pairing1), + new DrawableMatchPairing(pairing2), + new DrawableMatchPairing(new MatchPairing()), + } + }, + level2 = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Margin = new MarginPadding(20), + Children = new[] + { + new DrawableMatchPairing(new MatchPairing()), + new DrawableMatchPairing(new MatchPairing()) + } + } } }; + level1.Children[0].Progression = level2.Children[0]; + level1.Children[1].Progression = level2.Children[0]; + AddStep("mark complete", () => pairing1.Completed.Value = true); AddRepeatStep("change scores", () => pairing1.Team2Score.Value++, 5); AddStep("mark complete", () => pairing1.Completed.Value = true); + AddStep("add new team", () => pairing2.Team2.Value = new TournamentTeam { FlagName = "PT", FullName = "Portugal" }); + AddStep("Add progression", () => level1.Children[2].Progression = level2.Children[1]); - AddStep("add new team", () => pairing2.Team2.Value = - new TournamentTeam { FlagName = "PT", FullName = "Portugal" } - ); + AddStep("start match", () => pairing2.ResetScores()); + + AddRepeatStep("change scores", () => pairing2.Team1Score.Value++, 5); + AddStep("mark complete", () => pairing2.Completed.Value = true); + + AddStep("start submatch", () => level2.Children[0].Pairing.ResetScores()); + + AddRepeatStep("change scores", () => level2.Children[0].Pairing.Team1Score.Value++, 5); + AddStep("mark complete", () => level2.Children[0].Pairing.Completed.Value = true); } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index dd98e92ca3..461199327e 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -1,47 +1,148 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Lines; +using osu.Framework.MathUtils; using OpenTK; namespace osu.Game.Tournament.Screens.Ladder.Components { public class DrawableMatchPairing : CompositeDrawable { - private readonly MatchPairing pairing; + public readonly MatchPairing Pairing; private readonly FillFlowContainer flow; + private DrawableMatchPairing progression; + + private readonly Path path; + + public DrawableMatchPairing Progression + { + get => progression; + set + { + if (progression == value) return; + progression = value; + + if (LoadState == LoadState.Loaded) + updateProgression(); + + path.FadeInFromZero(200); + } + } + + private Vector2 progressionStart; + private Vector2 progressionEnd; + + private const float line_width = 2; + + private void updateProgression() + { + if (progression == null) + { + path.Positions = new List(); + return; + } + + Vector2 getCenteredVector(Vector2 top, Vector2 bottom) => new Vector2(top.X, top.Y + (bottom.Y - top.Y) / 2); + + const float padding = 5; + + var start = getCenteredVector(ScreenSpaceDrawQuad.TopRight, ScreenSpaceDrawQuad.BottomRight); + var end = getCenteredVector(progression.ScreenSpaceDrawQuad.TopLeft, progression.ScreenSpaceDrawQuad.BottomLeft); + + bool progressionAbove = progression.ScreenSpaceDrawQuad.TopLeft.Y < ScreenSpaceDrawQuad.TopLeft.Y; + + if (!Precision.AlmostEquals(progressionStart, start) || !Precision.AlmostEquals(progressionEnd, end)) + { + progressionStart = start; + progressionEnd = end; + + path.Origin = progressionAbove ? Anchor.BottomLeft : Anchor.TopLeft; + path.Y = progressionAbove ? line_width : -line_width; + + Vector2 startPosition = path.ToLocalSpace(start) + new Vector2(padding, 0); + Vector2 endPosition = path.ToLocalSpace(end) + new Vector2(-padding, 0); + Vector2 intermediate1 = startPosition + new Vector2(padding, 0); + Vector2 intermediate2 = new Vector2(intermediate1.X, endPosition.Y); + + path.Positions = new List + { + startPosition, + intermediate1, + intermediate2, + endPosition + }; + } + + var destinationForWinner = progressionAbove ? progression.Pairing.Team2 : progression.Pairing.Team1; + + destinationForWinner.Value = Pairing.Winner; + } + public DrawableMatchPairing(MatchPairing pairing) { - this.pairing = pairing; + Pairing = pairing; AutoSizeAxes = Axes.Both; Margin = new MarginPadding(5); - InternalChild = flow = new FillFlowContainer + InternalChildren = new Drawable[] { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(2) + flow = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(2) + }, + path = new Path + { + Alpha = 0, + BypassAutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreRight, + PathWidth = line_width, + }, }; pairing.Team1.BindValueChanged(_ => updateTeams()); pairing.Team2.BindValueChanged(_ => updateTeams()); + pairing.Completed.BindValueChanged(_ => updateProgression()); + updateTeams(); } + protected override void LoadComplete() + { + base.LoadComplete(); + updateTeams(); + } + + protected override void UpdateAfterAutoSize() + { + // required because the lines rely on flow being completed by other elements. + base.UpdateAfterAutoSize(); + updateProgression(); + } + private void updateTeams() { + if (LoadState != LoadState.Loaded) + return; + // todo: teams may need to be bindable for transitions at a later point. flow.Children = new[] { - new DrawableMatchTeam(pairing.Team1, pairing), - new DrawableMatchTeam(pairing.Team2, pairing) + new DrawableMatchTeam(Pairing.Team1, Pairing), + new DrawableMatchTeam(Pairing.Team2, Pairing) }; + + updateProgression(); } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index 26beb5c598..b19b551a04 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -27,5 +27,13 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Team1Score.ValueChanged += _ => Completed.Value = false; Team2Score.ValueChanged += _ => Completed.Value = false; } + + public TournamentTeam Winner => !Completed.Value ? null : (Team1Score.Value > Team2Score.Value ? Team1.Value : Team2.Value); + + public void ResetScores() + { + Team1Score.Value = 0; + Team2Score.Value = 0; + } } } From 041d8263961e746fedf1058227aa1a22c52e348b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Aug 2018 18:41:21 +0900 Subject: [PATCH 0120/2854] Simplify winner lookup --- .../Screens/Ladder/Components/DrawableMatchTeam.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index 1a190b4cc7..1f2447ef06 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -45,18 +45,10 @@ namespace osu.Game.Tournament.Screens.Ladder.Components if (pairing != null) { - completed.BindTo(pairing.Completed); + isWinner = () => pairing.Winner == Team; - if (team == pairing.Team1.Value) - { - score.BindTo(pairing.Team1Score); - isWinner = () => pairing.Team1Score.Value > pairing.Team2Score.Value; - } - else - { - score.BindTo(pairing.Team2Score); - isWinner = () => pairing.Team2Score.Value > pairing.Team1Score.Value; - } + completed.BindTo(pairing.Completed); + score.BindTo(team == pairing.Team1.Value ? pairing.Team1Score : pairing.Team2Score); } } From e4ea802c7bf00a5265a821689a9859f9861bc1d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Aug 2018 19:11:46 +0900 Subject: [PATCH 0121/2854] Add user interaction and stricter change validation rules --- .../TestCaseMatchPairings.cs | 23 ++--- .../Ladder/Components/DrawableMatchPairing.cs | 86 ++++++++++++------- .../Ladder/Components/DrawableMatchTeam.cs | 32 ++++++- .../Screens/Ladder/Components/MatchPairing.cs | 20 ++++- .../Ladder/Components/TournamentConditions.cs | 13 +++ 5 files changed, 129 insertions(+), 45 deletions(-) create mode 100644 osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs diff --git a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs index a1ab2c7a48..553e48a822 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Tests.Visual; @@ -21,6 +23,9 @@ namespace osu.Game.Tournament.Tests typeof(DrawableTournamentTeam), }; + [Cached] + private Bindable conditions = new Bindable(new TournamentConditions { BestOf = 9 }); + public TestCaseMatchPairings() { FillFlowContainer level1; @@ -30,8 +35,8 @@ namespace osu.Game.Tournament.Tests new TournamentTeam { FlagName = "AU", FullName = "Australia", }, new TournamentTeam { FlagName = "JP", FullName = "Japan", Acronym = "JPN" }) { - Team1Score = { Value = 8 }, - Team2Score = { Value = 6 }, + Team1Score = { Value = 4 }, + Team2Score = { Value = 1 }, }; var pairing2 = new MatchPairing( @@ -76,21 +81,19 @@ namespace osu.Game.Tournament.Tests level1.Children[0].Progression = level2.Children[0]; level1.Children[1].Progression = level2.Children[0]; - AddStep("mark complete", () => pairing1.Completed.Value = true); - AddRepeatStep("change scores", () => pairing1.Team2Score.Value++, 5); - AddStep("mark complete", () => pairing1.Completed.Value = true); + AddRepeatStep("change scores", () => pairing1.Team2Score.Value++, 4); AddStep("add new team", () => pairing2.Team2.Value = new TournamentTeam { FlagName = "PT", FullName = "Portugal" }); AddStep("Add progression", () => level1.Children[2].Progression = level2.Children[1]); - AddStep("start match", () => pairing2.ResetScores()); + AddStep("start match", () => pairing2.StartMatch()); - AddRepeatStep("change scores", () => pairing2.Team1Score.Value++, 5); - AddStep("mark complete", () => pairing2.Completed.Value = true); + AddRepeatStep("change scores", () => pairing2.Team1Score.Value++, 10); - AddStep("start submatch", () => level2.Children[0].Pairing.ResetScores()); + AddStep("start submatch", () => level2.Children[0].Pairing.StartMatch()); AddRepeatStep("change scores", () => level2.Children[0].Pairing.Team1Score.Value++, 5); - AddStep("mark complete", () => level2.Children[0].Pairing.Completed.Value = true); + + AddRepeatStep("change scores", () => level2.Children[0].Pairing.Team2Score.Value++, 4); } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 461199327e..7be302be73 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -2,6 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Lines; @@ -14,9 +16,10 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { public readonly MatchPairing Pairing; private readonly FillFlowContainer flow; - private DrawableMatchPairing progression; + private readonly Bindable conditions = new Bindable(); + private readonly Path path; public DrawableMatchPairing Progression @@ -39,6 +42,51 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private const float line_width = 2; + public DrawableMatchPairing(MatchPairing pairing) + { + Pairing = pairing; + + AutoSizeAxes = Axes.Both; + + Margin = new MarginPadding(5); + + InternalChildren = new Drawable[] + { + flow = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(2) + }, + path = new Path + { + Alpha = 0, + BypassAutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreRight, + PathWidth = line_width, + }, + }; + + pairing.Team1.BindValueChanged(_ => updateTeams()); + pairing.Team2.BindValueChanged(_ => updateTeams()); + + pairing.Team1Score.BindValueChanged(_ => updateWinConditions()); + pairing.Team2Score.BindValueChanged(_ => updateWinConditions()); + + pairing.Completed.BindValueChanged(_ => updateProgression()); + + updateTeams(); + } + + [BackgroundDependencyLoader(true)] + private void load(Bindable conditions) + { + this.conditions.BindValueChanged(_ => updateWinConditions()); + + if (conditions != null) + this.conditions.BindTo(conditions); + } + private void updateProgression() { if (progression == null) @@ -83,37 +131,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components destinationForWinner.Value = Pairing.Winner; } - public DrawableMatchPairing(MatchPairing pairing) + private void updateWinConditions() { - Pairing = pairing; + if (conditions.Value == null) return; - AutoSizeAxes = Axes.Both; - - Margin = new MarginPadding(5); - - InternalChildren = new Drawable[] - { - flow = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(2) - }, - path = new Path - { - Alpha = 0, - BypassAutoSizeAxes = Axes.Both, - Anchor = Anchor.CentreRight, - PathWidth = line_width, - }, - }; - - pairing.Team1.BindValueChanged(_ => updateTeams()); - pairing.Team2.BindValueChanged(_ => updateTeams()); - - pairing.Completed.BindValueChanged(_ => updateProgression()); - - updateTeams(); + Pairing.Completed.Value = Pairing.Team1Score.Value + Pairing.Team2Score.Value >= conditions.Value.BestOf; } protected override void LoadComplete() @@ -136,6 +158,9 @@ namespace osu.Game.Tournament.Screens.Ladder.Components // todo: teams may need to be bindable for transitions at a later point. + if (Pairing.Team1.Value == null || Pairing.Team2.Value == null) + Pairing.CancelMatchStart(); + flow.Children = new[] { new DrawableMatchTeam(Pairing.Team1, Pairing), @@ -143,6 +168,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components }; updateProgression(); + updateWinConditions(); } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index 1f2447ef06..06b400976c 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -7,16 +7,20 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.EventArgs; +using osu.Framework.Input.States; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Tournament.Components; using OpenTK; using OpenTK.Graphics; +using OpenTK.Input; namespace osu.Game.Tournament.Screens.Ladder.Components { public class DrawableMatchTeam : DrawableTournamentTeam { + private readonly MatchPairing pairing; private OsuSpriteText scoreText; private Box background; @@ -31,6 +35,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public DrawableMatchTeam(TournamentTeam team, MatchPairing pairing) : base(team) { + this.pairing = pairing; Size = new Vector2(150, 40); Masking = true; @@ -48,7 +53,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components isWinner = () => pairing.Winner == Team; completed.BindTo(pairing.Completed); - score.BindTo(team == pairing.Team1.Value ? pairing.Team1Score : pairing.Team2Score); + if (team != null) + score.BindTo(team == pairing.Team1.Value ? pairing.Team1Score : pairing.Team2Score); } } @@ -109,6 +115,30 @@ namespace osu.Game.Tournament.Screens.Ladder.Components }, true); } + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + if (Team == null) return true; + + if (args.Button == MouseButton.Left) + { + if (score.Value == null) + { + pairing.StartMatch(); + } + else if (!pairing.Completed) + score.Value++; + } + else + { + if (score.Value > 0) + score.Value--; + else + pairing.CancelMatchStart(); + } + + return true; + } + private void updateWinStyle() { bool winner = completed && isWinner?.Invoke() == true; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index b19b551a04..b615330cc2 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -23,15 +23,27 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { Team1.Value = team1; Team2.Value = team2; - - Team1Score.ValueChanged += _ => Completed.Value = false; - Team2Score.ValueChanged += _ => Completed.Value = false; } public TournamentTeam Winner => !Completed.Value ? null : (Team1Score.Value > Team2Score.Value ? Team1.Value : Team2.Value); - public void ResetScores() + /// + /// Remove scores from the match, in case of a false click or false start. + /// + public void CancelMatchStart() { + Team1Score.Value = null; + Team2Score.Value = null; + } + + /// + /// Initialise this match with zeroed scores. Will be a noop if either team is not present. + /// + public void StartMatch() + { + if (Team1.Value == null || Team2.Value == null) + return; + Team1Score.Value = 0; Team2Score.Value = 0; } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs new file mode 100644 index 0000000000..fd0e8cea87 --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs @@ -0,0 +1,13 @@ +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + /// + /// Conditions governing a tournament. + /// + public class TournamentConditions + { + /// + /// How many matches before a winner is decided. + /// + public int BestOf; + } +} From 6e6b6b285ae8fce66c9da2154337f53a410515d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Aug 2018 17:08:00 +0900 Subject: [PATCH 0122/2854] Don't require every-frame update --- .../Screens/Ladder/Components/DrawableMatchPairing.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 7be302be73..8191df6680 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -144,13 +144,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components updateTeams(); } - protected override void UpdateAfterAutoSize() - { - // required because the lines rely on flow being completed by other elements. - base.UpdateAfterAutoSize(); - updateProgression(); - } - private void updateTeams() { if (LoadState != LoadState.Loaded) @@ -167,7 +160,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components new DrawableMatchTeam(Pairing.Team2, Pairing) }; - updateProgression(); + SchedulerAfterChildren.Add(() => Scheduler.Add(updateProgression)); updateWinConditions(); } } From e6baf418fba4bd216ef1801383066f66d2c4cf34 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Sep 2018 01:34:17 +0900 Subject: [PATCH 0123/2854] Add tournament tests rider configuration --- .../runConfigurations/TournamentTests.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .idea/.idea.osu/.idea/runConfigurations/TournamentTests.xml diff --git a/.idea/.idea.osu/.idea/runConfigurations/TournamentTests.xml b/.idea/.idea.osu/.idea/runConfigurations/TournamentTests.xml new file mode 100644 index 0000000000..5b425e8582 --- /dev/null +++ b/.idea/.idea.osu/.idea/runConfigurations/TournamentTests.xml @@ -0,0 +1,18 @@ + + + + \ No newline at end of file From 234b04dfc567629da9056ee6925882afd066126d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Sep 2018 04:51:38 +0900 Subject: [PATCH 0124/2854] Add basic ladder manager and state retention --- .../TestCaseLadderManager.cs | 34 +++++++++++++ .../TestCaseMatchPairings.cs | 13 ++--- osu.Game.Tournament.Tests/teams.json | 1 + .../Components/TournamentTeam.cs | 13 ++++- .../Components/StorageBackedTeamList.cs | 1 - .../Ladder/Components/DrawableMatchPairing.cs | 33 +++++++++++- .../Ladder/Components/DrawableMatchTeam.cs | 47 ++++++++++++++--- .../Screens/Ladder/Components/LadderInfo.cs | 9 ++++ .../Ladder/Components/LadderManager.cs | 51 +++++++++++++++++++ .../Screens/Ladder/Components/MatchPairing.cs | 21 ++++++-- 10 files changed, 199 insertions(+), 24 deletions(-) create mode 100644 osu.Game.Tournament.Tests/TestCaseLadderManager.cs create mode 100644 osu.Game.Tournament.Tests/teams.json create mode 100644 osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs create mode 100644 osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs new file mode 100644 index 0000000000..dc4633aa17 --- /dev/null +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.IO; +using Newtonsoft.Json; +using osu.Framework.Allocation; +using osu.Game.Tests.Visual; +using osu.Game.Tournament.Components; +using osu.Game.Tournament.Screens.Ladder.Components; + +namespace osu.Game.Tournament.Tests +{ + public class TestCaseLadderManager : OsuTestCase + { + [Cached] + private readonly LadderManager manager; + + public TestCaseLadderManager() + { + var teams = JsonConvert.DeserializeObject>(File.ReadAllText(@"teams.json")); + var ladder = JsonConvert.DeserializeObject(File.ReadAllText(@"bracket.json")) ?? new LadderInfo(); + + Child = manager = new LadderManager(ladder, teams); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + File.WriteAllText(@"bracket.json", JsonConvert.SerializeObject(manager.Info)); + } + } +} diff --git a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs index 553e48a822..bd6b085b0f 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs @@ -28,8 +28,8 @@ namespace osu.Game.Tournament.Tests public TestCaseMatchPairings() { - FillFlowContainer level1; - FillFlowContainer level2; + Container level1; + Container level2; var pairing1 = new MatchPairing( new TournamentTeam { FlagName = "AU", FullName = "Australia", }, @@ -47,16 +47,14 @@ namespace osu.Game.Tournament.Tests } ); - Child = new FillFlowContainer + Child = new Container { AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, Children = new Drawable[] { - level1 = new FillFlowContainer + level1 = new Container { AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, Children = new[] { new DrawableMatchPairing(pairing1), @@ -64,10 +62,9 @@ namespace osu.Game.Tournament.Tests new DrawableMatchPairing(new MatchPairing()), } }, - level2 = new FillFlowContainer + level2 = new Container { AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, Margin = new MarginPadding(20), Children = new[] { diff --git a/osu.Game.Tournament.Tests/teams.json b/osu.Game.Tournament.Tests/teams.json new file mode 100644 index 0000000000..7df0040469 --- /dev/null +++ b/osu.Game.Tournament.Tests/teams.json @@ -0,0 +1 @@ +[{"Players":[{"id":3632846,"username":"lxLucasxl"},{"id":7110363,"username":"BubShish"},{"id":5748843,"username":"Fisk-"},{"id":4585260,"username":"A b y s s"},{"id":9513273,"username":"VorticalEx"},{"id":7341471,"username":"Bossplays_02"}],"Name":"Argentina","Acronym":"ARG"},{"Players":[{"id":2956184,"username":"Lusty Platypus"},{"id":2145124,"username":"Spartan-"},{"id":4018184,"username":"Rek"},{"id":4247722,"username":"PotassiumF"},{"id":9527845,"username":"AngeLItchysick"},{"id":8832989,"username":"[Crz]Yukikaze-"}],"Name":"Australia","Acronym":"AUS"},{"Players":[{"id":9530019,"username":"Lothus"},{"id":2288363,"username":"SillyFangirl"},{"id":4917435,"username":"FelipeLink"},{"id":5691061,"username":"andreymc"},{"id":4794096,"username":"Shedin"},{"id":3224958,"username":"Lazarento"}],"Name":"Brazil","Acronym":"BRA"},{"Players":[{"id":2747704,"username":"Dawt"},{"id":7025841,"username":"CommandoBlack"},{"id":5390121,"username":"Piggy"},{"id":2198070,"username":"beary605"},{"id":2777647,"username":"Freeflow"},{"id":9675053,"username":"Kiyora"}],"Name":"Canada","Acronym":"CAN"},{"Players":[{"id":5281416,"username":"WalterToro"},{"id":2225008,"username":"Skalim"},{"id":469808,"username":"Sophti"},{"id":4686036,"username":"sebaex"},{"id":4116072,"username":"Arkener"},{"id":4531184,"username":"Raizenn"}],"Name":"Chile","Acronym":"CHL"},{"Players":[{"id":89545,"username":"ZhangFan"},{"id":7215250,"username":"[Crz]Mix0130"},{"id":7961511,"username":"[Crz]Hina"},{"id":7082178,"username":"[Crz]Satori"},{"id":6659363,"username":"Wilben_Chan"},{"id":5270332,"username":"[Crz]Lucifer"}],"Name":"China","Acronym":"CHN"},{"Players":[{"id":2883132,"username":"Jole"},{"id":5001658,"username":"FreakyHands"},{"id":4402263,"username":"mart732c"},{"id":6751666,"username":"tailsdk"},{"id":5352616,"username":"Kainura"},{"id":8969233,"username":"zyglrox"}],"Name":"Denmark","Acronym":"DNK"},{"Players":[{"id":8132964,"username":"Camopoltergeist"},{"id":4789005,"username":"princesswell"},{"id":9663200,"username":"--Vanilla--"},{"id":1982941,"username":"matti644"},{"id":8370443,"username":"Your Daughter"},{"id":8105584,"username":"Twist-X"}],"Name":"Finland","Acronym":"FIN"},{"Players":[{"id":1594604,"username":"Azubeur"},{"id":2284328,"username":"Elementaires"},{"id":3897919,"username":"AntoAa"},{"id":4056690,"username":"Todestrieb"},{"id":7190228,"username":"Cunu"},{"id":3909293,"username":"DemonWaves"}],"Name":"France","Acronym":"FRA"},{"Players":[{"id":4516252,"username":"Malox"},{"id":3357640,"username":"ElectroYan"},{"id":5587671,"username":"-Dom-"},{"id":9764403,"username":"tyro901"},{"id":7009106,"username":"Nediz"},{"id":6232245,"username":"LastExceed"}],"Name":"Germany","Acronym":"GER"},{"Players":[{"id":5417362,"username":"Mooncha"},{"id":2121137,"username":"ng051106"},{"id":4544555,"username":"Opean"},{"id":643394,"username":"Snow Note"}],"Name":"['Hong Kong']","Acronym":"HKG"},{"Players":[{"id":5767941,"username":"RemFangirl"},{"id":4557440,"username":"reyss"},{"id":5492871,"username":"LovelySerenade"},{"id":6045757,"username":"Nixeria-sama"},{"id":5114499,"username":"lombit"},{"id":3497139,"username":"LordBoker-"}],"Name":"Indonesia","Acronym":"IDN"},{"Players":[{"id":3461860,"username":"Yomiel"},{"id":5245132,"username":"BadIsTheNewGod"},{"id":3244389,"username":"Mura7797"},{"id":8889323,"username":"extramen"},{"id":8485394,"username":"Cribob"},{"id":6380163,"username":"CribobFanBoy"}],"Name":"Italy","Acronym":"ITA"},{"Players":[{"id":1824775,"username":"inteliser"},{"id":7540718,"username":"tinpura"},{"id":1847698,"username":"PiraTom"},{"id":10011429,"username":"[ misa ]"},{"id":8679066,"username":"mach_jp"},{"id":10242062,"username":"AMDuskia1996"}],"Name":"Japan","Acronym":"JPN"},{"Players":[{"id":3946113,"username":"idqoos123"},{"id":10543278,"username":"hh27v5Fangirl"},{"id":8566617,"username":"capchon"},{"id":5315736,"username":"my2tic"}],"Name":"Macau","Acronym":"MAC"},{"Players":[{"id":7727987,"username":"Neokje"},{"id":8287005,"username":"[MY]xRay"},{"id":9627666,"username":"Minisora"},{"id":6237337,"username":"watarakisah"},{"id":6363947,"username":"Kiritolow"},{"id":4477497,"username":"cheewee10"}],"Name":"Malaysia","Acronym":"MYS"},{"Players":[{"id":1098581,"username":"mrdawn2"},{"id":9369363,"username":"TheSnooperPS"},{"id":6964358,"username":"Redenor"},{"id":9630674,"username":"Freek"},{"id":2827823,"username":"Boots"},{"id":5183940,"username":"2fast4you98"}],"Name":"Netherlands","Acronym":"NLD"},{"Players":[{"id":86188,"username":"Staiain"},{"id":7676585,"username":"Bizarrely_F4st"},{"id":3494742,"username":"KarlF"},{"id":3750387,"username":"Falniir"},{"id":9000473,"username":"Jesen"},{"id":2764122,"username":"Hjeg"}],"Name":"Norway","Acronym":"NOR"},{"Players":[{"id":914472,"username":"akuma123"},{"id":6114633,"username":"DaZeRo5"},{"id":11885200,"username":"DaKub"},{"id":10218427,"username":"Ovento17"}],"Name":"Peru","Acronym":"PER"},{"Players":[{"id":2039089,"username":"arcwinolivirus"},{"id":4469895,"username":"SurfChu85"},{"id":2471512,"username":"JztCallMeRon"},{"id":9770359,"username":"Toyohime-"},{"id":2722489,"username":"Cielo Day"},{"id":3770641,"username":"Ainyan"}],"Name":"Philippines","Acronym":"PHL"},{"Players":[{"id":743282,"username":"Tidek"},{"id":1654221,"username":"Hudonom"},{"id":6382502,"username":"Kroly-"},{"id":6905790,"username":"Arkitev"},{"id":2235750,"username":"_underjoy"},{"id":3353343,"username":"[-Agonys-]"}],"Name":"Poland","Acronym":"POL"},{"Players":[{"id":9074986,"username":"AngeloLagusa"},{"id":5145890,"username":"Jormungand"},{"id":9847747,"username":"MAZAFUKER1337"},{"id":8035172,"username":"fegasaren"},{"id":7767168,"username":"claer"}],"Name":"['Russian Federation']","Acronym":"RUS"},{"Players":[{"id":7199159,"username":"ByeForNow"},{"id":876528,"username":"Tamaneko"},{"id":8612061,"username":"Polytetral"},{"id":7462804,"username":"Lindyes"},{"id":4574597,"username":"OrienST8"},{"id":9362562,"username":"LuigiClaren"}],"Name":"Singapore","Acronym":"SGP"},{"Players":[{"id":6699923,"username":"SuddenDeath"},{"id":7014697,"username":"Estonians"},{"id":8474029,"username":"wonder5193"},{"id":8283444,"username":"[ Special ]"},{"id":903155,"username":"Nausicaa"},{"id":7945868,"username":"SnowScent"}],"Name":"['South Korea']","Acronym":"KOR"},{"Players":[{"id":3154852,"username":"aitor98"},{"id":8141215,"username":"David5_"},{"id":7935867,"username":"miguel-580"},{"id":6809566,"username":"itsdarious555"},{"id":8497100,"username":"GreenSoul"}],"Name":"Spain","Acronym":"ESP"},{"Players":[{"id":1612580,"username":"Vent"},{"id":6872025,"username":"Couil"},{"id":2229274,"username":"Xytox"},{"id":4899311,"username":"Stug"},{"id":5045509,"username":"YoShiZoRi"},{"id":3918056,"username":"Craty"}],"Name":"Sweden","Acronym":"SWE"},{"Players":[{"id":4952941,"username":"Gamer97"},{"id":8642966,"username":"Adyrem"},{"id":8372292,"username":"doere_"},{"id":9593126,"username":"Monogai"},{"id":3974114,"username":"Haprapra"},{"id":2573716,"username":"Akayro"}],"Name":"Switzerland","Acronym":"CHE"},{"Players":[{"id":766374,"username":"LostCool"},{"id":2838908,"username":"4ksrub"},{"id":6535376,"username":"SharpKunG1412"},{"id":2772110,"username":"BossMadWolf"},{"id":8521723,"username":"MyZterioN-"},{"id":6456531,"username":"-[DaNieL_TH]-"}],"Name":"Thailand","Acronym":"THA"},{"Players":[{"id":2656856,"username":"Sakaki"},{"id":6193819,"username":"SaKuRaLaN"},{"id":1990582,"username":"mspstommy"},{"id":8819232,"username":"Tamamo Desu"},{"id":11531528,"username":"Red MewFew"},{"id":1967808,"username":"luckygino"}],"Name":"Taiwan","Acronym":"TWN"},{"Players":[{"id":3359035,"username":"Amascite"},{"id":4168230,"username":"PikachuNick"},{"id":3617889,"username":"itsjakey"},{"id":3799946,"username":"xSnaggles"},{"id":6814203,"username":"Civilization"},{"id":6701945,"username":"Domblade"}],"Name":"['United Kingdom']","Acronym":"GBR"},{"Players":[{"id":7616811,"username":"TheToaphster"},{"id":2141612,"username":"stupud man"},{"id":7687954,"username":"Neuro-"},{"id":3251373,"username":"-Electro-"},{"id":5610085,"username":"EtienneXC"},{"id":2594280,"username":"Chrubble"}],"Name":"['United States']","Acronym":"USA"},{"Players":[{"id":2243452,"username":"Nakatoru"},{"id":8065567,"username":"Aezlack"},{"id":8301758,"username":"Edvo"},{"id":2140739,"username":"[_Chichinya_]"},{"id":8198818,"username":"[_Gearfrik_]"},{"id":1489811,"username":"_Yisus_"}],"Name":"Venezuela","Acronym":"VEN"}] \ No newline at end of file diff --git a/osu.Game.Tournament/Components/TournamentTeam.cs b/osu.Game.Tournament/Components/TournamentTeam.cs index 03d787af76..14e874e9ef 100644 --- a/osu.Game.Tournament/Components/TournamentTeam.cs +++ b/osu.Game.Tournament/Components/TournamentTeam.cs @@ -1,6 +1,9 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; +using osu.Game.Users; + namespace osu.Game.Tournament.Components { public class TournamentTeam @@ -10,10 +13,16 @@ namespace osu.Game.Tournament.Components /// public string FullName; + private string flagName; + /// /// Name of the file containing the flag. /// - public string FlagName; + public string FlagName + { + get { return flagName ?? Acronym.Substring(0, 2); } + set { flagName = value; } + } private string acronym; @@ -25,5 +34,7 @@ namespace osu.Game.Tournament.Components get { return acronym ?? FullName.Substring(0, 3); } set { acronym = value; } } + + public List Players { get; set; } } } diff --git a/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs b/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs index 44ee9d64ca..625f05edac 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs @@ -50,7 +50,6 @@ namespace osu.Game.Tournament.Screens.Drawings.Components teams.Add(new TournamentTeam { - FlagName = split[0].Trim(), FullName = split[1].Trim(), Acronym = split.Length >= 3 ? split[2].Trim() : null }); diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 8191df6680..62d38a2ee6 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -7,8 +7,12 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Lines; +using osu.Framework.Input.EventArgs; +using osu.Framework.Input.States; using osu.Framework.MathUtils; using OpenTK; +using OpenTK.Input; +using SixLabors.Primitives; namespace osu.Game.Tournament.Screens.Ladder.Components { @@ -46,6 +50,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { Pairing = pairing; + Position = new Vector2(pairing.Position.X, pairing.Position.Y); + AutoSizeAxes = Axes.Both; Margin = new MarginPadding(5); @@ -103,15 +109,19 @@ namespace osu.Game.Tournament.Screens.Ladder.Components var end = getCenteredVector(progression.ScreenSpaceDrawQuad.TopLeft, progression.ScreenSpaceDrawQuad.BottomLeft); bool progressionAbove = progression.ScreenSpaceDrawQuad.TopLeft.Y < ScreenSpaceDrawQuad.TopLeft.Y; + bool progressionToRight = progression.ScreenSpaceDrawQuad.TopLeft.X > ScreenSpaceDrawQuad.TopLeft.X; if (!Precision.AlmostEquals(progressionStart, start) || !Precision.AlmostEquals(progressionEnd, end)) { progressionStart = start; progressionEnd = end; - path.Origin = progressionAbove ? Anchor.BottomLeft : Anchor.TopLeft; + path.Origin = progressionAbove ? Anchor.y2 : Anchor.y0; path.Y = progressionAbove ? line_width : -line_width; + path.Origin |= progressionToRight ? Anchor.x0 : Anchor.x2; + //path.X = progressionToRight ? line_width : -line_width; + Vector2 startPosition = path.ToLocalSpace(start) + new Vector2(padding, 0); Vector2 endPosition = path.ToLocalSpace(end) + new Vector2(-padding, 0); Vector2 intermediate1 = startPosition + new Vector2(padding, 0); @@ -131,6 +141,12 @@ namespace osu.Game.Tournament.Screens.Ladder.Components destinationForWinner.Value = Pairing.Winner; } + protected override void UpdateAfterAutoSize() + { + base.UpdateAfterAutoSize(); + updateProgression(); + } + private void updateWinConditions() { if (conditions.Value == null) return; @@ -163,5 +179,20 @@ namespace osu.Game.Tournament.Screens.Ladder.Components SchedulerAfterChildren.Add(() => Scheduler.Add(updateProgression)); updateWinConditions(); } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => args.Button == MouseButton.Left; + + protected override bool OnDragStart(InputState state) => true; + + protected override bool OnDrag(InputState state) + { + if (base.OnDrag(state)) return true; + + this.MoveToOffset(state.Mouse.Delta); + + var pos = Position; + Pairing.Position = new Point((int)pos.X, (int)pos.Y); + return true; + } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index 06b400976c..1077438693 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -2,11 +2,15 @@ // 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; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.EventArgs; using osu.Framework.Input.States; using osu.Game.Graphics; @@ -18,8 +22,9 @@ using OpenTK.Input; namespace osu.Game.Tournament.Screens.Ladder.Components { - public class DrawableMatchTeam : DrawableTournamentTeam + public class DrawableMatchTeam : DrawableTournamentTeam, IHasContextMenu { + private readonly Bindable team; private readonly MatchPairing pairing; private OsuSpriteText scoreText; private Box background; @@ -31,10 +36,12 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private Color4 colourNormal; private readonly Func isWinner; + private LadderManager manager; - public DrawableMatchTeam(TournamentTeam team, MatchPairing pairing) + public DrawableMatchTeam(Bindable team, MatchPairing pairing) : base(team) { + this.team = team.GetBoundCopy(); this.pairing = pairing; Size = new Vector2(150, 40); @@ -53,14 +60,16 @@ namespace osu.Game.Tournament.Screens.Ladder.Components isWinner = () => pairing.Winner == Team; completed.BindTo(pairing.Completed); - if (team != null) - score.BindTo(team == pairing.Team1.Value ? pairing.Team1Score : pairing.Team2Score); + if (team.Value != null) + score.BindTo(team.Value == pairing.Team1.Value ? pairing.Team1Score : pairing.Team2Score); } } - [BackgroundDependencyLoader] - private void load(OsuColour colours) + [BackgroundDependencyLoader(true)] + private void load(OsuColour colours, LadderManager manager) { + this.manager = manager; + colourWinner = colours.BlueDarker; colourNormal = OsuColour.Gray(0.2f); @@ -117,7 +126,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - if (Team == null) return true; + if (Team == null) return false; if (args.Button == MouseButton.Left) { @@ -136,7 +145,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components pairing.CancelMatchStart(); } - return true; + return false; } private void updateWinStyle() @@ -147,5 +156,27 @@ namespace osu.Game.Tournament.Screens.Ladder.Components scoreText.Font = AcronymText.Font = winner ? "Exo2.0-Bold" : "Exo2.0-Regular"; } + + public MenuItem[] ContextMenuItems => new[] + { + new MenuItem("Populate team", () => team.Value = manager.Teams.Random()), + }; + } + + internal static class Extensions + { + public static T Random(this IEnumerable enumerable) + { + if (enumerable == null) + { + throw new ArgumentNullException(nameof(enumerable)); + } + + // note: creating a Random instance each call may not be correct for you, + // consider a thread-safe static instance + var r = new Random(); + var list = enumerable as IList ?? enumerable.ToList(); + return list.Count == 0 ? default(T) : list[r.Next(0, list.Count)]; + } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs new file mode 100644 index 0000000000..e65cc7a512 --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + public class LadderInfo + { + public List Pairings = new List(); + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs new file mode 100644 index 0000000000..b7764f946f --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.States; +using osu.Game.Graphics.Cursor; +using osu.Game.Tournament.Components; +using SixLabors.Primitives; + +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + public class LadderManager : CompositeDrawable + { + public readonly LadderInfo Info; + public readonly List Teams; + private readonly OsuContextMenuContainer content; + + public LadderManager(LadderInfo info, List teams) + { + Info = info; + Teams = teams; + + RelativeSizeAxes = Axes.Both; + + InternalChild = content = new OsuContextMenuContainer + { + RelativeSizeAxes = Axes.Both + }; + + foreach (var pairing in info.Pairings) + addPairing(pairing); + } + + protected void AddPairing(MatchPairing pairing) + { + Info.Pairings.Add(pairing); + addPairing(pairing); + } + + private void addPairing(MatchPairing pairing) => content.Add(new DrawableMatchPairing(pairing)); + + protected override bool OnClick(InputState state) + { + AddPairing(new MatchPairing + { + Position = new Point((int)state.Mouse.Position.X, (int)state.Mouse.Position.Y) + }); + + return true; + } + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index b615330cc2..d2a8f66cfd 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -1,8 +1,10 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using Newtonsoft.Json; using osu.Framework.Configuration; using osu.Game.Tournament.Components; +using SixLabors.Primitives; namespace osu.Game.Tournament.Screens.Ladder.Components { @@ -11,13 +13,22 @@ namespace osu.Game.Tournament.Screens.Ladder.Components /// public class MatchPairing { - public Bindable Team1 = new Bindable(); - public Bindable Team1Score = new Bindable(); + public readonly Bindable Team1 = new Bindable(); - public Bindable Team2 = new Bindable(); - public Bindable Team2Score = new Bindable(); + public readonly Bindable Team1Score = new Bindable(); - public Bindable Completed = new Bindable(); + public readonly Bindable Team2 = new Bindable(); + + public readonly Bindable Team2Score = new Bindable(); + + public readonly Bindable Completed = new Bindable(); + + [JsonProperty] + public Point Position; + + public MatchPairing() + { + } public MatchPairing(TournamentTeam team1 = null, TournamentTeam team2 = null) { From ff125f4c71a4f6ee0d8cd746469b3fbb9530bc6d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 15 Sep 2018 22:13:32 +0900 Subject: [PATCH 0125/2854] Reduce noise in json output and handle the case the file doesn't exist --- osu.Game.Tournament.Tests/TestCaseLadderManager.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index dc4633aa17..b61619de12 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tournament.Tests public TestCaseLadderManager() { var teams = JsonConvert.DeserializeObject>(File.ReadAllText(@"teams.json")); - var ladder = JsonConvert.DeserializeObject(File.ReadAllText(@"bracket.json")) ?? new LadderInfo(); + var ladder = File.Exists(@"bracket.json") ? JsonConvert.DeserializeObject(File.ReadAllText(@"bracket.json")) : new LadderInfo(); Child = manager = new LadderManager(ladder, teams); } @@ -28,7 +28,12 @@ namespace osu.Game.Tournament.Tests { base.Dispose(isDisposing); - File.WriteAllText(@"bracket.json", JsonConvert.SerializeObject(manager.Info)); + File.WriteAllText(@"bracket.json", JsonConvert.SerializeObject(manager.Info, + new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + })); } } } From 74014bec408cd14cb863086469c27f501e14ac2b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 16 Sep 2018 05:35:51 +0900 Subject: [PATCH 0126/2854] wip --- .../TestCaseLadderManager.cs | 6 +- .../TestCaseMatchPairings.cs | 6 +- .../Ladder/Components/DrawableMatchPairing.cs | 87 +---------- .../Ladder/Components/DrawableMatchTeam.cs | 1 + .../Screens/Ladder/Components/LadderInfo.cs | 1 + .../Ladder/Components/LadderManager.cs | 135 ++++++++++++++++-- .../Screens/Ladder/Components/MatchPairing.cs | 8 +- 7 files changed, 148 insertions(+), 96 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index b61619de12..00a5df411f 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using Newtonsoft.Json; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Game.Tests.Visual; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; @@ -16,6 +17,9 @@ namespace osu.Game.Tournament.Tests [Cached] private readonly LadderManager manager; + [Cached] + private Bindable conditions = new Bindable(new TournamentConditions { BestOf = 9 }); + public TestCaseLadderManager() { var teams = JsonConvert.DeserializeObject>(File.ReadAllText(@"teams.json")); @@ -28,7 +32,7 @@ namespace osu.Game.Tournament.Tests { base.Dispose(isDisposing); - File.WriteAllText(@"bracket.json", JsonConvert.SerializeObject(manager.Info, + File.WriteAllText(@"bracket.json", JsonConvert.SerializeObject(manager.CreateInfo(), new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, diff --git a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs index bd6b085b0f..ca2523c898 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs @@ -75,12 +75,12 @@ namespace osu.Game.Tournament.Tests } }; - level1.Children[0].Progression = level2.Children[0]; - level1.Children[1].Progression = level2.Children[0]; + level1.Children[0].Pairing.Progression.Value = level2.Children[0].Pairing; + level1.Children[1].Pairing.Progression.Value = level2.Children[0].Pairing; AddRepeatStep("change scores", () => pairing1.Team2Score.Value++, 4); AddStep("add new team", () => pairing2.Team2.Value = new TournamentTeam { FlagName = "PT", FullName = "Portugal" }); - AddStep("Add progression", () => level1.Children[2].Progression = level2.Children[1]); + AddStep("Add progression", () => level1.Children[2].Pairing.Progression.Value = level2.Children[1].Pairing); AddStep("start match", () => pairing2.StartMatch()); diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 62d38a2ee6..8f33cdcbbc 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -1,15 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Lines; using osu.Framework.Input.EventArgs; using osu.Framework.Input.States; -using osu.Framework.MathUtils; using OpenTK; using OpenTK.Input; using SixLabors.Primitives; @@ -20,32 +17,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { public readonly MatchPairing Pairing; private readonly FillFlowContainer flow; - private DrawableMatchPairing progression; - private readonly Bindable conditions = new Bindable(); - private readonly Path path; - - public DrawableMatchPairing Progression - { - get => progression; - set - { - if (progression == value) return; - progression = value; - - if (LoadState == LoadState.Loaded) - updateProgression(); - - path.FadeInFromZero(200); - } - } - - private Vector2 progressionStart; - private Vector2 progressionEnd; - - private const float line_width = 2; - public DrawableMatchPairing(MatchPairing pairing) { Pairing = pairing; @@ -63,14 +36,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components AutoSizeAxes = Axes.Both, Direction = FillDirection.Vertical, Spacing = new Vector2(2) - }, - path = new Path - { - Alpha = 0, - BypassAutoSizeAxes = Axes.Both, - Anchor = Anchor.CentreRight, - PathWidth = line_width, - }, + } }; pairing.Team1.BindValueChanged(_ => updateTeams()); @@ -80,6 +46,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components pairing.Team2Score.BindValueChanged(_ => updateWinConditions()); pairing.Completed.BindValueChanged(_ => updateProgression()); + pairing.Progression.BindValueChanged(_ => updateProgression()); updateTeams(); } @@ -95,58 +62,16 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private void updateProgression() { - if (progression == null) - { - path.Positions = new List(); - return; - } + var progression = Pairing.Progression?.Value; - Vector2 getCenteredVector(Vector2 top, Vector2 bottom) => new Vector2(top.X, top.Y + (bottom.Y - top.Y) / 2); + if (progression == null) return; - const float padding = 5; - - var start = getCenteredVector(ScreenSpaceDrawQuad.TopRight, ScreenSpaceDrawQuad.BottomRight); - var end = getCenteredVector(progression.ScreenSpaceDrawQuad.TopLeft, progression.ScreenSpaceDrawQuad.BottomLeft); - - bool progressionAbove = progression.ScreenSpaceDrawQuad.TopLeft.Y < ScreenSpaceDrawQuad.TopLeft.Y; - bool progressionToRight = progression.ScreenSpaceDrawQuad.TopLeft.X > ScreenSpaceDrawQuad.TopLeft.X; - - if (!Precision.AlmostEquals(progressionStart, start) || !Precision.AlmostEquals(progressionEnd, end)) - { - progressionStart = start; - progressionEnd = end; - - path.Origin = progressionAbove ? Anchor.y2 : Anchor.y0; - path.Y = progressionAbove ? line_width : -line_width; - - path.Origin |= progressionToRight ? Anchor.x0 : Anchor.x2; - //path.X = progressionToRight ? line_width : -line_width; - - Vector2 startPosition = path.ToLocalSpace(start) + new Vector2(padding, 0); - Vector2 endPosition = path.ToLocalSpace(end) + new Vector2(-padding, 0); - Vector2 intermediate1 = startPosition + new Vector2(padding, 0); - Vector2 intermediate2 = new Vector2(intermediate1.X, endPosition.Y); - - path.Positions = new List - { - startPosition, - intermediate1, - intermediate2, - endPosition - }; - } - - var destinationForWinner = progressionAbove ? progression.Pairing.Team2 : progression.Pairing.Team1; + bool progressionAbove = progression.ID < Pairing.ID; + var destinationForWinner = progressionAbove ? progression.Team2 : progression.Team1; destinationForWinner.Value = Pairing.Winner; } - protected override void UpdateAfterAutoSize() - { - base.UpdateAfterAutoSize(); - updateProgression(); - } - private void updateWinConditions() { if (conditions.Value == null) return; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index 1077438693..fcfc87e5c3 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -160,6 +160,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public MenuItem[] ContextMenuItems => new[] { new MenuItem("Populate team", () => team.Value = manager.Teams.Random()), + new MenuItem("Join with", () => manager.JoinRequest(pairing)), }; } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs index e65cc7a512..0860966502 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs @@ -5,5 +5,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public class LadderInfo { public List Pairings = new List(); + public List<(int, int)> Progressions = new List<(int, int)>(); } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs index b7764f946f..834df747d0 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs @@ -1,51 +1,166 @@ +using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Lines; using osu.Framework.Input.States; using osu.Game.Graphics.Cursor; using osu.Game.Tournament.Components; +using OpenTK; using SixLabors.Primitives; namespace osu.Game.Tournament.Screens.Ladder.Components { public class LadderManager : CompositeDrawable { - public readonly LadderInfo Info; public readonly List Teams; - private readonly OsuContextMenuContainer content; + private readonly Container pairingsContainer; + private readonly Container paths; public LadderManager(LadderInfo info, List teams) { - Info = info; Teams = teams; RelativeSizeAxes = Axes.Both; - InternalChild = content = new OsuContextMenuContainer + InternalChild = new OsuContextMenuContainer { - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + pairingsContainer = new Container { RelativeSizeAxes = Axes.Both }, + paths = new Container { RelativeSizeAxes = Axes.Both }, + } }; + foreach (var pair in info.Progressions) + info.Pairings.Single(p => p.ID == pair.Item1).Progression.Value = info.Pairings.Single(p => p.ID == pair.Item2); + foreach (var pairing in info.Pairings) addPairing(pairing); } - protected void AddPairing(MatchPairing pairing) + public LadderInfo CreateInfo() { - Info.Pairings.Add(pairing); - addPairing(pairing); + var pairings = pairingsContainer.Select(p => p.Pairing).ToList(); + + return new LadderInfo + { + Pairings = pairings, + Progressions = pairings + .Where(p => p.Progression.Value != null) + .Select(p => (p.ID, p.Progression.Value.ID)) + .ToList() + }; } - private void addPairing(MatchPairing pairing) => content.Add(new DrawableMatchPairing(pairing)); + private void addPairing(MatchPairing pairing) => pairingsContainer.Add(new DrawableMatchPairing(pairing)); protected override bool OnClick(InputState state) { - AddPairing(new MatchPairing + addPairing(new MatchPairing { Position = new Point((int)state.Mouse.Position.X, (int)state.Mouse.Position.Y) }); return true; } + + protected override void Update() + { + base.Update(); + + paths.Clear(); + + int id = 0; + foreach (var pairing in pairingsContainer.OrderBy(d => d.Y).ThenBy(d => d.X)) + { + pairing.Pairing.ID = id++; + + if (pairing.Pairing.Progression.Value != null) + { + var progression = pairingsContainer.Single(p => p.Pairing == pairing.Pairing.Progression.Value); + + const float line_width = 2; + + var path = new Path + { + BypassAutoSizeAxes = Axes.Both, + PathWidth = line_width, + }; + + paths.Add(path); + + Vector2 getCenteredVector(Vector2 top, Vector2 bottom) => new Vector2(top.X, top.Y + (bottom.Y - top.Y) / 2); + + const float padding = 5; + + var start = getCenteredVector(pairing.ScreenSpaceDrawQuad.TopRight, pairing.ScreenSpaceDrawQuad.BottomRight); + var end = getCenteredVector(progression.ScreenSpaceDrawQuad.TopLeft, progression.ScreenSpaceDrawQuad.BottomLeft); + + bool progressionAbove = progression.ScreenSpaceDrawQuad.TopLeft.Y < pairing.ScreenSpaceDrawQuad.TopLeft.Y; + bool progressionToRight = progression.ScreenSpaceDrawQuad.TopLeft.X > pairing.ScreenSpaceDrawQuad.TopLeft.X; + + //if (!Precision.AlmostEquals(progressionStart, start) || !Precision.AlmostEquals(progressionEnd, end)) + { + // var progressionStart = start; + // var progressionEnd = end; + + Vector2 startPosition = path.ToLocalSpace(start) + new Vector2(padding, 0); + Vector2 endPosition = path.ToLocalSpace(end) + new Vector2(-padding, 0); + Vector2 intermediate1 = startPosition + new Vector2(padding, 0); + Vector2 intermediate2 = new Vector2(intermediate1.X, endPosition.Y); + + path.Positions = new List + { + startPosition, + intermediate1, + intermediate2, + endPosition + }; + } + } + } + } + + public void JoinRequest(MatchPairing pairing) + { + AddInternal(new JoinRequestHandler(pairing, handleProgression)); + } + + private bool handleProgression(JoinRequestHandler handler, InputState state) + { + var found = pairingsContainer.FirstOrDefault(d => d.ReceiveMouseInputAt(state.Mouse.NativeState.Position)); + + if (found != null) + { + handler.Source.Progression.Value = found.Pairing; + return true; + } + + return false; + } + + private class JoinRequestHandler : CompositeDrawable + { + public readonly MatchPairing Source; + private readonly Func onClick; + + public JoinRequestHandler(MatchPairing source, Func onClick) + { + Source = source; + this.onClick = onClick; + RelativeSizeAxes = Axes.Both; + } + + protected override bool OnClick(InputState state) + { + if (onClick(this, state)) + Expire(); + + return true; + } + } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index d2a8f66cfd..47a94b854e 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -13,6 +13,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components /// public class MatchPairing { + public int ID; + public readonly Bindable Team1 = new Bindable(); public readonly Bindable Team1Score = new Bindable(); @@ -23,6 +25,9 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly Bindable Completed = new Bindable(); + [JsonIgnore] + public readonly Bindable Progression = new Bindable(); + [JsonProperty] public Point Position; @@ -36,7 +41,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Team2.Value = team2; } - public TournamentTeam Winner => !Completed.Value ? null : (Team1Score.Value > Team2Score.Value ? Team1.Value : Team2.Value); + [JsonIgnore] + public TournamentTeam Winner => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team1.Value : Team2.Value; /// /// Remove scores from the match, in case of a false click or false start. From c6071f6e4d221351c87afd844a2509977919a20f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 Sep 2018 23:47:01 +0900 Subject: [PATCH 0127/2854] Better line drawing --- .../Ladder/Components/LadderManager.cs | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs index 834df747d0..37d2461304 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs @@ -94,32 +94,36 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Vector2 getCenteredVector(Vector2 top, Vector2 bottom) => new Vector2(top.X, top.Y + (bottom.Y - top.Y) / 2); - const float padding = 5; + const float padding = 10; - var start = getCenteredVector(pairing.ScreenSpaceDrawQuad.TopRight, pairing.ScreenSpaceDrawQuad.BottomRight); - var end = getCenteredVector(progression.ScreenSpaceDrawQuad.TopLeft, progression.ScreenSpaceDrawQuad.BottomLeft); + var q1 = pairing.ScreenSpaceDrawQuad; + var q2 = progression.ScreenSpaceDrawQuad; - bool progressionAbove = progression.ScreenSpaceDrawQuad.TopLeft.Y < pairing.ScreenSpaceDrawQuad.TopLeft.Y; - bool progressionToRight = progression.ScreenSpaceDrawQuad.TopLeft.X > pairing.ScreenSpaceDrawQuad.TopLeft.X; + bool progressionToRight = q2.TopLeft.X > q1.TopLeft.X; - //if (!Precision.AlmostEquals(progressionStart, start) || !Precision.AlmostEquals(progressionEnd, end)) + if (!progressionToRight) { - // var progressionStart = start; - // var progressionEnd = end; - - Vector2 startPosition = path.ToLocalSpace(start) + new Vector2(padding, 0); - Vector2 endPosition = path.ToLocalSpace(end) + new Vector2(-padding, 0); - Vector2 intermediate1 = startPosition + new Vector2(padding, 0); - Vector2 intermediate2 = new Vector2(intermediate1.X, endPosition.Y); - - path.Positions = new List - { - startPosition, - intermediate1, - intermediate2, - endPosition - }; + var temp = q2; + q2 = q1; + q1 = temp; } + + var c1 = getCenteredVector(q1.TopRight, q1.BottomRight) + new Vector2(padding, 0); + var c2 = getCenteredVector(q2.TopLeft, q2.BottomLeft) - new Vector2(padding, 0); + + var p1 = c1; + var p2 = p1 + new Vector2(padding, 0); + + if (p2.X > c2.X) + { + c2 = getCenteredVector(q2.TopRight, q2.BottomRight) + new Vector2(padding, 0); + p2.X = c2.X + padding; + } + + var p3 = new Vector2(p2.X, c2.Y); + var p4 = new Vector2(c2.X, p3.Y); + + path.Positions = new[] { p1, p2, p3, p4 }.Select(p => path.ToLocalSpace(p)).ToList(); } } } From 1de82afd169c7a30048727d5a65a05bad2e9e1dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Sep 2018 02:00:16 +0900 Subject: [PATCH 0128/2854] Betterify pairing request logic --- .../Ladder/Components/LadderManager.cs | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs index 37d2461304..3e8d2649eb 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; @@ -130,40 +129,33 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public void JoinRequest(MatchPairing pairing) { - AddInternal(new JoinRequestHandler(pairing, handleProgression)); - } - - private bool handleProgression(JoinRequestHandler handler, InputState state) - { - var found = pairingsContainer.FirstOrDefault(d => d.ReceiveMouseInputAt(state.Mouse.NativeState.Position)); - - if (found != null) - { - handler.Source.Progression.Value = found.Pairing; - return true; - } - - return false; + AddInternal(new JoinRequestHandler(pairingsContainer, pairing)); } private class JoinRequestHandler : CompositeDrawable { + private readonly Container pairingsContainer; public readonly MatchPairing Source; - private readonly Func onClick; - public JoinRequestHandler(MatchPairing source, Func onClick) + public JoinRequestHandler(Container pairingsContainer, MatchPairing source) { + this.pairingsContainer = pairingsContainer; Source = source; - this.onClick = onClick; RelativeSizeAxes = Axes.Both; } protected override bool OnClick(InputState state) { - if (onClick(this, state)) - Expire(); + var found = pairingsContainer.FirstOrDefault(d => d.ReceiveMouseInputAt(state.Mouse.NativeState.Position)); - return true; + if (found != null) + { + Source.Progression.Value = found.Pairing; + Expire(); + return true; + } + + return false; } } } From ffadd5dfd0c0635ab60727a249dcf17a41f395fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Sep 2018 18:51:37 +0900 Subject: [PATCH 0129/2854] Improve join request visual guide --- .../TestCaseLadderManager.cs | 1 + .../Ladder/Components/ProgressionPath.cs | 59 +++++++++++++ .../Ladder/{Components => }/LadderManager.cs | 84 +++++++------------ 3 files changed, 90 insertions(+), 54 deletions(-) create mode 100644 osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs rename osu.Game.Tournament/Screens/Ladder/{Components => }/LadderManager.cs (62%) diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index 00a5df411f..9544a5c17e 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Game.Tests.Visual; using osu.Game.Tournament.Components; +using osu.Game.Tournament.Screens.Ladder; using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament.Tests diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs new file mode 100644 index 0000000000..c44f67a171 --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs @@ -0,0 +1,59 @@ +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Lines; +using OpenTK; + +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + public class ProgressionPath : Path + { + public DrawableMatchPairing Source { get; private set; } + public DrawableMatchPairing Destination { get; private set; } + + public ProgressionPath(DrawableMatchPairing source, DrawableMatchPairing destination) + { + Source = source; + Destination = destination; + + PathWidth = 2; + BypassAutoSizeAxes = Axes.Both; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Vector2 getCenteredVector(Vector2 top, Vector2 bottom) => new Vector2(top.X, top.Y + (bottom.Y - top.Y) / 2); + + const float padding = 10; + + var q1 = Source.ScreenSpaceDrawQuad; + var q2 = Destination.ScreenSpaceDrawQuad; + + bool progressionToRight = q2.TopLeft.X > q1.TopLeft.X; + + if (!progressionToRight) + { + var temp = q2; + q2 = q1; + q1 = temp; + } + + var c1 = getCenteredVector(q1.TopRight, q1.BottomRight) + new Vector2(padding, 0); + var c2 = getCenteredVector(q2.TopLeft, q2.BottomLeft) - new Vector2(padding, 0); + + var p1 = c1; + var p2 = p1 + new Vector2(padding, 0); + + if (p2.X > c2.X) + { + c2 = getCenteredVector(q2.TopRight, q2.BottomRight) + new Vector2(padding, 0); + p2.X = c2.X + padding; + } + + var p3 = new Vector2(p2.X, c2.Y); + var p4 = new Vector2(c2.X, p3.Y); + + Positions = new[] { p1, p2, p3, p4 }.Select(ToLocalSpace).ToList(); + } + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs similarity index 62% rename from osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs rename to osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 3e8d2649eb..bf66447224 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -6,10 +6,10 @@ using osu.Framework.Graphics.Lines; using osu.Framework.Input.States; using osu.Game.Graphics.Cursor; using osu.Game.Tournament.Components; -using OpenTK; +using osu.Game.Tournament.Screens.Ladder.Components; using SixLabors.Primitives; -namespace osu.Game.Tournament.Screens.Ladder.Components +namespace osu.Game.Tournament.Screens.Ladder { public class LadderManager : CompositeDrawable { @@ -78,75 +78,51 @@ namespace osu.Game.Tournament.Screens.Ladder.Components pairing.Pairing.ID = id++; if (pairing.Pairing.Progression.Value != null) - { - var progression = pairingsContainer.Single(p => p.Pairing == pairing.Pairing.Progression.Value); - - const float line_width = 2; - - var path = new Path - { - BypassAutoSizeAxes = Axes.Both, - PathWidth = line_width, - }; - - paths.Add(path); - - Vector2 getCenteredVector(Vector2 top, Vector2 bottom) => new Vector2(top.X, top.Y + (bottom.Y - top.Y) / 2); - - const float padding = 10; - - var q1 = pairing.ScreenSpaceDrawQuad; - var q2 = progression.ScreenSpaceDrawQuad; - - bool progressionToRight = q2.TopLeft.X > q1.TopLeft.X; - - if (!progressionToRight) - { - var temp = q2; - q2 = q1; - q1 = temp; - } - - var c1 = getCenteredVector(q1.TopRight, q1.BottomRight) + new Vector2(padding, 0); - var c2 = getCenteredVector(q2.TopLeft, q2.BottomLeft) - new Vector2(padding, 0); - - var p1 = c1; - var p2 = p1 + new Vector2(padding, 0); - - if (p2.X > c2.X) - { - c2 = getCenteredVector(q2.TopRight, q2.BottomRight) + new Vector2(padding, 0); - p2.X = c2.X + padding; - } - - var p3 = new Vector2(p2.X, c2.Y); - var p4 = new Vector2(c2.X, p3.Y); - - path.Positions = new[] { p1, p2, p3, p4 }.Select(p => path.ToLocalSpace(p)).ToList(); - } + paths.Add(new ProgressionPath(pairing, pairingsContainer.Single(p => p.Pairing == pairing.Pairing.Progression.Value))); } } - public void JoinRequest(MatchPairing pairing) - { - AddInternal(new JoinRequestHandler(pairingsContainer, pairing)); - } + public void JoinRequest(MatchPairing pairing) => AddInternal(new JoinRequestHandler(pairingsContainer, pairing)); private class JoinRequestHandler : CompositeDrawable { private readonly Container pairingsContainer; public readonly MatchPairing Source; + private ProgressionPath path; + public JoinRequestHandler(Container pairingsContainer, MatchPairing source) { this.pairingsContainer = pairingsContainer; - Source = source; RelativeSizeAxes = Axes.Both; + + Source = source; + Source.Progression.Value = null; + } + + private DrawableMatchPairing findTarget(InputState state) => pairingsContainer.FirstOrDefault(d => d.ReceiveMouseInputAt(state.Mouse.NativeState.Position)); + + protected override bool OnMouseMove(InputState state) + { + var found = findTarget(state); + + if (found == path?.Destination) + return false; + + path?.Expire(); + path = null; + + if (found == null) + return false; + + AddInternal(path = new ProgressionPath(pairingsContainer.First(c => c.Pairing == Source), found) { Alpha = 0.4f }); + + return base.OnMouseMove(state); } protected override bool OnClick(InputState state) { - var found = pairingsContainer.FirstOrDefault(d => d.ReceiveMouseInputAt(state.Mouse.NativeState.Position)); + var found = findTarget(state); if (found != null) { From 0076ef34476e2099f96e6365254a4211c3f8eb80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Sep 2018 18:52:00 +0900 Subject: [PATCH 0130/2854] Fix layout in MatchPairing test case --- .../TestCaseMatchPairings.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs index ca2523c898..f1116b2db7 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs @@ -47,14 +47,16 @@ namespace osu.Game.Tournament.Tests } ); - Child = new Container + Child = new FillFlowContainer { - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, Children = new Drawable[] { - level1 = new Container + level1 = new FillFlowContainer { - AutoSizeAxes = Axes.Both, + AutoSizeAxes = Axes.X, + Direction = FillDirection.Vertical, Children = new[] { new DrawableMatchPairing(pairing1), @@ -62,9 +64,10 @@ namespace osu.Game.Tournament.Tests new DrawableMatchPairing(new MatchPairing()), } }, - level2 = new Container + level2 = new FillFlowContainer { - AutoSizeAxes = Axes.Both, + AutoSizeAxes = Axes.X, + Direction = FillDirection.Vertical, Margin = new MarginPadding(20), Children = new[] { From f2f4e964c569cb7d3ca02f49896f3b0a1bb6fa30 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Sep 2018 19:58:47 +0900 Subject: [PATCH 0131/2854] Add deletion support --- .../TestCaseLadderManager.cs | 8 +++++- .../Ladder/Components/DrawableMatchPairing.cs | 9 ++++++ .../Ladder/Components/DrawableMatchTeam.cs | 8 ++++-- .../Screens/Ladder/Components/MatchPairing.cs | 15 ++++++++++ .../Screens/Ladder/LadderManager.cs | 28 +++++++++++-------- 5 files changed, 52 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index 9544a5c17e..b53660c703 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -6,6 +6,8 @@ using System.IO; using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Game.Graphics.Cursor; using osu.Game.Tests.Visual; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder; @@ -26,7 +28,11 @@ namespace osu.Game.Tournament.Tests var teams = JsonConvert.DeserializeObject>(File.ReadAllText(@"teams.json")); var ladder = File.Exists(@"bracket.json") ? JsonConvert.DeserializeObject(File.ReadAllText(@"bracket.json")) : new LadderInfo(); - Child = manager = new LadderManager(ladder, teams); + Child = new OsuContextMenuContainer + { + RelativeSizeAxes = Axes.Both, + Child = manager = new LadderManager(ladder, teams) + }; } protected override void Dispose(bool isDisposing) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 8f33cdcbbc..131959892c 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -119,5 +119,14 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Pairing.Position = new Point((int)pos.X, (int)pos.Y); return true; } + + public void Remove() + { + if (Pairing.ProgressionSource.Value != null) + Pairing.ProgressionSource.Value.Progression.Value = null; + + Pairing.Progression.Value = null; + Expire(); + } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index fcfc87e5c3..e74545f073 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -15,6 +15,7 @@ using osu.Framework.Input.EventArgs; using osu.Framework.Input.States; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; using OpenTK; using OpenTK.Graphics; @@ -157,10 +158,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components scoreText.Font = AcronymText.Font = winner ? "Exo2.0-Bold" : "Exo2.0-Regular"; } - public MenuItem[] ContextMenuItems => new[] + public MenuItem[] ContextMenuItems => new MenuItem[] { - new MenuItem("Populate team", () => team.Value = manager.Teams.Random()), - new MenuItem("Join with", () => manager.JoinRequest(pairing)), + new OsuMenuItem("Populate team", MenuItemType.Standard, () => team.Value = manager.Teams.Random()), + new OsuMenuItem("Join with", MenuItemType.Standard, () => manager.RequestJoin(pairing)), + new OsuMenuItem("Remove", MenuItemType.Destructive, () => manager.Remove(pairing)), }; } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index 47a94b854e..c0ca25e220 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -28,11 +28,26 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [JsonIgnore] public readonly Bindable Progression = new Bindable(); + [JsonIgnore] + public readonly Bindable ProgressionSource = new Bindable(); + [JsonProperty] public Point Position; + private MatchPairing lastProgression; // todo: fix if we ever get LastValue inside Bindable<>. + public MatchPairing() { + Progression.ValueChanged += progression => + { + if (lastProgression != null) + lastProgression.ProgressionSource.Value = null; + + if (progression != null) + progression.ProgressionSource.Value = this; + + lastProgression = progression; + }; } public MatchPairing(TournamentTeam team1 = null, TournamentTeam team2 = null) diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index bf66447224..807b5f05a9 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -2,16 +2,18 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Lines; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.States; -using osu.Game.Graphics.Cursor; +using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; using SixLabors.Primitives; namespace osu.Game.Tournament.Screens.Ladder { - public class LadderManager : CompositeDrawable + public class LadderManager : CompositeDrawable, IHasContextMenu { public readonly List Teams; private readonly Container pairingsContainer; @@ -23,7 +25,7 @@ namespace osu.Game.Tournament.Screens.Ladder RelativeSizeAxes = Axes.Both; - InternalChild = new OsuContextMenuContainer + InternalChild = new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] @@ -56,15 +58,14 @@ namespace osu.Game.Tournament.Screens.Ladder private void addPairing(MatchPairing pairing) => pairingsContainer.Add(new DrawableMatchPairing(pairing)); - protected override bool OnClick(InputState state) + public MenuItem[] ContextMenuItems => new MenuItem[] { - addPairing(new MatchPairing + new OsuMenuItem("Create new match", MenuItemType.Highlighted, () => { - Position = new Point((int)state.Mouse.Position.X, (int)state.Mouse.Position.Y) - }); - - return true; - } + var pos = ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); + addPairing(new MatchPairing { Position = new Point((int)pos.X, (int)pos.Y) }); + }), + }; protected override void Update() { @@ -82,7 +83,7 @@ namespace osu.Game.Tournament.Screens.Ladder } } - public void JoinRequest(MatchPairing pairing) => AddInternal(new JoinRequestHandler(pairingsContainer, pairing)); + public void RequestJoin(MatchPairing pairing) => AddInternal(new JoinRequestHandler(pairingsContainer, pairing)); private class JoinRequestHandler : CompositeDrawable { @@ -126,7 +127,8 @@ namespace osu.Game.Tournament.Screens.Ladder if (found != null) { - Source.Progression.Value = found.Pairing; + if (found.Pairing != Source) + Source.Progression.Value = found.Pairing; Expire(); return true; } @@ -134,5 +136,7 @@ namespace osu.Game.Tournament.Screens.Ladder return false; } } + + public void Remove(MatchPairing pairing) => pairingsContainer.FirstOrDefault(p => p.Pairing == pairing)?.Remove(); } } From cddc7f74d4b41920c172df124fb9d2e5c0f3f218 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Sep 2018 22:07:03 +0900 Subject: [PATCH 0132/2854] Fix the possibility of a double-direction progression bind --- .../Screens/Ladder/Components/MatchPairing.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index c0ca25e220..994486ffdd 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -41,13 +41,22 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Progression.ValueChanged += progression => { if (lastProgression != null) + // clear the source from the previous progression. lastProgression.ProgressionSource.Value = null; if (progression != null) + // set the source on the new progression. progression.ProgressionSource.Value = this; lastProgression = progression; }; + + ProgressionSource.ValueChanged += source => + { + if (source != null) + // ennsure no two-way progressions. + Progression.Value = null; + }; } public MatchPairing(TournamentTeam team1 = null, TournamentTeam team2 = null) From 36e151719744f33efba1f22ab355884a20e9fe23 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Sep 2018 22:24:21 +0900 Subject: [PATCH 0133/2854] Display paths underneath matches --- osu.Game.Tournament/Screens/Ladder/LadderManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 807b5f05a9..e57f9b739b 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -30,8 +30,8 @@ namespace osu.Game.Tournament.Screens.Ladder RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - pairingsContainer = new Container { RelativeSizeAxes = Axes.Both }, paths = new Container { RelativeSizeAxes = Axes.Both }, + pairingsContainer = new Container { RelativeSizeAxes = Axes.Both }, } }; From 2f2dcec8c765d4c359e804fc0687ddb9671347b0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 Sep 2018 05:52:25 +0900 Subject: [PATCH 0134/2854] Complete editing support --- .../TestCaseLadderManager.cs | 2 +- .../TestCaseMatchPairings.cs | 2 +- .../Components/DrawableTournamentTeam.cs | 2 +- .../Components/TournamentTeam.cs | 6 +- .../Ladder/Components/DrawableMatchPairing.cs | 86 +++++++++- .../Ladder/Components/DrawableMatchTeam.cs | 25 ++- .../Ladder/Components/LadderSettings.cs | 147 ++++++++++++++++++ .../Screens/Ladder/Components/MatchPairing.cs | 26 +--- .../Ladder/Components/TournamentConditions.cs | 4 - .../Screens/Ladder/LadderManager.cs | 62 ++++++-- osu.Game/Overlays/Settings/SettingsItem.cs | 2 + 11 files changed, 306 insertions(+), 58 deletions(-) create mode 100644 osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index b53660c703..ac56803c90 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tournament.Tests private readonly LadderManager manager; [Cached] - private Bindable conditions = new Bindable(new TournamentConditions { BestOf = 9 }); + private Bindable conditions = new Bindable(new TournamentConditions()); public TestCaseLadderManager() { diff --git a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs index f1116b2db7..dc67807c64 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tournament.Tests }; [Cached] - private Bindable conditions = new Bindable(new TournamentConditions { BestOf = 9 }); + private Bindable conditions = new Bindable(new TournamentConditions()); public TestCaseMatchPairings() { diff --git a/osu.Game.Tournament/Components/DrawableTournamentTeam.cs b/osu.Game.Tournament/Components/DrawableTournamentTeam.cs index ec60d24c2c..016db57773 100644 --- a/osu.Game.Tournament/Components/DrawableTournamentTeam.cs +++ b/osu.Game.Tournament/Components/DrawableTournamentTeam.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tournament.Components AcronymText = new OsuSpriteText { - Text = team?.Acronym.ToUpperInvariant() ?? string.Empty, + Text = team?.Acronym?.ToUpperInvariant() ?? string.Empty, Font = @"Exo2.0-Regular" }; } diff --git a/osu.Game.Tournament/Components/TournamentTeam.cs b/osu.Game.Tournament/Components/TournamentTeam.cs index 14e874e9ef..cb6fc9fb92 100644 --- a/osu.Game.Tournament/Components/TournamentTeam.cs +++ b/osu.Game.Tournament/Components/TournamentTeam.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tournament.Components /// public string FlagName { - get { return flagName ?? Acronym.Substring(0, 2); } + get { return flagName ?? Acronym?.Substring(0, 2); } set { flagName = value; } } @@ -31,10 +31,12 @@ namespace osu.Game.Tournament.Components /// public string Acronym { - get { return acronym ?? FullName.Substring(0, 3); } + get { return acronym ?? FullName?.Substring(0, 3); } set { acronym = value; } } public List Players { get; set; } + + public override string ToString() => FullName ?? Acronym; } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 131959892c..f6771d70b4 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -5,9 +5,11 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input.EventArgs; using osu.Framework.Input.States; using OpenTK; +using OpenTK.Graphics; using OpenTK.Input; using SixLabors.Primitives; @@ -18,6 +20,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly MatchPairing Pairing; private readonly FillFlowContainer flow; private readonly Bindable conditions = new Bindable(); + private readonly Drawable selectionBox; + private Bindable globalSelection; + + [Resolved(CanBeNull = true)] + private LadderEditorInfo editorInfo { get; set; } = null; public DrawableMatchPairing(MatchPairing pairing) { @@ -29,8 +36,20 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Margin = new MarginPadding(5); - InternalChildren = new Drawable[] + InternalChildren = new[] { + selectionBox = new Container + { + CornerRadius = 5, + Masking = true, + Scale = new Vector2(1.05f), + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0, + Colour = Color4.YellowGreen, + Child = new Box { RelativeSizeAxes = Axes.Both } + }, flow = new FillFlowContainer { AutoSizeAxes = Axes.Both, @@ -38,13 +57,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Spacing = new Vector2(2) } }; - pairing.Team1.BindValueChanged(_ => updateTeams()); pairing.Team2.BindValueChanged(_ => updateTeams()); - pairing.Team1Score.BindValueChanged(_ => updateWinConditions()); pairing.Team2Score.BindValueChanged(_ => updateWinConditions()); - + pairing.BestOf.BindValueChanged(_ => updateWinConditions()); pairing.Completed.BindValueChanged(_ => updateProgression()); pairing.Progression.BindValueChanged(_ => updateProgression()); @@ -60,6 +77,27 @@ namespace osu.Game.Tournament.Screens.Ladder.Components this.conditions.BindTo(conditions); } + private bool selected; + + public bool Selected + { + get => selected; + + set + { + if (value == selected) return; + selected = value; + + if (selected) + { + selectionBox.Show(); + editorInfo.Selected.Value = Pairing; + } + else + selectionBox.Hide(); + } + } + private void updateProgression() { var progression = Pairing.Progression?.Value; @@ -76,13 +114,22 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { if (conditions.Value == null) return; - Pairing.Completed.Value = Pairing.Team1Score.Value + Pairing.Team2Score.Value >= conditions.Value.BestOf; + Pairing.Completed.Value = Pairing.Team1Score.Value + Pairing.Team2Score.Value >= Pairing.BestOf.Value; } protected override void LoadComplete() { base.LoadComplete(); updateTeams(); + + if (editorInfo != null) + { + globalSelection = editorInfo.Selected.GetBoundCopy(); + globalSelection.BindValueChanged(s => + { + if (s != Pairing) Selected = false; + }); + } } private void updateTeams() @@ -109,10 +156,34 @@ namespace osu.Game.Tournament.Screens.Ladder.Components protected override bool OnDragStart(InputState state) => true; + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (Selected && editorInfo.EditingEnabled && args.Key == Key.Delete) + { + Remove(); + return true; + } + + return base.OnKeyDown(state, args); + } + + protected override bool OnClick(InputState state) + { + if (!editorInfo.EditingEnabled) + return false; + + Selected = true; + return true; + } + protected override bool OnDrag(InputState state) { if (base.OnDrag(state)) return true; + if (!editorInfo.EditingEnabled) + return false; + + Selected = true; this.MoveToOffset(state.Mouse.Delta); var pos = Position; @@ -122,10 +193,9 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public void Remove() { - if (Pairing.ProgressionSource.Value != null) - Pairing.ProgressionSource.Value.Progression.Value = null; - + Selected = false; Pairing.Progression.Value = null; + Expire(); } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index e74545f073..a517c5f475 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -25,7 +25,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { public class DrawableMatchTeam : DrawableTournamentTeam, IHasContextMenu { - private readonly Bindable team; private readonly MatchPairing pairing; private OsuSpriteText scoreText; private Box background; @@ -39,10 +38,12 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private readonly Func isWinner; private LadderManager manager; + [Resolved(CanBeNull = true)] + private LadderEditorInfo editorInfo { get; set; } = null; + public DrawableMatchTeam(Bindable team, MatchPairing pairing) : base(team) { - this.team = team.GetBoundCopy(); this.pairing = pairing; Size = new Vector2(150, 40); @@ -127,7 +128,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - if (Team == null) return false; + if (Team == null || editorInfo.EditingEnabled) return false; if (args.Button == MouseButton.Left) { @@ -158,12 +159,20 @@ namespace osu.Game.Tournament.Screens.Ladder.Components scoreText.Font = AcronymText.Font = winner ? "Exo2.0-Bold" : "Exo2.0-Regular"; } - public MenuItem[] ContextMenuItems => new MenuItem[] + public MenuItem[] ContextMenuItems { - new OsuMenuItem("Populate team", MenuItemType.Standard, () => team.Value = manager.Teams.Random()), - new OsuMenuItem("Join with", MenuItemType.Standard, () => manager.RequestJoin(pairing)), - new OsuMenuItem("Remove", MenuItemType.Destructive, () => manager.Remove(pairing)), - }; + get + { + if (!editorInfo.EditingEnabled) + return new MenuItem[0]; + + return new MenuItem[] + { + new OsuMenuItem("Join with", MenuItemType.Standard, () => manager.RequestJoin(pairing)), + new OsuMenuItem("Remove", MenuItemType.Destructive, () => manager.Remove(pairing)), + }; + } + } } internal static class Extensions diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs new file mode 100644 index 0000000000..9ef2ccfa0b --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs @@ -0,0 +1,147 @@ +// Copyright (c) 2007-2018 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.Settings; +using osu.Game.Screens.Play.PlayerSettings; +using osu.Game.Tournament.Components; + +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + public class LadderEditorSettings : PlayerSettingsGroup + { + private const int padding = 10; + + protected override string Title => @"ladder"; + + private PlayerSliderBar sliderBestOf; + + private SettingsDropdown dropdownTeam1; + private SettingsDropdown dropdownTeam2; + + [Resolved] + private LadderEditorInfo editorInfo { get; set; } = null; + + [BackgroundDependencyLoader] + private void load() + { + var teamEntries = editorInfo.Teams.Select(t => new KeyValuePair(t.ToString(), t)).Prepend(new KeyValuePair("Empty", new TournamentTeam())); + + Children = new Drawable[] + { + new PlayerCheckbox + { + Bindable = editorInfo.EditingEnabled, + LabelText = "Enable editing" + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = padding }, + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = "Team1", + }, + }, + }, + dropdownTeam1 = new SettingsDropdown + { + Items = teamEntries, + Bindable = new Bindable + { + Value = teamEntries.First().Value, + Default = teamEntries.First().Value + } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = padding }, + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = "Team2", + }, + }, + }, + dropdownTeam2 = new SettingsDropdown + { + Items = teamEntries, + Bindable = new Bindable + { + Value = teamEntries.First().Value, + Default = teamEntries.First().Value + } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = padding }, + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = "Best of", + }, + }, + }, + sliderBestOf = new PlayerSliderBar + { + Bindable = new BindableDouble + { + Default = 5, + Value = 5, + MinValue = 1, + MaxValue = 20, + Precision = 1, + }, + } + }; + + editorInfo.Selected.ValueChanged += selection => + { + dropdownTeam1.Bindable.Value = dropdownTeam1.Items.FirstOrDefault(i => i.Value.Acronym == selection?.Team1.Value?.Acronym).Value; + dropdownTeam2.Bindable.Value = dropdownTeam1.Items.FirstOrDefault(i => i.Value.Acronym == selection?.Team2.Value?.Acronym).Value; + sliderBestOf.Bindable.Value = selection?.BestOf ?? sliderBestOf.Bindable.Default; + }; + + dropdownTeam1.Bindable.ValueChanged += val => + { + if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.Team1.Value = val.Acronym == null ? null : val; + }; + + dropdownTeam2.Bindable.ValueChanged += val => + { + if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.Team2.Value = val.Acronym == null ? null : val; + }; + + sliderBestOf.Bindable.ValueChanged += val => + { + if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.BestOf.Value = (int)val; + }; + + editorInfo.EditingEnabled.ValueChanged += enabled => + { + if (!enabled) editorInfo.Selected.Value = null; + }; + } + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index 994486ffdd..772a196f37 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -25,38 +25,16 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly Bindable Completed = new Bindable(); - [JsonIgnore] - public readonly Bindable Progression = new Bindable(); + public readonly BindableInt BestOf = new BindableInt(5); [JsonIgnore] - public readonly Bindable ProgressionSource = new Bindable(); + public readonly Bindable Progression = new Bindable(); [JsonProperty] public Point Position; - private MatchPairing lastProgression; // todo: fix if we ever get LastValue inside Bindable<>. - public MatchPairing() { - Progression.ValueChanged += progression => - { - if (lastProgression != null) - // clear the source from the previous progression. - lastProgression.ProgressionSource.Value = null; - - if (progression != null) - // set the source on the new progression. - progression.ProgressionSource.Value = this; - - lastProgression = progression; - }; - - ProgressionSource.ValueChanged += source => - { - if (source != null) - // ennsure no two-way progressions. - Progression.Value = null; - }; } public MatchPairing(TournamentTeam team1 = null, TournamentTeam team2 = null) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs index fd0e8cea87..045149945c 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs @@ -5,9 +5,5 @@ namespace osu.Game.Tournament.Screens.Ladder.Components /// public class TournamentConditions { - /// - /// How many matches before a winner is decided. - /// - public int BestOf; } } diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index e57f9b739b..6c14a4225f 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -1,5 +1,8 @@ +using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; @@ -13,15 +16,25 @@ using SixLabors.Primitives; namespace osu.Game.Tournament.Screens.Ladder { + public class LadderEditorInfo + { + public readonly BindableBool EditingEnabled = new BindableBool(); + public List Teams = new List(); + public readonly Bindable Selected = new Bindable(); + } + public class LadderManager : CompositeDrawable, IHasContextMenu { public readonly List Teams; private readonly Container pairingsContainer; private readonly Container paths; + [Cached] + private readonly LadderEditorInfo editorInfo = new LadderEditorInfo(); + public LadderManager(LadderInfo info, List teams) { - Teams = teams; + editorInfo.Teams = Teams = teams; RelativeSizeAxes = Axes.Both; @@ -32,11 +45,25 @@ namespace osu.Game.Tournament.Screens.Ladder { paths = new Container { RelativeSizeAxes = Axes.Both }, pairingsContainer = new Container { RelativeSizeAxes = Axes.Both }, + new LadderEditorSettings + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Margin = new MarginPadding(5) + } } }; foreach (var pair in info.Progressions) - info.Pairings.Single(p => p.ID == pair.Item1).Progression.Value = info.Pairings.Single(p => p.ID == pair.Item2); + { + var src = info.Pairings.FirstOrDefault(p => p.ID == pair.Item1); + var dest = info.Pairings.FirstOrDefault(p => p.ID == pair.Item2); + + if (src == null) throw new InvalidOperationException(); + + if (dest != null) + src.Progression.Value = dest; + } foreach (var pairing in info.Pairings) addPairing(pairing); @@ -58,14 +85,23 @@ namespace osu.Game.Tournament.Screens.Ladder private void addPairing(MatchPairing pairing) => pairingsContainer.Add(new DrawableMatchPairing(pairing)); - public MenuItem[] ContextMenuItems => new MenuItem[] + public MenuItem[] ContextMenuItems { - new OsuMenuItem("Create new match", MenuItemType.Highlighted, () => + get { - var pos = ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); - addPairing(new MatchPairing { Position = new Point((int)pos.X, (int)pos.Y) }); - }), - }; + if (!editorInfo.EditingEnabled) + return new MenuItem[0]; + + return new MenuItem[] + { + new OsuMenuItem("Create new match", MenuItemType.Highlighted, () => + { + var pos = ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); + addPairing(new MatchPairing { Position = new Point((int)pos.X, (int)pos.Y) }); + }), + }; + } + } protected override void Update() { @@ -79,7 +115,15 @@ namespace osu.Game.Tournament.Screens.Ladder pairing.Pairing.ID = id++; if (pairing.Pairing.Progression.Value != null) - paths.Add(new ProgressionPath(pairing, pairingsContainer.Single(p => p.Pairing == pairing.Pairing.Progression.Value))); + { + var dest = pairingsContainer.FirstOrDefault(p => p.Pairing == pairing.Pairing.Progression.Value); + + if (dest == null) + // clean up outdated progressions. + pairing.Pairing.Progression.Value = null; + else + paths.Add(new ProgressionPath(pairing, dest)); + } } } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 0f8d3aa2ac..88c3971ab2 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -63,8 +63,10 @@ namespace osu.Game.Overlays.Settings set { + controlWithCurrent?.Current.UnbindBindings(); bindable = value; controlWithCurrent?.Current.BindTo(bindable); + if (ShowsDefaultIndicator) { restoreDefaultButton.Bindable = bindable.GetBoundCopy(); From a113cf41183d2bead8c5a95a5f685d7ac8ffce4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 Sep 2018 06:36:45 +0900 Subject: [PATCH 0135/2854] Start IDs at 1 --- osu.Game.Tournament/Screens/Ladder/LadderManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 6c14a4225f..235ac4a86a 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -109,7 +109,7 @@ namespace osu.Game.Tournament.Screens.Ladder paths.Clear(); - int id = 0; + int id = 1; foreach (var pairing in pairingsContainer.OrderBy(d => d.Y).ThenBy(d => d.X)) { pairing.Pairing.ID = id++; From 3cc75bac3443ac43cab7b62868c5397a2081784d Mon Sep 17 00:00:00 2001 From: Kyle Chang Date: Fri, 21 Sep 2018 20:26:01 -0400 Subject: [PATCH 0136/2854] Adjust easings --- osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs index 348093ec9b..102275043c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden) }; private const int rotate_offset = 360; - private const float rotate_starting_width = 2.5f; + private const float rotate_starting_width = 2.0f; public void ApplyToDrawableHitObjects(IEnumerable drawables) @@ -63,9 +63,9 @@ namespace osu.Game.Rulesets.Osu.Mods circle .RotateTo(origRotate+rotate_offset) - .RotateTo(origRotate, moveDuration) + .RotateTo(origRotate, moveDuration, Easing.InOutSine) .ScaleTo(origScale * new Vector2(rotate_starting_width, 0)) - .ScaleTo(origScale, moveDuration, Easing.InQuad) + .ScaleTo(origScale, moveDuration, Easing.InOutSine) .FadeTo(1); } @@ -88,7 +88,7 @@ namespace osu.Game.Rulesets.Osu.Mods slider .ScaleTo(0) - .ScaleTo(origScale, moveDuration) + .ScaleTo(origScale, moveDuration, Easing.InOutSine) .FadeTo(1); } From 8d773fec97e681cf2895d7324f13a5f4ce0cb447 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Sep 2018 02:16:59 +0900 Subject: [PATCH 0137/2854] Fix incorrect best-of- scoring method --- .../Screens/Ladder/Components/DrawableMatchPairing.cs | 4 +++- osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index f6771d70b4..7f8956c24d 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -114,7 +114,9 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { if (conditions.Value == null) return; - Pairing.Completed.Value = Pairing.Team1Score.Value + Pairing.Team2Score.Value >= Pairing.BestOf.Value; + var instaWinAmount = Pairing.BestOf.Value / 2; + + Pairing.Completed.Value = Pairing.Team1Score + Pairing.Team2Score >= Pairing.BestOf || Pairing.Team1Score > instaWinAmount || Pairing.Team2Score > instaWinAmount; } protected override void LoadComplete() diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index 772a196f37..c47e3ea440 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly Bindable Completed = new Bindable(); - public readonly BindableInt BestOf = new BindableInt(5); + public readonly BindableInt BestOf = new BindableInt(11); [JsonIgnore] public readonly Bindable Progression = new Bindable(); From a3a2a149ca87464d1163f5396c570df69051d193 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Sep 2018 02:17:07 +0900 Subject: [PATCH 0138/2854] Use textbox rather than dropdowns --- .../Ladder/Components/LadderSettings.cs | 50 +++++++------------ 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs index 9ef2ccfa0b..a2275784a0 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs @@ -1,16 +1,14 @@ // Copyright (c) 2007-2018 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.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Overlays.Settings; +using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Play.PlayerSettings; -using osu.Game.Tournament.Components; namespace osu.Game.Tournament.Screens.Ladder.Components { @@ -22,8 +20,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private PlayerSliderBar sliderBestOf; - private SettingsDropdown dropdownTeam1; - private SettingsDropdown dropdownTeam2; + private OsuTextBox textboxTeam1; + private OsuTextBox textboxTeam2; [Resolved] private LadderEditorInfo editorInfo { get; set; } = null; @@ -31,7 +29,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [BackgroundDependencyLoader] private void load() { - var teamEntries = editorInfo.Teams.Select(t => new KeyValuePair(t.ToString(), t)).Prepend(new KeyValuePair("Empty", new TournamentTeam())); + var teamEntries = editorInfo.Teams; Children = new Drawable[] { @@ -55,15 +53,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components }, }, }, - dropdownTeam1 = new SettingsDropdown - { - Items = teamEntries, - Bindable = new Bindable - { - Value = teamEntries.First().Value, - Default = teamEntries.First().Value - } - }, + textboxTeam1 = new OsuTextBox { RelativeSizeAxes = Axes.X, Height = 20 }, new Container { RelativeSizeAxes = Axes.X, @@ -79,15 +69,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components }, }, }, - dropdownTeam2 = new SettingsDropdown - { - Items = teamEntries, - Bindable = new Bindable - { - Value = teamEntries.First().Value, - Default = teamEntries.First().Value - } - }, + textboxTeam2 = new OsuTextBox { RelativeSizeAxes = Axes.X, Height = 20 }, new Container { RelativeSizeAxes = Axes.X, @@ -107,10 +89,10 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { Bindable = new BindableDouble { - Default = 5, - Value = 5, + Default = 11, + Value = 11, MinValue = 1, - MaxValue = 20, + MaxValue = 21, Precision = 1, }, } @@ -118,19 +100,21 @@ namespace osu.Game.Tournament.Screens.Ladder.Components editorInfo.Selected.ValueChanged += selection => { - dropdownTeam1.Bindable.Value = dropdownTeam1.Items.FirstOrDefault(i => i.Value.Acronym == selection?.Team1.Value?.Acronym).Value; - dropdownTeam2.Bindable.Value = dropdownTeam1.Items.FirstOrDefault(i => i.Value.Acronym == selection?.Team2.Value?.Acronym).Value; + textboxTeam1.Text = selection?.Team1.Value?.Acronym; + textboxTeam2.Text = selection?.Team2.Value?.Acronym; sliderBestOf.Bindable.Value = selection?.BestOf ?? sliderBestOf.Bindable.Default; }; - dropdownTeam1.Bindable.ValueChanged += val => + textboxTeam1.OnCommit = (val, newText) => { - if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.Team1.Value = val.Acronym == null ? null : val; + if (newText && editorInfo.Selected.Value != null) + editorInfo.Selected.Value.Team1.Value = teamEntries.FirstOrDefault(t => t.Acronym == val.Text); }; - dropdownTeam2.Bindable.ValueChanged += val => + textboxTeam2.OnCommit = (val, newText) => { - if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.Team2.Value = val.Acronym == null ? null : val; + if (newText && editorInfo.Selected.Value != null) + editorInfo.Selected.Value.Team2.Value = teamEntries.FirstOrDefault(t => t.Acronym == val.Text); }; sliderBestOf.Bindable.ValueChanged += val => From 68cef7646848fb9e3bacae79bf72e7fd1ff11a8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Sep 2018 05:19:03 +0900 Subject: [PATCH 0139/2854] Add grouping and move BestOf out of pairing --- .../Ladder/Components/DrawableMatchPairing.cs | 8 +-- .../Screens/Ladder/Components/LadderInfo.cs | 4 ++ .../Ladder/Components/LadderSettings.cs | 64 +++++++++---------- .../Screens/Ladder/Components/MatchPairing.cs | 2 +- .../Ladder/Components/TournamentGrouping.cs | 15 +++++ 5 files changed, 54 insertions(+), 39 deletions(-) create mode 100644 osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 7f8956c24d..a7ebbfa8b3 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -61,7 +61,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components pairing.Team2.BindValueChanged(_ => updateTeams()); pairing.Team1Score.BindValueChanged(_ => updateWinConditions()); pairing.Team2Score.BindValueChanged(_ => updateWinConditions()); - pairing.BestOf.BindValueChanged(_ => updateWinConditions()); + pairing.Grouping.BindValueChanged(_ => updateWinConditions()); pairing.Completed.BindValueChanged(_ => updateProgression()); pairing.Progression.BindValueChanged(_ => updateProgression()); @@ -112,11 +112,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private void updateWinConditions() { - if (conditions.Value == null) return; + if (conditions.Value == null || Pairing.Grouping.Value == null) return; - var instaWinAmount = Pairing.BestOf.Value / 2; + var instaWinAmount = Pairing.Grouping.Value.BestOf / 2; - Pairing.Completed.Value = Pairing.Team1Score + Pairing.Team2Score >= Pairing.BestOf || Pairing.Team1Score > instaWinAmount || Pairing.Team2Score > instaWinAmount; + Pairing.Completed.Value = Pairing.Grouping.Value.BestOf > 0 && (Pairing.Team1Score + Pairing.Team2Score >= Pairing.Grouping.Value.BestOf || Pairing.Team1Score > instaWinAmount || Pairing.Team2Score > instaWinAmount); } protected override void LoadComplete() diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs index 0860966502..e1da676f22 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using System.Collections.Generic; namespace osu.Game.Tournament.Screens.Ladder.Components @@ -6,5 +9,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { public List Pairings = new List(); public List<(int, int)> Progressions = new List<(int, int)>(); + public List Groupings = new List(); } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs index a2275784a0..7532cee0f0 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs @@ -3,7 +3,6 @@ using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -18,8 +17,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components protected override string Title => @"ladder"; - private PlayerSliderBar sliderBestOf; - private OsuTextBox textboxTeam1; private OsuTextBox textboxTeam2; @@ -70,39 +67,38 @@ namespace osu.Game.Tournament.Screens.Ladder.Components }, }, textboxTeam2 = new OsuTextBox { RelativeSizeAxes = Axes.X, Height = 20 }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = padding }, - Children = new Drawable[] - { - new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Text = "Best of", - }, - }, - }, - sliderBestOf = new PlayerSliderBar - { - Bindable = new BindableDouble - { - Default = 11, - Value = 11, - MinValue = 1, - MaxValue = 21, - Precision = 1, - }, - } + // new Container + // { + // RelativeSizeAxes = Axes.X, + // AutoSizeAxes = Axes.Y, + // Padding = new MarginPadding { Horizontal = padding }, + // Children = new Drawable[] + // { + // new OsuSpriteText + // { + // Anchor = Anchor.CentreLeft, + // Origin = Anchor.CentreLeft, + // Text = "Best of", + // }, + // }, + // }, + // sliderBestOf = new PlayerSliderBar + // { + // Bindable = new BindableDouble + // { + // Default = 11, + // Value = 11, + // MinValue = 1, + // MaxValue = 21, + // Precision = 1, + // }, + // } }; editorInfo.Selected.ValueChanged += selection => { textboxTeam1.Text = selection?.Team1.Value?.Acronym; textboxTeam2.Text = selection?.Team2.Value?.Acronym; - sliderBestOf.Bindable.Value = selection?.BestOf ?? sliderBestOf.Bindable.Default; }; textboxTeam1.OnCommit = (val, newText) => @@ -117,10 +113,10 @@ namespace osu.Game.Tournament.Screens.Ladder.Components editorInfo.Selected.Value.Team2.Value = teamEntries.FirstOrDefault(t => t.Acronym == val.Text); }; - sliderBestOf.Bindable.ValueChanged += val => - { - if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.BestOf.Value = (int)val; - }; + // sliderBestOf.Bindable.ValueChanged += val => + // { + // if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.BestOf.Value = (int)val; + // }; editorInfo.EditingEnabled.ValueChanged += enabled => { diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index c47e3ea440..0bdfb5c300 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly Bindable Completed = new Bindable(); - public readonly BindableInt BestOf = new BindableInt(11); + public readonly Bindable Grouping = new Bindable(); [JsonIgnore] public readonly Bindable Progression = new Bindable(); diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs new file mode 100644 index 0000000000..0178885307 --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs @@ -0,0 +1,15 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + public class TournamentGrouping + { + public int ID; + + public string Name; + public string Description; + + public int BestOf; + } +} From ad63ff2d069ff544061ac59aba240c0ad273ee16 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Sep 2018 16:34:46 +0900 Subject: [PATCH 0140/2854] Add scrollability --- .../Screens/Ladder/LadderManager.cs | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 235ac4a86a..2cf7e00314 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -12,6 +12,7 @@ using osu.Framework.Input.States; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; +using OpenTK; using SixLabors.Primitives; namespace osu.Game.Tournament.Screens.Ladder @@ -43,8 +44,15 @@ namespace osu.Game.Tournament.Screens.Ladder RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - paths = new Container { RelativeSizeAxes = Axes.Both }, - pairingsContainer = new Container { RelativeSizeAxes = Axes.Both }, + new ScrollableContainer + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + paths = new Container { RelativeSizeAxes = Axes.Both }, + pairingsContainer = new Container { RelativeSizeAxes = Axes.Both }, + } + }, new LadderEditorSettings { Anchor = Anchor.TopRight, @@ -183,4 +191,17 @@ namespace osu.Game.Tournament.Screens.Ladder public void Remove(MatchPairing pairing) => pairingsContainer.FirstOrDefault(p => p.Pairing == pairing)?.Remove(); } + + public class ScrollableContainer : Container + { + protected override bool OnDragStart(InputState state) => true; + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + + protected override bool OnDrag(InputState state) + { + Position += state.Mouse.Delta; + return base.OnDrag(state); + } + } } From 1644775f7be43dc12301be7cf28a68045a152950 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Sep 2018 23:30:37 +0900 Subject: [PATCH 0141/2854] Add grouping configuration --- .../Ladder/Components/LadderSettings.cs | 18 ++++++++++++++++++ .../Screens/Ladder/Components/MatchPairing.cs | 1 + .../Ladder/Components/TournamentGrouping.cs | 6 ++++-- .../Screens/Ladder/LadderManager.cs | 12 +++++++++++- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs index 7532cee0f0..379bb36c09 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs @@ -1,12 +1,15 @@ // Copyright (c) 2007-2018 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.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; using osu.Game.Screens.Play.PlayerSettings; namespace osu.Game.Tournament.Screens.Ladder.Components @@ -19,6 +22,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private OsuTextBox textboxTeam1; private OsuTextBox textboxTeam2; + private SettingsDropdown groupingDropdown; [Resolved] private LadderEditorInfo editorInfo { get; set; } = null; @@ -28,6 +32,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { var teamEntries = editorInfo.Teams; + var groupingOptions = editorInfo.Groupings.Select(g => new KeyValuePair(g.Name, g)).Prepend(new KeyValuePair("None", new TournamentGrouping())); + Children = new Drawable[] { new PlayerCheckbox @@ -67,6 +73,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components }, }, textboxTeam2 = new OsuTextBox { RelativeSizeAxes = Axes.X, Height = 20 }, + groupingDropdown = new SettingsDropdown + { + Bindable = new Bindable(), + Items = groupingOptions + }, // new Container // { // RelativeSizeAxes = Axes.X, @@ -99,6 +110,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { textboxTeam1.Text = selection?.Team1.Value?.Acronym; textboxTeam2.Text = selection?.Team2.Value?.Acronym; + groupingDropdown.Bindable.Value = selection?.Grouping.Value ?? groupingOptions.First().Value; }; textboxTeam1.OnCommit = (val, newText) => @@ -113,6 +125,12 @@ namespace osu.Game.Tournament.Screens.Ladder.Components editorInfo.Selected.Value.Team2.Value = teamEntries.FirstOrDefault(t => t.Acronym == val.Text); }; + groupingDropdown.Bindable.ValueChanged += grouping => + { + if (editorInfo.Selected.Value != null) + editorInfo.Selected.Value.Grouping.Value = grouping; + }; + // sliderBestOf.Bindable.ValueChanged += val => // { // if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.BestOf.Value = (int)val; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index 0bdfb5c300..a97354a1c4 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -25,6 +25,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly Bindable Completed = new Bindable(); + [JsonIgnore] public readonly Bindable Grouping = new Bindable(); [JsonIgnore] diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs index 0178885307..675bf5fc4f 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs @@ -1,15 +1,17 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; + namespace osu.Game.Tournament.Screens.Ladder.Components { public class TournamentGrouping { - public int ID; - public string Name; public string Description; public int BestOf; + + public List Pairings = new List(); } } diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 2cf7e00314..8939e72be2 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -21,6 +21,7 @@ namespace osu.Game.Tournament.Screens.Ladder { public readonly BindableBool EditingEnabled = new BindableBool(); public List Teams = new List(); + public List Groupings = new List(); public readonly Bindable Selected = new Bindable(); } @@ -36,6 +37,7 @@ namespace osu.Game.Tournament.Screens.Ladder public LadderManager(LadderInfo info, List teams) { editorInfo.Teams = Teams = teams; + editorInfo.Groupings = info.Groupings; RelativeSizeAxes = Axes.Both; @@ -75,19 +77,27 @@ namespace osu.Game.Tournament.Screens.Ladder foreach (var pairing in info.Pairings) addPairing(pairing); + + foreach (var group in info.Groupings) + foreach (var id in group.Pairings) + info.Pairings.Single(p => p.ID == id).Grouping.Value = group; } public LadderInfo CreateInfo() { var pairings = pairingsContainer.Select(p => p.Pairing).ToList(); + foreach (var g in editorInfo.Groupings) + g.Pairings = pairings.Where(p => p.Grouping.Value == g).Select(p => p.ID).ToList(); + return new LadderInfo { Pairings = pairings, Progressions = pairings .Where(p => p.Progression.Value != null) .Select(p => (p.ID, p.Progression.Value.ID)) - .ToList() + .ToList(), + Groupings = editorInfo.Groupings }; } From c7c55f21392d029cb2f5ff851e1b08e495940af1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 00:57:44 +0900 Subject: [PATCH 0142/2854] Add headings --- .../Ladder/DrawableTournamentGrouping.cs | 39 ++++++++++++++++ .../Screens/Ladder/LadderManager.cs | 46 +++++++++++++------ .../Screens/Ladder/ScrollableContainer.cs | 22 +++++++++ 3 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 osu.Game.Tournament/Screens/Ladder/DrawableTournamentGrouping.cs create mode 100644 osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs diff --git a/osu.Game.Tournament/Screens/Ladder/DrawableTournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/DrawableTournamentGrouping.cs new file mode 100644 index 0000000000..28e1183b27 --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/DrawableTournamentGrouping.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Tournament.Screens.Ladder.Components; + +namespace osu.Game.Tournament.Screens.Ladder +{ + public class DrawableTournamentGrouping : CompositeDrawable + { + public DrawableTournamentGrouping(TournamentGrouping grouping) + { + AutoSizeAxes = Axes.Both; + InternalChild = new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = grouping.Description.ToUpper(), + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre + }, + new OsuSpriteText + { + Text = grouping.Name.ToUpper(), + Font = "Exo2.0-Bold", + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre + }, + } + }; + } + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 8939e72be2..95b2a0a8d1 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using osu.Framework.Allocation; +using osu.Framework.Caching; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -12,7 +14,6 @@ using osu.Framework.Input.States; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; -using OpenTK; using SixLabors.Primitives; namespace osu.Game.Tournament.Screens.Ladder @@ -30,6 +31,7 @@ namespace osu.Game.Tournament.Screens.Ladder public readonly List Teams; private readonly Container pairingsContainer; private readonly Container paths; + private readonly Container headings; [Cached] private readonly LadderEditorInfo editorInfo = new LadderEditorInfo(); @@ -52,6 +54,7 @@ namespace osu.Game.Tournament.Screens.Ladder Children = new Drawable[] { paths = new Container { RelativeSizeAxes = Axes.Both }, + headings = new Container() { RelativeSizeAxes = Axes.Both }, pairingsContainer = new Container { RelativeSizeAxes = Axes.Both }, } }, @@ -81,6 +84,9 @@ namespace osu.Game.Tournament.Screens.Ladder foreach (var group in info.Groupings) foreach (var id in group.Pairings) info.Pairings.Single(p => p.ID == id).Grouping.Value = group; + + // todo: fix this + Scheduler.AddDelayed(() => layout.Invalidate(), 100, true); } public LadderInfo CreateInfo() @@ -121,11 +127,20 @@ namespace osu.Game.Tournament.Screens.Ladder } } + private Cached layout = new Cached(); + protected override void Update() { base.Update(); + if (!layout.IsValid) + updateLayout(); + } + + private void updateLayout() + { paths.Clear(); + headings.Clear(); int id = 1; foreach (var pairing in pairingsContainer.OrderBy(d => d.Y).ThenBy(d => d.X)) @@ -143,6 +158,22 @@ namespace osu.Game.Tournament.Screens.Ladder paths.Add(new ProgressionPath(pairing, dest)); } } + + foreach (var group in editorInfo.Groupings) + { + var topPairing = pairingsContainer.Where(p => p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); + + if (topPairing == null) continue; + + headings.Add(new DrawableTournamentGrouping(group) + { + Position = headings.ToLocalSpace((topPairing.ScreenSpaceDrawQuad.TopLeft + topPairing.ScreenSpaceDrawQuad.TopRight) / 2), + Margin = new MarginPadding { Bottom = 10 }, + Origin = Anchor.BottomCentre, + }); + } + + layout.Validate(); } public void RequestJoin(MatchPairing pairing) => AddInternal(new JoinRequestHandler(pairingsContainer, pairing)); @@ -201,17 +232,4 @@ namespace osu.Game.Tournament.Screens.Ladder public void Remove(MatchPairing pairing) => pairingsContainer.FirstOrDefault(p => p.Pairing == pairing)?.Remove(); } - - public class ScrollableContainer : Container - { - protected override bool OnDragStart(InputState state) => true; - - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; - - protected override bool OnDrag(InputState state) - { - Position += state.Mouse.Delta; - return base.OnDrag(state); - } - } } diff --git a/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs b/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs new file mode 100644 index 0000000000..bb4f2e58a9 --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs @@ -0,0 +1,22 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.States; +using OpenTK; + +namespace osu.Game.Tournament.Screens.Ladder +{ + public class ScrollableContainer : Container + { + protected override bool OnDragStart(InputState state) => true; + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + + protected override bool OnDrag(InputState state) + { + Position += state.Mouse.Delta; + return base.OnDrag(state); + } + } +} From d2ce974ba8ade2586ee8ae343e2dab2cfd09a77d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 02:31:48 +0900 Subject: [PATCH 0143/2854] Add loser progressions --- .../Ladder/Components/DrawableMatchPairing.cs | 19 +++++-- .../Ladder/Components/DrawableMatchTeam.cs | 7 ++- .../Screens/Ladder/Components/LadderInfo.cs | 2 +- .../Ladder/Components/LadderSettings.cs | 16 +++++- .../Screens/Ladder/Components/MatchPairing.cs | 8 +++ .../Components/TournamentProgression.cs | 20 +++++++ .../Ladder/DrawableTournamentGrouping.cs | 4 +- .../Screens/Ladder/LadderManager.cs | 54 ++++++++++++++----- 8 files changed, 108 insertions(+), 22 deletions(-) create mode 100644 osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index a7ebbfa8b3..285baad872 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -64,6 +64,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components pairing.Grouping.BindValueChanged(_ => updateWinConditions()); pairing.Completed.BindValueChanged(_ => updateProgression()); pairing.Progression.BindValueChanged(_ => updateProgression()); + pairing.LosersProgression.BindValueChanged(_ => updateProgression()); updateTeams(); } @@ -102,12 +103,21 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { var progression = Pairing.Progression?.Value; - if (progression == null) return; + if (progression != null) + { + bool progressionAbove = progression.ID < Pairing.ID; - bool progressionAbove = progression.ID < Pairing.ID; + var destinationForWinner = progressionAbove || progression.Team1.Value != null && progression.Team1.Value != Pairing.Winner ? progression.Team2 : progression.Team1; + destinationForWinner.Value = Pairing.Winner; + } - var destinationForWinner = progressionAbove ? progression.Team2 : progression.Team1; - destinationForWinner.Value = Pairing.Winner; + if ((progression = Pairing.LosersProgression?.Value) != null) + { + bool progressionAbove = progression.ID < Pairing.ID; + + var destinationForLoser = progressionAbove || progression.Team1.Value != null && progression.Team1.Value != Pairing.Winner ? progression.Team2 : progression.Team1; + destinationForLoser.Value = Pairing.Loser; + } } private void updateWinConditions() @@ -197,6 +207,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { Selected = false; Pairing.Progression.Value = null; + Pairing.LosersProgression.Value = null; Expire(); } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index a517c5f475..c66d3fff55 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -141,6 +141,10 @@ namespace osu.Game.Tournament.Screens.Ladder.Components } else { + if (pairing.Progression.Value.Completed) + // don't allow changing scores if the match has a progression. can cause large data loss + return false; + if (score.Value > 0) score.Value--; else @@ -168,7 +172,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components return new MenuItem[] { - new OsuMenuItem("Join with", MenuItemType.Standard, () => manager.RequestJoin(pairing)), + new OsuMenuItem("Join with", MenuItemType.Standard, () => manager.RequestJoin(pairing, false)), + new OsuMenuItem("Join with (loser)", MenuItemType.Standard, () => manager.RequestJoin(pairing, true)), new OsuMenuItem("Remove", MenuItemType.Destructive, () => manager.Remove(pairing)), }; } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs index e1da676f22..ba64b974d9 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs @@ -8,7 +8,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public class LadderInfo { public List Pairings = new List(); - public List<(int, int)> Progressions = new List<(int, int)>(); + public List Progressions = new List(); public List Groupings = new List(); } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs index 379bb36c09..d8e37a5c80 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs @@ -23,6 +23,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private OsuTextBox textboxTeam1; private OsuTextBox textboxTeam2; private SettingsDropdown groupingDropdown; + private PlayerCheckbox losersCheckbox; [Resolved] private LadderEditorInfo editorInfo { get; set; } = null; @@ -32,7 +33,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { var teamEntries = editorInfo.Teams; - var groupingOptions = editorInfo.Groupings.Select(g => new KeyValuePair(g.Name, g)).Prepend(new KeyValuePair("None", new TournamentGrouping())); + var groupingOptions = editorInfo.Groupings.Select(g => new KeyValuePair(g.Name, g)) + .Prepend(new KeyValuePair("None", new TournamentGrouping())); Children = new Drawable[] { @@ -78,6 +80,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Bindable = new Bindable(), Items = groupingOptions }, + losersCheckbox = new PlayerCheckbox + { + LabelText = "Losers Bracket", + Bindable = new Bindable() + } // new Container // { // RelativeSizeAxes = Axes.X, @@ -111,6 +118,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components textboxTeam1.Text = selection?.Team1.Value?.Acronym; textboxTeam2.Text = selection?.Team2.Value?.Acronym; groupingDropdown.Bindable.Value = selection?.Grouping.Value ?? groupingOptions.First().Value; + losersCheckbox.Current.Value = selection?.Losers.Value ?? false; }; textboxTeam1.OnCommit = (val, newText) => @@ -131,6 +139,12 @@ namespace osu.Game.Tournament.Screens.Ladder.Components editorInfo.Selected.Value.Grouping.Value = grouping; }; + losersCheckbox.Current.ValueChanged += losers => + { + if (editorInfo.Selected.Value != null) + editorInfo.Selected.Value.Losers.Value = losers; + }; + // sliderBestOf.Bindable.ValueChanged += val => // { // if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.BestOf.Value = (int)val; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index a97354a1c4..49a477701f 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -25,12 +25,17 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly Bindable Completed = new Bindable(); + public readonly Bindable Losers = new Bindable(); + [JsonIgnore] public readonly Bindable Grouping = new Bindable(); [JsonIgnore] public readonly Bindable Progression = new Bindable(); + [JsonIgnore] + public readonly Bindable LosersProgression = new Bindable(); + [JsonProperty] public Point Position; @@ -47,6 +52,9 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [JsonIgnore] public TournamentTeam Winner => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team1.Value : Team2.Value; + [JsonIgnore] + public TournamentTeam Loser => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team2.Value : Team1.Value; + /// /// Remove scores from the match, in case of a false click or false start. /// diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs new file mode 100644 index 0000000000..9f2d2c4045 --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + public class TournamentProgression + { + public int Item1; + public int Item2; + + public bool Losers; + + public TournamentProgression(int item1, int item2, bool losers = false) + { + Item1 = item1; + Item2 = item2; + Losers = losers; + } + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/DrawableTournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/DrawableTournamentGrouping.cs index 28e1183b27..df7f621c24 100644 --- a/osu.Game.Tournament/Screens/Ladder/DrawableTournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/DrawableTournamentGrouping.cs @@ -10,7 +10,7 @@ namespace osu.Game.Tournament.Screens.Ladder { public class DrawableTournamentGrouping : CompositeDrawable { - public DrawableTournamentGrouping(TournamentGrouping grouping) + public DrawableTournamentGrouping(TournamentGrouping grouping, bool losers = false) { AutoSizeAxes = Axes.Both; InternalChild = new FillFlowContainer @@ -27,7 +27,7 @@ namespace osu.Game.Tournament.Screens.Ladder }, new OsuSpriteText { - Text = grouping.Name.ToUpper(), + Text = ((losers ? "Losers " : "") + grouping.Name).ToUpper(), Font = "Exo2.0-Bold", Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 95b2a0a8d1..d5ff651ef3 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Caching; using osu.Framework.Configuration; @@ -54,7 +53,7 @@ namespace osu.Game.Tournament.Screens.Ladder Children = new Drawable[] { paths = new Container { RelativeSizeAxes = Axes.Both }, - headings = new Container() { RelativeSizeAxes = Axes.Both }, + headings = new Container { RelativeSizeAxes = Axes.Both }, pairingsContainer = new Container { RelativeSizeAxes = Axes.Both }, } }, @@ -75,7 +74,12 @@ namespace osu.Game.Tournament.Screens.Ladder if (src == null) throw new InvalidOperationException(); if (dest != null) - src.Progression.Value = dest; + { + if (pair.Losers) + src.LosersProgression.Value = dest; + else + src.Progression.Value = dest; + } } foreach (var pairing in info.Pairings) @@ -99,10 +103,9 @@ namespace osu.Game.Tournament.Screens.Ladder return new LadderInfo { Pairings = pairings, - Progressions = pairings - .Where(p => p.Progression.Value != null) - .Select(p => (p.ID, p.Progression.Value.ID)) - .ToList(), + Progressions = pairings.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat( + pairings.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) + .ToList(), Groupings = editorInfo.Groupings }; } @@ -120,7 +123,7 @@ namespace osu.Game.Tournament.Screens.Ladder { new OsuMenuItem("Create new match", MenuItemType.Highlighted, () => { - var pos = ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); + var pos = pairingsContainer.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); addPairing(new MatchPairing { Position = new Point((int)pos.X, (int)pos.Y) }); }), }; @@ -161,7 +164,7 @@ namespace osu.Game.Tournament.Screens.Ladder foreach (var group in editorInfo.Groupings) { - var topPairing = pairingsContainer.Where(p => p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); + var topPairing = pairingsContainer.Where(p => !p.Pairing.Losers && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); if (topPairing == null) continue; @@ -173,25 +176,44 @@ namespace osu.Game.Tournament.Screens.Ladder }); } + foreach (var group in editorInfo.Groupings) + { + var topPairing = pairingsContainer.Where(p => p.Pairing.Losers && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); + + if (topPairing == null) continue; + + headings.Add(new DrawableTournamentGrouping(group, true) + { + Position = headings.ToLocalSpace((topPairing.ScreenSpaceDrawQuad.TopLeft + topPairing.ScreenSpaceDrawQuad.TopRight) / 2), + Margin = new MarginPadding { Bottom = 10 }, + Origin = Anchor.BottomCentre, + }); + } + layout.Validate(); } - public void RequestJoin(MatchPairing pairing) => AddInternal(new JoinRequestHandler(pairingsContainer, pairing)); + public void RequestJoin(MatchPairing pairing, bool losers) => AddInternal(new JoinRequestHandler(pairingsContainer, pairing, losers)); private class JoinRequestHandler : CompositeDrawable { private readonly Container pairingsContainer; public readonly MatchPairing Source; + private readonly bool losers; private ProgressionPath path; - public JoinRequestHandler(Container pairingsContainer, MatchPairing source) + public JoinRequestHandler(Container pairingsContainer, MatchPairing source, bool losers) { this.pairingsContainer = pairingsContainer; RelativeSizeAxes = Axes.Both; Source = source; - Source.Progression.Value = null; + this.losers = losers; + if (losers) + Source.LosersProgression.Value = null; + else + Source.Progression.Value = null; } private DrawableMatchPairing findTarget(InputState state) => pairingsContainer.FirstOrDefault(d => d.ReceiveMouseInputAt(state.Mouse.NativeState.Position)); @@ -221,7 +243,13 @@ namespace osu.Game.Tournament.Screens.Ladder if (found != null) { if (found.Pairing != Source) - Source.Progression.Value = found.Pairing; + { + if (losers) + Source.LosersProgression.Value = found.Pairing; + else + Source.Progression.Value = found.Pairing; + } + Expire(); return true; } From baefcb9deb52a6c3336e173af6cf1fd6217934bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 03:14:30 +0900 Subject: [PATCH 0144/2854] Simplify team storage --- .../TestCaseLadderManager.cs | 5 +---- .../Ladder/Components/DrawableMatchPairing.cs | 4 ++-- .../Ladder/Components/DrawableMatchTeam.cs | 21 ++++--------------- .../Screens/Ladder/Components/LadderInfo.cs | 2 ++ .../Screens/Ladder/Components/MatchPairing.cs | 11 ++++++++-- .../Screens/Ladder/LadderManager.cs | 13 ++++++++++-- 6 files changed, 29 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index ac56803c90..767681849b 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using System.IO; using Newtonsoft.Json; using osu.Framework.Allocation; @@ -9,7 +8,6 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Tests.Visual; -using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder; using osu.Game.Tournament.Screens.Ladder.Components; @@ -25,13 +23,12 @@ namespace osu.Game.Tournament.Tests public TestCaseLadderManager() { - var teams = JsonConvert.DeserializeObject>(File.ReadAllText(@"teams.json")); var ladder = File.Exists(@"bracket.json") ? JsonConvert.DeserializeObject(File.ReadAllText(@"bracket.json")) : new LadderInfo(); Child = new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, - Child = manager = new LadderManager(ladder, teams) + Child = manager = new LadderManager(ladder) }; } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 285baad872..42670b7bc0 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -107,7 +107,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { bool progressionAbove = progression.ID < Pairing.ID; - var destinationForWinner = progressionAbove || progression.Team1.Value != null && progression.Team1.Value != Pairing.Winner ? progression.Team2 : progression.Team1; + var destinationForWinner = progressionAbove || progression.Team1.Value != null && progression.Team1.Value != Pairing.Team1.Value && progression.Team1.Value != Pairing.Team2.Value ? progression.Team2 : progression.Team1; destinationForWinner.Value = Pairing.Winner; } @@ -115,7 +115,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { bool progressionAbove = progression.ID < Pairing.ID; - var destinationForLoser = progressionAbove || progression.Team1.Value != null && progression.Team1.Value != Pairing.Winner ? progression.Team2 : progression.Team1; + var destinationForLoser = progressionAbove || progression.Team1.Value != null && progression.Team1.Value != Pairing.Team1.Value && progression.Team1.Value != Pairing.Team2.Value ? progression.Team2 : progression.Team1; destinationForLoser.Value = Pairing.Loser; } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index c66d3fff55..f346e3c242 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -145,6 +145,10 @@ namespace osu.Game.Tournament.Screens.Ladder.Components // don't allow changing scores if the match has a progression. can cause large data loss return false; + if (pairing.Completed && pairing.Winner != Team) + // don't allow changing scores from the non-winner + return false; + if (score.Value > 0) score.Value--; else @@ -179,21 +183,4 @@ namespace osu.Game.Tournament.Screens.Ladder.Components } } } - - internal static class Extensions - { - public static T Random(this IEnumerable enumerable) - { - if (enumerable == null) - { - throw new ArgumentNullException(nameof(enumerable)); - } - - // note: creating a Random instance each call may not be correct for you, - // consider a thread-safe static instance - var r = new Random(); - var list = enumerable as IList ?? enumerable.ToList(); - return list.Count == 0 ? default(T) : list[r.Next(0, list.Count)]; - } - } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs index ba64b974d9..567cdb0daa 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Game.Tournament.Components; namespace osu.Game.Tournament.Screens.Ladder.Components { @@ -10,5 +11,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public List Pairings = new List(); public List Progressions = new List(); public List Groupings = new List(); + public List Teams = new List(); } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index 49a477701f..93d1b7085c 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -15,12 +15,18 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { public int ID; + [JsonIgnore] public readonly Bindable Team1 = new Bindable(); + public string Team1Acronym; + public readonly Bindable Team1Score = new Bindable(); + [JsonIgnore] public readonly Bindable Team2 = new Bindable(); + public string Team2Acronym; + public readonly Bindable Team2Score = new Bindable(); public readonly Bindable Completed = new Bindable(); @@ -36,14 +42,15 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [JsonIgnore] public readonly Bindable LosersProgression = new Bindable(); - [JsonProperty] public Point Position; public MatchPairing() { + Team1.BindValueChanged(t => Team1Acronym = t?.Acronym, true); + Team2.BindValueChanged(t => Team2Acronym = t?.Acronym, true); } - public MatchPairing(TournamentTeam team1 = null, TournamentTeam team2 = null) + public MatchPairing(TournamentTeam team1 = null, TournamentTeam team2 = null) : this() { Team1.Value = team1; Team2.Value = team2; diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index d5ff651ef3..665230eff7 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -35,9 +35,9 @@ namespace osu.Game.Tournament.Screens.Ladder [Cached] private readonly LadderEditorInfo editorInfo = new LadderEditorInfo(); - public LadderManager(LadderInfo info, List teams) + public LadderManager(LadderInfo info) { - editorInfo.Teams = Teams = teams; + editorInfo.Teams = Teams = info.Teams; editorInfo.Groupings = info.Groupings; RelativeSizeAxes = Axes.Both; @@ -66,6 +66,14 @@ namespace osu.Game.Tournament.Screens.Ladder } }; + // assign teams + foreach (var pairing in info.Pairings) + { + pairing.Team1.Value = info.Teams.FirstOrDefault(t => t.Acronym == pairing.Team1Acronym); + pairing.Team2.Value = info.Teams.FirstOrDefault(t => t.Acronym == pairing.Team2Acronym); + } + + // assign progressions foreach (var pair in info.Progressions) { var src = info.Pairings.FirstOrDefault(p => p.ID == pair.Item1); @@ -106,6 +114,7 @@ namespace osu.Game.Tournament.Screens.Ladder Progressions = pairings.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat( pairings.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) .ToList(), + Teams = editorInfo.Teams, Groupings = editorInfo.Groupings }; } From fdccec06b3c8809a4a2bf30a40a6327834cb6e6e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 03:55:24 +0900 Subject: [PATCH 0145/2854] Change colour for losers pairings --- .../Screens/Ladder/Components/DrawableMatchPairing.cs | 5 +++-- .../Screens/Ladder/Components/DrawableMatchTeam.cs | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 42670b7bc0..ee69f5c079 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -65,6 +65,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components pairing.Completed.BindValueChanged(_ => updateProgression()); pairing.Progression.BindValueChanged(_ => updateProgression()); pairing.LosersProgression.BindValueChanged(_ => updateProgression()); + pairing.Losers.BindValueChanged(_ => updateTeams()); updateTeams(); } @@ -156,8 +157,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components flow.Children = new[] { - new DrawableMatchTeam(Pairing.Team1, Pairing), - new DrawableMatchTeam(Pairing.Team2, Pairing) + new DrawableMatchTeam(Pairing.Team1, Pairing, Pairing.Losers), + new DrawableMatchTeam(Pairing.Team2, Pairing, Pairing.Losers) }; SchedulerAfterChildren.Add(() => Scheduler.Add(updateProgression)); diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index f346e3c242..7e3d5f4f2b 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -2,8 +2,6 @@ // 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; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -26,6 +24,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public class DrawableMatchTeam : DrawableTournamentTeam, IHasContextMenu { private readonly MatchPairing pairing; + private readonly bool losers; private OsuSpriteText scoreText; private Box background; @@ -41,10 +40,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [Resolved(CanBeNull = true)] private LadderEditorInfo editorInfo { get; set; } = null; - public DrawableMatchTeam(Bindable team, MatchPairing pairing) + public DrawableMatchTeam(Bindable team, MatchPairing pairing, bool losers) : base(team) { this.pairing = pairing; + this.losers = losers; Size = new Vector2(150, 40); Masking = true; @@ -72,7 +72,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { this.manager = manager; - colourWinner = colours.BlueDarker; + colourWinner = losers ? colours.YellowDarker : colours.BlueDarker; colourNormal = OsuColour.Gray(0.2f); InternalChildren = new Drawable[] From 2bba426622371a6a1e073efc3b8e7c4cd936dceb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 03:55:35 +0900 Subject: [PATCH 0146/2854] Fix pairing line being incorrectly offset when scrolled --- .../Screens/Ladder/LadderEditorInfo.cs | 18 ++++++++++++++ .../Screens/Ladder/LadderManager.cs | 24 +++++++++---------- 2 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 osu.Game.Tournament/Screens/Ladder/LadderEditorInfo.cs diff --git a/osu.Game.Tournament/Screens/Ladder/LadderEditorInfo.cs b/osu.Game.Tournament/Screens/Ladder/LadderEditorInfo.cs new file mode 100644 index 0000000000..00272b9378 --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/LadderEditorInfo.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Configuration; +using osu.Game.Tournament.Components; +using osu.Game.Tournament.Screens.Ladder.Components; + +namespace osu.Game.Tournament.Screens.Ladder +{ + public class LadderEditorInfo + { + public readonly BindableBool EditingEnabled = new BindableBool(); + public List Teams = new List(); + public List Groupings = new List(); + public readonly Bindable Selected = new Bindable(); + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 665230eff7..8e206a7194 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Caching; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; @@ -13,18 +12,12 @@ using osu.Framework.Input.States; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; +using OpenTK; +using OpenTK.Graphics; using SixLabors.Primitives; namespace osu.Game.Tournament.Screens.Ladder { - public class LadderEditorInfo - { - public readonly BindableBool EditingEnabled = new BindableBool(); - public List Teams = new List(); - public List Groupings = new List(); - public readonly Bindable Selected = new Bindable(); - } - public class LadderManager : CompositeDrawable, IHasContextMenu { public readonly List Teams; @@ -32,6 +25,8 @@ namespace osu.Game.Tournament.Screens.Ladder private readonly Container paths; private readonly Container headings; + private readonly ScrollableContainer scrollContent; + [Cached] private readonly LadderEditorInfo editorInfo = new LadderEditorInfo(); @@ -47,7 +42,7 @@ namespace osu.Game.Tournament.Screens.Ladder RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new ScrollableContainer + scrollContent = new ScrollableContainer { RelativeSizeAxes = Axes.Both, Children = new Drawable[] @@ -202,7 +197,7 @@ namespace osu.Game.Tournament.Screens.Ladder layout.Validate(); } - public void RequestJoin(MatchPairing pairing, bool losers) => AddInternal(new JoinRequestHandler(pairingsContainer, pairing, losers)); + public void RequestJoin(MatchPairing pairing, bool losers) => scrollContent.Add(new JoinRequestHandler(pairingsContainer, pairing, losers)); private class JoinRequestHandler : CompositeDrawable { @@ -227,6 +222,8 @@ namespace osu.Game.Tournament.Screens.Ladder private DrawableMatchPairing findTarget(InputState state) => pairingsContainer.FirstOrDefault(d => d.ReceiveMouseInputAt(state.Mouse.NativeState.Position)); + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + protected override bool OnMouseMove(InputState state) { var found = findTarget(state); @@ -240,7 +237,10 @@ namespace osu.Game.Tournament.Screens.Ladder if (found == null) return false; - AddInternal(path = new ProgressionPath(pairingsContainer.First(c => c.Pairing == Source), found) { Alpha = 0.4f }); + AddInternal(path = new ProgressionPath(pairingsContainer.First(c => c.Pairing == Source), found) + { + Colour = Color4.Yellow, + }); return base.OnMouseMove(state); } From 492cdb6a05e788772bc0c79262b6a59d7654647f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 04:21:48 +0900 Subject: [PATCH 0147/2854] Fix namespacing --- .../Ladder/{ => Components}/DrawableTournamentGrouping.cs | 3 +-- .../Screens/Ladder/{ => Components}/LadderEditorInfo.cs | 6 +----- 2 files changed, 2 insertions(+), 7 deletions(-) rename osu.Game.Tournament/Screens/Ladder/{ => Components}/DrawableTournamentGrouping.cs (92%) rename osu.Game.Tournament/Screens/Ladder/{ => Components}/LadderEditorInfo.cs (65%) diff --git a/osu.Game.Tournament/Screens/Ladder/DrawableTournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs similarity index 92% rename from osu.Game.Tournament/Screens/Ladder/DrawableTournamentGrouping.cs rename to osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs index df7f621c24..475e735522 100644 --- a/osu.Game.Tournament/Screens/Ladder/DrawableTournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs @@ -4,9 +4,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Tournament.Screens.Ladder.Components; -namespace osu.Game.Tournament.Screens.Ladder +namespace osu.Game.Tournament.Screens.Ladder.Components { public class DrawableTournamentGrouping : CompositeDrawable { diff --git a/osu.Game.Tournament/Screens/Ladder/LadderEditorInfo.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs similarity index 65% rename from osu.Game.Tournament/Screens/Ladder/LadderEditorInfo.cs rename to osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs index 00272b9378..b02c6eea84 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderEditorInfo.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs @@ -1,12 +1,8 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - using System.Collections.Generic; using osu.Framework.Configuration; using osu.Game.Tournament.Components; -using osu.Game.Tournament.Screens.Ladder.Components; -namespace osu.Game.Tournament.Screens.Ladder +namespace osu.Game.Tournament.Screens.Ladder.Components { public class LadderEditorInfo { From 756141d9ed6b2ca3d2941ce73002bb3c5dbceea7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 04:51:40 +0900 Subject: [PATCH 0148/2854] Add basic scaling support --- .../Screens/Ladder/Components/ProgressionPath.cs | 4 ++-- .../Screens/Ladder/ScrollableContainer.cs | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs index c44f67a171..841148ce90 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs @@ -24,11 +24,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components base.LoadComplete(); Vector2 getCenteredVector(Vector2 top, Vector2 bottom) => new Vector2(top.X, top.Y + (bottom.Y - top.Y) / 2); - const float padding = 10; - var q1 = Source.ScreenSpaceDrawQuad; var q2 = Destination.ScreenSpaceDrawQuad; + float padding = q1.Width / 20; + bool progressionToRight = q2.TopLeft.X > q1.TopLeft.X; if (!progressionToRight) diff --git a/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs b/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs index bb4f2e58a9..f818a55296 100644 --- a/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs +++ b/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.States; using OpenTK; @@ -13,10 +14,22 @@ namespace osu.Game.Tournament.Screens.Ladder public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + private Vector2 target; + + private float scale = 1; + protected override bool OnDrag(InputState state) { - Position += state.Mouse.Delta; + this.MoveTo(target += state.Mouse.Delta, 1000, Easing.OutQuint); + return base.OnDrag(state); } + + protected override bool OnScroll(InputState state) + { + this.ScaleTo(scale += state.Mouse.ScrollDelta.Y / 15, 1000, Easing.OutQuint); + + return base.OnScroll(state); + } } } From 40ec24c721aed28fdeb82eaa8a35e18ece3c6cd9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 04:52:05 +0900 Subject: [PATCH 0149/2854] Increase line thickness to match design --- .../Screens/Ladder/Components/ProgressionPath.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs index 841148ce90..56add9dc3d 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs @@ -15,7 +15,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Source = source; Destination = destination; - PathWidth = 2; + PathWidth = 3; BypassAutoSizeAxes = Axes.Both; } From fbda872a53efc74e62134f92c52ad42dd6cf8461 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 04:58:34 +0900 Subject: [PATCH 0150/2854] Update line colours to match bracket type --- .../Screens/Ladder/LadderManager.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 8e206a7194..5e0b5a8449 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -3,12 +3,14 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Caching; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Lines; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.States; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; @@ -144,6 +146,16 @@ namespace osu.Game.Tournament.Screens.Ladder updateLayout(); } + private Color4 normalPathColour; + private Color4 losersPathColour; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + normalPathColour = colours.BlueDarker.Darken(2); + losersPathColour = colours.YellowDarker.Darken(2); + } + private void updateLayout() { paths.Clear(); @@ -162,7 +174,7 @@ namespace osu.Game.Tournament.Screens.Ladder // clean up outdated progressions. pairing.Pairing.Progression.Value = null; else - paths.Add(new ProgressionPath(pairing, dest)); + paths.Add(new ProgressionPath(pairing, dest) { Colour = pairing.Pairing.Losers ? losersPathColour : normalPathColour }); } } From 56981acc9256390b08b80fba2e69ab5f72e44f6d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 05:54:42 +0900 Subject: [PATCH 0151/2854] Fix default value of dropdown --- osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs index d8e37a5c80..1cf155ffde 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs @@ -77,7 +77,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components textboxTeam2 = new OsuTextBox { RelativeSizeAxes = Axes.X, Height = 20 }, groupingDropdown = new SettingsDropdown { - Bindable = new Bindable(), + Bindable = new Bindable { Default = groupingOptions.First().Value }, Items = groupingOptions }, losersCheckbox = new PlayerCheckbox From a5888feca4dab0e82f9097cfa98571e18f907a08 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 10:21:50 +0900 Subject: [PATCH 0152/2854] Wip zoomable container logic --- .../Screens/Ladder/ScrollableContainer.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs b/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs index f818a55296..f9263760fb 100644 --- a/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs +++ b/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs @@ -20,16 +20,24 @@ namespace osu.Game.Tournament.Screens.Ladder protected override bool OnDrag(InputState state) { - this.MoveTo(target += state.Mouse.Delta, 1000, Easing.OutQuint); - + this.TransformTo(nameof(OriginPosition), target -= state.Mouse.Delta / scale, 1000, Easing.OutQuint); return base.OnDrag(state); } + protected override bool OnScroll(InputState state) { - this.ScaleTo(scale += state.Mouse.ScrollDelta.Y / 15, 1000, Easing.OutQuint); + this.ScaleTo(scale += state.Mouse.ScrollDelta.Y / 15 * scale, 1000, Easing.OutQuint); + target = ToLocalSpace(state.Mouse.NativeState.Position) / 2; + this.TransformTo(nameof(OriginPosition), target, 1000, Easing.OutQuint); return base.OnScroll(state); } + + protected override void Update() + { + base.Update(); + Invalidate(); + } } } From 991d85a9f34d9fd36a810b4d131e03a61feeb746 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 10:27:54 +0900 Subject: [PATCH 0153/2854] Cleanups --- .../TestCaseLadderManager.cs | 4 --- .../TestCaseMatchPairings.cs | 5 ---- .../Ladder/Components/DrawableMatchPairing.cs | 12 +-------- .../Ladder/Components/LadderEditorInfo.cs | 3 +++ ...derSettings.cs => LadderEditorSettings.cs} | 26 ------------------- .../Ladder/Components/ProgressionPath.cs | 3 +++ .../Ladder/Components/TournamentConditions.cs | 9 ------- .../Screens/Ladder/LadderManager.cs | 3 +++ 8 files changed, 10 insertions(+), 55 deletions(-) rename osu.Game.Tournament/Screens/Ladder/Components/{LadderSettings.cs => LadderEditorSettings.cs} (83%) delete mode 100644 osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index 767681849b..2dfb1b8a63 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -4,7 +4,6 @@ using System.IO; using Newtonsoft.Json; using osu.Framework.Allocation; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Tests.Visual; @@ -18,9 +17,6 @@ namespace osu.Game.Tournament.Tests [Cached] private readonly LadderManager manager; - [Cached] - private Bindable conditions = new Bindable(new TournamentConditions()); - public TestCaseLadderManager() { var ladder = File.Exists(@"bracket.json") ? JsonConvert.DeserializeObject(File.ReadAllText(@"bracket.json")) : new LadderInfo(); diff --git a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs index dc67807c64..b4a754e439 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs @@ -3,8 +3,6 @@ using System; using System.Collections.Generic; -using osu.Framework.Allocation; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Tests.Visual; @@ -23,9 +21,6 @@ namespace osu.Game.Tournament.Tests typeof(DrawableTournamentTeam), }; - [Cached] - private Bindable conditions = new Bindable(new TournamentConditions()); - public TestCaseMatchPairings() { Container level1; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index ee69f5c079..9b50322098 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -19,7 +19,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { public readonly MatchPairing Pairing; private readonly FillFlowContainer flow; - private readonly Bindable conditions = new Bindable(); private readonly Drawable selectionBox; private Bindable globalSelection; @@ -70,15 +69,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components updateTeams(); } - [BackgroundDependencyLoader(true)] - private void load(Bindable conditions) - { - this.conditions.BindValueChanged(_ => updateWinConditions()); - - if (conditions != null) - this.conditions.BindTo(conditions); - } - private bool selected; public bool Selected @@ -123,7 +113,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private void updateWinConditions() { - if (conditions.Value == null || Pairing.Grouping.Value == null) return; + if (Pairing.Grouping.Value == null) return; var instaWinAmount = Pairing.Grouping.Value.BestOf / 2; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs index b02c6eea84..cc91c98188 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using System.Collections.Generic; using osu.Framework.Configuration; using osu.Game.Tournament.Components; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs similarity index 83% rename from osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs rename to osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index 1cf155ffde..95067e8803 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -85,32 +85,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components LabelText = "Losers Bracket", Bindable = new Bindable() } - // new Container - // { - // RelativeSizeAxes = Axes.X, - // AutoSizeAxes = Axes.Y, - // Padding = new MarginPadding { Horizontal = padding }, - // Children = new Drawable[] - // { - // new OsuSpriteText - // { - // Anchor = Anchor.CentreLeft, - // Origin = Anchor.CentreLeft, - // Text = "Best of", - // }, - // }, - // }, - // sliderBestOf = new PlayerSliderBar - // { - // Bindable = new BindableDouble - // { - // Default = 11, - // Value = 11, - // MinValue = 1, - // MaxValue = 21, - // Precision = 1, - // }, - // } }; editorInfo.Selected.ValueChanged += selection => diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs index 56add9dc3d..4496430e79 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Lines; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs deleted file mode 100644 index 045149945c..0000000000 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace osu.Game.Tournament.Screens.Ladder.Components -{ - /// - /// Conditions governing a tournament. - /// - public class TournamentConditions - { - } -} diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 5e0b5a8449..3c3d8243a3 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2018 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; From e74fd042aa4e8244efc8c38c97d4f08575a1b236 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 10:35:00 +0900 Subject: [PATCH 0154/2854] Use MouseUp instead of MouseDown for now --- .../Screens/Ladder/Components/DrawableMatchPairing.cs | 7 ++----- .../Screens/Ladder/Components/DrawableMatchTeam.cs | 3 ++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 9b50322098..5f58ca0ba5 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -155,9 +155,9 @@ namespace osu.Game.Tournament.Screens.Ladder.Components updateWinConditions(); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => args.Button == MouseButton.Left; + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => editorInfo.EditingEnabled; - protected override bool OnDragStart(InputState state) => true; + protected override bool OnDragStart(InputState state) => editorInfo.EditingEnabled; protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { @@ -183,9 +183,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { if (base.OnDrag(state)) return true; - if (!editorInfo.EditingEnabled) - return false; - Selected = true; this.MoveToOffset(state.Mouse.Delta); diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index 7e3d5f4f2b..b0570a24dc 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -126,7 +126,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components }, true); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + //TODO: use OnClick instead once we have per-button clicks. + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { if (Team == null || editorInfo.EditingEnabled) return false; From c210ea7c39d0b1e6180348132f4eb36dd7406501 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 10:46:09 +0900 Subject: [PATCH 0155/2854] Improve zoom logic --- .../Screens/Ladder/ScrollableContainer.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs b/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs index f9263760fb..9d4ad2eccf 100644 --- a/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs +++ b/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs @@ -20,18 +20,18 @@ namespace osu.Game.Tournament.Screens.Ladder protected override bool OnDrag(InputState state) { - this.TransformTo(nameof(OriginPosition), target -= state.Mouse.Delta / scale, 1000, Easing.OutQuint); - return base.OnDrag(state); + this.MoveTo(target += state.Mouse.Delta, 1000, Easing.OutQuint); + return true; } - protected override bool OnScroll(InputState state) { - this.ScaleTo(scale += state.Mouse.ScrollDelta.Y / 15 * scale, 1000, Easing.OutQuint); - target = ToLocalSpace(state.Mouse.NativeState.Position) / 2; - this.TransformTo(nameof(OriginPosition), target, 1000, Easing.OutQuint); + var newScale = scale + state.Mouse.ScrollDelta.Y / 15 * scale; + this.MoveTo(target = target - state.Mouse.Position * (newScale - scale), 1000, Easing.OutQuint); - return base.OnScroll(state); + this.ScaleTo(scale = newScale, 1000, Easing.OutQuint); + + return true; } protected override void Update() From 2abe96fb9c18f0bf9f4d9f88943b945b55579b29 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 13:39:27 +0900 Subject: [PATCH 0156/2854] Fix crash --- .../Screens/Ladder/Components/DrawableMatchTeam.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index b0570a24dc..12638609b6 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -142,7 +142,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components } else { - if (pairing.Progression.Value.Completed) + if (pairing.Progression.Value?.Completed.Value != false) // don't allow changing scores if the match has a progression. can cause large data loss return false; From 73f451f27af420ec923da06c6e4d404de84db9aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Sep 2018 13:39:33 +0900 Subject: [PATCH 0157/2854] Fix right click regression --- .../Screens/Ladder/Components/DrawableMatchPairing.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 5f58ca0ba5..6e4674b3bf 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -155,7 +155,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components updateWinConditions(); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => editorInfo.EditingEnabled; + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => args.Button == MouseButton.Left && editorInfo.EditingEnabled; protected override bool OnDragStart(InputState state) => editorInfo.EditingEnabled; From 9a5127d506e255ab70ae00d2a4ad83bbe27d2c73 Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Sun, 30 Sep 2018 14:29:55 +0200 Subject: [PATCH 0158/2854] fixed object scale upon hit --- osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs index ec80537a52..8c6f82dee4 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -29,15 +29,14 @@ namespace osu.Game.Rulesets.Osu.Mods { if (!(drawable is DrawableHitCircle d)) return; - d.ApproachCircle.Hide(); var h = d.HitObject; - using (d.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) { var origScale = d.Scale; - d.ScaleTo(1.1f); - d.ScaleTo(origScale, h.TimePreempt); + d.ScaleTo(1.1f, 1) // if duration = 0 then components (i.e. flash) scale with it -> we don't want that + .Then() + .ScaleTo(origScale, h.TimePreempt); } } } From 98042eb7d4aaec3e53530b6ffbc2bff2b014ea71 Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Sun, 30 Sep 2018 14:46:36 +0200 Subject: [PATCH 0159/2854] add the default fadeout-while-scaling-up to clicked objects --- osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs index 8c6f82dee4..a4f841f188 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -36,7 +36,9 @@ namespace osu.Game.Rulesets.Osu.Mods var origScale = d.Scale; d.ScaleTo(1.1f, 1) // if duration = 0 then components (i.e. flash) scale with it -> we don't want that .Then() - .ScaleTo(origScale, h.TimePreempt); + .ScaleTo(origScale, h.TimePreempt) + .Then() + .ScaleTo(d.Scale * 1.5f, 400, Easing.OutQuad); // reapply overwritten ScaleTo } } } From 8b09935c378b38f72f8c7429d1ed84a3fdf5e198 Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Sun, 30 Sep 2018 14:49:52 +0200 Subject: [PATCH 0160/2854] previous commit only added scaling-up (oops), this one includes fading --- osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs index a4f841f188..db46d3992a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Osu.Mods .Then() .ScaleTo(origScale, h.TimePreempt) .Then() + .FadeOut(800) .ScaleTo(d.Scale * 1.5f, 400, Easing.OutQuad); // reapply overwritten ScaleTo } } From 01a4c8d92b5556d1757adb7a828a8ffbe7bc5d7c Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Mon, 1 Oct 2018 12:11:19 +0200 Subject: [PATCH 0161/2854] Add deflate mod to OsuRuleset.cs --- osu.Game.Rulesets.Osu/OsuRuleset.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 6736d10dab..30dd9c73d6 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -121,6 +121,7 @@ namespace osu.Game.Rulesets.Osu return new Mod[] { new OsuModTransform(), new OsuModWiggle(), + new OsuModDeflate(), }; default: return new Mod[] { }; From 0ae9c78c38ce3cd33b3b3cfce14234050ad6b05f Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Mon, 1 Oct 2018 12:29:21 +0200 Subject: [PATCH 0162/2854] set drawableOnApplyCustomUpdateState private to make AppVeyor happy :) --- osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs index db46d3992a..d61d552ce2 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Mods drawable.ApplyCustomUpdateState += drawableOnApplyCustomUpdateState; } - protected void drawableOnApplyCustomUpdateState(DrawableHitObject drawable, ArmedState state) + private void drawableOnApplyCustomUpdateState(DrawableHitObject drawable, ArmedState state) { if (!(drawable is DrawableHitCircle d)) return; From 1366b53a7150c56165a534dacea2eae9933e0a3b Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Tue, 9 Oct 2018 13:16:27 +0200 Subject: [PATCH 0163/2854] Added traceable mod + HideButApproachCircle function for DrawableHitCircle --- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 32 +++++++++++++++++++ .../Objects/Drawables/DrawableHitCircle.cs | 9 ++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 1 + 3 files changed, 42 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs new file mode 100644 index 0000000000..43eac55ffd --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; + +namespace osu.Game.Rulesets.Osu.Mods +{ + internal class OsuModTraceable : Mod, IApplicableToDrawableHitObjects + { + public override string Name => "Traceable"; + public override string ShortenedName => "TC"; + public override FontAwesome Icon => FontAwesome.fa_snapchat_ghost; + public override ModType Type => ModType.Fun; + public override string Description => "Put your faith in the approach circles..."; + public override double ScoreMultiplier => 1; + + public void ApplyToDrawableHitObjects(IEnumerable drawables) + { + foreach (var drawable in drawables) + { + if (drawable is DrawableHitCircle c) + c.HideButApproachCircle(); + if (drawable is DrawableSlider s) + s.HeadCircle.HideButApproachCircle(); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 4bdddcef11..79cc88e474 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -76,6 +76,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } + public void HideButApproachCircle() + { + circle.Hide(); + circle.AlwaysPresent = true; + ring.Hide(); + number.Hide(); + glow.Hide(); + } + protected override void CheckForResult(bool userTriggered, double timeOffset) { if (!userTriggered) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 6736d10dab..0b6aee7881 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -121,6 +121,7 @@ namespace osu.Game.Rulesets.Osu return new Mod[] { new OsuModTransform(), new OsuModWiggle(), + new OsuModTraceable(), }; default: return new Mod[] { }; From 954bcd8c123dcad1112ed94bc07bf3a824adfc9d Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Tue, 9 Oct 2018 18:36:12 +0200 Subject: [PATCH 0164/2854] Treat non-DrawableHitCircle's similar to OsuModHidden --- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 61 +++++++++++++++++-- .../Objects/Drawables/DrawableHitCircle.cs | 2 + 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index 43eac55ffd..dfd398e7e4 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -1,11 +1,14 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using osu.Game.Graphics; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Osu.Mods { @@ -21,11 +24,61 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableHitObjects(IEnumerable drawables) { foreach (var drawable in drawables) + drawable.ApplyCustomUpdateState += ApplyTraceableState; + } + + /* Similar to ApplyHiddenState, only different if drawable is DrawableHitCircle. + * If we'd use ApplyHiddenState instead but only on non-DrawableHitCircle's, then + * the nested object HeadCircle of DrawableSlider would still use ApplyHiddenState, + * thus treating the DrawableHitCircle with the hidden mod instead of the traceable mod. + */ + protected void ApplyTraceableState(DrawableHitObject drawable, ArmedState state) + { + if (!(drawable is DrawableOsuHitObject d)) + return; + + var h = d.HitObject; + + var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadeIn; + + // new duration from completed fade in to end (before fading out) + var longFadeDuration = ((h as IHasEndTime)?.EndTime ?? h.StartTime) - fadeOutStartTime; + + switch (drawable) { - if (drawable is DrawableHitCircle c) - c.HideButApproachCircle(); - if (drawable is DrawableSlider s) - s.HeadCircle.HideButApproachCircle(); + case DrawableHitCircle circle: + // we only want to see the approach circle + using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + circle.HideButApproachCircle(); + + // approach circle fades out quickly at StartTime + using (drawable.BeginAbsoluteSequence(h.StartTime, true)) + circle.ApproachCircle.FadeOut(50); + + break; + case DrawableSlider slider: + using (slider.BeginAbsoluteSequence(fadeOutStartTime, true)) + slider.Body.FadeOut(longFadeDuration, Easing.Out); + + break; + case DrawableSliderTick sliderTick: + // slider ticks fade out over up to one second + var tickFadeOutDuration = Math.Min(sliderTick.HitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000); + + using (sliderTick.BeginAbsoluteSequence(sliderTick.HitObject.StartTime - tickFadeOutDuration, true)) + sliderTick.FadeOut(tickFadeOutDuration); + + break; + case DrawableSpinner spinner: + // hide elements we don't care about. + spinner.Disc.Hide(); + spinner.Ticks.Hide(); + spinner.Background.Hide(); + + using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true)) + spinner.FadeOut(h.TimePreempt); + + break; } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 79cc88e474..1644480aa4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -81,6 +81,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables circle.Hide(); circle.AlwaysPresent = true; ring.Hide(); + flash.Hide(); + explode.Hide(); number.Hide(); glow.Hide(); } From edb69463fd4318145ff97e2bb3a0219fc8f646dc Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Tue, 9 Oct 2018 18:52:34 +0200 Subject: [PATCH 0165/2854] Trimming whitespace from comment...... --- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index dfd398e7e4..a6d8d35125 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Mods } /* Similar to ApplyHiddenState, only different if drawable is DrawableHitCircle. - * If we'd use ApplyHiddenState instead but only on non-DrawableHitCircle's, then + * If we'd use ApplyHiddenState instead but only on non-DrawableHitCircle's, then * the nested object HeadCircle of DrawableSlider would still use ApplyHiddenState, * thus treating the DrawableHitCircle with the hidden mod instead of the traceable mod. */ From 951ac30de8a6db2af540b7aef26f5fb352b41330 Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Wed, 10 Oct 2018 01:36:37 +0200 Subject: [PATCH 0166/2854] ArmedState now considered, SliderBody now deflates (TODO: handle slider's nested objects) --- osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs | 55 +++++++++++++++++---- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs index d61d552ce2..f5be85158f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -7,6 +7,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; using System.Collections.Generic; +using OpenTK; namespace osu.Game.Rulesets.Osu.Mods { @@ -27,19 +28,53 @@ namespace osu.Game.Rulesets.Osu.Mods private void drawableOnApplyCustomUpdateState(DrawableHitObject drawable, ArmedState state) { - if (!(drawable is DrawableHitCircle d)) + if (!(drawable is DrawableOsuHitObject d)) return; - d.ApproachCircle.Hide(); + var h = d.HitObject; - using (d.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) + + switch (drawable) { - var origScale = d.Scale; - d.ScaleTo(1.1f, 1) // if duration = 0 then components (i.e. flash) scale with it -> we don't want that - .Then() - .ScaleTo(origScale, h.TimePreempt) - .Then() - .FadeOut(800) - .ScaleTo(d.Scale * 1.5f, 400, Easing.OutQuad); // reapply overwritten ScaleTo + case DrawableHitCircle c: + c.ApproachCircle.Hide(); + using (d.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) + { + var origScale = d.Scale; + d.ScaleTo(1.1f, 1) + .Then() + .ScaleTo(origScale, h.TimePreempt); + } + switch (state) + { + case ArmedState.Miss: + d.FadeOut(100); + break; + case ArmedState.Hit: + d.FadeOut(800) + .ScaleTo(d.Scale * 1.5f, 400, Easing.OutQuad); + break; + } + break; + case DrawableSlider s: + using (d.BeginAbsoluteSequence(h.StartTime - h.TimePreempt + 1, true)) + { + float origPathWidth = s.Body.PathWidth; + var origBodySize = s.Body.Size; + var origBodyDrawPos = s.Body.DrawPosition; + + s.Body.MoveTo(origBodyDrawPos - new Vector2(origPathWidth), 1) + .Then() + .MoveTo(origBodyDrawPos, h.TimePreempt); + + s.Body.ResizeTo(origBodySize * 2, 1) + .Then() + .ResizeTo(origBodySize, h.TimePreempt); + + s.Body.TransformTo("PathWidth", origPathWidth * 2, 1) + .Then() + .TransformTo("PathWidth", origPathWidth, h.TimePreempt); + } + break; } } } From 19efa1cafb33859e7444d981790a5c90c42e3f70 Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Fri, 12 Oct 2018 20:55:42 +0200 Subject: [PATCH 0167/2854] Added TestCase, first RepeatPoint now deflates aswell --- osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs | 27 +- .../Objects/Drawables/DrawableRepeatPoint.cs | 2 + osu.Game.Tests/Visual/TestCaseDeflate.cs | 637 ++++++++++++++++++ 3 files changed, 655 insertions(+), 11 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestCaseDeflate.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs index f5be85158f..a049248021 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -33,6 +33,8 @@ namespace osu.Game.Rulesets.Osu.Mods var h = d.HitObject; + float rescale = 2; + switch (drawable) { case DrawableHitCircle c: @@ -55,6 +57,7 @@ namespace osu.Game.Rulesets.Osu.Mods break; } break; + case DrawableSlider s: using (d.BeginAbsoluteSequence(h.StartTime - h.TimePreempt + 1, true)) { @@ -62,19 +65,21 @@ namespace osu.Game.Rulesets.Osu.Mods var origBodySize = s.Body.Size; var origBodyDrawPos = s.Body.DrawPosition; - s.Body.MoveTo(origBodyDrawPos - new Vector2(origPathWidth), 1) - .Then() - .MoveTo(origBodyDrawPos, h.TimePreempt); - - s.Body.ResizeTo(origBodySize * 2, 1) - .Then() - .ResizeTo(origBodySize, h.TimePreempt); - - s.Body.TransformTo("PathWidth", origPathWidth * 2, 1) - .Then() - .TransformTo("PathWidth", origPathWidth, h.TimePreempt); + // Fits nicely for CS=4, too big on lower CS, too small on higher CS + s.Body.Animate( + b => b.MoveTo(origBodyDrawPos - new Vector2(origPathWidth)).MoveTo(origBodyDrawPos, h.TimePreempt), + b => b.ResizeTo(origBodySize * rescale).ResizeTo(origBodySize, h.TimePreempt), + b => b.TransformTo("PathWidth", origPathWidth * rescale).TransformTo("PathWidth", origPathWidth, h.TimePreempt) + ); } break; + case DrawableRepeatPoint rp: + if (!rp.IsFirstRepeat) + break; + var origSizeRP = rp.Size; + using (d.BeginAbsoluteSequence(h.StartTime - h.TimePreempt + 1, true)) + rp.ResizeTo(origSizeRP * rescale).ResizeTo(origSizeRP, h.TimePreempt); + break; } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index dfe7937e81..65b6b5b004 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -41,6 +41,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }; } + public bool IsFirstRepeat => repeatPoint.RepeatIndex == 0; + protected override void CheckForResult(bool userTriggered, double timeOffset) { if (repeatPoint.StartTime <= Time.Current) diff --git a/osu.Game.Tests/Visual/TestCaseDeflate.cs b/osu.Game.Tests/Visual/TestCaseDeflate.cs new file mode 100644 index 0000000000..8a24e2414c --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseDeflate.cs @@ -0,0 +1,637 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using System.IO; +using System.Text; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using Decoder = osu.Game.Beatmaps.Formats.Decoder; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Screens.Play; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Tests.Visual +{ + [Description("Player instantiated with an deflate mod.")] + public class TestCaseDeflate : TestCasePlayer + { + protected override IBeatmap CreateBeatmap(Ruleset ruleset) + { + Beatmap beatmap; + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) + using (var reader = new StreamReader(stream)) + beatmap = Decoder.GetDecoder(reader).Decode(reader); + beatmap.BeatmapInfo.Ruleset = ruleset.RulesetInfo; + beatmap.BeatmapInfo.BaseDifficulty.CircleSize = 4; + return beatmap; + } + + protected override Player CreatePlayer(Ruleset ruleset) + { + Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod(), ruleset.GetAllMods().First(mod => mod is OsuModDeflate) }); + return new ScoreAccessiblePlayer + { + AllowPause = false, + AllowLeadIn = false, + AllowResults = false, + }; + } + + protected override bool ContinueCondition(Player player) => base.ContinueCondition(player) && ((ScoreAccessiblePlayer)player).ScoreProcessor.TotalScore > 0; + + private class ScoreAccessiblePlayer : Player + { + public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; + } + + private const string test_beatmap_data = + @"osu file format v14 + +[General] +AudioFilename: R4V3 B0Y - S3RL Feat Krystal.mp3 +AudioLeadIn: 0 +PreviewTime: 56176 +Countdown: 0 +SampleSet: Soft +StackLeniency: 0.7 +Mode: 0 +LetterboxInBreaks: 0 +WidescreenStoryboard: 1 + +[Editor] +Bookmarks: 12382,23354,34325,45297,56268,67240,78211,89182,100154,111125,122097,128605,133068,144040,155011,165982,175582 +DistanceSpacing: 1.5 +BeatDivisor: 4 +GridSize: 4 +TimelineZoom: 3.799998 + +[Metadata] +Title:R4V3 B0Y +TitleUnicode:R4V3 B0Y +Artist:S3RL feat Krystal +ArtistUnicode:S3RL feat Krystal +Creator:DeRandom Otaku +Version:FCL's H4RD +Source: +Tags:happy hardcore EMFA music rave boy Shmiklak FCL ByBy ByBy13 ByBy_Chan Marvollo M_a_r_v_o_l_l_o +BeatmapID:1056070 +BeatmapSetID:481451 + +[Difficulty] +HPDrainRate:5 +CircleSize:4 +OverallDifficulty:6 +ApproachRate:7.5 +SliderMultiplier:1.4 +SliderTickRate:1 + +[Events] +//Background and Video events +0,0,'BG.jpg',0,0 +//Break Periods +2,89376,99323 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples + +[TimingPoints] +34,342.857142857143,4,1,1,100,1,0 +34,-133.333333333333,4,1,1,100,0,0 +1405,-133.333333333333,4,2,1,70,0,0 +12376,-125,4,2,2,70,0,0 +21976,-100,4,2,2,45,0,0 +23348,-100,4,2,2,85,0,0 +26091,-100,4,2,2,85,0,0 +31576,-100,4,2,2,85,0,0 +34319,-100,4,2,2,85,0,0 +37062,-100,4,2,2,85,0,0 +42462,-100,4,2,2,5,0,0 +42548,-100,4,2,2,85,0,0 +43919,-100,4,2,2,65,0,0 +45291,-100,4,2,2,50,0,0 +50691,-100,4,2,2,5,0,0 +50776,-100,4,2,2,50,0,0 +56262,-100,4,2,2,90,0,1 +65862,-125,4,2,2,90,0,0 +67234,-100,4,2,2,90,0,1 +72719,-100,4,2,2,90,0,1 +76834,-125,4,2,2,45,0,0 +78205,-125,4,2,0,50,0,0 +100148,-125,4,2,2,50,0,0 +100491,-125,4,2,0,50,0,0 +111119,-100,4,2,2,50,0,0 +116091,-100,4,2,2,10,0,0 +116262,-100,4,2,2,50,0,0 +116434,-100,4,2,2,10,0,0 +116605,-100,4,2,2,50,0,0 +121748,-133.333333333333,4,2,2,50,0,0 +122091,-100,4,2,2,50,0,0 +127576,-100,4,2,2,50,0,0 +130319,-90.9090909090909,4,2,2,50,0,0 +131005,-83.3333333333333,4,2,2,50,0,0 +131691,-76.9230769230769,4,2,2,50,0,0 +132376,-133.333333333333,4,2,2,50,0,0 +133062,-100,4,2,2,90,0,1 +135719,-100,4,2,2,5,0,1 +135805,-100,4,2,2,90,0,1 +141205,-100,4,2,2,5,0,1 +141291,-100,4,2,2,90,0,1 +142662,-100,4,2,2,70,0,0 +143691,-100,4,2,2,40,0,0 +144034,-100,4,2,2,90,0,1 +146691,-100,4,2,2,5,0,1 +146776,-100,4,2,2,90,0,1 +152176,-100,4,2,2,5,0,1 +152262,-100,4,2,2,90,0,1 +154662,-100,4,2,2,60,0,1 +155005,-100,4,2,2,75,0,0 +165976,-100,4,2,2,75,0,0 +171462,-133.333333333333,4,2,2,60,0,0 +174205,-153.846153846153,4,2,2,50,0,0 +174891,-153.846153846154,4,2,2,40,0,0 +175576,-100,4,2,2,25,0,0 +176262,-125,4,2,2,25,0,0 +176605,-125,4,2,2,15,0,0 + + +[Colours] + Combo1 : 0,255,255 +Combo2 : 17,255,17 +Combo3 : 0,128,192 +Combo4 : 124,0,249 + +[HitObjects] +138,122,34,6,0,L|133:229,3,105.000004005432,0|0|0|0,2:0|2:0|2:0|2:0,2:2:0:0: +133,226,1405,6,0,L|248:220,1,105.000004005432,2|2,1:2|0:0,0:0:0:0: +339,341,2091,2,0,L|222:338,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: +133,226,2776,2,0,P|128:187|129:158,1,52.5000020027162,2|0,0:0|0:0,0:0:0:0: +188,123,3119,2,0,P|193:84|190:55,1,52.5000020027162,2|0,0:0|0:0,0:0:0:0: +262,34,3462,2,0,P|290:62|366:69,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: +431,74,3976,1,0,0:0:0:0: +496,117,4148,6,0,L|503:229,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: +456,286,4662,1,0,0:0:0:0: +408,349,4834,2,0,L|296:356,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: +172,266,5519,2,0,P|155:216|165:148,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: +206,101,6034,1,0,0:0:0:0: +239,172,6205,2,0,L|361:162,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: +494,260,6891,6,0,B|443:252|443:252|386:267,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: +324,308,7405,1,0,0:0:0:0: +248,287,7576,2,0,L|241:227,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: +169,202,7919,2,0,L|176:142,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: +239,104,8262,2,0,P|282:119|345:101,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: +416,92,8776,1,0,0:0:0:0: +484,132,8948,2,0,L|491:195,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: +445,249,9291,2,0,L|452:312,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: +388,349,9634,6,0,P|354:328|289:335,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: +216,360,10148,1,0,0:0:0:0: +143,328,10319,2,0,L|72:315,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: +102,240,10662,2,0,L|173:227,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: +224,195,11005,5,2,0:0:0:0: +160,148,11176,1,0,0:0:0:0: +233,120,11348,1,2,0:0:0:0: +172,69,11519,1,0,0:0:0:0: +247,45,11691,2,0,P|285:39|314:43,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: +355,95,12034,2,0,P|378:105|399:121,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: +482,162,12376,6,0,P|450:190|445:261,1,112,2|2,0:0|0:0,0:0:0:0: +298,342,13062,2,0,P|296:287|259:247,1,112,2|2,0:0|0:0,0:0:0:0: +132,135,13748,2,0,L|127:62,1,56,2|0,0:0|0:0,0:0:0:0: +209,57,14091,2,0,L|235:-11,1,56,2|0,0:0|0:0,0:0:0:0: +304,41,14434,2,0,P|348:52|380:44,2,56,2|0|2,0:0|0:0|0:0,0:0:0:0: +272,119,14948,1,0,0:0:0:0: +335,173,15119,6,0,L|340:298,1,112,2|2,0:0|0:0,0:0:0:0: +274,339,15634,1,0,0:0:0:0: +216,278,15805,2,0,B|165:262|165:262|86:278,1,112,2|2,0:0|0:0,0:0:0:0: +5,139,16491,2,0,B|56:123|56:123|135:139,1,112,2|2,0:0|0:0,0:0:0:0: +189,171,17005,1,0,0:0:0:0: +257,123,17176,2,0,L|379:117,1,112,2|2,0:0|0:0,0:0:0:0: +434,171,17691,1,0,0:0:0:0: +434,171,17776,1,0,0:0:0:0: +434,171,17862,6,0,P|450:218|426:288,1,112,2|2,0:0|0:0,0:0:0:0: +356,308,18376,1,0,0:0:0:0: +288,261,18548,2,0,L|207:258,1,56,2|0,0:0|0:0,0:0:0:0: +170,316,18891,2,0,L|89:319,1,56,2|0,0:0|0:0,0:0:0:0: +38,234,19234,2,0,P|110:237|160:210,1,112,2|2,0:0|0:0,0:0:0:0: +179,144,19748,1,0,0:0:0:0: +104,105,19919,2,0,L|100:31,1,56,2|0,0:0|0:0,0:0:0:0: +179,18,20262,2,0,L|210:8,3,28,2|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: +247,8,20605,6,0,P|236:48|268:122,1,112,2|2,0:0|0:0,0:0:0:0: +311,178,21119,1,0,0:0:0:0: +288,259,21291,2,0,L|286:330,1,56,2|0,0:0|0:0,0:0:0:0: +363,349,21634,2,0,L|361:293,1,56,2|0,0:0|0:0,0:0:0:0: +408,231,21976,5,0,0:0:0:0: +462,125,23005,2,0,L|469:80,3,35 +499,48,23348,6,0,L|348:53,1,140,4|0,1:2|1:0,0:0:0:0: +178,161,24034,1,0,1:0:0:0: +244,241,24205,1,0,0:0:0:0: +244,241,24291,2,0,L|365:231,1,105,0|0,0:0|0:0,0:0:0:0: +434,292,24719,2,0,P|423:335|439:379,1,70,0|0,1:0|0:0,0:0:0:0: +325,333,25062,2,0,P|329:298|319:265,1,70,0|0,1:0|0:0,0:0:0:0: +250,185,25405,1,0,1:0:0:0: +308,135,25576,1,0,0:0:0:0: +213,111,25748,2,0,L|203:64,2,35,0|0|0,1:0|0:0|0:0,0:0:0:0: +146,183,26091,6,0,L|44:180,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: +202,370,26776,1,0,1:0:0:0: +253,288,26948,1,0,0:0:0:0: +253,288,27034,2,0,B|236:237|236:237|246:176,1,105,0|0,0:0|0:0,0:0:0:0: +318,112,27462,2,0,L|466:102,2,140,0|0|0,1:0|1:0|1:0,0:0:0:0: +139,224,28491,2,0,P|110:228|77:229,3,35,0|0|0|0,1:0|0:0|0:0|0:0,0:0:0:0: +69,267,28834,6,0,P|131:258|212:289,1,140,0|0,1:0|1:0,0:0:0:0: +402,217,29519,1,0,1:0:0:0: +303,180,29691,1,0,0:0:0:0: +303,180,29776,2,0,P|288:123|299:60,1,105,0|0,0:0|0:0,0:0:0:0: +200,26,30205,2,0,P|155:51|54:21,2,140,0|0|0,1:0|1:0|1:0,0:0:0:0: +166,125,31062,1,0,0:0:0:0: +242,197,31234,2,0,L|246:257,3,35,0|0|0|0,1:0|0:0|0:0|0:0,0:0:0:0: +293,248,31576,6,0,L|139:246,1,140,0|0,1:0|1:0,0:0:0:0: +66,307,32091,1,0,0:0:0:0: +71,202,32262,2,0,L|84:35,1,140,0|0,1:0|1:0,0:0:0:0: +289,23,32948,5,0,0:0:0:0: +218,53,33119,1,0,0:0:0:0: +176,118,33291,2,0,P|174:157|190:205,2,70,0|0|0,0:0|0:0|0:0,0:0:0:0: +176,118,34148,1,8,0:0:0:0: +265,172,34319,6,0,L|418:174,1,140,4|0,1:2|1:0,0:0:0:0: +440,273,34834,1,0,0:0:0:0: +337,253,35005,2,0,P|281:253|235:345,1,140,0|0,1:0|1:0,0:0:0:0: +232,315,35519,1,0,0:0:0:0: +130,341,35691,2,0,L|36:328,1,70,0|0,1:0|0:0,0:0:0:0: +95,231,36034,2,0,L|15:179,1,70,0|0,1:0|0:0,0:0:0:0: +110,119,36376,2,0,L|59:38,1,70,0|0,1:0|0:0,0:0:0:0: +185,0,36719,1,0,1:0:0:0: +185,0,36805,2,0,L|193:69,1,70,0|0,0:0|0:0,0:0:0:0: +224,85,37062,6,0,L|307:102,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: +262,183,37576,1,0,0:0:0:0: +185,253,37748,2,0,P|117:286|52:236,1,140,0|0,1:0|1:0,0:0:0:0: +116,166,38262,1,0,0:0:0:0: +185,253,38434,2,0,P|222:247|281:246,1,70,0|0,1:0|0:0,0:0:0:0: +344,299,38776,2,0,P|380:310|433:333,1,70,0|0,1:0|0:0,0:0:0:0: +500,267,39119,1,0,1:0:0:0: +379,96,39462,2,0,L|374:53,3,35,0|0|0|0,1:0|0:0|0:0|0:0,0:0:0:0: +395,12,39805,6,0,L|224:2,1,140,0|0,1:0|1:0,0:0:0:0: +172,69,40319,1,0,0:0:0:0: +267,112,40491,2,0,P|250:151|275:259,1,140,0|0,1:0|1:0,0:0:0:0: +264,244,41005,1,0,0:0:0:0: +182,309,41176,2,0,P|155:264|127:236,1,70,0|0,1:0|0:0,0:0:0:0: +69,176,41519,2,0,P|96:131|124:103,1,70,0|0,1:0|0:0,0:0:0:0: +202,71,41862,1,0,1:0:0:0: +179,173,42034,1,0,0:0:0:0: +137,37,42205,1,0,1:0:0:0: +137,37,42291,2,0,L|35:33,1,70,0|0,0:0|0:0,0:0:0:0: +23,63,42548,6,0,B|6:161|6:161|23:218,1,140,0|0,1:0|1:0,0:0:0:0: +62,295,43062,1,0,0:0:0:0: +141,226,43234,1,0,1:0:0:0: +168,348,43405,1,0,0:0:0:0: +168,348,43491,2,0,L|295:355,1,105,0|0,0:0|0:0,0:0:0:0: +367,309,43919,1,0,0:0:0:0: +201,89,45291,6,0,L|279:83,1,70,2|0,0:0|0:0,0:0:0:0: +201,162,45634,2,0,L|279:156,1,70,2|0,0:0|0:0,0:0:0:0: +202,235,45976,2,0,L|280:229,1,70,2|0,0:0|0:0,0:0:0:0: +202,308,46319,2,0,L|280:302,1,70,2|0,0:0|0:0,0:0:0:0: +411,345,46662,6,0,P|368:344|311:362,1,70,2|0,0:0|0:0,0:0:0:0: +399,261,47005,2,0,P|360:241|301:234,1,70,2|0,0:0|0:0,0:0:0:0: +427,187,47348,2,0,P|400:153|349:121,1,70,2|0,0:0|0:0,0:0:0:0: +484,134,47691,2,0,P|473:91|441:41,1,70,2|0,0:0|0:0,0:0:0:0: +348,30,48034,6,0,B|308:39|308:39|264:31,1,70,2|0,0:0|0:0,0:0:0:0: +205,108,48376,2,0,B|245:117|245:117|289:109,1,70,2|0,0:0|0:0,0:0:0:0: +345,188,48719,2,0,B|305:197|305:197|261:189,1,70,2|0,0:0|0:0,0:0:0:0: +199,263,49062,2,0,B|239:272|239:272|283:264,1,70,2|0,0:0|0:0,0:0:0:0: +345,188,49405,6,0,L|354:101,1,70,2|0,0:0|0:0,0:0:0:0: +252,150,49748,2,0,L|243:63,1,70,2|0,0:0|0:0,0:0:0:0: +146,118,50091,2,0,P|123:75|90:43,1,70,2|0,0:0|0:0,0:0:0:0: +8,28,50434,2,0,L|6:177,1,140,10|2,0:0|0:0,0:0:0:0: +93,242,50948,5,0,0:0:0:0: +93,242,51119,2,0,L|190:238,1,70,2|0,0:0|0:0,0:0:0:0: +239,311,51462,2,0,L|308:313,1,70,2|0,0:0|0:0,0:0:0:0: +393,250,51805,1,2,0:0:0:0: +429,348,51976,1,0,0:0:0:0: +429,348,52319,5,0,0:0:0:0: +486,259,52491,2,0,P|487:222|486:172,1,70,2|0,0:0|0:0,0:0:0:0: +382,170,52834,2,0,P|367:136|345:91,1,70,2|0,0:0|0:0,0:0:0:0: +261,55,53176,1,2,0:0:0:0: +242,158,53348,1,0,0:0:0:0: +242,158,53519,6,0,L|70:148,1,140,8|8,0:0|0:0,0:0:0:0: +196,338,54205,2,0,L|205:214,2,105,8|8|8,0:0|0:0|0:0,0:0:0:0: +296,304,54891,5,8,0:0:0:0: +296,304,55062,1,8,0:0:0:0: +327,203,55234,1,8,0:0:0:0: +327,203,55405,1,8,0:0:0:0: +355,102,55576,2,0,L|342:44,3,35,8|8|8|8,0:0|0:0|0:0|0:0,0:0:0:0: +365,18,55919,2,0,P|305:35|246:17,1,105,0|0,0:0|0:0,0:0:0:0: +230,40,56262,5,0,1:0:0:0: +285,129,56434,1,0,0:0:0:0: +223,213,56605,2,0,L|134:207,1,70,0|0,1:0|0:0,0:0:0:0: +66,267,56948,2,0,P|55:317|63:357,1,70,0|0,1:0|0:0,0:0:0:0: +156,369,57291,1,0,1:0:0:0: +156,369,57462,1,0,0:0:0:0: +318,236,57805,5,0,0:0:0:0: +250,155,57976,2,0,L|254:68,1,70,0|0,1:0|0:0,0:0:0:0: +349,42,58319,2,0,P|397:52|447:48,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: +253,85,58834,1,0,0:0:0:0: +169,21,59005,6,0,L|22:18,1,140,0|0,1:0|1:0,0:0:0:0: +9,121,59519,1,0,0:0:0:0: +53,216,59691,1,0,1:0:0:0: +53,216,59862,2,0,L|130:212,1,70,0|0,0:0|1:0,0:0:0:0: +220,252,60205,1,0,0:0:0:0: +173,158,60376,5,0,1:0:0:0: +173,158,60462,1,0,0:0:0:0: +173,158,60548,2,0,P|169:112|176:70,1,70,0|0,0:0|1:0,0:0:0:0: +253,22,60891,1,0,0:0:0:0: +302,114,61062,2,0,L|450:111,1,140,2|2,0:0|0:0,0:0:0:0: +503,196,61576,1,8,0:0:0:0: +503,196,61748,5,0,1:0:0:0: +411,247,61919,2,0,L|406:328,1,70,0|0,0:0|1:0,0:0:0:0: +317,372,62262,1,0,0:0:0:0: +216,340,62434,2,0,L|208:247,1,70,0|0,1:0|0:0,0:0:0:0: +250,173,62776,2,0,P|263:124|255:77,1,70,0|0,1:0|0:0,0:0:0:0: +58,51,63291,5,0,0:0:0:0: +18,148,63462,2,0,P|28:185|22:232,1,70,0|0,1:0|0:0,0:0:0:0: +17,321,63805,2,0,P|51:322|85:312,1,70,0|0,1:0|0:0,0:0:0:0: +172,254,64148,1,0,1:0:0:0: +172,254,64319,1,0,0:0:0:0: +240,334,64491,6,0,B|300:320|300:320|393:333,1,140,0|0,1:0|1:0,0:0:0:0: +466,274,65005,1,0,0:0:0:0: +408,186,65176,2,0,P|401:138|406:98,1,70,0|0,1:0|0:0,0:0:0:0: +298,101,65519,2,0,P|305:53|300:13,1,70,0|0,1:0|0:0,0:0:0:0: +303,31,65862,6,0,L|177:28,1,112,2|0,0:0|0:0,0:0:0:0: +124,79,66376,1,0,0:0:0:0: +200,113,66548,2,0,L|261:111,1,56 +134,161,66891,2,0,L|126:196,3,28,0|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: +89,224,67234,6,0,L|203:220,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: +239,260,67576,2,0,P|292:270|365:248,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: +390,227,67919,2,0,L|400:133,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: +372,69,68262,1,0,1:0:0:0: +251,140,68491,1,0,0:0:0:0: +251,140,68605,6,0,P|194:130|131:146,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: +105,180,68948,2,0,B|97:228|97:228|115:297,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: +166,310,69291,1,0,1:0:0:0: +275,223,69519,1,0,0:0:0:0: +332,263,69634,2,0,L|436:258,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: +499,298,69976,6,0,P|507:243|449:180,1,140,0|0,1:0|1:0,0:0:0:0: +328,131,70548,1,0,0:0:0:0: +278,180,70662,2,0,P|230:192|182:178,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: +122,155,71005,2,0,L|28:151,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: +11,219,71348,6,0,P|2:248|0:281,2,46.6666666666667,0|0|0,1:0|0:0|0:0,0:0:0:0: +28,151,71691,2,0,B|44:105|44:105|26:42,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: +88,20,72034,2,0,L|243:16,1,140,2|2,0:0|0:0,0:0:0:0: +227,16,72719,6,0,P|215:75|218:109,1,70,0|0,1:0|0:0,0:0:0:0: +299,148,73062,2,0,P|311:207|308:241,1,70,0|0,1:0|0:0,0:0:0:0: +260,308,73405,1,0,1:0:0:0: +260,308,73576,1,0,0:0:0:0: +361,278,73748,2,0,L|463:273,1,70,0|0,1:0|0:0,0:0:0:0: +494,74,74262,5,0,0:0:0:0: +430,157,74434,2,0,P|394:163|338:153,1,70,0|0,1:0|0:0,0:0:0:0: +280,93,74776,2,0,P|245:90|210:96,1,70,0|0,1:0|0:0,0:0:0:0: +121,152,75119,2,0,L|111:235,1,70,0|0,1:0|0:0,0:0:0:0: +61,328,75462,6,0,P|117:309|214:344,1,140,0|0,1:0|1:0,0:0:0:0: +296,303,75976,1,0,0:0:0:0: +213,237,76148,2,0,L|208:148,1,70,0|0,1:0|0:0,0:0:0:0: +307,204,76491,2,0,L|312:115,1,70,0|0,1:0|0:0,0:0:0:0: +270,59,76834,6,0,P|241:55|195:64,1,56,0|0,0:0|0:0,0:0:0:0: +172,130,77176,2,0,P|144:138|106:166,1,56 +65,215,77519,2,0,B|61:260|61:260|76:287|76:287|71:331,1,112 +71,322,78205,6,0,P|130:294|193:287,1,112 +288,161,78891,2,0,P|229:133|166:126,1,112 +22,84,79576,6,0,L|18:20,1,56 +100,46,79919,2,0,P|136:51|173:39,1,56,0|0,0:0|0:0,0:0:0:0: +234,19,80262,2,0,B|280:36|280:36|365:20,1,112,0|0,0:0|0:0,0:0:0:0: +341,24,80948,6,0,L|332:160,1,112 +333,135,81462,1,0,0:0:0:0: +277,198,81634,2,0,P|228:183|152:187,1,112,0|0,0:0|0:0,0:0:0:0: +64,315,82319,6,0,L|61:191,1,112 +61,203,82834,1,0,0:0:0:0: +107,133,83005,2,0,P|119:105|82:13,1,112,0|0,0:0|0:0,0:0:0:0: +100,28,83691,6,0,L|180:24,1,56,2|0,0:0|0:0,0:0:0:0: +224,74,84034,1,0,0:0:0:0: +224,74,84205,1,0,0:0:0:0: +289,21,84376,2,0,P|325:17|367:28,1,56,2|0,0:0|0:0,0:0:0:0: +369,100,84719,2,0,P|403:111|436:139,1,56,2|0,0:0|0:0,0:0:0:0: +481,208,85062,6,0,L|414:204,1,56,2|0,0:0|0:0,0:0:0:0: +367,266,85405,1,0,0:0:0:0: +367,266,85576,1,0,0:0:0:0: +307,206,85748,2,0,P|298:175|299:138,1,56,2|0,0:0|0:0,0:0:0:0: +213,154,86091,2,0,P|217:122|234:88,1,56,2|0,0:0|0:0,0:0:0:0: +168,18,86434,6,0,B|87:16|87:16|123:49,1,112,2|0,0:0|0:0,0:0:0:0: +81,116,86948,1,0,0:0:0:0: +117,191,87119,2,0,P|155:198|188:192,1,56,2|0,0:0|0:0,0:0:0:0: +230,256,87462,2,0,P|257:249|285:250,1,56,2|0,0:0|0:0,0:0:0:0: +359,290,87805,5,2,0:0:0:0: +359,290,87976,1,0,0:0:0:0: +389,211,88148,1,0,0:0:0:0: +389,211,88319,1,0,0:0:0:0: +466,177,88491,2,0,B|483:139|483:139|472:54,2,112,2|2|4,0:0|0:0|0:3,0:0:0:0: +185,144,100148,6,0,P|137:124|63:139,1,112,4|2,1:2|0:0,0:0:0:0: +35,189,100662,1,2,0:0:0:0: +35,189,100834,2,0,L|27:318,1,112,2|2,0:0|0:0,0:0:0:0: +164,352,101519,1,2,0:3:0:0: +297,294,101862,2,0,L|428:300,2,112,2|2|2,0:0|0:0|0:0,0:0:0:0: +281,222,102719,1,2,0:0:0:0: +305,153,102891,5,2,0:3:0:0: +225,274,103234,2,0,P|183:292|107:272,2,112,2|2|2,0:0|0:0|0:0,0:0:0:0: +305,153,104262,1,2,0:3:0:0: +186,67,104605,2,0,B|148:52|148:52|54:66,2,112,2|2|2,0:0|0:0|0:0,0:0:0:0: +256,48,105462,1,2,0:0:0:0: +326,30,105634,5,2,0:3:0:0: +428,132,105976,2,0,L|434:266,2,112,2|2|2,0:0|0:0|0:0,0:0:0:0: +308,216,107005,1,2,0:3:0:0: +189,131,107348,2,0,P|147:120|77:142,2,112,2|2|2,0:0|0:0|0:0,0:0:0:0: +230,190,108205,1,2,0:0:0:0: +233,262,108376,6,0,L|239:322,1,56,2|0,0:3|0:0,0:0:0:0: +311,325,108719,2,0,L|316:269,1,56,2|2,0:0|0:0,0:0:0:0: +369,219,109062,2,0,L|361:107,2,112,2|2|0,0:0|0:0|0:0,0:0:0:0: +81,124,110776,2,0,L|92:77,3,28,0|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: +77,52,111119,6,0,L|238:47,1,140,2|2,0:0|0:0,0:0:0:0: +290,102,111634,1,0,0:0:0:0: +349,33,111805,2,0,L|435:29,1,70,2|0,0:0|0:0,0:0:0:0: +439,118,112148,2,0,L|518:150,1,70,2|0,0:0|0:0,0:0:0:0: +426,309,112662,5,0,0:0:0:0: +379,230,112834,2,0,P|338:227|295:238,1,70,2|0,0:0|0:0,0:0:0:0: +237,286,113176,1,2,0:0:0:0: +237,286,113348,1,0,0:0:0:0: +172,221,113519,2,0,L|165:128,1,70,2|0,0:0|0:0,0:0:0:0: +166,151,114034,5,0,0:0:0:0: +237,94,114205,2,0,P|278:84|334:88,1,70,2|0,0:0|0:0,0:0:0:0: +375,142,114548,2,0,P|409:143|443:152,1,70,2|0,0:0|0:0,0:0:0:0: +496,226,114891,2,0,L|502:313,1,70,2|0,0:0|0:0,0:0:0:0: +434,358,115234,5,2,0:0:0:0: +434,358,115319,1,0,0:0:0:0: +434,358,115405,2,0,L|333:347,2,70,0|2|0,0:0|0:0|0:0,0:0:0:0: +398,273,115919,2,0,L|411:117,1,140,2|2,0:0|0:0,0:0:0:0: +255,34,116605,6,0,P|211:59|119:19,1,140,2|2,0:0|0:0,0:0:0:0: +45,68,117119,1,0,0:0:0:0: +108,133,117291,2,0,L|115:214,1,70,2|0,0:0|0:0,0:0:0:0: +197,240,117634,2,0,L|190:321,1,70,2|0,0:0|0:0,0:0:0:0: +363,369,118148,5,0,0:0:0:0: +332,283,118319,2,0,P|329:226|345:193,1,70,2|0,0:0|0:0,0:0:0:0: +386,140,118662,1,2,0:0:0:0: +386,140,118834,1,0,0:0:0:0: +331,67,119005,2,0,L|239:63,1,70,2|0,0:0|0:0,0:0:0:0: +261,63,119519,5,0,0:0:0:0: +198,129,119691,2,0,P|159:137|111:126,1,70,2|0,0:0|0:0,0:0:0:0: +68,201,120034,2,0,P|60:240|71:288,1,70,2|0,0:0|0:0,0:0:0:0: +128,333,120376,2,0,L|221:325,1,70,2|0,0:0|0:0,0:0:0:0: +282,293,120719,5,2,0:0:0:0: +282,293,121062,1,2,0:0:0:0: +282,293,121405,1,2,0:0:0:0: +282,293,121748,2,0,L|383:298,1,78.7500030040742,2|0,0:0|0:0,0:0:0:0: +403,281,122091,6,0,L|395:114,1,140,0|0,1:0|0:0,0:0:0:0: +221,91,122776,2,0,L|214:230,1,140,0|0,0:0|0:0,0:0:0:0: +354,347,123462,2,0,P|389:364|474:287,1,140,0|0,0:0|0:0,0:0:0:0: +329,202,124148,2,0,P|294:185|209:262,1,140,0|0,0:0|0:0,0:0:0:0: +68,341,124834,6,0,L|59:269,1,70,0|0,0:0|0:0,0:0:0:0: +139,313,125176,2,0,L|130:241,1,70,0|0,0:0|0:0,0:0:0:0: +211,286,125519,2,0,L|202:214,1,70,0|0,0:0|0:0,0:0:0:0: +282,260,125862,2,0,L|273:188,1,70,0|0,0:0|0:0,0:0:0:0: +192,108,126205,6,0,P|162:100|107:103,1,70 +172,192,126548,2,0,P|142:181|87:179,1,70 +146,274,126891,2,0,P|117:260|63:254,1,70 +110,354,127234,2,0,P|83:338|29:327,1,70 +150,279,127576,5,0,0:0:0:0: +150,279,127748,1,0,0:0:0:0: +242,327,127919,1,0,0:0:0:0: +242,327,128091,1,0,0:0:0:0: +285,232,128262,2,0,L|379:227,1,70,0|0,0:0|0:0,0:0:0:0: +422,166,128605,2,0,L|491:169,1,70,0|0,0:0|0:0,0:0:0:0: +441,76,128948,5,0,0:0:0:0: +441,76,129119,1,0,0:0:0:0: +388,150,129291,1,0,0:0:0:0: +388,150,129462,1,0,0:0:0:0: +338,73,129634,2,0,P|286:69|238:75,1,70,0|0,0:0|0:0,0:0:0:0: +210,140,129976,2,0,P|175:141|140:137,1,70,0|0,0:0|0:0,0:0:0:0: +79,205,130319,6,0,L|68:260,3,38.5000011749268,0|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: +108,267,130662,2,0,L|115:319,3,38.5000011749268,0|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: +145,334,131005,6,0,P|169:324|197:323,3,41.9999987182618,0|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: +220,339,131348,2,0,P|237:335|263:321,3,41.9999987182618,0|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: +289,300,131691,6,0,L|294:258,14,22.7499989585877,0|0|0|0|0|0|0|0|0|0|0|0|0|0|0,0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0,0:0:0:0: +314,240,132376,6,0,B|356:235|356:235|374:221|374:221|433:213,1,105.000004005432,0|0,0:0|0:0,0:0:0:0: +444,142,132891,1,0,0:0:0:0: +444,142,132976,1,0,0:0:0:0: +444,142,133062,6,0,B|461:90|461:90|446:-12,1,140,4|0,1:2|1:0,0:0:0:0: +238,15,133748,1,0,1:0:0:0: +181,103,133919,1,0,0:0:0:0: +181,103,134005,2,0,L|310:99,1,105,0|0,0:0|0:0,0:0:0:0: +366,167,134434,6,0,P|378:224|363:254,1,70,0|0,1:0|0:0,0:0:0:0: +276,270,134776,2,0,P|262:326|236:347,1,70,0|0,1:0|0:0,0:0:0:0: +151,356,135119,1,0,1:0:0:0: +61,300,135291,1,0,0:0:0:0: +164,254,135462,1,0,1:0:0:0: +164,254,135548,2,0,L|174:156,1,70 +195,162,135805,6,0,P|243:178|290:168,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: +148,67,136319,1,0,0:0:0:0: +148,67,136491,1,0,1:0:0:0: +247,98,136662,1,0,0:0:0:0: +247,98,136748,2,0,B|286:80|286:80|372:100,1,105,0|0,0:0|0:0,0:0:0:0: +432,154,137176,6,0,L|437:294,2,140,0|0|0,1:0|1:0|1:0,0:0:0:0: +307,324,138205,2,0,P|282:331|245:328,3,35,0|0|0|0,1:0|0:0|0:0|0:0,0:0:0:0: +246,309,138548,6,0,P|201:298|114:358,1,140,0|0,1:0|1:0,0:0:0:0: +19,157,139234,1,0,1:0:0:0: +113,112,139405,1,0,0:0:0:0: +113,112,139491,2,0,P|153:134|236:108,1,105,0|0,0:0|0:0,0:0:0:0: +310,159,139919,6,0,P|276:209|172:226,1,140,0|0,1:0|1:0,0:0:0:0: +387,334,140605,2,0,L|381:238,1,70,0|0,1:0|0:0,0:0:0:0: +450,183,140948,1,0,1:0:0:0: +450,183,141034,2,0,L|456:87,1,70,0|0,0:0|0:0,0:0:0:0: +434,85,141291,6,0,P|399:71|337:77,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: +468,292,141976,1,0,1:0:0:0: +377,238,142148,1,0,0:0:0:0: +377,238,142234,2,0,P|327:245|261:229,1,105 +178,185,142662,5,2,0:0:0:0: +178,185,143005,1,2,0:0:0:0: +178,185,143348,1,0,0:0:0:0: +178,185,143691,2,0,L|171:266,1,70,0|0,0:0|0:0,0:0:0:0: +171,254,144034,6,0,P|224:241|320:257,1,140,4|0,1:2|1:0,0:0:0:0: +408,282,144548,1,0,0:0:0:0: +480,205,144719,1,0,1:0:0:0: +433,116,144891,1,0,0:0:0:0: +433,116,144976,2,0,L|443:2,1,105,0|0,0:0|0:0,0:0:0:0: +337,69,145405,6,0,P|303:79|242:60,1,70,0|0,1:0|0:0,0:0:0:0: +172,116,145748,2,0,L|86:103,1,70,0|0,1:0|0:0,0:0:0:0: +38,188,146091,1,0,1:0:0:0: +38,188,146262,1,0,0:0:0:0: +110,264,146434,1,0,1:0:0:0: +110,264,146519,2,0,L|196:251,1,70,0|0,0:0|0:0,0:0:0:0: +208,269,146776,6,0,P|246:279|300:266,1,70,0|0,1:0|0:0,0:0:0:0: +369,227,147119,1,0,1:0:0:0: +369,227,147462,1,0,1:0:0:0: +312,138,147634,1,0,0:0:0:0: +312,138,147719,2,0,P|302:86|316:15,1,105,0|0,0:0|0:0,0:0:0:0: +210,71,148148,6,0,B|159:84|159:84|45:57,2,140,0|0|0,1:0|1:0|1:0,0:0:0:0: +410,132,149176,2,0,P|421:151|426:191,3,35,0|0|0|0,1:0|0:0|0:0|0:0,0:0:0:0: +458,203,149519,6,0,P|408:195|334:253,1,140,0|0,1:0|1:0,0:0:0:0: +232,232,150034,1,0,0:0:0:0: +309,162,150205,1,0,1:0:0:0: +192,144,150376,1,0,0:0:0:0: +192,144,150462,2,0,L|188:19,1,105,0|0,0:0|0:0,0:0:0:0: +112,155,150891,6,0,P|139:204|239:212,1,140,0|0,1:0|1:0,0:0:0:0: +216,221,151405,1,0,0:0:0:0: +296,288,151576,2,0,L|398:280,1,70,0|0,1:0|0:0,0:0:0:0: +445,213,151919,1,0,1:0:0:0: +445,213,152005,2,0,L|452:111,1,70,0|0,0:0|0:0,0:0:0:0: +434,113,152262,6,0,P|428:66|434:17,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: +352,180,152776,1,0,0:0:0:0: +352,180,152948,2,0,B|287:163|287:163|183:186,1,140,0|0,1:0|1:0,0:0:0:0: +215,178,153634,6,0,P|163:175|121:181,1,70,0|0,1:0|0:0,0:0:0:0: +106,274,153976,2,0,L|200:277,1,70,0|0,1:0|0:0,0:0:0:0: +264,329,154319,1,0,1:0:0:0: +264,329,154662,1,0,0:0:0:0: +360,286,154834,2,0,L|365:255,2,17.5,0|0|0,0:0|0:0|0:0,0:0:0:0: +376,312,155005,6,0,L|542:302,1,140,4|0,1:2|1:0,0:0:0:0: +460,213,155519,1,0,0:0:0:0: +460,213,155691,2,0,L|466:132,1,70,0|0,1:0|0:0,0:0:0:0: +363,113,156034,2,0,L|357:32,1,70,0|0,1:0|0:0,0:0:0:0: +253,34,156376,6,0,P|224:40|168:26,1,70,0|0,1:0|0:0,0:0:0:0: +101,98,156719,1,0,1:0:0:0: +101,98,156891,1,0,0:0:0:0: +190,152,157062,2,0,L|195:247,1,70,0|0,1:0|0:0,0:0:0:0: +290,274,157405,2,0,P|303:225|295:182,1,70,0|0,1:0|0:0,0:0:0:0: +271,105,157748,6,0,B|354:84|354:84|425:103,1,140,0|0,1:0|1:0,0:0:0:0: +484,169,158262,1,0,0:0:0:0: +484,169,158434,2,0,P|492:218|484:257,1,70,0|0,1:0|0:0,0:0:0:0: +396,286,158776,2,0,P|388:335|396:374,1,70,0|0,1:0|0:0,0:0:0:0: +285,341,159119,5,0,1:0:0:0: +285,341,159291,1,0,0:0:0:0: +210,267,159462,2,0,L|214:175,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: +129,334,159976,1,0,0:0:0:0: +51,262,160148,2,0,P|40:217|43:169,1,70,0|0,1:0|0:0,0:0:0:0: +125,104,160491,6,0,L|298:97,1,140,0|0,1:0|1:0,0:0:0:0: +341,170,161005,1,0,0:0:0:0: +373,70,161176,2,0,L|377:-8,1,70,0|0,1:0|0:0,0:0:0:0: +468,51,161519,1,0,1:0:0:0: +468,51,161691,1,0,0:0:0:0: +424,146,161862,6,0,P|419:203|429:235,1,70,0|0,1:0|0:0,0:0:0:0: +467,309,162205,2,0,L|388:311,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: +421,214,162719,1,0,0:0:0:0: +332,271,162891,2,0,P|321:309|343:361,1,70,0|0,1:0|0:0,0:0:0:0: +225,359,163234,6,0,P|162:331|86:357,1,140,0|0,1:0|1:0,0:0:0:0: +26,270,163748,1,0,0:0:0:0: +26,270,163919,2,0,L|30:169,1,70,0|0,1:0|0:0,0:0:0:0: +112,136,164262,2,0,L|108:35,1,70,0|0,1:0|0:0,0:0:0:0: +207,103,164605,6,0,P|272:122|351:76,2,140,0|0|0,1:0|1:0|1:0,0:0:0:0: +291,296,165634,1,0,1:0:0:0: +364,221,165805,1,8,0:0:0:0: +364,221,165891,1,0,0:0:0:0: +364,221,165976,6,0,P|373:159|328:87,1,140,4|0,1:2|1:0,0:0:0:0: +175,93,166662,2,0,P|140:152|148:221,1,140,0|0,1:0|1:0,0:0:0:0: +256,356,167348,2,0,L|256:212,1,140,0|0,1:0|1:0,0:0:0:0: +152,88,168034,1,0,1:0:0:0: +360,88,168376,1,0,1:0:0:0: +284,300,168719,6,0,P|216:308|157:273,1,140,0|0,1:0|1:0,0:0:0:0: +157,111,169405,2,0,P|216:76|285:84,1,140,0|0,1:0|1:0,0:0:0:0: +420,192,170091,2,0,L|276:192,1,140,0|0,1:0|1:0,0:0:0:0: +152,296,170777,1,0,1:0:0:0: +152,88,171119,1,0,1:0:0:0: +152,88,171291,1,0,0:0:0:0: +152,88,171462,6,0,L|264:88,1,105.000004005432,6|2,0:0|0:0,0:0:0:0: +392,176,172148,2,0,L|280:176,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: +160,272,172834,2,0,L|272:272,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: +400,352,173519,2,0,L|288:352,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: +105,324,174205,6,0,P|85:285|89:219,1,90.9999958343508,2|2,0:0|0:0,0:0:0:0: +95,219,174719,1,0,0:0:0:0: +103,197,174891,2,0,L|99:88,1,90.9999958343508,2|2,0:0|0:0,0:0:0:0: +240,73,175576,5,0,0:0:0:0: +265,106,175919,1,0,0:0:0:0: +279,136,176262,2,0,B|314:129|351:139|351:139|329:100,1,112"; + } +} From a876beeadaa5123a0ae148eb9e16a8a29765d96a Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Fri, 12 Oct 2018 21:34:24 +0200 Subject: [PATCH 0168/2854] Variable adjustments --- osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs index a049248021..2638d5bf78 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Mods var h = d.HitObject; - float rescale = 2; + const float rescale = 2; switch (drawable) { @@ -76,9 +76,9 @@ namespace osu.Game.Rulesets.Osu.Mods case DrawableRepeatPoint rp: if (!rp.IsFirstRepeat) break; - var origSizeRP = rp.Size; + var origSizeRp = rp.Size; using (d.BeginAbsoluteSequence(h.StartTime - h.TimePreempt + 1, true)) - rp.ResizeTo(origSizeRP * rescale).ResizeTo(origSizeRP, h.TimePreempt); + rp.ResizeTo(origSizeRp * rescale).ResizeTo(origSizeRp, h.TimePreempt); break; } } From f087c43b49c10fd99a0d3822a97c9a4dc6ae7025 Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Fri, 12 Oct 2018 22:31:35 +0200 Subject: [PATCH 0169/2854] Nvm about the new TestCase :^) --- osu.Game.Tests/Visual/TestCaseDeflate.cs | 637 ----------------------- 1 file changed, 637 deletions(-) delete mode 100644 osu.Game.Tests/Visual/TestCaseDeflate.cs diff --git a/osu.Game.Tests/Visual/TestCaseDeflate.cs b/osu.Game.Tests/Visual/TestCaseDeflate.cs deleted file mode 100644 index 8a24e2414c..0000000000 --- a/osu.Game.Tests/Visual/TestCaseDeflate.cs +++ /dev/null @@ -1,637 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using System.IO; -using System.Text; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets; -using Decoder = osu.Game.Beatmaps.Formats.Decoder; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Screens.Play; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Tests.Visual -{ - [Description("Player instantiated with an deflate mod.")] - public class TestCaseDeflate : TestCasePlayer - { - protected override IBeatmap CreateBeatmap(Ruleset ruleset) - { - Beatmap beatmap; - using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) - using (var reader = new StreamReader(stream)) - beatmap = Decoder.GetDecoder(reader).Decode(reader); - beatmap.BeatmapInfo.Ruleset = ruleset.RulesetInfo; - beatmap.BeatmapInfo.BaseDifficulty.CircleSize = 4; - return beatmap; - } - - protected override Player CreatePlayer(Ruleset ruleset) - { - Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod(), ruleset.GetAllMods().First(mod => mod is OsuModDeflate) }); - return new ScoreAccessiblePlayer - { - AllowPause = false, - AllowLeadIn = false, - AllowResults = false, - }; - } - - protected override bool ContinueCondition(Player player) => base.ContinueCondition(player) && ((ScoreAccessiblePlayer)player).ScoreProcessor.TotalScore > 0; - - private class ScoreAccessiblePlayer : Player - { - public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; - } - - private const string test_beatmap_data = - @"osu file format v14 - -[General] -AudioFilename: R4V3 B0Y - S3RL Feat Krystal.mp3 -AudioLeadIn: 0 -PreviewTime: 56176 -Countdown: 0 -SampleSet: Soft -StackLeniency: 0.7 -Mode: 0 -LetterboxInBreaks: 0 -WidescreenStoryboard: 1 - -[Editor] -Bookmarks: 12382,23354,34325,45297,56268,67240,78211,89182,100154,111125,122097,128605,133068,144040,155011,165982,175582 -DistanceSpacing: 1.5 -BeatDivisor: 4 -GridSize: 4 -TimelineZoom: 3.799998 - -[Metadata] -Title:R4V3 B0Y -TitleUnicode:R4V3 B0Y -Artist:S3RL feat Krystal -ArtistUnicode:S3RL feat Krystal -Creator:DeRandom Otaku -Version:FCL's H4RD -Source: -Tags:happy hardcore EMFA music rave boy Shmiklak FCL ByBy ByBy13 ByBy_Chan Marvollo M_a_r_v_o_l_l_o -BeatmapID:1056070 -BeatmapSetID:481451 - -[Difficulty] -HPDrainRate:5 -CircleSize:4 -OverallDifficulty:6 -ApproachRate:7.5 -SliderMultiplier:1.4 -SliderTickRate:1 - -[Events] -//Background and Video events -0,0,'BG.jpg',0,0 -//Break Periods -2,89376,99323 -//Storyboard Layer 0 (Background) -//Storyboard Layer 1 (Fail) -//Storyboard Layer 2 (Pass) -//Storyboard Layer 3 (Foreground) -//Storyboard Sound Samples - -[TimingPoints] -34,342.857142857143,4,1,1,100,1,0 -34,-133.333333333333,4,1,1,100,0,0 -1405,-133.333333333333,4,2,1,70,0,0 -12376,-125,4,2,2,70,0,0 -21976,-100,4,2,2,45,0,0 -23348,-100,4,2,2,85,0,0 -26091,-100,4,2,2,85,0,0 -31576,-100,4,2,2,85,0,0 -34319,-100,4,2,2,85,0,0 -37062,-100,4,2,2,85,0,0 -42462,-100,4,2,2,5,0,0 -42548,-100,4,2,2,85,0,0 -43919,-100,4,2,2,65,0,0 -45291,-100,4,2,2,50,0,0 -50691,-100,4,2,2,5,0,0 -50776,-100,4,2,2,50,0,0 -56262,-100,4,2,2,90,0,1 -65862,-125,4,2,2,90,0,0 -67234,-100,4,2,2,90,0,1 -72719,-100,4,2,2,90,0,1 -76834,-125,4,2,2,45,0,0 -78205,-125,4,2,0,50,0,0 -100148,-125,4,2,2,50,0,0 -100491,-125,4,2,0,50,0,0 -111119,-100,4,2,2,50,0,0 -116091,-100,4,2,2,10,0,0 -116262,-100,4,2,2,50,0,0 -116434,-100,4,2,2,10,0,0 -116605,-100,4,2,2,50,0,0 -121748,-133.333333333333,4,2,2,50,0,0 -122091,-100,4,2,2,50,0,0 -127576,-100,4,2,2,50,0,0 -130319,-90.9090909090909,4,2,2,50,0,0 -131005,-83.3333333333333,4,2,2,50,0,0 -131691,-76.9230769230769,4,2,2,50,0,0 -132376,-133.333333333333,4,2,2,50,0,0 -133062,-100,4,2,2,90,0,1 -135719,-100,4,2,2,5,0,1 -135805,-100,4,2,2,90,0,1 -141205,-100,4,2,2,5,0,1 -141291,-100,4,2,2,90,0,1 -142662,-100,4,2,2,70,0,0 -143691,-100,4,2,2,40,0,0 -144034,-100,4,2,2,90,0,1 -146691,-100,4,2,2,5,0,1 -146776,-100,4,2,2,90,0,1 -152176,-100,4,2,2,5,0,1 -152262,-100,4,2,2,90,0,1 -154662,-100,4,2,2,60,0,1 -155005,-100,4,2,2,75,0,0 -165976,-100,4,2,2,75,0,0 -171462,-133.333333333333,4,2,2,60,0,0 -174205,-153.846153846153,4,2,2,50,0,0 -174891,-153.846153846154,4,2,2,40,0,0 -175576,-100,4,2,2,25,0,0 -176262,-125,4,2,2,25,0,0 -176605,-125,4,2,2,15,0,0 - - -[Colours] - Combo1 : 0,255,255 -Combo2 : 17,255,17 -Combo3 : 0,128,192 -Combo4 : 124,0,249 - -[HitObjects] -138,122,34,6,0,L|133:229,3,105.000004005432,0|0|0|0,2:0|2:0|2:0|2:0,2:2:0:0: -133,226,1405,6,0,L|248:220,1,105.000004005432,2|2,1:2|0:0,0:0:0:0: -339,341,2091,2,0,L|222:338,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: -133,226,2776,2,0,P|128:187|129:158,1,52.5000020027162,2|0,0:0|0:0,0:0:0:0: -188,123,3119,2,0,P|193:84|190:55,1,52.5000020027162,2|0,0:0|0:0,0:0:0:0: -262,34,3462,2,0,P|290:62|366:69,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: -431,74,3976,1,0,0:0:0:0: -496,117,4148,6,0,L|503:229,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: -456,286,4662,1,0,0:0:0:0: -408,349,4834,2,0,L|296:356,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: -172,266,5519,2,0,P|155:216|165:148,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: -206,101,6034,1,0,0:0:0:0: -239,172,6205,2,0,L|361:162,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: -494,260,6891,6,0,B|443:252|443:252|386:267,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: -324,308,7405,1,0,0:0:0:0: -248,287,7576,2,0,L|241:227,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: -169,202,7919,2,0,L|176:142,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: -239,104,8262,2,0,P|282:119|345:101,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: -416,92,8776,1,0,0:0:0:0: -484,132,8948,2,0,L|491:195,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: -445,249,9291,2,0,L|452:312,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: -388,349,9634,6,0,P|354:328|289:335,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: -216,360,10148,1,0,0:0:0:0: -143,328,10319,2,0,L|72:315,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: -102,240,10662,2,0,L|173:227,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: -224,195,11005,5,2,0:0:0:0: -160,148,11176,1,0,0:0:0:0: -233,120,11348,1,2,0:0:0:0: -172,69,11519,1,0,0:0:0:0: -247,45,11691,2,0,P|285:39|314:43,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: -355,95,12034,2,0,P|378:105|399:121,1,52.5000020027161,2|0,0:0|0:0,0:0:0:0: -482,162,12376,6,0,P|450:190|445:261,1,112,2|2,0:0|0:0,0:0:0:0: -298,342,13062,2,0,P|296:287|259:247,1,112,2|2,0:0|0:0,0:0:0:0: -132,135,13748,2,0,L|127:62,1,56,2|0,0:0|0:0,0:0:0:0: -209,57,14091,2,0,L|235:-11,1,56,2|0,0:0|0:0,0:0:0:0: -304,41,14434,2,0,P|348:52|380:44,2,56,2|0|2,0:0|0:0|0:0,0:0:0:0: -272,119,14948,1,0,0:0:0:0: -335,173,15119,6,0,L|340:298,1,112,2|2,0:0|0:0,0:0:0:0: -274,339,15634,1,0,0:0:0:0: -216,278,15805,2,0,B|165:262|165:262|86:278,1,112,2|2,0:0|0:0,0:0:0:0: -5,139,16491,2,0,B|56:123|56:123|135:139,1,112,2|2,0:0|0:0,0:0:0:0: -189,171,17005,1,0,0:0:0:0: -257,123,17176,2,0,L|379:117,1,112,2|2,0:0|0:0,0:0:0:0: -434,171,17691,1,0,0:0:0:0: -434,171,17776,1,0,0:0:0:0: -434,171,17862,6,0,P|450:218|426:288,1,112,2|2,0:0|0:0,0:0:0:0: -356,308,18376,1,0,0:0:0:0: -288,261,18548,2,0,L|207:258,1,56,2|0,0:0|0:0,0:0:0:0: -170,316,18891,2,0,L|89:319,1,56,2|0,0:0|0:0,0:0:0:0: -38,234,19234,2,0,P|110:237|160:210,1,112,2|2,0:0|0:0,0:0:0:0: -179,144,19748,1,0,0:0:0:0: -104,105,19919,2,0,L|100:31,1,56,2|0,0:0|0:0,0:0:0:0: -179,18,20262,2,0,L|210:8,3,28,2|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: -247,8,20605,6,0,P|236:48|268:122,1,112,2|2,0:0|0:0,0:0:0:0: -311,178,21119,1,0,0:0:0:0: -288,259,21291,2,0,L|286:330,1,56,2|0,0:0|0:0,0:0:0:0: -363,349,21634,2,0,L|361:293,1,56,2|0,0:0|0:0,0:0:0:0: -408,231,21976,5,0,0:0:0:0: -462,125,23005,2,0,L|469:80,3,35 -499,48,23348,6,0,L|348:53,1,140,4|0,1:2|1:0,0:0:0:0: -178,161,24034,1,0,1:0:0:0: -244,241,24205,1,0,0:0:0:0: -244,241,24291,2,0,L|365:231,1,105,0|0,0:0|0:0,0:0:0:0: -434,292,24719,2,0,P|423:335|439:379,1,70,0|0,1:0|0:0,0:0:0:0: -325,333,25062,2,0,P|329:298|319:265,1,70,0|0,1:0|0:0,0:0:0:0: -250,185,25405,1,0,1:0:0:0: -308,135,25576,1,0,0:0:0:0: -213,111,25748,2,0,L|203:64,2,35,0|0|0,1:0|0:0|0:0,0:0:0:0: -146,183,26091,6,0,L|44:180,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: -202,370,26776,1,0,1:0:0:0: -253,288,26948,1,0,0:0:0:0: -253,288,27034,2,0,B|236:237|236:237|246:176,1,105,0|0,0:0|0:0,0:0:0:0: -318,112,27462,2,0,L|466:102,2,140,0|0|0,1:0|1:0|1:0,0:0:0:0: -139,224,28491,2,0,P|110:228|77:229,3,35,0|0|0|0,1:0|0:0|0:0|0:0,0:0:0:0: -69,267,28834,6,0,P|131:258|212:289,1,140,0|0,1:0|1:0,0:0:0:0: -402,217,29519,1,0,1:0:0:0: -303,180,29691,1,0,0:0:0:0: -303,180,29776,2,0,P|288:123|299:60,1,105,0|0,0:0|0:0,0:0:0:0: -200,26,30205,2,0,P|155:51|54:21,2,140,0|0|0,1:0|1:0|1:0,0:0:0:0: -166,125,31062,1,0,0:0:0:0: -242,197,31234,2,0,L|246:257,3,35,0|0|0|0,1:0|0:0|0:0|0:0,0:0:0:0: -293,248,31576,6,0,L|139:246,1,140,0|0,1:0|1:0,0:0:0:0: -66,307,32091,1,0,0:0:0:0: -71,202,32262,2,0,L|84:35,1,140,0|0,1:0|1:0,0:0:0:0: -289,23,32948,5,0,0:0:0:0: -218,53,33119,1,0,0:0:0:0: -176,118,33291,2,0,P|174:157|190:205,2,70,0|0|0,0:0|0:0|0:0,0:0:0:0: -176,118,34148,1,8,0:0:0:0: -265,172,34319,6,0,L|418:174,1,140,4|0,1:2|1:0,0:0:0:0: -440,273,34834,1,0,0:0:0:0: -337,253,35005,2,0,P|281:253|235:345,1,140,0|0,1:0|1:0,0:0:0:0: -232,315,35519,1,0,0:0:0:0: -130,341,35691,2,0,L|36:328,1,70,0|0,1:0|0:0,0:0:0:0: -95,231,36034,2,0,L|15:179,1,70,0|0,1:0|0:0,0:0:0:0: -110,119,36376,2,0,L|59:38,1,70,0|0,1:0|0:0,0:0:0:0: -185,0,36719,1,0,1:0:0:0: -185,0,36805,2,0,L|193:69,1,70,0|0,0:0|0:0,0:0:0:0: -224,85,37062,6,0,L|307:102,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: -262,183,37576,1,0,0:0:0:0: -185,253,37748,2,0,P|117:286|52:236,1,140,0|0,1:0|1:0,0:0:0:0: -116,166,38262,1,0,0:0:0:0: -185,253,38434,2,0,P|222:247|281:246,1,70,0|0,1:0|0:0,0:0:0:0: -344,299,38776,2,0,P|380:310|433:333,1,70,0|0,1:0|0:0,0:0:0:0: -500,267,39119,1,0,1:0:0:0: -379,96,39462,2,0,L|374:53,3,35,0|0|0|0,1:0|0:0|0:0|0:0,0:0:0:0: -395,12,39805,6,0,L|224:2,1,140,0|0,1:0|1:0,0:0:0:0: -172,69,40319,1,0,0:0:0:0: -267,112,40491,2,0,P|250:151|275:259,1,140,0|0,1:0|1:0,0:0:0:0: -264,244,41005,1,0,0:0:0:0: -182,309,41176,2,0,P|155:264|127:236,1,70,0|0,1:0|0:0,0:0:0:0: -69,176,41519,2,0,P|96:131|124:103,1,70,0|0,1:0|0:0,0:0:0:0: -202,71,41862,1,0,1:0:0:0: -179,173,42034,1,0,0:0:0:0: -137,37,42205,1,0,1:0:0:0: -137,37,42291,2,0,L|35:33,1,70,0|0,0:0|0:0,0:0:0:0: -23,63,42548,6,0,B|6:161|6:161|23:218,1,140,0|0,1:0|1:0,0:0:0:0: -62,295,43062,1,0,0:0:0:0: -141,226,43234,1,0,1:0:0:0: -168,348,43405,1,0,0:0:0:0: -168,348,43491,2,0,L|295:355,1,105,0|0,0:0|0:0,0:0:0:0: -367,309,43919,1,0,0:0:0:0: -201,89,45291,6,0,L|279:83,1,70,2|0,0:0|0:0,0:0:0:0: -201,162,45634,2,0,L|279:156,1,70,2|0,0:0|0:0,0:0:0:0: -202,235,45976,2,0,L|280:229,1,70,2|0,0:0|0:0,0:0:0:0: -202,308,46319,2,0,L|280:302,1,70,2|0,0:0|0:0,0:0:0:0: -411,345,46662,6,0,P|368:344|311:362,1,70,2|0,0:0|0:0,0:0:0:0: -399,261,47005,2,0,P|360:241|301:234,1,70,2|0,0:0|0:0,0:0:0:0: -427,187,47348,2,0,P|400:153|349:121,1,70,2|0,0:0|0:0,0:0:0:0: -484,134,47691,2,0,P|473:91|441:41,1,70,2|0,0:0|0:0,0:0:0:0: -348,30,48034,6,0,B|308:39|308:39|264:31,1,70,2|0,0:0|0:0,0:0:0:0: -205,108,48376,2,0,B|245:117|245:117|289:109,1,70,2|0,0:0|0:0,0:0:0:0: -345,188,48719,2,0,B|305:197|305:197|261:189,1,70,2|0,0:0|0:0,0:0:0:0: -199,263,49062,2,0,B|239:272|239:272|283:264,1,70,2|0,0:0|0:0,0:0:0:0: -345,188,49405,6,0,L|354:101,1,70,2|0,0:0|0:0,0:0:0:0: -252,150,49748,2,0,L|243:63,1,70,2|0,0:0|0:0,0:0:0:0: -146,118,50091,2,0,P|123:75|90:43,1,70,2|0,0:0|0:0,0:0:0:0: -8,28,50434,2,0,L|6:177,1,140,10|2,0:0|0:0,0:0:0:0: -93,242,50948,5,0,0:0:0:0: -93,242,51119,2,0,L|190:238,1,70,2|0,0:0|0:0,0:0:0:0: -239,311,51462,2,0,L|308:313,1,70,2|0,0:0|0:0,0:0:0:0: -393,250,51805,1,2,0:0:0:0: -429,348,51976,1,0,0:0:0:0: -429,348,52319,5,0,0:0:0:0: -486,259,52491,2,0,P|487:222|486:172,1,70,2|0,0:0|0:0,0:0:0:0: -382,170,52834,2,0,P|367:136|345:91,1,70,2|0,0:0|0:0,0:0:0:0: -261,55,53176,1,2,0:0:0:0: -242,158,53348,1,0,0:0:0:0: -242,158,53519,6,0,L|70:148,1,140,8|8,0:0|0:0,0:0:0:0: -196,338,54205,2,0,L|205:214,2,105,8|8|8,0:0|0:0|0:0,0:0:0:0: -296,304,54891,5,8,0:0:0:0: -296,304,55062,1,8,0:0:0:0: -327,203,55234,1,8,0:0:0:0: -327,203,55405,1,8,0:0:0:0: -355,102,55576,2,0,L|342:44,3,35,8|8|8|8,0:0|0:0|0:0|0:0,0:0:0:0: -365,18,55919,2,0,P|305:35|246:17,1,105,0|0,0:0|0:0,0:0:0:0: -230,40,56262,5,0,1:0:0:0: -285,129,56434,1,0,0:0:0:0: -223,213,56605,2,0,L|134:207,1,70,0|0,1:0|0:0,0:0:0:0: -66,267,56948,2,0,P|55:317|63:357,1,70,0|0,1:0|0:0,0:0:0:0: -156,369,57291,1,0,1:0:0:0: -156,369,57462,1,0,0:0:0:0: -318,236,57805,5,0,0:0:0:0: -250,155,57976,2,0,L|254:68,1,70,0|0,1:0|0:0,0:0:0:0: -349,42,58319,2,0,P|397:52|447:48,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: -253,85,58834,1,0,0:0:0:0: -169,21,59005,6,0,L|22:18,1,140,0|0,1:0|1:0,0:0:0:0: -9,121,59519,1,0,0:0:0:0: -53,216,59691,1,0,1:0:0:0: -53,216,59862,2,0,L|130:212,1,70,0|0,0:0|1:0,0:0:0:0: -220,252,60205,1,0,0:0:0:0: -173,158,60376,5,0,1:0:0:0: -173,158,60462,1,0,0:0:0:0: -173,158,60548,2,0,P|169:112|176:70,1,70,0|0,0:0|1:0,0:0:0:0: -253,22,60891,1,0,0:0:0:0: -302,114,61062,2,0,L|450:111,1,140,2|2,0:0|0:0,0:0:0:0: -503,196,61576,1,8,0:0:0:0: -503,196,61748,5,0,1:0:0:0: -411,247,61919,2,0,L|406:328,1,70,0|0,0:0|1:0,0:0:0:0: -317,372,62262,1,0,0:0:0:0: -216,340,62434,2,0,L|208:247,1,70,0|0,1:0|0:0,0:0:0:0: -250,173,62776,2,0,P|263:124|255:77,1,70,0|0,1:0|0:0,0:0:0:0: -58,51,63291,5,0,0:0:0:0: -18,148,63462,2,0,P|28:185|22:232,1,70,0|0,1:0|0:0,0:0:0:0: -17,321,63805,2,0,P|51:322|85:312,1,70,0|0,1:0|0:0,0:0:0:0: -172,254,64148,1,0,1:0:0:0: -172,254,64319,1,0,0:0:0:0: -240,334,64491,6,0,B|300:320|300:320|393:333,1,140,0|0,1:0|1:0,0:0:0:0: -466,274,65005,1,0,0:0:0:0: -408,186,65176,2,0,P|401:138|406:98,1,70,0|0,1:0|0:0,0:0:0:0: -298,101,65519,2,0,P|305:53|300:13,1,70,0|0,1:0|0:0,0:0:0:0: -303,31,65862,6,0,L|177:28,1,112,2|0,0:0|0:0,0:0:0:0: -124,79,66376,1,0,0:0:0:0: -200,113,66548,2,0,L|261:111,1,56 -134,161,66891,2,0,L|126:196,3,28,0|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: -89,224,67234,6,0,L|203:220,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: -239,260,67576,2,0,P|292:270|365:248,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: -390,227,67919,2,0,L|400:133,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: -372,69,68262,1,0,1:0:0:0: -251,140,68491,1,0,0:0:0:0: -251,140,68605,6,0,P|194:130|131:146,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: -105,180,68948,2,0,B|97:228|97:228|115:297,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: -166,310,69291,1,0,1:0:0:0: -275,223,69519,1,0,0:0:0:0: -332,263,69634,2,0,L|436:258,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: -499,298,69976,6,0,P|507:243|449:180,1,140,0|0,1:0|1:0,0:0:0:0: -328,131,70548,1,0,0:0:0:0: -278,180,70662,2,0,P|230:192|182:178,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: -122,155,71005,2,0,L|28:151,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: -11,219,71348,6,0,P|2:248|0:281,2,46.6666666666667,0|0|0,1:0|0:0|0:0,0:0:0:0: -28,151,71691,2,0,B|44:105|44:105|26:42,1,93.3333333333333,0|0,1:0|0:0,0:0:0:0: -88,20,72034,2,0,L|243:16,1,140,2|2,0:0|0:0,0:0:0:0: -227,16,72719,6,0,P|215:75|218:109,1,70,0|0,1:0|0:0,0:0:0:0: -299,148,73062,2,0,P|311:207|308:241,1,70,0|0,1:0|0:0,0:0:0:0: -260,308,73405,1,0,1:0:0:0: -260,308,73576,1,0,0:0:0:0: -361,278,73748,2,0,L|463:273,1,70,0|0,1:0|0:0,0:0:0:0: -494,74,74262,5,0,0:0:0:0: -430,157,74434,2,0,P|394:163|338:153,1,70,0|0,1:0|0:0,0:0:0:0: -280,93,74776,2,0,P|245:90|210:96,1,70,0|0,1:0|0:0,0:0:0:0: -121,152,75119,2,0,L|111:235,1,70,0|0,1:0|0:0,0:0:0:0: -61,328,75462,6,0,P|117:309|214:344,1,140,0|0,1:0|1:0,0:0:0:0: -296,303,75976,1,0,0:0:0:0: -213,237,76148,2,0,L|208:148,1,70,0|0,1:0|0:0,0:0:0:0: -307,204,76491,2,0,L|312:115,1,70,0|0,1:0|0:0,0:0:0:0: -270,59,76834,6,0,P|241:55|195:64,1,56,0|0,0:0|0:0,0:0:0:0: -172,130,77176,2,0,P|144:138|106:166,1,56 -65,215,77519,2,0,B|61:260|61:260|76:287|76:287|71:331,1,112 -71,322,78205,6,0,P|130:294|193:287,1,112 -288,161,78891,2,0,P|229:133|166:126,1,112 -22,84,79576,6,0,L|18:20,1,56 -100,46,79919,2,0,P|136:51|173:39,1,56,0|0,0:0|0:0,0:0:0:0: -234,19,80262,2,0,B|280:36|280:36|365:20,1,112,0|0,0:0|0:0,0:0:0:0: -341,24,80948,6,0,L|332:160,1,112 -333,135,81462,1,0,0:0:0:0: -277,198,81634,2,0,P|228:183|152:187,1,112,0|0,0:0|0:0,0:0:0:0: -64,315,82319,6,0,L|61:191,1,112 -61,203,82834,1,0,0:0:0:0: -107,133,83005,2,0,P|119:105|82:13,1,112,0|0,0:0|0:0,0:0:0:0: -100,28,83691,6,0,L|180:24,1,56,2|0,0:0|0:0,0:0:0:0: -224,74,84034,1,0,0:0:0:0: -224,74,84205,1,0,0:0:0:0: -289,21,84376,2,0,P|325:17|367:28,1,56,2|0,0:0|0:0,0:0:0:0: -369,100,84719,2,0,P|403:111|436:139,1,56,2|0,0:0|0:0,0:0:0:0: -481,208,85062,6,0,L|414:204,1,56,2|0,0:0|0:0,0:0:0:0: -367,266,85405,1,0,0:0:0:0: -367,266,85576,1,0,0:0:0:0: -307,206,85748,2,0,P|298:175|299:138,1,56,2|0,0:0|0:0,0:0:0:0: -213,154,86091,2,0,P|217:122|234:88,1,56,2|0,0:0|0:0,0:0:0:0: -168,18,86434,6,0,B|87:16|87:16|123:49,1,112,2|0,0:0|0:0,0:0:0:0: -81,116,86948,1,0,0:0:0:0: -117,191,87119,2,0,P|155:198|188:192,1,56,2|0,0:0|0:0,0:0:0:0: -230,256,87462,2,0,P|257:249|285:250,1,56,2|0,0:0|0:0,0:0:0:0: -359,290,87805,5,2,0:0:0:0: -359,290,87976,1,0,0:0:0:0: -389,211,88148,1,0,0:0:0:0: -389,211,88319,1,0,0:0:0:0: -466,177,88491,2,0,B|483:139|483:139|472:54,2,112,2|2|4,0:0|0:0|0:3,0:0:0:0: -185,144,100148,6,0,P|137:124|63:139,1,112,4|2,1:2|0:0,0:0:0:0: -35,189,100662,1,2,0:0:0:0: -35,189,100834,2,0,L|27:318,1,112,2|2,0:0|0:0,0:0:0:0: -164,352,101519,1,2,0:3:0:0: -297,294,101862,2,0,L|428:300,2,112,2|2|2,0:0|0:0|0:0,0:0:0:0: -281,222,102719,1,2,0:0:0:0: -305,153,102891,5,2,0:3:0:0: -225,274,103234,2,0,P|183:292|107:272,2,112,2|2|2,0:0|0:0|0:0,0:0:0:0: -305,153,104262,1,2,0:3:0:0: -186,67,104605,2,0,B|148:52|148:52|54:66,2,112,2|2|2,0:0|0:0|0:0,0:0:0:0: -256,48,105462,1,2,0:0:0:0: -326,30,105634,5,2,0:3:0:0: -428,132,105976,2,0,L|434:266,2,112,2|2|2,0:0|0:0|0:0,0:0:0:0: -308,216,107005,1,2,0:3:0:0: -189,131,107348,2,0,P|147:120|77:142,2,112,2|2|2,0:0|0:0|0:0,0:0:0:0: -230,190,108205,1,2,0:0:0:0: -233,262,108376,6,0,L|239:322,1,56,2|0,0:3|0:0,0:0:0:0: -311,325,108719,2,0,L|316:269,1,56,2|2,0:0|0:0,0:0:0:0: -369,219,109062,2,0,L|361:107,2,112,2|2|0,0:0|0:0|0:0,0:0:0:0: -81,124,110776,2,0,L|92:77,3,28,0|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: -77,52,111119,6,0,L|238:47,1,140,2|2,0:0|0:0,0:0:0:0: -290,102,111634,1,0,0:0:0:0: -349,33,111805,2,0,L|435:29,1,70,2|0,0:0|0:0,0:0:0:0: -439,118,112148,2,0,L|518:150,1,70,2|0,0:0|0:0,0:0:0:0: -426,309,112662,5,0,0:0:0:0: -379,230,112834,2,0,P|338:227|295:238,1,70,2|0,0:0|0:0,0:0:0:0: -237,286,113176,1,2,0:0:0:0: -237,286,113348,1,0,0:0:0:0: -172,221,113519,2,0,L|165:128,1,70,2|0,0:0|0:0,0:0:0:0: -166,151,114034,5,0,0:0:0:0: -237,94,114205,2,0,P|278:84|334:88,1,70,2|0,0:0|0:0,0:0:0:0: -375,142,114548,2,0,P|409:143|443:152,1,70,2|0,0:0|0:0,0:0:0:0: -496,226,114891,2,0,L|502:313,1,70,2|0,0:0|0:0,0:0:0:0: -434,358,115234,5,2,0:0:0:0: -434,358,115319,1,0,0:0:0:0: -434,358,115405,2,0,L|333:347,2,70,0|2|0,0:0|0:0|0:0,0:0:0:0: -398,273,115919,2,0,L|411:117,1,140,2|2,0:0|0:0,0:0:0:0: -255,34,116605,6,0,P|211:59|119:19,1,140,2|2,0:0|0:0,0:0:0:0: -45,68,117119,1,0,0:0:0:0: -108,133,117291,2,0,L|115:214,1,70,2|0,0:0|0:0,0:0:0:0: -197,240,117634,2,0,L|190:321,1,70,2|0,0:0|0:0,0:0:0:0: -363,369,118148,5,0,0:0:0:0: -332,283,118319,2,0,P|329:226|345:193,1,70,2|0,0:0|0:0,0:0:0:0: -386,140,118662,1,2,0:0:0:0: -386,140,118834,1,0,0:0:0:0: -331,67,119005,2,0,L|239:63,1,70,2|0,0:0|0:0,0:0:0:0: -261,63,119519,5,0,0:0:0:0: -198,129,119691,2,0,P|159:137|111:126,1,70,2|0,0:0|0:0,0:0:0:0: -68,201,120034,2,0,P|60:240|71:288,1,70,2|0,0:0|0:0,0:0:0:0: -128,333,120376,2,0,L|221:325,1,70,2|0,0:0|0:0,0:0:0:0: -282,293,120719,5,2,0:0:0:0: -282,293,121062,1,2,0:0:0:0: -282,293,121405,1,2,0:0:0:0: -282,293,121748,2,0,L|383:298,1,78.7500030040742,2|0,0:0|0:0,0:0:0:0: -403,281,122091,6,0,L|395:114,1,140,0|0,1:0|0:0,0:0:0:0: -221,91,122776,2,0,L|214:230,1,140,0|0,0:0|0:0,0:0:0:0: -354,347,123462,2,0,P|389:364|474:287,1,140,0|0,0:0|0:0,0:0:0:0: -329,202,124148,2,0,P|294:185|209:262,1,140,0|0,0:0|0:0,0:0:0:0: -68,341,124834,6,0,L|59:269,1,70,0|0,0:0|0:0,0:0:0:0: -139,313,125176,2,0,L|130:241,1,70,0|0,0:0|0:0,0:0:0:0: -211,286,125519,2,0,L|202:214,1,70,0|0,0:0|0:0,0:0:0:0: -282,260,125862,2,0,L|273:188,1,70,0|0,0:0|0:0,0:0:0:0: -192,108,126205,6,0,P|162:100|107:103,1,70 -172,192,126548,2,0,P|142:181|87:179,1,70 -146,274,126891,2,0,P|117:260|63:254,1,70 -110,354,127234,2,0,P|83:338|29:327,1,70 -150,279,127576,5,0,0:0:0:0: -150,279,127748,1,0,0:0:0:0: -242,327,127919,1,0,0:0:0:0: -242,327,128091,1,0,0:0:0:0: -285,232,128262,2,0,L|379:227,1,70,0|0,0:0|0:0,0:0:0:0: -422,166,128605,2,0,L|491:169,1,70,0|0,0:0|0:0,0:0:0:0: -441,76,128948,5,0,0:0:0:0: -441,76,129119,1,0,0:0:0:0: -388,150,129291,1,0,0:0:0:0: -388,150,129462,1,0,0:0:0:0: -338,73,129634,2,0,P|286:69|238:75,1,70,0|0,0:0|0:0,0:0:0:0: -210,140,129976,2,0,P|175:141|140:137,1,70,0|0,0:0|0:0,0:0:0:0: -79,205,130319,6,0,L|68:260,3,38.5000011749268,0|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: -108,267,130662,2,0,L|115:319,3,38.5000011749268,0|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: -145,334,131005,6,0,P|169:324|197:323,3,41.9999987182618,0|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: -220,339,131348,2,0,P|237:335|263:321,3,41.9999987182618,0|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: -289,300,131691,6,0,L|294:258,14,22.7499989585877,0|0|0|0|0|0|0|0|0|0|0|0|0|0|0,0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0,0:0:0:0: -314,240,132376,6,0,B|356:235|356:235|374:221|374:221|433:213,1,105.000004005432,0|0,0:0|0:0,0:0:0:0: -444,142,132891,1,0,0:0:0:0: -444,142,132976,1,0,0:0:0:0: -444,142,133062,6,0,B|461:90|461:90|446:-12,1,140,4|0,1:2|1:0,0:0:0:0: -238,15,133748,1,0,1:0:0:0: -181,103,133919,1,0,0:0:0:0: -181,103,134005,2,0,L|310:99,1,105,0|0,0:0|0:0,0:0:0:0: -366,167,134434,6,0,P|378:224|363:254,1,70,0|0,1:0|0:0,0:0:0:0: -276,270,134776,2,0,P|262:326|236:347,1,70,0|0,1:0|0:0,0:0:0:0: -151,356,135119,1,0,1:0:0:0: -61,300,135291,1,0,0:0:0:0: -164,254,135462,1,0,1:0:0:0: -164,254,135548,2,0,L|174:156,1,70 -195,162,135805,6,0,P|243:178|290:168,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: -148,67,136319,1,0,0:0:0:0: -148,67,136491,1,0,1:0:0:0: -247,98,136662,1,0,0:0:0:0: -247,98,136748,2,0,B|286:80|286:80|372:100,1,105,0|0,0:0|0:0,0:0:0:0: -432,154,137176,6,0,L|437:294,2,140,0|0|0,1:0|1:0|1:0,0:0:0:0: -307,324,138205,2,0,P|282:331|245:328,3,35,0|0|0|0,1:0|0:0|0:0|0:0,0:0:0:0: -246,309,138548,6,0,P|201:298|114:358,1,140,0|0,1:0|1:0,0:0:0:0: -19,157,139234,1,0,1:0:0:0: -113,112,139405,1,0,0:0:0:0: -113,112,139491,2,0,P|153:134|236:108,1,105,0|0,0:0|0:0,0:0:0:0: -310,159,139919,6,0,P|276:209|172:226,1,140,0|0,1:0|1:0,0:0:0:0: -387,334,140605,2,0,L|381:238,1,70,0|0,1:0|0:0,0:0:0:0: -450,183,140948,1,0,1:0:0:0: -450,183,141034,2,0,L|456:87,1,70,0|0,0:0|0:0,0:0:0:0: -434,85,141291,6,0,P|399:71|337:77,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: -468,292,141976,1,0,1:0:0:0: -377,238,142148,1,0,0:0:0:0: -377,238,142234,2,0,P|327:245|261:229,1,105 -178,185,142662,5,2,0:0:0:0: -178,185,143005,1,2,0:0:0:0: -178,185,143348,1,0,0:0:0:0: -178,185,143691,2,0,L|171:266,1,70,0|0,0:0|0:0,0:0:0:0: -171,254,144034,6,0,P|224:241|320:257,1,140,4|0,1:2|1:0,0:0:0:0: -408,282,144548,1,0,0:0:0:0: -480,205,144719,1,0,1:0:0:0: -433,116,144891,1,0,0:0:0:0: -433,116,144976,2,0,L|443:2,1,105,0|0,0:0|0:0,0:0:0:0: -337,69,145405,6,0,P|303:79|242:60,1,70,0|0,1:0|0:0,0:0:0:0: -172,116,145748,2,0,L|86:103,1,70,0|0,1:0|0:0,0:0:0:0: -38,188,146091,1,0,1:0:0:0: -38,188,146262,1,0,0:0:0:0: -110,264,146434,1,0,1:0:0:0: -110,264,146519,2,0,L|196:251,1,70,0|0,0:0|0:0,0:0:0:0: -208,269,146776,6,0,P|246:279|300:266,1,70,0|0,1:0|0:0,0:0:0:0: -369,227,147119,1,0,1:0:0:0: -369,227,147462,1,0,1:0:0:0: -312,138,147634,1,0,0:0:0:0: -312,138,147719,2,0,P|302:86|316:15,1,105,0|0,0:0|0:0,0:0:0:0: -210,71,148148,6,0,B|159:84|159:84|45:57,2,140,0|0|0,1:0|1:0|1:0,0:0:0:0: -410,132,149176,2,0,P|421:151|426:191,3,35,0|0|0|0,1:0|0:0|0:0|0:0,0:0:0:0: -458,203,149519,6,0,P|408:195|334:253,1,140,0|0,1:0|1:0,0:0:0:0: -232,232,150034,1,0,0:0:0:0: -309,162,150205,1,0,1:0:0:0: -192,144,150376,1,0,0:0:0:0: -192,144,150462,2,0,L|188:19,1,105,0|0,0:0|0:0,0:0:0:0: -112,155,150891,6,0,P|139:204|239:212,1,140,0|0,1:0|1:0,0:0:0:0: -216,221,151405,1,0,0:0:0:0: -296,288,151576,2,0,L|398:280,1,70,0|0,1:0|0:0,0:0:0:0: -445,213,151919,1,0,1:0:0:0: -445,213,152005,2,0,L|452:111,1,70,0|0,0:0|0:0,0:0:0:0: -434,113,152262,6,0,P|428:66|434:17,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: -352,180,152776,1,0,0:0:0:0: -352,180,152948,2,0,B|287:163|287:163|183:186,1,140,0|0,1:0|1:0,0:0:0:0: -215,178,153634,6,0,P|163:175|121:181,1,70,0|0,1:0|0:0,0:0:0:0: -106,274,153976,2,0,L|200:277,1,70,0|0,1:0|0:0,0:0:0:0: -264,329,154319,1,0,1:0:0:0: -264,329,154662,1,0,0:0:0:0: -360,286,154834,2,0,L|365:255,2,17.5,0|0|0,0:0|0:0|0:0,0:0:0:0: -376,312,155005,6,0,L|542:302,1,140,4|0,1:2|1:0,0:0:0:0: -460,213,155519,1,0,0:0:0:0: -460,213,155691,2,0,L|466:132,1,70,0|0,1:0|0:0,0:0:0:0: -363,113,156034,2,0,L|357:32,1,70,0|0,1:0|0:0,0:0:0:0: -253,34,156376,6,0,P|224:40|168:26,1,70,0|0,1:0|0:0,0:0:0:0: -101,98,156719,1,0,1:0:0:0: -101,98,156891,1,0,0:0:0:0: -190,152,157062,2,0,L|195:247,1,70,0|0,1:0|0:0,0:0:0:0: -290,274,157405,2,0,P|303:225|295:182,1,70,0|0,1:0|0:0,0:0:0:0: -271,105,157748,6,0,B|354:84|354:84|425:103,1,140,0|0,1:0|1:0,0:0:0:0: -484,169,158262,1,0,0:0:0:0: -484,169,158434,2,0,P|492:218|484:257,1,70,0|0,1:0|0:0,0:0:0:0: -396,286,158776,2,0,P|388:335|396:374,1,70,0|0,1:0|0:0,0:0:0:0: -285,341,159119,5,0,1:0:0:0: -285,341,159291,1,0,0:0:0:0: -210,267,159462,2,0,L|214:175,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: -129,334,159976,1,0,0:0:0:0: -51,262,160148,2,0,P|40:217|43:169,1,70,0|0,1:0|0:0,0:0:0:0: -125,104,160491,6,0,L|298:97,1,140,0|0,1:0|1:0,0:0:0:0: -341,170,161005,1,0,0:0:0:0: -373,70,161176,2,0,L|377:-8,1,70,0|0,1:0|0:0,0:0:0:0: -468,51,161519,1,0,1:0:0:0: -468,51,161691,1,0,0:0:0:0: -424,146,161862,6,0,P|419:203|429:235,1,70,0|0,1:0|0:0,0:0:0:0: -467,309,162205,2,0,L|388:311,2,70,0|0|0,1:0|0:0|1:0,0:0:0:0: -421,214,162719,1,0,0:0:0:0: -332,271,162891,2,0,P|321:309|343:361,1,70,0|0,1:0|0:0,0:0:0:0: -225,359,163234,6,0,P|162:331|86:357,1,140,0|0,1:0|1:0,0:0:0:0: -26,270,163748,1,0,0:0:0:0: -26,270,163919,2,0,L|30:169,1,70,0|0,1:0|0:0,0:0:0:0: -112,136,164262,2,0,L|108:35,1,70,0|0,1:0|0:0,0:0:0:0: -207,103,164605,6,0,P|272:122|351:76,2,140,0|0|0,1:0|1:0|1:0,0:0:0:0: -291,296,165634,1,0,1:0:0:0: -364,221,165805,1,8,0:0:0:0: -364,221,165891,1,0,0:0:0:0: -364,221,165976,6,0,P|373:159|328:87,1,140,4|0,1:2|1:0,0:0:0:0: -175,93,166662,2,0,P|140:152|148:221,1,140,0|0,1:0|1:0,0:0:0:0: -256,356,167348,2,0,L|256:212,1,140,0|0,1:0|1:0,0:0:0:0: -152,88,168034,1,0,1:0:0:0: -360,88,168376,1,0,1:0:0:0: -284,300,168719,6,0,P|216:308|157:273,1,140,0|0,1:0|1:0,0:0:0:0: -157,111,169405,2,0,P|216:76|285:84,1,140,0|0,1:0|1:0,0:0:0:0: -420,192,170091,2,0,L|276:192,1,140,0|0,1:0|1:0,0:0:0:0: -152,296,170777,1,0,1:0:0:0: -152,88,171119,1,0,1:0:0:0: -152,88,171291,1,0,0:0:0:0: -152,88,171462,6,0,L|264:88,1,105.000004005432,6|2,0:0|0:0,0:0:0:0: -392,176,172148,2,0,L|280:176,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: -160,272,172834,2,0,L|272:272,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: -400,352,173519,2,0,L|288:352,1,105.000004005432,2|2,0:0|0:0,0:0:0:0: -105,324,174205,6,0,P|85:285|89:219,1,90.9999958343508,2|2,0:0|0:0,0:0:0:0: -95,219,174719,1,0,0:0:0:0: -103,197,174891,2,0,L|99:88,1,90.9999958343508,2|2,0:0|0:0,0:0:0:0: -240,73,175576,5,0,0:0:0:0: -265,106,175919,1,0,0:0:0:0: -279,136,176262,2,0,B|314:129|351:139|351:139|329:100,1,112"; - } -} From c26d226a75fa4e7e4e975b32fa6b5035c21b78dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 Oct 2018 07:09:33 +0900 Subject: [PATCH 0170/2854] Make saving the ladder to disk a button-based operation rather than on dispose --- osu.Game.Tournament.Tests/LadderTestCase.cs | 43 +++++++++++++++++++ .../TestCaseLadderManager.cs | 26 +++-------- 2 files changed, 50 insertions(+), 19 deletions(-) create mode 100644 osu.Game.Tournament.Tests/LadderTestCase.cs diff --git a/osu.Game.Tournament.Tests/LadderTestCase.cs b/osu.Game.Tournament.Tests/LadderTestCase.cs new file mode 100644 index 0000000000..3044451a9e --- /dev/null +++ b/osu.Game.Tournament.Tests/LadderTestCase.cs @@ -0,0 +1,43 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using Newtonsoft.Json; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Tests.Visual; +using osu.Game.Tournament.Screens.Ladder.Components; + +namespace osu.Game.Tournament.Tests +{ + public abstract class LadderTestCase : OsuTestCase + { + protected LadderInfo Ladder; + + protected LadderTestCase() + { + Ladder = File.Exists(@"bracket.json") ? JsonConvert.DeserializeObject(File.ReadAllText(@"bracket.json")) : new LadderInfo(); + + Add(new OsuButton + { + Text = "Save Changes", + Width = 140, + Height = 50, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Padding = new MarginPadding(10), + Action = SaveChanges, + }); + } + + protected virtual void SaveChanges() + { + File.WriteAllText(@"bracket.json", JsonConvert.SerializeObject(Ladder, + new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + })); + } + } +} diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index 2dfb1b8a63..a7b93c8055 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -1,43 +1,31 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.IO; -using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics.Cursor; -using osu.Game.Tests.Visual; using osu.Game.Tournament.Screens.Ladder; -using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament.Tests { - public class TestCaseLadderManager : OsuTestCase + public class TestCaseLadderManager : LadderTestCase { [Cached] private readonly LadderManager manager; public TestCaseLadderManager() { - var ladder = File.Exists(@"bracket.json") ? JsonConvert.DeserializeObject(File.ReadAllText(@"bracket.json")) : new LadderInfo(); - - Child = new OsuContextMenuContainer + Add(new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, - Child = manager = new LadderManager(ladder) - }; + Child = manager = new LadderManager(Ladder) + }); } - protected override void Dispose(bool isDisposing) + protected override void SaveChanges() { - base.Dispose(isDisposing); - - File.WriteAllText(@"bracket.json", JsonConvert.SerializeObject(manager.CreateInfo(), - new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - DefaultValueHandling = DefaultValueHandling.Ignore - })); + Ladder = manager.CreateInfo(); + base.SaveChanges(); } } } From a4bb4255b167cf051dd9fe60a1942641790529e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 Oct 2018 07:10:13 +0900 Subject: [PATCH 0171/2854] Add grouping manager --- .../TestCaseGroupingManager.cs | 50 +++++++++++++++++++ .../Components/DrawableTournamentGrouping.cs | 2 +- .../Ladder/Components/TournamentGrouping.cs | 5 +- osu.Game/Overlays/Settings/SettingsTextBox.cs | 2 +- 4 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Tournament.Tests/TestCaseGroupingManager.cs diff --git a/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs b/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs new file mode 100644 index 0000000000..0d39ef1c73 --- /dev/null +++ b/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs @@ -0,0 +1,50 @@ +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays.Settings; +using osu.Game.Tournament.Screens.Ladder.Components; + +namespace osu.Game.Tournament.Tests +{ + public class TestCaseGroupingManager : LadderTestCase + { + public TestCaseGroupingManager() + { + FillFlowContainer items; + + Add(items = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.Both + }); + + foreach (var g in Ladder.Groupings) + items.Add(new GroupingRow(g)); + } + + public class GroupingRow : CompositeDrawable + { + public readonly TournamentGrouping Grouping; + + public GroupingRow(TournamentGrouping grouping) + { + Grouping = grouping; + InternalChildren = new Drawable[] + { + new FillFlowContainer + { + Direction = FillDirection.Horizontal, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new SettingsTextBox { Width = 0.4f, Bindable = Grouping.Name }, + new SettingsTextBox { Width = 0.4f, Bindable = Grouping.Description }, + } + } + }; + + RelativeSizeAxes = Axes.X; + Height = 40; + } + } + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs index 475e735522..8a38f402aa 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { new OsuSpriteText { - Text = grouping.Description.ToUpper(), + Text = grouping.Description.Value.ToUpper(), Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre }, diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs index 675bf5fc4f..2e72e1fe06 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs @@ -2,13 +2,14 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Framework.Configuration; namespace osu.Game.Tournament.Screens.Ladder.Components { public class TournamentGrouping { - public string Name; - public string Description; + public readonly Bindable Name = new Bindable(); + public readonly Bindable Description = new Bindable(); public int BestOf; diff --git a/osu.Game/Overlays/Settings/SettingsTextBox.cs b/osu.Game/Overlays/Settings/SettingsTextBox.cs index ce9218bbe7..106b2372e0 100644 --- a/osu.Game/Overlays/Settings/SettingsTextBox.cs +++ b/osu.Game/Overlays/Settings/SettingsTextBox.cs @@ -8,6 +8,6 @@ namespace osu.Game.Overlays.Settings { public class SettingsTextBox : SettingsItem { - protected override Drawable CreateControl() => new OsuTextBox(); + protected override Drawable CreateControl() => new OsuTextBox { RelativeSizeAxes = Axes.X }; } } From 2173f46a4678eb2c708be15ea2362030e845528e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 Oct 2018 19:45:59 +0900 Subject: [PATCH 0172/2854] Add missing licence header --- osu.Game.Tournament.Tests/TestCaseGroupingManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs b/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs index 0d39ef1c73..e9df1eb62e 100644 --- a/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Overlays.Settings; From bac7d644370bdb210b1a1aa6af71d7c53754a58f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 Oct 2018 23:45:52 +0900 Subject: [PATCH 0173/2854] Improve the completeness of APIBeatmap's transform methods --- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 193ccf1f6b..c9ea66d05f 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -61,17 +61,13 @@ namespace osu.Game.Online.API.Requests.Responses { return new BeatmapInfo { - Metadata = this, + Metadata = !string.IsNullOrEmpty(Artist) ? this : (BeatmapMetadata)BeatmapSet, Ruleset = rulesets.GetRuleset(ruleset), StarDifficulty = starDifficulty, OnlineBeatmapID = OnlineBeatmapID, Version = version, Status = Status, - BeatmapSet = new BeatmapSetInfo - { - OnlineBeatmapSetID = OnlineBeatmapSetID, - Status = BeatmapSet?.Status ?? BeatmapSetOnlineStatus.None - }, + BeatmapSet = BeatmapSet.ToBeatmapSet(rulesets), BaseDifficulty = new BeatmapDifficulty { DrainRate = drainRate, From 522f106f746f34a3a80f64ff236704c506103b30 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 14 Oct 2018 00:40:33 +0900 Subject: [PATCH 0174/2854] Add initial version of beatmap card --- .../TestCaseBeatmapPanel.cs | 42 +++++++ .../Components/TournamentBeatmapPanel.cs | 110 ++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs create mode 100644 osu.Game.Tournament/Components/TournamentBeatmapPanel.cs diff --git a/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs b/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs new file mode 100644 index 0000000000..8298cbd8ea --- /dev/null +++ b/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets; +using osu.Game.Tests.Visual; +using osu.Game.Tournament.Components; + +namespace osu.Game.Tournament.Tests +{ + public class TestCaseBeatmapPanel : OsuTestCase + { + [Resolved] + protected APIAccess API { get; set; } + + [Resolved] + protected RulesetStore Rulesets { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = 1091460 }); + req.Success += success; + API.Queue(req); + } + + private void success(APIBeatmap apiBeatmap) + { + var beatmap = apiBeatmap.ToBeatmap(Rulesets); + Add(new TournamentBeatmapPanel(beatmap) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }); + } + } +} diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs new file mode 100644 index 0000000000..5eb597a3c6 --- /dev/null +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -0,0 +1,110 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using OpenTK.Graphics; + +namespace osu.Game.Tournament.Components +{ + public class TournamentBeatmapPanel : CompositeDrawable + { + private readonly BeatmapInfo beatmap; + private const float horizontal_padding = 10; + private const float vertical_padding = 5; + + public TournamentBeatmapPanel(BeatmapInfo beatmap) + { + this.beatmap = beatmap; + Width = 400; + Height = 50; + } + + [BackgroundDependencyLoader] + private void load() + { + CornerRadius = 25; + Masking = true; + + AddRangeInternal(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + new UpdateableBeatmapSetCover + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.5f), + BeatmapSet = beatmap.BeatmapSet, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Padding = new MarginPadding(vertical_padding), + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = new LocalisedString(( + $"{beatmap.Metadata.ArtistUnicode} - {beatmap.Metadata.TitleUnicode}", + $"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title}")), + Font = @"Exo2.0-BoldItalic", + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Padding = new MarginPadding(vertical_padding), + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "mapper", + Font = @"Exo2.0-RegularItalic", + Padding = new MarginPadding { Right = 5 }, + TextSize = 14 + }, + new OsuSpriteText + { + Text = beatmap.Metadata.AuthorString, + Font = @"Exo2.0-BoldItalic", + Padding = new MarginPadding { Right = 20 }, + TextSize = 14 + }, + new OsuSpriteText + { + Text = "difficulty", + Font = @"Exo2.0-RegularItalic", + Padding = new MarginPadding { Right = 5 }, + TextSize = 14 + }, + new OsuSpriteText + { + Text = beatmap.Version, + Font = @"Exo2.0-BoldItalic", + TextSize = 14 + }, + } + } + }, + }, + }); + } + } +} From f5716c3d213c3d6e4ed597aa18116a9372cb5cee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 14 Oct 2018 01:03:04 +0900 Subject: [PATCH 0175/2854] Add ability to change best of, add and delete groupings --- .../TestCaseGroupingManager.cs | 44 ++++++++++++++++--- .../Ladder/Components/TournamentGrouping.cs | 2 +- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs b/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs index e9df1eb62e..a0a4dfdc8b 100644 --- a/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs @@ -1,8 +1,10 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; using osu.Game.Tournament.Screens.Ladder.Components; @@ -10,20 +12,43 @@ namespace osu.Game.Tournament.Tests { public class TestCaseGroupingManager : LadderTestCase { + private readonly FillFlowContainer items; + public TestCaseGroupingManager() { - FillFlowContainer items; - - Add(items = new FillFlowContainer + Add(new FillFlowContainer { Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + items = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + new TriangleButton + { + Width = 100, + Text = "Add", + Action = addNew + }, + } }); foreach (var g in Ladder.Groupings) items.Add(new GroupingRow(g)); } + protected override void SaveChanges() + { + Ladder.Groupings = items.Children.Select(c => c.Grouping).ToList(); + base.SaveChanges(); + } + + private void addNew() => items.Add(new GroupingRow(new TournamentGrouping())); + public class GroupingRow : CompositeDrawable { public readonly TournamentGrouping Grouping; @@ -39,8 +64,15 @@ namespace osu.Game.Tournament.Tests RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new SettingsTextBox { Width = 0.4f, Bindable = Grouping.Name }, - new SettingsTextBox { Width = 0.4f, Bindable = Grouping.Description }, + new SettingsTextBox { Width = 0.3f, Bindable = Grouping.Name }, + new SettingsTextBox { Width = 0.3f, Bindable = Grouping.Description }, + new SettingsSlider { LabelText = "Best of", Width = 0.3f, Bindable = Grouping.BestOf }, + new DangerousSettingsButton + { + Width = 0.1f, + Text = "Delete", + Action = () => Expire() + }, } } }; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs index 2e72e1fe06..d7c89cb006 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs @@ -11,7 +11,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly Bindable Name = new Bindable(); public readonly Bindable Description = new Bindable(); - public int BestOf; + public readonly BindableInt BestOf = new BindableInt(9) { Default = 9, MinValue = 3, MaxValue = 23 }; public List Pairings = new List(); } From e136f72c8ec8cae0e3e43ec057eeabc38e33b249 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 14 Oct 2018 03:03:17 +0900 Subject: [PATCH 0176/2854] Fix incorrect access definitions --- osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs b/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs index 8298cbd8ea..e5cb2f155c 100644 --- a/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs +++ b/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs @@ -16,22 +16,22 @@ namespace osu.Game.Tournament.Tests public class TestCaseBeatmapPanel : OsuTestCase { [Resolved] - protected APIAccess API { get; set; } + private APIAccess api { get; set; } [Resolved] - protected RulesetStore Rulesets { get; set; } + private RulesetStore rulesets { get; set; } [BackgroundDependencyLoader] private void load() { var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = 1091460 }); req.Success += success; - API.Queue(req); + api.Queue(req); } private void success(APIBeatmap apiBeatmap) { - var beatmap = apiBeatmap.ToBeatmap(Rulesets); + var beatmap = apiBeatmap.ToBeatmap(rulesets); Add(new TournamentBeatmapPanel(beatmap) { Anchor = Anchor.Centre, From b1862a863bd3f7d8c881c3eb50606cbc15191c6b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 14 Oct 2018 03:03:40 +0900 Subject: [PATCH 0177/2854] Fix not being able to decrement scores of matches with no defined progression --- .../Screens/Ladder/Components/DrawableMatchTeam.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index 3edc28b4e5..dca8d0f0a8 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -141,7 +141,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components } else { - if (pairing.Progression.Value?.Completed.Value != false) + if (pairing.Progression.Value?.Completed.Value == true) // don't allow changing scores if the match has a progression. can cause large data loss return false; From c4b486f1d4cf3d38219fea14d68589b16a0db29b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 14 Oct 2018 03:04:06 +0900 Subject: [PATCH 0178/2854] Fix transfer of teams in the case loser and winner progression are equal --- .../Ladder/Components/DrawableMatchPairing.cs | 54 ++++++++++++++----- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index da56f83d68..1f58bc86ea 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Game.Tournament.Components; using OpenTK; using OpenTK.Graphics; using OpenTK.Input; @@ -91,23 +92,50 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private void updateProgression() { - var progression = Pairing.Progression?.Value; - - if (progression != null) + if (!Pairing.Completed) { - bool progressionAbove = progression.ID < Pairing.ID; + // ensure we clear any of our teams from our progression. + // this is not pretty logic but should suffice for now. + if (Pairing.Progression.Value != null && Pairing.Progression.Value.Team1.Value == Pairing.Team1.Value) + Pairing.Progression.Value.Team1.Value = null; - var destinationForWinner = progressionAbove || progression.Team1.Value != null && progression.Team1.Value != Pairing.Team1.Value && progression.Team1.Value != Pairing.Team2.Value ? progression.Team2 : progression.Team1; - destinationForWinner.Value = Pairing.Winner; + if (Pairing.Progression.Value != null && Pairing.Progression.Value.Team2.Value == Pairing.Team2.Value) + Pairing.Progression.Value.Team2.Value = null; + + if (Pairing.LosersProgression.Value != null && Pairing.LosersProgression.Value.Team1.Value == Pairing.Team1.Value) + Pairing.LosersProgression.Value.Team1.Value = null; + + if (Pairing.LosersProgression.Value != null && Pairing.LosersProgression.Value.Team2.Value == Pairing.Team2.Value) + Pairing.LosersProgression.Value.Team2.Value = null; + } + else + { + transferProgression(Pairing.Progression?.Value, Pairing.Winner); + transferProgression(Pairing.LosersProgression?.Value, Pairing.Loser); + } + } + + private void transferProgression(MatchPairing destination, TournamentTeam team) + { + if (destination == null) return; + + bool progressionAbove = destination.ID < Pairing.ID; + + Bindable destinationTeam; + + // check for the case where we have already transferred out value + if (destination.Team1.Value == team) + destinationTeam = destination.Team1; + else if (destination.Team2.Value == team) + destinationTeam = destination.Team2; + else + { + destinationTeam = progressionAbove ? destination.Team2 : destination.Team1; + if (destinationTeam.Value != null) + destinationTeam = progressionAbove ? destination.Team1 : destination.Team2; } - if ((progression = Pairing.LosersProgression?.Value) != null) - { - bool progressionAbove = progression.ID < Pairing.ID; - - var destinationForLoser = progressionAbove || progression.Team1.Value != null && progression.Team1.Value != Pairing.Team1.Value && progression.Team1.Value != Pairing.Team2.Value ? progression.Team2 : progression.Team1; - destinationForLoser.Value = Pairing.Loser; - } + destinationTeam.Value = team; } private void updateWinConditions() From b17ead22a362082c4ac8d19983fe32cced8411c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 14 Oct 2018 03:05:34 +0900 Subject: [PATCH 0179/2854] fixup! Fix incorrect access definitions --- osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs b/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs index e5cb2f155c..d6c9e0c901 100644 --- a/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs +++ b/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs @@ -16,10 +16,10 @@ namespace osu.Game.Tournament.Tests public class TestCaseBeatmapPanel : OsuTestCase { [Resolved] - private APIAccess api { get; set; } + private APIAccess api { get; set; } = null; [Resolved] - private RulesetStore rulesets { get; set; } + private RulesetStore rulesets { get; set; } = null; [BackgroundDependencyLoader] private void load() From a02caeef64d5fb12de8271be48e119193c01a532 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 14 Oct 2018 05:19:50 +0900 Subject: [PATCH 0180/2854] Add team intro screen Also adds dates to groups and matches (must be manually populated via json) --- .../TestCaseTeamOverview.cs | 18 ++ .../Screens/Ladder/Components/MatchPairing.cs | 3 + .../Ladder/Components/TournamentGrouping.cs | 3 + .../Screens/TeamIntro/TeamIntro.cs | 192 ++++++++++++++++++ 4 files changed, 216 insertions(+) create mode 100644 osu.Game.Tournament.Tests/TestCaseTeamOverview.cs create mode 100644 osu.Game.Tournament/Screens/TeamIntro/TeamIntro.cs diff --git a/osu.Game.Tournament.Tests/TestCaseTeamOverview.cs b/osu.Game.Tournament.Tests/TestCaseTeamOverview.cs new file mode 100644 index 0000000000..b853abb27c --- /dev/null +++ b/osu.Game.Tournament.Tests/TestCaseTeamOverview.cs @@ -0,0 +1,18 @@ +using System.Linq; +using osu.Game.Tournament.Screens.TeamIntro; + +namespace osu.Game.Tournament.Tests +{ + public class TestCaseTeamIntro : LadderTestCase + { + public TestCaseTeamIntro() + { + var team1 = Ladder.Teams.First(t => t.Acronym == "USA"); + var team2 = Ladder.Teams.First(t => t.Acronym == "JPN"); + + var round = Ladder.Groupings.First(g => g.Name == "Quarter Finals"); + + Add(new TeamIntroScreen(team1, team2, round)); + } + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index 93d1b7085c..5dc2009c4d 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using Newtonsoft.Json; using osu.Framework.Configuration; using osu.Game.Tournament.Components; @@ -42,6 +43,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [JsonIgnore] public readonly Bindable LosersProgression = new Bindable(); + public readonly Bindable Date = new Bindable(); + public Point Position; public MatchPairing() diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs index d7c89cb006..d8680b7210 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using osu.Framework.Configuration; @@ -13,6 +14,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly BindableInt BestOf = new BindableInt(9) { Default = 9, MinValue = 3, MaxValue = 23 }; + public readonly Bindable StartDate = new Bindable(); + public List Pairings = new List(); } } diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntro.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntro.cs new file mode 100644 index 0000000000..fce29a2c89 --- /dev/null +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntro.cs @@ -0,0 +1,192 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Screens; +using osu.Game.Tournament.Components; +using osu.Game.Tournament.Screens.Ladder.Components; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Tournament.Screens.TeamIntro +{ + public class TeamIntroScreen : OsuScreen + { + public TeamIntroScreen(TournamentTeam team1, TournamentTeam team2, TournamentGrouping round) + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = Color4.White, + RelativeSizeAxes = Axes.Both + }, + new TeamWithPlayers(team1, true) + { + RelativeSizeAxes = Axes.Both, + Margin = new MarginPadding(40), + Width = 0.5f, + Height = 0.6f, + Anchor = Anchor.Centre, + Origin = Anchor.CentreRight + }, + new TeamWithPlayers(team2) + { + RelativeSizeAxes = Axes.Both, + Margin = new MarginPadding(40), + Width = 0.5f, + Height = 0.6f, + Anchor = Anchor.Centre, + Origin = Anchor.CentreLeft + }, + new RoundDisplay(round) + { + RelativeSizeAxes = Axes.Both, + Height = 0.3f, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + } + }; + } + + private class RoundDisplay : CompositeDrawable + { + public RoundDisplay(TournamentGrouping group) + { + var col = OsuColour.Gray(0.33f); + + InternalChildren = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Colour = col, + Text = "COMING UP NEXT", + Font = "Exo2.0-SemiBold", + TextSize = 15, + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Colour = col, + Text = group.Name.Value, + Font = "Exo2.0-Light", + Spacing = new Vector2(10, 0), + TextSize = 50, + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Colour = col, + Text = group.StartDate.Value.ToString("dd MMMM HH:mm UTC"), + TextSize = 20, + }, + } + } + }; + } + } + + private class TeamWithPlayers : CompositeDrawable + { + private readonly Color4 red = new Color4(129, 68, 65, 255); + private readonly Color4 blue = new Color4(41, 91, 97, 255); + + public TeamWithPlayers(TournamentTeam team, bool left = false) + { + FillFlowContainer players; + var colour = left ? red : blue; + InternalChildren = new Drawable[] + { + new TeamDisplay(team, left ? "Team Red" : "Team Blue", colour) + { + Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = left ? Anchor.CentreRight : Anchor.CentreLeft, + }, + players = new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(0, 5), + Padding = new MarginPadding(20), + Anchor = !left ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = !left ? Anchor.CentreRight : Anchor.CentreLeft, + RelativePositionAxes = Axes.Both, + X = left ? 0.1f : -0.1f, + }, + }; + + foreach (var p in team.Players) + players.Add(new OsuSpriteText + { + Text = p.Username, + TextSize = 24, + Colour = colour, + Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = left ? Anchor.CentreRight : Anchor.CentreLeft, + }); + } + + private class TeamDisplay : DrawableTournamentTeam + { + public TeamDisplay(TournamentTeam team, string teamName, Color4 colour) + : base(team) + { + AutoSizeAxes = Axes.Both; + + Flag.Anchor = Flag.Origin = Anchor.TopCentre; + Flag.RelativeSizeAxes = Axes.None; + Flag.Size = new Vector2(300, 200); + Flag.Scale = new Vector2(0.4f); + Flag.Margin = new MarginPadding { Bottom = 20 }; + + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + Children = new Drawable[] + { + Flag, + new OsuSpriteText + { + Text = team.FullName.ToUpper(), + TextSize = 40, + Colour = Color4.Black, + Font = "Exo2.0-Light", + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + }, + new OsuSpriteText + { + Text = teamName.ToUpper(), + TextSize = 20, + Colour = colour, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + } + } + }; + } + } + } + } +} From 0c4ea4beb102d0df710c94472a2fc92ed8e36e20 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 14 Oct 2018 05:20:10 +0900 Subject: [PATCH 0181/2854] Allow dynamic recompilation of beatmap panel testcase --- osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs b/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs index d6c9e0c901..93068f6224 100644 --- a/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs +++ b/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; @@ -21,6 +23,11 @@ namespace osu.Game.Tournament.Tests [Resolved] private RulesetStore rulesets { get; set; } = null; + public override IReadOnlyList RequiredTypes => new[] + { + typeof(TournamentBeatmapPanel), + }; + [BackgroundDependencyLoader] private void load() { From 63fbe4e946fc3bfcda0397646303207e66062a4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 14 Oct 2018 18:00:28 +0900 Subject: [PATCH 0182/2854] Add map pool beatmaps to groupings --- osu.Game.Tournament.Tests/TestCaseTeamOverview.cs | 5 ++++- .../Screens/Ladder/Components/GroupingBeatmap.cs | 11 +++++++++++ .../Screens/Ladder/Components/TournamentGrouping.cs | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tournament/Screens/Ladder/Components/GroupingBeatmap.cs diff --git a/osu.Game.Tournament.Tests/TestCaseTeamOverview.cs b/osu.Game.Tournament.Tests/TestCaseTeamOverview.cs index b853abb27c..006c8805c1 100644 --- a/osu.Game.Tournament.Tests/TestCaseTeamOverview.cs +++ b/osu.Game.Tournament.Tests/TestCaseTeamOverview.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using System.Linq; using osu.Game.Tournament.Screens.TeamIntro; @@ -10,7 +13,7 @@ namespace osu.Game.Tournament.Tests var team1 = Ladder.Teams.First(t => t.Acronym == "USA"); var team2 = Ladder.Teams.First(t => t.Acronym == "JPN"); - var round = Ladder.Groupings.First(g => g.Name == "Quarter Finals"); + var round = Ladder.Groupings.First(g => g.Name == "Finals"); Add(new TeamIntroScreen(team1, team2, round)); } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/GroupingBeatmap.cs b/osu.Game.Tournament/Screens/Ladder/Components/GroupingBeatmap.cs new file mode 100644 index 0000000000..7395a2ae5f --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/GroupingBeatmap.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + public class GroupingBeatmap + { + public int ID; + public string Mods; + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs index d8680b7210..7530910bc1 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs @@ -14,6 +14,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly BindableInt BestOf = new BindableInt(9) { Default = 9, MinValue = 3, MaxValue = 23 }; + public readonly List Beatmaps = new List(); + public readonly Bindable StartDate = new Bindable(); public List Pairings = new List(); From 61083190d08472cb709fa69382d4159db2fd6c6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 14 Oct 2018 18:09:22 +0900 Subject: [PATCH 0183/2854] Add WIP map pool testcase --- osu.Game.Tournament.Tests/TestCaseMapPool.cs | 42 ++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 osu.Game.Tournament.Tests/TestCaseMapPool.cs diff --git a/osu.Game.Tournament.Tests/TestCaseMapPool.cs b/osu.Game.Tournament.Tests/TestCaseMapPool.cs new file mode 100644 index 0000000000..11d9a7806b --- /dev/null +++ b/osu.Game.Tournament.Tests/TestCaseMapPool.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Tournament.Components; +using osu.Game.Tournament.Screens.Ladder.Components; + +namespace osu.Game.Tournament.Tests +{ + public class TestCaseMapPool : LadderTestCase + { + public TestCaseMapPool() + { + var round = Ladder.Groupings.First(g => g.Name == "Finals"); + + Add(new MapPoolScreen(round)); + } + } + + public class MapPoolScreen : CompositeDrawable + { + private readonly FillFlowContainer maps; + + public MapPoolScreen(TournamentGrouping round) + { + InternalChildren = new Drawable[] + { + maps = new FillFlowContainer + { + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.Both, + }, + }; + + //foreach (var b in round.Beatmaps) + // maps.Add(new TournamentBeatmapPanel(new BeatmapInfo() { OnlineBeatmapID = b.ID })); + } + } +} From dea2acaea3f24b6fb33cf7f1746002e7151941f7 Mon Sep 17 00:00:00 2001 From: Paul Teng Date: Sun, 14 Oct 2018 11:18:10 -0400 Subject: [PATCH 0184/2854] Add new interface that allows restarts --- .../Rulesets/Mods/IApplicableForceRestart.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 osu.Game/Rulesets/Mods/IApplicableForceRestart.cs diff --git a/osu.Game/Rulesets/Mods/IApplicableForceRestart.cs b/osu.Game/Rulesets/Mods/IApplicableForceRestart.cs new file mode 100644 index 0000000000..2c9a6b6cb9 --- /dev/null +++ b/osu.Game/Rulesets/Mods/IApplicableForceRestart.cs @@ -0,0 +1,16 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mods +{ + /// + /// Represents a mod which can override (and block) a fail. + /// + public interface IApplicableRestartOnFail : IApplicableMod + { + /// + /// Whether we allow restarting + /// + bool AllowRestart { get; } + } +} From fd774c6a09edf2abd7954e7fd4fc8703bd3d015e Mon Sep 17 00:00:00 2001 From: Paul Teng Date: Sun, 14 Oct 2018 11:18:52 -0400 Subject: [PATCH 0185/2854] Allow restarts in ModPerfect --- osu.Game/Rulesets/Mods/ModPerfect.cs | 4 +++- osu.Game/Screens/Play/Player.cs | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModPerfect.cs b/osu.Game/Rulesets/Mods/ModPerfect.cs index 802890866f..5d6065b4a2 100644 --- a/osu.Game/Rulesets/Mods/ModPerfect.cs +++ b/osu.Game/Rulesets/Mods/ModPerfect.cs @@ -6,13 +6,15 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mods { - public abstract class ModPerfect : ModSuddenDeath + public abstract class ModPerfect : ModSuddenDeath, IApplicableRestartOnFail { public override string Name => "Perfect"; public override string ShortenedName => "PF"; public override FontAwesome Icon => FontAwesome.fa_osu_mod_perfect; public override string Description => "SS or quit."; + public bool AllowRestart => true; + protected override bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Accuracy.Value != 1; } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b3cbeb3850..0577369b05 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -289,6 +289,12 @@ namespace osu.Game.Screens.Play if (Beatmap.Value.Mods.Value.OfType().Any(m => !m.AllowFail)) return false; + if (Beatmap.Value.Mods.Value.OfType().Any(m => m.AllowRestart)) + { + Restart(); + return false; + } + adjustableClock.Stop(); HasFailed = true; From 39c767af2d349e3219b20690208694a4eb7d778a Mon Sep 17 00:00:00 2001 From: Paul Teng Date: Sun, 14 Oct 2018 11:25:05 -0400 Subject: [PATCH 0186/2854] Update file name and update summary --- .../{IApplicableForceRestart.cs => IApplicableRestartOnFail.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game/Rulesets/Mods/{IApplicableForceRestart.cs => IApplicableRestartOnFail.cs} (86%) diff --git a/osu.Game/Rulesets/Mods/IApplicableForceRestart.cs b/osu.Game/Rulesets/Mods/IApplicableRestartOnFail.cs similarity index 86% rename from osu.Game/Rulesets/Mods/IApplicableForceRestart.cs rename to osu.Game/Rulesets/Mods/IApplicableRestartOnFail.cs index 2c9a6b6cb9..43b3f36624 100644 --- a/osu.Game/Rulesets/Mods/IApplicableForceRestart.cs +++ b/osu.Game/Rulesets/Mods/IApplicableRestartOnFail.cs @@ -4,7 +4,7 @@ namespace osu.Game.Rulesets.Mods { /// - /// Represents a mod which can override (and block) a fail. + /// Represents a mod which can request to restart on fail. /// public interface IApplicableRestartOnFail : IApplicableMod { From 3a5af47ee8c1149d53c8a3a315d1dc49017ce2a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Oct 2018 15:20:12 +0900 Subject: [PATCH 0187/2854] Populate beatmaps with api information when not present --- osu.Game.Tournament.Tests/LadderTestCase.cs | 32 ++++++++++++++++++- .../TestCaseGroupingManager.cs | 6 ++++ .../TestCaseLadderManager.cs | 5 +-- osu.Game.Tournament.Tests/TestCaseMapPool.cs | 13 ++++---- .../TestCaseTeamOverview.cs | 4 ++- .../Ladder/Components/GroupingBeatmap.cs | 4 +++ osu.sln.DotSettings | 1 + 7 files changed, 55 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tournament.Tests/LadderTestCase.cs b/osu.Game.Tournament.Tests/LadderTestCase.cs index 3044451a9e..ee8cfb8f8a 100644 --- a/osu.Game.Tournament.Tests/LadderTestCase.cs +++ b/osu.Game.Tournament.Tests/LadderTestCase.cs @@ -3,8 +3,13 @@ using System.IO; using Newtonsoft.Json; +using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Rulesets; using osu.Game.Tests.Visual; using osu.Game.Tournament.Screens.Ladder.Components; @@ -14,10 +19,35 @@ namespace osu.Game.Tournament.Tests { protected LadderInfo Ladder; - protected LadderTestCase() + [Resolved] + private APIAccess api { get; set; } = null; + + [Resolved] + private RulesetStore rulesets { get; set; } = null; + + [BackgroundDependencyLoader] + private void load() { Ladder = File.Exists(@"bracket.json") ? JsonConvert.DeserializeObject(File.ReadAllText(@"bracket.json")) : new LadderInfo(); + bool addedInfo = false; + + foreach (var g in Ladder.Groupings) + foreach (var b in g.Beatmaps) + { + if (b.BeatmapInfo == null) + { + var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); + req.Success += i => b.BeatmapInfo = i.ToBeatmap(rulesets); + req.Perform(api); + + addedInfo = true; + } + } + + if (addedInfo) + SaveChanges(); + Add(new OsuButton { Text = "Save Changes", diff --git a/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs b/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs index a0a4dfdc8b..2b79ba0225 100644 --- a/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -37,6 +38,11 @@ namespace osu.Game.Tournament.Tests } }); + } + + [BackgroundDependencyLoader] + private void load() + { foreach (var g in Ladder.Groupings) items.Add(new GroupingRow(g)); } diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index a7b93c8055..35a86e83b8 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -11,9 +11,10 @@ namespace osu.Game.Tournament.Tests public class TestCaseLadderManager : LadderTestCase { [Cached] - private readonly LadderManager manager; + private LadderManager manager; - public TestCaseLadderManager() + [BackgroundDependencyLoader] + private void load() { Add(new OsuContextMenuContainer { diff --git a/osu.Game.Tournament.Tests/TestCaseMapPool.cs b/osu.Game.Tournament.Tests/TestCaseMapPool.cs index 11d9a7806b..2c6999d6ae 100644 --- a/osu.Game.Tournament.Tests/TestCaseMapPool.cs +++ b/osu.Game.Tournament.Tests/TestCaseMapPool.cs @@ -2,9 +2,9 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; @@ -12,7 +12,8 @@ namespace osu.Game.Tournament.Tests { public class TestCaseMapPool : LadderTestCase { - public TestCaseMapPool() + [BackgroundDependencyLoader] + private void load() { var round = Ladder.Groupings.First(g => g.Name == "Finals"); @@ -22,10 +23,10 @@ namespace osu.Game.Tournament.Tests public class MapPoolScreen : CompositeDrawable { - private readonly FillFlowContainer maps; - public MapPoolScreen(TournamentGrouping round) { + FillFlowContainer maps; + InternalChildren = new Drawable[] { maps = new FillFlowContainer @@ -35,8 +36,8 @@ namespace osu.Game.Tournament.Tests }, }; - //foreach (var b in round.Beatmaps) - // maps.Add(new TournamentBeatmapPanel(new BeatmapInfo() { OnlineBeatmapID = b.ID })); + foreach (var b in round.Beatmaps) + maps.Add(new TournamentBeatmapPanel(b.BeatmapInfo)); } } } diff --git a/osu.Game.Tournament.Tests/TestCaseTeamOverview.cs b/osu.Game.Tournament.Tests/TestCaseTeamOverview.cs index 006c8805c1..52a5a7204c 100644 --- a/osu.Game.Tournament.Tests/TestCaseTeamOverview.cs +++ b/osu.Game.Tournament.Tests/TestCaseTeamOverview.cs @@ -2,13 +2,15 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Linq; +using osu.Framework.Allocation; using osu.Game.Tournament.Screens.TeamIntro; namespace osu.Game.Tournament.Tests { public class TestCaseTeamIntro : LadderTestCase { - public TestCaseTeamIntro() + [BackgroundDependencyLoader] + private void load() { var team1 = Ladder.Teams.First(t => t.Acronym == "USA"); var team2 = Ladder.Teams.First(t => t.Acronym == "JPN"); diff --git a/osu.Game.Tournament/Screens/Ladder/Components/GroupingBeatmap.cs b/osu.Game.Tournament/Screens/Ladder/Components/GroupingBeatmap.cs index 7395a2ae5f..416f960404 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/GroupingBeatmap.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/GroupingBeatmap.cs @@ -1,11 +1,15 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Beatmaps; + namespace osu.Game.Tournament.Screens.Ladder.Components { public class GroupingBeatmap { public int ID; public string Mods; + + public BeatmapInfo BeatmapInfo; } } diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 404b19deda..345400305c 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -666,6 +666,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste True True True + True True True True From f324072d44d70ec439fe8ef17e95f7dff2d426a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Oct 2018 15:25:56 +0900 Subject: [PATCH 0188/2854] Make map pool layout more correct --- osu.Game.Tournament.Tests/TestCaseMapPool.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseMapPool.cs b/osu.Game.Tournament.Tests/TestCaseMapPool.cs index 2c6999d6ae..10ebd4e63c 100644 --- a/osu.Game.Tournament.Tests/TestCaseMapPool.cs +++ b/osu.Game.Tournament.Tests/TestCaseMapPool.cs @@ -5,8 +5,10 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Screens; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; +using OpenTK; namespace osu.Game.Tournament.Tests { @@ -21,7 +23,7 @@ namespace osu.Game.Tournament.Tests } } - public class MapPoolScreen : CompositeDrawable + public class MapPoolScreen : OsuScreen { public MapPoolScreen(TournamentGrouping round) { @@ -31,13 +33,19 @@ namespace osu.Game.Tournament.Tests { maps = new FillFlowContainer { + Spacing = new Vector2(20), + Padding = new MarginPadding(50), Direction = FillDirection.Full, RelativeSizeAxes = Axes.Both, }, }; foreach (var b in round.Beatmaps) - maps.Add(new TournamentBeatmapPanel(b.BeatmapInfo)); + maps.Add(new TournamentBeatmapPanel(b.BeatmapInfo) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }); } } } From dfaff3aaed193ade60771e9e1b3b5a6e84158e72 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Oct 2018 15:45:41 +0900 Subject: [PATCH 0189/2854] Fix filename mismatch --- .../{TestCaseTeamOverview.cs => TestCaseTeamIntro.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename osu.Game.Tournament.Tests/{TestCaseTeamOverview.cs => TestCaseTeamIntro.cs} (100%) diff --git a/osu.Game.Tournament.Tests/TestCaseTeamOverview.cs b/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs similarity index 100% rename from osu.Game.Tournament.Tests/TestCaseTeamOverview.cs rename to osu.Game.Tournament.Tests/TestCaseTeamIntro.cs From 830eda2a9f15f274cd830b62005a62f0119d0346 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Oct 2018 16:07:59 +0900 Subject: [PATCH 0190/2854] Fix unused properties --- osu.Game.Tournament/Components/TournamentTeam.cs | 4 ++++ .../Screens/Ladder/Components/TournamentGrouping.cs | 3 +++ 2 files changed, 7 insertions(+) diff --git a/osu.Game.Tournament/Components/TournamentTeam.cs b/osu.Game.Tournament/Components/TournamentTeam.cs index cb6fc9fb92..78e1386706 100644 --- a/osu.Game.Tournament/Components/TournamentTeam.cs +++ b/osu.Game.Tournament/Components/TournamentTeam.cs @@ -1,11 +1,14 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; +using Newtonsoft.Json; using osu.Game.Users; namespace osu.Game.Tournament.Components { + [Serializable] public class TournamentTeam { /// @@ -35,6 +38,7 @@ namespace osu.Game.Tournament.Components set { acronym = value; } } + [JsonProperty] public List Players { get; set; } public override string ToString() => FullName ?? Acronym; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs index 7530910bc1..17f76a0143 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs @@ -3,10 +3,12 @@ using System; using System.Collections.Generic; +using Newtonsoft.Json; using osu.Framework.Configuration; namespace osu.Game.Tournament.Screens.Ladder.Components { + [Serializable] public class TournamentGrouping { public readonly Bindable Name = new Bindable(); @@ -14,6 +16,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly BindableInt BestOf = new BindableInt(9) { Default = 9, MinValue = 3, MaxValue = 23 }; + [JsonProperty] public readonly List Beatmaps = new List(); public readonly Bindable StartDate = new Bindable(); From 216de3c53d80882e4c3ef49f0d02167890790aa5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Oct 2018 17:34:58 +0900 Subject: [PATCH 0191/2854] Don't cache manager --- osu.Game.Tournament.Tests/TestCaseLadderManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index 35a86e83b8..a7af038ca8 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -10,7 +10,6 @@ namespace osu.Game.Tournament.Tests { public class TestCaseLadderManager : LadderTestCase { - [Cached] private LadderManager manager; [BackgroundDependencyLoader] From 143d9d54f9ae5f8e3141ecf3158f8c66b039a905 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Oct 2018 18:02:47 +0900 Subject: [PATCH 0192/2854] Add basic scene manager --- .../TestCaseSceneManager.cs | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 osu.Game.Tournament.Tests/TestCaseSceneManager.cs diff --git a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs new file mode 100644 index 0000000000..8a56ec883e --- /dev/null +++ b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs @@ -0,0 +1,93 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.UserInterface; +using osu.Game.Tournament.Screens.Ladder; +using osu.Game.Tournament.Screens.TeamIntro; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Tournament.Tests +{ + public class TestCaseSceneManager : LadderTestCase + { + private LadderManager bracket; + private MapPoolScreen mapPool; + private TeamIntroScreen teamIntro; + private Container screens; + + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Width = 0.2f, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuButton { RelativeSizeAxes = Axes.X, Text = "TeamIntro", Action = () => setScreen(teamIntro) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "MapPool", Action = () => setScreen(mapPool) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket", Action = () => setScreen(bracket) }, + } + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Size = new Vector2(0.8f), + Masking = true, + Children = new Drawable[] + { + new Box + { + Colour = Color4.White, + RelativeSizeAxes = Axes.Both, + }, + screens = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + bracket = new LadderManager(Ladder), + mapPool = new MapPoolScreen(Ladder.Groupings.First(g => g.Name == "Finals")), + teamIntro = new TeamIntroScreen(Ladder.Teams.First(t => t.Acronym == "USA"), Ladder.Teams.First(t => t.Acronym == "JPN"), Ladder.Groupings.First(g => g.Name == "Finals")) + } + }, + } + }, + }; + } + + private void setScreen(Drawable screen) + { + foreach (var s in screens.Children) + { + if (s == screen) + s.FadeIn(100); + else + s.FadeOut(100); + } + } + } +} From 7a753ad9e2750dcc6ee0f22b7748250edabf9e1f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Oct 2018 02:17:54 +0900 Subject: [PATCH 0193/2854] Change grouping title colours to match white background --- .../Screens/Ladder/Components/DrawableTournamentGrouping.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs index 8a38f402aa..bb984dd61b 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; +using OpenTK.Graphics; namespace osu.Game.Tournament.Screens.Ladder.Components { @@ -21,6 +22,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components new OsuSpriteText { Text = grouping.Description.Value.ToUpper(), + Colour = Color4.Black, Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre }, @@ -28,6 +30,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { Text = ((losers ? "Losers " : "") + grouping.Name).ToUpper(), Font = "Exo2.0-Bold", + Colour = Color4.Black, Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre }, From 12c0b2c37d208b17e3787af4936da18178e6fe17 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Oct 2018 02:18:09 +0900 Subject: [PATCH 0194/2854] Add test videos and adjust alignment to match --- .../TestCaseSceneManager.cs | 14 ++++++++++---- osu.Game.Tournament.Tests/TestCaseTeamIntro.cs | 7 ++++++- .../Screens/TeamIntro/TeamIntro.cs | 16 ++++++++-------- osu.Game/osu.Game.csproj | 2 +- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs index 8a56ec883e..a1434b3a31 100644 --- a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Video; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Screens.Ladder; using osu.Game.Tournament.Screens.TeamIntro; @@ -53,16 +54,18 @@ namespace osu.Game.Tournament.Tests new Container { RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + FillAspectRatio = 16/9f, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Size = new Vector2(0.8f), + Size = new Vector2(0.8f, 1), Masking = true, Children = new Drawable[] { - new Box + new VideoSprite(@"C:\Users\Dean\BG Side Logo - OWC.m4v") { - Colour = Color4.White, RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, }, screens = new Container { @@ -71,12 +74,15 @@ namespace osu.Game.Tournament.Tests { bracket = new LadderManager(Ladder), mapPool = new MapPoolScreen(Ladder.Groupings.First(g => g.Name == "Finals")), - teamIntro = new TeamIntroScreen(Ladder.Teams.First(t => t.Acronym == "USA"), Ladder.Teams.First(t => t.Acronym == "JPN"), Ladder.Groupings.First(g => g.Name == "Finals")) + teamIntro = new TeamIntroScreen(Ladder.Teams.First(t => t.Acronym == "USA"), Ladder.Teams.First(t => t.Acronym == "JPN"), + Ladder.Groupings.First(g => g.Name == "Finals")) } }, } }, }; + + setScreen(teamIntro); } private void setScreen(Drawable screen) diff --git a/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs b/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs index 52a5a7204c..fff28ba900 100644 --- a/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs +++ b/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs @@ -3,6 +3,7 @@ using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Tournament.Screens.TeamIntro; namespace osu.Game.Tournament.Tests @@ -17,7 +18,11 @@ namespace osu.Game.Tournament.Tests var round = Ladder.Groupings.First(g => g.Name == "Finals"); - Add(new TeamIntroScreen(team1, team2, round)); + Add(new TeamIntroScreen(team1, team2, round) + { + FillMode = FillMode.Fit, + FillAspectRatio = 16 / 9f + }); } } } diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntro.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntro.cs index fce29a2c89..f8820d570a 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntro.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntro.cs @@ -3,7 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Video; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Screens; @@ -22,15 +22,14 @@ namespace osu.Game.Tournament.Screens.TeamIntro InternalChildren = new Drawable[] { - new Box + new VideoSprite(@"C:\Users\Dean\BG Team - Both OWC.m4v") { - Colour = Color4.White, - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, + Loop = true, }, new TeamWithPlayers(team1, true) { RelativeSizeAxes = Axes.Both, - Margin = new MarginPadding(40), Width = 0.5f, Height = 0.6f, Anchor = Anchor.Centre, @@ -39,7 +38,6 @@ namespace osu.Game.Tournament.Screens.TeamIntro new TeamWithPlayers(team2) { RelativeSizeAxes = Axes.Both, - Margin = new MarginPadding(40), Width = 0.5f, Height = 0.6f, Anchor = Anchor.Centre, @@ -48,7 +46,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro new RoundDisplay(round) { RelativeSizeAxes = Axes.Both, - Height = 0.3f, + Height = 0.25f, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, } @@ -119,7 +117,9 @@ namespace osu.Game.Tournament.Screens.TeamIntro new TeamDisplay(team, left ? "Team Red" : "Team Blue", colour) { Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = left ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.Both, + X = (left ? -1 : 1) * 0.36f, }, players = new FillFlowContainer { diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index cc21f4f6a4..cf1863a58c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,7 @@ - + From 510b52a50379c8a8fab72d529c3dc74e9f89822d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C5=8Dtar=C5=8D=20Oreki?= Date: Thu, 18 Oct 2018 20:56:43 +0200 Subject: [PATCH 0195/2854] Update overrided functions to match their bases --- .../Graphics/UserInterface/TooltipIconButton.cs | 9 +++++---- osu.Game/Overlays/Changelog/ChangelogBadges.cs | 9 +++++---- osu.Game/Overlays/Changelog/Header/TextBadgePair.cs | 9 +++++---- .../Changelog/Header/TextBadgePairListing.cs | 13 +++++++------ osu.Game/Overlays/Changelog/StreamBadge.cs | 13 +++++++------ osu.Game/Overlays/ChangelogOverlay.cs | 2 +- 6 files changed, 30 insertions(+), 25 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs index f26df3f8c1..8e26b2d47c 100644 --- a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Framework.Input.States; using System; @@ -69,21 +70,21 @@ namespace osu.Game.Graphics.UserInterface }; } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { if (isEnabled) { sampleClick?.Play(); Action?.Invoke(); } - return base.OnClick(state); + return base.OnClick(e); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { if (isEnabled) sampleHover?.Play(); - return base.OnHover(state); + return base.OnHover(e); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Changelog/ChangelogBadges.cs b/osu.Game/Overlays/Changelog/ChangelogBadges.cs index 365d808108..f5d263bec0 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBadges.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBadges.cs @@ -5,6 +5,7 @@ using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Framework.Input.States; using osu.Game.Online.API.Requests.Responses; using System; @@ -95,7 +96,7 @@ namespace osu.Game.Overlays.Changelog Selected?.Invoke(source.LatestBuild, EventArgs.Empty); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { foreach (StreamBadge streamBadge in badgesContainer.Children) { @@ -109,10 +110,10 @@ namespace osu.Game.Overlays.Changelog else streamBadge.Deactivate(); } - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { foreach (StreamBadge streamBadge in badgesContainer.Children) { @@ -121,7 +122,7 @@ namespace osu.Game.Overlays.Changelog else if (streamBadge.LatestBuild.UpdateStream.Id == selectedStreamId) streamBadge.DisableDim(); } - base.OnHoverLost(state); + base.OnHoverLost(e); } } } diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index dd4ca1ce0b..76db9e4444 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; using osu.Framework.Input.States; using osu.Game.Graphics.UserInterface; using System; @@ -114,18 +115,18 @@ namespace osu.Game.Overlays.Changelog.Header .FadeIn(duration, easing); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { if (!IsActivated) sampleHover?.Play(); - return base.OnHover(state); + return base.OnHover(e); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { OnActivated(); sampleActivate?.Play(); - return base.OnClick(state); + return base.OnClick(e); } protected virtual void OnActivated() diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs index 31a06ce67d..19f1674551 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs @@ -4,6 +4,7 @@ using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; +using osu.Framework.Input.Events; using osu.Framework.Input.States; namespace osu.Game.Overlays.Changelog.Header @@ -48,23 +49,23 @@ namespace osu.Game.Overlays.Changelog.Header SetTextColour(badgeColour, 100); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { Activate(); - return base.OnClick(state); + return base.OnClick(e); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { LineBadge.Uncollapse(); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { if (!IsActivated) LineBadge.Collapse(); - base.OnHoverLost(state); + base.OnHoverLost(e); } public void UpdateBadgeWidth() => LineBadge.ResizeWidthTo(Text.DrawWidth); diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index ad5a858545..552af17eb6 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -8,6 +8,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; using osu.Framework.Input.States; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; @@ -107,23 +108,23 @@ namespace osu.Game.Overlays.Changelog } } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { Activate(false); sampleClick?.Play(); - return base.OnClick(state); + return base.OnClick(e); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { sampleHover?.Play(); DisableDim(); this.FadeIn(transition_duration); lineBadge.Uncollapse(); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { if (!isActivated) { @@ -132,7 +133,7 @@ namespace osu.Game.Overlays.Changelog } else EnableDim(); - base.OnHoverLost(state); + base.OnHoverLost(e); } public void EnableDim() => text.FadeTo(0.5f, transition_duration); diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index a14fa77da3..30b24d40bb 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays private float savedScrollPosition; // receive input outside our bounds so we can trigger a close event on ourselves. - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; public ChangelogOverlay() { From b8ac328ae9a02c639efd5670ca176c80d72feff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C5=8Dtar=C5=8D=20Oreki?= Date: Thu, 18 Oct 2018 21:04:21 +0200 Subject: [PATCH 0196/2854] Rename APIChangelog to APIChangelogBuild --- osu.Game.Tests/Visual/TestCaseChangelogOverlay.cs | 2 +- .../Online/API/Requests/GetChangelogBuildRequest.cs | 2 +- .../API/Requests/GetChangelogLatestBuildsRequest.cs | 2 +- osu.Game/Online/API/Requests/GetChangelogRequest.cs | 2 +- .../{APIChangelog.cs => APIChangelogBuild.cs} | 6 +++--- osu.Game/Overlays/Changelog/ChangelogBadges.cs | 6 +++--- osu.Game/Overlays/Changelog/ChangelogContent.cs | 10 +++++----- osu.Game/Overlays/Changelog/ChangelogContentGroup.cs | 8 ++++---- osu.Game/Overlays/Changelog/StreamBadge.cs | 4 ++-- osu.Game/Overlays/ChangelogOverlay.cs | 10 +++++----- 10 files changed, 26 insertions(+), 26 deletions(-) rename osu.Game/Online/API/Requests/Responses/{APIChangelog.cs => APIChangelogBuild.cs} (95%) diff --git a/osu.Game.Tests/Visual/TestCaseChangelogOverlay.cs b/osu.Game.Tests/Visual/TestCaseChangelogOverlay.cs index d64f1c00f7..2bd8cc2016 100644 --- a/osu.Game.Tests/Visual/TestCaseChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseChangelogOverlay.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual AddWaitStep(3); AddStep(@"Show with Lazer 2018.712.0", () => { - changelog.FetchAndShowBuild(new APIChangelog + changelog.FetchAndShowBuild(new APIChangelogBuild { Version = "2018.712.0", UpdateStream = new UpdateStream { Name = "lazer" }, diff --git a/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs b/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs index 2338b90865..73d0ec6d20 100644 --- a/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs +++ b/osu.Game/Online/API/Requests/GetChangelogBuildRequest.cs @@ -5,7 +5,7 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Online.API.Requests { - public class GetChangelogBuildRequest : APIRequest + public class GetChangelogBuildRequest : APIRequest { private readonly string name; private readonly string version; diff --git a/osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs b/osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs index 7940fd8ff5..314a03e7f6 100644 --- a/osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs +++ b/osu.Game/Online/API/Requests/GetChangelogLatestBuildsRequest.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; namespace osu.Game.Online.API.Requests { - public class GetChangelogLatestBuildsRequest : APIRequest> + public class GetChangelogLatestBuildsRequest : APIRequest> { protected override string Target => @"changelog/latest-builds"; protected override string Uri => $@"https://houtarouoreki.github.io/fake-api/{Target}"; // for testing diff --git a/osu.Game/Online/API/Requests/GetChangelogRequest.cs b/osu.Game/Online/API/Requests/GetChangelogRequest.cs index ec8536c607..b43f8d8ee1 100644 --- a/osu.Game/Online/API/Requests/GetChangelogRequest.cs +++ b/osu.Game/Online/API/Requests/GetChangelogRequest.cs @@ -5,7 +5,7 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Online.API.Requests { - public class GetChangelogRequest : APIRequest + public class GetChangelogRequest : APIRequest { protected override string Target => @"changelog"; protected override string Uri => $@"https://houtarouoreki.github.io/fake-api/{Target}/index"; // for testing diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelog.cs b/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs similarity index 95% rename from osu.Game/Online/API/Requests/Responses/APIChangelog.cs rename to osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs index da5437515c..86ee8b49dd 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelog.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; namespace osu.Game.Online.API.Requests.Responses { - public class APIChangelog + public class APIChangelogBuild { [JsonProperty("id")] public long Id { get; set; } @@ -40,10 +40,10 @@ namespace osu.Game.Online.API.Requests.Responses public class Versions { [JsonProperty("next")] - public APIChangelog Next { get; set; } + public APIChangelogBuild Next { get; set; } [JsonProperty("previous")] - public APIChangelog Previous { get; set; } + public APIChangelogBuild Previous { get; set; } } public class ChangelogEntry diff --git a/osu.Game/Overlays/Changelog/ChangelogBadges.cs b/osu.Game/Overlays/Changelog/ChangelogBadges.cs index f5d263bec0..875aa9cd58 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBadges.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBadges.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Changelog private const float vertical_padding = 20; private const float horizontal_padding = 85; - public delegate void SelectionHandler(APIChangelog releaseStream, EventArgs args); + public delegate void SelectionHandler(APIChangelogBuild releaseStream, EventArgs args); public event SelectionHandler Selected; @@ -46,9 +46,9 @@ namespace osu.Game.Overlays.Changelog }; } - public void Populate(List latestBuilds) + public void Populate(List latestBuilds) { - foreach (APIChangelog updateStream in latestBuilds) + foreach (APIChangelogBuild updateStream in latestBuilds) { var streamBadge = new StreamBadge(updateStream); streamBadge.Selected += onBadgeSelected; diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 588a61f678..fadfb9048f 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Changelog private APIAccess api; private ChangelogContentGroup changelogContentGroup; - public delegate void BuildSelectedEventHandler(APIChangelog build, EventArgs args); + public delegate void BuildSelectedEventHandler(APIChangelogBuild build, EventArgs args); public event BuildSelectedEventHandler BuildSelected; @@ -28,12 +28,12 @@ namespace osu.Game.Overlays.Changelog Padding = new MarginPadding{ Bottom = 100 }; } - public void ShowListing(APIChangelog[] changelog) + public void ShowListing(APIChangelogBuild[] changelog) { DateTime currentDate = new DateTime(); Clear(); - foreach (APIChangelog build in changelog) + foreach (APIChangelogBuild build in changelog) { if (build.CreatedAt.Date != currentDate) { @@ -71,7 +71,7 @@ namespace osu.Game.Overlays.Changelog } } - public void ShowBuild(APIChangelog changelogBuild) + public void ShowBuild(APIChangelogBuild changelogBuild) { Child = changelogContentGroup = new ChangelogContentGroup(changelogBuild); changelogContentGroup.GenerateText(changelogBuild.ChangelogEntries); @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Changelog changelogContentGroup.BuildSelected += OnBuildSelected; } - protected virtual void OnBuildSelected(APIChangelog build, EventArgs args) + protected virtual void OnBuildSelected(APIChangelogBuild build, EventArgs args) { BuildSelected?.Invoke(build, EventArgs.Empty); } diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index c349f44d37..2dc8fd2bf7 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -21,13 +21,13 @@ namespace osu.Game.Overlays.Changelog private readonly SortedDictionary> categories = new SortedDictionary>(); - public delegate void BuildSelectedEventHandler(APIChangelog build, EventArgs args); + public delegate void BuildSelectedEventHandler(APIChangelogBuild build, EventArgs args); public event BuildSelectedEventHandler BuildSelected; public readonly FillFlowContainer ChangelogEntries; - public ChangelogContentGroup(APIChangelog build) + public ChangelogContentGroup(APIChangelogBuild build) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -115,7 +115,7 @@ namespace osu.Game.Overlays.Changelog }; } - public ChangelogContentGroup(APIChangelog build, bool newDate) + public ChangelogContentGroup(APIChangelogBuild build, bool newDate) { OsuHoverContainer clickableBuildText; RelativeSizeAxes = Axes.X; @@ -202,7 +202,7 @@ namespace osu.Game.Overlays.Changelog } } - protected virtual void OnBuildSelected(APIChangelog build) + protected virtual void OnBuildSelected(APIChangelogBuild build) { BuildSelected?.Invoke(build, EventArgs.Empty); } diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index 552af17eb6..5fb466769e 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -33,10 +33,10 @@ namespace osu.Game.Overlays.Changelog private SampleChannel sampleClick; private SampleChannel sampleHover; - public readonly APIChangelog LatestBuild; + public readonly APIChangelogBuild LatestBuild; private readonly FillFlowContainer text; - public StreamBadge(APIChangelog latestBuild) + public StreamBadge(APIChangelogBuild latestBuild) { LatestBuild = latestBuild; Height = badge_height; diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 30b24d40bb..302a9b351f 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -152,7 +152,7 @@ namespace osu.Game.Overlays return false; } - private void onBuildSelected(APIChangelog build, EventArgs e) => FetchAndShowBuild(build); + private void onBuildSelected(APIChangelogBuild build, EventArgs e) => FetchAndShowBuild(build); private void fetchListing() { @@ -183,12 +183,12 @@ namespace osu.Game.Overlays /// /// Fetches and shows a specific build from a specific update stream. /// - /// Must contain at least and - /// . If and - /// are specified, the header will instantly display them. + /// Must contain at least and + /// . If and + /// are specified, the header will instantly display them. /// Whether to update badges. Should be set to false in case /// the function is called by selecting a badge, to avoid an infinite loop. - public void FetchAndShowBuild(APIChangelog build, bool updateBadges = true) + public void FetchAndShowBuild(APIChangelogBuild build, bool updateBadges = true) { var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); From 390d8d230fb41bd01de3d9d0af1f24eadcef30a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C5=8Dtar=C5=8D=20Oreki?= Date: Thu, 18 Oct 2018 21:30:09 +0200 Subject: [PATCH 0197/2854] Remove redundant usings --- osu.Game/Graphics/UserInterface/TooltipIconButton.cs | 1 - osu.Game/Overlays/Changelog/ChangelogBadges.cs | 1 - osu.Game/Overlays/Changelog/Header/TextBadgePair.cs | 1 - osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs | 1 - osu.Game/Overlays/Changelog/StreamBadge.cs | 1 - 5 files changed, 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs index 8e26b2d47c..1233483054 100644 --- a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs +++ b/osu.Game/Graphics/UserInterface/TooltipIconButton.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Framework.Input.States; using System; namespace osu.Game.Graphics.UserInterface diff --git a/osu.Game/Overlays/Changelog/ChangelogBadges.cs b/osu.Game/Overlays/Changelog/ChangelogBadges.cs index 875aa9cd58..eabe5fe258 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBadges.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBadges.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Framework.Input.States; using osu.Game.Online.API.Requests.Responses; using System; using System.Collections.Generic; diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs index 76db9e4444..387b7d4405 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePair.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; -using osu.Framework.Input.States; using osu.Game.Graphics.UserInterface; using System; diff --git a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs index 19f1674551..ab4165b30b 100644 --- a/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs +++ b/osu.Game/Overlays/Changelog/Header/TextBadgePairListing.cs @@ -5,7 +5,6 @@ using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Input.Events; -using osu.Framework.Input.States; namespace osu.Game.Overlays.Changelog.Header { diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index 5fb466769e..dcae8dc04f 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; -using osu.Framework.Input.States; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; From 5568e9ff8a6b9993dae33e41388e768a67366f74 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Oct 2018 01:29:08 +0900 Subject: [PATCH 0198/2854] Reduce test case crashes when missing data is present --- osu.Game.Tournament.Tests/TestCaseMapPool.cs | 7 +++-- .../TestCaseTeamIntro.cs | 6 ++-- .../Screens/TeamIntro/TeamIntro.cs | 28 +++++++++++-------- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseMapPool.cs b/osu.Game.Tournament.Tests/TestCaseMapPool.cs index 10ebd4e63c..e4637b8fc3 100644 --- a/osu.Game.Tournament.Tests/TestCaseMapPool.cs +++ b/osu.Game.Tournament.Tests/TestCaseMapPool.cs @@ -17,9 +17,10 @@ namespace osu.Game.Tournament.Tests [BackgroundDependencyLoader] private void load() { - var round = Ladder.Groupings.First(g => g.Name == "Finals"); + var round = Ladder.Groupings.FirstOrDefault(g => g.Name == "Finals"); - Add(new MapPoolScreen(round)); + if (round != null) + Add(new MapPoolScreen(round)); } } @@ -37,7 +38,7 @@ namespace osu.Game.Tournament.Tests Padding = new MarginPadding(50), Direction = FillDirection.Full, RelativeSizeAxes = Axes.Both, - }, + } }; foreach (var b in round.Beatmaps) diff --git a/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs b/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs index fff28ba900..845f5638a0 100644 --- a/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs +++ b/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs @@ -13,10 +13,10 @@ namespace osu.Game.Tournament.Tests [BackgroundDependencyLoader] private void load() { - var team1 = Ladder.Teams.First(t => t.Acronym == "USA"); - var team2 = Ladder.Teams.First(t => t.Acronym == "JPN"); + var team1 = Ladder.Teams.FirstOrDefault(t => t.Acronym == "USA"); + var team2 = Ladder.Teams.FirstOrDefault(t => t.Acronym == "JPN"); - var round = Ladder.Groupings.First(g => g.Name == "Finals"); + var round = Ladder.Groupings.FirstOrDefault(g => g.Name == "Finals"); Add(new TeamIntroScreen(team1, team2, round) { diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntro.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntro.cs index f8820d570a..766feaa806 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntro.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntro.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 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.Containers; using osu.Framework.Graphics.Video; @@ -84,7 +85,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Colour = col, - Text = group.Name.Value, + Text = group?.Name.Value ?? "Unknown Grouping", Font = "Exo2.0-Light", Spacing = new Vector2(10, 0), TextSize = 50, @@ -94,7 +95,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Colour = col, - Text = group.StartDate.Value.ToString("dd MMMM HH:mm UTC"), + Text = (group?.StartDate.Value ?? DateTimeOffset.Now).ToString("dd MMMM HH:mm UTC"), TextSize = 20, }, } @@ -134,15 +135,18 @@ namespace osu.Game.Tournament.Screens.TeamIntro }, }; - foreach (var p in team.Players) - players.Add(new OsuSpriteText - { - Text = p.Username, - TextSize = 24, - Colour = colour, - Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = left ? Anchor.CentreRight : Anchor.CentreLeft, - }); + if (team != null) + { + foreach (var p in team.Players) + players.Add(new OsuSpriteText + { + Text = p.Username, + TextSize = 24, + Colour = colour, + Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = left ? Anchor.CentreRight : Anchor.CentreLeft, + }); + } } private class TeamDisplay : DrawableTournamentTeam @@ -168,7 +172,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Flag, new OsuSpriteText { - Text = team.FullName.ToUpper(), + Text = team?.FullName.ToUpper() ?? "???", TextSize = 40, Colour = Color4.Black, Font = "Exo2.0-Light", From eacc0fe7964ca94fcdaf9ad170e4804ae3d14ab3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 2 Sep 2018 02:12:09 +0900 Subject: [PATCH 0199/2854] Use local framework --- osu.Game/osu.Game.csproj | 2 +- osu.sln | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index cf1863a58c..3022b66762 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -11,6 +11,7 @@ + @@ -18,7 +19,6 @@ - diff --git a/osu.sln b/osu.sln index f6ed7a5c42..c62fc02dba 100644 --- a/osu.sln +++ b/osu.sln @@ -31,6 +31,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tournament", "osu. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tournament.Tests", "osu.Game.Tournament.Tests\osu.Game.Tournament.Tests.csproj", "{5789E78D-38F9-4072-AB7B-978F34B2C17F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework", "..\osu-framework\osu.Framework\osu.Framework.csproj", "{7A69A230-45A1-4444-8C43-A582E4F48C1E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -93,6 +95,10 @@ Global {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Debug|Any CPU.Build.0 = Debug|Any CPU {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Release|Any CPU.ActiveCfg = Release|Any CPU {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Release|Any CPU.Build.0 = Release|Any CPU + {7A69A230-45A1-4444-8C43-A582E4F48C1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A69A230-45A1-4444-8C43-A582E4F48C1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A69A230-45A1-4444-8C43-A582E4F48C1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A69A230-45A1-4444-8C43-A582E4F48C1E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From d7d83a27d40289d73e819984721a9b18a0fa94e6 Mon Sep 17 00:00:00 2001 From: Paul Teng Date: Wed, 24 Oct 2018 13:36:35 -0400 Subject: [PATCH 0200/2854] Add restart as mods settings --- osu.Game/Configuration/OsuConfigManager.cs | 2 ++ osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 9ac2cabe9f..bcdd3acd10 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -84,6 +84,7 @@ namespace osu.Game.Configuration Set(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised); Set(OsuSetting.IncreaseFirstObjectVisibility, true); + Set(OsuSetting.RestartOnFail, false); // Update Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer); @@ -148,6 +149,7 @@ namespace osu.Game.Configuration BeatmapSkins, BeatmapHitsounds, IncreaseFirstObjectVisibility, + RestartOnFail, ScoreDisplayMode } } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs index a9cefa81da..a3a0bf118b 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs @@ -20,6 +20,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay LabelText = "Increase visibility of first object with \"Hidden\" mod", Bindable = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility) }, + new SettingsCheckbox + { + LabelText = "Restart on fail with \"Perfect\" and \"Sudden Death\" mod", + Bindable = config.GetBindable(OsuSetting.RestartOnFail) + }, }; } } From 794afa988fe6e599ca9e13a1de130d7a765bf4c0 Mon Sep 17 00:00:00 2001 From: Paul Teng Date: Wed, 24 Oct 2018 13:37:27 -0400 Subject: [PATCH 0201/2854] Make both SD and PF auto-restart based on settings --- osu.Game/Rulesets/Mods/ModPerfect.cs | 4 +--- osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 12 +++++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModPerfect.cs b/osu.Game/Rulesets/Mods/ModPerfect.cs index 5d6065b4a2..802890866f 100644 --- a/osu.Game/Rulesets/Mods/ModPerfect.cs +++ b/osu.Game/Rulesets/Mods/ModPerfect.cs @@ -6,15 +6,13 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mods { - public abstract class ModPerfect : ModSuddenDeath, IApplicableRestartOnFail + public abstract class ModPerfect : ModSuddenDeath { public override string Name => "Perfect"; public override string ShortenedName => "PF"; public override FontAwesome Icon => FontAwesome.fa_osu_mod_perfect; public override string Description => "SS or quit."; - public bool AllowRestart => true; - protected override bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Accuracy.Value != 1; } } diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index 48f7d496a5..af7d9be785 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -3,11 +3,13 @@ using System; using osu.Game.Graphics; +using osu.Game.Configuration; using osu.Game.Rulesets.Scoring; +using osu.Framework.Configuration; namespace osu.Game.Rulesets.Mods { - public abstract class ModSuddenDeath : Mod, IApplicableToScoreProcessor + public abstract class ModSuddenDeath : Mod, IReadFromConfig, IApplicableToScoreProcessor, IApplicableRestartOnFail { public override string Name => "Sudden Death"; public override string ShortenedName => "SD"; @@ -18,11 +20,19 @@ namespace osu.Game.Rulesets.Mods public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) }; + protected Bindable RestartOnFail = new Bindable(); + public bool AllowRestart => RestartOnFail.Value; + public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { scoreProcessor.FailConditions += FailCondition; } + public void ReadFromConfig(OsuConfigManager config) + { + RestartOnFail = config.GetBindable(OsuSetting.RestartOnFail); + } + protected virtual bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Combo.Value == 0; } } From cb9ec94dc2af3c511554b9a8a6ce0439164938c9 Mon Sep 17 00:00:00 2001 From: Paul Teng Date: Mon, 29 Oct 2018 08:19:38 -0400 Subject: [PATCH 0202/2854] Remove option from settings --- osu.Game/Configuration/OsuConfigManager.cs | 2 -- .../Settings/Sections/Gameplay/ModsSettings.cs | 5 ----- osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 10 ++-------- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index bcdd3acd10..9ac2cabe9f 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -84,7 +84,6 @@ namespace osu.Game.Configuration Set(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised); Set(OsuSetting.IncreaseFirstObjectVisibility, true); - Set(OsuSetting.RestartOnFail, false); // Update Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer); @@ -149,7 +148,6 @@ namespace osu.Game.Configuration BeatmapSkins, BeatmapHitsounds, IncreaseFirstObjectVisibility, - RestartOnFail, ScoreDisplayMode } } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs index a3a0bf118b..a9cefa81da 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs @@ -20,11 +20,6 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay LabelText = "Increase visibility of first object with \"Hidden\" mod", Bindable = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility) }, - new SettingsCheckbox - { - LabelText = "Restart on fail with \"Perfect\" and \"Sudden Death\" mod", - Bindable = config.GetBindable(OsuSetting.RestartOnFail) - }, }; } } diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index af7d9be785..77e08dff86 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -9,7 +9,7 @@ using osu.Framework.Configuration; namespace osu.Game.Rulesets.Mods { - public abstract class ModSuddenDeath : Mod, IReadFromConfig, IApplicableToScoreProcessor, IApplicableRestartOnFail + public abstract class ModSuddenDeath : Mod, IApplicableToScoreProcessor, IApplicableRestartOnFail { public override string Name => "Sudden Death"; public override string ShortenedName => "SD"; @@ -20,19 +20,13 @@ namespace osu.Game.Rulesets.Mods public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) }; - protected Bindable RestartOnFail = new Bindable(); - public bool AllowRestart => RestartOnFail.Value; + public bool AllowRestart => true; public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { scoreProcessor.FailConditions += FailCondition; } - public void ReadFromConfig(OsuConfigManager config) - { - RestartOnFail = config.GetBindable(OsuSetting.RestartOnFail); - } - protected virtual bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Combo.Value == 0; } } From 52b9a3f5e9a0096ff5529a223a317b8b489977ec Mon Sep 17 00:00:00 2001 From: Paul Teng Date: Mon, 29 Oct 2018 17:51:49 -0400 Subject: [PATCH 0203/2854] Remove unused using --- osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index 77e08dff86..733fd6345a 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -3,9 +3,7 @@ using System; using osu.Game.Graphics; -using osu.Game.Configuration; using osu.Game.Rulesets.Scoring; -using osu.Framework.Configuration; namespace osu.Game.Rulesets.Mods { From 007dfedbb751fa0e4767740e39fcc621e73cdf54 Mon Sep 17 00:00:00 2001 From: Paul Teng Date: Tue, 30 Oct 2018 07:12:06 -0400 Subject: [PATCH 0204/2854] Combine RestartOnFail into FailOverride --- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 1 + .../Rulesets/Mods/IApplicableFailOverride.cs | 5 +++++ .../Rulesets/Mods/IApplicableRestartOnFail.cs | 16 ---------------- osu.Game/Rulesets/Mods/ModAutoplay.cs | 1 + osu.Game/Rulesets/Mods/ModNoFail.cs | 1 + osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 5 +++-- osu.Game/Screens/Play/Player.cs | 2 +- 7 files changed, 12 insertions(+), 19 deletions(-) delete mode 100644 osu.Game/Rulesets/Mods/IApplicableRestartOnFail.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 8d27502b3c..2240425654 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -19,6 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); public bool AllowFail => false; + public bool RestartOnFail => false; public void Update(Playfield playfield) { diff --git a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs index 6a4042a906..f650259373 100644 --- a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs +++ b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs @@ -12,5 +12,10 @@ namespace osu.Game.Rulesets.Mods /// Whether we should allow failing at the current point in time. /// bool AllowFail { get; } + + /// + /// Whether we want to restart on fail. Only used if AllowFail is true. + /// + bool RestartOnFail { get; } } } diff --git a/osu.Game/Rulesets/Mods/IApplicableRestartOnFail.cs b/osu.Game/Rulesets/Mods/IApplicableRestartOnFail.cs deleted file mode 100644 index 43b3f36624..0000000000 --- a/osu.Game/Rulesets/Mods/IApplicableRestartOnFail.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mods -{ - /// - /// Represents a mod which can request to restart on fail. - /// - public interface IApplicableRestartOnFail : IApplicableMod - { - /// - /// Whether we allow restarting - /// - bool AllowRestart { get; } - } -} diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index 5c03cb9736..849eaeeb5a 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -30,6 +30,7 @@ namespace osu.Game.Rulesets.Mods public override string Description => "Watch a perfect automated play through the song."; public override double ScoreMultiplier => 1; public bool AllowFail => false; + public bool RestartOnFail => false; public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) }; } } diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs index 7510f62432..c30c6d712d 100644 --- a/osu.Game/Rulesets/Mods/ModNoFail.cs +++ b/osu.Game/Rulesets/Mods/ModNoFail.cs @@ -21,5 +21,6 @@ namespace osu.Game.Rulesets.Mods /// We never fail, 'yo. /// public bool AllowFail => false; + public bool RestartOnFail => false; } } diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index 733fd6345a..252df98f32 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mods { - public abstract class ModSuddenDeath : Mod, IApplicableToScoreProcessor, IApplicableRestartOnFail + public abstract class ModSuddenDeath : Mod, IApplicableToScoreProcessor, IApplicableFailOverride { public override string Name => "Sudden Death"; public override string ShortenedName => "SD"; @@ -18,7 +18,8 @@ namespace osu.Game.Rulesets.Mods public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) }; - public bool AllowRestart => true; + public bool AllowFail => true; + public bool RestartOnFail => true; public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0577369b05..863cfeda07 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -289,7 +289,7 @@ namespace osu.Game.Screens.Play if (Beatmap.Value.Mods.Value.OfType().Any(m => !m.AllowFail)) return false; - if (Beatmap.Value.Mods.Value.OfType().Any(m => m.AllowRestart)) + if (Beatmap.Value.Mods.Value.OfType().Any(m => m.RestartOnFail)) { Restart(); return false; From d8f97a32c779f40005c6328c925ec9c327a5b54a Mon Sep 17 00:00:00 2001 From: Paul Teng Date: Wed, 31 Oct 2018 07:03:37 -0400 Subject: [PATCH 0205/2854] Make sure restart on fail actually fails --- osu.Game/Screens/Play/Player.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 863cfeda07..b388387866 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -289,15 +289,16 @@ namespace osu.Game.Screens.Play if (Beatmap.Value.Mods.Value.OfType().Any(m => !m.AllowFail)) return false; - if (Beatmap.Value.Mods.Value.OfType().Any(m => m.RestartOnFail)) - { - Restart(); - return false; - } - adjustableClock.Stop(); HasFailed = true; + + if (Beatmap.Value.Mods.Value.OfType().Any(m => m.RestartOnFail)) + { + Restart(); + return true; + } + failOverlay.Retries = RestartCount; failOverlay.Show(); return true; From 9f5546fd62f92a863ce7e0b930271a7ab936ed44 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 2 Nov 2018 21:13:46 +0900 Subject: [PATCH 0206/2854] Fix API regression in direct --- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 4 ++-- osu.Game/osu.Game.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index c9ea66d05f..55eee11664 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -61,13 +61,13 @@ namespace osu.Game.Online.API.Requests.Responses { return new BeatmapInfo { - Metadata = !string.IsNullOrEmpty(Artist) ? this : (BeatmapMetadata)BeatmapSet, + Metadata = !string.IsNullOrEmpty(Artist) ? this : BeatmapSet as BeatmapMetadata, Ruleset = rulesets.GetRuleset(ruleset), StarDifficulty = starDifficulty, OnlineBeatmapID = OnlineBeatmapID, Version = version, Status = Status, - BeatmapSet = BeatmapSet.ToBeatmapSet(rulesets), + BeatmapSet = BeatmapSet?.ToBeatmapSet(rulesets), BaseDifficulty = new BeatmapDifficulty { DrainRate = drainRate, diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 921da942f0..e641c93ad3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -19,7 +19,7 @@ - + From b610095ff598e29e0020dcd68ca75705feac1c7c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Nov 2018 04:58:35 +0900 Subject: [PATCH 0207/2854] Update drawings design --- .../Components/ScrollingTeamContainer.cs | 3 + .../Screens/Drawings/DrawingsScreen.cs | 260 +++++++++--------- 2 files changed, 131 insertions(+), 132 deletions(-) diff --git a/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs b/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs index 816a3ef958..6e050fd0bb 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Threading; +using osu.Game.Graphics; using osu.Game.Tournament.Components; using OpenTK; using OpenTK.Graphics; @@ -52,6 +53,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components Origin = Anchor.Centre, AutoSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.33f), Masking = true, CornerRadius = 10f, @@ -358,6 +360,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components outline = new Box { RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.33f), Alpha = 0 }, Flag diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index 8e6738d8d6..38f0be0d5a 100644 --- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -12,13 +12,14 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Video; using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Framework.Platform; +using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Screens; -using osu.Game.Screens.Backgrounds; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Drawings.Components; using OpenTK; @@ -32,7 +33,7 @@ namespace osu.Game.Tournament.Screens.Drawings protected override bool HideOverlaysOnEnter => true; - protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault(); + protected override BackgroundScreen CreateBackground() => null; private ScrollingTeamContainer teamsContainer; private GroupContainer groupsContainer; @@ -56,6 +57,8 @@ namespace osu.Game.Tournament.Screens.Drawings [BackgroundDependencyLoader] private void load(TextureStore textures, Storage storage) { + RelativeSizeAxes = Axes.Both; + this.storage = storage; TextureStore flagStore = new TextureStore(); @@ -79,168 +82,161 @@ namespace osu.Game.Tournament.Screens.Drawings Children = new Drawable[] { - new Box + // Main container + new Container { RelativeSizeAxes = Axes.Both, - Colour = new Color4(77, 77, 77, 255) + Children = new Drawable[] + { + new VideoSprite(@"C:\Users\Dean\BG Logoless - OWC.m4v") + { + RelativeSizeAxes = Axes.Both, + Loop = true, + }, + new Sprite + { + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fill, + Texture = textures.Get(@"Backgrounds/Drawings/background.png") + }, + // Visualiser + new VisualiserContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + + RelativeSizeAxes = Axes.X, + Size = new Vector2(1, 10), + + Colour = new Color4(255, 204, 34, 255), + + Lines = 6 + }, + // Groups + groupsContainer = new GroupContainer(drawingsConfig.Get(DrawingsConfig.Groups), drawingsConfig.Get(DrawingsConfig.TeamsPerGroup)) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + + Padding = new MarginPadding + { + Top = 35f, + Bottom = 35f + } + }, + // Scrolling teams + teamsContainer = new ScrollingTeamContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + + RelativeSizeAxes = Axes.X, + }, + // Scrolling team name + fullTeamNameText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + + Position = new Vector2(0, 45f), + + Colour = OsuColour.Gray(0.33f), + + Alpha = 0, + + Font = "Exo2.0-Light", + TextSize = 42f + } + } }, - new Sprite + // Control panel container + new Container { RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fill, - Texture = textures.Get(@"Backgrounds/Drawings/background.png") - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, + AlwaysPresent = true, + Width = 0.15f, + Anchor = Anchor.TopRight, Children = new Drawable[] { - // Main container - new Container + new Box { RelativeSizeAxes = Axes.Both, - Width = 0.85f, + Colour = new Color4(54, 54, 54, 255) + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + Text = "Control Panel", + TextSize = 22f, + Font = "Exo2.0-Bold" + }, + new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.75f, + + Position = new Vector2(0, 35f), + + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5f), Children = new Drawable[] { - // Visualiser - new VisualiserContainer + new TriangleButton { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - Size = new Vector2(1, 10), - Colour = new Color4(255, 204, 34, 255), - - Lines = 6 + Text = "Begin random", + Action = teamsContainer.StartScrolling, }, - // Groups - groupsContainer = new GroupContainer(drawingsConfig.Get(DrawingsConfig.Groups), drawingsConfig.Get(DrawingsConfig.TeamsPerGroup)) + new TriangleButton { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - - Padding = new MarginPadding - { - Top = 35f, - Bottom = 35f - } - }, - // Scrolling teams - teamsContainer = new ScrollingTeamContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, + + Text = "Stop random", + Action = teamsContainer.StopScrolling, }, - // Scrolling team name - fullTeamNameText = new OsuSpriteText + new TriangleButton { - Anchor = Anchor.Centre, - Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, - Position = new Vector2(0, 45f), - - Alpha = 0, - - Font = "Exo2.0-Light", - TextSize = 42f + Text = "Reload", + Action = reloadTeams } } }, - // Control panel container - new Container + new FillFlowContainer { - RelativeSizeAxes = Axes.Both, - Width = 0.15f, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.75f, + + Position = new Vector2(0, -5f), + + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5f), Children = new Drawable[] { - new Box + new TriangleButton { - RelativeSizeAxes = Axes.Both, - Colour = new Color4(54, 54, 54, 255) - }, - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - - Text = "Control Panel", - TextSize = 22f, - Font = "Exo2.0-Bold" - }, - new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Width = 0.75f, - Position = new Vector2(0, 35f), - - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5f), - - Children = new Drawable[] - { - new TriangleButton - { - RelativeSizeAxes = Axes.X, - - Text = "Begin random", - Action = teamsContainer.StartScrolling, - }, - new TriangleButton - { - RelativeSizeAxes = Axes.X, - - Text = "Stop random", - Action = teamsContainer.StopScrolling, - }, - new TriangleButton - { - RelativeSizeAxes = Axes.X, - - Text = "Reload", - Action = reloadTeams - } - } - }, - new FillFlowContainer - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Width = 0.75f, - - Position = new Vector2(0, -5f), - - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5f), - - Children = new Drawable[] - { - new TriangleButton - { - RelativeSizeAxes = Axes.X, - - Text = "Reset", - Action = () => reset() - } - } + Text = "Reset", + Action = () => reset() } } } From a918e83b1e4b1523c48b4d54cdb1a81eb88b6982 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Nov 2018 04:58:59 +0900 Subject: [PATCH 0208/2854] Add drawings screen --- osu.Game.Tournament.Tests/TestCaseSceneManager.cs | 15 +++++++++++---- .../Screens/Ladder/Components/ProgressionPath.cs | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs index a1434b3a31..9454a5674c 100644 --- a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Video; using osu.Game.Graphics.UserInterface; +using osu.Game.Tournament.Screens.Drawings; using osu.Game.Tournament.Screens.Ladder; using osu.Game.Tournament.Screens.TeamIntro; using OpenTK; @@ -20,6 +21,7 @@ namespace osu.Game.Tournament.Tests private LadderManager bracket; private MapPoolScreen mapPool; private TeamIntroScreen teamIntro; + private DrawingsScreen drawings; private Container screens; [BackgroundDependencyLoader] @@ -44,6 +46,7 @@ namespace osu.Game.Tournament.Tests Direction = FillDirection.Vertical, Children = new Drawable[] { + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Drawings", Action = () => setScreen(drawings) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "TeamIntro", Action = () => setScreen(teamIntro) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "MapPool", Action = () => setScreen(mapPool) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket", Action = () => setScreen(bracket) }, @@ -54,16 +57,19 @@ namespace osu.Game.Tournament.Tests new Container { RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Both, + X = 0.2f, FillMode = FillMode.Fit, FillAspectRatio = 16/9f, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, Size = new Vector2(0.8f, 1), - Masking = true, + //Masking = true, Children = new Drawable[] { new VideoSprite(@"C:\Users\Dean\BG Side Logo - OWC.m4v") { + Loop = true, RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, }, @@ -75,7 +81,8 @@ namespace osu.Game.Tournament.Tests bracket = new LadderManager(Ladder), mapPool = new MapPoolScreen(Ladder.Groupings.First(g => g.Name == "Finals")), teamIntro = new TeamIntroScreen(Ladder.Teams.First(t => t.Acronym == "USA"), Ladder.Teams.First(t => t.Acronym == "JPN"), - Ladder.Groupings.First(g => g.Name == "Finals")) + Ladder.Groupings.First(g => g.Name == "Finals")), + drawings = new DrawingsScreen() } }, } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs index 4496430e79..880b2fefa4 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs @@ -56,7 +56,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components var p3 = new Vector2(p2.X, c2.Y); var p4 = new Vector2(c2.X, p3.Y); - Positions = new[] { p1, p2, p3, p4 }.Select(ToLocalSpace).ToList(); + Vertices = new[] { p1, p2, p3, p4 }.Select(ToLocalSpace).ToList(); } } } From 97b32a72f52ccb255e505710c8cde83d5b1ba69c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Nov 2018 04:59:11 +0900 Subject: [PATCH 0209/2854] Read and write bracket from storage instead of arbitrary location --- osu.Game.Tournament.Tests/LadderTestCase.cs | 34 ++++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tournament.Tests/LadderTestCase.cs b/osu.Game.Tournament.Tests/LadderTestCase.cs index ee8cfb8f8a..ebc78e14be 100644 --- a/osu.Game.Tournament.Tests/LadderTestCase.cs +++ b/osu.Game.Tournament.Tests/LadderTestCase.cs @@ -5,6 +5,7 @@ using System.IO; using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; @@ -17,7 +18,10 @@ namespace osu.Game.Tournament.Tests { public abstract class LadderTestCase : OsuTestCase { + private const string bracket_filename = "bracket.json"; + protected LadderInfo Ladder; + private Storage storage; [Resolved] private APIAccess api { get; set; } = null; @@ -26,9 +30,19 @@ namespace osu.Game.Tournament.Tests private RulesetStore rulesets { get; set; } = null; [BackgroundDependencyLoader] - private void load() + private void load(Storage storage) { - Ladder = File.Exists(@"bracket.json") ? JsonConvert.DeserializeObject(File.ReadAllText(@"bracket.json")) : new LadderInfo(); + this.storage = storage; + + string content = null; + if (storage.Exists(bracket_filename)) + { + using (Stream stream = storage.GetStream(bracket_filename, FileAccess.Read, FileMode.Open)) + using (var sr = new StreamReader(stream)) + content = sr.ReadToEnd(); + } + + Ladder = content != null ? JsonConvert.DeserializeObject(content) : new LadderInfo(); bool addedInfo = false; @@ -62,12 +76,16 @@ namespace osu.Game.Tournament.Tests protected virtual void SaveChanges() { - File.WriteAllText(@"bracket.json", JsonConvert.SerializeObject(Ladder, - new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - DefaultValueHandling = DefaultValueHandling.Ignore - })); + using (var stream = storage.GetStream(bracket_filename, FileAccess.Write, FileMode.Create)) + using (var sw = new StreamWriter(stream)) + { + sw.Write(JsonConvert.SerializeObject(Ladder, + new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + })); + } } } } From 2e348edd8dadbc6f88e45390bae49a0ffdcae66c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Nov 2018 05:29:06 +0900 Subject: [PATCH 0210/2854] Fix filename --- .../Screens/TeamIntro/{TeamIntro.cs => TeamIntroScreen.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename osu.Game.Tournament/Screens/TeamIntro/{TeamIntro.cs => TeamIntroScreen.cs} (100%) diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntro.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs similarity index 100% rename from osu.Game.Tournament/Screens/TeamIntro/TeamIntro.cs rename to osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs From 54a02ee2d7f7bd50455955d6d85d524f53742e02 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Nov 2018 07:12:07 +0900 Subject: [PATCH 0211/2854] Add showcase screen --- .../TestCaseSceneManager.cs | 4 + osu.Game.Tournament.Tests/TestCaseShowcase.cs | 18 +++ .../Components/TournamentBeatmapPanel.cs | 4 +- .../Screens/Showcase/ShowcaseScreen.cs | 143 ++++++++++++++++++ 4 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tournament.Tests/TestCaseShowcase.cs create mode 100644 osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs diff --git a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs index 9454a5674c..aec9038356 100644 --- a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Video; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Screens.Drawings; using osu.Game.Tournament.Screens.Ladder; +using osu.Game.Tournament.Screens.Showcase; using osu.Game.Tournament.Screens.TeamIntro; using OpenTK; using OpenTK.Graphics; @@ -23,6 +24,7 @@ namespace osu.Game.Tournament.Tests private TeamIntroScreen teamIntro; private DrawingsScreen drawings; private Container screens; + private ShowcaseScreen showcase; [BackgroundDependencyLoader] private void load() @@ -47,6 +49,7 @@ namespace osu.Game.Tournament.Tests Children = new Drawable[] { new OsuButton { RelativeSizeAxes = Axes.X, Text = "Drawings", Action = () => setScreen(drawings) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Showcase", Action = () => setScreen(showcase) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "TeamIntro", Action = () => setScreen(teamIntro) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "MapPool", Action = () => setScreen(mapPool) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket", Action = () => setScreen(bracket) }, @@ -79,6 +82,7 @@ namespace osu.Game.Tournament.Tests Children = new Drawable[] { bracket = new LadderManager(Ladder), + showcase = new ShowcaseScreen(), mapPool = new MapPoolScreen(Ladder.Groupings.First(g => g.Name == "Finals")), teamIntro = new TeamIntroScreen(Ladder.Teams.First(t => t.Acronym == "USA"), Ladder.Teams.First(t => t.Acronym == "JPN"), Ladder.Groupings.First(g => g.Name == "Finals")), diff --git a/osu.Game.Tournament.Tests/TestCaseShowcase.cs b/osu.Game.Tournament.Tests/TestCaseShowcase.cs new file mode 100644 index 0000000000..66183683d1 --- /dev/null +++ b/osu.Game.Tournament.Tests/TestCaseShowcase.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Tests.Visual; +using osu.Game.Tournament.Screens.Showcase; + +namespace osu.Game.Tournament.Tests +{ + public class TestCaseShowcase : OsuTestCase + { + [BackgroundDependencyLoader] + private void load() + { + Add(new ShowcaseScreen()); + } + } +} diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 5eb597a3c6..9542c26faf 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -20,11 +20,13 @@ namespace osu.Game.Tournament.Components private const float horizontal_padding = 10; private const float vertical_padding = 5; + public const float HEIGHT = 50; + public TournamentBeatmapPanel(BeatmapInfo beatmap) { this.beatmap = beatmap; Width = 400; - Height = 50; + Height = HEIGHT; } [BackgroundDependencyLoader] diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs new file mode 100644 index 0000000000..7585ad4078 --- /dev/null +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -0,0 +1,143 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets; +using osu.Game.Screens; +using osu.Game.Screens.Menu; +using osu.Game.Tournament.Components; +using OpenTK; + +namespace osu.Game.Tournament.Screens.Showcase +{ + public class ShowcaseScreen : OsuScreen + { + private readonly Container panelContainer; + + [Resolved] + private APIAccess api { get; set; } = null; + + [Resolved] + private RulesetStore rulesets { get; set; } = null; + + [BackgroundDependencyLoader] + private void load() + { + var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = 1091460 }); + req.Success += success; + api.Queue(req); + } + + private void success(APIBeatmap apiBeatmap) + { + var beatmap = apiBeatmap.ToBeatmap(rulesets); + panelContainer.Children = new Drawable[] + { + new OsuSpriteText + { + Text = $"Length {beatmap.OnlineInfo.Length}s", + Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, + Colour = OsuColour.Gray(0.33f), + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + }, + new OsuSpriteText + { + Text = $"BPM {beatmap.BeatmapSet.OnlineInfo.BPM:0.#}", + Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, + Colour = OsuColour.Gray(0.33f), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft + }, + new OsuSpriteText + { + Text = $"AR {beatmap.BaseDifficulty.ApproachRate:0.#}", + Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, + Colour = OsuColour.Gray(0.33f), + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight + }, + new OsuSpriteText + { + Text = $"Star Rating {beatmap.StarDifficulty:0.#}", + Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, + Colour = OsuColour.Gray(0.33f), + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight + }, + new TournamentBeatmapPanel(beatmap) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + } + }; + } + + public ShowcaseScreen() + { + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new Container + { + Masking = true, + RelativeSizeAxes = Axes.X, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = -10, + Width = 0.9f, + Height = TournamentBeatmapPanel.HEIGHT, + CornerRadius = TournamentBeatmapPanel.HEIGHT / 2, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.93f), + }, + new Container + { + Masking = true, + CornerRadius = TournamentBeatmapPanel.HEIGHT / 2, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Width = 0.7f, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.86f), + }, + panelContainer = new Container + { + RelativeSizeAxes = Axes.Both, + } + } + }, + new OsuLogo() + { + Triangles = false, + Colour = OsuColour.Gray(0.33f), + Scale = new Vector2(0.08f), + Margin = new MarginPadding(50), + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + } + } + } + }; + } + } +} From 89a1414c635d39261a6c65090086b63bb65bdbe3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Nov 2018 21:14:16 +0900 Subject: [PATCH 0212/2854] Read flag name from file --- .../Screens/Drawings/Components/StorageBackedTeamList.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs b/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs index 625f05edac..fa7ce5edfa 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs @@ -51,7 +51,8 @@ namespace osu.Game.Tournament.Screens.Drawings.Components teams.Add(new TournamentTeam { FullName = split[1].Trim(), - Acronym = split.Length >= 3 ? split[2].Trim() : null + Acronym = split.Length >= 3 ? split[2].Trim() : null, + FlagName = split[0].Trim() }); } } From 7e092c608400fab60c139b1dab4d24c01ab07bf5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Nov 2018 21:15:02 +0900 Subject: [PATCH 0213/2854] Read videos from storage --- .../TestCaseSceneManager.cs | 5 +++-- .../Screens/Drawings/DrawingsScreen.cs | 2 +- .../Screens/Showcase/ShowcaseScreen.cs | 2 +- .../Screens/TeamIntro/TeamIntroScreen.cs | 16 +++++++++++++++- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs index aec9038356..32a47e2c19 100644 --- a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Video; +using osu.Framework.Platform; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Screens.Drawings; using osu.Game.Tournament.Screens.Ladder; @@ -27,7 +28,7 @@ namespace osu.Game.Tournament.Tests private ShowcaseScreen showcase; [BackgroundDependencyLoader] - private void load() + private void load(Storage storage) { Children = new Drawable[] { @@ -70,7 +71,7 @@ namespace osu.Game.Tournament.Tests //Masking = true, Children = new Drawable[] { - new VideoSprite(@"C:\Users\Dean\BG Side Logo - OWC.m4v") + new VideoSprite(storage.GetStream(@"BG Side Logo - OWC.m4v")) { Loop = true, RelativeSizeAxes = Axes.Both, diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index 38f0be0d5a..625d881bdb 100644 --- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -88,7 +88,7 @@ namespace osu.Game.Tournament.Screens.Drawings RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new VideoSprite(@"C:\Users\Dean\BG Logoless - OWC.m4v") + new VideoSprite(storage.GetStream("BG Logoless - OWC.m4v")) { RelativeSizeAxes = Axes.Both, Loop = true, diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index 7585ad4078..81ecd45746 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -126,7 +126,7 @@ namespace osu.Game.Tournament.Screens.Showcase } } }, - new OsuLogo() + new OsuLogo { Triangles = false, Colour = OsuColour.Gray(0.33f), diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index 766feaa806..2c50970624 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -2,9 +2,11 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Video; +using osu.Framework.Platform; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Screens; @@ -17,13 +19,25 @@ namespace osu.Game.Tournament.Screens.TeamIntro { public class TeamIntroScreen : OsuScreen { + private readonly TournamentTeam team1; + private readonly TournamentTeam team2; + private readonly TournamentGrouping round; + public TeamIntroScreen(TournamentTeam team1, TournamentTeam team2, TournamentGrouping round) + { + this.team1 = team1; + this.team2 = team2; + this.round = round; + } + + [BackgroundDependencyLoader] + private void load(Storage storage) { RelativeSizeAxes = Axes.Both; InternalChildren = new Drawable[] { - new VideoSprite(@"C:\Users\Dean\BG Team - Both OWC.m4v") + new VideoSprite(storage.GetStream(@"BG Team - Both OWC.m4v")) { RelativeSizeAxes = Axes.Both, Loop = true, From c7e5ae0573820149efc327a94f63d79e7fcc03e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Nov 2018 21:25:45 +0900 Subject: [PATCH 0214/2854] Read from stable --- .../Screens/Showcase/ShowcaseScreen.cs | 124 ++++++++++++++++-- 1 file changed, 112 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index 81ecd45746..95d11e2143 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -1,11 +1,15 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.IO; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Platform.Windows; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Legacy; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.API; @@ -21,7 +25,8 @@ namespace osu.Game.Tournament.Screens.Showcase { public class ShowcaseScreen : OsuScreen { - private readonly Container panelContainer; + private Container panel; + private readonly Container panelContents; [Resolved] private APIAccess api { get; set; } = null; @@ -29,22 +34,78 @@ namespace osu.Game.Tournament.Screens.Showcase [Resolved] private RulesetStore rulesets { get; set; } = null; + private int lastBeatmapId; + private int lastMods; + [BackgroundDependencyLoader] private void load() { - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = 1091460 }); - req.Success += success; - api.Queue(req); + var stable = new StableStorage(); + + const string file_ipc_filename = "ipc.txt"; + + if (stable.Exists(file_ipc_filename)) + { + Scheduler.AddDelayed(delegate + { + try + { + using (var stream = stable.GetStream(file_ipc_filename)) + using (var sr = new StreamReader(stream)) + { + var beatmapId = int.Parse(sr.ReadLine()); + var mods = int.Parse(sr.ReadLine()); + + if (lastBeatmapId == beatmapId) + return; + + lastMods = mods; + lastBeatmapId = beatmapId; + + var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); + req.Success += success; + api.Queue(req); + } + } + catch + { + // file might be in use. + } + }, 250, true); + } } private void success(APIBeatmap apiBeatmap) { + panel.FadeInFromZero(300, Easing.OutQuint); + var beatmap = apiBeatmap.ToBeatmap(rulesets); - panelContainer.Children = new Drawable[] + + var legacyMods = (LegacyMods)lastMods; + var bpm = beatmap.BeatmapSet.OnlineInfo.BPM; + var length = beatmap.OnlineInfo.Length; + string extra = ""; + + var ar = beatmap.BaseDifficulty.ApproachRate; + if ((legacyMods & LegacyMods.HardRock) > 0) + { + //ar *= 1.4f; + extra = "*"; + } + + if ((legacyMods & LegacyMods.DoubleTime) > 0) + { + //ar *= 1.5f; + bpm *= 1.5f; + length /= 1.5f; + extra = "*"; + } + + panelContents.Children = new Drawable[] { new OsuSpriteText { - Text = $"Length {beatmap.OnlineInfo.Length}s", + Text = $"Length {length}s", Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, Colour = OsuColour.Gray(0.33f), Anchor = Anchor.TopLeft, @@ -52,7 +113,7 @@ namespace osu.Game.Tournament.Screens.Showcase }, new OsuSpriteText { - Text = $"BPM {beatmap.BeatmapSet.OnlineInfo.BPM:0.#}", + Text = $"BPM {bpm:0.#}", Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, Colour = OsuColour.Gray(0.33f), Anchor = Anchor.BottomLeft, @@ -60,7 +121,7 @@ namespace osu.Game.Tournament.Screens.Showcase }, new OsuSpriteText { - Text = $"AR {beatmap.BaseDifficulty.ApproachRate:0.#}", + Text = $"AR {ar:0.#}{extra}", Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, Colour = OsuColour.Gray(0.33f), Anchor = Anchor.TopRight, @@ -68,7 +129,7 @@ namespace osu.Game.Tournament.Screens.Showcase }, new OsuSpriteText { - Text = $"Star Rating {beatmap.StarDifficulty:0.#}", + Text = $"Star Rating {beatmap.StarDifficulty:0.#}{extra}", Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, Colour = OsuColour.Gray(0.33f), Anchor = Anchor.BottomRight, @@ -95,7 +156,7 @@ namespace osu.Game.Tournament.Screens.Showcase Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Y = -10, - Width = 0.9f, + Width = 0.95f, Height = TournamentBeatmapPanel.HEIGHT, CornerRadius = TournamentBeatmapPanel.HEIGHT / 2, Children = new Drawable[] @@ -105,7 +166,7 @@ namespace osu.Game.Tournament.Screens.Showcase RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.93f), }, - new Container + panel = new Container { Masking = true, CornerRadius = TournamentBeatmapPanel.HEIGHT / 2, @@ -120,7 +181,7 @@ namespace osu.Game.Tournament.Screens.Showcase RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.86f), }, - panelContainer = new Container + panelContents = new Container { RelativeSizeAxes = Axes.Both, } @@ -139,5 +200,44 @@ namespace osu.Game.Tournament.Screens.Showcase } }; } + + /// + /// A method of accessing an osu-stable install in a controlled fashion. + /// + private class StableStorage : WindowsStorage + { + protected override string LocateBasePath() + { + bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); + + string stableInstallPath; + + try + { + stableInstallPath = "E:\\osu!mappool"; + + if (checkExists(stableInstallPath)) + return stableInstallPath; + } + catch + { + } + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + return null; + } + + public StableStorage() + : base(string.Empty, null) + { + } + } } } From fb93aea909383075518ddf2393dc17454443356a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Nov 2018 23:15:30 +0900 Subject: [PATCH 0215/2854] Use logo-less video by default --- osu.Game.Tournament.Tests/TestCaseSceneManager.cs | 3 ++- osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs | 6 ------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs index 32a47e2c19..13c00f081a 100644 --- a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs @@ -71,9 +71,10 @@ namespace osu.Game.Tournament.Tests //Masking = true, Children = new Drawable[] { - new VideoSprite(storage.GetStream(@"BG Side Logo - OWC.m4v")) + new VideoSprite(storage.GetStream("BG Logoless - OWC.m4v")) { Loop = true, + ShowLastFrameDuringHideCutoff = true, RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, }, diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index 625d881bdb..9e8074d7dc 100644 --- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -12,7 +12,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.Video; using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Framework.Platform; @@ -88,11 +87,6 @@ namespace osu.Game.Tournament.Screens.Drawings RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new VideoSprite(storage.GetStream("BG Logoless - OWC.m4v")) - { - RelativeSizeAxes = Axes.Both, - Loop = true, - }, new Sprite { RelativeSizeAxes = Axes.Both, From 9c5eddea549cfcac278714a1b24296bcfae46646 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Nov 2018 14:49:09 +0900 Subject: [PATCH 0216/2854] Allow running tournament tools from main executable --- osu.Desktop/Program.cs | 4 + osu.Desktop/osu.Desktop.csproj | 1 + osu.Game.Tournament.Tests/TestCaseMapPool.cs | 33 +---- .../TestCaseSceneManager.cs | 97 +-------------- .../Screens/MapPool/MapPoolScreen.cs | 35 ++++++ .../Screens/TournamentSceneManager.cs | 113 ++++++++++++++++++ osu.Game.Tournament/TournamentGame.cs | 99 +++++++++++++++ osu.Game/OsuGameBase.cs | 12 +- 8 files changed, 262 insertions(+), 132 deletions(-) create mode 100644 osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs create mode 100644 osu.Game.Tournament/Screens/TournamentSceneManager.cs create mode 100644 osu.Game.Tournament/TournamentGame.cs diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 257155478f..b864cf3c64 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -11,6 +11,7 @@ using osu.Framework.Development; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.IPC; +using osu.Game.Tournament; namespace osu.Desktop { @@ -45,6 +46,9 @@ namespace osu.Desktop default: host.Run(new OsuGameDesktop(args)); break; + case "--tournament": + host.Run(new TournamentGame()); + break; } } diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index e1e59804e5..a3e7625c44 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -17,6 +17,7 @@ osu.Desktop.Program + diff --git a/osu.Game.Tournament.Tests/TestCaseMapPool.cs b/osu.Game.Tournament.Tests/TestCaseMapPool.cs index e4637b8fc3..1101d2828a 100644 --- a/osu.Game.Tournament.Tests/TestCaseMapPool.cs +++ b/osu.Game.Tournament.Tests/TestCaseMapPool.cs @@ -3,12 +3,7 @@ using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Screens; -using osu.Game.Tournament.Components; -using osu.Game.Tournament.Screens.Ladder.Components; -using OpenTK; +using osu.Game.Tournament.Screens.MapPool; namespace osu.Game.Tournament.Tests { @@ -23,30 +18,4 @@ namespace osu.Game.Tournament.Tests Add(new MapPoolScreen(round)); } } - - public class MapPoolScreen : OsuScreen - { - public MapPoolScreen(TournamentGrouping round) - { - FillFlowContainer maps; - - InternalChildren = new Drawable[] - { - maps = new FillFlowContainer - { - Spacing = new Vector2(20), - Padding = new MarginPadding(50), - Direction = FillDirection.Full, - RelativeSizeAxes = Axes.Both, - } - }; - - foreach (var b in round.Beatmaps) - maps.Add(new TournamentBeatmapPanel(b.BeatmapInfo) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }); - } - } } diff --git a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs index 62e5f77802..9853e31ee3 100644 --- a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs @@ -1,111 +1,18 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Video; using osu.Framework.Platform; -using osu.Game.Graphics.UserInterface; -using osu.Game.Tournament.Screens.Drawings; -using osu.Game.Tournament.Screens.Ladder; -using osu.Game.Tournament.Screens.Showcase; -using osu.Game.Tournament.Screens.TeamIntro; -using OpenTK; -using OpenTK.Graphics; +using osu.Game.Tournament.Screens; namespace osu.Game.Tournament.Tests { public class TestCaseSceneManager : LadderTestCase { - private LadderManager bracket; - private MapPoolScreen mapPool; - private TeamIntroScreen teamIntro; - private DrawingsScreen drawings; - private Container screens; - private ShowcaseScreen showcase; - [BackgroundDependencyLoader] private void load(Storage storage) { - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Width = 0.2f, - Children = new Drawable[] - { - new Box - { - Colour = Color4.Black, - RelativeSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Drawings", Action = () => setScreen(drawings) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Showcase", Action = () => setScreen(showcase) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "TeamIntro", Action = () => setScreen(teamIntro) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "MapPool", Action = () => setScreen(mapPool) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket", Action = () => setScreen(bracket) }, - } - }, - }, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.Both, - X = 0.2f, - FillMode = FillMode.Fit, - FillAspectRatio = 16/9f, - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, - Size = new Vector2(0.8f, 1), - //Masking = true, - Children = new Drawable[] - { - new VideoSprite(storage.GetStream("BG Logoless - OWC.m4v")) - { - Loop = true, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - }, - screens = new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - bracket = new LadderManager(Ladder), - showcase = new ShowcaseScreen(), - mapPool = new MapPoolScreen(Ladder.Groupings.First(g => g.Name == "Finals")), - teamIntro = new TeamIntroScreen(Ladder.Teams.First(t => t.Acronym == "USA"), Ladder.Teams.First(t => t.Acronym == "JPN"), - Ladder.Groupings.First(g => g.Name == "Finals")), - drawings = new DrawingsScreen() - } - }, - } - }, - }; - - setScreen(teamIntro); - } - - private void setScreen(Drawable screen) - { - foreach (var s in screens.Children) - { - if (s == screen) - s.FadeIn(100); - else - s.FadeOut(100); - } + Add(new TournamentSceneManager()); } } } diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs new file mode 100644 index 0000000000..be7b3a0b3b --- /dev/null +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -0,0 +1,35 @@ +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Screens; +using osu.Game.Tournament.Components; +using osu.Game.Tournament.Screens.Ladder.Components; +using OpenTK; + +namespace osu.Game.Tournament.Screens.MapPool +{ + public class MapPoolScreen : OsuScreen + { + public MapPoolScreen(TournamentGrouping round) + { + FillFlowContainer maps; + + InternalChildren = new Drawable[] + { + maps = new FillFlowContainer + { + Spacing = new Vector2(20), + Padding = new MarginPadding(50), + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.Both, + } + }; + + foreach (var b in round.Beatmaps) + maps.Add(new TournamentBeatmapPanel(b.BeatmapInfo) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }); + } + } +} diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs new file mode 100644 index 0000000000..82f17a85c4 --- /dev/null +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -0,0 +1,113 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Video; +using osu.Framework.Platform; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens; +using osu.Game.Tournament.Screens.Drawings; +using osu.Game.Tournament.Screens.Ladder; +using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Tournament.Screens.MapPool; +using osu.Game.Tournament.Screens.Showcase; +using osu.Game.Tournament.Screens.TeamIntro; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Tournament.Screens +{ + public class TournamentSceneManager : OsuScreen + { + private LadderManager bracket; + private MapPoolScreen mapPool; + private TeamIntroScreen teamIntro; + private DrawingsScreen drawings; + private Container screens; + private ShowcaseScreen showcase; + + [BackgroundDependencyLoader] + private void load(LadderInfo ladder, Storage storage) + { + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Y, + Width = 200, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Drawings", Action = () => setScreen(drawings) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Showcase", Action = () => setScreen(showcase) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "TeamIntro", Action = () => setScreen(teamIntro) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "MapPool", Action = () => setScreen(mapPool) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket", Action = () => setScreen(bracket) }, + } + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + X = 200, + FillMode = FillMode.Fit, + FillAspectRatio = 16/9f, + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + Size = new Vector2(0.8f, 1), + //Masking = true, + Children = new Drawable[] + { + new VideoSprite(storage.GetStream("BG Logoless - OWC.m4v")) + { + Loop = true, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + }, + screens = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + bracket = new LadderManager(ladder), + showcase = new ShowcaseScreen(), + mapPool = new MapPoolScreen(ladder.Groupings.First(g => g.Name == "Finals")), + teamIntro = new TeamIntroScreen(ladder.Teams.First(t => t.Acronym == "USA"), ladder.Teams.First(t => t.Acronym == "JPN"), + ladder.Groupings.First(g => g.Name == "Finals")), + drawings = new DrawingsScreen() + } + }, + } + }, + }; + + setScreen(teamIntro); + } + + private void setScreen(Drawable screen) + { + foreach (var s in screens.Children) + { + if (s == screen) + s.FadeIn(100); + else + s.FadeOut(100); + } + } + } +} diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs new file mode 100644 index 0000000000..1bbbfdc37a --- /dev/null +++ b/osu.Game.Tournament/TournamentGame.cs @@ -0,0 +1,99 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using Newtonsoft.Json; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Platform; +using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests; +using osu.Game.Rulesets; +using osu.Game.Tournament.Screens; +using osu.Game.Tournament.Screens.Ladder.Components; + +namespace osu.Game.Tournament +{ + public class TournamentGame : OsuGameBase + { + private const string bracket_filename = "bracket.json"; + + protected LadderInfo Ladder; + private Storage storage; + + private DependencyContainer dependencies; + + [Cached] + private readonly Bindable ruleset = new Bindable(); + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + + [BackgroundDependencyLoader] + private void load(Storage storage) + { + this.storage = storage; + + string content = null; + if (storage.Exists(bracket_filename)) + { + using (Stream stream = storage.GetStream(bracket_filename, FileAccess.Read, FileMode.Open)) + using (var sr = new StreamReader(stream)) + content = sr.ReadToEnd(); + } + + Ladder = content != null ? JsonConvert.DeserializeObject(content) : new LadderInfo(); + dependencies.Cache(Ladder); + + bool addedInfo = false; + + foreach (var g in Ladder.Groupings) + foreach (var b in g.Beatmaps) + { + if (b.BeatmapInfo == null) + { + var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); + req.Success += i => b.BeatmapInfo = i.ToBeatmap(RulesetStore); + req.Perform(API); + + addedInfo = true; + } + } + + if (addedInfo) + SaveChanges(); + + Add(new OsuButton + { + Text = "Save Changes", + Width = 140, + Height = 50, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Padding = new MarginPadding(10), + Action = SaveChanges, + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Add(new TournamentSceneManager()); + } + + protected virtual void SaveChanges() + { + using (var stream = storage.GetStream(bracket_filename, FileAccess.Write, FileMode.Create)) + using (var sw = new StreamWriter(stream)) + { + sw.Write(JsonConvert.SerializeObject(Ladder, + new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + })); + } + } + } +} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 73c970ce5d..ea1dbfa369 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -62,6 +62,8 @@ namespace osu.Game protected MenuCursorContainer MenuCursorContainer; + protected APIAccess API; + private Container content; protected override Container Content => content; @@ -146,14 +148,14 @@ namespace osu.Game dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio)); dependencies.CacheAs(SkinManager); - var api = new APIAccess(LocalConfig); + API = new APIAccess(LocalConfig); - dependencies.Cache(api); - dependencies.CacheAs(api); + dependencies.Cache(API); + dependencies.CacheAs(API); dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); - dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host)); + dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, API, Audio, Host)); dependencies.Cache(ScoreStore = new ScoreStore(contextFactory, Host, BeatmapManager, RulesetStore)); dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); @@ -177,7 +179,7 @@ namespace osu.Game FileStore.Cleanup(); - AddInternal(api); + AddInternal(API); GlobalActionContainer globalBinding; From 6f2554873714d10c8c09057cdccff0a7690f0e87 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Nov 2018 14:49:20 +0900 Subject: [PATCH 0217/2854] Remove now unnecessary null setters --- osu.Game.Tournament.Tests/LadderTestCase.cs | 4 ++-- osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs | 4 ++-- .../Screens/Ladder/Components/DrawableMatchPairing.cs | 2 +- .../Screens/Ladder/Components/DrawableMatchTeam.cs | 2 +- .../Screens/Ladder/Components/LadderEditorSettings.cs | 2 +- osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tournament.Tests/LadderTestCase.cs b/osu.Game.Tournament.Tests/LadderTestCase.cs index ebc78e14be..7c8d8856e6 100644 --- a/osu.Game.Tournament.Tests/LadderTestCase.cs +++ b/osu.Game.Tournament.Tests/LadderTestCase.cs @@ -24,10 +24,10 @@ namespace osu.Game.Tournament.Tests private Storage storage; [Resolved] - private APIAccess api { get; set; } = null; + private APIAccess api { get; set; } [Resolved] - private RulesetStore rulesets { get; set; } = null; + private RulesetStore rulesets { get; set; } [BackgroundDependencyLoader] private void load(Storage storage) diff --git a/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs b/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs index 93068f6224..de80d36067 100644 --- a/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs +++ b/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs @@ -18,10 +18,10 @@ namespace osu.Game.Tournament.Tests public class TestCaseBeatmapPanel : OsuTestCase { [Resolved] - private APIAccess api { get; set; } = null; + private APIAccess api { get; set; } [Resolved] - private RulesetStore rulesets { get; set; } = null; + private RulesetStore rulesets { get; set; } public override IReadOnlyList RequiredTypes => new[] { diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 1f58bc86ea..81b3223ea7 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private Bindable globalSelection; [Resolved(CanBeNull = true)] - private LadderEditorInfo editorInfo { get; set; } = null; + private LadderEditorInfo editorInfo { get; set; } public DrawableMatchPairing(MatchPairing pairing) { diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index dca8d0f0a8..70ddb6b664 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private LadderManager manager; [Resolved(CanBeNull = true)] - private LadderEditorInfo editorInfo { get; set; } = null; + private LadderEditorInfo editorInfo { get; set; } public DrawableMatchTeam(Bindable team, MatchPairing pairing, bool losers) : base(team) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index 95067e8803..d7b827237a 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -26,7 +26,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private PlayerCheckbox losersCheckbox; [Resolved] - private LadderEditorInfo editorInfo { get; set; } = null; + private LadderEditorInfo editorInfo { get; set; } [BackgroundDependencyLoader] private void load() diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index 95d11e2143..dcbaad8ab5 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -25,14 +25,14 @@ namespace osu.Game.Tournament.Screens.Showcase { public class ShowcaseScreen : OsuScreen { - private Container panel; + private readonly Container panel; private readonly Container panelContents; [Resolved] - private APIAccess api { get; set; } = null; + private APIAccess api { get; set; } [Resolved] - private RulesetStore rulesets { get; set; } = null; + private RulesetStore rulesets { get; set; } private int lastBeatmapId; private int lastMods; From 85fe4db2ec86674fa60afe936fd2fe21daa551f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Nov 2018 15:08:14 +0900 Subject: [PATCH 0218/2854] Hide game cursor so it is not included in stream --- osu.Game.Tournament/TournamentGame.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 1bbbfdc37a..d4e6ee2a44 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -80,6 +80,8 @@ namespace osu.Game.Tournament { base.LoadComplete(); Add(new TournamentSceneManager()); + + MenuCursorContainer.Cursor.Alpha = 0; } protected virtual void SaveChanges() From a2b28e0bf4326b94b46e77945626940268526056 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Nov 2018 16:15:03 +0900 Subject: [PATCH 0219/2854] Add missing header --- osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index be7b3a0b3b..6d7dca0aad 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Screens; From 49d3beac1930db116fb5c7a8a45cfb9752ebda70 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Nov 2018 16:31:43 +0900 Subject: [PATCH 0220/2854] Add rider config for tournament run --- .../runConfigurations/osu___Tournament_.xml | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .idea/.idea.osu/.idea/runConfigurations/osu___Tournament_.xml diff --git a/.idea/.idea.osu/.idea/runConfigurations/osu___Tournament_.xml b/.idea/.idea.osu/.idea/runConfigurations/osu___Tournament_.xml new file mode 100644 index 0000000000..a5f93489e8 --- /dev/null +++ b/.idea/.idea.osu/.idea/runConfigurations/osu___Tournament_.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file From 5a4292717fbe707f93e3e1ad3e74c030dcfbd545 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Nov 2018 17:59:01 +0900 Subject: [PATCH 0221/2854] Allow tests to run custom game --- osu.Game.Tournament.Tests/LadderTestCase.cs | 79 +------------------ .../TestCaseGroupingManager.cs | 4 +- .../TestCaseLadderManager.cs | 4 +- .../TestCaseSceneManager.cs | 3 +- .../TournamentTestBrowser.cs | 27 +++++++ .../TournamentTestRunner.cs | 22 ++++++ .../osu.Game.Tournament.Tests.csproj | 3 + 7 files changed, 60 insertions(+), 82 deletions(-) create mode 100644 osu.Game.Tournament.Tests/TournamentTestBrowser.cs create mode 100644 osu.Game.Tournament.Tests/TournamentTestRunner.cs diff --git a/osu.Game.Tournament.Tests/LadderTestCase.cs b/osu.Game.Tournament.Tests/LadderTestCase.cs index 7c8d8856e6..fcc4d7dc1b 100644 --- a/osu.Game.Tournament.Tests/LadderTestCase.cs +++ b/osu.Game.Tournament.Tests/LadderTestCase.cs @@ -1,91 +1,16 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.IO; -using Newtonsoft.Json; using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Platform; -using osu.Game.Beatmaps; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Rulesets; using osu.Game.Tests.Visual; using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament.Tests { - public abstract class LadderTestCase : OsuTestCase + public class LadderTestCase : OsuTestCase { - private const string bracket_filename = "bracket.json"; - - protected LadderInfo Ladder; - private Storage storage; [Resolved] - private APIAccess api { get; set; } - - [Resolved] - private RulesetStore rulesets { get; set; } - - [BackgroundDependencyLoader] - private void load(Storage storage) - { - this.storage = storage; - - string content = null; - if (storage.Exists(bracket_filename)) - { - using (Stream stream = storage.GetStream(bracket_filename, FileAccess.Read, FileMode.Open)) - using (var sr = new StreamReader(stream)) - content = sr.ReadToEnd(); - } - - Ladder = content != null ? JsonConvert.DeserializeObject(content) : new LadderInfo(); - - bool addedInfo = false; - - foreach (var g in Ladder.Groupings) - foreach (var b in g.Beatmaps) - { - if (b.BeatmapInfo == null) - { - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); - req.Success += i => b.BeatmapInfo = i.ToBeatmap(rulesets); - req.Perform(api); - - addedInfo = true; - } - } - - if (addedInfo) - SaveChanges(); - - Add(new OsuButton - { - Text = "Save Changes", - Width = 140, - Height = 50, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Padding = new MarginPadding(10), - Action = SaveChanges, - }); - } - - protected virtual void SaveChanges() - { - using (var stream = storage.GetStream(bracket_filename, FileAccess.Write, FileMode.Create)) - using (var sw = new StreamWriter(stream)) - { - sw.Write(JsonConvert.SerializeObject(Ladder, - new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - DefaultValueHandling = DefaultValueHandling.Ignore - })); - } - } + protected LadderInfo Ladder { get; set; } } } diff --git a/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs b/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs index 2b79ba0225..531019c28c 100644 --- a/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs @@ -47,10 +47,10 @@ namespace osu.Game.Tournament.Tests items.Add(new GroupingRow(g)); } - protected override void SaveChanges() + protected override void Dispose(bool isDisposing) { Ladder.Groupings = items.Children.Select(c => c.Grouping).ToList(); - base.SaveChanges(); + base.Dispose(isDisposing); } private void addNew() => items.Add(new GroupingRow(new TournamentGrouping())); diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index a7af038ca8..7a7f7e0771 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -22,10 +22,10 @@ namespace osu.Game.Tournament.Tests }); } - protected override void SaveChanges() + protected override void Dispose(bool isDisposing) { + base.Dispose(isDisposing); Ladder = manager.CreateInfo(); - base.SaveChanges(); } } } diff --git a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs index 9853e31ee3..a19c933d8b 100644 --- a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs @@ -3,11 +3,12 @@ using osu.Framework.Allocation; using osu.Framework.Platform; +using osu.Game.Tests.Visual; using osu.Game.Tournament.Screens; namespace osu.Game.Tournament.Tests { - public class TestCaseSceneManager : LadderTestCase + public class TestCaseSceneManager : OsuTestCase { [BackgroundDependencyLoader] private void load(Storage storage) diff --git a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs new file mode 100644 index 0000000000..8549be0879 --- /dev/null +++ b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs @@ -0,0 +1,27 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Testing; +using osu.Game.Graphics; +using osu.Game.Screens.Backgrounds; + +namespace osu.Game.Tournament.Tests +{ + public class TournamentTestBrowser : TournamentGame + { + protected override void LoadComplete() + { + base.LoadComplete(); + + LoadComponentAsync(new BackgroundScreenDefault + { + Colour = OsuColour.Gray(0.5f), + Depth = 10 + }, AddInternal); + + // Have to construct this here, rather than in the constructor, because + // we depend on some dependencies to be loaded within OsuGameBase.load(). + Add(new TestBrowser()); + } + } +} diff --git a/osu.Game.Tournament.Tests/TournamentTestRunner.cs b/osu.Game.Tournament.Tests/TournamentTestRunner.cs new file mode 100644 index 0000000000..51c2c65cb4 --- /dev/null +++ b/osu.Game.Tournament.Tests/TournamentTestRunner.cs @@ -0,0 +1,22 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework; +using osu.Framework.Platform; + +namespace osu.Game.Tournament.Tests +{ + public static class TournamentTestRunner + { + [STAThread] + public static int Main(string[] args) + { + using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true)) + { + host.Run(new TournamentTestBrowser()); + return 0; + } + } + } +} diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index b3090d5b78..4a65846d68 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -1,5 +1,8 @@  + + osu.Game.Tournament.Tests.TournamentTestRunner + From afb3b38098d275d40117cc69140fda47e485add0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Nov 2018 18:32:59 +0900 Subject: [PATCH 0222/2854] Fix regression in testing logic --- osu.Game.Tournament.Tests/LadderTestCase.cs | 3 +- .../TestCaseLadderManager.cs | 8 +- .../TournamentTestBrowser.cs | 2 +- .../Screens/Showcase/ShowcaseScreen.cs | 229 +++++++++++------- osu.Game.Tournament/TournamentGame.cs | 85 +------ osu.Game.Tournament/TournamentGameBase.cs | 98 ++++++++ 6 files changed, 247 insertions(+), 178 deletions(-) create mode 100644 osu.Game.Tournament/TournamentGameBase.cs diff --git a/osu.Game.Tournament.Tests/LadderTestCase.cs b/osu.Game.Tournament.Tests/LadderTestCase.cs index fcc4d7dc1b..b296956d42 100644 --- a/osu.Game.Tournament.Tests/LadderTestCase.cs +++ b/osu.Game.Tournament.Tests/LadderTestCase.cs @@ -9,8 +9,7 @@ namespace osu.Game.Tournament.Tests { public class LadderTestCase : OsuTestCase { - [Resolved] - protected LadderInfo Ladder { get; set; } + protected LadderInfo Ladder { get; private set; } } } diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index 7a7f7e0771..5fa378a854 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -25,7 +25,13 @@ namespace osu.Game.Tournament.Tests protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - Ladder = manager.CreateInfo(); + + var newInfo = manager.CreateInfo(); + + Ladder.Teams = newInfo.Teams; + Ladder.Groupings = newInfo.Groupings; + Ladder.Pairings = newInfo.Pairings; + Ladder.Progressions = newInfo.Progressions; } } } diff --git a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs index 8549be0879..429adb2c0d 100644 --- a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs +++ b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs @@ -7,7 +7,7 @@ using osu.Game.Screens.Backgrounds; namespace osu.Game.Tournament.Tests { - public class TournamentTestBrowser : TournamentGame + public class TournamentTestBrowser : TournamentGameBase { protected override void LoadComplete() { diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index dcbaad8ab5..6c0eac1c23 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -23,77 +23,116 @@ using OpenTK; namespace osu.Game.Tournament.Screens.Showcase { - public class ShowcaseScreen : OsuScreen + public class SongBar : CompositeDrawable { - private readonly Container panel; - private readonly Container panelContents; + private BeatmapInfo beatmap; - [Resolved] - private APIAccess api { get; set; } + public BeatmapInfo Beatmap + { + get { return beatmap; } + set + { + if (beatmap == value) + return; - [Resolved] - private RulesetStore rulesets { get; set; } + beatmap = value; + update(); + } + } - private int lastBeatmapId; - private int lastMods; + private LegacyMods mods; + + public LegacyMods Mods + { + get { return mods; } + set + { + mods = value; + update(); + } + } + + private Container panelContents; [BackgroundDependencyLoader] private void load() { - var stable = new StableStorage(); + RelativeSizeAxes = Axes.Both; - const string file_ipc_filename = "ipc.txt"; - - if (stable.Exists(file_ipc_filename)) + InternalChildren = new Drawable[] { - Scheduler.AddDelayed(delegate + new Container { - try + Masking = true, + RelativeSizeAxes = Axes.X, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = -10, + Width = 0.95f, + Height = TournamentBeatmapPanel.HEIGHT, + CornerRadius = TournamentBeatmapPanel.HEIGHT / 2, + Children = new Drawable[] { - using (var stream = stable.GetStream(file_ipc_filename)) - using (var sr = new StreamReader(stream)) + new Box { - var beatmapId = int.Parse(sr.ReadLine()); - var mods = int.Parse(sr.ReadLine()); - - if (lastBeatmapId == beatmapId) - return; - - lastMods = mods; - lastBeatmapId = beatmapId; - - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); - req.Success += success; - api.Queue(req); + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.93f), + }, + new Container + { + Masking = true, + CornerRadius = TournamentBeatmapPanel.HEIGHT / 2, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Width = 0.7f, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.86f), + }, + panelContents = new Container + { + RelativeSizeAxes = Axes.Both, + } + } + }, + new OsuLogo + { + Triangles = false, + Colour = OsuColour.Gray(0.33f), + Scale = new Vector2(0.08f), + Margin = new MarginPadding(50), + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, } } - catch - { - // file might be in use. - } - }, 250, true); - } + } + }; } - private void success(APIBeatmap apiBeatmap) + private void update() { - panel.FadeInFromZero(300, Easing.OutQuint); + if (beatmap == null) + { + panelContents.Clear(); + return; + } - var beatmap = apiBeatmap.ToBeatmap(rulesets); - - var legacyMods = (LegacyMods)lastMods; var bpm = beatmap.BeatmapSet.OnlineInfo.BPM; var length = beatmap.OnlineInfo.Length; string extra = ""; var ar = beatmap.BaseDifficulty.ApproachRate; - if ((legacyMods & LegacyMods.HardRock) > 0) + if ((mods & LegacyMods.HardRock) > 0) { //ar *= 1.4f; extra = "*"; } - if ((legacyMods & LegacyMods.DoubleTime) > 0) + if ((mods & LegacyMods.DoubleTime) > 0) { //ar *= 1.5f; bpm *= 1.5f; @@ -142,63 +181,73 @@ namespace osu.Game.Tournament.Screens.Showcase } }; } + } + + public class ShowcaseScreen : OsuScreen + { + [Resolved] + private APIAccess api { get; set; } + + [Resolved] + private RulesetStore rulesets { get; set; } + + private int lastBeatmapId; + private int lastMods; + + private readonly SongBar songBar; public ShowcaseScreen() { - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] + Add(songBar = new SongBar { - new Container + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre + }); + } + + [BackgroundDependencyLoader] + private void load() + { + var stable = new StableStorage(); + + const string file_ipc_filename = "ipc.txt"; + + if (stable.Exists(file_ipc_filename)) + { + Scheduler.AddDelayed(delegate { - Masking = true, - RelativeSizeAxes = Axes.X, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Y = -10, - Width = 0.95f, - Height = TournamentBeatmapPanel.HEIGHT, - CornerRadius = TournamentBeatmapPanel.HEIGHT / 2, - Children = new Drawable[] + try { - new Box + using (var stream = stable.GetStream(file_ipc_filename)) + using (var sr = new StreamReader(stream)) { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.93f), - }, - panel = new Container - { - Masking = true, - CornerRadius = TournamentBeatmapPanel.HEIGHT / 2, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Width = 0.7f, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.86f), - }, - panelContents = new Container - { - RelativeSizeAxes = Axes.Both, - } - } - }, - new OsuLogo - { - Triangles = false, - Colour = OsuColour.Gray(0.33f), - Scale = new Vector2(0.08f), - Margin = new MarginPadding(50), - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, + var beatmapId = int.Parse(sr.ReadLine()); + var mods = int.Parse(sr.ReadLine()); + + if (lastBeatmapId == beatmapId) + return; + + lastMods = mods; + lastBeatmapId = beatmapId; + + var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); + req.Success += success; + api.Queue(req); } } - } - }; + catch + { + // file might be in use. + } + }, 250, true); + } + } + + private void success(APIBeatmap apiBeatmap) + { + songBar.FadeInFromZero(300, Easing.OutQuint); + songBar.Mods = (LegacyMods)lastMods; + songBar.Beatmap = apiBeatmap.ToBeatmap(rulesets); } /// diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index d4e6ee2a44..b4bdf250b6 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -1,81 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.IO; -using Newtonsoft.Json; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Platform; -using osu.Game.Beatmaps; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API.Requests; -using osu.Game.Rulesets; using osu.Game.Tournament.Screens; -using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament { - public class TournamentGame : OsuGameBase + public class TournamentGame : TournamentGameBase { - private const string bracket_filename = "bracket.json"; - - protected LadderInfo Ladder; - private Storage storage; - - private DependencyContainer dependencies; - - [Cached] - private readonly Bindable ruleset = new Bindable(); - - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - - [BackgroundDependencyLoader] - private void load(Storage storage) - { - this.storage = storage; - - string content = null; - if (storage.Exists(bracket_filename)) - { - using (Stream stream = storage.GetStream(bracket_filename, FileAccess.Read, FileMode.Open)) - using (var sr = new StreamReader(stream)) - content = sr.ReadToEnd(); - } - - Ladder = content != null ? JsonConvert.DeserializeObject(content) : new LadderInfo(); - dependencies.Cache(Ladder); - - bool addedInfo = false; - - foreach (var g in Ladder.Groupings) - foreach (var b in g.Beatmaps) - { - if (b.BeatmapInfo == null) - { - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); - req.Success += i => b.BeatmapInfo = i.ToBeatmap(RulesetStore); - req.Perform(API); - - addedInfo = true; - } - } - - if (addedInfo) - SaveChanges(); - - Add(new OsuButton - { - Text = "Save Changes", - Width = 140, - Height = 50, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Padding = new MarginPadding(10), - Action = SaveChanges, - }); - } - protected override void LoadComplete() { base.LoadComplete(); @@ -83,19 +14,5 @@ namespace osu.Game.Tournament MenuCursorContainer.Cursor.Alpha = 0; } - - protected virtual void SaveChanges() - { - using (var stream = storage.GetStream(bracket_filename, FileAccess.Write, FileMode.Create)) - using (var sw = new StreamWriter(stream)) - { - sw.Write(JsonConvert.SerializeObject(Ladder, - new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - DefaultValueHandling = DefaultValueHandling.Ignore - })); - } - } } } diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs new file mode 100644 index 0000000000..e76038d818 --- /dev/null +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -0,0 +1,98 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using Newtonsoft.Json; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Platform; +using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests; +using osu.Game.Rulesets; +using osu.Game.Tournament.Screens.Ladder.Components; + +namespace osu.Game.Tournament +{ + public abstract class TournamentGameBase : OsuGameBase + { + private const string bracket_filename = "bracket.json"; + + protected LadderInfo Ladder; + private Storage storage; + + private DependencyContainer dependencies; + + [Cached] + private readonly Bindable ruleset = new Bindable(); + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + return dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + } + + [BackgroundDependencyLoader] + private void load(Storage storage) + { + this.storage = storage; + + string content = null; + if (storage.Exists(bracket_filename)) + using (Stream stream = storage.GetStream(bracket_filename, FileAccess.Read, FileMode.Open)) + using (var sr = new StreamReader(stream)) + { + content = sr.ReadToEnd(); + } + + Ladder = content != null ? JsonConvert.DeserializeObject(content) : new LadderInfo(); + dependencies.Cache(Ladder); + + bool addedInfo = false; + + foreach (var g in Ladder.Groupings) + foreach (var b in g.Beatmaps) + if (b.BeatmapInfo == null) + { + var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); + req.Success += i => b.BeatmapInfo = i.ToBeatmap(RulesetStore); + req.Perform(API); + + addedInfo = true; + } + + if (addedInfo) + SaveChanges(); + + Add(new OsuButton + { + Text = "Save Changes", + Width = 140, + Height = 50, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Padding = new MarginPadding(10), + Action = SaveChanges, + }); + } + + protected override void LoadComplete() + { + MenuCursorContainer.Cursor.Alpha = 0; + } + + protected virtual void SaveChanges() + { + using (var stream = storage.GetStream(bracket_filename, FileAccess.Write, FileMode.Create)) + using (var sw = new StreamWriter(stream)) + { + sw.Write(JsonConvert.SerializeObject(Ladder, + new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + })); + } + } + } +} From ca9df94ea282a0c8932dfde261fd6618c003ea69 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Nov 2018 19:23:03 +0900 Subject: [PATCH 0223/2854] Add skeleton for gameplay screen --- osu.Game.Tournament.Tests/TestCaseGameplay.cs | 25 ++ osu.Game.Tournament.Tests/TestCaseShowcase.cs | 7 + osu.Game.Tournament/Components/SongBar.cs | 172 +++++++++++ .../Screens/BeatmapInfoScreen.cs | 127 ++++++++ .../Screens/Gameplay/GameplayScreen.cs | 10 + .../Screens/Showcase/ShowcaseScreen.cs | 285 +----------------- .../Screens/TournamentSceneManager.cs | 10 +- 7 files changed, 350 insertions(+), 286 deletions(-) create mode 100644 osu.Game.Tournament.Tests/TestCaseGameplay.cs create mode 100644 osu.Game.Tournament/Components/SongBar.cs create mode 100644 osu.Game.Tournament/Screens/BeatmapInfoScreen.cs create mode 100644 osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs diff --git a/osu.Game.Tournament.Tests/TestCaseGameplay.cs b/osu.Game.Tournament.Tests/TestCaseGameplay.cs new file mode 100644 index 0000000000..eefcd79661 --- /dev/null +++ b/osu.Game.Tournament.Tests/TestCaseGameplay.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Game.Tests.Visual; +using osu.Game.Tournament.Screens.Gameplay; + +namespace osu.Game.Tournament.Tests +{ + public class TestCaseGameplay : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(GameplayScreen) + }; + + [BackgroundDependencyLoader] + private void load() + { + Add(new GameplayScreen()); + } + } +} diff --git a/osu.Game.Tournament.Tests/TestCaseShowcase.cs b/osu.Game.Tournament.Tests/TestCaseShowcase.cs index 66183683d1..dcd4b6aec7 100644 --- a/osu.Game.Tournament.Tests/TestCaseShowcase.cs +++ b/osu.Game.Tournament.Tests/TestCaseShowcase.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Game.Tests.Visual; using osu.Game.Tournament.Screens.Showcase; @@ -9,6 +11,11 @@ namespace osu.Game.Tournament.Tests { public class TestCaseShowcase : OsuTestCase { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ShowcaseScreen) + }; + [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs new file mode 100644 index 0000000000..880b80edf8 --- /dev/null +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -0,0 +1,172 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Screens.Menu; +using OpenTK; + +namespace osu.Game.Tournament.Components +{ + public class SongBar : CompositeDrawable + { + private BeatmapInfo beatmap; + + public BeatmapInfo Beatmap + { + get => beatmap; + set + { + if (beatmap == value) + return; + + beatmap = value; + update(); + } + } + + private LegacyMods mods; + + public LegacyMods Mods + { + get => mods; + set + { + mods = value; + update(); + } + } + + private Container panelContents; + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Container + { + Masking = true, + RelativeSizeAxes = Axes.X, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = -10, + Width = 0.95f, + Height = TournamentBeatmapPanel.HEIGHT, + CornerRadius = TournamentBeatmapPanel.HEIGHT / 2, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.93f), + }, + new Container + { + Masking = true, + CornerRadius = TournamentBeatmapPanel.HEIGHT / 2, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Width = 0.7f, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.86f), + }, + panelContents = new Container + { + RelativeSizeAxes = Axes.Both, + } + } + }, + new OsuLogo + { + Triangles = false, + Colour = OsuColour.Gray(0.33f), + Scale = new Vector2(0.08f), + Margin = new MarginPadding(50), + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + } + } + } + }; + } + + private void update() + { + if (beatmap == null) + { + panelContents.Clear(); + return; + } + + var bpm = beatmap.BeatmapSet.OnlineInfo.BPM; + var length = beatmap.OnlineInfo.Length; + string extra = ""; + + var ar = beatmap.BaseDifficulty.ApproachRate; + if ((mods & LegacyMods.HardRock) > 0) extra = "*"; + + if ((mods & LegacyMods.DoubleTime) > 0) + { + //ar *= 1.5f; + bpm *= 1.5f; + length /= 1.5f; + extra = "*"; + } + + panelContents.Children = new Drawable[] + { + new OsuSpriteText + { + Text = $"Length {length}s", + Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, + Colour = OsuColour.Gray(0.33f), + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + }, + new OsuSpriteText + { + Text = $"BPM {bpm:0.#}", + Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, + Colour = OsuColour.Gray(0.33f), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft + }, + new OsuSpriteText + { + Text = $"AR {ar:0.#}{extra}", + Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, + Colour = OsuColour.Gray(0.33f), + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight + }, + new OsuSpriteText + { + Text = $"Star Rating {beatmap.StarDifficulty:0.#}{extra}", + Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, + Colour = OsuColour.Gray(0.33f), + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight + }, + new TournamentBeatmapPanel(beatmap) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + } + }; + } + } +} diff --git a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs new file mode 100644 index 0000000000..18ba947582 --- /dev/null +++ b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs @@ -0,0 +1,127 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Platform.Windows; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets; +using osu.Game.Screens; +using osu.Game.Tournament.Components; + +namespace osu.Game.Tournament.Screens +{ + public abstract class BeatmapInfoScreen : OsuScreen + { + [Resolved] + protected APIAccess API { get; private set; } + + [Resolved] + protected RulesetStore Rulesets { get; private set; } + + private int lastBeatmapId; + private int lastMods; + + protected readonly SongBar SongBar; + + protected BeatmapInfoScreen() + { + Add(SongBar = new SongBar + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre + }); + } + + [BackgroundDependencyLoader] + private void load() + { + var stable = new StableStorage(); + + const string file_ipc_filename = "ipc.txt"; + + if (stable.Exists(file_ipc_filename)) + Scheduler.AddDelayed(delegate + { + try + { + using (var stream = stable.GetStream(file_ipc_filename)) + using (var sr = new StreamReader(stream)) + { + var beatmapId = int.Parse(sr.ReadLine()); + var mods = int.Parse(sr.ReadLine()); + + if (lastBeatmapId == beatmapId) + return; + + lastMods = mods; + lastBeatmapId = beatmapId; + + var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); + req.Success += success; + API.Queue(req); + } + } + catch + { + // file might be in use. + } + }, 250, true); + } + + private void success(APIBeatmap apiBeatmap) + { + SongBar.FadeInFromZero(300, Easing.OutQuint); + SongBar.Mods = (LegacyMods)lastMods; + SongBar.Beatmap = apiBeatmap.ToBeatmap(Rulesets); + } + + /// + /// A method of accessing an osu-stable install in a controlled fashion. + /// + private class StableStorage : WindowsStorage + { + protected override string LocateBasePath() + { + bool checkExists(string p) + { + return Directory.Exists(Path.Combine(p, "Songs")); + } + + string stableInstallPath; + + try + { + stableInstallPath = "E:\\osu!mappool"; + + if (checkExists(stableInstallPath)) + return stableInstallPath; + } + catch + { + } + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + return null; + } + + public StableStorage() + : base(string.Empty, null) + { + } + } + } +} diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs new file mode 100644 index 0000000000..2a9754b066 --- /dev/null +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Tournament.Screens.Gameplay +{ + public class GameplayScreen : BeatmapInfoScreen + { + + } +} diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index 6c0eac1c23..ce458413a5 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -1,292 +1,9 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; -using System.IO; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Platform.Windows; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Legacy; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Online.API.Requests.Responses; -using osu.Game.Rulesets; -using osu.Game.Screens; -using osu.Game.Screens.Menu; -using osu.Game.Tournament.Components; -using OpenTK; - namespace osu.Game.Tournament.Screens.Showcase { - public class SongBar : CompositeDrawable + public class ShowcaseScreen : BeatmapInfoScreen { - private BeatmapInfo beatmap; - - public BeatmapInfo Beatmap - { - get { return beatmap; } - set - { - if (beatmap == value) - return; - - beatmap = value; - update(); - } - } - - private LegacyMods mods; - - public LegacyMods Mods - { - get { return mods; } - set - { - mods = value; - update(); - } - } - - private Container panelContents; - - [BackgroundDependencyLoader] - private void load() - { - RelativeSizeAxes = Axes.Both; - - InternalChildren = new Drawable[] - { - new Container - { - Masking = true, - RelativeSizeAxes = Axes.X, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Y = -10, - Width = 0.95f, - Height = TournamentBeatmapPanel.HEIGHT, - CornerRadius = TournamentBeatmapPanel.HEIGHT / 2, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.93f), - }, - new Container - { - Masking = true, - CornerRadius = TournamentBeatmapPanel.HEIGHT / 2, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Width = 0.7f, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.86f), - }, - panelContents = new Container - { - RelativeSizeAxes = Axes.Both, - } - } - }, - new OsuLogo - { - Triangles = false, - Colour = OsuColour.Gray(0.33f), - Scale = new Vector2(0.08f), - Margin = new MarginPadding(50), - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - } - } - } - }; - } - - private void update() - { - if (beatmap == null) - { - panelContents.Clear(); - return; - } - - var bpm = beatmap.BeatmapSet.OnlineInfo.BPM; - var length = beatmap.OnlineInfo.Length; - string extra = ""; - - var ar = beatmap.BaseDifficulty.ApproachRate; - if ((mods & LegacyMods.HardRock) > 0) - { - //ar *= 1.4f; - extra = "*"; - } - - if ((mods & LegacyMods.DoubleTime) > 0) - { - //ar *= 1.5f; - bpm *= 1.5f; - length /= 1.5f; - extra = "*"; - } - - panelContents.Children = new Drawable[] - { - new OsuSpriteText - { - Text = $"Length {length}s", - Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, - Colour = OsuColour.Gray(0.33f), - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, - }, - new OsuSpriteText - { - Text = $"BPM {bpm:0.#}", - Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, - Colour = OsuColour.Gray(0.33f), - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft - }, - new OsuSpriteText - { - Text = $"AR {ar:0.#}{extra}", - Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, - Colour = OsuColour.Gray(0.33f), - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight - }, - new OsuSpriteText - { - Text = $"Star Rating {beatmap.StarDifficulty:0.#}{extra}", - Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, - Colour = OsuColour.Gray(0.33f), - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight - }, - new TournamentBeatmapPanel(beatmap) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - } - }; - } - } - - public class ShowcaseScreen : OsuScreen - { - [Resolved] - private APIAccess api { get; set; } - - [Resolved] - private RulesetStore rulesets { get; set; } - - private int lastBeatmapId; - private int lastMods; - - private readonly SongBar songBar; - - public ShowcaseScreen() - { - Add(songBar = new SongBar - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre - }); - } - - [BackgroundDependencyLoader] - private void load() - { - var stable = new StableStorage(); - - const string file_ipc_filename = "ipc.txt"; - - if (stable.Exists(file_ipc_filename)) - { - Scheduler.AddDelayed(delegate - { - try - { - using (var stream = stable.GetStream(file_ipc_filename)) - using (var sr = new StreamReader(stream)) - { - var beatmapId = int.Parse(sr.ReadLine()); - var mods = int.Parse(sr.ReadLine()); - - if (lastBeatmapId == beatmapId) - return; - - lastMods = mods; - lastBeatmapId = beatmapId; - - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); - req.Success += success; - api.Queue(req); - } - } - catch - { - // file might be in use. - } - }, 250, true); - } - } - - private void success(APIBeatmap apiBeatmap) - { - songBar.FadeInFromZero(300, Easing.OutQuint); - songBar.Mods = (LegacyMods)lastMods; - songBar.Beatmap = apiBeatmap.ToBeatmap(rulesets); - } - - /// - /// A method of accessing an osu-stable install in a controlled fashion. - /// - private class StableStorage : WindowsStorage - { - protected override string LocateBasePath() - { - bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); - - string stableInstallPath; - - try - { - stableInstallPath = "E:\\osu!mappool"; - - if (checkExists(stableInstallPath)) - return stableInstallPath; - } - catch - { - } - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - return null; - } - - public StableStorage() - : base(string.Empty, null) - { - } - } } } diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index 82f17a85c4..87223c04a6 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -11,6 +11,7 @@ using osu.Framework.Platform; using osu.Game.Graphics.UserInterface; using osu.Game.Screens; using osu.Game.Tournament.Screens.Drawings; +using osu.Game.Tournament.Screens.Gameplay; using osu.Game.Tournament.Screens.Ladder; using osu.Game.Tournament.Screens.Ladder.Components; using osu.Game.Tournament.Screens.MapPool; @@ -25,6 +26,7 @@ namespace osu.Game.Tournament.Screens { private LadderManager bracket; private MapPoolScreen mapPool; + private GameplayScreen gameplay; private TeamIntroScreen teamIntro; private DrawingsScreen drawings; private Container screens; @@ -54,9 +56,12 @@ namespace osu.Game.Tournament.Screens { new OsuButton { RelativeSizeAxes = Axes.X, Text = "Drawings", Action = () => setScreen(drawings) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Showcase", Action = () => setScreen(showcase) }, + new Container { RelativeSizeAxes = Axes.X, Height = 50 }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket", Action = () => setScreen(bracket) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "TeamIntro", Action = () => setScreen(teamIntro) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "MapPool", Action = () => setScreen(mapPool) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket", Action = () => setScreen(bracket) }, + new Container { RelativeSizeAxes = Axes.X, Height = 50 }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Gameplay", Action = () => setScreen(gameplay) }, } }, }, @@ -89,7 +94,8 @@ namespace osu.Game.Tournament.Screens mapPool = new MapPoolScreen(ladder.Groupings.First(g => g.Name == "Finals")), teamIntro = new TeamIntroScreen(ladder.Teams.First(t => t.Acronym == "USA"), ladder.Teams.First(t => t.Acronym == "JPN"), ladder.Groupings.First(g => g.Name == "Finals")), - drawings = new DrawingsScreen() + drawings = new DrawingsScreen(), + gameplay = new GameplayScreen() } }, } From 3427127589afb95b16de9ce6963122ceffc06748 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Nov 2018 19:23:16 +0900 Subject: [PATCH 0224/2854] Automatically keep window wide enough to display correctly --- osu.Game.Tournament/TournamentGameBase.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index e76038d818..cbb7a9fd35 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Drawing; using System.IO; using Newtonsoft.Json; using osu.Framework.Allocation; @@ -27,16 +28,20 @@ namespace osu.Game.Tournament [Cached] private readonly Bindable ruleset = new Bindable(); + private Bindable windowSize; + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { return dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); } [BackgroundDependencyLoader] - private void load(Storage storage) + private void load(Storage storage, FrameworkConfigManager frameworkConfig) { this.storage = storage; + windowSize = frameworkConfig.GetBindable(FrameworkSetting.WindowedSize); + string content = null; if (storage.Exists(bracket_filename)) using (Stream stream = storage.GetStream(bracket_filename, FileAccess.Read, FileMode.Open)) @@ -81,6 +86,18 @@ namespace osu.Game.Tournament MenuCursorContainer.Cursor.Alpha = 0; } + protected override void Update() + { + + base.Update(); + var minWidth = (int)(windowSize.Value.Height / 9f * 16 + 400); + if (windowSize.Value.Width < minWidth) + { + // todo: can be removed after ppy/osu-framework#1975 + windowSize.Value = Host.Window.ClientSize = new Size(minWidth, windowSize.Value.Height); + } + } + protected virtual void SaveChanges() { using (var stream = storage.GetStream(bracket_filename, FileAccess.Write, FileMode.Create)) From 4e872880493fc64a051dea9b889ac1be18feb43c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Nov 2018 20:13:04 +0900 Subject: [PATCH 0225/2854] Add the concept of "current match" --- .../TestCaseTeamIntro.cs | 16 +++++--- .../Screens/TeamIntro/TeamIntroScreen.cs | 39 +++++++++++-------- osu.Game.Tournament/TournamentGameBase.cs | 8 ++++ 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs b/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs index 845f5638a0..6fab1fd875 100644 --- a/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs +++ b/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs @@ -3,22 +3,28 @@ using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Game.Tournament.Screens.Ladder.Components; using osu.Game.Tournament.Screens.TeamIntro; namespace osu.Game.Tournament.Tests { public class TestCaseTeamIntro : LadderTestCase { + [Cached] + private readonly Bindable currentMatch = new Bindable(); + [BackgroundDependencyLoader] private void load() { - var team1 = Ladder.Teams.FirstOrDefault(t => t.Acronym == "USA"); - var team2 = Ladder.Teams.FirstOrDefault(t => t.Acronym == "JPN"); + var pairing = new MatchPairing(); + pairing.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == "USA"); + pairing.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == "JPN"); + pairing.Grouping.Value = Ladder.Groupings.FirstOrDefault(g => g.Name == "Finals"); + currentMatch.Value = pairing; - var round = Ladder.Groupings.FirstOrDefault(g => g.Name == "Finals"); - - Add(new TeamIntroScreen(team1, team2, round) + Add(new TeamIntroScreen() { FillMode = FillMode.Fit, FillAspectRatio = 16 / 9f diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index 2c50970624..4b5e9aaf09 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Video; @@ -19,30 +20,34 @@ namespace osu.Game.Tournament.Screens.TeamIntro { public class TeamIntroScreen : OsuScreen { - private readonly TournamentTeam team1; - private readonly TournamentTeam team2; - private readonly TournamentGrouping round; + private VideoSprite background; - public TeamIntroScreen(TournamentTeam team1, TournamentTeam team2, TournamentGrouping round) - { - this.team1 = team1; - this.team2 = team2; - this.round = round; - } + [Resolved] + private Bindable currentMatch { get; set; } [BackgroundDependencyLoader] private void load(Storage storage) { RelativeSizeAxes = Axes.Both; + background = new VideoSprite(storage.GetStream(@"BG Team - Both OWC.m4v")) + { + RelativeSizeAxes = Axes.Both, + Loop = true, + }; + + currentMatch.BindValueChanged(matchChanged, true); + } + + private void matchChanged(MatchPairing pairing) + { + if (pairing == null) + return; + InternalChildren = new Drawable[] { - new VideoSprite(storage.GetStream(@"BG Team - Both OWC.m4v")) - { - RelativeSizeAxes = Axes.Both, - Loop = true, - }, - new TeamWithPlayers(team1, true) + background, + new TeamWithPlayers(pairing.Team1, true) { RelativeSizeAxes = Axes.Both, Width = 0.5f, @@ -50,7 +55,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = Anchor.Centre, Origin = Anchor.CentreRight }, - new TeamWithPlayers(team2) + new TeamWithPlayers(pairing.Team2) { RelativeSizeAxes = Axes.Both, Width = 0.5f, @@ -58,7 +63,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = Anchor.Centre, Origin = Anchor.CentreLeft }, - new RoundDisplay(round) + new RoundDisplay(pairing.Grouping) { RelativeSizeAxes = Axes.Both, Height = 0.25f, diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index cbb7a9fd35..9e9ed58297 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -3,6 +3,7 @@ using System.Drawing; using System.IO; +using System.Linq; using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Configuration; @@ -28,6 +29,9 @@ namespace osu.Game.Tournament [Cached] private readonly Bindable ruleset = new Bindable(); + [Cached] + private readonly Bindable currentMatch = new Bindable(); + private Bindable windowSize; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -51,6 +55,10 @@ namespace osu.Game.Tournament } Ladder = content != null ? JsonConvert.DeserializeObject(content) : new LadderInfo(); + + //todo: temp + currentMatch.Value = Ladder.Pairings.FirstOrDefault(); + dependencies.Cache(Ladder); bool addedInfo = false; From 968d39c0e6858ad0d4ba423931ab43ad37a83fb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Nov 2018 20:13:09 +0900 Subject: [PATCH 0226/2854] Rearrange scenes --- osu.Game.Tournament/Screens/TournamentSceneManager.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index 87223c04a6..303e15aa8d 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -58,9 +58,9 @@ namespace osu.Game.Tournament.Screens new OsuButton { RelativeSizeAxes = Axes.X, Text = "Showcase", Action = () => setScreen(showcase) }, new Container { RelativeSizeAxes = Axes.X, Height = 50 }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket", Action = () => setScreen(bracket) }, + new Container { RelativeSizeAxes = Axes.X, Height = 50 }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "TeamIntro", Action = () => setScreen(teamIntro) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "MapPool", Action = () => setScreen(mapPool) }, - new Container { RelativeSizeAxes = Axes.X, Height = 50 }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Gameplay", Action = () => setScreen(gameplay) }, } }, @@ -92,8 +92,7 @@ namespace osu.Game.Tournament.Screens bracket = new LadderManager(ladder), showcase = new ShowcaseScreen(), mapPool = new MapPoolScreen(ladder.Groupings.First(g => g.Name == "Finals")), - teamIntro = new TeamIntroScreen(ladder.Teams.First(t => t.Acronym == "USA"), ladder.Teams.First(t => t.Acronym == "JPN"), - ladder.Groupings.First(g => g.Name == "Finals")), + teamIntro = new TeamIntroScreen(), drawings = new DrawingsScreen(), gameplay = new GameplayScreen() } From 53ec01d51f972c7117f56d8c9b0577f86ad89beb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Nov 2018 20:18:11 +0900 Subject: [PATCH 0227/2854] Perform mappings earlier in execution --- .../TestCaseTeamIntro.cs | 2 +- .../Screens/Ladder/LadderManager.cs | 29 --------------- osu.Game.Tournament/TournamentGameBase.cs | 35 +++++++++++++++++-- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs b/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs index 6fab1fd875..004009f269 100644 --- a/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs +++ b/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tournament.Tests pairing.Grouping.Value = Ladder.Groupings.FirstOrDefault(g => g.Name == "Finals"); currentMatch.Value = pairing; - Add(new TeamIntroScreen() + Add(new TeamIntroScreen { FillMode = FillMode.Fit, FillAspectRatio = 16 / 9f diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index ebac91c4f7..2ad296dd77 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 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; @@ -67,37 +66,9 @@ namespace osu.Game.Tournament.Screens.Ladder } }; - // assign teams - foreach (var pairing in info.Pairings) - { - pairing.Team1.Value = info.Teams.FirstOrDefault(t => t.Acronym == pairing.Team1Acronym); - pairing.Team2.Value = info.Teams.FirstOrDefault(t => t.Acronym == pairing.Team2Acronym); - } - - // assign progressions - foreach (var pair in info.Progressions) - { - var src = info.Pairings.FirstOrDefault(p => p.ID == pair.Item1); - var dest = info.Pairings.FirstOrDefault(p => p.ID == pair.Item2); - - if (src == null) throw new InvalidOperationException(); - - if (dest != null) - { - if (pair.Losers) - src.LosersProgression.Value = dest; - else - src.Progression.Value = dest; - } - } - foreach (var pairing in info.Pairings) addPairing(pairing); - foreach (var group in info.Groupings) - foreach (var id in group.Pairings) - info.Pairings.Single(p => p.ID == id).Grouping.Value = group; - // todo: fix this Scheduler.AddDelayed(() => layout.Invalidate(), 100, true); } diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 9e9ed58297..f7b829b4c7 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Drawing; using System.IO; using System.Linq; @@ -56,13 +57,38 @@ namespace osu.Game.Tournament Ladder = content != null ? JsonConvert.DeserializeObject(content) : new LadderInfo(); - //todo: temp - currentMatch.Value = Ladder.Pairings.FirstOrDefault(); - dependencies.Cache(Ladder); bool addedInfo = false; + // assign teams + foreach (var pairing in Ladder.Pairings) + { + pairing.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == pairing.Team1Acronym); + pairing.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == pairing.Team2Acronym); + } + + // assign progressions + foreach (var pair in Ladder.Progressions) + { + var src = Ladder.Pairings.FirstOrDefault(p => p.ID == pair.Item1); + var dest = Ladder.Pairings.FirstOrDefault(p => p.ID == pair.Item2); + + if (src == null) throw new InvalidOperationException(); + + if (dest != null) + { + if (pair.Losers) + src.LosersProgression.Value = dest; + else + src.Progression.Value = dest; + } + } + + foreach (var group in Ladder.Groupings) + foreach (var id in group.Pairings) + Ladder.Pairings.Single(p => p.ID == id).Grouping.Value = group; + foreach (var g in Ladder.Groupings) foreach (var b in g.Beatmaps) if (b.BeatmapInfo == null) @@ -74,6 +100,9 @@ namespace osu.Game.Tournament addedInfo = true; } + //todo: temp + currentMatch.Value = Ladder.Pairings.FirstOrDefault(); + if (addedInfo) SaveChanges(); From 555d63165b04f716a42434f0702c766bd1b1a367 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Nov 2018 00:27:12 +0900 Subject: [PATCH 0228/2854] Add custom font --- .../Resources/Fonts/Aquatico-Light.bin | Bin 0 -> 4998 bytes .../Resources/Fonts/Aquatico-Light_0.png | Bin 0 -> 68407 bytes .../Resources/Fonts/Aquatico-Regular.bin | Bin 0 -> 4994 bytes .../Resources/Fonts/Aquatico-Regular_0.png | Bin 0 -> 69000 bytes .../Screens/TeamIntro/TeamIntroScreen.cs | 9 +++++---- osu.Game.Tournament/TournamentGameBase.cs | 6 ++++++ 6 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Tournament/Resources/Fonts/Aquatico-Light.bin create mode 100644 osu.Game.Tournament/Resources/Fonts/Aquatico-Light_0.png create mode 100644 osu.Game.Tournament/Resources/Fonts/Aquatico-Regular.bin create mode 100644 osu.Game.Tournament/Resources/Fonts/Aquatico-Regular_0.png diff --git a/osu.Game.Tournament/Resources/Fonts/Aquatico-Light.bin b/osu.Game.Tournament/Resources/Fonts/Aquatico-Light.bin new file mode 100644 index 0000000000000000000000000000000000000000..42cfdf08de4977e66acd9063ef31741f88715f0f GIT binary patch literal 4998 zcmZ9O$&XZ36vof1YC$6UOoK#>Od^8_O?Lw-Gzv7>Ey|#X4HzR~kimgL8AMGq{sB0| zg_`Kb7$YVoy3mDjr7K z#f*s9A3y8r5Jg8vtUG>k&xynPj?Uh6_~4-vQ7Q9^$8;|`ioB$wI$eMNzivrh*R}WX zk=@;0$BrC~j*Al_W^2WY7>YvwN9>C^5v8uERR>~H^hY(Ogmx}(llEmXznB^~WlcMm zbETb+598j(x_)W5OZ!pR(%7bTH)!uD=jr$2%Es6j>+`c)rOlUipx72GV}rELxKG*w zY3CJVVoa4bHLjPoP};*vZANU#&njklhqUjrwmGjuTO{orJ$p;sEbXS4E$vQebBaYp zPb`r(GwzbMSlSQG7#nkpCDLxJ%qwn<30k)-RBPEKty-C1Op1xpwqy^MO5?wt*ePvt z-mhD~zo|s5j{Bu`#yUOQBki|p#Po(QGo|%PJ0lNzvIm`Ux3p!_K9{z%;R{|Zm-cq$ zP5EN)*GQ8d9~F;i9bQezo~o8Orx?o`aaU@c_KfeU=cBvvEc@LftxxNgx2)6Jc%kjt zRnms!)udRU7TTX{khY_Ef80#oFYTYoh3IWOi}wT4&dRHv7Hv@4CwdpwG-gtJm5Lp&SKk;HccZjp zs^i+mvo~nngVHXjM+@{UIcv_!hoqI2U*(>2mdE8LX}x;3tD&jip7%7g16sFJ z+6Bd3(a`W8fcn7dVbawao1Hst{k!(Wa(&-qd|@aIU3VIz4Zi9 zuW6`BthKhINyM?{taz3fqj?wNn)obS$r>;GmBhr6-&iJcHc^L(+Due#!VMESo2X+f zF}JQ4vImfTf$SB;e)aqW`wsJvWRJZzLtr8)Do+SR?ox=(@>Lm z-kN*P6Ye?Won$lY7ox_nh;Ab;Ai_-EhtYx|E5#Cc8mKM|F+lyLJuDPC|AU zBF;$S_jX1hLlSZlSq@ z5ViU4@g_NY?mByjtVR<5OyO?I#9b4aj%AHZ#}aGEY9td`nd}gE$;z6YM6rpLSZON_R^srgHccA%;GON{0(G2|~X@~J+?1$`T}Co-x1R75t)4a3nVd6ExDLp*L9Q3F2atV%>=pg- zevqR<)LTyw^_r;HM7<_w#X2e)%}-FJ$!8dMeTFduxG|O>GBA;qiA+soZ=xa-Rhp=L zEHO#!SI^{%&|D?t$|1V}v7et{KNI_zsAD+qM;*h7)o3XbT1reNaYr%{*F;e(XUNeYX8<`X$eHrSvTis*tQ*dmItE!eF63Mw=l369{|p2G literal 0 HcmV?d00001 diff --git a/osu.Game.Tournament/Resources/Fonts/Aquatico-Light_0.png b/osu.Game.Tournament/Resources/Fonts/Aquatico-Light_0.png new file mode 100644 index 0000000000000000000000000000000000000000..332d9ca0564224402f3b28d295b9ec05d3736822 GIT binary patch literal 68407 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 Rd(f$N8tR(DYOlLK{x5_sc-8;_ literal 0 HcmV?d00001 diff --git a/osu.Game.Tournament/Resources/Fonts/Aquatico-Regular.bin b/osu.Game.Tournament/Resources/Fonts/Aquatico-Regular.bin new file mode 100644 index 0000000000000000000000000000000000000000..3047c2eb3ef7354d250cf4c88b919269a696a06c GIT binary patch literal 4994 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.WindowedSize); From a0d64c1b13f779488dbfb4a7d952b0e07bbfbfa8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Nov 2018 01:20:32 +0900 Subject: [PATCH 0229/2854] Add ability to select current match --- osu.Game.Tournament.Tests/LadderTestCase.cs | 1 - .../Ladder/Components => }/LadderInfo.cs | 8 ++++- .../Ladder/Components/DrawableMatchPairing.cs | 25 ++++++++++++++- .../Ladder/Components/DrawableMatchTeam.cs | 9 +++++- .../Screens/Ladder/Components/MatchPairing.cs | 5 +++ .../Screens/Ladder/LadderManager.cs | 13 ++++++++ .../Screens/TeamIntro/TeamIntroScreen.cs | 32 ++++++++++++------- .../Screens/TournamentSceneManager.cs | 1 - osu.Game.Tournament/TournamentGame.cs | 9 +++++- osu.Game.Tournament/TournamentGameBase.cs | 9 ++---- 10 files changed, 88 insertions(+), 24 deletions(-) rename osu.Game.Tournament/{Screens/Ladder/Components => }/LadderInfo.cs (70%) diff --git a/osu.Game.Tournament.Tests/LadderTestCase.cs b/osu.Game.Tournament.Tests/LadderTestCase.cs index b296956d42..acc96930ee 100644 --- a/osu.Game.Tournament.Tests/LadderTestCase.cs +++ b/osu.Game.Tournament.Tests/LadderTestCase.cs @@ -3,7 +3,6 @@ using osu.Framework.Allocation; using osu.Game.Tests.Visual; -using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament.Tests { diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs b/osu.Game.Tournament/LadderInfo.cs similarity index 70% rename from osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs rename to osu.Game.Tournament/LadderInfo.cs index 567cdb0daa..c433987491 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs +++ b/osu.Game.Tournament/LadderInfo.cs @@ -2,9 +2,12 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using Newtonsoft.Json; +using osu.Framework.Configuration; using osu.Game.Tournament.Components; +using osu.Game.Tournament.Screens.Ladder.Components; -namespace osu.Game.Tournament.Screens.Ladder.Components +namespace osu.Game.Tournament { public class LadderInfo { @@ -12,5 +15,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public List Progressions = new List(); public List Groupings = new List(); public List Teams = new List(); + + [JsonIgnore] + public Bindable CurrentMatch = new Bindable(); } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 81b3223ea7..5d0837c542 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -20,6 +20,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly MatchPairing Pairing; private readonly FillFlowContainer flow; private readonly Drawable selectionBox; + private readonly Drawable currentMatchSelectionBox; private Bindable globalSelection; [Resolved(CanBeNull = true)] @@ -49,6 +50,18 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Colour = Color4.YellowGreen, Child = new Box { RelativeSizeAxes = Axes.Both } }, + currentMatchSelectionBox = new Container + { + CornerRadius = 5, + Masking = true, + Scale = new Vector2(1.05f), + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0, + Colour = Color4.OrangeRed, + Child = new Box { RelativeSizeAxes = Axes.Both } + }, flow = new FillFlowContainer { AutoSizeAxes = Axes.Both, @@ -65,10 +78,19 @@ namespace osu.Game.Tournament.Screens.Ladder.Components pairing.Progression.BindValueChanged(_ => updateProgression()); pairing.LosersProgression.BindValueChanged(_ => updateProgression()); pairing.Losers.BindValueChanged(_ => updateTeams()); + pairing.Current.BindValueChanged(_ => updateCurrentMatch(), true); updateTeams(); } + private void updateCurrentMatch() + { + if (Pairing.Current.Value) + currentMatchSelectionBox.Show(); + else + currentMatchSelectionBox.Hide(); + } + private bool selected; public bool Selected @@ -144,7 +166,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components var instaWinAmount = Pairing.Grouping.Value.BestOf / 2; - Pairing.Completed.Value = Pairing.Grouping.Value.BestOf > 0 && (Pairing.Team1Score + Pairing.Team2Score >= Pairing.Grouping.Value.BestOf || Pairing.Team1Score > instaWinAmount || Pairing.Team2Score > instaWinAmount); + Pairing.Completed.Value = Pairing.Grouping.Value.BestOf > 0 + && (Pairing.Team1Score + Pairing.Team2Score >= Pairing.Grouping.Value.BestOf || Pairing.Team1Score > instaWinAmount || Pairing.Team2Score > instaWinAmount); } protected override void LoadComplete() diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index 70ddb6b664..804680ba28 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -126,10 +126,16 @@ namespace osu.Game.Tournament.Screens.Ladder.Components } //TODO: use OnClick instead once we have per-button clicks. - protected override bool OnMouseUp(MouseUpEvent e) + protected override bool OnClick(ClickEvent e) { if (Team == null || editorInfo.EditingEnabled) return false; + if (!pairing.Current.Value) + { + manager.SetCurrent(pairing); + return true; + } + if (e.Button == MouseButton.Left) { if (score.Value == null) @@ -176,6 +182,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components return new MenuItem[] { + new OsuMenuItem("Set as current", MenuItemType.Standard, () => manager.SetCurrent(pairing)), new OsuMenuItem("Join with", MenuItemType.Standard, () => manager.RequestJoin(pairing, false)), new OsuMenuItem("Join with (loser)", MenuItemType.Standard, () => manager.RequestJoin(pairing, true)), new OsuMenuItem("Remove", MenuItemType.Destructive, () => manager.Remove(pairing)), diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index 5dc2009c4d..aa0c3229c9 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -43,6 +43,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [JsonIgnore] public readonly Bindable LosersProgression = new Bindable(); + /// + /// Should not be set directly. Use LadderInfo.CurrentMatch.Value = this instead. + /// + public readonly Bindable Current = new Bindable(); + public readonly Bindable Date = new Bindable(); public Point Position; diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 2ad296dd77..f348eff571 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -23,6 +23,7 @@ using SixLabors.Primitives; namespace osu.Game.Tournament.Screens.Ladder { + [Cached] public class LadderManager : CompositeDrawable, IHasContextMenu { public readonly List Teams; @@ -30,6 +31,8 @@ namespace osu.Game.Tournament.Screens.Ladder private readonly Container paths; private readonly Container headings; + private readonly LadderInfo info; + private readonly ScrollableContainer scrollContent; [Cached] @@ -37,6 +40,7 @@ namespace osu.Game.Tournament.Screens.Ladder public LadderManager(LadderInfo info) { + this.info = info; editorInfo.Teams = Teams = info.Teams; editorInfo.Groupings = info.Groupings; @@ -255,5 +259,14 @@ namespace osu.Game.Tournament.Screens.Ladder } public void Remove(MatchPairing pairing) => pairingsContainer.FirstOrDefault(p => p.Pairing == pairing)?.Remove(); + + public void SetCurrent(MatchPairing pairing) + { + if (info.CurrentMatch.Value != null) + info.CurrentMatch.Value.Current.Value = false; + + info.CurrentMatch.Value = pairing; + info.CurrentMatch.Value.Current.Value = true; + } } } diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index 522af5f6c9..d515312dd0 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -3,7 +3,6 @@ using System; using osu.Framework.Allocation; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Video; @@ -20,33 +19,42 @@ namespace osu.Game.Tournament.Screens.TeamIntro { public class TeamIntroScreen : OsuScreen { - private VideoSprite background; + private Container mainContainer; [Resolved] - private Bindable currentMatch { get; set; } + private LadderInfo ladderInfo { get; set; } [BackgroundDependencyLoader] private void load(Storage storage) { RelativeSizeAxes = Axes.Both; - background = new VideoSprite(storage.GetStream(@"BG Team - Both OWC.m4v")) + InternalChildren = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Loop = true, + new VideoSprite(storage.GetStream(@"BG Team - Both OWC.m4v")) + { + RelativeSizeAxes = Axes.Both, + Loop = true, + }, + mainContainer = new Container + { + RelativeSizeAxes = Axes.Both, + } }; - currentMatch.BindValueChanged(matchChanged, true); + ladderInfo.CurrentMatch.BindValueChanged(matchChanged, true); } private void matchChanged(MatchPairing pairing) { if (pairing == null) - return; - - InternalChildren = new Drawable[] { - background, + mainContainer.Clear(); + return; + } + + mainContainer.Children = new Drawable[] + { new TeamWithPlayers(pairing.Team1, true) { RelativeSizeAxes = Axes.Both, @@ -148,6 +156,8 @@ namespace osu.Game.Tournament.Screens.TeamIntro AutoSizeAxes = Axes.Both, Spacing = new Vector2(0, 5), Padding = new MarginPadding(20), + + Anchor = !left ? Anchor.CentreRight : Anchor.CentreLeft, Origin = !left ? Anchor.CentreRight : Anchor.CentreLeft, RelativePositionAxes = Axes.Both, diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index 303e15aa8d..91d389b63d 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -13,7 +13,6 @@ using osu.Game.Screens; using osu.Game.Tournament.Screens.Drawings; using osu.Game.Tournament.Screens.Gameplay; using osu.Game.Tournament.Screens.Ladder; -using osu.Game.Tournament.Screens.Ladder.Components; using osu.Game.Tournament.Screens.MapPool; using osu.Game.Tournament.Screens.Showcase; using osu.Game.Tournament.Screens.TeamIntro; diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index b4bdf250b6..f970700fc5 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Graphics; +using osu.Game.Graphics.Cursor; using osu.Game.Tournament.Screens; namespace osu.Game.Tournament @@ -10,7 +12,12 @@ namespace osu.Game.Tournament protected override void LoadComplete() { base.LoadComplete(); - Add(new TournamentSceneManager()); + + Add(new OsuContextMenuContainer + { + RelativeSizeAxes = Axes.Both, + Child = new TournamentSceneManager() + }); MenuCursorContainer.Cursor.Alpha = 0; } diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 6eb1feaf6d..2ae6c60067 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -15,7 +15,6 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; -using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament { @@ -31,9 +30,6 @@ namespace osu.Game.Tournament [Cached] private readonly Bindable ruleset = new Bindable(); - [Cached] - private readonly Bindable currentMatch = new Bindable(); - private Bindable windowSize; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -95,6 +91,8 @@ namespace osu.Game.Tournament foreach (var id in group.Pairings) Ladder.Pairings.Single(p => p.ID == id).Grouping.Value = group; + Ladder.CurrentMatch.Value = Ladder.Pairings.FirstOrDefault(p => p.Current.Value); + foreach (var g in Ladder.Groupings) foreach (var b in g.Beatmaps) if (b.BeatmapInfo == null) @@ -106,9 +104,6 @@ namespace osu.Game.Tournament addedInfo = true; } - //todo: temp - currentMatch.Value = Ladder.Pairings.FirstOrDefault(); - if (addedInfo) SaveChanges(); From 878b16c596a30e0cd9272cd44a68f75815142750 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Nov 2018 02:50:44 +0900 Subject: [PATCH 0230/2854] Add automatic country/user information lookups --- .../Components/TournamentTeam.cs | 2 +- osu.Game.Tournament/Resources/countries.json | 1252 +++++++++++++++++ osu.Game.Tournament/TournamentGameBase.cs | 35 +- 3 files changed, 1287 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tournament/Resources/countries.json diff --git a/osu.Game.Tournament/Components/TournamentTeam.cs b/osu.Game.Tournament/Components/TournamentTeam.cs index 78e1386706..62dc703fee 100644 --- a/osu.Game.Tournament/Components/TournamentTeam.cs +++ b/osu.Game.Tournament/Components/TournamentTeam.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tournament.Components } [JsonProperty] - public List Players { get; set; } + public List Players { get; set; } = new List(); public override string ToString() => FullName ?? Acronym; } diff --git a/osu.Game.Tournament/Resources/countries.json b/osu.Game.Tournament/Resources/countries.json new file mode 100644 index 0000000000..ec2ca2bf37 --- /dev/null +++ b/osu.Game.Tournament/Resources/countries.json @@ -0,0 +1,1252 @@ +[ + { + "FlagName": "BD", + "FullName": "Bangladesh", + "Acronym": "BGD" + }, + { + "FlagName": "BE", + "FullName": "Belgium", + "Acronym": "BEL" + }, + { + "FlagName": "BF", + "FullName": "Burkina Faso", + "Acronym": "BFA" + }, + { + "FlagName": "BG", + "FullName": "Bulgaria", + "Acronym": "BGR" + }, + { + "FlagName": "BA", + "FullName": "Bosnia and Herzegovina", + "Acronym": "BIH" + }, + { + "FlagName": "BB", + "FullName": "Barbados", + "Acronym": "BRB" + }, + { + "FlagName": "WF", + "FullName": "Wallis and Futuna", + "Acronym": "WLF" + }, + { + "FlagName": "BL", + "FullName": "Saint Barthelemy", + "Acronym": "BLM" + }, + { + "FlagName": "BM", + "FullName": "Bermuda", + "Acronym": "BMU" + }, + { + "FlagName": "BN", + "FullName": "Brunei", + "Acronym": "BRN" + }, + { + "FlagName": "BO", + "FullName": "Bolivia", + "Acronym": "BOL" + }, + { + "FlagName": "BH", + "FullName": "Bahrain", + "Acronym": "BHR" + }, + { + "FlagName": "BI", + "FullName": "Burundi", + "Acronym": "BDI" + }, + { + "FlagName": "BJ", + "FullName": "Benin", + "Acronym": "BEN" + }, + { + "FlagName": "BT", + "FullName": "Bhutan", + "Acronym": "BTN" + }, + { + "FlagName": "JM", + "FullName": "Jamaica", + "Acronym": "JAM" + }, + { + "FlagName": "BV", + "FullName": "Bouvet Island", + "Acronym": "BVT" + }, + { + "FlagName": "BW", + "FullName": "Botswana", + "Acronym": "BWA" + }, + { + "FlagName": "WS", + "FullName": "Samoa", + "Acronym": "WSM" + }, + { + "FlagName": "BQ", + "FullName": "Bonaire, Saint Eustatius and Saba", + "Acronym": "BES" + }, + { + "FlagName": "BR", + "FullName": "Brazil", + "Acronym": "BRA" + }, + { + "FlagName": "BS", + "FullName": "Bahamas", + "Acronym": "BHS" + }, + { + "FlagName": "JE", + "FullName": "Jersey", + "Acronym": "JEY" + }, + { + "FlagName": "BY", + "FullName": "Belarus", + "Acronym": "BLR" + }, + { + "FlagName": "BZ", + "FullName": "Belize", + "Acronym": "BLZ" + }, + { + "FlagName": "RU", + "FullName": "Russia", + "Acronym": "RUS" + }, + { + "FlagName": "RW", + "FullName": "Rwanda", + "Acronym": "RWA" + }, + { + "FlagName": "RS", + "FullName": "Serbia", + "Acronym": "SRB" + }, + { + "FlagName": "TL", + "FullName": "East Timor", + "Acronym": "TLS" + }, + { + "FlagName": "RE", + "FullName": "Reunion", + "Acronym": "REU" + }, + { + "FlagName": "TM", + "FullName": "Turkmenistan", + "Acronym": "TKM" + }, + { + "FlagName": "TJ", + "FullName": "Tajikistan", + "Acronym": "TJK" + }, + { + "FlagName": "RO", + "FullName": "Romania", + "Acronym": "ROU" + }, + { + "FlagName": "TK", + "FullName": "Tokelau", + "Acronym": "TKL" + }, + { + "FlagName": "GW", + "FullName": "Guinea-Bissau", + "Acronym": "GNB" + }, + { + "FlagName": "GU", + "FullName": "Guam", + "Acronym": "GUM" + }, + { + "FlagName": "GT", + "FullName": "Guatemala", + "Acronym": "GTM" + }, + { + "FlagName": "GS", + "FullName": "South Georgia and the South Sandwich Islands", + "Acronym": "SGS" + }, + { + "FlagName": "GR", + "FullName": "Greece", + "Acronym": "GRC" + }, + { + "FlagName": "GQ", + "FullName": "Equatorial Guinea", + "Acronym": "GNQ" + }, + { + "FlagName": "GP", + "FullName": "Guadeloupe", + "Acronym": "GLP" + }, + { + "FlagName": "JP", + "FullName": "Japan", + "Acronym": "JPN" + }, + { + "FlagName": "GY", + "FullName": "Guyana", + "Acronym": "GUY" + }, + { + "FlagName": "GG", + "FullName": "Guernsey", + "Acronym": "GGY" + }, + { + "FlagName": "GF", + "FullName": "French Guiana", + "Acronym": "GUF" + }, + { + "FlagName": "GE", + "FullName": "Georgia", + "Acronym": "GEO" + }, + { + "FlagName": "GD", + "FullName": "Grenada", + "Acronym": "GRD" + }, + { + "FlagName": "GB", + "FullName": "United Kingdom", + "Acronym": "GBR" + }, + { + "FlagName": "GA", + "FullName": "Gabon", + "Acronym": "GAB" + }, + { + "FlagName": "SV", + "FullName": "El Salvador", + "Acronym": "SLV" + }, + { + "FlagName": "GN", + "FullName": "Guinea", + "Acronym": "GIN" + }, + { + "FlagName": "GM", + "FullName": "Gambia", + "Acronym": "GMB" + }, + { + "FlagName": "GL", + "FullName": "Greenland", + "Acronym": "GRL" + }, + { + "FlagName": "GI", + "FullName": "Gibraltar", + "Acronym": "GIB" + }, + { + "FlagName": "GH", + "FullName": "Ghana", + "Acronym": "GHA" + }, + { + "FlagName": "OM", + "FullName": "Oman", + "Acronym": "OMN" + }, + { + "FlagName": "TN", + "FullName": "Tunisia", + "Acronym": "TUN" + }, + { + "FlagName": "JO", + "FullName": "Jordan", + "Acronym": "JOR" + }, + { + "FlagName": "HR", + "FullName": "Croatia", + "Acronym": "HRV" + }, + { + "FlagName": "HT", + "FullName": "Haiti", + "Acronym": "HTI" + }, + { + "FlagName": "HU", + "FullName": "Hungary", + "Acronym": "HUN" + }, + { + "FlagName": "HK", + "FullName": "Hong Kong", + "Acronym": "HKG" + }, + { + "FlagName": "HN", + "FullName": "Honduras", + "Acronym": "HND" + }, + { + "FlagName": "HM", + "FullName": "Heard Island and McDonald Islands", + "Acronym": "HMD" + }, + { + "FlagName": "VE", + "FullName": "Venezuela", + "Acronym": "VEN" + }, + { + "FlagName": "PR", + "FullName": "Puerto Rico", + "Acronym": "PRI" + }, + { + "FlagName": "PS", + "FullName": "Palestinian Territory", + "Acronym": "PSE" + }, + { + "FlagName": "PW", + "FullName": "Palau", + "Acronym": "PLW" + }, + { + "FlagName": "PT", + "FullName": "Portugal", + "Acronym": "PRT" + }, + { + "FlagName": "SJ", + "FullName": "Svalbard and Jan Mayen", + "Acronym": "SJM" + }, + { + "FlagName": "PY", + "FullName": "Paraguay", + "Acronym": "PRY" + }, + { + "FlagName": "IQ", + "FullName": "Iraq", + "Acronym": "IRQ" + }, + { + "FlagName": "PA", + "FullName": "Panama", + "Acronym": "PAN" + }, + { + "FlagName": "PF", + "FullName": "French Polynesia", + "Acronym": "PYF" + }, + { + "FlagName": "PG", + "FullName": "Papua New Guinea", + "Acronym": "PNG" + }, + { + "FlagName": "PE", + "FullName": "Peru", + "Acronym": "PER" + }, + { + "FlagName": "PK", + "FullName": "Pakistan", + "Acronym": "PAK" + }, + { + "FlagName": "PH", + "FullName": "Philippines", + "Acronym": "PHL" + }, + { + "FlagName": "PN", + "FullName": "Pitcairn", + "Acronym": "PCN" + }, + { + "FlagName": "PL", + "FullName": "Poland", + "Acronym": "POL" + }, + { + "FlagName": "PM", + "FullName": "Saint Pierre and Miquelon", + "Acronym": "SPM" + }, + { + "FlagName": "ZM", + "FullName": "Zambia", + "Acronym": "ZMB" + }, + { + "FlagName": "EH", + "FullName": "Western Sahara", + "Acronym": "ESH" + }, + { + "FlagName": "EE", + "FullName": "Estonia", + "Acronym": "EST" + }, + { + "FlagName": "EG", + "FullName": "Egypt", + "Acronym": "EGY" + }, + { + "FlagName": "ZA", + "FullName": "South Africa", + "Acronym": "ZAF" + }, + { + "FlagName": "EC", + "FullName": "Ecuador", + "Acronym": "ECU" + }, + { + "FlagName": "IT", + "FullName": "Italy", + "Acronym": "ITA" + }, + { + "FlagName": "VN", + "FullName": "Vietnam", + "Acronym": "VNM" + }, + { + "FlagName": "SB", + "FullName": "Solomon Islands", + "Acronym": "SLB" + }, + { + "FlagName": "ET", + "FullName": "Ethiopia", + "Acronym": "ETH" + }, + { + "FlagName": "SO", + "FullName": "Somalia", + "Acronym": "SOM" + }, + { + "FlagName": "ZW", + "FullName": "Zimbabwe", + "Acronym": "ZWE" + }, + { + "FlagName": "SA", + "FullName": "Saudi Arabia", + "Acronym": "SAU" + }, + { + "FlagName": "ES", + "FullName": "Spain", + "Acronym": "ESP" + }, + { + "FlagName": "ER", + "FullName": "Eritrea", + "Acronym": "ERI" + }, + { + "FlagName": "ME", + "FullName": "Montenegro", + "Acronym": "MNE" + }, + { + "FlagName": "MD", + "FullName": "Moldova", + "Acronym": "MDA" + }, + { + "FlagName": "MG", + "FullName": "Madagascar", + "Acronym": "MDG" + }, + { + "FlagName": "MF", + "FullName": "Saint Martin", + "Acronym": "MAF" + }, + { + "FlagName": "MA", + "FullName": "Morocco", + "Acronym": "MAR" + }, + { + "FlagName": "MC", + "FullName": "Monaco", + "Acronym": "MCO" + }, + { + "FlagName": "UZ", + "FullName": "Uzbekistan", + "Acronym": "UZB" + }, + { + "FlagName": "MM", + "FullName": "Myanmar", + "Acronym": "MMR" + }, + { + "FlagName": "ML", + "FullName": "Mali", + "Acronym": "MLI" + }, + { + "FlagName": "MO", + "FullName": "Macao", + "Acronym": "MAC" + }, + { + "FlagName": "MN", + "FullName": "Mongolia", + "Acronym": "MNG" + }, + { + "FlagName": "MH", + "FullName": "Marshall Islands", + "Acronym": "MHL" + }, + { + "FlagName": "MK", + "FullName": "Macedonia", + "Acronym": "MKD" + }, + { + "FlagName": "MU", + "FullName": "Mauritius", + "Acronym": "MUS" + }, + { + "FlagName": "MT", + "FullName": "Malta", + "Acronym": "MLT" + }, + { + "FlagName": "MW", + "FullName": "Malawi", + "Acronym": "MWI" + }, + { + "FlagName": "MV", + "FullName": "Maldives", + "Acronym": "MDV" + }, + { + "FlagName": "MQ", + "FullName": "Martinique", + "Acronym": "MTQ" + }, + { + "FlagName": "MP", + "FullName": "Northern Mariana Islands", + "Acronym": "MNP" + }, + { + "FlagName": "MS", + "FullName": "Montserrat", + "Acronym": "MSR" + }, + { + "FlagName": "MR", + "FullName": "Mauritania", + "Acronym": "MRT" + }, + { + "FlagName": "IM", + "FullName": "Isle of Man", + "Acronym": "IMN" + }, + { + "FlagName": "UG", + "FullName": "Uganda", + "Acronym": "UGA" + }, + { + "FlagName": "TZ", + "FullName": "Tanzania", + "Acronym": "TZA" + }, + { + "FlagName": "MY", + "FullName": "Malaysia", + "Acronym": "MYS" + }, + { + "FlagName": "MX", + "FullName": "Mexico", + "Acronym": "MEX" + }, + { + "FlagName": "IL", + "FullName": "Israel", + "Acronym": "ISR" + }, + { + "FlagName": "FR", + "FullName": "France", + "Acronym": "FRA" + }, + { + "FlagName": "IO", + "FullName": "British Indian Ocean Territory", + "Acronym": "IOT" + }, + { + "FlagName": "SH", + "FullName": "Saint Helena", + "Acronym": "SHN" + }, + { + "FlagName": "FI", + "FullName": "Finland", + "Acronym": "FIN" + }, + { + "FlagName": "FJ", + "FullName": "Fiji", + "Acronym": "FJI" + }, + { + "FlagName": "FK", + "FullName": "Falkland Islands", + "Acronym": "FLK" + }, + { + "FlagName": "FM", + "FullName": "Micronesia", + "Acronym": "FSM" + }, + { + "FlagName": "FO", + "FullName": "Faroe Islands", + "Acronym": "FRO" + }, + { + "FlagName": "NI", + "FullName": "Nicaragua", + "Acronym": "NIC" + }, + { + "FlagName": "NL", + "FullName": "Netherlands", + "Acronym": "NLD" + }, + { + "FlagName": "NO", + "FullName": "Norway", + "Acronym": "NOR" + }, + { + "FlagName": "NA", + "FullName": "Namibia", + "Acronym": "NAM" + }, + { + "FlagName": "VU", + "FullName": "Vanuatu", + "Acronym": "VUT" + }, + { + "FlagName": "NC", + "FullName": "New Caledonia", + "Acronym": "NCL" + }, + { + "FlagName": "NE", + "FullName": "Niger", + "Acronym": "NER" + }, + { + "FlagName": "NF", + "FullName": "Norfolk Island", + "Acronym": "NFK" + }, + { + "FlagName": "NG", + "FullName": "Nigeria", + "Acronym": "NGA" + }, + { + "FlagName": "NZ", + "FullName": "New Zealand", + "Acronym": "NZL" + }, + { + "FlagName": "NP", + "FullName": "Nepal", + "Acronym": "NPL" + }, + { + "FlagName": "NR", + "FullName": "Nauru", + "Acronym": "NRU" + }, + { + "FlagName": "NU", + "FullName": "Niue", + "Acronym": "NIU" + }, + { + "FlagName": "CK", + "FullName": "Cook Islands", + "Acronym": "COK" + }, + { + "FlagName": "XK", + "FullName": "Kosovo", + "Acronym": "XKX" + }, + { + "FlagName": "CI", + "FullName": "Ivory Coast", + "Acronym": "CIV" + }, + { + "FlagName": "CH", + "FullName": "Switzerland", + "Acronym": "CHE" + }, + { + "FlagName": "CO", + "FullName": "Colombia", + "Acronym": "COL" + }, + { + "FlagName": "CN", + "FullName": "China", + "Acronym": "CHN" + }, + { + "FlagName": "CM", + "FullName": "Cameroon", + "Acronym": "CMR" + }, + { + "FlagName": "CL", + "FullName": "Chile", + "Acronym": "CHL" + }, + { + "FlagName": "CC", + "FullName": "Cocos Islands", + "Acronym": "CCK" + }, + { + "FlagName": "CA", + "FullName": "Canada", + "Acronym": "CAN" + }, + { + "FlagName": "CG", + "FullName": "Republic of the Congo", + "Acronym": "COG" + }, + { + "FlagName": "CF", + "FullName": "Central African Republic", + "Acronym": "CAF" + }, + { + "FlagName": "CD", + "FullName": "Democratic Republic of the Congo", + "Acronym": "COD" + }, + { + "FlagName": "CZ", + "FullName": "Czech Republic", + "Acronym": "CZE" + }, + { + "FlagName": "CY", + "FullName": "Cyprus", + "Acronym": "CYP" + }, + { + "FlagName": "CX", + "FullName": "Christmas Island", + "Acronym": "CXR" + }, + { + "FlagName": "CR", + "FullName": "Costa Rica", + "Acronym": "CRI" + }, + { + "FlagName": "CW", + "FullName": "Curacao", + "Acronym": "CUW" + }, + { + "FlagName": "CV", + "FullName": "Cape Verde", + "Acronym": "CPV" + }, + { + "FlagName": "CU", + "FullName": "Cuba", + "Acronym": "CUB" + }, + { + "FlagName": "SZ", + "FullName": "Swaziland", + "Acronym": "SWZ" + }, + { + "FlagName": "SY", + "FullName": "Syria", + "Acronym": "SYR" + }, + { + "FlagName": "SX", + "FullName": "Sint Maarten", + "Acronym": "SXM" + }, + { + "FlagName": "KG", + "FullName": "Kyrgyzstan", + "Acronym": "KGZ" + }, + { + "FlagName": "KE", + "FullName": "Kenya", + "Acronym": "KEN" + }, + { + "FlagName": "SS", + "FullName": "South Sudan", + "Acronym": "SSD" + }, + { + "FlagName": "SR", + "FullName": "Suriname", + "Acronym": "SUR" + }, + { + "FlagName": "KI", + "FullName": "Kiribati", + "Acronym": "KIR" + }, + { + "FlagName": "KH", + "FullName": "Cambodia", + "Acronym": "KHM" + }, + { + "FlagName": "KN", + "FullName": "Saint Kitts and Nevis", + "Acronym": "KNA" + }, + { + "FlagName": "KM", + "FullName": "Comoros", + "Acronym": "COM" + }, + { + "FlagName": "ST", + "FullName": "Sao Tome and Principe", + "Acronym": "STP" + }, + { + "FlagName": "SK", + "FullName": "Slovakia", + "Acronym": "SVK" + }, + { + "FlagName": "KR", + "FullName": "South Korea", + "Acronym": "KOR" + }, + { + "FlagName": "SI", + "FullName": "Slovenia", + "Acronym": "SVN" + }, + { + "FlagName": "KP", + "FullName": "North Korea", + "Acronym": "PRK" + }, + { + "FlagName": "KW", + "FullName": "Kuwait", + "Acronym": "KWT" + }, + { + "FlagName": "SN", + "FullName": "Senegal", + "Acronym": "SEN" + }, + { + "FlagName": "SM", + "FullName": "San Marino", + "Acronym": "SMR" + }, + { + "FlagName": "SL", + "FullName": "Sierra Leone", + "Acronym": "SLE" + }, + { + "FlagName": "SC", + "FullName": "Seychelles", + "Acronym": "SYC" + }, + { + "FlagName": "KZ", + "FullName": "Kazakhstan", + "Acronym": "KAZ" + }, + { + "FlagName": "KY", + "FullName": "Cayman Islands", + "Acronym": "CYM" + }, + { + "FlagName": "SG", + "FullName": "Singapore", + "Acronym": "SGP" + }, + { + "FlagName": "SE", + "FullName": "Sweden", + "Acronym": "SWE" + }, + { + "FlagName": "SD", + "FullName": "Sudan", + "Acronym": "SDN" + }, + { + "FlagName": "DO", + "FullName": "Dominican Republic", + "Acronym": "DOM" + }, + { + "FlagName": "DM", + "FullName": "Dominica", + "Acronym": "DMA" + }, + { + "FlagName": "DJ", + "FullName": "Djibouti", + "Acronym": "DJI" + }, + { + "FlagName": "DK", + "FullName": "Denmark", + "Acronym": "DNK" + }, + { + "FlagName": "VG", + "FullName": "British Virgin Islands", + "Acronym": "VGB" + }, + { + "FlagName": "DE", + "FullName": "Germany", + "Acronym": "DEU" + }, + { + "FlagName": "YE", + "FullName": "Yemen", + "Acronym": "YEM" + }, + { + "FlagName": "DZ", + "FullName": "Algeria", + "Acronym": "DZA" + }, + { + "FlagName": "US", + "FullName": "United States", + "Acronym": "USA" + }, + { + "FlagName": "UY", + "FullName": "Uruguay", + "Acronym": "URY" + }, + { + "FlagName": "YT", + "FullName": "Mayotte", + "Acronym": "MYT" + }, + { + "FlagName": "UM", + "FullName": "United States Minor Outlying Islands", + "Acronym": "UMI" + }, + { + "FlagName": "LB", + "FullName": "Lebanon", + "Acronym": "LBN" + }, + { + "FlagName": "LC", + "FullName": "Saint Lucia", + "Acronym": "LCA" + }, + { + "FlagName": "LA", + "FullName": "Laos", + "Acronym": "LAO" + }, + { + "FlagName": "TV", + "FullName": "Tuvalu", + "Acronym": "TUV" + }, + { + "FlagName": "TW", + "FullName": "Taiwan", + "Acronym": "TWN" + }, + { + "FlagName": "TT", + "FullName": "Trinidad and Tobago", + "Acronym": "TTO" + }, + { + "FlagName": "TR", + "FullName": "Turkey", + "Acronym": "TUR" + }, + { + "FlagName": "LK", + "FullName": "Sri Lanka", + "Acronym": "LKA" + }, + { + "FlagName": "LI", + "FullName": "Liechtenstein", + "Acronym": "LIE" + }, + { + "FlagName": "LV", + "FullName": "Latvia", + "Acronym": "LVA" + }, + { + "FlagName": "TO", + "FullName": "Tonga", + "Acronym": "TON" + }, + { + "FlagName": "LT", + "FullName": "Lithuania", + "Acronym": "LTU" + }, + { + "FlagName": "LU", + "FullName": "Luxembourg", + "Acronym": "LUX" + }, + { + "FlagName": "LR", + "FullName": "Liberia", + "Acronym": "LBR" + }, + { + "FlagName": "LS", + "FullName": "Lesotho", + "Acronym": "LSO" + }, + { + "FlagName": "TH", + "FullName": "Thailand", + "Acronym": "THA" + }, + { + "FlagName": "TF", + "FullName": "French Southern Territories", + "Acronym": "ATF" + }, + { + "FlagName": "TG", + "FullName": "Togo", + "Acronym": "TGO" + }, + { + "FlagName": "TD", + "FullName": "Chad", + "Acronym": "TCD" + }, + { + "FlagName": "TC", + "FullName": "Turks and Caicos Islands", + "Acronym": "TCA" + }, + { + "FlagName": "LY", + "FullName": "Libya", + "Acronym": "LBY" + }, + { + "FlagName": "VA", + "FullName": "Vatican", + "Acronym": "VAT" + }, + { + "FlagName": "VC", + "FullName": "Saint Vincent and the Grenadines", + "Acronym": "VCT" + }, + { + "FlagName": "AE", + "FullName": "United Arab Emirates", + "Acronym": "ARE" + }, + { + "FlagName": "AD", + "FullName": "Andorra", + "Acronym": "AND" + }, + { + "FlagName": "AG", + "FullName": "Antigua and Barbuda", + "Acronym": "ATG" + }, + { + "FlagName": "AF", + "FullName": "Afghanistan", + "Acronym": "AFG" + }, + { + "FlagName": "AI", + "FullName": "Anguilla", + "Acronym": "AIA" + }, + { + "FlagName": "VI", + "FullName": "U.S. Virgin Islands", + "Acronym": "VIR" + }, + { + "FlagName": "IS", + "FullName": "Iceland", + "Acronym": "ISL" + }, + { + "FlagName": "IR", + "FullName": "Iran", + "Acronym": "IRN" + }, + { + "FlagName": "AM", + "FullName": "Armenia", + "Acronym": "ARM" + }, + { + "FlagName": "AL", + "FullName": "Albania", + "Acronym": "ALB" + }, + { + "FlagName": "AO", + "FullName": "Angola", + "Acronym": "AGO" + }, + { + "FlagName": "AQ", + "FullName": "Antarctica", + "Acronym": "ATA" + }, + { + "FlagName": "AS", + "FullName": "American Samoa", + "Acronym": "ASM" + }, + { + "FlagName": "AR", + "FullName": "Argentina", + "Acronym": "ARG" + }, + { + "FlagName": "AU", + "FullName": "Australia", + "Acronym": "AUS" + }, + { + "FlagName": "AT", + "FullName": "Austria", + "Acronym": "AUT" + }, + { + "FlagName": "AW", + "FullName": "Aruba", + "Acronym": "ABW" + }, + { + "FlagName": "IN", + "FullName": "India", + "Acronym": "IND" + }, + { + "FlagName": "AX", + "FullName": "Aland Islands", + "Acronym": "ALA" + }, + { + "FlagName": "AZ", + "FullName": "Azerbaijan", + "Acronym": "AZE" + }, + { + "FlagName": "IE", + "FullName": "Ireland", + "Acronym": "IRL" + }, + { + "FlagName": "ID", + "FullName": "Indonesia", + "Acronym": "IDN" + }, + { + "FlagName": "UA", + "FullName": "Ukraine", + "Acronym": "UKR" + }, + { + "FlagName": "QA", + "FullName": "Qatar", + "Acronym": "QAT" + }, + { + "FlagName": "MZ", + "FullName": "Mozambique", + "Acronym": "MOZ" + } +] \ No newline at end of file diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 2ae6c60067..f398dd0a3d 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; @@ -15,6 +16,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; +using osu.Game.Tournament.Components; namespace osu.Game.Tournament { @@ -87,12 +89,26 @@ namespace osu.Game.Tournament } } + // link pairings to groupings foreach (var group in Ladder.Groupings) foreach (var id in group.Pairings) Ladder.Pairings.Single(p => p.ID == id).Grouping.Value = group; Ladder.CurrentMatch.Value = Ladder.Pairings.FirstOrDefault(p => p.Current.Value); + // add full player info based on user IDs + foreach (var t in Ladder.Teams) + foreach (var p in t.Players) + if (p.Id == 1) + { + var req = new GetUserRequest(p.Id); + req.Success += i => p.Username = i.Username; + req.Perform(API); + + addedInfo = true; + } + + // add full beatmap info based on beatmap IDs foreach (var g in Ladder.Groupings) foreach (var b in g.Beatmaps) if (b.BeatmapInfo == null) @@ -104,6 +120,24 @@ namespace osu.Game.Tournament addedInfo = true; } + + List countries; + using (Stream stream = Resources.GetStream("Resources/countries.json")) + using (var sr = new StreamReader(stream)) + countries = JsonConvert.DeserializeObject>(sr.ReadToEnd()); + + foreach (var t in Ladder.Teams) + if (string.IsNullOrEmpty(t.FullName)) + { + var result = countries.FirstOrDefault(c => c.Acronym == t.Acronym); + if (result != null) + { + t.Acronym = result.Acronym; + t.FlagName = result.FlagName; + t.FullName = result.FullName; + } + } + if (addedInfo) SaveChanges(); @@ -126,7 +160,6 @@ namespace osu.Game.Tournament protected override void Update() { - base.Update(); var minWidth = (int)(windowSize.Value.Height / 9f * 16 + 400); if (windowSize.Value.Width < minWidth) From 3eabac0e3de1cbc04db391ca80421f91c9b5cd7c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Nov 2018 01:23:00 +0900 Subject: [PATCH 0231/2854] Move all IPC handling to its own class --- osu.Game.Tournament/IPC/FileBasedIPC.cs | 107 ++++++++++++++++++ .../Screens/BeatmapInfoScreen.cs | 104 ++--------------- osu.Game.Tournament/TournamentGameBase.cs | 5 + 3 files changed, 123 insertions(+), 93 deletions(-) create mode 100644 osu.Game.Tournament/IPC/FileBasedIPC.cs diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs new file mode 100644 index 0000000000..c42ccc11c2 --- /dev/null +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -0,0 +1,107 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Platform.Windows; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Rulesets; + +namespace osu.Game.Tournament.IPC +{ + public class FileBasedIPC : Component + { + [Resolved] + protected APIAccess API { get; private set; } + + [Resolved] + protected RulesetStore Rulesets { get; private set; } + + public readonly Bindable Beatmap = new Bindable(); + + public readonly Bindable Mods = new Bindable(); + + [BackgroundDependencyLoader] + private void load() + { + var stable = new StableStorage(); + + const string file_ipc_filename = "ipc.txt"; + + if (stable.Exists(file_ipc_filename)) + Scheduler.AddDelayed(delegate + { + try + { + using (var stream = stable.GetStream(file_ipc_filename)) + using (var sr = new StreamReader(stream)) + { + var beatmapId = int.Parse(sr.ReadLine()); + var mods = int.Parse(sr.ReadLine()); + + if (Beatmap.Value?.OnlineBeatmapID != beatmapId) + { + var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); + req.Success += b => Beatmap.Value = b.ToBeatmap(Rulesets); + API.Queue(req); + } + + Mods.Value = (LegacyMods)mods; + } + } + catch + { + // file might be in use. + } + }, 250, true); + } + + /// + /// A method of accessing an osu-stable install in a controlled fashion. + /// + private class StableStorage : WindowsStorage + { + protected override string LocateBasePath() + { + bool checkExists(string p) + { + return Directory.Exists(Path.Combine(p, "Songs")); + } + + string stableInstallPath; + + try + { + stableInstallPath = "E:\\osu!mappool"; + + if (checkExists(stableInstallPath)) + return stableInstallPath; + } + catch + { + } + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + return null; + } + + public StableStorage() + : base(string.Empty, null) + { + } + } + } +} diff --git a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs index 18ba947582..11cbb56b49 100644 --- a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs +++ b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs @@ -1,33 +1,18 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; -using System.IO; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Platform.Windows; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Online.API.Requests.Responses; -using osu.Game.Rulesets; using osu.Game.Screens; using osu.Game.Tournament.Components; +using osu.Game.Tournament.IPC; namespace osu.Game.Tournament.Screens { public abstract class BeatmapInfoScreen : OsuScreen { - [Resolved] - protected APIAccess API { get; private set; } - - [Resolved] - protected RulesetStore Rulesets { get; private set; } - - private int lastBeatmapId; - private int lastMods; - protected readonly SongBar SongBar; protected BeatmapInfoScreen() @@ -40,88 +25,21 @@ namespace osu.Game.Tournament.Screens } [BackgroundDependencyLoader] - private void load() + private void load(FileBasedIPC ipc) { - var stable = new StableStorage(); - - const string file_ipc_filename = "ipc.txt"; - - if (stable.Exists(file_ipc_filename)) - Scheduler.AddDelayed(delegate - { - try - { - using (var stream = stable.GetStream(file_ipc_filename)) - using (var sr = new StreamReader(stream)) - { - var beatmapId = int.Parse(sr.ReadLine()); - var mods = int.Parse(sr.ReadLine()); - - if (lastBeatmapId == beatmapId) - return; - - lastMods = mods; - lastBeatmapId = beatmapId; - - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); - req.Success += success; - API.Queue(req); - } - } - catch - { - // file might be in use. - } - }, 250, true); + ipc.Beatmap.BindValueChanged(beatmapChanged, true); + ipc.Mods.BindValueChanged(modsChanged, true); } - private void success(APIBeatmap apiBeatmap) + private void modsChanged(LegacyMods mods) + { + SongBar.Mods = mods; + } + + private void beatmapChanged(BeatmapInfo beatmap) { SongBar.FadeInFromZero(300, Easing.OutQuint); - SongBar.Mods = (LegacyMods)lastMods; - SongBar.Beatmap = apiBeatmap.ToBeatmap(Rulesets); - } - - /// - /// A method of accessing an osu-stable install in a controlled fashion. - /// - private class StableStorage : WindowsStorage - { - protected override string LocateBasePath() - { - bool checkExists(string p) - { - return Directory.Exists(Path.Combine(p, "Songs")); - } - - string stableInstallPath; - - try - { - stableInstallPath = "E:\\osu!mappool"; - - if (checkExists(stableInstallPath)) - return stableInstallPath; - } - catch - { - } - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - return null; - } - - public StableStorage() - : base(string.Empty, null) - { - } + SongBar.Beatmap = beatmap; } } } diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index f398dd0a3d..562b141095 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -17,6 +17,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; using osu.Game.Tournament.Components; +using osu.Game.Tournament.IPC; namespace osu.Game.Tournament { @@ -33,6 +34,7 @@ namespace osu.Game.Tournament private readonly Bindable ruleset = new Bindable(); private Bindable windowSize; + private FileBasedIPC ipc; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { @@ -63,6 +65,9 @@ namespace osu.Game.Tournament dependencies.Cache(Ladder); + dependencies.Cache(ipc = new FileBasedIPC()); + Add(ipc); + bool addedInfo = false; // assign teams From e4a767d656c9add79f0d4fc1863f363396fcb0c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Nov 2018 03:51:39 +0900 Subject: [PATCH 0232/2854] Move control panel logic to its own class --- .../Components/ControlPanel.cs | 73 ++++++++++++ .../Screens/Drawings/DrawingsScreen.cs | 106 ++++-------------- 2 files changed, 97 insertions(+), 82 deletions(-) create mode 100644 osu.Game.Tournament/Components/ControlPanel.cs diff --git a/osu.Game.Tournament/Components/ControlPanel.cs b/osu.Game.Tournament/Components/ControlPanel.cs new file mode 100644 index 0000000000..f016eb30a4 --- /dev/null +++ b/osu.Game.Tournament/Components/ControlPanel.cs @@ -0,0 +1,73 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Tournament.Components +{ + /// + /// An element anchored to the right-hand area of a screen that provides streamer level controls. + /// Should be off-screen. + /// + public class ControlPanel : Container + { + private readonly FillFlowContainer buttons; + + protected override Container Content => buttons; + + public ControlPanel() + { + RelativeSizeAxes = Axes.Both; + AlwaysPresent = true; + Width = 0.15f; + Anchor = Anchor.TopRight; + + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new Color4(54, 54, 54, 255) + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + Text = "Control Panel", + TextSize = 22f, + Font = "Exo2.0-Bold" + }, + buttons = new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.75f, + + Position = new Vector2(0, 35f), + + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5f), + }, + }; + } + + public class Spacer : CompositeDrawable + { + public Spacer(float height = 20) + { + RelativeSizeAxes = Axes.X; + Height = height; + AlwaysPresent = true; + } + } + } +} diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index 9e8074d7dc..f94c882c85 100644 --- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; @@ -147,93 +146,36 @@ namespace osu.Game.Tournament.Screens.Drawings } }, // Control panel container - new Container + new ControlPanel { - RelativeSizeAxes = Axes.Both, - AlwaysPresent = true, - Width = 0.15f, - Anchor = Anchor.TopRight, - - Children = new Drawable[] + new TriangleButton { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = new Color4(54, 54, 54, 255) - }, - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, - Text = "Control Panel", - TextSize = 22f, - Font = "Exo2.0-Bold" - }, - new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, + Text = "Begin random", + Action = teamsContainer.StartScrolling, + }, + new TriangleButton + { + RelativeSizeAxes = Axes.X, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Width = 0.75f, + Text = "Stop random", + Action = teamsContainer.StopScrolling, + }, + new TriangleButton + { + RelativeSizeAxes = Axes.X, - Position = new Vector2(0, 35f), + Text = "Reload", + Action = reloadTeams + }, + new ControlPanel.Spacer(), + new TriangleButton + { + RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5f), - - Children = new Drawable[] - { - new TriangleButton - { - RelativeSizeAxes = Axes.X, - - Text = "Begin random", - Action = teamsContainer.StartScrolling, - }, - new TriangleButton - { - RelativeSizeAxes = Axes.X, - - Text = "Stop random", - Action = teamsContainer.StopScrolling, - }, - new TriangleButton - { - RelativeSizeAxes = Axes.X, - - Text = "Reload", - Action = reloadTeams - } - } - }, - new FillFlowContainer - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Width = 0.75f, - - Position = new Vector2(0, -5f), - - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5f), - - Children = new Drawable[] - { - new TriangleButton - { - RelativeSizeAxes = Axes.X, - - Text = "Reset", - Action = () => reset() - } - } - } + Text = "Reset", + Action = () => reset() } } }; From 5c84c3c0a8157b65a64ddcffaa871d9446297de2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Nov 2018 06:29:04 +0900 Subject: [PATCH 0233/2854] Add support for picks and bans --- .../TestCaseLadderManager.cs | 2 +- osu.Game.Tournament.Tests/TestCaseMapPool.cs | 13 +- .../Components/TournamentBeatmapPanel.cs | 68 ++++++++-- .../Ladder/Components/BeatmapChoice.cs | 24 ++++ .../Screens/Ladder/Components/MatchPairing.cs | 6 +- .../Screens/Ladder/LadderManager.cs | 27 ++-- .../Screens/MapPool/MapPoolScreen.cs | 125 +++++++++++++++++- .../Screens/TournamentSceneManager.cs | 5 +- 8 files changed, 232 insertions(+), 38 deletions(-) create mode 100644 osu.Game.Tournament/Screens/Ladder/Components/BeatmapChoice.cs diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index 5fa378a854..3ddd52eeac 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tournament.Tests Add(new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, - Child = manager = new LadderManager(Ladder) + Child = manager = new LadderManager() }); } diff --git a/osu.Game.Tournament.Tests/TestCaseMapPool.cs b/osu.Game.Tournament.Tests/TestCaseMapPool.cs index 1101d2828a..d953dbc5d3 100644 --- a/osu.Game.Tournament.Tests/TestCaseMapPool.cs +++ b/osu.Game.Tournament.Tests/TestCaseMapPool.cs @@ -1,7 +1,8 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Linq; +using System; +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Game.Tournament.Screens.MapPool; @@ -9,13 +10,15 @@ namespace osu.Game.Tournament.Tests { public class TestCaseMapPool : LadderTestCase { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(MapPoolScreen) + }; + [BackgroundDependencyLoader] private void load() { - var round = Ladder.Groupings.FirstOrDefault(g => g.Name == "Finals"); - - if (round != null) - Add(new MapPoolScreen(round)); + Add(new MapPoolScreen { Width = 0.7f }); } } } diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 9542c26faf..7f9494e3c9 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -1,7 +1,11 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -10,28 +14,35 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Tournament.Screens.Ladder.Components; using OpenTK.Graphics; namespace osu.Game.Tournament.Components { public class TournamentBeatmapPanel : CompositeDrawable { - private readonly BeatmapInfo beatmap; + public readonly BeatmapInfo Beatmap; + private const float horizontal_padding = 10; private const float vertical_padding = 5; public const float HEIGHT = 50; + private readonly Bindable currentMatch = new Bindable(); + public TournamentBeatmapPanel(BeatmapInfo beatmap) { - this.beatmap = beatmap; + Beatmap = beatmap; Width = 400; Height = HEIGHT; } [BackgroundDependencyLoader] - private void load() + private void load(LadderInfo ladder) { + currentMatch.BindValueChanged(matchChanged); + currentMatch.BindTo(ladder.CurrentMatch); + CornerRadius = 25; Masking = true; @@ -46,7 +57,7 @@ namespace osu.Game.Tournament.Components { RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.5f), - BeatmapSet = beatmap.BeatmapSet, + BeatmapSet = Beatmap.BeatmapSet, }, new FillFlowContainer { @@ -62,8 +73,8 @@ namespace osu.Game.Tournament.Components Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Text = new LocalisedString(( - $"{beatmap.Metadata.ArtistUnicode} - {beatmap.Metadata.TitleUnicode}", - $"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title}")), + $"{Beatmap.Metadata.ArtistUnicode} - {Beatmap.Metadata.TitleUnicode}", + $"{Beatmap.Metadata.Artist} - {Beatmap.Metadata.Title}")), Font = @"Exo2.0-BoldItalic", }, new FillFlowContainer @@ -84,7 +95,7 @@ namespace osu.Game.Tournament.Components }, new OsuSpriteText { - Text = beatmap.Metadata.AuthorString, + Text = Beatmap.Metadata.AuthorString, Font = @"Exo2.0-BoldItalic", Padding = new MarginPadding { Right = 20 }, TextSize = 14 @@ -98,7 +109,7 @@ namespace osu.Game.Tournament.Components }, new OsuSpriteText { - Text = beatmap.Version, + Text = Beatmap.Version, Font = @"Exo2.0-BoldItalic", TextSize = 14 }, @@ -108,5 +119,46 @@ namespace osu.Game.Tournament.Components }, }); } + + private void matchChanged(MatchPairing match) + { + match.PicksBans.CollectionChanged += picksBansOnCollectionChanged; + updateState(); + } + + private void updateState() + { + var found = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == Beatmap.OnlineBeatmapID); + + if (found != null) + { + switch (found.Team) + { + case TeamColour.Red: + Colour = Color4.Red; + break; + case TeamColour.Blue: + Colour = Color4.Blue; + break; + } + } + else + { + Colour = Color4.White; + } + } + + private void picksBansOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + var list = (ObservableCollection)sender; + if (sender != currentMatch.Value.PicksBans) + { + // todo: we need a last attribute in bindable valuechanged events badly. + list.CollectionChanged -= picksBansOnCollectionChanged; + return; + } + + updateState(); + } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/BeatmapChoice.cs b/osu.Game.Tournament/Screens/Ladder/Components/BeatmapChoice.cs new file mode 100644 index 0000000000..d0b2556603 --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/BeatmapChoice.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + public class BeatmapChoice + { + public TeamColour Team; + public ChoiceType Type; + public int BeatmapID; + } + + public enum TeamColour + { + Red, + Blue + } + + public enum ChoiceType + { + Pick, + Ban, + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index aa0c3229c9..63c96d350d 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.ObjectModel; using Newtonsoft.Json; using osu.Framework.Configuration; using osu.Game.Tournament.Components; @@ -34,6 +35,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly Bindable Losers = new Bindable(); + public readonly ObservableCollection PicksBans = new ObservableCollection(); + [JsonIgnore] public readonly Bindable Grouping = new Bindable(); @@ -58,7 +61,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Team2.BindValueChanged(t => Team2Acronym = t?.Acronym, true); } - public MatchPairing(TournamentTeam team1 = null, TournamentTeam team2 = null) : this() + public MatchPairing(TournamentTeam team1 = null, TournamentTeam team2 = null) + : this() { Team1.Value = team1; Team2.Value = team2; diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index f348eff571..805358e231 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -26,20 +26,24 @@ namespace osu.Game.Tournament.Screens.Ladder [Cached] public class LadderManager : CompositeDrawable, IHasContextMenu { - public readonly List Teams; - private readonly Container pairingsContainer; - private readonly Container paths; - private readonly Container headings; + public List Teams; + private Container pairingsContainer; + private Container paths; + private Container headings; - private readonly LadderInfo info; + private LadderInfo info; - private readonly ScrollableContainer scrollContent; + private ScrollableContainer scrollContent; [Cached] - private readonly LadderEditorInfo editorInfo = new LadderEditorInfo(); + private LadderEditorInfo editorInfo = new LadderEditorInfo(); - public LadderManager(LadderInfo info) + [BackgroundDependencyLoader] + private void load(LadderInfo info, OsuColour colours) { + normalPathColour = colours.BlueDarker.Darken(2); + losersPathColour = colours.YellowDarker.Darken(2); + this.info = info; editorInfo.Teams = Teams = info.Teams; editorInfo.Groupings = info.Groupings; @@ -128,13 +132,6 @@ namespace osu.Game.Tournament.Screens.Ladder private Color4 normalPathColour; private Color4 losersPathColour; - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - normalPathColour = colours.BlueDarker.Darken(2); - losersPathColour = colours.YellowDarker.Darken(2); - } - private void updateLayout() { paths.Clear(); diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 6d7dca0aad..94f52267de 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -1,33 +1,148 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Screens; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; using OpenTK; +using OpenTK.Input; namespace osu.Game.Tournament.Screens.MapPool { public class MapPoolScreen : OsuScreen { - public MapPoolScreen(TournamentGrouping round) - { - FillFlowContainer maps; + private readonly FillFlowContainer maps; + private readonly Bindable currentMatch = new Bindable(); + + public MapPoolScreen() + { InternalChildren = new Drawable[] { - maps = new FillFlowContainer + maps = new FillFlowContainer { Spacing = new Vector2(20), Padding = new MarginPadding(50), Direction = FillDirection.Full, RelativeSizeAxes = Axes.Both, + }, + new ControlPanel + { + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "Current Mode" + }, + buttonRedBan = new TriangleButton + { + RelativeSizeAxes = Axes.X, + Text = "Red Ban", + Action = () => setMode(TeamColour.Red, ChoiceType.Ban) + }, + buttonBlueBan = new TriangleButton + { + RelativeSizeAxes = Axes.X, + Text = "Blue Ban", + Action = () => setMode(TeamColour.Blue, ChoiceType.Ban) + }, + buttonRedPick = new TriangleButton + { + RelativeSizeAxes = Axes.X, + Text = "Red Pick", + Action = () => setMode(TeamColour.Red, ChoiceType.Pick) + }, + buttonBluePick = new TriangleButton + { + RelativeSizeAxes = Axes.X, + Text = "Blue Pick", + Action = () => setMode(TeamColour.Blue, ChoiceType.Pick) + } + } } }; + } - foreach (var b in round.Beatmaps) + private TeamColour pickColour; + private ChoiceType pickType; + + private readonly TriangleButton buttonRedBan; + private readonly TriangleButton buttonBlueBan; + private readonly TriangleButton buttonRedPick; + private readonly TriangleButton buttonBluePick; + + private void setMode(TeamColour colour, ChoiceType choiceType) + { + pickColour = colour; + pickType = choiceType; + + var enabled = currentMatch.Value.PicksBans.Count == 0; + + buttonRedBan.Enabled.Value = enabled || pickColour == TeamColour.Red && pickType == ChoiceType.Ban; + buttonBlueBan.Enabled.Value = enabled || pickColour == TeamColour.Blue && pickType == ChoiceType.Ban; + buttonRedPick.Enabled.Value = enabled || pickColour == TeamColour.Red && pickType == ChoiceType.Pick; + buttonBluePick.Enabled.Value = enabled || pickColour == TeamColour.Blue && pickType == ChoiceType.Pick; + } + + private void setNextMode() + { + const TeamColour roll_winner = TeamColour.Red; //todo: draw from match + + var nextColour = (currentMatch.Value.PicksBans.LastOrDefault()?.Team ?? roll_winner) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; + + setMode(nextColour, currentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= 2 ? ChoiceType.Pick : ChoiceType.Ban); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + var map = maps.FirstOrDefault(m => m.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)); + if (map != null) + { + if (e.Button == MouseButton.Left) + { + currentMatch.Value.PicksBans.Add(new BeatmapChoice + { + Team = pickColour, + Type = pickType, + BeatmapID = map.Beatmap.OnlineBeatmapID ?? -1 + }); + + setNextMode(); + } + else + { + var existing = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == map.Beatmap.OnlineBeatmapID); + if (existing != null) + { + currentMatch.Value.PicksBans.Remove(existing); + setNextMode(); + } + } + + return true; + } + + return base.OnMouseDown(e); + } + + [BackgroundDependencyLoader] + private void load(LadderInfo ladder) + { + currentMatch.BindValueChanged(matchChanged); + currentMatch.BindTo(ladder.CurrentMatch); + } + + private void matchChanged(MatchPairing match) + { + foreach (var b in match.Grouping.Value.Beatmaps) maps.Add(new TournamentBeatmapPanel(b.BeatmapInfo) { Anchor = Anchor.TopCentre, diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index 91d389b63d..4cd6c681fa 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -88,9 +87,9 @@ namespace osu.Game.Tournament.Screens RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - bracket = new LadderManager(ladder), + bracket = new LadderManager(), showcase = new ShowcaseScreen(), - mapPool = new MapPoolScreen(ladder.Groupings.First(g => g.Name == "Finals")), + mapPool = new MapPoolScreen(), teamIntro = new TeamIntroScreen(), drawings = new DrawingsScreen(), gameplay = new GameplayScreen() From 5da6f11a141b76c5d98c0cd52a2baef7aea16405 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Nov 2018 06:36:36 +0900 Subject: [PATCH 0234/2854] Automate picks and bans from IPC --- .../Screens/MapPool/MapPoolScreen.cs | 63 ++++++++++++------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 94f52267de..fb9bd7b9a5 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -7,10 +7,12 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; +using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Screens; using osu.Game.Tournament.Components; +using osu.Game.Tournament.IPC; using osu.Game.Tournament.Screens.Ladder.Components; using OpenTK; using OpenTK.Input; @@ -23,6 +25,14 @@ namespace osu.Game.Tournament.Screens.MapPool private readonly Bindable currentMatch = new Bindable(); + private TeamColour pickColour; + private ChoiceType pickType; + + private readonly TriangleButton buttonRedBan; + private readonly TriangleButton buttonBlueBan; + private readonly TriangleButton buttonRedPick; + private readonly TriangleButton buttonBluePick; + public MapPoolScreen() { InternalChildren = new Drawable[] @@ -71,13 +81,20 @@ namespace osu.Game.Tournament.Screens.MapPool }; } - private TeamColour pickColour; - private ChoiceType pickType; + [BackgroundDependencyLoader] + private void load(LadderInfo ladder, FileBasedIPC ipc) + { + currentMatch.BindValueChanged(matchChanged); + currentMatch.BindTo(ladder.CurrentMatch); - private readonly TriangleButton buttonRedBan; - private readonly TriangleButton buttonBlueBan; - private readonly TriangleButton buttonRedPick; - private readonly TriangleButton buttonBluePick; + ipc.Beatmap.BindValueChanged(beatmapChanged); + } + + private void beatmapChanged(BeatmapInfo beatmap) + { + if (beatmap.OnlineBeatmapID != null) + addForBeatmap(beatmap.OnlineBeatmapID.Value); + } private void setMode(TeamColour colour, ChoiceType choiceType) { @@ -106,17 +123,8 @@ namespace osu.Game.Tournament.Screens.MapPool var map = maps.FirstOrDefault(m => m.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)); if (map != null) { - if (e.Button == MouseButton.Left) - { - currentMatch.Value.PicksBans.Add(new BeatmapChoice - { - Team = pickColour, - Type = pickType, - BeatmapID = map.Beatmap.OnlineBeatmapID ?? -1 - }); - - setNextMode(); - } + if (e.Button == MouseButton.Left && map.Beatmap.OnlineBeatmapID != null) + addForBeatmap(map.Beatmap.OnlineBeatmapID.Value); else { var existing = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == map.Beatmap.OnlineBeatmapID); @@ -133,11 +141,24 @@ namespace osu.Game.Tournament.Screens.MapPool return base.OnMouseDown(e); } - [BackgroundDependencyLoader] - private void load(LadderInfo ladder) + private void addForBeatmap(int beatmapId) { - currentMatch.BindValueChanged(matchChanged); - currentMatch.BindTo(ladder.CurrentMatch); + if (currentMatch.Value.Grouping.Value.Beatmaps.All(b => b.BeatmapInfo.OnlineBeatmapID != beatmapId)) + // don't attempt to add if the beatmap isn't in our pool + return; + + if (currentMatch.Value.PicksBans.Any(p => p.BeatmapID == beatmapId)) + // don't attempt to add if already exists. + return; + + currentMatch.Value.PicksBans.Add(new BeatmapChoice + { + Team = pickColour, + Type = pickType, + BeatmapID = beatmapId + }); + + setNextMode(); } private void matchChanged(MatchPairing match) From bd6d3f147316df900655ddc9a0d816ab11ef1f9e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Nov 2018 06:47:42 +0900 Subject: [PATCH 0235/2854] Improve appearance --- .../Components/TournamentBeatmapPanel.cs | 20 ++++++++++++-- .../Screens/MapPool/MapPoolScreen.cs | 26 ++++++++++++++----- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 7f9494e3c9..b804fc44a7 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -132,19 +132,35 @@ namespace osu.Game.Tournament.Components if (found != null) { + BorderThickness = 6; + switch (found.Team) { case TeamColour.Red: - Colour = Color4.Red; + BorderColour = Color4.Red; break; case TeamColour.Blue: - Colour = Color4.Blue; + BorderColour = Color4.Blue; + break; + } + + switch (found.Type) + { + case ChoiceType.Pick: + Colour = Color4.White; + Alpha = 1; + break; + case ChoiceType.Ban: + Colour = Color4.Gray; + Alpha = 0.5f; break; } } else { Colour = Color4.White; + BorderThickness = 0; + Alpha = 1; } } diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index fb9bd7b9a5..be56869115 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -15,6 +15,7 @@ using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Screens.Ladder.Components; using OpenTK; +using OpenTK.Graphics; using OpenTK.Input; namespace osu.Game.Tournament.Screens.MapPool @@ -75,7 +76,14 @@ namespace osu.Game.Tournament.Screens.MapPool RelativeSizeAxes = Axes.X, Text = "Blue Pick", Action = () => setMode(TeamColour.Blue, ChoiceType.Pick) - } + }, + new ControlPanel.Spacer(), + new TriangleButton + { + RelativeSizeAxes = Axes.X, + Text = "Reset", + Action = reset + }, } } }; @@ -101,12 +109,12 @@ namespace osu.Game.Tournament.Screens.MapPool pickColour = colour; pickType = choiceType; - var enabled = currentMatch.Value.PicksBans.Count == 0; + Color4 setColour(bool active) => active ? Color4.White : Color4.Gray; - buttonRedBan.Enabled.Value = enabled || pickColour == TeamColour.Red && pickType == ChoiceType.Ban; - buttonBlueBan.Enabled.Value = enabled || pickColour == TeamColour.Blue && pickType == ChoiceType.Ban; - buttonRedPick.Enabled.Value = enabled || pickColour == TeamColour.Red && pickType == ChoiceType.Pick; - buttonBluePick.Enabled.Value = enabled || pickColour == TeamColour.Blue && pickType == ChoiceType.Pick; + buttonRedBan.Colour = setColour(pickColour == TeamColour.Red && pickType == ChoiceType.Ban); + buttonBlueBan.Colour = setColour(pickColour == TeamColour.Blue && pickType == ChoiceType.Ban); + buttonRedPick.Colour = setColour(pickColour == TeamColour.Red && pickType == ChoiceType.Pick); + buttonBluePick.Colour = setColour(pickColour == TeamColour.Blue && pickType == ChoiceType.Pick); } private void setNextMode() @@ -141,6 +149,12 @@ namespace osu.Game.Tournament.Screens.MapPool return base.OnMouseDown(e); } + private void reset() + { + currentMatch.Value.PicksBans.Clear(); + setNextMode(); + } + private void addForBeatmap(int beatmapId) { if (currentMatch.Value.Grouping.Value.Beatmaps.All(b => b.BeatmapInfo.OnlineBeatmapID != beatmapId)) From a31507ff0e87443888faeb393d2c5a3266a96856 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Nov 2018 13:08:59 +0900 Subject: [PATCH 0236/2854] Safety check --- osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index be56869115..c7bdf8feee 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -157,6 +157,9 @@ namespace osu.Game.Tournament.Screens.MapPool private void addForBeatmap(int beatmapId) { + if (currentMatch.Value == null) + return; + if (currentMatch.Value.Grouping.Value.Beatmaps.All(b => b.BeatmapInfo.OnlineBeatmapID != beatmapId)) // don't attempt to add if the beatmap isn't in our pool return; From 6ff29c1ea4fbed864489d00be08aa6c03e7a31ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Nov 2018 16:55:55 +0900 Subject: [PATCH 0237/2854] Fix non-unbinding bindable bind --- osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index d515312dd0..7c4f139de7 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Video; @@ -21,11 +22,10 @@ namespace osu.Game.Tournament.Screens.TeamIntro { private Container mainContainer; - [Resolved] - private LadderInfo ladderInfo { get; set; } + private readonly Bindable currentMatch = new Bindable(); [BackgroundDependencyLoader] - private void load(Storage storage) + private void load(LadderInfo ladder, Storage storage) { RelativeSizeAxes = Axes.Both; @@ -42,7 +42,8 @@ namespace osu.Game.Tournament.Screens.TeamIntro } }; - ladderInfo.CurrentMatch.BindValueChanged(matchChanged, true); + currentMatch.BindValueChanged(matchChanged); + currentMatch.BindTo(ladder.CurrentMatch); } private void matchChanged(MatchPairing pairing) From ee6263f3956ab5b96c48b0801d32911502338fc2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Nov 2018 20:14:50 +0900 Subject: [PATCH 0238/2854] Fix old maps not getting cleared when switching matches --- .../Screens/MapPool/MapPoolScreen.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index c7bdf8feee..e8d6f18fd7 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -180,12 +180,17 @@ namespace osu.Game.Tournament.Screens.MapPool private void matchChanged(MatchPairing match) { - foreach (var b in match.Grouping.Value.Beatmaps) - maps.Add(new TournamentBeatmapPanel(b.BeatmapInfo) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }); + maps.Clear(); + + if (match.Grouping.Value != null) + { + foreach (var b in match.Grouping.Value.Beatmaps) + maps.Add(new TournamentBeatmapPanel(b.BeatmapInfo) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }); + } } } } From 0003a9310f809ad3a1fa2d3fcdfc2bcf48d50d20 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Nov 2018 20:15:08 +0900 Subject: [PATCH 0239/2854] Expose LargeTextureStore --- osu.Game/OsuGameBase.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index ea1dbfa369..c7f787cff1 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -110,9 +110,9 @@ namespace osu.Game dependencies.Cache(contextFactory = new DatabaseContextFactory(Host.Storage)); - var largeStore = new LargeTextureStore(new TextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures"))); - largeStore.AddStore(new TextureLoaderStore(new OnlineStore())); - dependencies.Cache(largeStore); + LargeTextureStore = new LargeTextureStore(new TextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures"))); + LargeTextureStore.AddStore(new TextureLoaderStore(new OnlineStore())); + dependencies.Cache(LargeTextureStore); dependencies.CacheAs(this); dependencies.Cache(LocalConfig); @@ -243,6 +243,8 @@ namespace osu.Game private readonly List fileImporters = new List(); + protected LargeTextureStore LargeTextureStore; + public void Import(params string[] paths) { var extension = Path.GetExtension(paths.First())?.ToLowerInvariant(); From 160984719d31ac5ab87829f4921f6f4ab5933432 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Nov 2018 20:15:22 +0900 Subject: [PATCH 0240/2854] Add team and star displays --- .../Screens/Drawings/DrawingsScreen.cs | 14 -- .../Screens/Gameplay/GameplayScreen.cs | 208 ++++++++++++++++++ osu.Game.Tournament/TournamentGameBase.cs | 4 +- 3 files changed, 211 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index f94c882c85..0e146cb15a 100644 --- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Graphics; @@ -47,11 +46,6 @@ namespace osu.Game.Tournament.Screens.Drawings public ITeamList TeamList; - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => - dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - [BackgroundDependencyLoader] private void load(TextureStore textures, Storage storage) { @@ -59,14 +53,6 @@ namespace osu.Game.Tournament.Screens.Drawings this.storage = storage; - TextureStore flagStore = new TextureStore(); - // Local flag store - flagStore.AddStore(new TextureLoaderStore(new NamespacedResourceStore(new StorageBackedResourceStore(storage), "Drawings"))); - // Default texture store - flagStore.AddStore(textures); - - dependencies.Cache(flagStore); - if (TeamList == null) TeamList = new StorageBackedTeamList(storage); diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 2a9754b066..9f46776a6d 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -1,10 +1,218 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Logging; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Tournament.Components; +using osu.Game.Tournament.Screens.Ladder.Components; +using OpenTK; +using OpenTK.Graphics; + namespace osu.Game.Tournament.Screens.Gameplay { public class GameplayScreen : BeatmapInfoScreen { + [BackgroundDependencyLoader] + private void load(LadderInfo ladder, TextureStore textures) + { + Add(new Container + { + RelativeSizeAxes = Axes.X, + Height = 100, + Children = new Drawable[] + { + new Sprite + { + Y = 5, + Texture = textures.Get("game-screen-logo"), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + FillMode = FillMode.Fit, + RelativeSizeAxes = Axes.Both, + Size = Vector2.One + }, + new RoundDisplay + { + Y = 10, + Anchor = Anchor.BottomCentre, + Origin = Anchor.TopCentre, + }, + new TeamScoreDisplay(TeamColour.Red) + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + }, + new TeamScoreDisplay(TeamColour.Blue) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + } + }, + }); + } + private class TeamScoreDisplay : CompositeDrawable + { + private readonly TeamColour teamColour; + + private readonly Color4 red = new Color4(129, 68, 65, 255); + private readonly Color4 blue = new Color4(41, 91, 97, 255); + + private readonly Bindable currentMatch = new Bindable(); + private readonly Bindable currentTeam = new Bindable(); + private readonly Bindable currentTeamScore = new Bindable(); + + public TeamScoreDisplay(TeamColour teamColour) + { + this.teamColour = teamColour; + + RelativeSizeAxes = Axes.Y; + Width = 300; + } + + [BackgroundDependencyLoader] + private void load(LadderInfo ladder) + { + currentMatch.BindValueChanged(matchChanged); + currentMatch.BindTo(ladder.CurrentMatch); + } + + private void matchChanged(MatchPairing match) + { + currentTeamScore.UnbindBindings(); + currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.Team1Score : match.Team2Score); + + currentTeam.UnbindBindings(); + currentTeam.BindTo(teamColour == TeamColour.Red ? match.Team1 : match.Team2); + + // team may change to same team, which means score is not in a good state. + // thus we handle this manually. + teamChanged(currentTeam.Value); + } + + private void teamChanged(TournamentTeam team) + { + InternalChildren = new Drawable[] + { + new TeamDisplay(team, teamColour == TeamColour.Red ? red : blue, teamColour != TeamColour.Red), + new ScoreDisplay(currentTeamScore, teamColour != TeamColour.Red, currentMatch.Value.Grouping.Value.BestOf / 2 + 1) + }; + } + } + + private class ScoreDisplay : CompositeDrawable + { + private readonly Bindable currentTeamScore = new Bindable(); + private readonly StarCounter counter; + + public ScoreDisplay(Bindable score, bool flip, int count) + { + var anchor = flip ? Anchor.CentreRight : Anchor.CentreLeft; + + Anchor = anchor; + Origin = anchor; + + InternalChild = counter = new StarCounter(count) + { + Anchor = anchor, + X = (flip ? -1 : 1) * 90, + Y = 5, + Scale = flip ? new Vector2(-1, 1) : Vector2.One, + Colour = new Color4(95, 41, 60, 255), + }; + + currentTeamScore.BindValueChanged(scoreChanged); + currentTeamScore.BindTo(score); + } + + private void scoreChanged(int? score) => counter.CountStars = score ?? 0; + } + + private class TeamDisplay : DrawableTournamentTeam + { + public TeamDisplay(TournamentTeam team, Color4 colour, bool flip) + : base(team) + { + RelativeSizeAxes = Axes.Both; + + var anchor = flip ? Anchor.CentreRight : Anchor.CentreLeft; + + Anchor = Origin = anchor; + + Flag.Anchor = Flag.Origin = anchor; + Flag.RelativeSizeAxes = Axes.None; + Flag.Size = new Vector2(60, 40); + Flag.Margin = new MarginPadding(20); + + InternalChild = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + Flag, + new OsuSpriteText + { + Text = team?.FullName.ToUpper() ?? "???", + X = (flip ? -1 : 1) * 90, + Y = -10, + TextSize = 20, + Colour = colour, + Font = "Aquatico-Regular", + Origin = anchor, + Anchor = anchor, + }, + } + }; + } + } + + private class RoundDisplay : CompositeDrawable + { + private readonly Bindable currentMatch = new Bindable(); + + public RoundDisplay() + { + CornerRadius = 10; + Masking = true; + Width = 200; + Height = 20; + } + + [BackgroundDependencyLoader] + private void load(LadderInfo ladder) + { + currentMatch.BindValueChanged(matchChanged); + currentMatch.BindTo(ladder.CurrentMatch); + } + + private void matchChanged(MatchPairing match) + { + InternalChildren = new Drawable[] + { + new Box + { + Colour = new Color4(95, 41, 60, 255), + RelativeSizeAxes = Axes.Both, + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = Color4.White, + Text = match.Grouping.Value?.Name.Value ?? "Unknown Grouping", + Font = "Aquatico-Regular", + TextSize = 18, + }, + }; + } + }; } } diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 562b141095..df93c42d98 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -10,6 +10,7 @@ using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Game.Beatmaps; @@ -49,6 +50,8 @@ namespace osu.Game.Tournament Fonts.AddStore(new GlyphStore(Resources, @"Resources/Fonts/Aquatico-Regular")); Fonts.AddStore(new GlyphStore(Resources, @"Resources/Fonts/Aquatico-Light")); + Textures.AddStore(new TextureLoaderStore(new ResourceStore(new StorageBackedResourceStore(storage)))); + this.storage = storage; windowSize = frameworkConfig.GetBindable(FrameworkSetting.WindowedSize); @@ -125,7 +128,6 @@ namespace osu.Game.Tournament addedInfo = true; } - List countries; using (Stream stream = Resources.GetStream("Resources/countries.json")) using (var sr = new StreamReader(stream)) From 0be2f5ac94722fd03eb6e2e63298b1000f03b74e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Nov 2018 16:10:58 +0900 Subject: [PATCH 0241/2854] Add non-working warmup toggle button --- .../Screens/Gameplay/GameplayScreen.cs | 79 ++++++++++++------- 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 9f46776a6d..107b8f2736 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Logging; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; @@ -20,45 +19,67 @@ namespace osu.Game.Tournament.Screens.Gameplay { public class GameplayScreen : BeatmapInfoScreen { + private BindableBool warmup = new BindableBool(); + [BackgroundDependencyLoader] private void load(LadderInfo ladder, TextureStore textures) { - Add(new Container + AddRange(new Drawable[] { - RelativeSizeAxes = Axes.X, - Height = 100, - Children = new Drawable[] + new Container { - new Sprite + RelativeSizeAxes = Axes.X, + Height = 100, + Children = new Drawable[] { - Y = 5, - Texture = textures.Get("game-screen-logo"), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - FillMode = FillMode.Fit, - RelativeSizeAxes = Axes.Both, - Size = Vector2.One + new Sprite + { + Y = 5, + Texture = textures.Get("game-screen-logo"), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + FillMode = FillMode.Fit, + RelativeSizeAxes = Axes.Both, + Size = Vector2.One + }, + new RoundDisplay + { + Y = 10, + Anchor = Anchor.BottomCentre, + Origin = Anchor.TopCentre, + }, + new TeamScoreDisplay(TeamColour.Red) + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + }, + new TeamScoreDisplay(TeamColour.Blue) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }, }, - new RoundDisplay - { - Y = 10, - Anchor = Anchor.BottomCentre, - Origin = Anchor.TopCentre, - }, - new TeamScoreDisplay(TeamColour.Red) - { - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, - }, - new TeamScoreDisplay(TeamColour.Blue) - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - } }, + new ControlPanel + { + Children = new Drawable[] + { + new TriangleButton + { + RelativeSizeAxes = Axes.X, + Text = "Toggle warmup", + Action = toggleWarmup + } + } + } }); } + private void toggleWarmup() + { + warmup.Toggle(); + } + private class TeamScoreDisplay : CompositeDrawable { private readonly TeamColour teamColour; From 1c6c59864486c863649b11126ce83ea0fa5c6bdc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Nov 2018 16:11:12 +0900 Subject: [PATCH 0242/2854] Fill out username from user ids, rather than user ids from user ids --- osu.Game.Tournament/TournamentGameBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index df93c42d98..416be35456 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -107,7 +107,7 @@ namespace osu.Game.Tournament // add full player info based on user IDs foreach (var t in Ladder.Teams) foreach (var p in t.Players) - if (p.Id == 1) + if (string.IsNullOrEmpty(p.Username)) { var req = new GetUserRequest(p.Id); req.Success += i => p.Username = i.Username; From 66dc7d6b0285b90184eab24c3bd2eb4ea7ed4901 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Nov 2018 16:26:09 +0900 Subject: [PATCH 0243/2854] Fix debug tools not working --- osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs | 2 +- osu.Game.Tournament/TournamentGameBase.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 107b8f2736..2d3ed89b97 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tournament.Screens.Gameplay { public class GameplayScreen : BeatmapInfoScreen { - private BindableBool warmup = new BindableBool(); + private readonly BindableBool warmup = new BindableBool(); [BackgroundDependencyLoader] private void load(LadderInfo ladder, TextureStore textures) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 416be35456..28136a6394 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -163,6 +163,7 @@ namespace osu.Game.Tournament protected override void LoadComplete() { MenuCursorContainer.Cursor.Alpha = 0; + base.LoadComplete(); } protected override void Update() From 8a917e4cc7f137cf883b316e16c389507969c7c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Nov 2018 16:57:45 +0900 Subject: [PATCH 0244/2854] Fix context menus (temporarily) --- osu.Game.Tournament/Screens/Ladder/LadderManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 805358e231..9e43cffc72 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -255,6 +255,9 @@ namespace osu.Game.Tournament.Screens.Ladder } } + // todo: remove after ppy/osu-framework#1980 is merged. + public override bool HandlePositionalInput => true; + public void Remove(MatchPairing pairing) => pairingsContainer.FirstOrDefault(p => p.Pairing == pairing)?.Remove(); public void SetCurrent(MatchPairing pairing) From 9064f3fe0f0477affaba7bc378ada4116c6b6dd1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Nov 2018 17:39:46 +0900 Subject: [PATCH 0245/2854] Fix ladder manager not saving changes out correctly --- .../TestCaseLadderManager.cs | 16 +--- .../Ladder/Components/LadderEditorInfo.cs | 4 - .../Ladder/Components/LadderEditorSettings.cs | 7 +- .../Screens/Ladder/LadderManager.cs | 84 +++++++++---------- 4 files changed, 46 insertions(+), 65 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index 3ddd52eeac..63c8f5e391 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -10,28 +10,14 @@ namespace osu.Game.Tournament.Tests { public class TestCaseLadderManager : LadderTestCase { - private LadderManager manager; - [BackgroundDependencyLoader] private void load() { Add(new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, - Child = manager = new LadderManager() + Child = new LadderManager() }); } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - var newInfo = manager.CreateInfo(); - - Ladder.Teams = newInfo.Teams; - Ladder.Groupings = newInfo.Groupings; - Ladder.Pairings = newInfo.Pairings; - Ladder.Progressions = newInfo.Progressions; - } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs index cc91c98188..0ecf387f7c 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs @@ -1,17 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using osu.Framework.Configuration; -using osu.Game.Tournament.Components; namespace osu.Game.Tournament.Screens.Ladder.Components { public class LadderEditorInfo { public readonly BindableBool EditingEnabled = new BindableBool(); - public List Teams = new List(); - public List Groupings = new List(); public readonly Bindable Selected = new Bindable(); } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index d7b827237a..3fa7fdf7e8 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -28,12 +28,15 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [Resolved] private LadderEditorInfo editorInfo { get; set; } + [Resolved] + private LadderInfo ladderInfo { get; set; } + [BackgroundDependencyLoader] private void load() { - var teamEntries = editorInfo.Teams; + var teamEntries = ladderInfo.Teams; - var groupingOptions = editorInfo.Groupings.Select(g => new KeyValuePair(g.Name, g)) + var groupingOptions = ladderInfo.Groupings.Select(g => new KeyValuePair(g.Name, g)) .Prepend(new KeyValuePair("None", new TournamentGrouping())); Children = new Drawable[] diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 9e43cffc72..5ef7d047e2 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -1,7 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; +using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Caching; @@ -15,7 +15,6 @@ using osu.Framework.Input.Events; using osu.Framework.Input.States; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; -using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; using OpenTK; using OpenTK.Graphics; @@ -26,28 +25,24 @@ namespace osu.Game.Tournament.Screens.Ladder [Cached] public class LadderManager : CompositeDrawable, IHasContextMenu { - public List Teams; private Container pairingsContainer; private Container paths; private Container headings; - private LadderInfo info; - private ScrollableContainer scrollContent; [Cached] private LadderEditorInfo editorInfo = new LadderEditorInfo(); + [Resolved] + private LadderInfo ladderInfo { get; set; } + [BackgroundDependencyLoader] - private void load(LadderInfo info, OsuColour colours) + private void load(OsuColour colours) { normalPathColour = colours.BlueDarker.Darken(2); losersPathColour = colours.YellowDarker.Darken(2); - this.info = info; - editorInfo.Teams = Teams = info.Teams; - editorInfo.Groupings = info.Groupings; - RelativeSizeAxes = Axes.Both; InternalChild = new Container @@ -74,32 +69,29 @@ namespace osu.Game.Tournament.Screens.Ladder } }; - foreach (var pairing in info.Pairings) + foreach (var pairing in ladderInfo.Pairings) addPairing(pairing); // todo: fix this Scheduler.AddDelayed(() => layout.Invalidate(), 100, true); } - public LadderInfo CreateInfo() + private void updateInfo() { - var pairings = pairingsContainer.Select(p => p.Pairing).ToList(); + ladderInfo.Pairings = pairingsContainer.Select(p => p.Pairing).ToList(); + foreach (var g in ladderInfo.Groupings) + g.Pairings = ladderInfo.Pairings.Where(p => p.Grouping.Value == g).Select(p => p.ID).ToList(); - foreach (var g in editorInfo.Groupings) - g.Pairings = pairings.Where(p => p.Grouping.Value == g).Select(p => p.ID).ToList(); - - return new LadderInfo - { - Pairings = pairings, - Progressions = pairings.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat( - pairings.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) - .ToList(), - Teams = editorInfo.Teams, - Groupings = editorInfo.Groupings - }; + ladderInfo.Progressions = ladderInfo.Pairings.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat( + ladderInfo.Pairings.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) + .ToList(); } - private void addPairing(MatchPairing pairing) => pairingsContainer.Add(new DrawableMatchPairing(pairing)); + private void addPairing(MatchPairing pairing) + { + pairingsContainer.Add(new DrawableMatchPairing(pairing)); + updateInfo(); + } public MenuItem[] ContextMenuItems { @@ -154,7 +146,7 @@ namespace osu.Game.Tournament.Screens.Ladder } } - foreach (var group in editorInfo.Groupings) + foreach (var group in ladderInfo.Groupings) { var topPairing = pairingsContainer.Where(p => !p.Pairing.Losers && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); @@ -168,7 +160,7 @@ namespace osu.Game.Tournament.Screens.Ladder }); } - foreach (var group in editorInfo.Groupings) + foreach (var group in ladderInfo.Groupings) { var topPairing = pairingsContainer.Where(p => p.Pairing.Losers && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); @@ -183,25 +175,42 @@ namespace osu.Game.Tournament.Screens.Ladder } layout.Validate(); + updateInfo(); } - public void RequestJoin(MatchPairing pairing, bool losers) => scrollContent.Add(new JoinRequestHandler(pairingsContainer, pairing, losers)); + public void RequestJoin(MatchPairing pairing, bool losers) => scrollContent.Add(new JoinRequestHandler(pairingsContainer, pairing, losers, updateInfo)); + + // todo: remove after ppy/osu-framework#1980 is merged. + public override bool HandlePositionalInput => true; + + public void Remove(MatchPairing pairing) => pairingsContainer.FirstOrDefault(p => p.Pairing == pairing)?.Remove(); + + public void SetCurrent(MatchPairing pairing) + { + if (ladderInfo.CurrentMatch.Value != null) + ladderInfo.CurrentMatch.Value.Current.Value = false; + + ladderInfo.CurrentMatch.Value = pairing; + ladderInfo.CurrentMatch.Value.Current.Value = true; + } private class JoinRequestHandler : CompositeDrawable { private readonly Container pairingsContainer; public readonly MatchPairing Source; private readonly bool losers; + private readonly Action complete; private ProgressionPath path; - public JoinRequestHandler(Container pairingsContainer, MatchPairing source, bool losers) + public JoinRequestHandler(Container pairingsContainer, MatchPairing source, bool losers, Action complete) { this.pairingsContainer = pairingsContainer; RelativeSizeAxes = Axes.Both; Source = source; this.losers = losers; + this.complete = complete; if (losers) Source.LosersProgression.Value = null; else @@ -247,6 +256,7 @@ namespace osu.Game.Tournament.Screens.Ladder Source.Progression.Value = found.Pairing; } + complete?.Invoke(); Expire(); return true; } @@ -254,19 +264,5 @@ namespace osu.Game.Tournament.Screens.Ladder return false; } } - - // todo: remove after ppy/osu-framework#1980 is merged. - public override bool HandlePositionalInput => true; - - public void Remove(MatchPairing pairing) => pairingsContainer.FirstOrDefault(p => p.Pairing == pairing)?.Remove(); - - public void SetCurrent(MatchPairing pairing) - { - if (info.CurrentMatch.Value != null) - info.CurrentMatch.Value.Current.Value = false; - - info.CurrentMatch.Value = pairing; - info.CurrentMatch.Value.Current.Value = true; - } } } From e170372932a14153ab2b41973af592038982d638 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Nov 2018 17:54:05 +0900 Subject: [PATCH 0246/2854] Populate json with enums better --- .../Screens/Ladder/Components/BeatmapChoice.cs | 9 +++++++++ osu.Game.Tournament/TournamentGameBase.cs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/BeatmapChoice.cs b/osu.Game.Tournament/Screens/Ladder/Components/BeatmapChoice.cs index d0b2556603..5b38e539c6 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/BeatmapChoice.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/BeatmapChoice.cs @@ -1,21 +1,30 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + namespace osu.Game.Tournament.Screens.Ladder.Components { public class BeatmapChoice { + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] public TeamColour Team; + + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] public ChoiceType Type; + public int BeatmapID; } + [JsonConverter(typeof(StringEnumConverter))] public enum TeamColour { Red, Blue } + [JsonConverter(typeof(StringEnumConverter))] public enum ChoiceType { Pick, diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 28136a6394..70868d398e 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -186,7 +186,7 @@ namespace osu.Game.Tournament new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, - DefaultValueHandling = DefaultValueHandling.Ignore + DefaultValueHandling = DefaultValueHandling.Ignore, })); } } From 1756ef95cbb520557b90991a4ee93684dd53232a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 10 Nov 2018 07:31:06 +0900 Subject: [PATCH 0247/2854] Add ability to adjust scores from gameplay screen --- .../Screens/Gameplay/GameplayScreen.cs | 22 ++++++++++++++++++- .../Screens/Ladder/Components/MatchPairing.cs | 2 ++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 2d3ed89b97..5593824e55 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -8,12 +8,14 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Events; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; using OpenTK; using OpenTK.Graphics; +using OpenTK.Input; namespace osu.Game.Tournament.Screens.Gameplay { @@ -119,12 +121,30 @@ namespace osu.Game.Tournament.Screens.Gameplay teamChanged(currentTeam.Value); } + + protected override bool OnMouseDown(MouseDownEvent e) + { + switch (e.Button) + { + case MouseButton.Left: + if (currentTeamScore.Value < currentMatch.Value.PointsToWin) + currentTeamScore.Value++; + return true; + case MouseButton.Right: + if (currentTeamScore.Value > 0) + currentTeamScore.Value--; + return true; + } + + return base.OnMouseDown(e); + } + private void teamChanged(TournamentTeam team) { InternalChildren = new Drawable[] { new TeamDisplay(team, teamColour == TeamColour.Red ? red : blue, teamColour != TeamColour.Red), - new ScoreDisplay(currentTeamScore, teamColour != TeamColour.Red, currentMatch.Value.Grouping.Value.BestOf / 2 + 1) + new ScoreDisplay(currentTeamScore, teamColour != TeamColour.Red, currentMatch.Value.PointsToWin) }; } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index 63c96d350d..729249c3d7 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -74,6 +74,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [JsonIgnore] public TournamentTeam Loser => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team2.Value : Team1.Value; + public int PointsToWin => Grouping.Value.BestOf / 2 + 1; + /// /// Remove scores from the match, in case of a false click or false start. /// From 82d53e6f17e7675c5a54a4371a0d338d27b586d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 10 Nov 2018 08:37:21 +0900 Subject: [PATCH 0248/2854] Fix many unnecessary requests being fired --- osu.Game.Tournament/IPC/FileBasedIPC.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index c42ccc11c2..a6e171b699 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -27,6 +27,8 @@ namespace osu.Game.Tournament.IPC public readonly Bindable Mods = new Bindable(); + private int lastBeatmapId; + [BackgroundDependencyLoader] private void load() { @@ -45,8 +47,9 @@ namespace osu.Game.Tournament.IPC var beatmapId = int.Parse(sr.ReadLine()); var mods = int.Parse(sr.ReadLine()); - if (Beatmap.Value?.OnlineBeatmapID != beatmapId) + if (lastBeatmapId != beatmapId) { + lastBeatmapId = beatmapId; var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); req.Success += b => Beatmap.Value = b.ToBeatmap(Rulesets); API.Queue(req); From 86aab9c31b480e656c06305639c30ed73a7c59ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 10 Nov 2018 08:54:56 +0900 Subject: [PATCH 0249/2854] Fix chat making needless requests --- osu.Game/Overlays/ChatOverlay.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index ff2ff9af14..65d5371130 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . +// Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; @@ -393,10 +393,10 @@ namespace osu.Game.Overlays api.Queue(req); return; } - } - // let's fetch a small number of messages to bring us up-to-date with the backlog. - fetchInitialMessages(channel); + // let's fetch a small number of messages to bring us up-to-date with the backlog. + fetchInitialMessages(channel); + } if (CurrentChannel == null) CurrentChannel = channel; From 713038bff88c9a24e6c5d13cab6a4d87c2516568 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 10 Nov 2018 17:26:21 +0900 Subject: [PATCH 0250/2854] Share header area between screens --- .../Screens/Gameplay/GameplayScreen.cs | 224 +---------------- .../Screens/Gameplay/MatchHeader.cs | 234 ++++++++++++++++++ .../Screens/MapPool/MapPoolScreen.cs | 5 +- 3 files changed, 245 insertions(+), 218 deletions(-) create mode 100644 osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 5593824e55..8b87d6d581 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -4,18 +4,9 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Input.Events; -using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; -using osu.Game.Tournament.Screens.Ladder.Components; -using OpenTK; -using OpenTK.Graphics; -using OpenTK.Input; namespace osu.Game.Tournament.Screens.Gameplay { @@ -28,40 +19,13 @@ namespace osu.Game.Tournament.Screens.Gameplay { AddRange(new Drawable[] { - new Container - { - RelativeSizeAxes = Axes.X, - Height = 100, - Children = new Drawable[] - { - new Sprite - { - Y = 5, - Texture = textures.Get("game-screen-logo"), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - FillMode = FillMode.Fit, - RelativeSizeAxes = Axes.Both, - Size = Vector2.One - }, - new RoundDisplay - { - Y = 10, - Anchor = Anchor.BottomCentre, - Origin = Anchor.TopCentre, - }, - new TeamScoreDisplay(TeamColour.Red) - { - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, - }, - new TeamScoreDisplay(TeamColour.Blue) - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - }, - }, - }, + new MatchHeader(), + // new CustomChatOverlay + // { + // Anchor = Anchor.BottomCentre, + // Origin = Anchor.BottomCentre, + // Size = new Vector2(0.4f, 1) + // }, new ControlPanel { Children = new Drawable[] @@ -81,179 +45,5 @@ namespace osu.Game.Tournament.Screens.Gameplay { warmup.Toggle(); } - - private class TeamScoreDisplay : CompositeDrawable - { - private readonly TeamColour teamColour; - - private readonly Color4 red = new Color4(129, 68, 65, 255); - private readonly Color4 blue = new Color4(41, 91, 97, 255); - - private readonly Bindable currentMatch = new Bindable(); - private readonly Bindable currentTeam = new Bindable(); - private readonly Bindable currentTeamScore = new Bindable(); - - public TeamScoreDisplay(TeamColour teamColour) - { - this.teamColour = teamColour; - - RelativeSizeAxes = Axes.Y; - Width = 300; - } - - [BackgroundDependencyLoader] - private void load(LadderInfo ladder) - { - currentMatch.BindValueChanged(matchChanged); - currentMatch.BindTo(ladder.CurrentMatch); - } - - private void matchChanged(MatchPairing match) - { - currentTeamScore.UnbindBindings(); - currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.Team1Score : match.Team2Score); - - currentTeam.UnbindBindings(); - currentTeam.BindTo(teamColour == TeamColour.Red ? match.Team1 : match.Team2); - - // team may change to same team, which means score is not in a good state. - // thus we handle this manually. - teamChanged(currentTeam.Value); - } - - - protected override bool OnMouseDown(MouseDownEvent e) - { - switch (e.Button) - { - case MouseButton.Left: - if (currentTeamScore.Value < currentMatch.Value.PointsToWin) - currentTeamScore.Value++; - return true; - case MouseButton.Right: - if (currentTeamScore.Value > 0) - currentTeamScore.Value--; - return true; - } - - return base.OnMouseDown(e); - } - - private void teamChanged(TournamentTeam team) - { - InternalChildren = new Drawable[] - { - new TeamDisplay(team, teamColour == TeamColour.Red ? red : blue, teamColour != TeamColour.Red), - new ScoreDisplay(currentTeamScore, teamColour != TeamColour.Red, currentMatch.Value.PointsToWin) - }; - } - } - - private class ScoreDisplay : CompositeDrawable - { - private readonly Bindable currentTeamScore = new Bindable(); - private readonly StarCounter counter; - - public ScoreDisplay(Bindable score, bool flip, int count) - { - var anchor = flip ? Anchor.CentreRight : Anchor.CentreLeft; - - Anchor = anchor; - Origin = anchor; - - InternalChild = counter = new StarCounter(count) - { - Anchor = anchor, - X = (flip ? -1 : 1) * 90, - Y = 5, - Scale = flip ? new Vector2(-1, 1) : Vector2.One, - Colour = new Color4(95, 41, 60, 255), - }; - - currentTeamScore.BindValueChanged(scoreChanged); - currentTeamScore.BindTo(score); - } - - private void scoreChanged(int? score) => counter.CountStars = score ?? 0; - } - - private class TeamDisplay : DrawableTournamentTeam - { - public TeamDisplay(TournamentTeam team, Color4 colour, bool flip) - : base(team) - { - RelativeSizeAxes = Axes.Both; - - var anchor = flip ? Anchor.CentreRight : Anchor.CentreLeft; - - Anchor = Origin = anchor; - - Flag.Anchor = Flag.Origin = anchor; - Flag.RelativeSizeAxes = Axes.None; - Flag.Size = new Vector2(60, 40); - Flag.Margin = new MarginPadding(20); - - InternalChild = new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - Flag, - new OsuSpriteText - { - Text = team?.FullName.ToUpper() ?? "???", - X = (flip ? -1 : 1) * 90, - Y = -10, - TextSize = 20, - Colour = colour, - Font = "Aquatico-Regular", - Origin = anchor, - Anchor = anchor, - }, - } - }; - } - } - - private class RoundDisplay : CompositeDrawable - { - private readonly Bindable currentMatch = new Bindable(); - - public RoundDisplay() - { - CornerRadius = 10; - Masking = true; - Width = 200; - Height = 20; - } - - [BackgroundDependencyLoader] - private void load(LadderInfo ladder) - { - currentMatch.BindValueChanged(matchChanged); - currentMatch.BindTo(ladder.CurrentMatch); - } - - private void matchChanged(MatchPairing match) - { - InternalChildren = new Drawable[] - { - new Box - { - Colour = new Color4(95, 41, 60, 255), - RelativeSizeAxes = Axes.Both, - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Colour = Color4.White, - Text = match.Grouping.Value?.Name.Value ?? "Unknown Grouping", - Font = "Aquatico-Regular", - TextSize = 18, - }, - }; - } - }; } } diff --git a/osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs new file mode 100644 index 0000000000..2ff5ee7bf6 --- /dev/null +++ b/osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs @@ -0,0 +1,234 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Tournament.Components; +using osu.Game.Tournament.Screens.Ladder.Components; +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Input; + +namespace osu.Game.Tournament.Screens.Gameplay +{ + public class MatchHeader : Container + { + [BackgroundDependencyLoader] + private void load(LadderInfo ladder, TextureStore textures) + { + RelativeSizeAxes = Axes.X; + Height = 100; + Children = new Drawable[] + { + new Sprite + { + Y = 5, + Texture = textures.Get("game-screen-logo"), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + FillMode = FillMode.Fit, + RelativeSizeAxes = Axes.Both, + Size = Vector2.One + }, + new RoundDisplay + { + Y = 10, + Anchor = Anchor.BottomCentre, + Origin = Anchor.TopCentre, + }, + new TeamScoreDisplay(TeamColour.Red) + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + }, + new TeamScoreDisplay(TeamColour.Blue) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }, + }; + } + + private class TeamScoreDisplay : CompositeDrawable + { + private readonly TeamColour teamColour; + + private readonly Color4 red = new Color4(129, 68, 65, 255); + private readonly Color4 blue = new Color4(41, 91, 97, 255); + + private readonly Bindable currentMatch = new Bindable(); + private readonly Bindable currentTeam = new Bindable(); + private readonly Bindable currentTeamScore = new Bindable(); + + public TeamScoreDisplay(TeamColour teamColour) + { + this.teamColour = teamColour; + + RelativeSizeAxes = Axes.Y; + Width = 300; + } + + [BackgroundDependencyLoader] + private void load(LadderInfo ladder) + { + currentMatch.BindValueChanged(matchChanged); + currentMatch.BindTo(ladder.CurrentMatch); + } + + private void matchChanged(MatchPairing match) + { + currentTeamScore.UnbindBindings(); + currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.Team1Score : match.Team2Score); + + currentTeam.UnbindBindings(); + currentTeam.BindTo(teamColour == TeamColour.Red ? match.Team1 : match.Team2); + + // team may change to same team, which means score is not in a good state. + // thus we handle this manually. + teamChanged(currentTeam.Value); + } + + + protected override bool OnMouseDown(MouseDownEvent e) + { + switch (e.Button) + { + case MouseButton.Left: + if (currentTeamScore.Value < currentMatch.Value.PointsToWin) + currentTeamScore.Value++; + return true; + case MouseButton.Right: + if (currentTeamScore.Value > 0) + currentTeamScore.Value--; + return true; + } + + return base.OnMouseDown(e); + } + + private void teamChanged(TournamentTeam team) + { + InternalChildren = new Drawable[] + { + new TeamDisplay(team, teamColour == TeamColour.Red ? red : blue, teamColour != TeamColour.Red), + new ScoreDisplay(currentTeamScore, teamColour != TeamColour.Red, currentMatch.Value.PointsToWin) + }; + } + } + + private class ScoreDisplay : CompositeDrawable + { + private readonly Bindable currentTeamScore = new Bindable(); + private readonly StarCounter counter; + + public ScoreDisplay(Bindable score, bool flip, int count) + { + var anchor = flip ? Anchor.CentreRight : Anchor.CentreLeft; + + Anchor = anchor; + Origin = anchor; + + InternalChild = counter = new StarCounter(count) + { + Anchor = anchor, + X = (flip ? -1 : 1) * 90, + Y = 5, + Scale = flip ? new Vector2(-1, 1) : Vector2.One, + Colour = new Color4(95, 41, 60, 255), + }; + + currentTeamScore.BindValueChanged(scoreChanged); + currentTeamScore.BindTo(score); + } + + private void scoreChanged(int? score) => counter.CountStars = score ?? 0; + } + + private class TeamDisplay : DrawableTournamentTeam + { + public TeamDisplay(TournamentTeam team, Color4 colour, bool flip) + : base(team) + { + RelativeSizeAxes = Axes.Both; + + var anchor = flip ? Anchor.CentreRight : Anchor.CentreLeft; + + Anchor = Origin = anchor; + + Flag.Anchor = Flag.Origin = anchor; + Flag.RelativeSizeAxes = Axes.None; + Flag.Size = new Vector2(60, 40); + Flag.Margin = new MarginPadding(20); + + InternalChild = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + Flag, + new OsuSpriteText + { + Text = team?.FullName.ToUpper() ?? "???", + X = (flip ? -1 : 1) * 90, + Y = -10, + TextSize = 20, + Colour = colour, + Font = "Aquatico-Regular", + Origin = anchor, + Anchor = anchor, + }, + } + }; + } + } + + private class RoundDisplay : CompositeDrawable + { + private readonly Bindable currentMatch = new Bindable(); + + public RoundDisplay() + { + CornerRadius = 10; + Masking = true; + Width = 200; + Height = 20; + } + + [BackgroundDependencyLoader] + private void load(LadderInfo ladder) + { + currentMatch.BindValueChanged(matchChanged); + currentMatch.BindTo(ladder.CurrentMatch); + } + + private void matchChanged(MatchPairing match) + { + InternalChildren = new Drawable[] + { + new Box + { + Colour = new Color4(95, 41, 60, 255), + RelativeSizeAxes = Axes.Both, + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = Color4.White, + Text = match.Grouping.Value?.Name.Value ?? "Unknown Grouping", + Font = "Aquatico-Regular", + TextSize = 18, + }, + }; + } + } + } +} diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index e8d6f18fd7..84c9e250c3 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Screens; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; +using osu.Game.Tournament.Screens.Gameplay; using osu.Game.Tournament.Screens.Ladder.Components; using OpenTK; using OpenTK.Graphics; @@ -38,9 +39,11 @@ namespace osu.Game.Tournament.Screens.MapPool { InternalChildren = new Drawable[] { + new MatchHeader(), maps = new FillFlowContainer { - Spacing = new Vector2(20), + Y = 100, + Spacing = new Vector2(10), Padding = new MarginPadding(50), Direction = FillDirection.Full, RelativeSizeAxes = Axes.Both, From 629657044d4234db4781de304e8d2954d6327f17 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Nov 2018 00:45:48 +0900 Subject: [PATCH 0251/2854] Add automatic scoring --- osu.Game.Tournament/IPC/FileBasedIPC.cs | 86 ++++++++++++++++--- .../Screens/Gameplay/GameplayScreen.cs | 46 +++++++++- 2 files changed, 116 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index a6e171b699..168ff80eb7 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -6,6 +6,7 @@ using System.IO; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Logging; using osu.Framework.Platform.Windows; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; @@ -15,6 +16,15 @@ using osu.Game.Rulesets; namespace osu.Game.Tournament.IPC { + public enum TourneyState + { + Initialising, + Idle, + WaitingForClients, + Playing, + Ranking + } + public class FileBasedIPC : Component { [Resolved] @@ -27,7 +37,11 @@ namespace osu.Game.Tournament.IPC public readonly Bindable Mods = new Bindable(); + public readonly Bindable State = new Bindable(); + private int lastBeatmapId; + public int Score1; + public int Score2; [BackgroundDependencyLoader] private void load() @@ -35,6 +49,8 @@ namespace osu.Game.Tournament.IPC var stable = new StableStorage(); const string file_ipc_filename = "ipc.txt"; + const string file_ipc_state_filename = "ipc-state.txt"; + const string file_ipc_scores_filename = "ipc-scores.txt"; if (stable.Exists(file_ipc_filename)) Scheduler.AddDelayed(delegate @@ -62,6 +78,35 @@ namespace osu.Game.Tournament.IPC { // file might be in use. } + + try + { + using (var stream = stable.GetStream(file_ipc_state_filename)) + using (var sr = new StreamReader(stream)) + { + State.Value = (TourneyState)Enum.Parse(typeof(TourneyState), sr.ReadLine()); + } + } + catch (Exception e) + { + Logger.Log(e.ToString(), LoggingTarget.Runtime); + // file might be in use. + } + + try + { + using (var stream = stable.GetStream(file_ipc_scores_filename)) + using (var sr = new StreamReader(stream)) + { + Score1 = int.Parse(sr.ReadLine()); + Score2 = int.Parse(sr.ReadLine()); + } + } + catch (Exception e) + { + Logger.Log(e.ToString(), LoggingTarget.Runtime); + // file might be in use. + } }, 250, true); } @@ -72,33 +117,46 @@ namespace osu.Game.Tournament.IPC { protected override string LocateBasePath() { + bool checkExists(string p) { - return Directory.Exists(Path.Combine(p, "Songs")); + return File.Exists(Path.Combine(p, "ipc.txt")); } - string stableInstallPath; + string stableInstallPath = string.Empty; try { - stableInstallPath = "E:\\osu!mappool"; + try + { + stableInstallPath = "E:\\osu!tourney"; + if (checkExists(stableInstallPath)) + return stableInstallPath; + + stableInstallPath = "E:\\osu!mappool"; + + if (checkExists(stableInstallPath)) + return stableInstallPath; + } + catch + { + } + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); if (checkExists(stableInstallPath)) return stableInstallPath; + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + return null; } - catch + finally { + Logger.Log($"Stable path for tourney usage: {stableInstallPath}"); } - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - return null; } public StableStorage() diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 8b87d6d581..31c5e9f426 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -4,9 +4,13 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; +using osu.Game.Tournament.IPC; +using osu.Game.Tournament.Screens.Ladder.Components; +using OpenTK.Graphics; namespace osu.Game.Tournament.Screens.Gameplay { @@ -14,9 +18,16 @@ namespace osu.Game.Tournament.Screens.Gameplay { private readonly BindableBool warmup = new BindableBool(); + private readonly Bindable currentMatch = new Bindable(); + + public readonly Bindable State = new Bindable(); + private TriangleButton warmupButton; + private FileBasedIPC ipc; + [BackgroundDependencyLoader] - private void load(LadderInfo ladder, TextureStore textures) + private void load(LadderInfo ladder, TextureStore textures, FileBasedIPC ipc) { + this.ipc = ipc; AddRange(new Drawable[] { new MatchHeader(), @@ -26,12 +37,21 @@ namespace osu.Game.Tournament.Screens.Gameplay // Origin = Anchor.BottomCentre, // Size = new Vector2(0.4f, 1) // }, + new Box + { + RelativeSizeAxes = Axes.Both, + Height = 720 / 1080f, + Colour = new Color4(0, 255, 0, 255), + Anchor = Anchor.Centre, + Origin= Anchor.Centre, + }, new ControlPanel { Children = new Drawable[] { - new TriangleButton + warmupButton = new TriangleButton { + Colour = Color4.Gray, RelativeSizeAxes = Axes.X, Text = "Toggle warmup", Action = toggleWarmup @@ -39,11 +59,33 @@ namespace osu.Game.Tournament.Screens.Gameplay } } }); + + State.BindValueChanged(stateChanged); + State.BindTo(ipc.State); + + currentMatch.BindTo(ladder.CurrentMatch); + } + + private void stateChanged(TourneyState state) + { + if (state == TourneyState.Ranking) + { + if (warmup.Value) return; + + if (ipc.Score1 > ipc.Score2) + currentMatch.Value.Team1Score.Value++; + else + currentMatch.Value.Team2Score.Value++; + } } private void toggleWarmup() { warmup.Toggle(); + if (warmup.Value) + warmupButton.Colour = Color4.White; + else + warmupButton.Colour = Color4.Gray; } } } From 976180ecc2e5ee6fcba20c01a543130fdd08e8b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Nov 2018 00:48:22 +0900 Subject: [PATCH 0252/2854] Fix incorrect order after bans --- osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 84c9e250c3..5a04640ed7 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -103,6 +103,9 @@ namespace osu.Game.Tournament.Screens.MapPool private void beatmapChanged(BeatmapInfo beatmap) { + if (currentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) < 2) + return; + if (beatmap.OnlineBeatmapID != null) addForBeatmap(beatmap.OnlineBeatmapID.Value); } @@ -126,7 +129,10 @@ namespace osu.Game.Tournament.Screens.MapPool var nextColour = (currentMatch.Value.PicksBans.LastOrDefault()?.Team ?? roll_winner) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; - setMode(nextColour, currentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= 2 ? ChoiceType.Pick : ChoiceType.Ban); + if (pickType == ChoiceType.Ban && currentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= 2) + setMode(pickColour, ChoiceType.Pick); + else + setMode(nextColour, currentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= 2 ? ChoiceType.Pick : ChoiceType.Ban); } protected override bool OnMouseDown(MouseDownEvent e) From 86423dce5fbaffc8a4f1c4555ea2ea889675f291 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Nov 2018 01:29:42 +0900 Subject: [PATCH 0253/2854] Animate song bar based on game mode --- osu.Game.Tournament/Components/SongBar.cs | 40 +++++++++++++++++-- osu.Game.Tournament/IPC/FileBasedIPC.cs | 6 +-- .../Screens/BeatmapInfoScreen.cs | 4 +- .../Screens/Gameplay/GameplayScreen.cs | 21 ++++++---- 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 880b80edf8..6d9dc6025f 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -11,6 +12,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Screens.Menu; using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Tournament.Components { @@ -44,6 +46,28 @@ namespace osu.Game.Tournament.Components } private Container panelContents; + private Container innerPanel; + private Container outerPanel; + + private const float main_width = 0.97f; + + public bool Expanded + { + set + { + if (value) + { + innerPanel.ResizeWidthTo(0.7f, 800, Easing.OutQuint); + outerPanel.ResizeWidthTo(main_width, 800, Easing.OutQuint); + } + else + { + innerPanel.ResizeWidthTo(1, 800, Easing.OutQuint); + outerPanel.ResizeWidthTo(0.3f, 800, Easing.OutQuint); + } + } + } + [BackgroundDependencyLoader] private void load() @@ -52,12 +76,20 @@ namespace osu.Game.Tournament.Components InternalChildren = new Drawable[] { - new Container + outerPanel = new Container { Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.2f), + Type = EdgeEffectType.Shadow, + Radius = 5, + }, RelativeSizeAxes = Axes.X, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + RelativePositionAxes = Axes.X, + X = -(1 - main_width) / 2, Y = -10, Width = 0.95f, Height = TournamentBeatmapPanel.HEIGHT, @@ -69,7 +101,7 @@ namespace osu.Game.Tournament.Components RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.93f), }, - new Container + innerPanel = new Container { Masking = true, CornerRadius = TournamentBeatmapPanel.HEIGHT / 2, diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 168ff80eb7..2ad30db748 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -87,9 +87,8 @@ namespace osu.Game.Tournament.IPC State.Value = (TourneyState)Enum.Parse(typeof(TourneyState), sr.ReadLine()); } } - catch (Exception e) + catch (Exception) { - Logger.Log(e.ToString(), LoggingTarget.Runtime); // file might be in use. } @@ -102,9 +101,8 @@ namespace osu.Game.Tournament.IPC Score2 = int.Parse(sr.ReadLine()); } } - catch (Exception e) + catch (Exception) { - Logger.Log(e.ToString(), LoggingTarget.Runtime); // file might be in use. } }, 250, true); diff --git a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs index 11cbb56b49..4ddd475b6e 100644 --- a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs +++ b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs @@ -19,8 +19,8 @@ namespace osu.Game.Tournament.Screens { Add(SongBar = new SongBar { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, }); } diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 31c5e9f426..a89200f3a5 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -54,7 +54,7 @@ namespace osu.Game.Tournament.Screens.Gameplay Colour = Color4.Gray, RelativeSizeAxes = Axes.X, Text = "Toggle warmup", - Action = toggleWarmup + Action = () => warmup.Toggle() } } } @@ -64,6 +64,8 @@ namespace osu.Game.Tournament.Screens.Gameplay State.BindTo(ipc.State); currentMatch.BindTo(ladder.CurrentMatch); + + warmup.BindValueChanged(w => warmupButton.Colour = !w ? Color4.White : Color4.Gray, true); } private void stateChanged(TourneyState state) @@ -77,15 +79,18 @@ namespace osu.Game.Tournament.Screens.Gameplay else currentMatch.Value.Team2Score.Value++; } - } - private void toggleWarmup() - { - warmup.Toggle(); - if (warmup.Value) - warmupButton.Colour = Color4.White; + if (state == TourneyState.Idle) + { + // show chat + SongBar.Expanded = false; + } else - warmupButton.Colour = Color4.Gray; + { + SongBar.Expanded = true; + } + + } } } From b5c2d94cc4ee8b5b4e3e5ee858098a974dc4d424 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Nov 2018 01:39:02 +0900 Subject: [PATCH 0254/2854] Avoid rendering more than one video at once --- .../Screens/BeatmapInfoScreen.cs | 3 +-- osu.Game.Tournament/Screens/IProvideVideo.cs | 9 ++++++++ .../Screens/Ladder/LadderManager.cs | 2 +- .../Screens/MapPool/MapPoolScreen.cs | 3 +-- .../Screens/TeamIntro/TeamIntroScreen.cs | 3 +-- .../Screens/TournamentSceneManager.cs | 13 +++++++++--- .../Screens/TournamentScreen.cs | 21 +++++++++++++++++++ 7 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 osu.Game.Tournament/Screens/IProvideVideo.cs create mode 100644 osu.Game.Tournament/Screens/TournamentScreen.cs diff --git a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs index 4ddd475b6e..def9f228cf 100644 --- a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs +++ b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs @@ -5,13 +5,12 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; -using osu.Game.Screens; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; namespace osu.Game.Tournament.Screens { - public abstract class BeatmapInfoScreen : OsuScreen + public abstract class BeatmapInfoScreen : TournamentScreen { protected readonly SongBar SongBar; diff --git a/osu.Game.Tournament/Screens/IProvideVideo.cs b/osu.Game.Tournament/Screens/IProvideVideo.cs new file mode 100644 index 0000000000..b5a4e1ad8e --- /dev/null +++ b/osu.Game.Tournament/Screens/IProvideVideo.cs @@ -0,0 +1,9 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Tournament.Screens +{ + public interface IProvideVideo + { + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 5ef7d047e2..4c9700462f 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -23,7 +23,7 @@ using SixLabors.Primitives; namespace osu.Game.Tournament.Screens.Ladder { [Cached] - public class LadderManager : CompositeDrawable, IHasContextMenu + public class LadderManager : TournamentScreen, IHasContextMenu { private Container pairingsContainer; private Container paths; diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 5a04640ed7..5a463c6d5e 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -10,7 +10,6 @@ using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Screens; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Screens.Gameplay; @@ -21,7 +20,7 @@ using OpenTK.Input; namespace osu.Game.Tournament.Screens.MapPool { - public class MapPoolScreen : OsuScreen + public class MapPoolScreen : TournamentScreen { private readonly FillFlowContainer maps; diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index 7c4f139de7..bb17598c70 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Video; using osu.Framework.Platform; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Screens; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; using OpenTK; @@ -18,7 +17,7 @@ using OpenTK.Graphics; namespace osu.Game.Tournament.Screens.TeamIntro { - public class TeamIntroScreen : OsuScreen + public class TeamIntroScreen : TournamentScreen, IProvideVideo { private Container mainContainer; diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index 4cd6c681fa..20af4c29be 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -29,6 +29,7 @@ namespace osu.Game.Tournament.Screens private DrawingsScreen drawings; private Container screens; private ShowcaseScreen showcase; + private VideoSprite video; [BackgroundDependencyLoader] private void load(LadderInfo ladder, Storage storage) @@ -76,7 +77,7 @@ namespace osu.Game.Tournament.Screens //Masking = true, Children = new Drawable[] { - new VideoSprite(storage.GetStream("BG Logoless - OWC.m4v")) + video = new VideoSprite(storage.GetStream("BG Logoless - OWC.m4v")) { Loop = true, RelativeSizeAxes = Axes.Both, @@ -107,9 +108,15 @@ namespace osu.Game.Tournament.Screens foreach (var s in screens.Children) { if (s == screen) - s.FadeIn(100); + { + s.Show(); + if (s is IProvideVideo) + video.FadeOut(200); + else + video.Show(); + } else - s.FadeOut(100); + s.Hide(); } } } diff --git a/osu.Game.Tournament/Screens/TournamentScreen.cs b/osu.Game.Tournament/Screens/TournamentScreen.cs new file mode 100644 index 0000000000..dcdd7f8ce5 --- /dev/null +++ b/osu.Game.Tournament/Screens/TournamentScreen.cs @@ -0,0 +1,21 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Screens; + +namespace osu.Game.Tournament.Screens +{ + public class TournamentScreen : OsuScreen + { + public override void Hide() + { + this.FadeOut(200); + } + + public override void Show() + { + this.FadeIn(200); + } + } +} From 21bbb6863631ee64d7dd9fad91bbb4015d9a2711 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Nov 2018 01:50:09 +0900 Subject: [PATCH 0255/2854] Handle delayed contract --- .../Screens/Gameplay/GameplayScreen.cs | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index a89200f3a5..f164d2d7b0 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -1,11 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using Microsoft.Diagnostics.Runtime.Interop; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; +using osu.Framework.Threading; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; @@ -68,6 +70,8 @@ namespace osu.Game.Tournament.Screens.Gameplay warmup.BindValueChanged(w => warmupButton.Colour = !w ? Color4.White : Color4.Gray, true); } + private ScheduledDelegate scheduledBarContract; + private void stateChanged(TourneyState state) { if (state == TourneyState.Ranking) @@ -80,17 +84,21 @@ namespace osu.Game.Tournament.Screens.Gameplay currentMatch.Value.Team2Score.Value++; } - if (state == TourneyState.Idle) - { - // show chat - SongBar.Expanded = false; - } - else - { - SongBar.Expanded = true; - } - + scheduledBarContract?.Cancel(); + switch (state) + { + case TourneyState.Idle: + // show chat + SongBar.Expanded = false; + break; + case TourneyState.Ranking: + scheduledBarContract = Scheduler.AddDelayed(() => SongBar.Expanded = false, 15000); + break; + default: + SongBar.Expanded = true; + break; + } } } } From 9c18f7a25afd2ce3ee28ba98bc12b0d22f203e7c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Nov 2018 09:05:36 +0900 Subject: [PATCH 0256/2854] Fix date display --- osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index bb17598c70..63ca968aef 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -71,7 +71,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = Anchor.Centre, Origin = Anchor.CentreLeft }, - new RoundDisplay(pairing.Grouping) + new RoundDisplay(pairing) { RelativeSizeAxes = Axes.Both, Height = 0.25f, @@ -83,7 +83,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro private class RoundDisplay : CompositeDrawable { - public RoundDisplay(TournamentGrouping group) + public RoundDisplay(MatchPairing pairing) { var col = OsuColour.Gray(0.33f); @@ -112,7 +112,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Colour = col, - Text = group?.Name.Value ?? "Unknown Grouping", + Text = pairing.Grouping.Value?.Name.Value ?? "Unknown Grouping", Font = "Aquatico-Light", Spacing = new Vector2(10, 0), TextSize = 50, @@ -123,7 +123,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Origin = Anchor.TopCentre, Font = "Aquatico-Light", Colour = col, - Text = (group?.StartDate.Value ?? DateTimeOffset.Now).ToString("dd MMMM HH:mm UTC"), + Text = pairing.Date.Value.ToUniversalTime().ToString("dd MMMM HH:mm UTC"), TextSize = 20, }, } From 2683b161d58df6a7eca79af413a5fb0490fa926a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Nov 2018 09:16:46 +0900 Subject: [PATCH 0257/2854] Fix missing logo --- .../Screens/Gameplay/MatchHeader.cs | 13 ++------ .../Screens/Showcase/ShowcaseScreen.cs | 8 +++++ .../Screens/Showcase/TournamentLogo.cs | 33 +++++++++++++++++++ .../Screens/TeamIntro/TeamIntroScreen.cs | 6 ++-- 4 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs diff --git a/osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs index 2ff5ee7bf6..5765c9cd12 100644 --- a/osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs @@ -6,13 +6,13 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Tournament.Screens.Showcase; using OpenTK; using OpenTK.Graphics; using OpenTK.Input; @@ -28,16 +28,7 @@ namespace osu.Game.Tournament.Screens.Gameplay Height = 100; Children = new Drawable[] { - new Sprite - { - Y = 5, - Texture = textures.Get("game-screen-logo"), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - FillMode = FillMode.Fit, - RelativeSizeAxes = Axes.Both, - Size = Vector2.One - }, + new TournamentLogo(), new RoundDisplay { Y = 10, diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index ce458413a5..6950b940fe 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -1,9 +1,17 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; +using osu.Framework.Graphics.Textures; + namespace osu.Game.Tournament.Screens.Showcase { public class ShowcaseScreen : BeatmapInfoScreen { + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + Add(new TournamentLogo()); + } } } diff --git a/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs b/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs new file mode 100644 index 0000000000..135081a641 --- /dev/null +++ b/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs @@ -0,0 +1,33 @@ +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using OpenTK; + +namespace osu.Game.Tournament.Screens.Showcase +{ + public class TournamentLogo : CompositeDrawable + { + public TournamentLogo() + { + RelativeSizeAxes = Axes.X; + Height = 100; + Margin = new MarginPadding { Vertical = 5 }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + InternalChild = new Sprite + { + Texture = textures.Get("game-screen-logo"), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + FillMode = FillMode.Fit, + RelativeSizeAxes = Axes.Both, + Size = Vector2.One + }; + } + } +} diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index 63ca968aef..05746c07d0 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -1,17 +1,18 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Video; using osu.Framework.Platform; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Tournament.Screens.Showcase; using OpenTK; using OpenTK.Graphics; @@ -24,7 +25,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro private readonly Bindable currentMatch = new Bindable(); [BackgroundDependencyLoader] - private void load(LadderInfo ladder, Storage storage) + private void load(TextureStore textures, LadderInfo ladder, Storage storage) { RelativeSizeAxes = Axes.Both; @@ -35,6 +36,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro RelativeSizeAxes = Axes.Both, Loop = true, }, + new TournamentLogo(), mainContainer = new Container { RelativeSizeAxes = Axes.Both, From e3576572a38f24ab1a18e4207cd9be9196fbad0b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Nov 2018 09:22:57 +0900 Subject: [PATCH 0258/2854] Fix warmup state, automate more --- .../Screens/Gameplay/GameplayScreen.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index f164d2d7b0..c1a4014554 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using Microsoft.Diagnostics.Runtime.Interop; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -33,17 +32,12 @@ namespace osu.Game.Tournament.Screens.Gameplay AddRange(new Drawable[] { new MatchHeader(), - // new CustomChatOverlay - // { - // Anchor = Anchor.BottomCentre, - // Origin = Anchor.BottomCentre, - // Size = new Vector2(0.4f, 1) - // }, new Box { RelativeSizeAxes = Axes.Both, Height = 720 / 1080f, Colour = new Color4(0, 255, 0, 255), + Y = 16, Anchor = Anchor.Centre, Origin= Anchor.Centre, }, @@ -53,7 +47,6 @@ namespace osu.Game.Tournament.Screens.Gameplay { warmupButton = new TriangleButton { - Colour = Color4.Gray, RelativeSizeAxes = Axes.X, Text = "Toggle warmup", Action = () => warmup.Toggle() @@ -65,9 +58,10 @@ namespace osu.Game.Tournament.Screens.Gameplay State.BindValueChanged(stateChanged); State.BindTo(ipc.State); + currentMatch.BindValueChanged(m => warmup.Value = m.Team1Score + m.Team2Score == 0); currentMatch.BindTo(ladder.CurrentMatch); - warmup.BindValueChanged(w => warmupButton.Colour = !w ? Color4.White : Color4.Gray, true); + warmup.BindValueChanged(w => warmupButton.Alpha = !w ? 0.5f : 1, true); } private ScheduledDelegate scheduledBarContract; From 949cf98d1a5316d87c0c51f98b0795e802e325fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Nov 2018 09:23:54 +0900 Subject: [PATCH 0259/2854] Adjust gameplay position ever so slightly --- osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index c1a4014554..99d996d138 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -35,9 +35,9 @@ namespace osu.Game.Tournament.Screens.Gameplay new Box { RelativeSizeAxes = Axes.Both, - Height = 720 / 1080f, + Height = 718 / 1080f, Colour = new Color4(0, 255, 0, 255), - Y = 16, + Y = 14, Anchor = Anchor.Centre, Origin= Anchor.Centre, }, From eabcef3e12723631810c0b6c12b42d261fc5f200 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Nov 2018 10:13:17 +0900 Subject: [PATCH 0260/2854] Add schedule screen --- osu.Game.Tournament.Tests/TestCaseSchedule.cs | 25 +++ .../Ladder/Components/DrawableMatchPairing.cs | 6 +- .../Ladder/Components/DrawableMatchTeam.cs | 17 +- .../Screens/Ladder/LadderManager.cs | 13 +- .../Screens/Schedule/ScheduleScreen.cs | 167 ++++++++++++++++++ .../Screens/TournamentSceneManager.cs | 4 + osu.Game.Tournament/TournamentGameBase.cs | 4 + osu.Game/OsuGameBase.cs | 2 +- 8 files changed, 221 insertions(+), 17 deletions(-) create mode 100644 osu.Game.Tournament.Tests/TestCaseSchedule.cs create mode 100644 osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs diff --git a/osu.Game.Tournament.Tests/TestCaseSchedule.cs b/osu.Game.Tournament.Tests/TestCaseSchedule.cs new file mode 100644 index 0000000000..a12586cb27 --- /dev/null +++ b/osu.Game.Tournament.Tests/TestCaseSchedule.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Game.Tests.Visual; +using osu.Game.Tournament.Screens.Schedule; + +namespace osu.Game.Tournament.Tests +{ + public class TestCaseSchedule : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ScheduleScreen) + }; + + [BackgroundDependencyLoader] + private void load() + { + Add(new ScheduleScreen()); + } + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 5d0837c542..def53ce510 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public class DrawableMatchPairing : CompositeDrawable { public readonly MatchPairing Pairing; - private readonly FillFlowContainer flow; + protected readonly FillFlowContainer Flow; private readonly Drawable selectionBox; private readonly Drawable currentMatchSelectionBox; private Bindable globalSelection; @@ -62,7 +62,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Colour = Color4.OrangeRed, Child = new Box { RelativeSizeAxes = Axes.Both } }, - flow = new FillFlowContainer + Flow = new FillFlowContainer { AutoSizeAxes = Axes.Both, Direction = FillDirection.Vertical, @@ -195,7 +195,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components if (Pairing.Team1.Value == null || Pairing.Team2.Value == null) Pairing.CancelMatchStart(); - flow.Children = new[] + Flow.Children = new[] { new DrawableMatchTeam(Pairing.Team1, Pairing, Pairing.Losers), new DrawableMatchTeam(Pairing.Team2, Pairing, Pairing.Losers) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index 804680ba28..7ff15ef434 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -36,6 +36,19 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private readonly Func isWinner; private LadderManager manager; + [Resolved] + private LadderInfo ladderInfo { get; set; } + + private void setCurrent() + { + //todo: tournamentgamebase? + if (ladderInfo.CurrentMatch.Value != null) + ladderInfo.CurrentMatch.Value.Current.Value = false; + + ladderInfo.CurrentMatch.Value = pairing; + ladderInfo.CurrentMatch.Value.Current.Value = true; + } + [Resolved(CanBeNull = true)] private LadderEditorInfo editorInfo { get; set; } @@ -132,7 +145,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components if (!pairing.Current.Value) { - manager.SetCurrent(pairing); + setCurrent(); return true; } @@ -182,7 +195,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components return new MenuItem[] { - new OsuMenuItem("Set as current", MenuItemType.Standard, () => manager.SetCurrent(pairing)), + new OsuMenuItem("Set as current", MenuItemType.Standard, setCurrent), new OsuMenuItem("Join with", MenuItemType.Standard, () => manager.RequestJoin(pairing, false)), new OsuMenuItem("Join with (loser)", MenuItemType.Standard, () => manager.RequestJoin(pairing, true)), new OsuMenuItem("Remove", MenuItemType.Destructive, () => manager.Remove(pairing)), diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 4c9700462f..2b1a1bad85 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -31,8 +31,8 @@ namespace osu.Game.Tournament.Screens.Ladder private ScrollableContainer scrollContent; - [Cached] - private LadderEditorInfo editorInfo = new LadderEditorInfo(); + [Resolved] + private LadderEditorInfo editorInfo { get; set;} [Resolved] private LadderInfo ladderInfo { get; set; } @@ -185,15 +185,6 @@ namespace osu.Game.Tournament.Screens.Ladder public void Remove(MatchPairing pairing) => pairingsContainer.FirstOrDefault(p => p.Pairing == pairing)?.Remove(); - public void SetCurrent(MatchPairing pairing) - { - if (ladderInfo.CurrentMatch.Value != null) - ladderInfo.CurrentMatch.Value.Current.Value = false; - - ladderInfo.CurrentMatch.Value = pairing; - ladderInfo.CurrentMatch.Value.Current.Value = true; - } - private class JoinRequestHandler : CompositeDrawable { private readonly Container pairingsContainer; diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs new file mode 100644 index 0000000000..a0c36cd6c0 --- /dev/null +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -0,0 +1,167 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Video; +using osu.Framework.Platform; +using osu.Game.Graphics.Sprites; +using osu.Game.Tournament.Screens.Ladder.Components; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Tournament.Screens.Schedule +{ + public class ScheduleScreen : TournamentScreen, IProvideVideo + { + private readonly Bindable currentMatch = new Bindable(); + private Container mainContainer; + private LadderInfo ladder; + + [BackgroundDependencyLoader] + private void load(LadderInfo ladder, Storage storage) + { + this.ladder = ladder; + + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new VideoSprite(storage.GetStream(@"BG Side Logo - OWC.m4v")) + { + RelativeSizeAxes = Axes.Both, + Loop = true, + }, + mainContainer = new Container + { + RelativeSizeAxes = Axes.Both, + } + }; + + currentMatch.BindValueChanged(matchChanged); + currentMatch.BindTo(ladder.CurrentMatch); + } + + private void matchChanged(MatchPairing pairing) + { + if (pairing == null) + { + mainContainer.Clear(); + return; + } + + mainContainer.Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Height = 0.65f, + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new ScheduleContainer("recent matches") + { + RelativeSizeAxes = Axes.Both, + Width = 0.4f, + ChildrenEnumerable = ladder.Pairings + .Where(p => p.Completed.Value) + .OrderByDescending(p => p.Date.Value) + .Take(8) + .Select(p => new SchedulePairing(p)) + }, + new ScheduleContainer("match overview") + { + RelativeSizeAxes = Axes.Both, + Width = 0.6f, + ChildrenEnumerable = ladder.Pairings + .Where(p => !p.Completed.Value) + .OrderBy(p => p.Date.Value) + .Take(8) + .Select(p => new SchedulePairing(p)) + }, + } + } + }, + new ScheduleContainer("current match") + { + RelativeSizeAxes = Axes.Both, + Height = 0.25f, + Children = new Drawable[] + { + new OsuSpriteText + { + Margin = new MarginPadding { Left = -10, Bottom = 10, Top = -5 }, + Spacing = new Vector2(10, 0), + Text = currentMatch.Value.Grouping.Value.Name.Value, + Colour = Color4.Black, + TextSize = 20 + }, + new SchedulePairing(currentMatch), + new OsuSpriteText + { + Text = "Start Time " + pairing.Date.Value.ToUniversalTime().ToString("HH:mm UTC"), + Colour = Color4.Black, + TextSize = 20 + }, + } + } + } + }; + } + + public class SchedulePairing : DrawableMatchPairing + { + public SchedulePairing(MatchPairing pairing) + : base(pairing) + { + Flow.Direction = FillDirection.Horizontal; + } + } + + public class ScheduleContainer : Container + { + protected override Container Content => content; + + private readonly FillFlowContainer content; + + public ScheduleContainer(string title) + { + Padding = new MarginPadding { Left = 30, Top = 30 }; + InternalChildren = new Drawable[] + { + new OsuSpriteText + { + X = 30, + Text = title, + Colour = Color4.Black, + Spacing = new Vector2(10, 0), + TextSize = 30 + }, + content = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.Both, + Margin = new MarginPadding(40) + }, + new Circle + { + Colour = new Color4(233, 187, 79, 255), + Width = 5, + RelativeSizeAxes = Axes.Y, + } + }; + } + } + } +} diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index 20af4c29be..c349e48fa2 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -13,6 +13,7 @@ using osu.Game.Tournament.Screens.Drawings; using osu.Game.Tournament.Screens.Gameplay; using osu.Game.Tournament.Screens.Ladder; using osu.Game.Tournament.Screens.MapPool; +using osu.Game.Tournament.Screens.Schedule; using osu.Game.Tournament.Screens.Showcase; using osu.Game.Tournament.Screens.TeamIntro; using OpenTK; @@ -22,6 +23,7 @@ namespace osu.Game.Tournament.Screens { public class TournamentSceneManager : OsuScreen { + private ScheduleScreen schedule; private LadderManager bracket; private MapPoolScreen mapPool; private GameplayScreen gameplay; @@ -56,6 +58,7 @@ namespace osu.Game.Tournament.Screens new OsuButton { RelativeSizeAxes = Axes.X, Text = "Drawings", Action = () => setScreen(drawings) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Showcase", Action = () => setScreen(showcase) }, new Container { RelativeSizeAxes = Axes.X, Height = 50 }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Schedule", Action = () => setScreen(schedule) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket", Action = () => setScreen(bracket) }, new Container { RelativeSizeAxes = Axes.X, Height = 50 }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "TeamIntro", Action = () => setScreen(teamIntro) }, @@ -88,6 +91,7 @@ namespace osu.Game.Tournament.Screens RelativeSizeAxes = Axes.Both, Children = new Drawable[] { + schedule = new ScheduleScreen(), bracket = new LadderManager(), showcase = new ShowcaseScreen(), mapPool = new MapPoolScreen(), diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 70868d398e..938d79929d 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -19,6 +19,7 @@ using osu.Game.Online.API.Requests; using osu.Game.Rulesets; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; +using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament { @@ -34,6 +35,9 @@ namespace osu.Game.Tournament [Cached] private readonly Bindable ruleset = new Bindable(); + [Cached] + private LadderEditorInfo editorInfo = new LadderEditorInfo(); + private Bindable windowSize; private FileBasedIPC ipc; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index c7f787cff1..971bc10ecd 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -297,7 +297,7 @@ namespace osu.Game } public override bool EnableDrag => true; // allow right-mouse dragging for absolute scroll in scroll containers. - public override bool EnableClick => false; + public override bool EnableClick => true; public override bool ChangeFocusOnClick => false; } } From be3904b647977331ea8a96dcb1321035a0b875be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Nov 2018 10:39:04 +0900 Subject: [PATCH 0261/2854] Add win screen --- osu.Game.Tournament.Tests/TeamWinTestCase.cs | 34 +++ .../Screens/TeamWin/TeamWinScreen.cs | 232 ++++++++++++++++++ .../Screens/TournamentSceneManager.cs | 7 +- 3 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tournament.Tests/TeamWinTestCase.cs create mode 100644 osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs diff --git a/osu.Game.Tournament.Tests/TeamWinTestCase.cs b/osu.Game.Tournament.Tests/TeamWinTestCase.cs new file mode 100644 index 0000000000..48ae9acb91 --- /dev/null +++ b/osu.Game.Tournament.Tests/TeamWinTestCase.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Tournament.Screens.TeamWin; + +namespace osu.Game.Tournament.Tests +{ + public class TestCaseTeamWin : LadderTestCase + { + [Cached] + private readonly Bindable currentMatch = new Bindable(); + + [BackgroundDependencyLoader] + private void load() + { + var pairing = new MatchPairing(); + pairing.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == "USA"); + pairing.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == "JPN"); + pairing.Grouping.Value = Ladder.Groupings.FirstOrDefault(g => g.Name == "Finals"); + currentMatch.Value = pairing; + + Add(new TeamWinScreen + { + FillMode = FillMode.Fit, + FillAspectRatio = 16 / 9f + }); + } + } +} diff --git a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs new file mode 100644 index 0000000000..3c1bcdd1ae --- /dev/null +++ b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs @@ -0,0 +1,232 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Video; +using osu.Framework.Platform; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Tournament.Components; +using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Tournament.Screens.Showcase; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Tournament.Screens.TeamWin +{ + public class TeamWinScreen : TournamentScreen, IProvideVideo + { + private Container mainContainer; + + private readonly Bindable currentMatch = new Bindable(); + private readonly Bindable currentCompleted = new Bindable(); + + private VideoSprite blueWinVideo; + private VideoSprite redWinVideo; + + [BackgroundDependencyLoader] + private void load(TextureStore textures, LadderInfo ladder, Storage storage) + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + blueWinVideo = new VideoSprite(storage.GetStream(@"BG Team - Win Blue.m4v")) + { + Alpha = 1, + RelativeSizeAxes = Axes.Both, + Loop = true, + }, + redWinVideo = new VideoSprite(storage.GetStream(@"BG Team - Win Red.m4v")) + { + Alpha = 0, + RelativeSizeAxes = Axes.Both, + Loop = true, + }, + new TournamentLogo() + { + Y = 40, + }, + mainContainer = new Container + { + RelativeSizeAxes = Axes.Both, + } + }; + + currentMatch.BindValueChanged(matchChanged); + currentMatch.BindTo(ladder.CurrentMatch); + + currentCompleted.BindValueChanged(_ => update()); + } + + private void matchChanged(MatchPairing pairing) + { + currentCompleted.UnbindBindings(); + currentCompleted.BindTo(pairing.Completed); + + update(); + } + + private void update() + { + var pairing = currentMatch.Value; + + if (pairing.Winner == null) + { + mainContainer.Clear(); + return; + } + + bool redWin = pairing.Winner == pairing.Team1.Value; + redWinVideo.Alpha = redWin ? 1 : 0; + blueWinVideo.Alpha = redWin ? 0 : 1; + + mainContainer.Children = new Drawable[] + { + new TeamWithPlayers(pairing.Winner, redWin) + { + RelativeSizeAxes = Axes.Both, + Width = 0.5f, + Height = 0.6f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }, + new RoundDisplay(pairing) + { + RelativeSizeAxes = Axes.Both, + Height = 0.25f, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + } + }; + } + + private class RoundDisplay : CompositeDrawable + { + public RoundDisplay(MatchPairing pairing) + { + var col = OsuColour.Gray(0.33f); + + InternalChildren = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Colour = col, + Text = "WINNER", + Font = "Aquatico-Regular", + TextSize = 15, + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Colour = col, + Text = pairing.Grouping.Value?.Name.Value ?? "Unknown Grouping", + Font = "Aquatico-Light", + Spacing = new Vector2(10, 0), + TextSize = 50, + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Font = "Aquatico-Light", + Colour = col, + Text = pairing.Date.Value.ToUniversalTime().ToString("dd MMMM HH:mm UTC"), + TextSize = 20, + }, + } + } + }; + } + } + + private class TeamWithPlayers : CompositeDrawable + { + private readonly Color4 red = new Color4(129, 68, 65, 255); + private readonly Color4 blue = new Color4(41, 91, 97, 255); + + public TeamWithPlayers(TournamentTeam team, bool left = false) + { + FillFlowContainer players; + var colour = left ? red : blue; + InternalChildren = new Drawable[] + { + new TeamDisplay(team, left ? "Team Red" : "Team Blue", colour) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + players = new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(0, 5), + Padding = new MarginPadding(20), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.Both, + }, + }; + } + + private class TeamDisplay : DrawableTournamentTeam + { + public TeamDisplay(TournamentTeam team, string teamName, Color4 colour) + : base(team) + { + AutoSizeAxes = Axes.Both; + + Flag.Anchor = Flag.Origin = Anchor.TopCentre; + Flag.RelativeSizeAxes = Axes.None; + Flag.Size = new Vector2(300, 200); + Flag.Scale = new Vector2(0.4f); + Flag.Margin = new MarginPadding { Bottom = 20 }; + + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + Children = new Drawable[] + { + Flag, + new OsuSpriteText + { + Text = team?.FullName.ToUpper() ?? "???", + TextSize = 40, + Colour = Color4.Black, + Font = "Aquatico-Light", + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + }, + new OsuSpriteText + { + Text = teamName.ToUpper(), + TextSize = 20, + Colour = colour, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + } + } + }; + } + } + } + } +} diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index c349e48fa2..02492953f2 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -16,6 +16,7 @@ using osu.Game.Tournament.Screens.MapPool; using osu.Game.Tournament.Screens.Schedule; using osu.Game.Tournament.Screens.Showcase; using osu.Game.Tournament.Screens.TeamIntro; +using osu.Game.Tournament.Screens.TeamWin; using OpenTK; using OpenTK.Graphics; @@ -27,6 +28,7 @@ namespace osu.Game.Tournament.Screens private LadderManager bracket; private MapPoolScreen mapPool; private GameplayScreen gameplay; + private TeamWinScreen winner; private TeamIntroScreen teamIntro; private DrawingsScreen drawings; private Container screens; @@ -64,6 +66,8 @@ namespace osu.Game.Tournament.Screens new OsuButton { RelativeSizeAxes = Axes.X, Text = "TeamIntro", Action = () => setScreen(teamIntro) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "MapPool", Action = () => setScreen(mapPool) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Gameplay", Action = () => setScreen(gameplay) }, + new Container { RelativeSizeAxes = Axes.X, Height = 50 }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Win", Action = () => setScreen(winner) }, } }, }, @@ -97,7 +101,8 @@ namespace osu.Game.Tournament.Screens mapPool = new MapPoolScreen(), teamIntro = new TeamIntroScreen(), drawings = new DrawingsScreen(), - gameplay = new GameplayScreen() + gameplay = new GameplayScreen(), + winner = new TeamWinScreen() } }, } From 123629ba9e94f06e1ff5823ed9507b6f49f04ab0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Nov 2018 10:48:57 +0900 Subject: [PATCH 0262/2854] Flash panels on select --- .../Components/TournamentBeatmapPanel.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index b804fc44a7..8df01b636b 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -29,6 +29,7 @@ namespace osu.Game.Tournament.Components public const float HEIGHT = 50; private readonly Bindable currentMatch = new Bindable(); + private Box flash; public TournamentBeatmapPanel(BeatmapInfo beatmap) { @@ -117,6 +118,13 @@ namespace osu.Game.Tournament.Components } }, }, + flash = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Gray, + Blending = BlendingMode.Additive, + Alpha = 0, + }, }); } @@ -126,12 +134,20 @@ namespace osu.Game.Tournament.Components updateState(); } + private BeatmapChoice choice; + private void updateState() { var found = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == Beatmap.OnlineBeatmapID); + bool doFlash = found != choice; + choice = found; + if (found != null) { + if (doFlash) + flash.FadeOutFromOne(500).Loop(0, 10); + BorderThickness = 6; switch (found.Team) From 00731560cb1ced2f569c5ba27e0d79d285c12767 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Nov 2018 01:25:22 +0900 Subject: [PATCH 0263/2854] Add CS display --- osu.Game.Tournament/Components/SongBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 6d9dc6025f..c0ae2116c4 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -179,7 +179,7 @@ namespace osu.Game.Tournament.Components }, new OsuSpriteText { - Text = $"AR {ar:0.#}{extra}", + Text = $"CS{beatmap.BaseDifficulty.CircleSize:0.#} / AR {ar:0.#}{extra}", Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, Colour = OsuColour.Gray(0.33f), Anchor = Anchor.TopRight, From d7a086be83cbb9054655b4fe25f9a1754627e7f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Nov 2018 01:25:31 +0900 Subject: [PATCH 0264/2854] Fix regression in song bar display logic --- osu.Game.Tournament/Components/TournamentBeatmapPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 8df01b636b..d2bc790b16 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -146,7 +146,7 @@ namespace osu.Game.Tournament.Components if (found != null) { if (doFlash) - flash.FadeOutFromOne(500).Loop(0, 10); + flash?.FadeOutFromOne(500).Loop(0, 10); BorderThickness = 6; From 01f814aacee8a79e6d917fb4d49e7eef85f9a4cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Nov 2018 14:11:58 +0900 Subject: [PATCH 0265/2854] Fix song length not being rounded correctly when DT is applied --- osu.Game.Tournament/Components/SongBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index c0ae2116c4..d145544b76 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -163,7 +163,7 @@ namespace osu.Game.Tournament.Components { new OsuSpriteText { - Text = $"Length {length}s", + Text = $"Length {length:0}s", Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, Colour = OsuColour.Gray(0.33f), Anchor = Anchor.TopLeft, From 8eff21d128d1aac39e2826cc093ef48da582270d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Nov 2018 14:12:14 +0900 Subject: [PATCH 0266/2854] Fix star colours being incorrect --- osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs index 5765c9cd12..c750694b87 100644 --- a/osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs @@ -107,10 +107,16 @@ namespace osu.Game.Tournament.Screens.Gameplay private void teamChanged(TournamentTeam team) { + var colour = teamColour == TeamColour.Red ? red : blue; + var flip = teamColour != TeamColour.Red; + InternalChildren = new Drawable[] { - new TeamDisplay(team, teamColour == TeamColour.Red ? red : blue, teamColour != TeamColour.Red), - new ScoreDisplay(currentTeamScore, teamColour != TeamColour.Red, currentMatch.Value.PointsToWin) + new TeamDisplay(team, colour, flip), + new ScoreDisplay(currentTeamScore, flip, currentMatch.Value.PointsToWin) + { + Colour = colour + } }; } } @@ -133,7 +139,6 @@ namespace osu.Game.Tournament.Screens.Gameplay X = (flip ? -1 : 1) * 90, Y = 5, Scale = flip ? new Vector2(-1, 1) : Vector2.One, - Colour = new Color4(95, 41, 60, 255), }; currentTeamScore.BindValueChanged(scoreChanged); From 604cb4cb9ee4060d3512cc58391c780f372947a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Nov 2018 14:12:41 +0900 Subject: [PATCH 0267/2854] Update to support new dropdown logic --- .../Screens/Ladder/Components/LadderEditorSettings.cs | 8 +++----- .../Screens/Ladder/Components/TournamentGrouping.cs | 2 ++ osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs | 5 ++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index 3fa7fdf7e8..afb20a68f0 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 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; @@ -36,8 +35,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { var teamEntries = ladderInfo.Teams; - var groupingOptions = ladderInfo.Groupings.Select(g => new KeyValuePair(g.Name, g)) - .Prepend(new KeyValuePair("None", new TournamentGrouping())); + var groupingOptions = ladderInfo.Groupings.Prepend(new TournamentGrouping()); Children = new Drawable[] { @@ -80,7 +78,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components textboxTeam2 = new OsuTextBox { RelativeSizeAxes = Axes.X, Height = 20 }, groupingDropdown = new SettingsDropdown { - Bindable = new Bindable { Default = groupingOptions.First().Value }, + Bindable = new Bindable { Default = groupingOptions.First() }, Items = groupingOptions }, losersCheckbox = new PlayerCheckbox @@ -94,7 +92,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { textboxTeam1.Text = selection?.Team1.Value?.Acronym; textboxTeam2.Text = selection?.Team2.Value?.Acronym; - groupingDropdown.Bindable.Value = selection?.Grouping.Value ?? groupingOptions.First().Value; + groupingDropdown.Bindable.Value = selection?.Grouping.Value ?? groupingOptions.First(); losersCheckbox.Current.Value = selection?.Losers.Value ?? false; }; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs index 17f76a0143..370f0ea643 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs @@ -22,5 +22,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly Bindable StartDate = new Bindable(); public List Pairings = new List(); + + public override string ToString() => Name.Value ?? "None"; } } diff --git a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs index 3c1bcdd1ae..a6f6e7038a 100644 --- a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs +++ b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs @@ -47,7 +47,7 @@ namespace osu.Game.Tournament.Screens.TeamWin RelativeSizeAxes = Axes.Both, Loop = true, }, - new TournamentLogo() + new TournamentLogo { Y = 40, }, @@ -163,7 +163,6 @@ namespace osu.Game.Tournament.Screens.TeamWin public TeamWithPlayers(TournamentTeam team, bool left = false) { - FillFlowContainer players; var colour = left ? red : blue; InternalChildren = new Drawable[] { @@ -172,7 +171,7 @@ namespace osu.Game.Tournament.Screens.TeamWin Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - players = new FillFlowContainer + new FillFlowContainer { Direction = FillDirection.Vertical, AutoSizeAxes = Axes.Both, From 54b87e9c9344942410f37ebcd94a3c79be593216 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Nov 2018 14:12:51 +0900 Subject: [PATCH 0268/2854] Don't crash on unexpected pairing links --- osu.Game.Tournament/TournamentGameBase.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 938d79929d..8a2e6471c1 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -104,7 +104,10 @@ namespace osu.Game.Tournament // link pairings to groupings foreach (var group in Ladder.Groupings) foreach (var id in group.Pairings) - Ladder.Pairings.Single(p => p.ID == id).Grouping.Value = group; + { + var found = Ladder.Pairings.FirstOrDefault(p => p.ID == id); + if (found != null) found.Grouping.Value = group; + } Ladder.CurrentMatch.Value = Ladder.Pairings.FirstOrDefault(p => p.Current.Value); From 46e163ec5e81c85c1f58a0129cdf144c327560ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Nov 2018 21:28:42 +0900 Subject: [PATCH 0269/2854] Add score display --- .../TestCaseMatchScoreDisplay.cs | 40 +++++ osu.Game.Tournament/IPC/FileBasedIPC.cs | 25 +-- osu.Game.Tournament/IPC/MatchIPCInfo.cs | 19 +++ osu.Game.Tournament/IPC/TourneyState.cs | 14 ++ .../Screens/BeatmapInfoScreen.cs | 2 +- .../Gameplay/{ => Components}/MatchHeader.cs | 8 +- .../Gameplay/Components/MatchScoreDisplay.cs | 144 ++++++++++++++++++ .../Screens/Gameplay/GameplayScreen.cs | 57 ++++++- .../Screens/MapPool/MapPoolScreen.cs | 4 +- osu.Game.Tournament/TournamentGameBase.cs | 2 +- 10 files changed, 277 insertions(+), 38 deletions(-) create mode 100644 osu.Game.Tournament.Tests/TestCaseMatchScoreDisplay.cs create mode 100644 osu.Game.Tournament/IPC/MatchIPCInfo.cs create mode 100644 osu.Game.Tournament/IPC/TourneyState.cs rename osu.Game.Tournament/Screens/Gameplay/{ => Components}/MatchHeader.cs (96%) create mode 100644 osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs diff --git a/osu.Game.Tournament.Tests/TestCaseMatchScoreDisplay.cs b/osu.Game.Tournament.Tests/TestCaseMatchScoreDisplay.cs new file mode 100644 index 0000000000..62c5ca786b --- /dev/null +++ b/osu.Game.Tournament.Tests/TestCaseMatchScoreDisplay.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.MathUtils; +using osu.Game.Tournament.IPC; +using osu.Game.Tournament.Screens.Gameplay.Components; + +namespace osu.Game.Tournament.Tests +{ + public class TestCaseMatchScoreDisplay : LadderTestCase + { + [Cached(Type = typeof(MatchIPCInfo))] + private MatchIPCInfo matchInfo = new MatchIPCInfo(); + + public TestCaseMatchScoreDisplay() + { + Add(new MatchScoreDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Scheduler.AddDelayed(() => + { + int amount = (int)((RNG.NextDouble() - 0.5) * 10000); + if (amount < 0) + matchInfo.Score1.Value -= amount; + else + matchInfo.Score2.Value += amount; + }, 100, true); + } + } +} diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 2ad30db748..6759122c16 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -4,8 +4,6 @@ using System; using System.IO; using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Framework.Platform.Windows; using osu.Game.Beatmaps; @@ -16,16 +14,7 @@ using osu.Game.Rulesets; namespace osu.Game.Tournament.IPC { - public enum TourneyState - { - Initialising, - Idle, - WaitingForClients, - Playing, - Ranking - } - - public class FileBasedIPC : Component + public class FileBasedIPC : MatchIPCInfo { [Resolved] protected APIAccess API { get; private set; } @@ -33,15 +22,7 @@ namespace osu.Game.Tournament.IPC [Resolved] protected RulesetStore Rulesets { get; private set; } - public readonly Bindable Beatmap = new Bindable(); - - public readonly Bindable Mods = new Bindable(); - - public readonly Bindable State = new Bindable(); - private int lastBeatmapId; - public int Score1; - public int Score2; [BackgroundDependencyLoader] private void load() @@ -97,8 +78,8 @@ namespace osu.Game.Tournament.IPC using (var stream = stable.GetStream(file_ipc_scores_filename)) using (var sr = new StreamReader(stream)) { - Score1 = int.Parse(sr.ReadLine()); - Score2 = int.Parse(sr.ReadLine()); + Score1.Value = int.Parse(sr.ReadLine()); + Score2.Value = int.Parse(sr.ReadLine()); } } catch (Exception) diff --git a/osu.Game.Tournament/IPC/MatchIPCInfo.cs b/osu.Game.Tournament/IPC/MatchIPCInfo.cs new file mode 100644 index 0000000000..d40ec35808 --- /dev/null +++ b/osu.Game.Tournament/IPC/MatchIPCInfo.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Legacy; + +namespace osu.Game.Tournament.IPC +{ + public class MatchIPCInfo : Component + { + public Bindable Beatmap { get; } = new Bindable(); + public Bindable Mods { get; } = new Bindable(); + public Bindable State { get; } = new Bindable(); + public BindableInt Score1 { get; } = new BindableInt(); + public BindableInt Score2 { get; } = new BindableInt(); + } +} diff --git a/osu.Game.Tournament/IPC/TourneyState.cs b/osu.Game.Tournament/IPC/TourneyState.cs new file mode 100644 index 0000000000..afa5b400ba --- /dev/null +++ b/osu.Game.Tournament/IPC/TourneyState.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Tournament.IPC +{ + public enum TourneyState + { + Initialising, + Idle, + WaitingForClients, + Playing, + Ranking + } +} diff --git a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs index def9f228cf..b75456c1e9 100644 --- a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs +++ b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tournament.Screens } [BackgroundDependencyLoader] - private void load(FileBasedIPC ipc) + private void load(MatchIPCInfo ipc) { ipc.Beatmap.BindValueChanged(beatmapChanged, true); ipc.Mods.BindValueChanged(modsChanged, true); diff --git a/osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs similarity index 96% rename from osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs rename to osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index c750694b87..9925f5ef74 100644 --- a/osu.Game.Tournament/Screens/Gameplay/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -17,7 +17,7 @@ using OpenTK; using OpenTK.Graphics; using OpenTK.Input; -namespace osu.Game.Tournament.Screens.Gameplay +namespace osu.Game.Tournament.Screens.Gameplay.Components { public class MatchHeader : Container { @@ -113,7 +113,7 @@ namespace osu.Game.Tournament.Screens.Gameplay InternalChildren = new Drawable[] { new TeamDisplay(team, colour, flip), - new ScoreDisplay(currentTeamScore, flip, currentMatch.Value.PointsToWin) + new TeamScore(currentTeamScore, flip, currentMatch.Value.PointsToWin) { Colour = colour } @@ -121,12 +121,12 @@ namespace osu.Game.Tournament.Screens.Gameplay } } - private class ScoreDisplay : CompositeDrawable + private class TeamScore : CompositeDrawable { private readonly Bindable currentTeamScore = new Bindable(); private readonly StarCounter counter; - public ScoreDisplay(Bindable score, bool flip, int count) + public TeamScore(Bindable score, bool flip, int count) { var anchor = flip ? Anchor.CentreRight : Anchor.CentreLeft; diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs new file mode 100644 index 0000000000..2c00c23d42 --- /dev/null +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs @@ -0,0 +1,144 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.UserInterface; +using osu.Game.Tournament.IPC; +using osu.Game.Tournament.Screens.Ladder.Components; +using OpenTK.Graphics; + +namespace osu.Game.Tournament.Screens.Gameplay.Components +{ + public class MatchScoreDisplay : CompositeDrawable + { + private readonly Color4 red = new Color4(186, 0, 18, 255); + private readonly Color4 blue = new Color4(17, 136, 170, 255); + + private const float bar_height = 20; + + private readonly Bindable currentMatch = new Bindable(); + + private readonly BindableInt score1 = new BindableInt(); + private readonly BindableInt score2 = new BindableInt(); + + private readonly MatchScoreCounter score1Text; + private readonly MatchScoreCounter score2Text; + + private readonly Circle score1Bar; + private readonly Circle score2Bar; + + public MatchScoreDisplay() + { + RelativeSizeAxes = Axes.X; + + InternalChildren = new Drawable[] + { + score1Bar = new Circle + { + Name = "top bar red", + RelativeSizeAxes = Axes.X, + Height = bar_height, + Width = 0, + Colour = red, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopRight + }, + score1Text = new MatchScoreCounter + { + Colour = red, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre + }, + score2Bar = new Circle + { + Name = "top bar blue", + RelativeSizeAxes = Axes.X, + Height = bar_height, + Width = 0, + Colour = blue, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopLeft + }, + score2Text = new MatchScoreCounter + { + Colour = blue, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre + }, + }; + } + + [BackgroundDependencyLoader] + private void load(LadderInfo ladder, MatchIPCInfo ipc) + { + currentMatch.BindTo(ladder.CurrentMatch); + + score1.BindValueChanged(_ => updateScores()); + score1.BindTo(ipc.Score1); + + score2.BindValueChanged(_ => updateScores()); + score2.BindTo(ipc.Score2); + } + + private void updateScores() + { + score1Text.Current.Value = score1.Value; + score2Text.Current.Value = score2.Value; + + var winningText = score1.Value > score2.Value ? score1Text : score2Text; + var losingText = score1.Value <= score2.Value ? score1Text : score2Text; + + winningText.Winning = true; + losingText.Winning = false; + + var winningBar = score1.Value > score2.Value ? score1Bar : score2Bar; + var losingBar = score1.Value <= score2.Value ? score1Bar : score2Bar; + + var diff = Math.Max(score1.Value, score2.Value) - Math.Min(score1.Value, score2.Value); + + losingBar.ResizeWidthTo(0, 400, Easing.OutQuint); + winningBar.ResizeWidthTo((float)Math.Pow(diff / 1000000f, 0.5), 400, Easing.OutQuint); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + score1Text.X = -Math.Max(score1Text.DrawWidth / 2, score1Bar.DrawWidth); + score2Text.X = Math.Max(score2Text.DrawWidth / 2, score2Bar.DrawWidth); + } + + private class MatchScoreCounter : ScoreCounter + { + public MatchScoreCounter() + { + Margin = new MarginPadding { Top = bar_height + 5, Horizontal = 10 }; + Winning = false; + + DisplayedCountSpriteText.FixedWidth = false; + } + + public bool Winning + { + set + { + if (value) + { + DisplayedCountSpriteText.Font = "Aquatico-Regular"; + DisplayedCountSpriteText.TextSize = 60; + } + else + { + DisplayedCountSpriteText.Font = "Aquatico-Light"; + DisplayedCountSpriteText.TextSize = 40; + } + } + } + } + } +} diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 99d996d138..a86c47839b 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -4,12 +4,14 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Framework.Threading; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; +using osu.Game.Tournament.Screens.Gameplay.Components; using osu.Game.Tournament.Screens.Ladder.Components; using OpenTK.Graphics; @@ -23,23 +25,62 @@ namespace osu.Game.Tournament.Screens.Gameplay public readonly Bindable State = new Bindable(); private TriangleButton warmupButton; - private FileBasedIPC ipc; + private MatchIPCInfo ipc; + + private readonly Color4 red = new Color4(186, 0, 18, 255); + private readonly Color4 blue = new Color4(17, 136, 170, 255); [BackgroundDependencyLoader] - private void load(LadderInfo ladder, TextureStore textures, FileBasedIPC ipc) + private void load(LadderInfo ladder, TextureStore textures, MatchIPCInfo ipc) { this.ipc = ipc; AddRange(new Drawable[] { new MatchHeader(), - new Box + new FillFlowContainer { - RelativeSizeAxes = Axes.Both, - Height = 718 / 1080f, - Colour = new Color4(0, 255, 0, 255), - Y = 14, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, Anchor = Anchor.Centre, - Origin= Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Circle + { + Name = "top bar red", + RelativeSizeAxes = Axes.X, + Height = 10, + Width = 0.5f, + Colour = red, + }, + new Circle + { + Name = "top bar blue", + RelativeSizeAxes = Axes.X, + Height = 10, + Width = 0.5f, + Colour = blue, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }, + } + }, + new Box + { + // chroma key area for stable gameplay + Name = "chroma", + RelativeSizeAxes = Axes.X, + Height = 480, + Colour = new Color4(0, 255, 0, 255), + }, + } }, new ControlPanel { diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 5a463c6d5e..c2cefd8c41 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -12,7 +12,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; -using osu.Game.Tournament.Screens.Gameplay; +using osu.Game.Tournament.Screens.Gameplay.Components; using osu.Game.Tournament.Screens.Ladder.Components; using OpenTK; using OpenTK.Graphics; @@ -92,7 +92,7 @@ namespace osu.Game.Tournament.Screens.MapPool } [BackgroundDependencyLoader] - private void load(LadderInfo ladder, FileBasedIPC ipc) + private void load(LadderInfo ladder, MatchIPCInfo ipc) { currentMatch.BindValueChanged(matchChanged); currentMatch.BindTo(ladder.CurrentMatch); diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 8a2e6471c1..31a3c162f5 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -72,7 +72,7 @@ namespace osu.Game.Tournament dependencies.Cache(Ladder); - dependencies.Cache(ipc = new FileBasedIPC()); + dependencies.CacheAs(ipc = new FileBasedIPC()); Add(ipc); bool addedInfo = false; From e6637532bc8ae9eeca7e672e985488b727f09816 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Nov 2018 12:20:21 +0900 Subject: [PATCH 0270/2854] Add back padding --- .../Screens/Gameplay/Components/MatchScoreDisplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs index 2c00c23d42..fe865a1728 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs @@ -109,8 +109,8 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components { base.UpdateAfterChildren(); - score1Text.X = -Math.Max(score1Text.DrawWidth / 2, score1Bar.DrawWidth); - score2Text.X = Math.Max(score2Text.DrawWidth / 2, score2Bar.DrawWidth); + score1Text.X = -Math.Max(5 + score1Text.DrawWidth / 2, score1Bar.DrawWidth); + score2Text.X = Math.Max(5 + score2Text.DrawWidth / 2, score2Bar.DrawWidth); } private class MatchScoreCounter : ScoreCounter From 1ab4713ef63a6764c78cb3063abe9476df3407ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Nov 2018 14:22:42 +0900 Subject: [PATCH 0271/2854] Add tournament chat display --- .../TestCaseMatchChatDisplay.cs | 103 +++++++++++ .../Components/MatchChatDisplay.cs | 167 ++++++++++++++++++ osu.Game/Online/Chat/Message.cs | 4 - 3 files changed, 270 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs create mode 100644 osu.Game.Tournament/Components/MatchChatDisplay.cs diff --git a/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs b/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs new file mode 100644 index 0000000000..aab56d1714 --- /dev/null +++ b/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs @@ -0,0 +1,103 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Online.Chat; +using osu.Game.Tests.Visual; +using osu.Game.Tournament.Components; +using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Users; +using OpenTK; + +namespace osu.Game.Tournament.Tests +{ + public class TestCaseMatchChatDisplay : OsuTestCase + { + private readonly Channel testChannel = new Channel(); + + private readonly User admin = new User + { + Username = "HappyStick", + Id = 2, + Colour = "f2ca34" + }; + + private readonly User redUser = new User + { + Username = "BanchoBot", + Id = 3, + }; + + private readonly User blueUser = new User + { + Username = "Zallius", + Id = 4, + }; + + [Cached] + private LadderInfo ladderInfo = new LadderInfo(); + + public TestCaseMatchChatDisplay() + { + MatchChatDisplay chatDisplay; + + Add(chatDisplay = new MatchChatDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(400, 80) + }); + + ladderInfo.CurrentMatch.Value = new MatchPairing + { + Team1 = + { + Value = new TournamentTeam { Players = new List { redUser } } + }, + Team2 = + { + Value = new TournamentTeam { Players = new List { blueUser } } + } + }; + + chatDisplay.Channel.Value = testChannel; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + AddStep("message from admin", () => testChannel.AddLocalEcho(new LocalEchoMessage + { + Sender = admin, + Content = "I am a wang!" + })); + + AddStep("message from team red", () => testChannel.AddLocalEcho(new LocalEchoMessage + { + Sender = redUser, + Content = "I am team red." + })); + + AddStep("message from team red", () => testChannel.AddLocalEcho(new LocalEchoMessage + { + Sender = redUser, + Content = "I plan to win!" + })); + + AddStep("message from team blue", () => testChannel.AddLocalEcho(new LocalEchoMessage + { + Sender = blueUser, + Content = "Not on my watch. Prepare to eat saaaaaaaaaand. Lots and lots of saaaaaaand." + })); + + AddStep("message from admin", () => testChannel.AddLocalEcho(new LocalEchoMessage + { + Sender = admin, + Content = "Okay okay, calm down guys. Let's do this!" + })); + } + } +} diff --git a/osu.Game.Tournament/Components/MatchChatDisplay.cs b/osu.Game.Tournament/Components/MatchChatDisplay.cs new file mode 100644 index 0000000000..a68507044d --- /dev/null +++ b/osu.Game.Tournament/Components/MatchChatDisplay.cs @@ -0,0 +1,167 @@ +// Copyright (c) 2007-2018 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Tournament.Components +{ + public class MatchChatDisplay : CompositeDrawable + { + private Channel lastChannel; + public readonly Bindable Channel = new Bindable(); + private readonly FillFlowContainer messagesFlow; + + public MatchChatDisplay() + { + CornerRadius = 5; + Masking = true; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = Color4.Black, + Alpha = 0.8f, + RelativeSizeAxes = Axes.Both, + }, + messagesFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + LayoutEasing = Easing.Out, + LayoutDuration = 500, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Direction = FillDirection.Vertical, + }, + }; + + Channel.BindValueChanged(channelChanged); + } + + private void channelChanged(Channel channel) + { + if (lastChannel != null) + lastChannel.NewMessagesArrived -= newMessages; + + lastChannel = channel; + + channel.NewMessagesArrived += newMessages; + } + + private void newMessages(IEnumerable messages) + { + var excessChildren = messagesFlow.Children.Count - 10; + if (excessChildren > 0) + { + foreach (var c in messagesFlow.Children.Take(excessChildren)) + c.Expire(); + } + + foreach (var message in messages) + { + var formatted = MessageFormatter.FormatMessage(message); + messagesFlow.Add(new MatchMessage(formatted) { Y = messagesFlow.Height }); + } + } + + private class MatchMessage : CompositeDrawable + { + private readonly Message message; + + public MatchMessage(Message message) + { + this.message = message; + } + + private readonly Color4 red = new Color4(186, 0, 18, 255); + private readonly Color4 blue = new Color4(17, 136, 170, 255); + + [BackgroundDependencyLoader] + private void load(LadderInfo info) + { + Circle colourBox; + + Margin = new MarginPadding(3); + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + OsuSpriteText senderText; + InternalChildren = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + Width = 0.2f, + Children = new Drawable[] + { + senderText = new OsuSpriteText + { + Font = @"Exo2.0-Bold", + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Text = message.Sender.ToString() + } + } + }, + new Container + { + Size = new Vector2(8, OsuSpriteText.FONT_SIZE), + Margin = new MarginPadding { Horizontal = 3 }, + Children = new Drawable[] + { + colourBox = new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(8), + }, + } + }, + new OsuTextFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.5f, + Text = message.DisplayContent + } + } + }, + }; + + if (message.Sender.Colour != null) + { + senderText.Colour = colourBox.Colour = OsuColour.FromHex(message.Sender.Colour); + } + else if (info.CurrentMatch.Value.Team1.Value.Players.Any(u => u.Id == message.Sender.Id)) + { + colourBox.Colour = red; + } + else if (info.CurrentMatch.Value.Team2.Value.Players.Any(u => u.Id == message.Sender.Id)) + { + colourBox.Colour = blue; + } + } + } + } +} diff --git a/osu.Game/Online/Chat/Message.cs b/osu.Game/Online/Chat/Message.cs index 3f0f352ac7..2eb3ca70c2 100644 --- a/osu.Game/Online/Chat/Message.cs +++ b/osu.Game/Online/Chat/Message.cs @@ -13,10 +13,6 @@ namespace osu.Game.Online.Chat [JsonProperty(@"message_id")] public readonly long? Id; - //todo: this should be inside sender. - [JsonProperty(@"sender_id")] - public long UserId; - [JsonProperty(@"channel_id")] public long ChannelId; From c9e2ee8f5623c229248b4618022d7024b05b0448 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Nov 2018 18:14:42 +0900 Subject: [PATCH 0272/2854] Fix masking of song bar --- osu.Game.Tournament/Components/SongBar.cs | 39 ++++++++++++------- .../Components/TournamentBeatmapPanel.cs | 2 +- .../Screens/Gameplay/GameplayScreen.cs | 6 +++ 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index d145544b76..94848b9130 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -48,14 +48,23 @@ namespace osu.Game.Tournament.Components private Container panelContents; private Container innerPanel; private Container outerPanel; + private TournamentBeatmapPanel panel; + + private float panelWidth => expanded ? 0.6f : 1; private const float main_width = 0.97f; + private bool expanded; + public bool Expanded { + get => expanded; set { - if (value) + expanded = value; + panel.ResizeWidthTo(panelWidth, 800, Easing.OutQuint); + + if (expanded) { innerPanel.ResizeWidthTo(0.7f, 800, Easing.OutQuint); outerPanel.ResizeWidthTo(main_width, 800, Easing.OutQuint); @@ -63,7 +72,7 @@ namespace osu.Game.Tournament.Components else { innerPanel.ResizeWidthTo(1, 800, Easing.OutQuint); - outerPanel.ResizeWidthTo(0.3f, 800, Easing.OutQuint); + outerPanel.ResizeWidthTo(0.2f, 800, Easing.OutQuint); } } } @@ -91,7 +100,7 @@ namespace osu.Game.Tournament.Components RelativePositionAxes = Axes.X, X = -(1 - main_width) / 2, Y = -10, - Width = 0.95f, + Width = main_width, Height = TournamentBeatmapPanel.HEIGHT, CornerRadius = TournamentBeatmapPanel.HEIGHT / 2, Children = new Drawable[] @@ -101,6 +110,15 @@ namespace osu.Game.Tournament.Components RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.93f), }, + new OsuLogo + { + Triangles = false, + Colour = OsuColour.Gray(0.33f), + Scale = new Vector2(0.08f), + Margin = new MarginPadding(50), + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + }, innerPanel = new Container { Masking = true, @@ -121,15 +139,6 @@ namespace osu.Game.Tournament.Components RelativeSizeAxes = Axes.Both, } } - }, - new OsuLogo - { - Triangles = false, - Colour = OsuColour.Gray(0.33f), - Scale = new Vector2(0.08f), - Margin = new MarginPadding(50), - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, } } } @@ -193,10 +202,12 @@ namespace osu.Game.Tournament.Components Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight }, - new TournamentBeatmapPanel(beatmap) + panel = new TournamentBeatmapPanel(beatmap) { Anchor = Anchor.Centre, - Origin = Anchor.Centre + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(panelWidth, 1) } }; } diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index d2bc790b16..4456abb48e 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tournament.Components currentMatch.BindValueChanged(matchChanged); currentMatch.BindTo(ladder.CurrentMatch); - CornerRadius = 25; + CornerRadius = HEIGHT / 2; Masking = true; AddRangeInternal(new Drawable[] diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index a86c47839b..d3fd08bb8b 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -91,6 +91,12 @@ namespace osu.Game.Tournament.Screens.Gameplay RelativeSizeAxes = Axes.X, Text = "Toggle warmup", Action = () => warmup.Toggle() + }, + new TriangleButton + { + RelativeSizeAxes = Axes.X, + Text = "Toggle chat", + Action = () => { State.Value = State.Value == TourneyState.Idle ? TourneyState.Playing : TourneyState.Idle; } } } } From e3e92f430221b2e477632a7eceb1a11684d53383 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Nov 2018 19:43:54 +0900 Subject: [PATCH 0273/2854] Add chat IPC and gameplay screen integration --- .../Components/MatchChatDisplay.cs | 38 ++++++++++++++++++- osu.Game.Tournament/Components/SongBar.cs | 4 +- osu.Game.Tournament/IPC/FileBasedIPC.cs | 1 + osu.Game.Tournament/IPC/MatchIPCInfo.cs | 1 + .../Screens/Gameplay/GameplayScreen.cs | 23 ++++++++++- osu.Game/Online/Chat/ChannelType.cs | 3 +- 6 files changed, 63 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tournament/Components/MatchChatDisplay.cs b/osu.Game.Tournament/Components/MatchChatDisplay.cs index a68507044d..c44eb88ef8 100644 --- a/osu.Game.Tournament/Components/MatchChatDisplay.cs +++ b/osu.Game.Tournament/Components/MatchChatDisplay.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; +using osu.Game.Tournament.IPC; using OpenTK; using OpenTK.Graphics; @@ -25,7 +26,7 @@ namespace osu.Game.Tournament.Components public MatchChatDisplay() { - CornerRadius = 5; + CornerRadius = 10; Masking = true; InternalChildren = new Drawable[] @@ -51,13 +52,46 @@ namespace osu.Game.Tournament.Components Channel.BindValueChanged(channelChanged); } + private readonly Bindable chatChannel = new Bindable(); + + private ChannelManager manager; + + [BackgroundDependencyLoader(true)] + private void load(MatchIPCInfo ipc) + { + if (ipc != null) + { + AddInternal(manager = new ChannelManager()); + + Channel.BindTo(manager.CurrentChannel); + + chatChannel.BindTo(ipc.ChatChannel); + chatChannel.BindValueChanged(channelString => + { + if (string.IsNullOrWhiteSpace(channelString)) + return; + + int id = int.Parse(channelString); + + var channel = manager.JoinedChannels.FirstOrDefault(ch => ch.Id == id) ?? new Channel + { + Id = id, + Type = ChannelType.Public + }; + + manager.JoinChannel(channel); + manager.CurrentChannel.Value = channel; + + }, true); + } + } + private void channelChanged(Channel channel) { if (lastChannel != null) lastChannel.NewMessagesArrived -= newMessages; lastChannel = channel; - channel.NewMessagesArrived += newMessages; } diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 94848b9130..53dec8e0a4 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -62,7 +62,7 @@ namespace osu.Game.Tournament.Components set { expanded = value; - panel.ResizeWidthTo(panelWidth, 800, Easing.OutQuint); + panel?.ResizeWidthTo(panelWidth, 800, Easing.OutQuint); if (expanded) { @@ -72,7 +72,7 @@ namespace osu.Game.Tournament.Components else { innerPanel.ResizeWidthTo(1, 800, Easing.OutQuint); - outerPanel.ResizeWidthTo(0.2f, 800, Easing.OutQuint); + outerPanel.ResizeWidthTo(0.25f, 800, Easing.OutQuint); } } } diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 6759122c16..87c6fb4c83 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -53,6 +53,7 @@ namespace osu.Game.Tournament.IPC } Mods.Value = (LegacyMods)mods; + ChatChannel.Value = sr.ReadLine(); } } catch diff --git a/osu.Game.Tournament/IPC/MatchIPCInfo.cs b/osu.Game.Tournament/IPC/MatchIPCInfo.cs index d40ec35808..be31df8009 100644 --- a/osu.Game.Tournament/IPC/MatchIPCInfo.cs +++ b/osu.Game.Tournament/IPC/MatchIPCInfo.cs @@ -13,6 +13,7 @@ namespace osu.Game.Tournament.IPC public Bindable Beatmap { get; } = new Bindable(); public Bindable Mods { get; } = new Bindable(); public Bindable State { get; } = new Bindable(); + public Bindable ChatChannel { get; } = new Bindable(); public BindableInt Score1 { get; } = new BindableInt(); public BindableInt Score2 { get; } = new BindableInt(); } diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index d3fd08bb8b..3c6ea39ae7 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -13,6 +13,7 @@ using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Screens.Gameplay.Components; using osu.Game.Tournament.Screens.Ladder.Components; +using OpenTK; using OpenTK.Graphics; namespace osu.Game.Tournament.Screens.Gameplay @@ -82,6 +83,14 @@ namespace osu.Game.Tournament.Screens.Gameplay }, } }, + chat = new MatchChatDisplay + { + RelativeSizeAxes = Axes.X, + Size = new Vector2(0.45f, 120), + Margin = new MarginPadding(10), + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }, new ControlPanel { Children = new Drawable[] @@ -102,8 +111,8 @@ namespace osu.Game.Tournament.Screens.Gameplay } }); - State.BindValueChanged(stateChanged); State.BindTo(ipc.State); + State.BindValueChanged(stateChanged, true); currentMatch.BindValueChanged(m => warmup.Value = m.Team1Score + m.Team2Score == 0); currentMatch.BindTo(ladder.CurrentMatch); @@ -112,6 +121,7 @@ namespace osu.Game.Tournament.Screens.Gameplay } private ScheduledDelegate scheduledBarContract; + private MatchChatDisplay chat; private void stateChanged(TourneyState state) { @@ -132,12 +142,21 @@ namespace osu.Game.Tournament.Screens.Gameplay case TourneyState.Idle: // show chat SongBar.Expanded = false; + using (chat.BeginDelayedSequence(500)) + { + chat.FadeIn(300); + chat.MoveToY(100).MoveToY(0, 500, Easing.OutQuint); + } + break; case TourneyState.Ranking: scheduledBarContract = Scheduler.AddDelayed(() => SongBar.Expanded = false, 15000); break; default: - SongBar.Expanded = true; + chat.FadeOut(200); + chat.MoveToY(100, 500, Easing.In); + using (SongBar.BeginDelayedSequence(300, true)) + SongBar.Expanded = true; break; } } diff --git a/osu.Game/Online/Chat/ChannelType.cs b/osu.Game/Online/Chat/ChannelType.cs index 4ac0a99fc6..2fbc48fb92 100644 --- a/osu.Game/Online/Chat/ChannelType.cs +++ b/osu.Game/Online/Chat/ChannelType.cs @@ -6,6 +6,7 @@ namespace osu.Game.Online.Chat public enum ChannelType { PM, - Public + Public, + Temporary } } From 5801ed7b1ae88d0c2bf8bd0477f893a730121c87 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Nov 2018 19:49:17 +0900 Subject: [PATCH 0274/2854] Adjust spacing on map pool screen --- osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index c2cefd8c41..65f4bfb149 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tournament.Screens.MapPool maps = new FillFlowContainer { Y = 100, - Spacing = new Vector2(10), + Spacing = new Vector2(10, 20), Padding = new MarginPadding(50), Direction = FillDirection.Full, RelativeSizeAxes = Axes.Both, From 67bb428aefdaf9625b9334d651a18ab076ea5831 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Nov 2018 20:14:34 +0900 Subject: [PATCH 0275/2854] Move editing functionality to its own screen --- .../TestCaseLadderManager.cs | 2 +- .../Ladder/Components/DrawableMatchPairing.cs | 18 +- .../Ladder/Components/DrawableMatchTeam.cs | 16 +- .../Ladder/Components/LadderEditorInfo.cs | 1 - .../Ladder/Components/LadderEditorSettings.cs | 15 - .../Screens/Ladder/Components/MatchPairing.cs | 2 +- .../Screens/Ladder/LadderEditorScreen.cs | 166 +++++++++++ .../Screens/Ladder/LadderManager.cs | 259 ------------------ .../Screens/Ladder/LadderScreen.cs | 135 +++++++++ .../Screens/TournamentSceneManager.cs | 8 +- osu.Game.Tournament/TournamentGameBase.cs | 4 - 11 files changed, 328 insertions(+), 298 deletions(-) create mode 100644 osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs delete mode 100644 osu.Game.Tournament/Screens/Ladder/LadderManager.cs create mode 100644 osu.Game.Tournament/Screens/Ladder/LadderScreen.cs diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index 63c8f5e391..3001f46ed2 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -16,7 +16,7 @@ namespace osu.Game.Tournament.Tests Add(new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, - Child = new LadderManager() + Child = new LadderScreen() }); } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index def53ce510..0e9baf33d2 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -30,8 +30,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { Pairing = pairing; - Position = new Vector2(pairing.Position.X, pairing.Position.Y); - AutoSizeAxes = Axes.Both; Margin = new MarginPadding(5); @@ -69,6 +67,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Spacing = new Vector2(2) } }; + pairing.Team1.BindValueChanged(_ => updateTeams()); pairing.Team2.BindValueChanged(_ => updateTeams()); pairing.Team1Score.BindValueChanged(_ => updateWinConditions()); @@ -79,6 +78,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components pairing.LosersProgression.BindValueChanged(_ => updateProgression()); pairing.Losers.BindValueChanged(_ => updateTeams()); pairing.Current.BindValueChanged(_ => updateCurrentMatch(), true); + pairing.Position.BindValueChanged(pos => + { + if (IsDragged) return; + Position = new Vector2(pos.X, pos.Y); + }, true); updateTeams(); } @@ -205,13 +209,13 @@ namespace osu.Game.Tournament.Screens.Ladder.Components updateWinConditions(); } - protected override bool OnMouseDown(MouseDownEvent e) => e.Button == MouseButton.Left && editorInfo.EditingEnabled; + protected override bool OnMouseDown(MouseDownEvent e) => e.Button == MouseButton.Left && editorInfo != null; - protected override bool OnDragStart(DragStartEvent e) => editorInfo.EditingEnabled; + protected override bool OnDragStart(DragStartEvent e) => editorInfo != null; protected override bool OnKeyDown(KeyDownEvent e) { - if (Selected && editorInfo.EditingEnabled && e.Key == Key.Delete) + if (Selected && editorInfo != null && e.Key == Key.Delete) { Remove(); return true; @@ -222,7 +226,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components protected override bool OnClick(ClickEvent e) { - if (!editorInfo.EditingEnabled) + if (editorInfo == null) return false; Selected = true; @@ -237,7 +241,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components this.MoveToOffset(e.Delta); var pos = Position; - Pairing.Position = new Point((int)pos.X, (int)pos.Y); + Pairing.Position.Value = new Point((int)pos.X, (int)pos.Y); return true; } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index 7ff15ef434..be09443684 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -34,7 +34,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private Color4 colourNormal; private readonly Func isWinner; - private LadderManager manager; + private LadderEditorScreen ladderEditor; [Resolved] private LadderInfo ladderInfo { get; set; } @@ -80,9 +80,9 @@ namespace osu.Game.Tournament.Screens.Ladder.Components } [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, LadderManager manager) + private void load(OsuColour colours, LadderEditorScreen ladderEditor) { - this.manager = manager; + this.ladderEditor = ladderEditor; colourWinner = losers ? colours.YellowDarker : colours.BlueDarker; colourNormal = OsuColour.Gray(0.2f); @@ -141,7 +141,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components //TODO: use OnClick instead once we have per-button clicks. protected override bool OnClick(ClickEvent e) { - if (Team == null || editorInfo.EditingEnabled) return false; + if (Team == null || editorInfo != null) return false; if (!pairing.Current.Value) { @@ -190,15 +190,15 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { get { - if (!editorInfo.EditingEnabled) + if (editorInfo == null) return new MenuItem[0]; return new MenuItem[] { new OsuMenuItem("Set as current", MenuItemType.Standard, setCurrent), - new OsuMenuItem("Join with", MenuItemType.Standard, () => manager.RequestJoin(pairing, false)), - new OsuMenuItem("Join with (loser)", MenuItemType.Standard, () => manager.RequestJoin(pairing, true)), - new OsuMenuItem("Remove", MenuItemType.Destructive, () => manager.Remove(pairing)), + new OsuMenuItem("Join with", MenuItemType.Standard, () => ladderEditor.RequestJoin(pairing, false)), + new OsuMenuItem("Join with (loser)", MenuItemType.Standard, () => ladderEditor.RequestJoin(pairing, true)), + new OsuMenuItem("Remove", MenuItemType.Destructive, () => ladderEditor.Remove(pairing)), }; } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs index 0ecf387f7c..1fd9455195 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs @@ -7,7 +7,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { public class LadderEditorInfo { - public readonly BindableBool EditingEnabled = new BindableBool(); public readonly Bindable Selected = new Bindable(); } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index afb20a68f0..90f20a3494 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -39,11 +39,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Children = new Drawable[] { - new PlayerCheckbox - { - Bindable = editorInfo.EditingEnabled, - LabelText = "Enable editing" - }, new Container { RelativeSizeAxes = Axes.X, @@ -119,16 +114,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.Losers.Value = losers; }; - - // sliderBestOf.Bindable.ValueChanged += val => - // { - // if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.BestOf.Value = (int)val; - // }; - - editorInfo.EditingEnabled.ValueChanged += enabled => - { - if (!enabled) editorInfo.Selected.Value = null; - }; } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index 729249c3d7..003f41cfa9 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly Bindable Date = new Bindable(); - public Point Position; + public readonly Bindable Position = new Bindable(); public MatchPairing() { diff --git a/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs new file mode 100644 index 0000000000..cebf7bb682 --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs @@ -0,0 +1,166 @@ +// Copyright (c) 2007-2018 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Framework.Input.States; +using osu.Game.Graphics.UserInterface; +using osu.Game.Tournament.Screens.Ladder.Components; +using OpenTK; +using OpenTK.Graphics; +using SixLabors.Primitives; + +namespace osu.Game.Tournament.Screens.Ladder +{ + [Cached] + public class LadderEditorScreen : LadderScreen, IHasContextMenu + { + [Cached] + private LadderEditorInfo editorInfo = new LadderEditorInfo(); + + [BackgroundDependencyLoader] + private void load() + { + ((Container)InternalChild).Add(new LadderEditorSettings + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Margin = new MarginPadding(5) + }); + } + + private void updateInfo() + { + LadderInfo.Pairings = PairingsContainer.Select(p => p.Pairing).ToList(); + foreach (var g in LadderInfo.Groupings) + g.Pairings = LadderInfo.Pairings.Where(p => p.Grouping.Value == g).Select(p => p.ID).ToList(); + + LadderInfo.Progressions = LadderInfo.Pairings.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat( + LadderInfo.Pairings.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) + .ToList(); + } + + protected override void AddPairing(MatchPairing pairing) + { + base.AddPairing(pairing); + updateInfo(); + } + + protected override void UpdateLayout() + { + base.UpdateLayout(); + updateInfo(); + } + + public void RequestJoin(MatchPairing pairing, bool losers) + { + ScrollContent.Add(new JoinRequestHandler(PairingsContainer, pairing, losers, updateInfo)); + } + + public MenuItem[] ContextMenuItems + { + get + { + if (editorInfo == null) + return new MenuItem[0]; + + return new MenuItem[] + { + new OsuMenuItem("Create new match", MenuItemType.Highlighted, () => + { + var pos = PairingsContainer.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); + AddPairing(new MatchPairing { Position = { Value = new Point((int)pos.X, (int)pos.Y) } }); + }), + }; + } + } + + public void Remove(MatchPairing pairing) + { + PairingsContainer.FirstOrDefault(p => p.Pairing == pairing)?.Remove(); + } + + private class JoinRequestHandler : CompositeDrawable + { + private readonly Container pairingsContainer; + public readonly MatchPairing Source; + private readonly bool losers; + private readonly Action complete; + + private ProgressionPath path; + + public JoinRequestHandler(Container pairingsContainer, MatchPairing source, bool losers, Action complete) + { + this.pairingsContainer = pairingsContainer; + RelativeSizeAxes = Axes.Both; + + Source = source; + this.losers = losers; + this.complete = complete; + if (losers) + Source.LosersProgression.Value = null; + else + Source.Progression.Value = null; + } + + private DrawableMatchPairing findTarget(InputState state) + { + return pairingsContainer.FirstOrDefault(d => d.ReceivePositionalInputAt(state.Mouse.Position)); + } + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) + { + return true; + } + + protected override bool OnMouseMove(MouseMoveEvent e) + { + var found = findTarget(e.CurrentState); + + if (found == path?.Destination) + return false; + + path?.Expire(); + path = null; + + if (found == null) + return false; + + AddInternal(path = new ProgressionPath(pairingsContainer.First(c => c.Pairing == Source), found) + { + Colour = Color4.Yellow, + }); + + return base.OnMouseMove(e); + } + + protected override bool OnClick(ClickEvent e) + { + var found = findTarget(e.CurrentState); + + if (found != null) + { + if (found.Pairing != Source) + { + if (losers) + Source.LosersProgression.Value = found.Pairing; + else + Source.Progression.Value = found.Pairing; + } + + complete?.Invoke(); + Expire(); + return true; + } + + return false; + } + } + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs deleted file mode 100644 index 2b1a1bad85..0000000000 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) 2007-2018 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.Caching; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Lines; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Events; -using osu.Framework.Input.States; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Game.Tournament.Screens.Ladder.Components; -using OpenTK; -using OpenTK.Graphics; -using SixLabors.Primitives; - -namespace osu.Game.Tournament.Screens.Ladder -{ - [Cached] - public class LadderManager : TournamentScreen, IHasContextMenu - { - private Container pairingsContainer; - private Container paths; - private Container headings; - - private ScrollableContainer scrollContent; - - [Resolved] - private LadderEditorInfo editorInfo { get; set;} - - [Resolved] - private LadderInfo ladderInfo { get; set; } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - normalPathColour = colours.BlueDarker.Darken(2); - losersPathColour = colours.YellowDarker.Darken(2); - - RelativeSizeAxes = Axes.Both; - - InternalChild = new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - scrollContent = new ScrollableContainer - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - paths = new Container { RelativeSizeAxes = Axes.Both }, - headings = new Container { RelativeSizeAxes = Axes.Both }, - pairingsContainer = new Container { RelativeSizeAxes = Axes.Both }, - } - }, - new LadderEditorSettings - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Margin = new MarginPadding(5) - } - } - }; - - foreach (var pairing in ladderInfo.Pairings) - addPairing(pairing); - - // todo: fix this - Scheduler.AddDelayed(() => layout.Invalidate(), 100, true); - } - - private void updateInfo() - { - ladderInfo.Pairings = pairingsContainer.Select(p => p.Pairing).ToList(); - foreach (var g in ladderInfo.Groupings) - g.Pairings = ladderInfo.Pairings.Where(p => p.Grouping.Value == g).Select(p => p.ID).ToList(); - - ladderInfo.Progressions = ladderInfo.Pairings.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat( - ladderInfo.Pairings.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) - .ToList(); - } - - private void addPairing(MatchPairing pairing) - { - pairingsContainer.Add(new DrawableMatchPairing(pairing)); - updateInfo(); - } - - public MenuItem[] ContextMenuItems - { - get - { - if (!editorInfo.EditingEnabled) - return new MenuItem[0]; - - return new MenuItem[] - { - new OsuMenuItem("Create new match", MenuItemType.Highlighted, () => - { - var pos = pairingsContainer.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); - addPairing(new MatchPairing { Position = new Point((int)pos.X, (int)pos.Y) }); - }), - }; - } - } - - private Cached layout = new Cached(); - - protected override void Update() - { - base.Update(); - - if (!layout.IsValid) - updateLayout(); - } - - private Color4 normalPathColour; - private Color4 losersPathColour; - - private void updateLayout() - { - paths.Clear(); - headings.Clear(); - - int id = 1; - foreach (var pairing in pairingsContainer.OrderBy(d => d.Y).ThenBy(d => d.X)) - { - pairing.Pairing.ID = id++; - - if (pairing.Pairing.Progression.Value != null) - { - var dest = pairingsContainer.FirstOrDefault(p => p.Pairing == pairing.Pairing.Progression.Value); - - if (dest == null) - // clean up outdated progressions. - pairing.Pairing.Progression.Value = null; - else - paths.Add(new ProgressionPath(pairing, dest) { Colour = pairing.Pairing.Losers ? losersPathColour : normalPathColour }); - } - } - - foreach (var group in ladderInfo.Groupings) - { - var topPairing = pairingsContainer.Where(p => !p.Pairing.Losers && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); - - if (topPairing == null) continue; - - headings.Add(new DrawableTournamentGrouping(group) - { - Position = headings.ToLocalSpace((topPairing.ScreenSpaceDrawQuad.TopLeft + topPairing.ScreenSpaceDrawQuad.TopRight) / 2), - Margin = new MarginPadding { Bottom = 10 }, - Origin = Anchor.BottomCentre, - }); - } - - foreach (var group in ladderInfo.Groupings) - { - var topPairing = pairingsContainer.Where(p => p.Pairing.Losers && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); - - if (topPairing == null) continue; - - headings.Add(new DrawableTournamentGrouping(group, true) - { - Position = headings.ToLocalSpace((topPairing.ScreenSpaceDrawQuad.TopLeft + topPairing.ScreenSpaceDrawQuad.TopRight) / 2), - Margin = new MarginPadding { Bottom = 10 }, - Origin = Anchor.BottomCentre, - }); - } - - layout.Validate(); - updateInfo(); - } - - public void RequestJoin(MatchPairing pairing, bool losers) => scrollContent.Add(new JoinRequestHandler(pairingsContainer, pairing, losers, updateInfo)); - - // todo: remove after ppy/osu-framework#1980 is merged. - public override bool HandlePositionalInput => true; - - public void Remove(MatchPairing pairing) => pairingsContainer.FirstOrDefault(p => p.Pairing == pairing)?.Remove(); - - private class JoinRequestHandler : CompositeDrawable - { - private readonly Container pairingsContainer; - public readonly MatchPairing Source; - private readonly bool losers; - private readonly Action complete; - - private ProgressionPath path; - - public JoinRequestHandler(Container pairingsContainer, MatchPairing source, bool losers, Action complete) - { - this.pairingsContainer = pairingsContainer; - RelativeSizeAxes = Axes.Both; - - Source = source; - this.losers = losers; - this.complete = complete; - if (losers) - Source.LosersProgression.Value = null; - else - Source.Progression.Value = null; - } - - private DrawableMatchPairing findTarget(InputState state) => pairingsContainer.FirstOrDefault(d => d.ReceivePositionalInputAt(state.Mouse.Position)); - - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - - protected override bool OnMouseMove(MouseMoveEvent e) - { - var found = findTarget(e.CurrentState); - - if (found == path?.Destination) - return false; - - path?.Expire(); - path = null; - - if (found == null) - return false; - - AddInternal(path = new ProgressionPath(pairingsContainer.First(c => c.Pairing == Source), found) - { - Colour = Color4.Yellow, - }); - - return base.OnMouseMove(e); - } - - protected override bool OnClick(ClickEvent e) - { - var found = findTarget(e.CurrentState); - - if (found != null) - { - if (found.Pairing != Source) - { - if (losers) - Source.LosersProgression.Value = found.Pairing; - else - Source.Progression.Value = found.Pairing; - } - - complete?.Invoke(); - Expire(); - return true; - } - - return false; - } - } - } -} diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs new file mode 100644 index 0000000000..69035c21db --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -0,0 +1,135 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Caching; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Lines; +using osu.Game.Graphics; +using osu.Game.Tournament.Screens.Ladder.Components; +using OpenTK.Graphics; + +namespace osu.Game.Tournament.Screens.Ladder +{ + public class LadderScreen : TournamentScreen + { + protected Container PairingsContainer; + private Container paths; + private Container headings; + + protected ScrollableContainer ScrollContent; + + [Resolved] + protected LadderInfo LadderInfo { get; private set; } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + normalPathColour = colours.BlueDarker.Darken(2); + losersPathColour = colours.YellowDarker.Darken(2); + + RelativeSizeAxes = Axes.Both; + + InternalChild = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + ScrollContent = new ScrollableContainer + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + paths = new Container { RelativeSizeAxes = Axes.Both }, + headings = new Container { RelativeSizeAxes = Axes.Both }, + PairingsContainer = new Container { RelativeSizeAxes = Axes.Both }, + } + }, + } + }; + + foreach (var pairing in LadderInfo.Pairings) + AddPairing(pairing); + + // todo: fix this + Scheduler.AddDelayed(() => layout.Invalidate(), 100, true); + } + + protected virtual void AddPairing(MatchPairing pairing) + { + PairingsContainer.Add(new DrawableMatchPairing(pairing)); + } + + private Cached layout = new Cached(); + + protected override void Update() + { + base.Update(); + + if (!layout.IsValid) + UpdateLayout(); + } + + private Color4 normalPathColour; + private Color4 losersPathColour; + + protected virtual void UpdateLayout() + { + paths.Clear(); + headings.Clear(); + + int id = 1; + foreach (var pairing in PairingsContainer.OrderBy(d => d.Y).ThenBy(d => d.X)) + { + pairing.Pairing.ID = id++; + + if (pairing.Pairing.Progression.Value != null) + { + var dest = PairingsContainer.FirstOrDefault(p => p.Pairing == pairing.Pairing.Progression.Value); + + if (dest == null) + // clean up outdated progressions. + pairing.Pairing.Progression.Value = null; + else + paths.Add(new ProgressionPath(pairing, dest) { Colour = pairing.Pairing.Losers ? losersPathColour : normalPathColour }); + } + } + + foreach (var group in LadderInfo.Groupings) + { + var topPairing = PairingsContainer.Where(p => !p.Pairing.Losers && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); + + if (topPairing == null) continue; + + headings.Add(new DrawableTournamentGrouping(group) + { + Position = headings.ToLocalSpace((topPairing.ScreenSpaceDrawQuad.TopLeft + topPairing.ScreenSpaceDrawQuad.TopRight) / 2), + Margin = new MarginPadding { Bottom = 10 }, + Origin = Anchor.BottomCentre, + }); + } + + foreach (var group in LadderInfo.Groupings) + { + var topPairing = PairingsContainer.Where(p => p.Pairing.Losers && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); + + if (topPairing == null) continue; + + headings.Add(new DrawableTournamentGrouping(group, true) + { + Position = headings.ToLocalSpace((topPairing.ScreenSpaceDrawQuad.TopLeft + topPairing.ScreenSpaceDrawQuad.TopRight) / 2), + Margin = new MarginPadding { Bottom = 10 }, + Origin = Anchor.BottomCentre, + }); + } + + layout.Validate(); + } + + // todo: remove after ppy/osu-framework#1980 is merged. + public override bool HandlePositionalInput => true; + } +} diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index 02492953f2..de9e6ee19d 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -25,7 +25,8 @@ namespace osu.Game.Tournament.Screens public class TournamentSceneManager : OsuScreen { private ScheduleScreen schedule; - private LadderManager bracket; + private LadderScreen bracket; + private LadderEditorScreen bracketEditor; private MapPoolScreen mapPool; private GameplayScreen gameplay; private TeamWinScreen winner; @@ -57,6 +58,8 @@ namespace osu.Game.Tournament.Screens Direction = FillDirection.Vertical, Children = new Drawable[] { + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket Editor", Action = () => setScreen(bracketEditor) }, + new Container { RelativeSizeAxes = Axes.X, Height = 50 }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Drawings", Action = () => setScreen(drawings) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Showcase", Action = () => setScreen(showcase) }, new Container { RelativeSizeAxes = Axes.X, Height = 50 }, @@ -96,7 +99,8 @@ namespace osu.Game.Tournament.Screens Children = new Drawable[] { schedule = new ScheduleScreen(), - bracket = new LadderManager(), + bracket = new LadderScreen(), + bracketEditor = new LadderEditorScreen(), showcase = new ShowcaseScreen(), mapPool = new MapPoolScreen(), teamIntro = new TeamIntroScreen(), diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 31a3c162f5..8734b2e64b 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -19,7 +19,6 @@ using osu.Game.Online.API.Requests; using osu.Game.Rulesets; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; -using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament { @@ -35,9 +34,6 @@ namespace osu.Game.Tournament [Cached] private readonly Bindable ruleset = new Bindable(); - [Cached] - private LadderEditorInfo editorInfo = new LadderEditorInfo(); - private Bindable windowSize; private FileBasedIPC ipc; From cf0976955bb36edf5be7ed9797cb2c8e2a19f1a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Nov 2018 20:30:12 +0900 Subject: [PATCH 0276/2854] Expose groupings editor --- .../TestCaseGroupingsEditorScreen.cs | 15 ++++++++ .../Groupings/GroupingsEditorScreen.cs | 37 +++++++++++++------ .../Ladder/Components/LadderEditorSettings.cs | 11 ++++++ .../Screens/Ladder/LadderScreen.cs | 3 -- .../Screens/TournamentSceneManager.cs | 4 ++ .../Screens/TournamentScreen.cs | 4 ++ 6 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 osu.Game.Tournament.Tests/TestCaseGroupingsEditorScreen.cs rename osu.Game.Tournament.Tests/TestCaseGroupingManager.cs => osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs (69%) diff --git a/osu.Game.Tournament.Tests/TestCaseGroupingsEditorScreen.cs b/osu.Game.Tournament.Tests/TestCaseGroupingsEditorScreen.cs new file mode 100644 index 0000000000..4e82e27fd9 --- /dev/null +++ b/osu.Game.Tournament.Tests/TestCaseGroupingsEditorScreen.cs @@ -0,0 +1,15 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Tournament.Screens.Groupings; + +namespace osu.Game.Tournament.Tests +{ + public class TestCaseGroupingsEditorScreen : LadderTestCase + { + public TestCaseGroupingsEditorScreen() + { + Add(new GroupingsEditorScreen()); + } + } +} diff --git a/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs b/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs similarity index 69% rename from osu.Game.Tournament.Tests/TestCaseGroupingManager.cs rename to osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs index 531019c28c..1b7e9e6bd1 100644 --- a/osu.Game.Tournament.Tests/TestCaseGroupingManager.cs +++ b/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 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.Graphics; @@ -9,13 +10,13 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; using osu.Game.Tournament.Screens.Ladder.Components; -namespace osu.Game.Tournament.Tests +namespace osu.Game.Tournament.Screens.Groupings { - public class TestCaseGroupingManager : LadderTestCase + public class GroupingsEditorScreen : TournamentScreen, IProvideVideo { private readonly FillFlowContainer items; - public TestCaseGroupingManager() + public GroupingsEditorScreen() { Add(new FillFlowContainer { @@ -37,29 +38,37 @@ namespace osu.Game.Tournament.Tests }, } }); - } [BackgroundDependencyLoader] private void load() { - foreach (var g in Ladder.Groupings) - items.Add(new GroupingRow(g)); + foreach (var g in LadderInfo.Groupings) + items.Add(new GroupingRow(g, updateGroupings)); } - protected override void Dispose(bool isDisposing) + protected override void LoadComplete() { - Ladder.Groupings = items.Children.Select(c => c.Grouping).ToList(); - base.Dispose(isDisposing); + base.LoadComplete(); + Scheduler.AddDelayed(() => LadderInfo.Groupings = items.Children.Select(c => c.Grouping).ToList(), 500, true); } - private void addNew() => items.Add(new GroupingRow(new TournamentGrouping())); + private void addNew() + { + items.Add(new GroupingRow(new TournamentGrouping(), updateGroupings)); + updateGroupings(); + } + + private void updateGroupings() + { + LadderInfo.Groupings = items.Children.Select(c => c.Grouping).ToList(); + } public class GroupingRow : CompositeDrawable { public readonly TournamentGrouping Grouping; - public GroupingRow(TournamentGrouping grouping) + public GroupingRow(TournamentGrouping grouping, Action onDelete) { Grouping = grouping; InternalChildren = new Drawable[] @@ -77,7 +86,11 @@ namespace osu.Game.Tournament.Tests { Width = 0.1f, Text = "Delete", - Action = () => Expire() + Action = () => + { + Expire(); + onDelete(); + } }, } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index 90f20a3494..4334f75728 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; @@ -115,5 +116,15 @@ namespace osu.Game.Tournament.Screens.Ladder.Components editorInfo.Selected.Value.Losers.Value = losers; }; } + + protected override bool OnHover(HoverEvent e) + { + return false; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + } + } } diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs index 69035c21db..5e0cb13375 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -22,9 +22,6 @@ namespace osu.Game.Tournament.Screens.Ladder protected ScrollableContainer ScrollContent; - [Resolved] - protected LadderInfo LadderInfo { get; private set; } - [BackgroundDependencyLoader] private void load(OsuColour colours) { diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index de9e6ee19d..0205838fc2 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -11,6 +11,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Screens; using osu.Game.Tournament.Screens.Drawings; using osu.Game.Tournament.Screens.Gameplay; +using osu.Game.Tournament.Screens.Groupings; using osu.Game.Tournament.Screens.Ladder; using osu.Game.Tournament.Screens.MapPool; using osu.Game.Tournament.Screens.Schedule; @@ -27,6 +28,7 @@ namespace osu.Game.Tournament.Screens private ScheduleScreen schedule; private LadderScreen bracket; private LadderEditorScreen bracketEditor; + private GroupingsEditorScreen groupingsEditor; private MapPoolScreen mapPool; private GameplayScreen gameplay; private TeamWinScreen winner; @@ -59,6 +61,7 @@ namespace osu.Game.Tournament.Screens Children = new Drawable[] { new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket Editor", Action = () => setScreen(bracketEditor) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Groupings Editor", Action = () => setScreen(groupingsEditor) }, new Container { RelativeSizeAxes = Axes.X, Height = 50 }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Drawings", Action = () => setScreen(drawings) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Showcase", Action = () => setScreen(showcase) }, @@ -101,6 +104,7 @@ namespace osu.Game.Tournament.Screens schedule = new ScheduleScreen(), bracket = new LadderScreen(), bracketEditor = new LadderEditorScreen(), + groupingsEditor = new GroupingsEditorScreen(), showcase = new ShowcaseScreen(), mapPool = new MapPoolScreen(), teamIntro = new TeamIntroScreen(), diff --git a/osu.Game.Tournament/Screens/TournamentScreen.cs b/osu.Game.Tournament/Screens/TournamentScreen.cs index dcdd7f8ce5..b440b8e796 100644 --- a/osu.Game.Tournament/Screens/TournamentScreen.cs +++ b/osu.Game.Tournament/Screens/TournamentScreen.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Screens; @@ -8,6 +9,9 @@ namespace osu.Game.Tournament.Screens { public class TournamentScreen : OsuScreen { + [Resolved] + protected LadderInfo LadderInfo { get; private set; } + public override void Hide() { this.FadeOut(200); From 4b74105ad9a9e7fb001b3fcbbf40875975441dd1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Nov 2018 20:34:51 +0900 Subject: [PATCH 0277/2854] Fix code sanity issues --- .../{TeamWinTestCase.cs => TestCaseTeamWin.cs} | 0 osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs | 3 +++ 2 files changed, 3 insertions(+) rename osu.Game.Tournament.Tests/{TeamWinTestCase.cs => TestCaseTeamWin.cs} (100%) diff --git a/osu.Game.Tournament.Tests/TeamWinTestCase.cs b/osu.Game.Tournament.Tests/TestCaseTeamWin.cs similarity index 100% rename from osu.Game.Tournament.Tests/TeamWinTestCase.cs rename to osu.Game.Tournament.Tests/TestCaseTeamWin.cs diff --git a/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs b/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs index 135081a641..4225518d53 100644 --- a/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs +++ b/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; From c1e3c4d4359555ddf32f4c96ac4037277dca37c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 10:40:44 +0900 Subject: [PATCH 0278/2854] Make chat tests work again, clear old messages --- .../TestCaseMatchChatDisplay.cs | 4 ++++ .../Components/MatchChatDisplay.cs | 22 ++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs b/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs index aab56d1714..cc053f8a7e 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Game.Online.Chat; using osu.Game.Tests.Visual; using osu.Game.Tournament.Components; +using osu.Game.Tournament.IPC; using osu.Game.Tournament.Screens.Ladder.Components; using osu.Game.Users; using OpenTK; @@ -39,6 +40,9 @@ namespace osu.Game.Tournament.Tests [Cached] private LadderInfo ladderInfo = new LadderInfo(); + [Cached] + private MatchIPCInfo matchInfo = new MatchIPCInfo(); // hide parent + public TestCaseMatchChatDisplay() { MatchChatDisplay chatDisplay; diff --git a/osu.Game.Tournament/Components/MatchChatDisplay.cs b/osu.Game.Tournament/Components/MatchChatDisplay.cs index c44eb88ef8..dc8f28d9bd 100644 --- a/osu.Game.Tournament/Components/MatchChatDisplay.cs +++ b/osu.Game.Tournament/Components/MatchChatDisplay.cs @@ -61,10 +61,6 @@ namespace osu.Game.Tournament.Components { if (ipc != null) { - AddInternal(manager = new ChannelManager()); - - Channel.BindTo(manager.CurrentChannel); - chatChannel.BindTo(ipc.ChatChannel); chatChannel.BindValueChanged(channelString => { @@ -73,7 +69,18 @@ namespace osu.Game.Tournament.Components int id = int.Parse(channelString); - var channel = manager.JoinedChannels.FirstOrDefault(ch => ch.Id == id) ?? new Channel + if (id <= 0) return; + + if (manager == null) + { + AddInternal(manager = new ChannelManager()); + Channel.BindTo(manager.CurrentChannel); + } + + foreach (var ch in manager.JoinedChannels.ToList()) + manager.LeaveChannel(ch); + + var channel = new Channel { Id = id, Type = ChannelType.Public @@ -81,7 +88,6 @@ namespace osu.Game.Tournament.Components manager.JoinChannel(channel); manager.CurrentChannel.Value = channel; - }, true); } } @@ -92,6 +98,10 @@ namespace osu.Game.Tournament.Components lastChannel.NewMessagesArrived -= newMessages; lastChannel = channel; + messagesFlow.Clear(); + + if (channel == null) return; + channel.NewMessagesArrived += newMessages; } From 2ee77670ee7682da1ef522bc666eabd32bd6f757 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 12:04:19 +0900 Subject: [PATCH 0279/2854] Add date entry for groupings --- .../Groupings/GroupingsEditorScreen.cs | 89 ++++++++++++++----- 1 file changed, 69 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs b/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs index 1b7e9e6bd1..b0bd17fe83 100644 --- a/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs @@ -4,8 +4,11 @@ using System; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; using osu.Game.Tournament.Screens.Ladder.Components; @@ -18,24 +21,36 @@ namespace osu.Game.Tournament.Screens.Groupings public GroupingsEditorScreen() { - Add(new FillFlowContainer + AddRange(new Drawable[] { - Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + new Box { - items = new FillFlowContainer + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.2f), + }, + new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.Both, + Width = 0.9f, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Children = new Drawable[] { - Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, - new TriangleButton - { - Width = 100, - Text = "Add", - Action = addNew - }, + items = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + new TriangleButton + { + Margin = new MarginPadding(20), + Width = 100, + Text = "Add", + Action = addNew + }, + } } }); } @@ -55,11 +70,11 @@ namespace osu.Game.Tournament.Screens.Groupings private void addNew() { - items.Add(new GroupingRow(new TournamentGrouping(), updateGroupings)); + items.Add(new GroupingRow(new TournamentGrouping { StartDate = { Value = DateTimeOffset.UtcNow } }, updateGroupings)); updateGroupings(); } - private void updateGroupings() + private void updateGroupings() { LadderInfo.Groupings = items.Children.Select(c => c.Grouping).ToList(); } @@ -75,8 +90,9 @@ namespace osu.Game.Tournament.Screens.Groupings { new FillFlowContainer { - Direction = FillDirection.Horizontal, - RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Children = new Drawable[] { new SettingsTextBox { Width = 0.3f, Bindable = Grouping.Name }, @@ -92,12 +108,45 @@ namespace osu.Game.Tournament.Screens.Groupings onDelete(); } }, + new DateTextBox { Width = 0.3f, Bindable = Grouping.StartDate }, } } }; RelativeSizeAxes = Axes.X; - Height = 40; + AutoSizeAxes = Axes.Y; + } + } + } + + public class DateTextBox : SettingsTextBox + { + public DateTextBox() + { + base.Bindable = new Bindable(); + ((OsuTextBox)Control).OnCommit = (sender, newText) => { + try + { + bindable.Value = DateTimeOffset.Parse(sender.Text); + } + catch + { + bindable.TriggerChange(); + } + }; + } + + // hold a reference to the provided bindable so we don't have to in every settings section. + private Bindable bindable; + + public new Bindable Bindable + { + get { return bindable; } + + set + { + bindable = value; + bindable.BindValueChanged(dto => base.Bindable.Value = dto.ToUniversalTime().ToString(), true); } } } From 87243a72d30f1d0b06368f0e63ac4df2cff1ce1b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 12:14:15 +0900 Subject: [PATCH 0280/2854] Add date entry for pairings --- .../Ladder/Components/LadderEditorSettings.cs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index 4334f75728..34b16ccab3 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 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.Configuration; @@ -11,6 +12,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; using osu.Game.Screens.Play.PlayerSettings; +using osu.Game.Tournament.Screens.Groupings; namespace osu.Game.Tournament.Screens.Ladder.Components { @@ -24,6 +26,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private OsuTextBox textboxTeam2; private SettingsDropdown groupingDropdown; private PlayerCheckbox losersCheckbox; + private DateTextBox dateTimeBox; [Resolved] private LadderEditorInfo editorInfo { get; set; } @@ -81,6 +84,10 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { LabelText = "Losers Bracket", Bindable = new Bindable() + }, + dateTimeBox = new DateTextBox + { + Bindable = new Bindable() } }; @@ -90,6 +97,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components textboxTeam2.Text = selection?.Team2.Value?.Acronym; groupingDropdown.Bindable.Value = selection?.Grouping.Value ?? groupingOptions.First(); losersCheckbox.Current.Value = selection?.Losers.Value ?? false; + dateTimeBox.Bindable.Value = selection?.Date.Value ?? DateTimeOffset.UtcNow; }; textboxTeam1.OnCommit = (val, newText) => @@ -107,7 +115,14 @@ namespace osu.Game.Tournament.Screens.Ladder.Components groupingDropdown.Bindable.ValueChanged += grouping => { if (editorInfo.Selected.Value != null) + { editorInfo.Selected.Value.Grouping.Value = grouping; + if (editorInfo.Selected.Value.Date.Value < grouping.StartDate.Value) + { + editorInfo.Selected.Value.Date.Value = grouping.StartDate.Value; + editorInfo.Selected.TriggerChange(); + } + } }; losersCheckbox.Current.ValueChanged += losers => @@ -115,6 +130,18 @@ namespace osu.Game.Tournament.Screens.Ladder.Components if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.Losers.Value = losers; }; + + dateTimeBox.Bindable.ValueChanged += date => + { + if (editorInfo.Selected.Value != null) + editorInfo.Selected.Value.Date.Value = date; + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + this.FadeIn(); } protected override bool OnHover(HoverEvent e) @@ -125,6 +152,5 @@ namespace osu.Game.Tournament.Screens.Ladder.Components protected override void OnHoverLost(HoverLostEvent e) { } - } } From 7f7d4ef442daab22e6b2c20440b405bf5b4adb83 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 13:36:36 +0900 Subject: [PATCH 0281/2854] =?UTF-8?q?Don=E2=80=99t=20hard=20fail=20if=20st?= =?UTF-8?q?able=20install=20is=20missing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- osu.Game.Tournament/IPC/FileBasedIPC.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 87c6fb4c83..9cc275e9e0 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -27,7 +27,18 @@ namespace osu.Game.Tournament.IPC [BackgroundDependencyLoader] private void load() { - var stable = new StableStorage(); + + StableStorage stable; + + try + { + stable = new StableStorage(); + } + catch + { + Logger.Log("Stable installation could not be found; disabling file based IPC"); + return; + } const string file_ipc_filename = "ipc.txt"; const string file_ipc_state_filename = "ipc-state.txt"; From c1e1306c976fe0cf99529945647f01ac4737e7ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 13:39:55 +0900 Subject: [PATCH 0282/2854] Show controls above screens --- .../Screens/TournamentSceneManager.cs | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index 0205838fc2..f3bc12a6cf 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -43,41 +43,6 @@ namespace osu.Game.Tournament.Screens { Children = new Drawable[] { - new Container - { - RelativeSizeAxes = Axes.Y, - Width = 200, - Children = new Drawable[] - { - new Box - { - Colour = Color4.Black, - RelativeSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket Editor", Action = () => setScreen(bracketEditor) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Groupings Editor", Action = () => setScreen(groupingsEditor) }, - new Container { RelativeSizeAxes = Axes.X, Height = 50 }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Drawings", Action = () => setScreen(drawings) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Showcase", Action = () => setScreen(showcase) }, - new Container { RelativeSizeAxes = Axes.X, Height = 50 }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Schedule", Action = () => setScreen(schedule) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket", Action = () => setScreen(bracket) }, - new Container { RelativeSizeAxes = Axes.X, Height = 50 }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "TeamIntro", Action = () => setScreen(teamIntro) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "MapPool", Action = () => setScreen(mapPool) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Gameplay", Action = () => setScreen(gameplay) }, - new Container { RelativeSizeAxes = Axes.X, Height = 50 }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Win", Action = () => setScreen(winner) }, - } - }, - }, - }, new Container { RelativeSizeAxes = Axes.Both, @@ -115,6 +80,41 @@ namespace osu.Game.Tournament.Screens }, } }, + new Container + { + RelativeSizeAxes = Axes.Y, + Width = 200, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket Editor", Action = () => setScreen(bracketEditor) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Groupings Editor", Action = () => setScreen(groupingsEditor) }, + new Container { RelativeSizeAxes = Axes.X, Height = 50 }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Drawings", Action = () => setScreen(drawings) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Showcase", Action = () => setScreen(showcase) }, + new Container { RelativeSizeAxes = Axes.X, Height = 50 }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Schedule", Action = () => setScreen(schedule) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket", Action = () => setScreen(bracket) }, + new Container { RelativeSizeAxes = Axes.X, Height = 50 }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "TeamIntro", Action = () => setScreen(teamIntro) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "MapPool", Action = () => setScreen(mapPool) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Gameplay", Action = () => setScreen(gameplay) }, + new Container { RelativeSizeAxes = Axes.X, Height = 50 }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Win", Action = () => setScreen(winner) }, + } + }, + }, + }, }; setScreen(teamIntro); From bed967b456ceeb38b1d46102983e8fc0e05356ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 14:05:22 +0900 Subject: [PATCH 0283/2854] Add better date string formatting --- osu.Game.Tournament/Components/DateTextBox.cs | 44 +++++++++++++++++++ .../Groupings/GroupingsEditorScreen.cs | 36 +-------------- .../Ladder/Components/LadderEditorSettings.cs | 2 +- 3 files changed, 47 insertions(+), 35 deletions(-) create mode 100644 osu.Game.Tournament/Components/DateTextBox.cs diff --git a/osu.Game.Tournament/Components/DateTextBox.cs b/osu.Game.Tournament/Components/DateTextBox.cs new file mode 100644 index 0000000000..171196f348 --- /dev/null +++ b/osu.Game.Tournament/Components/DateTextBox.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Configuration; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; + +namespace osu.Game.Tournament.Components +{ + public class DateTextBox : SettingsTextBox + { + public new Bindable Bindable + { + get { return bindable; } + + set + { + bindable = value; + bindable.BindValueChanged(dto => + base.Bindable.Value = dto.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"), true); + } + } + + // hold a reference to the provided bindable so we don't have to in every settings section. + private Bindable bindable; + + public DateTextBox() + { + base.Bindable = new Bindable(); + ((OsuTextBox)Control).OnCommit = (sender, newText) => + { + try + { + bindable.Value = DateTimeOffset.Parse(sender.Text); + } + catch + { + bindable.TriggerChange(); + } + }; + } + } +} diff --git a/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs b/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs index b0bd17fe83..d5d3d1dbda 100644 --- a/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs @@ -4,13 +4,13 @@ using System; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; +using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament.Screens.Groupings @@ -74,7 +74,7 @@ namespace osu.Game.Tournament.Screens.Groupings updateGroupings(); } - private void updateGroupings() + private void updateGroupings() { LadderInfo.Groupings = items.Children.Select(c => c.Grouping).ToList(); } @@ -118,36 +118,4 @@ namespace osu.Game.Tournament.Screens.Groupings } } } - - public class DateTextBox : SettingsTextBox - { - public DateTextBox() - { - base.Bindable = new Bindable(); - ((OsuTextBox)Control).OnCommit = (sender, newText) => { - try - { - bindable.Value = DateTimeOffset.Parse(sender.Text); - } - catch - { - bindable.TriggerChange(); - } - }; - } - - // hold a reference to the provided bindable so we don't have to in every settings section. - private Bindable bindable; - - public new Bindable Bindable - { - get { return bindable; } - - set - { - bindable = value; - bindable.BindValueChanged(dto => base.Bindable.Value = dto.ToUniversalTime().ToString(), true); - } - } - } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index 34b16ccab3..bb80a845e3 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -12,7 +12,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; using osu.Game.Screens.Play.PlayerSettings; -using osu.Game.Tournament.Screens.Groupings; +using osu.Game.Tournament.Components; namespace osu.Game.Tournament.Screens.Ladder.Components { From 81f39c2f399f7a4e527cda0b70205b4453e62059 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 14:35:40 +0900 Subject: [PATCH 0284/2854] Don't show matches on schedule which don't have teams set --- osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index a0c36cd6c0..325a6105cf 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -75,7 +75,7 @@ namespace osu.Game.Tournament.Screens.Schedule RelativeSizeAxes = Axes.Both, Width = 0.4f, ChildrenEnumerable = ladder.Pairings - .Where(p => p.Completed.Value) + .Where(p => p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null) .OrderByDescending(p => p.Date.Value) .Take(8) .Select(p => new SchedulePairing(p)) @@ -85,7 +85,7 @@ namespace osu.Game.Tournament.Screens.Schedule RelativeSizeAxes = Axes.Both, Width = 0.6f, ChildrenEnumerable = ladder.Pairings - .Where(p => !p.Completed.Value) + .Where(p => !p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null) .OrderBy(p => p.Date.Value) .Take(8) .Select(p => new SchedulePairing(p)) From 71184c602f1d9c1bf3023ce955c004d830328428 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 14:55:27 +0900 Subject: [PATCH 0285/2854] Show times on schedule --- .../Screens/Schedule/ScheduleScreen.cs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index 325a6105cf..4be24a65f7 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Video; using osu.Framework.Platform; +using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Tournament.Screens.Ladder.Components; using OpenTK; @@ -107,7 +108,7 @@ namespace osu.Game.Tournament.Screens.Schedule Colour = Color4.Black, TextSize = 20 }, - new SchedulePairing(currentMatch), + new SchedulePairing(currentMatch, false), new OsuSpriteText { Text = "Start Time " + pairing.Date.Value.ToUniversalTime().ToString("HH:mm UTC"), @@ -122,10 +123,29 @@ namespace osu.Game.Tournament.Screens.Schedule public class SchedulePairing : DrawableMatchPairing { - public SchedulePairing(MatchPairing pairing) + public SchedulePairing(MatchPairing pairing, bool showTimestamp = true) : base(pairing) { Flow.Direction = FillDirection.Horizontal; + + if (showTimestamp) + { + AddInternal(new DrawableDate(Pairing.Date.Value) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopLeft, + Colour = Color4.Black, + Margin = new MarginPadding { Horizontal = 10, Vertical = 5 }, + }); + AddInternal(new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomLeft, + Colour = Color4.Black, + Margin = new MarginPadding { Horizontal = 10, Vertical = 5 }, + Text = pairing.Date.Value.ToUniversalTime().ToString("HH:mm UTC") + }); + } } } From 5659ba6ef8d8dba5aef6cb3689394a3cadc59316 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 14:59:37 +0900 Subject: [PATCH 0286/2854] Add logo to ladder screen --- osu.Game.Tournament/Screens/Ladder/LadderScreen.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs index 5e0cb13375..76f65fd87f 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -8,13 +8,15 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Lines; +using osu.Framework.Graphics.Video; +using osu.Framework.Platform; using osu.Game.Graphics; using osu.Game.Tournament.Screens.Ladder.Components; using OpenTK.Graphics; namespace osu.Game.Tournament.Screens.Ladder { - public class LadderScreen : TournamentScreen + public class LadderScreen : TournamentScreen, IProvideVideo { protected Container PairingsContainer; private Container paths; @@ -23,7 +25,7 @@ namespace osu.Game.Tournament.Screens.Ladder protected ScrollableContainer ScrollContent; [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, Storage storage) { normalPathColour = colours.BlueDarker.Darken(2); losersPathColour = colours.YellowDarker.Darken(2); @@ -35,6 +37,11 @@ namespace osu.Game.Tournament.Screens.Ladder RelativeSizeAxes = Axes.Both, Children = new Drawable[] { + new VideoSprite(storage.GetStream(@"BG Side Logo - OWC.m4v")) + { + RelativeSizeAxes = Axes.Both, + Loop = true, + }, ScrollContent = new ScrollableContainer { RelativeSizeAxes = Axes.Both, From aebece3d89c533a9973b1552ac357f6744fab26a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 15:18:22 +0900 Subject: [PATCH 0287/2854] Use already populated beatmap values if available --- osu.Game.Tournament/IPC/FileBasedIPC.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 9cc275e9e0..4bf2cfee38 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Framework.Platform.Windows; @@ -25,9 +26,8 @@ namespace osu.Game.Tournament.IPC private int lastBeatmapId; [BackgroundDependencyLoader] - private void load() + private void load(LadderInfo ladder) { - StableStorage stable; try @@ -58,9 +58,17 @@ namespace osu.Game.Tournament.IPC if (lastBeatmapId != beatmapId) { lastBeatmapId = beatmapId; - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); - req.Success += b => Beatmap.Value = b.ToBeatmap(Rulesets); - API.Queue(req); + + var existing = ladder.CurrentMatch.Value?.Grouping.Value?.Beatmaps.FirstOrDefault(b => b.ID == beatmapId && b.BeatmapInfo != null); + + if (existing != null) + Beatmap.Value = existing.BeatmapInfo; + else + { + var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); + req.Success += b => Beatmap.Value = b.ToBeatmap(Rulesets); + API.Queue(req); + } } Mods.Value = (LegacyMods)mods; From 49e155c2c0ecb6dea2eec2bd2443969e192c9e7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 15:31:03 +0900 Subject: [PATCH 0288/2854] Fix chat not appearing at ranking --- .../Screens/Gameplay/GameplayScreen.cs | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 3c6ea39ae7..696abddded 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -86,6 +86,7 @@ namespace osu.Game.Tournament.Screens.Gameplay chat = new MatchChatDisplay { RelativeSizeAxes = Axes.X, + Y = 100, Size = new Vector2(0.45f, 120), Margin = new MarginPadding(10), Anchor = Anchor.BottomCentre, @@ -137,26 +138,34 @@ namespace osu.Game.Tournament.Screens.Gameplay scheduledBarContract?.Cancel(); + void expand() + { + chat.FadeOut(200); + chat.MoveToY(100, 500, Easing.In); + using (SongBar.BeginDelayedSequence(300, true)) + SongBar.Expanded = true; + } + + void contract() + { + SongBar.Expanded = false; + using (chat.BeginDelayedSequence(500)) + { + chat.FadeIn(300); + chat.MoveToY(0, 500, Easing.OutQuint); + } + } + switch (state) { case TourneyState.Idle: - // show chat - SongBar.Expanded = false; - using (chat.BeginDelayedSequence(500)) - { - chat.FadeIn(300); - chat.MoveToY(100).MoveToY(0, 500, Easing.OutQuint); - } - + contract(); break; case TourneyState.Ranking: - scheduledBarContract = Scheduler.AddDelayed(() => SongBar.Expanded = false, 15000); + scheduledBarContract = Scheduler.AddDelayed(contract, 10000); break; default: - chat.FadeOut(200); - chat.MoveToY(100, 500, Easing.In); - using (SongBar.BeginDelayedSequence(300, true)) - SongBar.Expanded = true; + expand(); break; } } From 26286177d3ba6aa4c34e27b1fa0b13f20ec80700 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 15:35:14 +0900 Subject: [PATCH 0289/2854] Colour chat names --- osu.Game.Tournament/Components/MatchChatDisplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Components/MatchChatDisplay.cs b/osu.Game.Tournament/Components/MatchChatDisplay.cs index dc8f28d9bd..70f04593fa 100644 --- a/osu.Game.Tournament/Components/MatchChatDisplay.cs +++ b/osu.Game.Tournament/Components/MatchChatDisplay.cs @@ -199,11 +199,11 @@ namespace osu.Game.Tournament.Components } else if (info.CurrentMatch.Value.Team1.Value.Players.Any(u => u.Id == message.Sender.Id)) { - colourBox.Colour = red; + senderText.Colour = colourBox.Colour = red; } else if (info.CurrentMatch.Value.Team2.Value.Players.Any(u => u.Id == message.Sender.Id)) { - colourBox.Colour = blue; + senderText.Colour = colourBox.Colour = blue; } } } From 9f519d7002b589aa91fb272eb0c2499daa1784b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 15:44:56 +0900 Subject: [PATCH 0290/2854] Actually add match score display to gameplay screen --- .../Gameplay/Components/MatchScoreDisplay.cs | 7 ++++--- .../Screens/Gameplay/GameplayScreen.cs | 13 ++++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs index fe865a1728..f84fca5e04 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs @@ -35,6 +35,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components public MatchScoreDisplay() { RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; InternalChildren = new Drawable[] { @@ -102,12 +103,12 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components var diff = Math.Max(score1.Value, score2.Value) - Math.Min(score1.Value, score2.Value); losingBar.ResizeWidthTo(0, 400, Easing.OutQuint); - winningBar.ResizeWidthTo((float)Math.Pow(diff / 1000000f, 0.5), 400, Easing.OutQuint); + winningBar.ResizeWidthTo((float)Math.Pow(diff / 1000000f, 0.5) / 2, 400, Easing.OutQuint); } - protected override void UpdateAfterChildren() + protected override void Update() { - base.UpdateAfterChildren(); + base.Update(); score1Text.X = -Math.Max(5 + score1Text.DrawWidth / 2, score1Bar.DrawWidth); score2Text.X = Math.Max(5 + score2Text.DrawWidth / 2, score2Bar.DrawWidth); diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 696abddded..470b7ed479 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -92,6 +92,12 @@ namespace osu.Game.Tournament.Screens.Gameplay Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, }, + scoreDisplay = new MatchScoreDisplay + { + Y = -65, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }, new ControlPanel { Children = new Drawable[] @@ -123,6 +129,7 @@ namespace osu.Game.Tournament.Screens.Gameplay private ScheduledDelegate scheduledBarContract; private MatchChatDisplay chat; + private MatchScoreDisplay scoreDisplay; private void stateChanged(TourneyState state) { @@ -142,13 +149,17 @@ namespace osu.Game.Tournament.Screens.Gameplay { chat.FadeOut(200); chat.MoveToY(100, 500, Easing.In); - using (SongBar.BeginDelayedSequence(300, true)) + using (BeginDelayedSequence(300, true)) + { + scoreDisplay.FadeIn(100); SongBar.Expanded = true; + } } void contract() { SongBar.Expanded = false; + scoreDisplay.FadeOut(100); using (chat.BeginDelayedSequence(500)) { chat.FadeIn(300); From 4ae9413ee66b3ce1a026af2ff0d63bb2c6bb418f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 16:00:12 +0900 Subject: [PATCH 0291/2854] Add mod icons --- .../Components/TournamentBeatmapPanel.cs | 21 +++++++++++++++++-- .../Screens/MapPool/MapPoolScreen.cs | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 4456abb48e..aeb9eefd35 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -9,12 +9,17 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; using osu.Framework.Localisation; +using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Tournament.Screens.Ladder.Components; +using OpenTK; using OpenTK.Graphics; namespace osu.Game.Tournament.Components @@ -22,6 +27,7 @@ namespace osu.Game.Tournament.Components public class TournamentBeatmapPanel : CompositeDrawable { public readonly BeatmapInfo Beatmap; + private readonly string mods; private const float horizontal_padding = 10; private const float vertical_padding = 5; @@ -31,15 +37,16 @@ namespace osu.Game.Tournament.Components private readonly Bindable currentMatch = new Bindable(); private Box flash; - public TournamentBeatmapPanel(BeatmapInfo beatmap) + public TournamentBeatmapPanel(BeatmapInfo beatmap, string mods = null) { Beatmap = beatmap; + this.mods = mods; Width = 400; Height = HEIGHT; } [BackgroundDependencyLoader] - private void load(LadderInfo ladder) + private void load(LadderInfo ladder, Storage storage) { currentMatch.BindValueChanged(matchChanged); currentMatch.BindTo(ladder.CurrentMatch); @@ -126,6 +133,16 @@ namespace osu.Game.Tournament.Components Alpha = 0, }, }); + + if (!string.IsNullOrEmpty(mods)) + AddInternal(new Sprite + { + Texture = new TextureStore(new TextureLoaderStore(new StorageBackedResourceStore(storage))).Get($"mods/{mods}"), + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Margin = new MarginPadding(20), + Scale = new Vector2(0.5f) + }); } private void matchChanged(MatchPairing match) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 65f4bfb149..81a140d5c2 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -193,7 +193,7 @@ namespace osu.Game.Tournament.Screens.MapPool if (match.Grouping.Value != null) { foreach (var b in match.Grouping.Value.Beatmaps) - maps.Add(new TournamentBeatmapPanel(b.BeatmapInfo) + maps.Add(new TournamentBeatmapPanel(b.BeatmapInfo, b.Mods) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, From 852f0337dd070512e22e6e5a10ee734255a41f86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 16:06:43 +0900 Subject: [PATCH 0292/2854] Group map pool by mod type --- .../Screens/MapPool/MapPoolScreen.cs | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 81a140d5c2..14dd990ec0 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tournament.Screens.MapPool { public class MapPoolScreen : TournamentScreen { - private readonly FillFlowContainer maps; + private readonly FillFlowContainer> mapFlows; private readonly Bindable currentMatch = new Bindable(); @@ -39,12 +39,12 @@ namespace osu.Game.Tournament.Screens.MapPool InternalChildren = new Drawable[] { new MatchHeader(), - maps = new FillFlowContainer + mapFlows = new FillFlowContainer> { Y = 100, Spacing = new Vector2(10, 20), Padding = new MarginPadding(50), - Direction = FillDirection.Full, + Direction = FillDirection.Vertical, RelativeSizeAxes = Axes.Both, }, new ControlPanel @@ -136,7 +136,9 @@ namespace osu.Game.Tournament.Screens.MapPool protected override bool OnMouseDown(MouseDownEvent e) { - var map = maps.FirstOrDefault(m => m.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)); + var maps = mapFlows.Select(f => f.FirstOrDefault(m => m.ReceivePositionalInputAt(e.ScreenSpaceMousePosition))); + var map = maps.FirstOrDefault(m => m != null); + if (map != null) { if (e.Button == MouseButton.Left && map.Beatmap.OnlineBeatmapID != null) @@ -188,16 +190,34 @@ namespace osu.Game.Tournament.Screens.MapPool private void matchChanged(MatchPairing match) { - maps.Clear(); + mapFlows.Clear(); if (match.Grouping.Value != null) { + FillFlowContainer currentFlow = null; + string currentMod = null; + foreach (var b in match.Grouping.Value.Beatmaps) - maps.Add(new TournamentBeatmapPanel(b.BeatmapInfo, b.Mods) + { + if (currentFlow == null || currentMod != b.Mods) + { + mapFlows.Add(currentFlow = new FillFlowContainer + { + Spacing = new Vector2(10, 20), + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }); + + currentMod = b.Mods; + } + + currentFlow.Add(new TournamentBeatmapPanel(b.BeatmapInfo, b.Mods) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }); + } } } } From 8b820f7346f851cc95cecd4db61391493c48d056 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 21:26:56 +0900 Subject: [PATCH 0293/2854] Move channel reading to new ipc file --- osu.Game.Tournament/IPC/FileBasedIPC.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 4bf2cfee38..83dbe11ffa 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -43,6 +43,7 @@ namespace osu.Game.Tournament.IPC const string file_ipc_filename = "ipc.txt"; const string file_ipc_state_filename = "ipc-state.txt"; const string file_ipc_scores_filename = "ipc-scores.txt"; + const string file_ipc_channel_filename = "ipc-channel.txt"; if (stable.Exists(file_ipc_filename)) Scheduler.AddDelayed(delegate @@ -72,7 +73,6 @@ namespace osu.Game.Tournament.IPC } Mods.Value = (LegacyMods)mods; - ChatChannel.Value = sr.ReadLine(); } } catch @@ -80,6 +80,19 @@ namespace osu.Game.Tournament.IPC // file might be in use. } + try + { + using (var stream = stable.GetStream(file_ipc_channel_filename)) + using (var sr = new StreamReader(stream)) + { + ChatChannel.Value = sr.ReadLine(); + } + } + catch (Exception) + { + // file might be in use. + } + try { using (var stream = stable.GetStream(file_ipc_state_filename)) From 4b047ad9ccc9048454e871867ad6e05c4bda525f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 21:27:02 +0900 Subject: [PATCH 0294/2854] Centralise chat display --- .../Components/MatchChatDisplay.cs | 12 +++++++ .../Screens/Gameplay/GameplayScreen.cs | 24 ++++---------- .../Screens/TournamentSceneManager.cs | 33 ++++++++++++++++++- 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tournament/Components/MatchChatDisplay.cs b/osu.Game.Tournament/Components/MatchChatDisplay.cs index 70f04593fa..2c94e06fc3 100644 --- a/osu.Game.Tournament/Components/MatchChatDisplay.cs +++ b/osu.Game.Tournament/Components/MatchChatDisplay.cs @@ -207,5 +207,17 @@ namespace osu.Game.Tournament.Components } } } + + public void Contract() + { + this.FadeIn(300); + this.MoveToY(0, 500, Easing.OutQuint); + } + + public void Expand() + { + this.FadeOut(200); + this.MoveToY(100, 500, Easing.In); + } } } diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 470b7ed479..372087cfcf 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -13,7 +13,6 @@ using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Screens.Gameplay.Components; using osu.Game.Tournament.Screens.Ladder.Components; -using OpenTK; using OpenTK.Graphics; namespace osu.Game.Tournament.Screens.Gameplay @@ -32,9 +31,11 @@ namespace osu.Game.Tournament.Screens.Gameplay private readonly Color4 blue = new Color4(17, 136, 170, 255); [BackgroundDependencyLoader] - private void load(LadderInfo ladder, TextureStore textures, MatchIPCInfo ipc) + private void load(LadderInfo ladder, TextureStore textures, MatchIPCInfo ipc, MatchChatDisplay chat) { + this.chat = chat; this.ipc = ipc; + AddRange(new Drawable[] { new MatchHeader(), @@ -83,15 +84,6 @@ namespace osu.Game.Tournament.Screens.Gameplay }, } }, - chat = new MatchChatDisplay - { - RelativeSizeAxes = Axes.X, - Y = 100, - Size = new Vector2(0.45f, 120), - Margin = new MarginPadding(10), - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - }, scoreDisplay = new MatchScoreDisplay { Y = -65, @@ -147,8 +139,8 @@ namespace osu.Game.Tournament.Screens.Gameplay void expand() { - chat.FadeOut(200); - chat.MoveToY(100, 500, Easing.In); + chat.Expand(); + using (BeginDelayedSequence(300, true)) { scoreDisplay.FadeIn(100); @@ -161,10 +153,7 @@ namespace osu.Game.Tournament.Screens.Gameplay SongBar.Expanded = false; scoreDisplay.FadeOut(100); using (chat.BeginDelayedSequence(500)) - { - chat.FadeIn(300); - chat.MoveToY(0, 500, Easing.OutQuint); - } + chat.Contract(); } switch (state) @@ -176,6 +165,7 @@ namespace osu.Game.Tournament.Screens.Gameplay scheduledBarContract = Scheduler.AddDelayed(contract, 10000); break; default: + chat.Expand(); expand(); break; } diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index f3bc12a6cf..7947f94cf9 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Video; using osu.Framework.Platform; using osu.Game.Graphics.UserInterface; using osu.Game.Screens; +using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Drawings; using osu.Game.Tournament.Screens.Gameplay; using osu.Game.Tournament.Screens.Groupings; @@ -38,6 +39,20 @@ namespace osu.Game.Tournament.Screens private ShowcaseScreen showcase; private VideoSprite video; + //todo: make less temporary + [Cached] + private MatchChatDisplay chat = new MatchChatDisplay + { + RelativeSizeAxes = Axes.X, + Y = 100, + Size = new Vector2(0.45f, 120), + Margin = new MarginPadding(10), + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }; + + private Container chatContainer; + [BackgroundDependencyLoader] private void load(LadderInfo ladder, Storage storage) { @@ -48,7 +63,7 @@ namespace osu.Game.Tournament.Screens RelativeSizeAxes = Axes.Both, X = 200, FillMode = FillMode.Fit, - FillAspectRatio = 16/9f, + FillAspectRatio = 16 / 9f, Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, Size = new Vector2(0.8f, 1), @@ -78,6 +93,11 @@ namespace osu.Game.Tournament.Screens winner = new TeamWinScreen() } }, + chatContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Child = chat + }, } }, new Container @@ -135,6 +155,17 @@ namespace osu.Game.Tournament.Screens else s.Hide(); } + + switch (screen) + { + case GameplayScreen _: + case MapPoolScreen _: + chatContainer.FadeIn(100); + break; + default: + chatContainer.FadeOut(100); + break; + } } } } From 92bb92e9c0c11502ef9ba2ce017a3919122888c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Nov 2018 21:28:33 +0900 Subject: [PATCH 0295/2854] Use local framework --- osu.Game/osu.Game.csproj | 2 +- osu.sln | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8c47df654a..3022b66762 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -11,6 +11,7 @@ + @@ -18,7 +19,6 @@ - diff --git a/osu.sln b/osu.sln index f6ed7a5c42..c62fc02dba 100644 --- a/osu.sln +++ b/osu.sln @@ -31,6 +31,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tournament", "osu. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tournament.Tests", "osu.Game.Tournament.Tests\osu.Game.Tournament.Tests.csproj", "{5789E78D-38F9-4072-AB7B-978F34B2C17F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework", "..\osu-framework\osu.Framework\osu.Framework.csproj", "{7A69A230-45A1-4444-8C43-A582E4F48C1E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -93,6 +95,10 @@ Global {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Debug|Any CPU.Build.0 = Debug|Any CPU {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Release|Any CPU.ActiveCfg = Release|Any CPU {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Release|Any CPU.Build.0 = Release|Any CPU + {7A69A230-45A1-4444-8C43-A582E4F48C1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A69A230-45A1-4444-8C43-A582E4F48C1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A69A230-45A1-4444-8C43-A582E4F48C1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A69A230-45A1-4444-8C43-A582E4F48C1E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 584adaf77db9dff6579312ae1209ba049a9ad5c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 18 Nov 2018 07:54:53 +0900 Subject: [PATCH 0296/2854] Limit horizontal bounds of score display --- .../Screens/Gameplay/Components/MatchScoreDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs index f84fca5e04..5ec9eef6ca 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs @@ -103,7 +103,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components var diff = Math.Max(score1.Value, score2.Value) - Math.Min(score1.Value, score2.Value); losingBar.ResizeWidthTo(0, 400, Easing.OutQuint); - winningBar.ResizeWidthTo((float)Math.Pow(diff / 1000000f, 0.5) / 2, 400, Easing.OutQuint); + winningBar.ResizeWidthTo(Math.Min(0.4f, (float)Math.Pow(diff / 1000000f, 0.5) / 2), 400, Easing.OutQuint); } protected override void Update() From 70739ae5a0dbfc5675e734daa4f0cd8f8f58aeef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 18 Nov 2018 08:25:23 +0900 Subject: [PATCH 0297/2854] Reduce rate of scale of score display --- .../Screens/Gameplay/Components/MatchScoreDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs index 5ec9eef6ca..7bc430e587 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs @@ -103,7 +103,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components var diff = Math.Max(score1.Value, score2.Value) - Math.Min(score1.Value, score2.Value); losingBar.ResizeWidthTo(0, 400, Easing.OutQuint); - winningBar.ResizeWidthTo(Math.Min(0.4f, (float)Math.Pow(diff / 1000000f, 0.5) / 2), 400, Easing.OutQuint); + winningBar.ResizeWidthTo(Math.Min(0.4f, (float)Math.Pow(diff / 1500000f, 0.5) / 2), 400, Easing.OutQuint); } protected override void Update() From e6529eac3e1ae04b1fc61214b205c51bc9ef4f31 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 18 Nov 2018 08:30:17 +0900 Subject: [PATCH 0298/2854] Add automation of map pool and win screen workflows --- .../Screens/Gameplay/GameplayScreen.cs | 24 +++++++- .../Screens/TournamentSceneManager.cs | 60 +++++++++---------- 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 372087cfcf..7517e83160 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -13,6 +13,8 @@ using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Screens.Gameplay.Components; using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Tournament.Screens.MapPool; +using osu.Game.Tournament.Screens.TeamWin; using OpenTK.Graphics; namespace osu.Game.Tournament.Screens.Gameplay @@ -30,6 +32,9 @@ namespace osu.Game.Tournament.Screens.Gameplay private readonly Color4 red = new Color4(186, 0, 18, 255); private readonly Color4 blue = new Color4(17, 136, 170, 255); + [Resolved] + private TournamentSceneManager sceneManager { get; set; } + [BackgroundDependencyLoader] private void load(LadderInfo ladder, TextureStore textures, MatchIPCInfo ipc, MatchChatDisplay chat) { @@ -119,10 +124,12 @@ namespace osu.Game.Tournament.Screens.Gameplay warmup.BindValueChanged(w => warmupButton.Alpha = !w ? 0.5f : 1, true); } - private ScheduledDelegate scheduledBarContract; + private ScheduledDelegate scheduledOperation; private MatchChatDisplay chat; private MatchScoreDisplay scoreDisplay; + private TourneyState lastState; + private void stateChanged(TourneyState state) { if (state == TourneyState.Ranking) @@ -135,7 +142,7 @@ namespace osu.Game.Tournament.Screens.Gameplay currentMatch.Value.Team2Score.Value++; } - scheduledBarContract?.Cancel(); + scheduledOperation?.Cancel(); void expand() { @@ -160,15 +167,26 @@ namespace osu.Game.Tournament.Screens.Gameplay { case TourneyState.Idle: contract(); + + if (lastState == TourneyState.Ranking) + { + if (currentMatch.Value?.Completed == true) + scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(TeamWinScreen)); }, 4000); + else if (currentMatch.Value?.Completed == false) + scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(MapPoolScreen)); }, 4000); + } + break; case TourneyState.Ranking: - scheduledBarContract = Scheduler.AddDelayed(contract, 10000); + scheduledOperation = Scheduler.AddDelayed(contract, 10000); break; default: chat.Expand(); expand(); break; } + + lastState = state; } } } diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index 7947f94cf9..ca3df2627a 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2018 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.Graphics; using osu.Framework.Graphics.Containers; @@ -24,19 +26,10 @@ using OpenTK.Graphics; namespace osu.Game.Tournament.Screens { + [Cached] public class TournamentSceneManager : OsuScreen { - private ScheduleScreen schedule; - private LadderScreen bracket; - private LadderEditorScreen bracketEditor; - private GroupingsEditorScreen groupingsEditor; - private MapPoolScreen mapPool; - private GameplayScreen gameplay; - private TeamWinScreen winner; - private TeamIntroScreen teamIntro; - private DrawingsScreen drawings; private Container screens; - private ShowcaseScreen showcase; private VideoSprite video; //todo: make less temporary @@ -81,16 +74,16 @@ namespace osu.Game.Tournament.Screens RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - schedule = new ScheduleScreen(), - bracket = new LadderScreen(), - bracketEditor = new LadderEditorScreen(), - groupingsEditor = new GroupingsEditorScreen(), - showcase = new ShowcaseScreen(), - mapPool = new MapPoolScreen(), - teamIntro = new TeamIntroScreen(), - drawings = new DrawingsScreen(), - gameplay = new GameplayScreen(), - winner = new TeamWinScreen() + new ScheduleScreen(), + new LadderScreen(), + new LadderEditorScreen(), + new GroupingsEditorScreen(), + new ShowcaseScreen(), + new MapPoolScreen(), + new TeamIntroScreen(), + new DrawingsScreen(), + new GameplayScreen(), + new TeamWinScreen() } }, chatContainer = new Container @@ -117,31 +110,34 @@ namespace osu.Game.Tournament.Screens Direction = FillDirection.Vertical, Children = new Drawable[] { - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket Editor", Action = () => setScreen(bracketEditor) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Groupings Editor", Action = () => setScreen(groupingsEditor) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket Editor", Action = () => SetScreen(typeof(LadderEditorScreen)) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Groupings Editor", Action = () => SetScreen(typeof(GroupingsEditorScreen)) }, new Container { RelativeSizeAxes = Axes.X, Height = 50 }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Drawings", Action = () => setScreen(drawings) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Showcase", Action = () => setScreen(showcase) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Drawings", Action = () => SetScreen(typeof(DrawingsScreen)) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Showcase", Action = () => SetScreen(typeof(ShowcaseScreen)) }, new Container { RelativeSizeAxes = Axes.X, Height = 50 }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Schedule", Action = () => setScreen(schedule) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket", Action = () => setScreen(bracket) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Schedule", Action = () => SetScreen(typeof(ScheduleScreen)) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket", Action = () => SetScreen(typeof(LadderScreen)) }, new Container { RelativeSizeAxes = Axes.X, Height = 50 }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "TeamIntro", Action = () => setScreen(teamIntro) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "MapPool", Action = () => setScreen(mapPool) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Gameplay", Action = () => setScreen(gameplay) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "TeamIntro", Action = () => SetScreen(typeof(TeamIntroScreen)) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "MapPool", Action = () => SetScreen(typeof(MapPoolScreen)) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Gameplay", Action = () => SetScreen(typeof(GameplayScreen)) }, new Container { RelativeSizeAxes = Axes.X, Height = 50 }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Win", Action = () => setScreen(winner) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Win", Action = () => SetScreen(typeof(TeamWinScreen)) }, } }, }, }, }; - setScreen(teamIntro); + SetScreen(typeof(ScheduleScreen)); } - private void setScreen(Drawable screen) + public void SetScreen(Type screenType) { + var screen = screens.FirstOrDefault(s => s.GetType() == screenType); + if (screen == null) return; + foreach (var s in screens.Children) { if (s == screen) From 06b08cd82dafee45a767b6fe03b884ecbe4ec558 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 18 Nov 2018 09:29:18 +0900 Subject: [PATCH 0299/2854] Automate return to gameplay after map pool selection --- .../Screens/MapPool/MapPoolScreen.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 14dd990ec0..a0670fcb87 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -7,11 +7,13 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; +using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; +using osu.Game.Tournament.Screens.Gameplay; using osu.Game.Tournament.Screens.Gameplay.Components; using osu.Game.Tournament.Screens.Ladder.Components; using OpenTK; @@ -165,6 +167,11 @@ namespace osu.Game.Tournament.Screens.MapPool setNextMode(); } + [Resolved] + private TournamentSceneManager sceneManager { get; set; } + + private ScheduledDelegate scheduledChange; + private void addForBeatmap(int beatmapId) { if (currentMatch.Value == null) @@ -186,6 +193,12 @@ namespace osu.Game.Tournament.Screens.MapPool }); setNextMode(); + + if (pickType == ChoiceType.Pick) + { + scheduledChange?.Cancel(); + scheduledChange = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(GameplayScreen)); }, 10000); + } } private void matchChanged(MatchPairing match) From 256d11620fd4be3f700d86616ec59ecfe0fd4693 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 18 Nov 2018 12:38:48 +0900 Subject: [PATCH 0300/2854] Add missing space after "CS" --- osu.Game.Tournament/Components/SongBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 53dec8e0a4..8e266aec28 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -188,7 +188,7 @@ namespace osu.Game.Tournament.Components }, new OsuSpriteText { - Text = $"CS{beatmap.BaseDifficulty.CircleSize:0.#} / AR {ar:0.#}{extra}", + Text = $"CS {beatmap.BaseDifficulty.CircleSize:0.#} / AR {ar:0.#}{extra}", Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, Colour = OsuColour.Gray(0.33f), Anchor = Anchor.TopRight, From 925532508cdac1f94066cf18e2d3c54a80550f1f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 18 Nov 2018 12:39:17 +0900 Subject: [PATCH 0301/2854] Change precedence of chat colouring --- osu.Game.Tournament/Components/MatchChatDisplay.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tournament/Components/MatchChatDisplay.cs b/osu.Game.Tournament/Components/MatchChatDisplay.cs index 2c94e06fc3..f80b73c4f6 100644 --- a/osu.Game.Tournament/Components/MatchChatDisplay.cs +++ b/osu.Game.Tournament/Components/MatchChatDisplay.cs @@ -193,17 +193,17 @@ namespace osu.Game.Tournament.Components }, }; - if (message.Sender.Colour != null) + if (info.CurrentMatch.Value.Team1.Value.Players.Any(u => u.Id == message.Sender.Id)) { - senderText.Colour = colourBox.Colour = OsuColour.FromHex(message.Sender.Colour); - } - else if (info.CurrentMatch.Value.Team1.Value.Players.Any(u => u.Id == message.Sender.Id)) - { - senderText.Colour = colourBox.Colour = red; + colourBox.Colour = red; } else if (info.CurrentMatch.Value.Team2.Value.Players.Any(u => u.Id == message.Sender.Id)) { - senderText.Colour = colourBox.Colour = blue; + colourBox.Colour = blue; + } + else if (message.Sender.Colour != null) + { + senderText.Colour = colourBox.Colour = OsuColour.FromHex(message.Sender.Colour); } } } From 5621101d40208d0a1cd8b426b464117609a83902 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 18 Nov 2018 12:39:25 +0900 Subject: [PATCH 0302/2854] Adjust gameplay screen size --- .../Screens/Gameplay/GameplayScreen.cs | 13 ++++++++----- .../Screens/TournamentSceneManager.cs | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 7517e83160..77dd2fc8d3 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -15,6 +15,7 @@ using osu.Game.Tournament.Screens.Gameplay.Components; using osu.Game.Tournament.Screens.Ladder.Components; using osu.Game.Tournament.Screens.MapPool; using osu.Game.Tournament.Screens.TeamWin; +using OpenTK; using OpenTK.Graphics; namespace osu.Game.Tournament.Screens.Gameplay @@ -48,6 +49,7 @@ namespace osu.Game.Tournament.Screens.Gameplay { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + Y = 5, Direction = FillDirection.Vertical, Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -63,7 +65,7 @@ namespace osu.Game.Tournament.Screens.Gameplay { Name = "top bar red", RelativeSizeAxes = Axes.X, - Height = 10, + Height = 8, Width = 0.5f, Colour = red, }, @@ -71,7 +73,7 @@ namespace osu.Game.Tournament.Screens.Gameplay { Name = "top bar blue", RelativeSizeAxes = Axes.X, - Height = 10, + Height = 9, Width = 0.5f, Colour = blue, Anchor = Anchor.TopRight, @@ -84,14 +86,15 @@ namespace osu.Game.Tournament.Screens.Gameplay // chroma key area for stable gameplay Name = "chroma", RelativeSizeAxes = Axes.X, - Height = 480, + Height = 500, Colour = new Color4(0, 255, 0, 255), }, } }, scoreDisplay = new MatchScoreDisplay { - Y = -65, + Y = -60, + Scale = new Vector2(0.86f), Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, }, @@ -168,7 +171,7 @@ namespace osu.Game.Tournament.Screens.Gameplay case TourneyState.Idle: contract(); - if (lastState == TourneyState.Ranking) + if (lastState == TourneyState.Ranking && !warmup.Value) { if (currentMatch.Value?.Completed == true) scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(TeamWinScreen)); }, 4000); diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index ca3df2627a..28066608d1 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tournament.Screens { RelativeSizeAxes = Axes.X, Y = 100, - Size = new Vector2(0.45f, 120), + Size = new Vector2(0.45f, 112), Margin = new MarginPadding(10), Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, From 8fab241ce3afe8e5aeab24d7c22ce39a74cf3d54 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 18 Nov 2018 21:15:29 +0900 Subject: [PATCH 0303/2854] Don't show matches on showcase which are more than four days away --- osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index 4be24a65f7..6781bd78e1 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 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.Configuration; @@ -76,7 +77,7 @@ namespace osu.Game.Tournament.Screens.Schedule RelativeSizeAxes = Axes.Both, Width = 0.4f, ChildrenEnumerable = ladder.Pairings - .Where(p => p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null) + .Where(p => p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4) .OrderByDescending(p => p.Date.Value) .Take(8) .Select(p => new SchedulePairing(p)) @@ -86,7 +87,7 @@ namespace osu.Game.Tournament.Screens.Schedule RelativeSizeAxes = Axes.Both, Width = 0.6f, ChildrenEnumerable = ladder.Pairings - .Where(p => !p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null) + .Where(p => !p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4) .OrderBy(p => p.Date.Value) .Take(8) .Select(p => new SchedulePairing(p)) From 7d3bcdfc1795d46b5758cf0bdc800fa97f1ce8a4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Nov 2018 13:17:51 +0900 Subject: [PATCH 0304/2854] Fix stats not displaying on showcase screen --- osu.Game.Tournament/Components/SongBar.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index aee16c72c4..04c4927fc1 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -53,6 +53,7 @@ namespace osu.Game.Tournament.Components private float panelWidth => expanded ? 0.6f : 1; private const float main_width = 0.97f; + private const float inner_panel_width = 0.7f; private bool expanded; @@ -66,7 +67,7 @@ namespace osu.Game.Tournament.Components if (expanded) { - innerPanel.ResizeWidthTo(0.7f, 800, Easing.OutQuint); + innerPanel.ResizeWidthTo(inner_panel_width, 800, Easing.OutQuint); outerPanel.ResizeWidthTo(main_width, 800, Easing.OutQuint); } else @@ -126,7 +127,7 @@ namespace osu.Game.Tournament.Components Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Width = 0.7f, + Width = inner_panel_width, Children = new Drawable[] { new Box @@ -143,6 +144,8 @@ namespace osu.Game.Tournament.Components } } }; + + Expanded = true; } private void update() From cbf82f892d697be8a11ad77acf7f4a60f8010f2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Nov 2018 14:10:33 +0900 Subject: [PATCH 0305/2854] Add OD display and fix "*" difficuty modifier display --- osu.Game.Tournament/Components/SongBar.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 04c4927fc1..3d9fe2f5ee 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -158,17 +158,22 @@ namespace osu.Game.Tournament.Components var bpm = beatmap.BeatmapSet.OnlineInfo.BPM; var length = beatmap.OnlineInfo.Length; - string extra = ""; + string hardRockExtra = ""; + string srExtra = ""; var ar = beatmap.BaseDifficulty.ApproachRate; - if ((mods & LegacyMods.HardRock) > 0) extra = "*"; + if ((mods & LegacyMods.HardRock) > 0) + { + hardRockExtra = "*"; + srExtra = "*"; + } if ((mods & LegacyMods.DoubleTime) > 0) { //ar *= 1.5f; bpm *= 1.5f; length /= 1.5f; - extra = "*"; + srExtra = "*"; } panelContents.Children = new Drawable[] @@ -191,7 +196,7 @@ namespace osu.Game.Tournament.Components }, new OsuSpriteText { - Text = $"CS {beatmap.BaseDifficulty.CircleSize:0.#} / AR {ar:0.#}{extra}", + Text = $"CS {beatmap.BaseDifficulty.CircleSize:0.#}{hardRockExtra} / AR {ar:0.#}{srExtra} / OD {beatmap.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}", Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, Colour = OsuColour.Gray(0.33f), Anchor = Anchor.TopRight, @@ -199,7 +204,7 @@ namespace osu.Game.Tournament.Components }, new OsuSpriteText { - Text = $"Star Rating {beatmap.StarDifficulty:0.#}{extra}", + Text = $"Star Rating {beatmap.StarDifficulty:0.#}{srExtra}", Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, Colour = OsuColour.Gray(0.33f), Anchor = Anchor.BottomRight, From 4e8c7a4dc0fa8530700dff7907e9095d15dc49a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Nov 2018 15:42:10 +0900 Subject: [PATCH 0306/2854] Adjust gameplay area to match better Also reduces size of score display to avoid hiding combos. --- .../Gameplay/Components/MatchHeader.cs | 2 +- .../Screens/Gameplay/GameplayScreen.cs | 24 +++++++++---------- .../Screens/Showcase/TournamentLogo.cs | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index 1085e7a8c3..210b42a4c8 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components private void load(LadderInfo ladder, TextureStore textures) { RelativeSizeAxes = Axes.X; - Height = 100; + Height = 95; Children = new Drawable[] { new TournamentLogo(), diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 29b1b85e02..8897a98a7e 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -45,20 +45,28 @@ namespace osu.Game.Tournament.Screens.Gameplay AddRange(new Drawable[] { new MatchHeader(), - new FillFlowContainer + new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Y = 5, - Direction = FillDirection.Vertical, Anchor = Anchor.Centre, Origin = Anchor.Centre, Children = new Drawable[] { + new Box + { + // chroma key area for stable gameplay + Name = "chroma", + RelativeSizeAxes = Axes.X, + Height = 512, + Colour = new Color4(0, 255, 0, 255), + }, new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + Y = -4, Children = new Drawable[] { new Circle @@ -73,7 +81,7 @@ namespace osu.Game.Tournament.Screens.Gameplay { Name = "top bar blue", RelativeSizeAxes = Axes.X, - Height = 9, + Height = 8, Width = 0.5f, Colour = blue, Anchor = Anchor.TopRight, @@ -81,20 +89,12 @@ namespace osu.Game.Tournament.Screens.Gameplay }, } }, - new Box - { - // chroma key area for stable gameplay - Name = "chroma", - RelativeSizeAxes = Axes.X, - Height = 500, - Colour = new Color4(0, 255, 0, 255), - }, } }, scoreDisplay = new MatchScoreDisplay { Y = -60, - Scale = new Vector2(0.86f), + Scale = new Vector2(0.8f), Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, }, diff --git a/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs b/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs index 5b0b4b62e2..cd4f646fe7 100644 --- a/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs +++ b/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs @@ -15,7 +15,7 @@ namespace osu.Game.Tournament.Screens.Showcase public TournamentLogo() { RelativeSizeAxes = Axes.X; - Height = 100; + Height = 95; Margin = new MarginPadding { Vertical = 5 }; } From fb05ea3de34e18cba986422e948b6e705eff126c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Nov 2018 16:23:44 +0900 Subject: [PATCH 0307/2854] Fix alignment and fonts of tem intro screen --- .../Screens/TeamIntro/TeamIntroScreen.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index 7567325bfe..55af305045 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -105,8 +105,9 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Colour = col, + Font = "Exo2.0-Black", Text = "COMING UP NEXT", - Font = "Aquatico-Regular", + Spacing = new Vector2(2, 0), TextSize = 15, }, new OsuSpriteText @@ -115,7 +116,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Origin = Anchor.TopCentre, Colour = col, Text = pairing.Grouping.Value?.Name.Value ?? "Unknown Grouping", - Font = "Aquatico-Light", + Font = "Exo2.0-Light", Spacing = new Vector2(10, 0), TextSize = 50, }, @@ -123,7 +124,6 @@ namespace osu.Game.Tournament.Screens.TeamIntro { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Font = "Aquatico-Light", Colour = col, Text = pairing.Date.Value.ToUniversalTime().ToString("dd MMMM HH:mm UTC"), TextSize = 20, @@ -158,11 +158,10 @@ namespace osu.Game.Tournament.Screens.TeamIntro AutoSizeAxes = Axes.Both, Spacing = new Vector2(0, 5), Padding = new MarginPadding(20), - - - Anchor = !left ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = !left ? Anchor.CentreRight : Anchor.CentreLeft, + Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = left ? Anchor.CentreRight : Anchor.CentreLeft, RelativePositionAxes = Axes.Both, + X = (left ? -1 : 1) * 0.66f, }, }; @@ -172,7 +171,6 @@ namespace osu.Game.Tournament.Screens.TeamIntro players.Add(new OsuSpriteText { Text = p.Username, - Font = "Aquatico-Regular", TextSize = 24, Colour = colour, Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft, @@ -215,6 +213,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro { Text = teamName.ToUpper(), TextSize = 20, + Font = "Aquatico-Regular", Colour = colour, Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, From 17a81259d4eae8d261caaf0b139a2a85bd8ae1ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Nov 2018 17:25:46 +0900 Subject: [PATCH 0308/2854] Fix spacing and colouring of song bar eleemnts Also uses MM:ss instead of ssss. --- osu.Game.Tournament/Components/SongBar.cs | 73 +++++++++++++++-------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 3d9fe2f5ee..14a01aebdf 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -1,11 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; 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.Legacy; using osu.Game.Graphics; @@ -178,38 +180,26 @@ namespace osu.Game.Tournament.Components panelContents.Children = new Drawable[] { - new OsuSpriteText + new DiffPiece(("Length", TimeSpan.FromSeconds(length).ToString(@"mm\:ss"))) { - Text = $"Length {length:0}s", - Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, - Colour = OsuColour.Gray(0.33f), - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, + Anchor = Anchor.CentreLeft, + Origin = Anchor.BottomLeft, }, - new OsuSpriteText + new DiffPiece(("BPM", $"{bpm:0.#}")) { - Text = $"BPM {bpm:0.#}", - Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, - Colour = OsuColour.Gray(0.33f), - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft + Anchor = Anchor.CentreLeft, + Origin = Anchor.TopLeft }, - new OsuSpriteText + new DiffPiece(("CS", $"{beatmap.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"), ("AR", $"{ar:0.#}{srExtra}"), ("OD", $"{beatmap.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}")) { - Text = $"CS {beatmap.BaseDifficulty.CircleSize:0.#}{hardRockExtra} / AR {ar:0.#}{srExtra} / OD {beatmap.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}", - Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, - Colour = OsuColour.Gray(0.33f), - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight - }, - new OsuSpriteText - { - Text = $"Star Rating {beatmap.StarDifficulty:0.#}{srExtra}", - Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, - Colour = OsuColour.Gray(0.33f), - Anchor = Anchor.BottomRight, + Anchor = Anchor.CentreRight, Origin = Anchor.BottomRight }, + new DiffPiece(("Star Rating", $"{beatmap.StarDifficulty:0.#}{srExtra}")) + { + Anchor = Anchor.CentreRight, + Origin = Anchor.TopRight + }, panel = new TournamentBeatmapPanel(beatmap) { Anchor = Anchor.Centre, @@ -219,5 +209,38 @@ namespace osu.Game.Tournament.Components } }; } + + public class DiffPiece : TextFlowContainer + { + public DiffPiece(params (string heading, string content)[] tuples) + { + Margin = new MarginPadding { Horizontal = 15, Vertical = 1 }; + AutoSizeAxes = Axes.Both; + + void cp(SpriteText s, Color4 colour) + { + s.Colour = colour; + s.TextSize = 15; + s.Font = @"Exo2.0-Bold"; + } + + bool first = true; + foreach (var t in tuples) + { + if (!first) + AddText(" / ", s => + { + cp(s, OsuColour.Gray(0.33f)); + s.Spacing = new Vector2(-2, 0); + }); + + AddText(new OsuSpriteText { Text = t.heading }, s => cp(s, OsuColour.Gray(0.33f))); + AddText(" ", s => cp(s, OsuColour.Gray(0.33f))); + AddText(new OsuSpriteText { Text = t.content }, s => cp(s, OsuColour.Gray(0.5f))); + first = false; + } + } + } } } + From fdba2bffb9dee193def8d9cf4cb929849c9d11df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Nov 2018 17:33:48 +0900 Subject: [PATCH 0309/2854] Ensure last state is set correctly --- .../Screens/Gameplay/GameplayScreen.cs | 103 +++++++++--------- 1 file changed, 54 insertions(+), 49 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 8897a98a7e..05cf4751f4 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -135,61 +135,66 @@ namespace osu.Game.Tournament.Screens.Gameplay private void stateChanged(TourneyState state) { - if (state == TourneyState.Ranking) + try { - if (warmup.Value) return; - - if (ipc.Score1 > ipc.Score2) - currentMatch.Value.Team1Score.Value++; - else - currentMatch.Value.Team2Score.Value++; - } - - scheduledOperation?.Cancel(); - - void expand() - { - chat.Expand(); - - using (BeginDelayedSequence(300, true)) + if (state == TourneyState.Ranking) { - scoreDisplay.FadeIn(100); - SongBar.Expanded = true; + if (warmup.Value) return; + + if (ipc.Score1 > ipc.Score2) + currentMatch.Value.Team1Score.Value++; + else + currentMatch.Value.Team2Score.Value++; + } + + scheduledOperation?.Cancel(); + + void expand() + { + chat.Expand(); + + using (BeginDelayedSequence(300, true)) + { + scoreDisplay.FadeIn(100); + SongBar.Expanded = true; + } + } + + void contract() + { + SongBar.Expanded = false; + scoreDisplay.FadeOut(100); + using (chat.BeginDelayedSequence(500)) + chat.Contract(); + } + + switch (state) + { + case TourneyState.Idle: + contract(); + + if (lastState == TourneyState.Ranking && !warmup.Value) + { + if (currentMatch.Value?.Completed == true) + scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(TeamWinScreen)); }, 4000); + else if (currentMatch.Value?.Completed == false) + scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(MapPoolScreen)); }, 4000); + } + + break; + case TourneyState.Ranking: + scheduledOperation = Scheduler.AddDelayed(contract, 10000); + break; + default: + chat.Expand(); + expand(); + break; } } - - void contract() + finally { - SongBar.Expanded = false; - scoreDisplay.FadeOut(100); - using (chat.BeginDelayedSequence(500)) - chat.Contract(); + lastState = state; } - - switch (state) - { - case TourneyState.Idle: - contract(); - - if (lastState == TourneyState.Ranking && !warmup.Value) - { - if (currentMatch.Value?.Completed == true) - scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(TeamWinScreen)); }, 4000); - else if (currentMatch.Value?.Completed == false) - scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(MapPoolScreen)); }, 4000); - } - - break; - case TourneyState.Ranking: - scheduledOperation = Scheduler.AddDelayed(contract, 10000); - break; - default: - chat.Expand(); - expand(); - break; - } - - lastState = state; } } } From 056025c5c2ddc5bca93d5f104372f8b224fe4891 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 1 Dec 2018 09:50:11 +0900 Subject: [PATCH 0310/2854] Fix handling of API responses --- osu.Game.Tournament/Components/TournamentBeatmapPanel.cs | 3 +++ osu.Game.Tournament/TournamentGameBase.cs | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 3165e5a2c4..ae2e2b5160 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; @@ -39,6 +40,8 @@ namespace osu.Game.Tournament.Components public TournamentBeatmapPanel(BeatmapInfo beatmap, string mods = null) { + if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); + Beatmap = beatmap; this.mods = mods; Width = 400; diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 8734b2e64b..e37b7cf8d6 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -113,8 +113,8 @@ namespace osu.Game.Tournament if (string.IsNullOrEmpty(p.Username)) { var req = new GetUserRequest(p.Id); - req.Success += i => p.Username = i.Username; req.Perform(API); + p.Username = req.Result.Username; addedInfo = true; } @@ -125,8 +125,8 @@ namespace osu.Game.Tournament if (b.BeatmapInfo == null) { var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); - req.Success += i => b.BeatmapInfo = i.ToBeatmap(RulesetStore); req.Perform(API); + b.BeatmapInfo = req.Result?.ToBeatmap(RulesetStore); addedInfo = true; } From 61ca79a8b2294eba4cd83be2a7b725912ae00dc6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 1 Dec 2018 15:32:11 +0900 Subject: [PATCH 0311/2854] Add conditional match support --- .../Components/ConditionalMatchPairing.cs | 12 +++++++++ .../Ladder/Components/DrawableMatchPairing.cs | 14 ++++++++++- .../Screens/Ladder/Components/MatchPairing.cs | 16 +++++++++++- .../Screens/Schedule/ScheduleScreen.cs | 25 +++++++++++++------ osu.Game.Tournament/TournamentGameBase.cs | 7 ++++++ 5 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 osu.Game.Tournament/Screens/Ladder/Components/ConditionalMatchPairing.cs diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ConditionalMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/ConditionalMatchPairing.cs new file mode 100644 index 0000000000..bff661bcf4 --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/ConditionalMatchPairing.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + /// + /// A pairing that may not necessarily occur. + /// + public class ConditionalMatchPairing : MatchPairing + { + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index e727e740f9..db942c6e4c 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -199,6 +199,18 @@ namespace osu.Game.Tournament.Screens.Ladder.Components if (Pairing.Team1.Value == null || Pairing.Team2.Value == null) Pairing.CancelMatchStart(); + if (Pairing.ConditionalPairings.Count > 0) + { + foreach (var conditional in Pairing.ConditionalPairings) + { + var team1Match = conditional.Acronyms.Contains(Pairing.Team1Acronym); + var team2Match = conditional.Acronyms.Contains(Pairing.Team2Acronym); + + if (team1Match && team2Match) + Pairing.Date.Value = conditional.Date; + } + } + Flow.Children = new[] { new DrawableMatchTeam(Pairing.Team1, Pairing, Pairing.Losers), @@ -226,7 +238,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components protected override bool OnClick(ClickEvent e) { - if (editorInfo == null) + if (editorInfo == null || Pairing is ConditionalMatchPairing) return false; Selected = true; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index 003f41cfa9..8461cf4ae1 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using Newtonsoft.Json; using osu.Framework.Configuration; @@ -17,6 +18,17 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { public int ID; + public List Acronyms + { + get + { + List acronyms = new List(); + if (Team1Acronym != null) acronyms.Add(Team1Acronym); + if (Team2Acronym != null) acronyms.Add(Team2Acronym); + return acronyms; + } + } + [JsonIgnore] public readonly Bindable Team1 = new Bindable(); @@ -53,6 +65,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly Bindable Date = new Bindable(); + public readonly BindableCollection ConditionalPairings = new BindableCollection(); + public readonly Bindable Position = new Bindable(); public MatchPairing() @@ -74,7 +88,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [JsonIgnore] public TournamentTeam Loser => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team2.Value : Team1.Value; - public int PointsToWin => Grouping.Value.BestOf / 2 + 1; + public int PointsToWin => Grouping.Value == null ? 0 : Grouping.Value.BestOf / 2 + 1; /// /// Remove scores from the match, in case of a false click or false start. diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index 37577ec3b0..f3f3667db1 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -56,6 +56,13 @@ namespace osu.Game.Tournament.Screens.Schedule return; } + var upcoming = ladder.Pairings.Where(p => !p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4); + var conditionals = ladder.Pairings.Where(p => !p.Completed.Value && (p.Team1.Value == null || p.Team2.Value == null) && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4) + .SelectMany(m => m.ConditionalPairings.Where(cp => m.Acronyms.TrueForAll(a => cp.Acronyms.Contains(a)))); + + upcoming = upcoming.Concat(conditionals); + upcoming = upcoming.OrderBy(p => p.Date.Value).Take(12); + mainContainer.Child = new FillFlowContainer { RelativeSizeAxes = Axes.Both, @@ -77,7 +84,8 @@ namespace osu.Game.Tournament.Screens.Schedule RelativeSizeAxes = Axes.Both, Width = 0.4f, ChildrenEnumerable = ladder.Pairings - .Where(p => p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4) + .Where(p => p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null + && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4) .OrderByDescending(p => p.Date.Value) .Take(8) .Select(p => new SchedulePairing(p)) @@ -86,11 +94,7 @@ namespace osu.Game.Tournament.Screens.Schedule { RelativeSizeAxes = Axes.Both, Width = 0.6f, - ChildrenEnumerable = ladder.Pairings - .Where(p => !p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4) - .OrderBy(p => p.Date.Value) - .Take(8) - .Select(p => new SchedulePairing(p)) + ChildrenEnumerable = upcoming.Select(p => new SchedulePairing(p)) }, } } @@ -129,6 +133,11 @@ namespace osu.Game.Tournament.Screens.Schedule { Flow.Direction = FillDirection.Horizontal; + bool conditional = pairing is ConditionalMatchPairing; + + if (conditional) + Colour = OsuColour.Gray(0.5f); + if (showTimestamp) { AddInternal(new DrawableDate(Pairing.Date.Value) @@ -136,6 +145,7 @@ namespace osu.Game.Tournament.Screens.Schedule Anchor = Anchor.TopRight, Origin = Anchor.TopLeft, Colour = Color4.Black, + Alpha = conditional ? 0.6f : 1, Margin = new MarginPadding { Horizontal = 10, Vertical = 5 }, }); AddInternal(new OsuSpriteText @@ -143,8 +153,9 @@ namespace osu.Game.Tournament.Screens.Schedule Anchor = Anchor.BottomRight, Origin = Anchor.BottomLeft, Colour = Color4.Black, + Alpha = conditional ? 0.6f : 1, Margin = new MarginPadding { Horizontal = 10, Vertical = 5 }, - Text = pairing.Date.Value.ToUniversalTime().ToString("HH:mm UTC") + Text = pairing.Date.Value.ToUniversalTime().ToString("HH:mm UTC") + (conditional ? " (conditional)" : "") }); } } diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index e37b7cf8d6..4938533a9e 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -78,6 +78,13 @@ namespace osu.Game.Tournament { pairing.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == pairing.Team1Acronym); pairing.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == pairing.Team2Acronym); + + foreach (var conditional in pairing.ConditionalPairings) + { + conditional.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == conditional.Team1Acronym); + conditional.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == conditional.Team2Acronym); + conditional.Grouping.Value = pairing.Grouping.Value; + } } // assign progressions From d16f6576ca70b62ea6d4ac30ea78d36a5d4d8359 Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Thu, 13 Dec 2018 00:10:15 -0600 Subject: [PATCH 0312/2854] Add Android project --- osu.Android/Assets/AboutAssets.txt | 19 + osu.Android/GameView.cs | 26 + osu.Android/MainActivity.cs | 26 + osu.Android/Properties/AndroidManifest.xml | 9 + osu.Android/Properties/AssemblyInfo.cs | 33 + osu.Android/Resources/AboutResources.txt | 44 + osu.Android/Resources/Resource.designer.cs | 6488 +++++++++++++++++ .../Resources/layout/activity_main.axml | 13 + .../mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../Resources/mipmap-hdpi/ic_launcher.png | Bin 0 -> 8828 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 1441 bytes .../mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 8828 bytes .../Resources/mipmap-mdpi/ic_launcher.png | Bin 0 -> 5045 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 958 bytes .../mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 5045 bytes .../Resources/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 12931 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 2056 bytes .../mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 12931 bytes .../Resources/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 21256 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 3403 bytes .../mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 21256 bytes .../Resources/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 30047 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 4889 bytes .../mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 30047 bytes osu.Android/Resources/values/Strings.xml | 4 + osu.Android/Resources/values/colors.xml | 6 + .../values/ic_launcher_background.xml | 4 + osu.Android/Resources/values/styles.xml | 11 + osu.Android/bass.dll | Bin 0 -> 210944 bytes osu.Android/lib/arm64-v8a/libbass.so | Bin 0 -> 308904 bytes osu.Android/lib/arm64-v8a/libbass_fx.so | Bin 0 -> 154408 bytes osu.Android/lib/armeabi-v7a/libbass.so | Bin 0 -> 222620 bytes osu.Android/lib/armeabi-v7a/libbass_fx.so | Bin 0 -> 92176 bytes osu.Android/osu.Android.csproj | 175 + 35 files changed, 6868 insertions(+) create mode 100644 osu.Android/Assets/AboutAssets.txt create mode 100644 osu.Android/GameView.cs create mode 100644 osu.Android/MainActivity.cs create mode 100644 osu.Android/Properties/AndroidManifest.xml create mode 100644 osu.Android/Properties/AssemblyInfo.cs create mode 100644 osu.Android/Resources/AboutResources.txt create mode 100644 osu.Android/Resources/Resource.designer.cs create mode 100644 osu.Android/Resources/layout/activity_main.axml create mode 100644 osu.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 osu.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 osu.Android/Resources/mipmap-hdpi/ic_launcher.png create mode 100644 osu.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 osu.Android/Resources/mipmap-hdpi/ic_launcher_round.png create mode 100644 osu.Android/Resources/mipmap-mdpi/ic_launcher.png create mode 100644 osu.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 osu.Android/Resources/mipmap-mdpi/ic_launcher_round.png create mode 100644 osu.Android/Resources/mipmap-xhdpi/ic_launcher.png create mode 100644 osu.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 osu.Android/Resources/mipmap-xhdpi/ic_launcher_round.png create mode 100644 osu.Android/Resources/mipmap-xxhdpi/ic_launcher.png create mode 100644 osu.Android/Resources/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 osu.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 osu.Android/Resources/mipmap-xxxhdpi/ic_launcher.png create mode 100644 osu.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 osu.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 osu.Android/Resources/values/Strings.xml create mode 100644 osu.Android/Resources/values/colors.xml create mode 100644 osu.Android/Resources/values/ic_launcher_background.xml create mode 100644 osu.Android/Resources/values/styles.xml create mode 100644 osu.Android/bass.dll create mode 100644 osu.Android/lib/arm64-v8a/libbass.so create mode 100644 osu.Android/lib/arm64-v8a/libbass_fx.so create mode 100644 osu.Android/lib/armeabi-v7a/libbass.so create mode 100644 osu.Android/lib/armeabi-v7a/libbass_fx.so create mode 100644 osu.Android/osu.Android.csproj diff --git a/osu.Android/Assets/AboutAssets.txt b/osu.Android/Assets/AboutAssets.txt new file mode 100644 index 0000000000..b0633374bd --- /dev/null +++ b/osu.Android/Assets/AboutAssets.txt @@ -0,0 +1,19 @@ +Any raw assets you want to be deployed with your application can be placed in +this directory (and child directories) and given a Build Action of "AndroidAsset". + +These files will be deployed with you package and will be accessible using Android's +AssetManager, like this: + +public class ReadAsset : Activity +{ + protected override void OnCreate (Bundle bundle) + { + base.OnCreate (bundle); + + InputStream input = Assets.Open ("my_asset.txt"); + } +} + +Additionally, some Android functions will automatically load asset files: + +Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); \ No newline at end of file diff --git a/osu.Android/GameView.cs b/osu.Android/GameView.cs new file mode 100644 index 0000000000..649ee0134b --- /dev/null +++ b/osu.Android/GameView.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Android.Content; +using Android.Util; +using osu.Framework; +using osu.Framework.Android; +using osu.Game; + +namespace osu.Android +{ + public class GameView : AndroidGameView + { + public GameView(Context context, IAttributeSet attrs) : + base(context, attrs) + { + CreateGame(); + } + + public GameView(Context context) : base(context) + { + CreateGame(); + } + public override Framework.Game CreateGame() => new OsuGame(); + } +} diff --git a/osu.Android/MainActivity.cs b/osu.Android/MainActivity.cs new file mode 100644 index 0000000000..3a8a5026bc --- /dev/null +++ b/osu.Android/MainActivity.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Android.App; +using Android.OS; +using Android.Runtime; +using Android.Widget; +using Android.Content.PM; + +namespace osu.Android +{ + [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true, ScreenOrientation = ScreenOrientation.Landscape, SupportsPictureInPicture = false)] + public class MainActivity : Activity + { + protected override void OnCreate(Bundle savedInstanceState) + { + base.OnCreate(savedInstanceState); + // Set our view from the "main" layout resource + SetContentView(Resource.Layout.activity_main); + } + public override void OnBackPressed() + { + + } + } +} diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml new file mode 100644 index 0000000000..12d1f326b9 --- /dev/null +++ b/osu.Android/Properties/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/osu.Android/Properties/AssemblyInfo.cs b/osu.Android/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..d222a06ee0 --- /dev/null +++ b/osu.Android/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Android.App; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("osu.Android")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("osu.Android")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Android/Resources/AboutResources.txt b/osu.Android/Resources/AboutResources.txt new file mode 100644 index 0000000000..c2bca974c4 --- /dev/null +++ b/osu.Android/Resources/AboutResources.txt @@ -0,0 +1,44 @@ +Images, layout descriptions, binary blobs and string dictionaries can be included +in your application as resource files. Various Android APIs are designed to +operate on the resource IDs instead of dealing with images, strings or binary blobs +directly. + +For example, a sample Android app that contains a user interface layout (main.axml), +an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) +would keep its resources in the "Resources" directory of the application: + +Resources/ + drawable/ + icon.png + + layout/ + main.axml + + values/ + strings.xml + +In order to get the build system to recognize Android resources, set the build action to +"AndroidResource". The native Android APIs do not operate directly with filenames, but +instead operate on resource IDs. When you compile an Android application that uses resources, +the build system will package the resources for distribution and generate a class called "R" +(this is an Android convention) that contains the tokens for each one of the resources +included. For example, for the above Resources layout, this is what the R class would expose: + +public class R { + public class drawable { + public const int icon = 0x123; + } + + public class layout { + public const int main = 0x456; + } + + public class strings { + public const int first_string = 0xabc; + public const int second_string = 0xbcd; + } +} + +You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main +to reference the layout/main.axml file, or R.strings.first_string to reference the first +string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/osu.Android/Resources/Resource.designer.cs b/osu.Android/Resources/Resource.designer.cs new file mode 100644 index 0000000000..ed8407c130 --- /dev/null +++ b/osu.Android/Resources/Resource.designer.cs @@ -0,0 +1,6488 @@ +#pragma warning disable 1591 +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +[assembly: global::Android.Runtime.ResourceDesignerAttribute("osu.Android.Resource", IsApplication=true)] + +namespace osu.Android +{ + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] + public partial class Resource + { + + static Resource() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + public static void UpdateIdValues() + { + } + + public partial class Animation + { + + // aapt resource value: 0x7f050000 + public const int abc_fade_in = 2131034112; + + // aapt resource value: 0x7f050001 + public const int abc_fade_out = 2131034113; + + // aapt resource value: 0x7f050002 + public const int abc_grow_fade_in_from_bottom = 2131034114; + + // aapt resource value: 0x7f050003 + public const int abc_popup_enter = 2131034115; + + // aapt resource value: 0x7f050004 + public const int abc_popup_exit = 2131034116; + + // aapt resource value: 0x7f050005 + public const int abc_shrink_fade_out_from_bottom = 2131034117; + + // aapt resource value: 0x7f050006 + public const int abc_slide_in_bottom = 2131034118; + + // aapt resource value: 0x7f050007 + public const int abc_slide_in_top = 2131034119; + + // aapt resource value: 0x7f050008 + public const int abc_slide_out_bottom = 2131034120; + + // aapt resource value: 0x7f050009 + public const int abc_slide_out_top = 2131034121; + + // aapt resource value: 0x7f05000a + public const int design_bottom_sheet_slide_in = 2131034122; + + // aapt resource value: 0x7f05000b + public const int design_bottom_sheet_slide_out = 2131034123; + + // aapt resource value: 0x7f05000c + public const int design_snackbar_in = 2131034124; + + // aapt resource value: 0x7f05000d + public const int design_snackbar_out = 2131034125; + + // aapt resource value: 0x7f05000e + public const int tooltip_enter = 2131034126; + + // aapt resource value: 0x7f05000f + public const int tooltip_exit = 2131034127; + + static Animation() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Animation() + { + } + } + + public partial class Animator + { + + // aapt resource value: 0x7f060000 + public const int design_appbar_state_list_animator = 2131099648; + + static Animator() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Animator() + { + } + } + + public partial class Attribute + { + + // aapt resource value: 0x7f010052 + public const int actionBarDivider = 2130772050; + + // aapt resource value: 0x7f010053 + public const int actionBarItemBackground = 2130772051; + + // aapt resource value: 0x7f01004c + public const int actionBarPopupTheme = 2130772044; + + // aapt resource value: 0x7f010051 + public const int actionBarSize = 2130772049; + + // aapt resource value: 0x7f01004e + public const int actionBarSplitStyle = 2130772046; + + // aapt resource value: 0x7f01004d + public const int actionBarStyle = 2130772045; + + // aapt resource value: 0x7f010048 + public const int actionBarTabBarStyle = 2130772040; + + // aapt resource value: 0x7f010047 + public const int actionBarTabStyle = 2130772039; + + // aapt resource value: 0x7f010049 + public const int actionBarTabTextStyle = 2130772041; + + // aapt resource value: 0x7f01004f + public const int actionBarTheme = 2130772047; + + // aapt resource value: 0x7f010050 + public const int actionBarWidgetTheme = 2130772048; + + // aapt resource value: 0x7f01006d + public const int actionButtonStyle = 2130772077; + + // aapt resource value: 0x7f010069 + public const int actionDropDownStyle = 2130772073; + + // aapt resource value: 0x7f0100c4 + public const int actionLayout = 2130772164; + + // aapt resource value: 0x7f010054 + public const int actionMenuTextAppearance = 2130772052; + + // aapt resource value: 0x7f010055 + public const int actionMenuTextColor = 2130772053; + + // aapt resource value: 0x7f010058 + public const int actionModeBackground = 2130772056; + + // aapt resource value: 0x7f010057 + public const int actionModeCloseButtonStyle = 2130772055; + + // aapt resource value: 0x7f01005a + public const int actionModeCloseDrawable = 2130772058; + + // aapt resource value: 0x7f01005c + public const int actionModeCopyDrawable = 2130772060; + + // aapt resource value: 0x7f01005b + public const int actionModeCutDrawable = 2130772059; + + // aapt resource value: 0x7f010060 + public const int actionModeFindDrawable = 2130772064; + + // aapt resource value: 0x7f01005d + public const int actionModePasteDrawable = 2130772061; + + // aapt resource value: 0x7f010062 + public const int actionModePopupWindowStyle = 2130772066; + + // aapt resource value: 0x7f01005e + public const int actionModeSelectAllDrawable = 2130772062; + + // aapt resource value: 0x7f01005f + public const int actionModeShareDrawable = 2130772063; + + // aapt resource value: 0x7f010059 + public const int actionModeSplitBackground = 2130772057; + + // aapt resource value: 0x7f010056 + public const int actionModeStyle = 2130772054; + + // aapt resource value: 0x7f010061 + public const int actionModeWebSearchDrawable = 2130772065; + + // aapt resource value: 0x7f01004a + public const int actionOverflowButtonStyle = 2130772042; + + // aapt resource value: 0x7f01004b + public const int actionOverflowMenuStyle = 2130772043; + + // aapt resource value: 0x7f0100c6 + public const int actionProviderClass = 2130772166; + + // aapt resource value: 0x7f0100c5 + public const int actionViewClass = 2130772165; + + // aapt resource value: 0x7f010075 + public const int activityChooserViewStyle = 2130772085; + + // aapt resource value: 0x7f01009a + public const int alertDialogButtonGroupStyle = 2130772122; + + // aapt resource value: 0x7f01009b + public const int alertDialogCenterButtons = 2130772123; + + // aapt resource value: 0x7f010099 + public const int alertDialogStyle = 2130772121; + + // aapt resource value: 0x7f01009c + public const int alertDialogTheme = 2130772124; + + // aapt resource value: 0x7f0100b2 + public const int allowStacking = 2130772146; + + // aapt resource value: 0x7f0100b3 + public const int alpha = 2130772147; + + // aapt resource value: 0x7f0100c1 + public const int alphabeticModifiers = 2130772161; + + // aapt resource value: 0x7f0100ba + public const int arrowHeadLength = 2130772154; + + // aapt resource value: 0x7f0100bb + public const int arrowShaftLength = 2130772155; + + // aapt resource value: 0x7f0100a1 + public const int autoCompleteTextViewStyle = 2130772129; + + // aapt resource value: 0x7f01003b + public const int autoSizeMaxTextSize = 2130772027; + + // aapt resource value: 0x7f01003a + public const int autoSizeMinTextSize = 2130772026; + + // aapt resource value: 0x7f010039 + public const int autoSizePresetSizes = 2130772025; + + // aapt resource value: 0x7f010038 + public const int autoSizeStepGranularity = 2130772024; + + // aapt resource value: 0x7f010037 + public const int autoSizeTextType = 2130772023; + + // aapt resource value: 0x7f010015 + public const int background = 2130771989; + + // aapt resource value: 0x7f010017 + public const int backgroundSplit = 2130771991; + + // aapt resource value: 0x7f010016 + public const int backgroundStacked = 2130771990; + + // aapt resource value: 0x7f0100fd + public const int backgroundTint = 2130772221; + + // aapt resource value: 0x7f0100fe + public const int backgroundTintMode = 2130772222; + + // aapt resource value: 0x7f0100bc + public const int barLength = 2130772156; + + // aapt resource value: 0x7f010128 + public const int behavior_autoHide = 2130772264; + + // aapt resource value: 0x7f010105 + public const int behavior_hideable = 2130772229; + + // aapt resource value: 0x7f010131 + public const int behavior_overlapTop = 2130772273; + + // aapt resource value: 0x7f010104 + public const int behavior_peekHeight = 2130772228; + + // aapt resource value: 0x7f010106 + public const int behavior_skipCollapsed = 2130772230; + + // aapt resource value: 0x7f010126 + public const int borderWidth = 2130772262; + + // aapt resource value: 0x7f010072 + public const int borderlessButtonStyle = 2130772082; + + // aapt resource value: 0x7f010120 + public const int bottomSheetDialogTheme = 2130772256; + + // aapt resource value: 0x7f010121 + public const int bottomSheetStyle = 2130772257; + + // aapt resource value: 0x7f01006f + public const int buttonBarButtonStyle = 2130772079; + + // aapt resource value: 0x7f01009f + public const int buttonBarNegativeButtonStyle = 2130772127; + + // aapt resource value: 0x7f0100a0 + public const int buttonBarNeutralButtonStyle = 2130772128; + + // aapt resource value: 0x7f01009e + public const int buttonBarPositiveButtonStyle = 2130772126; + + // aapt resource value: 0x7f01006e + public const int buttonBarStyle = 2130772078; + + // aapt resource value: 0x7f0100f2 + public const int buttonGravity = 2130772210; + + // aapt resource value: 0x7f01002a + public const int buttonPanelSideLayout = 2130772010; + + // aapt resource value: 0x7f0100a2 + public const int buttonStyle = 2130772130; + + // aapt resource value: 0x7f0100a3 + public const int buttonStyleSmall = 2130772131; + + // aapt resource value: 0x7f0100b4 + public const int buttonTint = 2130772148; + + // aapt resource value: 0x7f0100b5 + public const int buttonTintMode = 2130772149; + + // aapt resource value: 0x7f0100a4 + public const int checkboxStyle = 2130772132; + + // aapt resource value: 0x7f0100a5 + public const int checkedTextViewStyle = 2130772133; + + // aapt resource value: 0x7f0100d5 + public const int closeIcon = 2130772181; + + // aapt resource value: 0x7f010027 + public const int closeItemLayout = 2130772007; + + // aapt resource value: 0x7f0100f4 + public const int collapseContentDescription = 2130772212; + + // aapt resource value: 0x7f0100f3 + public const int collapseIcon = 2130772211; + + // aapt resource value: 0x7f010113 + public const int collapsedTitleGravity = 2130772243; + + // aapt resource value: 0x7f01010d + public const int collapsedTitleTextAppearance = 2130772237; + + // aapt resource value: 0x7f0100b6 + public const int color = 2130772150; + + // aapt resource value: 0x7f010091 + public const int colorAccent = 2130772113; + + // aapt resource value: 0x7f010098 + public const int colorBackgroundFloating = 2130772120; + + // aapt resource value: 0x7f010095 + public const int colorButtonNormal = 2130772117; + + // aapt resource value: 0x7f010093 + public const int colorControlActivated = 2130772115; + + // aapt resource value: 0x7f010094 + public const int colorControlHighlight = 2130772116; + + // aapt resource value: 0x7f010092 + public const int colorControlNormal = 2130772114; + + // aapt resource value: 0x7f0100b1 + public const int colorError = 2130772145; + + // aapt resource value: 0x7f01008f + public const int colorPrimary = 2130772111; + + // aapt resource value: 0x7f010090 + public const int colorPrimaryDark = 2130772112; + + // aapt resource value: 0x7f010096 + public const int colorSwitchThumbNormal = 2130772118; + + // aapt resource value: 0x7f0100da + public const int commitIcon = 2130772186; + + // aapt resource value: 0x7f0100c7 + public const int contentDescription = 2130772167; + + // aapt resource value: 0x7f010020 + public const int contentInsetEnd = 2130772000; + + // aapt resource value: 0x7f010024 + public const int contentInsetEndWithActions = 2130772004; + + // aapt resource value: 0x7f010021 + public const int contentInsetLeft = 2130772001; + + // aapt resource value: 0x7f010022 + public const int contentInsetRight = 2130772002; + + // aapt resource value: 0x7f01001f + public const int contentInsetStart = 2130771999; + + // aapt resource value: 0x7f010023 + public const int contentInsetStartWithNavigation = 2130772003; + + // aapt resource value: 0x7f01010e + public const int contentScrim = 2130772238; + + // aapt resource value: 0x7f010097 + public const int controlBackground = 2130772119; + + // aapt resource value: 0x7f010147 + public const int counterEnabled = 2130772295; + + // aapt resource value: 0x7f010148 + public const int counterMaxLength = 2130772296; + + // aapt resource value: 0x7f01014a + public const int counterOverflowTextAppearance = 2130772298; + + // aapt resource value: 0x7f010149 + public const int counterTextAppearance = 2130772297; + + // aapt resource value: 0x7f010018 + public const int customNavigationLayout = 2130771992; + + // aapt resource value: 0x7f0100d4 + public const int defaultQueryHint = 2130772180; + + // aapt resource value: 0x7f010067 + public const int dialogPreferredPadding = 2130772071; + + // aapt resource value: 0x7f010066 + public const int dialogTheme = 2130772070; + + // aapt resource value: 0x7f01000e + public const int displayOptions = 2130771982; + + // aapt resource value: 0x7f010014 + public const int divider = 2130771988; + + // aapt resource value: 0x7f010074 + public const int dividerHorizontal = 2130772084; + + // aapt resource value: 0x7f0100c0 + public const int dividerPadding = 2130772160; + + // aapt resource value: 0x7f010073 + public const int dividerVertical = 2130772083; + + // aapt resource value: 0x7f0100b8 + public const int drawableSize = 2130772152; + + // aapt resource value: 0x7f010009 + public const int drawerArrowStyle = 2130771977; + + // aapt resource value: 0x7f010086 + public const int dropDownListViewStyle = 2130772102; + + // aapt resource value: 0x7f01006a + public const int dropdownListPreferredItemHeight = 2130772074; + + // aapt resource value: 0x7f01007b + public const int editTextBackground = 2130772091; + + // aapt resource value: 0x7f01007a + public const int editTextColor = 2130772090; + + // aapt resource value: 0x7f0100a6 + public const int editTextStyle = 2130772134; + + // aapt resource value: 0x7f010025 + public const int elevation = 2130772005; + + // aapt resource value: 0x7f010145 + public const int errorEnabled = 2130772293; + + // aapt resource value: 0x7f010146 + public const int errorTextAppearance = 2130772294; + + // aapt resource value: 0x7f010029 + public const int expandActivityOverflowButtonDrawable = 2130772009; + + // aapt resource value: 0x7f0100ff + public const int expanded = 2130772223; + + // aapt resource value: 0x7f010114 + public const int expandedTitleGravity = 2130772244; + + // aapt resource value: 0x7f010107 + public const int expandedTitleMargin = 2130772231; + + // aapt resource value: 0x7f01010b + public const int expandedTitleMarginBottom = 2130772235; + + // aapt resource value: 0x7f01010a + public const int expandedTitleMarginEnd = 2130772234; + + // aapt resource value: 0x7f010108 + public const int expandedTitleMarginStart = 2130772232; + + // aapt resource value: 0x7f010109 + public const int expandedTitleMarginTop = 2130772233; + + // aapt resource value: 0x7f01010c + public const int expandedTitleTextAppearance = 2130772236; + + // aapt resource value: 0x7f010124 + public const int fabSize = 2130772260; + + // aapt resource value: 0x7f010004 + public const int fastScrollEnabled = 2130771972; + + // aapt resource value: 0x7f010007 + public const int fastScrollHorizontalThumbDrawable = 2130771975; + + // aapt resource value: 0x7f010008 + public const int fastScrollHorizontalTrackDrawable = 2130771976; + + // aapt resource value: 0x7f010005 + public const int fastScrollVerticalThumbDrawable = 2130771973; + + // aapt resource value: 0x7f010006 + public const int fastScrollVerticalTrackDrawable = 2130771974; + + // aapt resource value: 0x7f010158 + public const int font = 2130772312; + + // aapt resource value: 0x7f01003c + public const int fontFamily = 2130772028; + + // aapt resource value: 0x7f010151 + public const int fontProviderAuthority = 2130772305; + + // aapt resource value: 0x7f010154 + public const int fontProviderCerts = 2130772308; + + // aapt resource value: 0x7f010155 + public const int fontProviderFetchStrategy = 2130772309; + + // aapt resource value: 0x7f010156 + public const int fontProviderFetchTimeout = 2130772310; + + // aapt resource value: 0x7f010152 + public const int fontProviderPackage = 2130772306; + + // aapt resource value: 0x7f010153 + public const int fontProviderQuery = 2130772307; + + // aapt resource value: 0x7f010157 + public const int fontStyle = 2130772311; + + // aapt resource value: 0x7f010159 + public const int fontWeight = 2130772313; + + // aapt resource value: 0x7f010129 + public const int foregroundInsidePadding = 2130772265; + + // aapt resource value: 0x7f0100b9 + public const int gapBetweenBars = 2130772153; + + // aapt resource value: 0x7f0100d6 + public const int goIcon = 2130772182; + + // aapt resource value: 0x7f01012f + public const int headerLayout = 2130772271; + + // aapt resource value: 0x7f01000a + public const int height = 2130771978; + + // aapt resource value: 0x7f01001e + public const int hideOnContentScroll = 2130771998; + + // aapt resource value: 0x7f01014b + public const int hintAnimationEnabled = 2130772299; + + // aapt resource value: 0x7f010144 + public const int hintEnabled = 2130772292; + + // aapt resource value: 0x7f010143 + public const int hintTextAppearance = 2130772291; + + // aapt resource value: 0x7f01006c + public const int homeAsUpIndicator = 2130772076; + + // aapt resource value: 0x7f010019 + public const int homeLayout = 2130771993; + + // aapt resource value: 0x7f010012 + public const int icon = 2130771986; + + // aapt resource value: 0x7f0100c9 + public const int iconTint = 2130772169; + + // aapt resource value: 0x7f0100ca + public const int iconTintMode = 2130772170; + + // aapt resource value: 0x7f0100d2 + public const int iconifiedByDefault = 2130772178; + + // aapt resource value: 0x7f01007c + public const int imageButtonStyle = 2130772092; + + // aapt resource value: 0x7f01001b + public const int indeterminateProgressStyle = 2130771995; + + // aapt resource value: 0x7f010028 + public const int initialActivityCount = 2130772008; + + // aapt resource value: 0x7f010130 + public const int insetForeground = 2130772272; + + // aapt resource value: 0x7f01000b + public const int isLightTheme = 2130771979; + + // aapt resource value: 0x7f01012d + public const int itemBackground = 2130772269; + + // aapt resource value: 0x7f01012b + public const int itemIconTint = 2130772267; + + // aapt resource value: 0x7f01001d + public const int itemPadding = 2130771997; + + // aapt resource value: 0x7f01012e + public const int itemTextAppearance = 2130772270; + + // aapt resource value: 0x7f01012c + public const int itemTextColor = 2130772268; + + // aapt resource value: 0x7f010118 + public const int keylines = 2130772248; + + // aapt resource value: 0x7f0100d1 + public const int layout = 2130772177; + + // aapt resource value: 0x7f010000 + public const int layoutManager = 2130771968; + + // aapt resource value: 0x7f01011b + public const int layout_anchor = 2130772251; + + // aapt resource value: 0x7f01011d + public const int layout_anchorGravity = 2130772253; + + // aapt resource value: 0x7f01011a + public const int layout_behavior = 2130772250; + + // aapt resource value: 0x7f010116 + public const int layout_collapseMode = 2130772246; + + // aapt resource value: 0x7f010117 + public const int layout_collapseParallaxMultiplier = 2130772247; + + // aapt resource value: 0x7f01011f + public const int layout_dodgeInsetEdges = 2130772255; + + // aapt resource value: 0x7f01011e + public const int layout_insetEdge = 2130772254; + + // aapt resource value: 0x7f01011c + public const int layout_keyline = 2130772252; + + // aapt resource value: 0x7f010102 + public const int layout_scrollFlags = 2130772226; + + // aapt resource value: 0x7f010103 + public const int layout_scrollInterpolator = 2130772227; + + // aapt resource value: 0x7f01008e + public const int listChoiceBackgroundIndicator = 2130772110; + + // aapt resource value: 0x7f010068 + public const int listDividerAlertDialog = 2130772072; + + // aapt resource value: 0x7f01002e + public const int listItemLayout = 2130772014; + + // aapt resource value: 0x7f01002b + public const int listLayout = 2130772011; + + // aapt resource value: 0x7f0100ae + public const int listMenuViewStyle = 2130772142; + + // aapt resource value: 0x7f010087 + public const int listPopupWindowStyle = 2130772103; + + // aapt resource value: 0x7f010081 + public const int listPreferredItemHeight = 2130772097; + + // aapt resource value: 0x7f010083 + public const int listPreferredItemHeightLarge = 2130772099; + + // aapt resource value: 0x7f010082 + public const int listPreferredItemHeightSmall = 2130772098; + + // aapt resource value: 0x7f010084 + public const int listPreferredItemPaddingLeft = 2130772100; + + // aapt resource value: 0x7f010085 + public const int listPreferredItemPaddingRight = 2130772101; + + // aapt resource value: 0x7f010013 + public const int logo = 2130771987; + + // aapt resource value: 0x7f0100f7 + public const int logoDescription = 2130772215; + + // aapt resource value: 0x7f010132 + public const int maxActionInlineWidth = 2130772274; + + // aapt resource value: 0x7f0100f1 + public const int maxButtonHeight = 2130772209; + + // aapt resource value: 0x7f0100be + public const int measureWithLargestChild = 2130772158; + + // aapt resource value: 0x7f01012a + public const int menu = 2130772266; + + // aapt resource value: 0x7f01002c + public const int multiChoiceItemLayout = 2130772012; + + // aapt resource value: 0x7f0100f6 + public const int navigationContentDescription = 2130772214; + + // aapt resource value: 0x7f0100f5 + public const int navigationIcon = 2130772213; + + // aapt resource value: 0x7f01000d + public const int navigationMode = 2130771981; + + // aapt resource value: 0x7f0100c2 + public const int numericModifiers = 2130772162; + + // aapt resource value: 0x7f0100cd + public const int overlapAnchor = 2130772173; + + // aapt resource value: 0x7f0100cf + public const int paddingBottomNoButtons = 2130772175; + + // aapt resource value: 0x7f0100fb + public const int paddingEnd = 2130772219; + + // aapt resource value: 0x7f0100fa + public const int paddingStart = 2130772218; + + // aapt resource value: 0x7f0100d0 + public const int paddingTopNoTitle = 2130772176; + + // aapt resource value: 0x7f01008b + public const int panelBackground = 2130772107; + + // aapt resource value: 0x7f01008d + public const int panelMenuListTheme = 2130772109; + + // aapt resource value: 0x7f01008c + public const int panelMenuListWidth = 2130772108; + + // aapt resource value: 0x7f01014e + public const int passwordToggleContentDescription = 2130772302; + + // aapt resource value: 0x7f01014d + public const int passwordToggleDrawable = 2130772301; + + // aapt resource value: 0x7f01014c + public const int passwordToggleEnabled = 2130772300; + + // aapt resource value: 0x7f01014f + public const int passwordToggleTint = 2130772303; + + // aapt resource value: 0x7f010150 + public const int passwordToggleTintMode = 2130772304; + + // aapt resource value: 0x7f010078 + public const int popupMenuStyle = 2130772088; + + // aapt resource value: 0x7f010026 + public const int popupTheme = 2130772006; + + // aapt resource value: 0x7f010079 + public const int popupWindowStyle = 2130772089; + + // aapt resource value: 0x7f0100cb + public const int preserveIconSpacing = 2130772171; + + // aapt resource value: 0x7f010125 + public const int pressedTranslationZ = 2130772261; + + // aapt resource value: 0x7f01001c + public const int progressBarPadding = 2130771996; + + // aapt resource value: 0x7f01001a + public const int progressBarStyle = 2130771994; + + // aapt resource value: 0x7f0100dc + public const int queryBackground = 2130772188; + + // aapt resource value: 0x7f0100d3 + public const int queryHint = 2130772179; + + // aapt resource value: 0x7f0100a7 + public const int radioButtonStyle = 2130772135; + + // aapt resource value: 0x7f0100a8 + public const int ratingBarStyle = 2130772136; + + // aapt resource value: 0x7f0100a9 + public const int ratingBarStyleIndicator = 2130772137; + + // aapt resource value: 0x7f0100aa + public const int ratingBarStyleSmall = 2130772138; + + // aapt resource value: 0x7f010002 + public const int reverseLayout = 2130771970; + + // aapt resource value: 0x7f010123 + public const int rippleColor = 2130772259; + + // aapt resource value: 0x7f010112 + public const int scrimAnimationDuration = 2130772242; + + // aapt resource value: 0x7f010111 + public const int scrimVisibleHeightTrigger = 2130772241; + + // aapt resource value: 0x7f0100d8 + public const int searchHintIcon = 2130772184; + + // aapt resource value: 0x7f0100d7 + public const int searchIcon = 2130772183; + + // aapt resource value: 0x7f010080 + public const int searchViewStyle = 2130772096; + + // aapt resource value: 0x7f0100ab + public const int seekBarStyle = 2130772139; + + // aapt resource value: 0x7f010070 + public const int selectableItemBackground = 2130772080; + + // aapt resource value: 0x7f010071 + public const int selectableItemBackgroundBorderless = 2130772081; + + // aapt resource value: 0x7f0100c3 + public const int showAsAction = 2130772163; + + // aapt resource value: 0x7f0100bf + public const int showDividers = 2130772159; + + // aapt resource value: 0x7f0100e8 + public const int showText = 2130772200; + + // aapt resource value: 0x7f01002f + public const int showTitle = 2130772015; + + // aapt resource value: 0x7f01002d + public const int singleChoiceItemLayout = 2130772013; + + // aapt resource value: 0x7f010001 + public const int spanCount = 2130771969; + + // aapt resource value: 0x7f0100b7 + public const int spinBars = 2130772151; + + // aapt resource value: 0x7f01006b + public const int spinnerDropDownItemStyle = 2130772075; + + // aapt resource value: 0x7f0100ac + public const int spinnerStyle = 2130772140; + + // aapt resource value: 0x7f0100e7 + public const int splitTrack = 2130772199; + + // aapt resource value: 0x7f010030 + public const int srcCompat = 2130772016; + + // aapt resource value: 0x7f010003 + public const int stackFromEnd = 2130771971; + + // aapt resource value: 0x7f0100ce + public const int state_above_anchor = 2130772174; + + // aapt resource value: 0x7f010100 + public const int state_collapsed = 2130772224; + + // aapt resource value: 0x7f010101 + public const int state_collapsible = 2130772225; + + // aapt resource value: 0x7f010119 + public const int statusBarBackground = 2130772249; + + // aapt resource value: 0x7f01010f + public const int statusBarScrim = 2130772239; + + // aapt resource value: 0x7f0100cc + public const int subMenuArrow = 2130772172; + + // aapt resource value: 0x7f0100dd + public const int submitBackground = 2130772189; + + // aapt resource value: 0x7f01000f + public const int subtitle = 2130771983; + + // aapt resource value: 0x7f0100ea + public const int subtitleTextAppearance = 2130772202; + + // aapt resource value: 0x7f0100f9 + public const int subtitleTextColor = 2130772217; + + // aapt resource value: 0x7f010011 + public const int subtitleTextStyle = 2130771985; + + // aapt resource value: 0x7f0100db + public const int suggestionRowLayout = 2130772187; + + // aapt resource value: 0x7f0100e5 + public const int switchMinWidth = 2130772197; + + // aapt resource value: 0x7f0100e6 + public const int switchPadding = 2130772198; + + // aapt resource value: 0x7f0100ad + public const int switchStyle = 2130772141; + + // aapt resource value: 0x7f0100e4 + public const int switchTextAppearance = 2130772196; + + // aapt resource value: 0x7f010136 + public const int tabBackground = 2130772278; + + // aapt resource value: 0x7f010135 + public const int tabContentStart = 2130772277; + + // aapt resource value: 0x7f010138 + public const int tabGravity = 2130772280; + + // aapt resource value: 0x7f010133 + public const int tabIndicatorColor = 2130772275; + + // aapt resource value: 0x7f010134 + public const int tabIndicatorHeight = 2130772276; + + // aapt resource value: 0x7f01013a + public const int tabMaxWidth = 2130772282; + + // aapt resource value: 0x7f010139 + public const int tabMinWidth = 2130772281; + + // aapt resource value: 0x7f010137 + public const int tabMode = 2130772279; + + // aapt resource value: 0x7f010142 + public const int tabPadding = 2130772290; + + // aapt resource value: 0x7f010141 + public const int tabPaddingBottom = 2130772289; + + // aapt resource value: 0x7f010140 + public const int tabPaddingEnd = 2130772288; + + // aapt resource value: 0x7f01013e + public const int tabPaddingStart = 2130772286; + + // aapt resource value: 0x7f01013f + public const int tabPaddingTop = 2130772287; + + // aapt resource value: 0x7f01013d + public const int tabSelectedTextColor = 2130772285; + + // aapt resource value: 0x7f01013b + public const int tabTextAppearance = 2130772283; + + // aapt resource value: 0x7f01013c + public const int tabTextColor = 2130772284; + + // aapt resource value: 0x7f010036 + public const int textAllCaps = 2130772022; + + // aapt resource value: 0x7f010063 + public const int textAppearanceLargePopupMenu = 2130772067; + + // aapt resource value: 0x7f010088 + public const int textAppearanceListItem = 2130772104; + + // aapt resource value: 0x7f010089 + public const int textAppearanceListItemSecondary = 2130772105; + + // aapt resource value: 0x7f01008a + public const int textAppearanceListItemSmall = 2130772106; + + // aapt resource value: 0x7f010065 + public const int textAppearancePopupMenuHeader = 2130772069; + + // aapt resource value: 0x7f01007e + public const int textAppearanceSearchResultSubtitle = 2130772094; + + // aapt resource value: 0x7f01007d + public const int textAppearanceSearchResultTitle = 2130772093; + + // aapt resource value: 0x7f010064 + public const int textAppearanceSmallPopupMenu = 2130772068; + + // aapt resource value: 0x7f01009d + public const int textColorAlertDialogListItem = 2130772125; + + // aapt resource value: 0x7f010122 + public const int textColorError = 2130772258; + + // aapt resource value: 0x7f01007f + public const int textColorSearchUrl = 2130772095; + + // aapt resource value: 0x7f0100fc + public const int theme = 2130772220; + + // aapt resource value: 0x7f0100bd + public const int thickness = 2130772157; + + // aapt resource value: 0x7f0100e3 + public const int thumbTextPadding = 2130772195; + + // aapt resource value: 0x7f0100de + public const int thumbTint = 2130772190; + + // aapt resource value: 0x7f0100df + public const int thumbTintMode = 2130772191; + + // aapt resource value: 0x7f010033 + public const int tickMark = 2130772019; + + // aapt resource value: 0x7f010034 + public const int tickMarkTint = 2130772020; + + // aapt resource value: 0x7f010035 + public const int tickMarkTintMode = 2130772021; + + // aapt resource value: 0x7f010031 + public const int tint = 2130772017; + + // aapt resource value: 0x7f010032 + public const int tintMode = 2130772018; + + // aapt resource value: 0x7f01000c + public const int title = 2130771980; + + // aapt resource value: 0x7f010115 + public const int titleEnabled = 2130772245; + + // aapt resource value: 0x7f0100eb + public const int titleMargin = 2130772203; + + // aapt resource value: 0x7f0100ef + public const int titleMarginBottom = 2130772207; + + // aapt resource value: 0x7f0100ed + public const int titleMarginEnd = 2130772205; + + // aapt resource value: 0x7f0100ec + public const int titleMarginStart = 2130772204; + + // aapt resource value: 0x7f0100ee + public const int titleMarginTop = 2130772206; + + // aapt resource value: 0x7f0100f0 + public const int titleMargins = 2130772208; + + // aapt resource value: 0x7f0100e9 + public const int titleTextAppearance = 2130772201; + + // aapt resource value: 0x7f0100f8 + public const int titleTextColor = 2130772216; + + // aapt resource value: 0x7f010010 + public const int titleTextStyle = 2130771984; + + // aapt resource value: 0x7f010110 + public const int toolbarId = 2130772240; + + // aapt resource value: 0x7f010077 + public const int toolbarNavigationButtonStyle = 2130772087; + + // aapt resource value: 0x7f010076 + public const int toolbarStyle = 2130772086; + + // aapt resource value: 0x7f0100b0 + public const int tooltipForegroundColor = 2130772144; + + // aapt resource value: 0x7f0100af + public const int tooltipFrameBackground = 2130772143; + + // aapt resource value: 0x7f0100c8 + public const int tooltipText = 2130772168; + + // aapt resource value: 0x7f0100e0 + public const int track = 2130772192; + + // aapt resource value: 0x7f0100e1 + public const int trackTint = 2130772193; + + // aapt resource value: 0x7f0100e2 + public const int trackTintMode = 2130772194; + + // aapt resource value: 0x7f010127 + public const int useCompatPadding = 2130772263; + + // aapt resource value: 0x7f0100d9 + public const int voiceIcon = 2130772185; + + // aapt resource value: 0x7f01003d + public const int windowActionBar = 2130772029; + + // aapt resource value: 0x7f01003f + public const int windowActionBarOverlay = 2130772031; + + // aapt resource value: 0x7f010040 + public const int windowActionModeOverlay = 2130772032; + + // aapt resource value: 0x7f010044 + public const int windowFixedHeightMajor = 2130772036; + + // aapt resource value: 0x7f010042 + public const int windowFixedHeightMinor = 2130772034; + + // aapt resource value: 0x7f010041 + public const int windowFixedWidthMajor = 2130772033; + + // aapt resource value: 0x7f010043 + public const int windowFixedWidthMinor = 2130772035; + + // aapt resource value: 0x7f010045 + public const int windowMinWidthMajor = 2130772037; + + // aapt resource value: 0x7f010046 + public const int windowMinWidthMinor = 2130772038; + + // aapt resource value: 0x7f01003e + public const int windowNoTitle = 2130772030; + + static Attribute() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Attribute() + { + } + } + + public partial class Boolean + { + + // aapt resource value: 0x7f0b0000 + public const int abc_action_bar_embed_tabs = 2131427328; + + // aapt resource value: 0x7f0b0001 + public const int abc_allow_stacked_button_bar = 2131427329; + + // aapt resource value: 0x7f0b0002 + public const int abc_config_actionMenuItemAllCaps = 2131427330; + + // aapt resource value: 0x7f0b0003 + public const int abc_config_closeDialogWhenTouchOutside = 2131427331; + + // aapt resource value: 0x7f0b0004 + public const int abc_config_showMenuShortcutsWhenKeyboardPresent = 2131427332; + + static Boolean() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Boolean() + { + } + } + + public partial class Color + { + + // aapt resource value: 0x7f0c004b + public const int abc_background_cache_hint_selector_material_dark = 2131492939; + + // aapt resource value: 0x7f0c004c + public const int abc_background_cache_hint_selector_material_light = 2131492940; + + // aapt resource value: 0x7f0c004d + public const int abc_btn_colored_borderless_text_material = 2131492941; + + // aapt resource value: 0x7f0c004e + public const int abc_btn_colored_text_material = 2131492942; + + // aapt resource value: 0x7f0c004f + public const int abc_color_highlight_material = 2131492943; + + // aapt resource value: 0x7f0c0050 + public const int abc_hint_foreground_material_dark = 2131492944; + + // aapt resource value: 0x7f0c0051 + public const int abc_hint_foreground_material_light = 2131492945; + + // aapt resource value: 0x7f0c0000 + public const int abc_input_method_navigation_guard = 2131492864; + + // aapt resource value: 0x7f0c0052 + public const int abc_primary_text_disable_only_material_dark = 2131492946; + + // aapt resource value: 0x7f0c0053 + public const int abc_primary_text_disable_only_material_light = 2131492947; + + // aapt resource value: 0x7f0c0054 + public const int abc_primary_text_material_dark = 2131492948; + + // aapt resource value: 0x7f0c0055 + public const int abc_primary_text_material_light = 2131492949; + + // aapt resource value: 0x7f0c0056 + public const int abc_search_url_text = 2131492950; + + // aapt resource value: 0x7f0c0001 + public const int abc_search_url_text_normal = 2131492865; + + // aapt resource value: 0x7f0c0002 + public const int abc_search_url_text_pressed = 2131492866; + + // aapt resource value: 0x7f0c0003 + public const int abc_search_url_text_selected = 2131492867; + + // aapt resource value: 0x7f0c0057 + public const int abc_secondary_text_material_dark = 2131492951; + + // aapt resource value: 0x7f0c0058 + public const int abc_secondary_text_material_light = 2131492952; + + // aapt resource value: 0x7f0c0059 + public const int abc_tint_btn_checkable = 2131492953; + + // aapt resource value: 0x7f0c005a + public const int abc_tint_default = 2131492954; + + // aapt resource value: 0x7f0c005b + public const int abc_tint_edittext = 2131492955; + + // aapt resource value: 0x7f0c005c + public const int abc_tint_seek_thumb = 2131492956; + + // aapt resource value: 0x7f0c005d + public const int abc_tint_spinner = 2131492957; + + // aapt resource value: 0x7f0c005e + public const int abc_tint_switch_track = 2131492958; + + // aapt resource value: 0x7f0c0004 + public const int accent_material_dark = 2131492868; + + // aapt resource value: 0x7f0c0005 + public const int accent_material_light = 2131492869; + + // aapt resource value: 0x7f0c0006 + public const int background_floating_material_dark = 2131492870; + + // aapt resource value: 0x7f0c0007 + public const int background_floating_material_light = 2131492871; + + // aapt resource value: 0x7f0c0008 + public const int background_material_dark = 2131492872; + + // aapt resource value: 0x7f0c0009 + public const int background_material_light = 2131492873; + + // aapt resource value: 0x7f0c000a + public const int bright_foreground_disabled_material_dark = 2131492874; + + // aapt resource value: 0x7f0c000b + public const int bright_foreground_disabled_material_light = 2131492875; + + // aapt resource value: 0x7f0c000c + public const int bright_foreground_inverse_material_dark = 2131492876; + + // aapt resource value: 0x7f0c000d + public const int bright_foreground_inverse_material_light = 2131492877; + + // aapt resource value: 0x7f0c000e + public const int bright_foreground_material_dark = 2131492878; + + // aapt resource value: 0x7f0c000f + public const int bright_foreground_material_light = 2131492879; + + // aapt resource value: 0x7f0c0010 + public const int button_material_dark = 2131492880; + + // aapt resource value: 0x7f0c0011 + public const int button_material_light = 2131492881; + + // aapt resource value: 0x7f0c0049 + public const int colorAccent = 2131492937; + + // aapt resource value: 0x7f0c0047 + public const int colorPrimary = 2131492935; + + // aapt resource value: 0x7f0c0048 + public const int colorPrimaryDark = 2131492936; + + // aapt resource value: 0x7f0c003c + public const int design_bottom_navigation_shadow_color = 2131492924; + + // aapt resource value: 0x7f0c005f + public const int design_error = 2131492959; + + // aapt resource value: 0x7f0c003d + public const int design_fab_shadow_end_color = 2131492925; + + // aapt resource value: 0x7f0c003e + public const int design_fab_shadow_mid_color = 2131492926; + + // aapt resource value: 0x7f0c003f + public const int design_fab_shadow_start_color = 2131492927; + + // aapt resource value: 0x7f0c0040 + public const int design_fab_stroke_end_inner_color = 2131492928; + + // aapt resource value: 0x7f0c0041 + public const int design_fab_stroke_end_outer_color = 2131492929; + + // aapt resource value: 0x7f0c0042 + public const int design_fab_stroke_top_inner_color = 2131492930; + + // aapt resource value: 0x7f0c0043 + public const int design_fab_stroke_top_outer_color = 2131492931; + + // aapt resource value: 0x7f0c0044 + public const int design_snackbar_background_color = 2131492932; + + // aapt resource value: 0x7f0c0060 + public const int design_tint_password_toggle = 2131492960; + + // aapt resource value: 0x7f0c0012 + public const int dim_foreground_disabled_material_dark = 2131492882; + + // aapt resource value: 0x7f0c0013 + public const int dim_foreground_disabled_material_light = 2131492883; + + // aapt resource value: 0x7f0c0014 + public const int dim_foreground_material_dark = 2131492884; + + // aapt resource value: 0x7f0c0015 + public const int dim_foreground_material_light = 2131492885; + + // aapt resource value: 0x7f0c0016 + public const int error_color_material = 2131492886; + + // aapt resource value: 0x7f0c0017 + public const int foreground_material_dark = 2131492887; + + // aapt resource value: 0x7f0c0018 + public const int foreground_material_light = 2131492888; + + // aapt resource value: 0x7f0c0019 + public const int highlighted_text_material_dark = 2131492889; + + // aapt resource value: 0x7f0c001a + public const int highlighted_text_material_light = 2131492890; + + // aapt resource value: 0x7f0c004a + public const int ic_launcher_background = 2131492938; + + // aapt resource value: 0x7f0c001b + public const int material_blue_grey_800 = 2131492891; + + // aapt resource value: 0x7f0c001c + public const int material_blue_grey_900 = 2131492892; + + // aapt resource value: 0x7f0c001d + public const int material_blue_grey_950 = 2131492893; + + // aapt resource value: 0x7f0c001e + public const int material_deep_teal_200 = 2131492894; + + // aapt resource value: 0x7f0c001f + public const int material_deep_teal_500 = 2131492895; + + // aapt resource value: 0x7f0c0020 + public const int material_grey_100 = 2131492896; + + // aapt resource value: 0x7f0c0021 + public const int material_grey_300 = 2131492897; + + // aapt resource value: 0x7f0c0022 + public const int material_grey_50 = 2131492898; + + // aapt resource value: 0x7f0c0023 + public const int material_grey_600 = 2131492899; + + // aapt resource value: 0x7f0c0024 + public const int material_grey_800 = 2131492900; + + // aapt resource value: 0x7f0c0025 + public const int material_grey_850 = 2131492901; + + // aapt resource value: 0x7f0c0026 + public const int material_grey_900 = 2131492902; + + // aapt resource value: 0x7f0c0045 + public const int notification_action_color_filter = 2131492933; + + // aapt resource value: 0x7f0c0046 + public const int notification_icon_bg_color = 2131492934; + + // aapt resource value: 0x7f0c003b + public const int notification_material_background_media_default_color = 2131492923; + + // aapt resource value: 0x7f0c0027 + public const int primary_dark_material_dark = 2131492903; + + // aapt resource value: 0x7f0c0028 + public const int primary_dark_material_light = 2131492904; + + // aapt resource value: 0x7f0c0029 + public const int primary_material_dark = 2131492905; + + // aapt resource value: 0x7f0c002a + public const int primary_material_light = 2131492906; + + // aapt resource value: 0x7f0c002b + public const int primary_text_default_material_dark = 2131492907; + + // aapt resource value: 0x7f0c002c + public const int primary_text_default_material_light = 2131492908; + + // aapt resource value: 0x7f0c002d + public const int primary_text_disabled_material_dark = 2131492909; + + // aapt resource value: 0x7f0c002e + public const int primary_text_disabled_material_light = 2131492910; + + // aapt resource value: 0x7f0c002f + public const int ripple_material_dark = 2131492911; + + // aapt resource value: 0x7f0c0030 + public const int ripple_material_light = 2131492912; + + // aapt resource value: 0x7f0c0031 + public const int secondary_text_default_material_dark = 2131492913; + + // aapt resource value: 0x7f0c0032 + public const int secondary_text_default_material_light = 2131492914; + + // aapt resource value: 0x7f0c0033 + public const int secondary_text_disabled_material_dark = 2131492915; + + // aapt resource value: 0x7f0c0034 + public const int secondary_text_disabled_material_light = 2131492916; + + // aapt resource value: 0x7f0c0035 + public const int switch_thumb_disabled_material_dark = 2131492917; + + // aapt resource value: 0x7f0c0036 + public const int switch_thumb_disabled_material_light = 2131492918; + + // aapt resource value: 0x7f0c0061 + public const int switch_thumb_material_dark = 2131492961; + + // aapt resource value: 0x7f0c0062 + public const int switch_thumb_material_light = 2131492962; + + // aapt resource value: 0x7f0c0037 + public const int switch_thumb_normal_material_dark = 2131492919; + + // aapt resource value: 0x7f0c0038 + public const int switch_thumb_normal_material_light = 2131492920; + + // aapt resource value: 0x7f0c0039 + public const int tooltip_background_dark = 2131492921; + + // aapt resource value: 0x7f0c003a + public const int tooltip_background_light = 2131492922; + + static Color() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Color() + { + } + } + + public partial class Dimension + { + + // aapt resource value: 0x7f070012 + public const int abc_action_bar_content_inset_material = 2131165202; + + // aapt resource value: 0x7f070013 + public const int abc_action_bar_content_inset_with_nav = 2131165203; + + // aapt resource value: 0x7f070007 + public const int abc_action_bar_default_height_material = 2131165191; + + // aapt resource value: 0x7f070014 + public const int abc_action_bar_default_padding_end_material = 2131165204; + + // aapt resource value: 0x7f070015 + public const int abc_action_bar_default_padding_start_material = 2131165205; + + // aapt resource value: 0x7f070017 + public const int abc_action_bar_elevation_material = 2131165207; + + // aapt resource value: 0x7f070018 + public const int abc_action_bar_icon_vertical_padding_material = 2131165208; + + // aapt resource value: 0x7f070019 + public const int abc_action_bar_overflow_padding_end_material = 2131165209; + + // aapt resource value: 0x7f07001a + public const int abc_action_bar_overflow_padding_start_material = 2131165210; + + // aapt resource value: 0x7f070008 + public const int abc_action_bar_progress_bar_size = 2131165192; + + // aapt resource value: 0x7f07001b + public const int abc_action_bar_stacked_max_height = 2131165211; + + // aapt resource value: 0x7f07001c + public const int abc_action_bar_stacked_tab_max_width = 2131165212; + + // aapt resource value: 0x7f07001d + public const int abc_action_bar_subtitle_bottom_margin_material = 2131165213; + + // aapt resource value: 0x7f07001e + public const int abc_action_bar_subtitle_top_margin_material = 2131165214; + + // aapt resource value: 0x7f07001f + public const int abc_action_button_min_height_material = 2131165215; + + // aapt resource value: 0x7f070020 + public const int abc_action_button_min_width_material = 2131165216; + + // aapt resource value: 0x7f070021 + public const int abc_action_button_min_width_overflow_material = 2131165217; + + // aapt resource value: 0x7f070006 + public const int abc_alert_dialog_button_bar_height = 2131165190; + + // aapt resource value: 0x7f070022 + public const int abc_button_inset_horizontal_material = 2131165218; + + // aapt resource value: 0x7f070023 + public const int abc_button_inset_vertical_material = 2131165219; + + // aapt resource value: 0x7f070024 + public const int abc_button_padding_horizontal_material = 2131165220; + + // aapt resource value: 0x7f070025 + public const int abc_button_padding_vertical_material = 2131165221; + + // aapt resource value: 0x7f070026 + public const int abc_cascading_menus_min_smallest_width = 2131165222; + + // aapt resource value: 0x7f07000b + public const int abc_config_prefDialogWidth = 2131165195; + + // aapt resource value: 0x7f070027 + public const int abc_control_corner_material = 2131165223; + + // aapt resource value: 0x7f070028 + public const int abc_control_inset_material = 2131165224; + + // aapt resource value: 0x7f070029 + public const int abc_control_padding_material = 2131165225; + + // aapt resource value: 0x7f07000c + public const int abc_dialog_fixed_height_major = 2131165196; + + // aapt resource value: 0x7f07000d + public const int abc_dialog_fixed_height_minor = 2131165197; + + // aapt resource value: 0x7f07000e + public const int abc_dialog_fixed_width_major = 2131165198; + + // aapt resource value: 0x7f07000f + public const int abc_dialog_fixed_width_minor = 2131165199; + + // aapt resource value: 0x7f07002a + public const int abc_dialog_list_padding_bottom_no_buttons = 2131165226; + + // aapt resource value: 0x7f07002b + public const int abc_dialog_list_padding_top_no_title = 2131165227; + + // aapt resource value: 0x7f070010 + public const int abc_dialog_min_width_major = 2131165200; + + // aapt resource value: 0x7f070011 + public const int abc_dialog_min_width_minor = 2131165201; + + // aapt resource value: 0x7f07002c + public const int abc_dialog_padding_material = 2131165228; + + // aapt resource value: 0x7f07002d + public const int abc_dialog_padding_top_material = 2131165229; + + // aapt resource value: 0x7f07002e + public const int abc_dialog_title_divider_material = 2131165230; + + // aapt resource value: 0x7f07002f + public const int abc_disabled_alpha_material_dark = 2131165231; + + // aapt resource value: 0x7f070030 + public const int abc_disabled_alpha_material_light = 2131165232; + + // aapt resource value: 0x7f070031 + public const int abc_dropdownitem_icon_width = 2131165233; + + // aapt resource value: 0x7f070032 + public const int abc_dropdownitem_text_padding_left = 2131165234; + + // aapt resource value: 0x7f070033 + public const int abc_dropdownitem_text_padding_right = 2131165235; + + // aapt resource value: 0x7f070034 + public const int abc_edit_text_inset_bottom_material = 2131165236; + + // aapt resource value: 0x7f070035 + public const int abc_edit_text_inset_horizontal_material = 2131165237; + + // aapt resource value: 0x7f070036 + public const int abc_edit_text_inset_top_material = 2131165238; + + // aapt resource value: 0x7f070037 + public const int abc_floating_window_z = 2131165239; + + // aapt resource value: 0x7f070038 + public const int abc_list_item_padding_horizontal_material = 2131165240; + + // aapt resource value: 0x7f070039 + public const int abc_panel_menu_list_width = 2131165241; + + // aapt resource value: 0x7f07003a + public const int abc_progress_bar_height_material = 2131165242; + + // aapt resource value: 0x7f07003b + public const int abc_search_view_preferred_height = 2131165243; + + // aapt resource value: 0x7f07003c + public const int abc_search_view_preferred_width = 2131165244; + + // aapt resource value: 0x7f07003d + public const int abc_seekbar_track_background_height_material = 2131165245; + + // aapt resource value: 0x7f07003e + public const int abc_seekbar_track_progress_height_material = 2131165246; + + // aapt resource value: 0x7f07003f + public const int abc_select_dialog_padding_start_material = 2131165247; + + // aapt resource value: 0x7f070016 + public const int abc_switch_padding = 2131165206; + + // aapt resource value: 0x7f070040 + public const int abc_text_size_body_1_material = 2131165248; + + // aapt resource value: 0x7f070041 + public const int abc_text_size_body_2_material = 2131165249; + + // aapt resource value: 0x7f070042 + public const int abc_text_size_button_material = 2131165250; + + // aapt resource value: 0x7f070043 + public const int abc_text_size_caption_material = 2131165251; + + // aapt resource value: 0x7f070044 + public const int abc_text_size_display_1_material = 2131165252; + + // aapt resource value: 0x7f070045 + public const int abc_text_size_display_2_material = 2131165253; + + // aapt resource value: 0x7f070046 + public const int abc_text_size_display_3_material = 2131165254; + + // aapt resource value: 0x7f070047 + public const int abc_text_size_display_4_material = 2131165255; + + // aapt resource value: 0x7f070048 + public const int abc_text_size_headline_material = 2131165256; + + // aapt resource value: 0x7f070049 + public const int abc_text_size_large_material = 2131165257; + + // aapt resource value: 0x7f07004a + public const int abc_text_size_medium_material = 2131165258; + + // aapt resource value: 0x7f07004b + public const int abc_text_size_menu_header_material = 2131165259; + + // aapt resource value: 0x7f07004c + public const int abc_text_size_menu_material = 2131165260; + + // aapt resource value: 0x7f07004d + public const int abc_text_size_small_material = 2131165261; + + // aapt resource value: 0x7f07004e + public const int abc_text_size_subhead_material = 2131165262; + + // aapt resource value: 0x7f070009 + public const int abc_text_size_subtitle_material_toolbar = 2131165193; + + // aapt resource value: 0x7f07004f + public const int abc_text_size_title_material = 2131165263; + + // aapt resource value: 0x7f07000a + public const int abc_text_size_title_material_toolbar = 2131165194; + + // aapt resource value: 0x7f07008b + public const int compat_button_inset_horizontal_material = 2131165323; + + // aapt resource value: 0x7f07008c + public const int compat_button_inset_vertical_material = 2131165324; + + // aapt resource value: 0x7f07008d + public const int compat_button_padding_horizontal_material = 2131165325; + + // aapt resource value: 0x7f07008e + public const int compat_button_padding_vertical_material = 2131165326; + + // aapt resource value: 0x7f07008f + public const int compat_control_corner_material = 2131165327; + + // aapt resource value: 0x7f070069 + public const int design_appbar_elevation = 2131165289; + + // aapt resource value: 0x7f07006a + public const int design_bottom_navigation_active_item_max_width = 2131165290; + + // aapt resource value: 0x7f07006b + public const int design_bottom_navigation_active_text_size = 2131165291; + + // aapt resource value: 0x7f07006c + public const int design_bottom_navigation_elevation = 2131165292; + + // aapt resource value: 0x7f07006d + public const int design_bottom_navigation_height = 2131165293; + + // aapt resource value: 0x7f07006e + public const int design_bottom_navigation_item_max_width = 2131165294; + + // aapt resource value: 0x7f07006f + public const int design_bottom_navigation_item_min_width = 2131165295; + + // aapt resource value: 0x7f070070 + public const int design_bottom_navigation_margin = 2131165296; + + // aapt resource value: 0x7f070071 + public const int design_bottom_navigation_shadow_height = 2131165297; + + // aapt resource value: 0x7f070072 + public const int design_bottom_navigation_text_size = 2131165298; + + // aapt resource value: 0x7f070073 + public const int design_bottom_sheet_modal_elevation = 2131165299; + + // aapt resource value: 0x7f070074 + public const int design_bottom_sheet_peek_height_min = 2131165300; + + // aapt resource value: 0x7f070075 + public const int design_fab_border_width = 2131165301; + + // aapt resource value: 0x7f070076 + public const int design_fab_elevation = 2131165302; + + // aapt resource value: 0x7f070077 + public const int design_fab_image_size = 2131165303; + + // aapt resource value: 0x7f070078 + public const int design_fab_size_mini = 2131165304; + + // aapt resource value: 0x7f070079 + public const int design_fab_size_normal = 2131165305; + + // aapt resource value: 0x7f07007a + public const int design_fab_translation_z_pressed = 2131165306; + + // aapt resource value: 0x7f07007b + public const int design_navigation_elevation = 2131165307; + + // aapt resource value: 0x7f07007c + public const int design_navigation_icon_padding = 2131165308; + + // aapt resource value: 0x7f07007d + public const int design_navigation_icon_size = 2131165309; + + // aapt resource value: 0x7f070061 + public const int design_navigation_max_width = 2131165281; + + // aapt resource value: 0x7f07007e + public const int design_navigation_padding_bottom = 2131165310; + + // aapt resource value: 0x7f07007f + public const int design_navigation_separator_vertical_padding = 2131165311; + + // aapt resource value: 0x7f070062 + public const int design_snackbar_action_inline_max_width = 2131165282; + + // aapt resource value: 0x7f070063 + public const int design_snackbar_background_corner_radius = 2131165283; + + // aapt resource value: 0x7f070080 + public const int design_snackbar_elevation = 2131165312; + + // aapt resource value: 0x7f070064 + public const int design_snackbar_extra_spacing_horizontal = 2131165284; + + // aapt resource value: 0x7f070065 + public const int design_snackbar_max_width = 2131165285; + + // aapt resource value: 0x7f070066 + public const int design_snackbar_min_width = 2131165286; + + // aapt resource value: 0x7f070081 + public const int design_snackbar_padding_horizontal = 2131165313; + + // aapt resource value: 0x7f070082 + public const int design_snackbar_padding_vertical = 2131165314; + + // aapt resource value: 0x7f070067 + public const int design_snackbar_padding_vertical_2lines = 2131165287; + + // aapt resource value: 0x7f070083 + public const int design_snackbar_text_size = 2131165315; + + // aapt resource value: 0x7f070084 + public const int design_tab_max_width = 2131165316; + + // aapt resource value: 0x7f070068 + public const int design_tab_scrollable_min_width = 2131165288; + + // aapt resource value: 0x7f070085 + public const int design_tab_text_size = 2131165317; + + // aapt resource value: 0x7f070086 + public const int design_tab_text_size_2line = 2131165318; + + // aapt resource value: 0x7f070050 + public const int disabled_alpha_material_dark = 2131165264; + + // aapt resource value: 0x7f070051 + public const int disabled_alpha_material_light = 2131165265; + + // aapt resource value: 0x7f070000 + public const int fastscroll_default_thickness = 2131165184; + + // aapt resource value: 0x7f070001 + public const int fastscroll_margin = 2131165185; + + // aapt resource value: 0x7f070002 + public const int fastscroll_minimum_range = 2131165186; + + // aapt resource value: 0x7f070052 + public const int highlight_alpha_material_colored = 2131165266; + + // aapt resource value: 0x7f070053 + public const int highlight_alpha_material_dark = 2131165267; + + // aapt resource value: 0x7f070054 + public const int highlight_alpha_material_light = 2131165268; + + // aapt resource value: 0x7f070055 + public const int hint_alpha_material_dark = 2131165269; + + // aapt resource value: 0x7f070056 + public const int hint_alpha_material_light = 2131165270; + + // aapt resource value: 0x7f070057 + public const int hint_pressed_alpha_material_dark = 2131165271; + + // aapt resource value: 0x7f070058 + public const int hint_pressed_alpha_material_light = 2131165272; + + // aapt resource value: 0x7f070003 + public const int item_touch_helper_max_drag_scroll_per_frame = 2131165187; + + // aapt resource value: 0x7f070004 + public const int item_touch_helper_swipe_escape_max_velocity = 2131165188; + + // aapt resource value: 0x7f070005 + public const int item_touch_helper_swipe_escape_velocity = 2131165189; + + // aapt resource value: 0x7f070090 + public const int notification_action_icon_size = 2131165328; + + // aapt resource value: 0x7f070091 + public const int notification_action_text_size = 2131165329; + + // aapt resource value: 0x7f070092 + public const int notification_big_circle_margin = 2131165330; + + // aapt resource value: 0x7f070088 + public const int notification_content_margin_start = 2131165320; + + // aapt resource value: 0x7f070093 + public const int notification_large_icon_height = 2131165331; + + // aapt resource value: 0x7f070094 + public const int notification_large_icon_width = 2131165332; + + // aapt resource value: 0x7f070089 + public const int notification_main_column_padding_top = 2131165321; + + // aapt resource value: 0x7f07008a + public const int notification_media_narrow_margin = 2131165322; + + // aapt resource value: 0x7f070095 + public const int notification_right_icon_size = 2131165333; + + // aapt resource value: 0x7f070087 + public const int notification_right_side_padding_top = 2131165319; + + // aapt resource value: 0x7f070096 + public const int notification_small_icon_background_padding = 2131165334; + + // aapt resource value: 0x7f070097 + public const int notification_small_icon_size_as_large = 2131165335; + + // aapt resource value: 0x7f070098 + public const int notification_subtext_size = 2131165336; + + // aapt resource value: 0x7f070099 + public const int notification_top_pad = 2131165337; + + // aapt resource value: 0x7f07009a + public const int notification_top_pad_large_text = 2131165338; + + // aapt resource value: 0x7f070059 + public const int tooltip_corner_radius = 2131165273; + + // aapt resource value: 0x7f07005a + public const int tooltip_horizontal_padding = 2131165274; + + // aapt resource value: 0x7f07005b + public const int tooltip_margin = 2131165275; + + // aapt resource value: 0x7f07005c + public const int tooltip_precise_anchor_extra_offset = 2131165276; + + // aapt resource value: 0x7f07005d + public const int tooltip_precise_anchor_threshold = 2131165277; + + // aapt resource value: 0x7f07005e + public const int tooltip_vertical_padding = 2131165278; + + // aapt resource value: 0x7f07005f + public const int tooltip_y_offset_non_touch = 2131165279; + + // aapt resource value: 0x7f070060 + public const int tooltip_y_offset_touch = 2131165280; + + static Dimension() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Dimension() + { + } + } + + public partial class Drawable + { + + // aapt resource value: 0x7f020000 + public const int abc_ab_share_pack_mtrl_alpha = 2130837504; + + // aapt resource value: 0x7f020001 + public const int abc_action_bar_item_background_material = 2130837505; + + // aapt resource value: 0x7f020002 + public const int abc_btn_borderless_material = 2130837506; + + // aapt resource value: 0x7f020003 + public const int abc_btn_check_material = 2130837507; + + // aapt resource value: 0x7f020004 + public const int abc_btn_check_to_on_mtrl_000 = 2130837508; + + // aapt resource value: 0x7f020005 + public const int abc_btn_check_to_on_mtrl_015 = 2130837509; + + // aapt resource value: 0x7f020006 + public const int abc_btn_colored_material = 2130837510; + + // aapt resource value: 0x7f020007 + public const int abc_btn_default_mtrl_shape = 2130837511; + + // aapt resource value: 0x7f020008 + public const int abc_btn_radio_material = 2130837512; + + // aapt resource value: 0x7f020009 + public const int abc_btn_radio_to_on_mtrl_000 = 2130837513; + + // aapt resource value: 0x7f02000a + public const int abc_btn_radio_to_on_mtrl_015 = 2130837514; + + // aapt resource value: 0x7f02000b + public const int abc_btn_switch_to_on_mtrl_00001 = 2130837515; + + // aapt resource value: 0x7f02000c + public const int abc_btn_switch_to_on_mtrl_00012 = 2130837516; + + // aapt resource value: 0x7f02000d + public const int abc_cab_background_internal_bg = 2130837517; + + // aapt resource value: 0x7f02000e + public const int abc_cab_background_top_material = 2130837518; + + // aapt resource value: 0x7f02000f + public const int abc_cab_background_top_mtrl_alpha = 2130837519; + + // aapt resource value: 0x7f020010 + public const int abc_control_background_material = 2130837520; + + // aapt resource value: 0x7f020011 + public const int abc_dialog_material_background = 2130837521; + + // aapt resource value: 0x7f020012 + public const int abc_edit_text_material = 2130837522; + + // aapt resource value: 0x7f020013 + public const int abc_ic_ab_back_material = 2130837523; + + // aapt resource value: 0x7f020014 + public const int abc_ic_arrow_drop_right_black_24dp = 2130837524; + + // aapt resource value: 0x7f020015 + public const int abc_ic_clear_material = 2130837525; + + // aapt resource value: 0x7f020016 + public const int abc_ic_commit_search_api_mtrl_alpha = 2130837526; + + // aapt resource value: 0x7f020017 + public const int abc_ic_go_search_api_material = 2130837527; + + // aapt resource value: 0x7f020018 + public const int abc_ic_menu_copy_mtrl_am_alpha = 2130837528; + + // aapt resource value: 0x7f020019 + public const int abc_ic_menu_cut_mtrl_alpha = 2130837529; + + // aapt resource value: 0x7f02001a + public const int abc_ic_menu_overflow_material = 2130837530; + + // aapt resource value: 0x7f02001b + public const int abc_ic_menu_paste_mtrl_am_alpha = 2130837531; + + // aapt resource value: 0x7f02001c + public const int abc_ic_menu_selectall_mtrl_alpha = 2130837532; + + // aapt resource value: 0x7f02001d + public const int abc_ic_menu_share_mtrl_alpha = 2130837533; + + // aapt resource value: 0x7f02001e + public const int abc_ic_search_api_material = 2130837534; + + // aapt resource value: 0x7f02001f + public const int abc_ic_star_black_16dp = 2130837535; + + // aapt resource value: 0x7f020020 + public const int abc_ic_star_black_36dp = 2130837536; + + // aapt resource value: 0x7f020021 + public const int abc_ic_star_black_48dp = 2130837537; + + // aapt resource value: 0x7f020022 + public const int abc_ic_star_half_black_16dp = 2130837538; + + // aapt resource value: 0x7f020023 + public const int abc_ic_star_half_black_36dp = 2130837539; + + // aapt resource value: 0x7f020024 + public const int abc_ic_star_half_black_48dp = 2130837540; + + // aapt resource value: 0x7f020025 + public const int abc_ic_voice_search_api_material = 2130837541; + + // aapt resource value: 0x7f020026 + public const int abc_item_background_holo_dark = 2130837542; + + // aapt resource value: 0x7f020027 + public const int abc_item_background_holo_light = 2130837543; + + // aapt resource value: 0x7f020028 + public const int abc_list_divider_mtrl_alpha = 2130837544; + + // aapt resource value: 0x7f020029 + public const int abc_list_focused_holo = 2130837545; + + // aapt resource value: 0x7f02002a + public const int abc_list_longpressed_holo = 2130837546; + + // aapt resource value: 0x7f02002b + public const int abc_list_pressed_holo_dark = 2130837547; + + // aapt resource value: 0x7f02002c + public const int abc_list_pressed_holo_light = 2130837548; + + // aapt resource value: 0x7f02002d + public const int abc_list_selector_background_transition_holo_dark = 2130837549; + + // aapt resource value: 0x7f02002e + public const int abc_list_selector_background_transition_holo_light = 2130837550; + + // aapt resource value: 0x7f02002f + public const int abc_list_selector_disabled_holo_dark = 2130837551; + + // aapt resource value: 0x7f020030 + public const int abc_list_selector_disabled_holo_light = 2130837552; + + // aapt resource value: 0x7f020031 + public const int abc_list_selector_holo_dark = 2130837553; + + // aapt resource value: 0x7f020032 + public const int abc_list_selector_holo_light = 2130837554; + + // aapt resource value: 0x7f020033 + public const int abc_menu_hardkey_panel_mtrl_mult = 2130837555; + + // aapt resource value: 0x7f020034 + public const int abc_popup_background_mtrl_mult = 2130837556; + + // aapt resource value: 0x7f020035 + public const int abc_ratingbar_indicator_material = 2130837557; + + // aapt resource value: 0x7f020036 + public const int abc_ratingbar_material = 2130837558; + + // aapt resource value: 0x7f020037 + public const int abc_ratingbar_small_material = 2130837559; + + // aapt resource value: 0x7f020038 + public const int abc_scrubber_control_off_mtrl_alpha = 2130837560; + + // aapt resource value: 0x7f020039 + public const int abc_scrubber_control_to_pressed_mtrl_000 = 2130837561; + + // aapt resource value: 0x7f02003a + public const int abc_scrubber_control_to_pressed_mtrl_005 = 2130837562; + + // aapt resource value: 0x7f02003b + public const int abc_scrubber_primary_mtrl_alpha = 2130837563; + + // aapt resource value: 0x7f02003c + public const int abc_scrubber_track_mtrl_alpha = 2130837564; + + // aapt resource value: 0x7f02003d + public const int abc_seekbar_thumb_material = 2130837565; + + // aapt resource value: 0x7f02003e + public const int abc_seekbar_tick_mark_material = 2130837566; + + // aapt resource value: 0x7f02003f + public const int abc_seekbar_track_material = 2130837567; + + // aapt resource value: 0x7f020040 + public const int abc_spinner_mtrl_am_alpha = 2130837568; + + // aapt resource value: 0x7f020041 + public const int abc_spinner_textfield_background_material = 2130837569; + + // aapt resource value: 0x7f020042 + public const int abc_switch_thumb_material = 2130837570; + + // aapt resource value: 0x7f020043 + public const int abc_switch_track_mtrl_alpha = 2130837571; + + // aapt resource value: 0x7f020044 + public const int abc_tab_indicator_material = 2130837572; + + // aapt resource value: 0x7f020045 + public const int abc_tab_indicator_mtrl_alpha = 2130837573; + + // aapt resource value: 0x7f020046 + public const int abc_text_cursor_material = 2130837574; + + // aapt resource value: 0x7f020047 + public const int abc_text_select_handle_left_mtrl_dark = 2130837575; + + // aapt resource value: 0x7f020048 + public const int abc_text_select_handle_left_mtrl_light = 2130837576; + + // aapt resource value: 0x7f020049 + public const int abc_text_select_handle_middle_mtrl_dark = 2130837577; + + // aapt resource value: 0x7f02004a + public const int abc_text_select_handle_middle_mtrl_light = 2130837578; + + // aapt resource value: 0x7f02004b + public const int abc_text_select_handle_right_mtrl_dark = 2130837579; + + // aapt resource value: 0x7f02004c + public const int abc_text_select_handle_right_mtrl_light = 2130837580; + + // aapt resource value: 0x7f02004d + public const int abc_textfield_activated_mtrl_alpha = 2130837581; + + // aapt resource value: 0x7f02004e + public const int abc_textfield_default_mtrl_alpha = 2130837582; + + // aapt resource value: 0x7f02004f + public const int abc_textfield_search_activated_mtrl_alpha = 2130837583; + + // aapt resource value: 0x7f020050 + public const int abc_textfield_search_default_mtrl_alpha = 2130837584; + + // aapt resource value: 0x7f020051 + public const int abc_textfield_search_material = 2130837585; + + // aapt resource value: 0x7f020052 + public const int abc_vector_test = 2130837586; + + // aapt resource value: 0x7f020053 + public const int avd_hide_password = 2130837587; + + // aapt resource value: 0x7f02006a + public const int avd_hide_password_1 = 2130837610; + + // aapt resource value: 0x7f02006b + public const int avd_hide_password_2 = 2130837611; + + // aapt resource value: 0x7f02006c + public const int avd_hide_password_3 = 2130837612; + + // aapt resource value: 0x7f020054 + public const int avd_show_password = 2130837588; + + // aapt resource value: 0x7f02006d + public const int avd_show_password_1 = 2130837613; + + // aapt resource value: 0x7f02006e + public const int avd_show_password_2 = 2130837614; + + // aapt resource value: 0x7f02006f + public const int avd_show_password_3 = 2130837615; + + // aapt resource value: 0x7f020055 + public const int design_bottom_navigation_item_background = 2130837589; + + // aapt resource value: 0x7f020056 + public const int design_fab_background = 2130837590; + + // aapt resource value: 0x7f020057 + public const int design_ic_visibility = 2130837591; + + // aapt resource value: 0x7f020058 + public const int design_ic_visibility_off = 2130837592; + + // aapt resource value: 0x7f020059 + public const int design_password_eye = 2130837593; + + // aapt resource value: 0x7f02005a + public const int design_snackbar_background = 2130837594; + + // aapt resource value: 0x7f02005b + public const int navigation_empty_icon = 2130837595; + + // aapt resource value: 0x7f02005c + public const int notification_action_background = 2130837596; + + // aapt resource value: 0x7f02005d + public const int notification_bg = 2130837597; + + // aapt resource value: 0x7f02005e + public const int notification_bg_low = 2130837598; + + // aapt resource value: 0x7f02005f + public const int notification_bg_low_normal = 2130837599; + + // aapt resource value: 0x7f020060 + public const int notification_bg_low_pressed = 2130837600; + + // aapt resource value: 0x7f020061 + public const int notification_bg_normal = 2130837601; + + // aapt resource value: 0x7f020062 + public const int notification_bg_normal_pressed = 2130837602; + + // aapt resource value: 0x7f020063 + public const int notification_icon_background = 2130837603; + + // aapt resource value: 0x7f020068 + public const int notification_template_icon_bg = 2130837608; + + // aapt resource value: 0x7f020069 + public const int notification_template_icon_low_bg = 2130837609; + + // aapt resource value: 0x7f020064 + public const int notification_tile_bg = 2130837604; + + // aapt resource value: 0x7f020065 + public const int notify_panel_notification_icon_bg = 2130837605; + + // aapt resource value: 0x7f020066 + public const int tooltip_frame_dark = 2130837606; + + // aapt resource value: 0x7f020067 + public const int tooltip_frame_light = 2130837607; + + static Drawable() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Drawable() + { + } + } + + public partial class Id + { + + // aapt resource value: 0x7f080032 + public const int ALT = 2131230770; + + // aapt resource value: 0x7f080033 + public const int CTRL = 2131230771; + + // aapt resource value: 0x7f080034 + public const int FUNCTION = 2131230772; + + // aapt resource value: 0x7f080035 + public const int META = 2131230773; + + // aapt resource value: 0x7f080036 + public const int SHIFT = 2131230774; + + // aapt resource value: 0x7f080037 + public const int SYM = 2131230775; + + // aapt resource value: 0x7f08009d + public const int action0 = 2131230877; + + // aapt resource value: 0x7f08007c + public const int action_bar = 2131230844; + + // aapt resource value: 0x7f080001 + public const int action_bar_activity_content = 2131230721; + + // aapt resource value: 0x7f08007b + public const int action_bar_container = 2131230843; + + // aapt resource value: 0x7f080077 + public const int action_bar_root = 2131230839; + + // aapt resource value: 0x7f080002 + public const int action_bar_spinner = 2131230722; + + // aapt resource value: 0x7f08005b + public const int action_bar_subtitle = 2131230811; + + // aapt resource value: 0x7f08005a + public const int action_bar_title = 2131230810; + + // aapt resource value: 0x7f08009a + public const int action_container = 2131230874; + + // aapt resource value: 0x7f08007d + public const int action_context_bar = 2131230845; + + // aapt resource value: 0x7f0800a1 + public const int action_divider = 2131230881; + + // aapt resource value: 0x7f08009b + public const int action_image = 2131230875; + + // aapt resource value: 0x7f080003 + public const int action_menu_divider = 2131230723; + + // aapt resource value: 0x7f080004 + public const int action_menu_presenter = 2131230724; + + // aapt resource value: 0x7f080079 + public const int action_mode_bar = 2131230841; + + // aapt resource value: 0x7f080078 + public const int action_mode_bar_stub = 2131230840; + + // aapt resource value: 0x7f08005c + public const int action_mode_close_button = 2131230812; + + // aapt resource value: 0x7f08009c + public const int action_text = 2131230876; + + // aapt resource value: 0x7f0800aa + public const int actions = 2131230890; + + // aapt resource value: 0x7f08005d + public const int activity_chooser_view_content = 2131230813; + + // aapt resource value: 0x7f080027 + public const int add = 2131230759; + + // aapt resource value: 0x7f080070 + public const int alertTitle = 2131230832; + + // aapt resource value: 0x7f080052 + public const int all = 2131230802; + + // aapt resource value: 0x7f080038 + public const int always = 2131230776; + + // aapt resource value: 0x7f080056 + public const int async = 2131230806; + + // aapt resource value: 0x7f080044 + public const int auto = 2131230788; + + // aapt resource value: 0x7f08002f + public const int beginning = 2131230767; + + // aapt resource value: 0x7f080057 + public const int blocking = 2131230807; + + // aapt resource value: 0x7f08003d + public const int bottom = 2131230781; + + // aapt resource value: 0x7f080063 + public const int buttonPanel = 2131230819; + + // aapt resource value: 0x7f08009e + public const int cancel_action = 2131230878; + + // aapt resource value: 0x7f080045 + public const int center = 2131230789; + + // aapt resource value: 0x7f080046 + public const int center_horizontal = 2131230790; + + // aapt resource value: 0x7f080047 + public const int center_vertical = 2131230791; + + // aapt resource value: 0x7f080073 + public const int checkbox = 2131230835; + + // aapt resource value: 0x7f0800a6 + public const int chronometer = 2131230886; + + // aapt resource value: 0x7f08004e + public const int clip_horizontal = 2131230798; + + // aapt resource value: 0x7f08004f + public const int clip_vertical = 2131230799; + + // aapt resource value: 0x7f080039 + public const int collapseActionView = 2131230777; + + // aapt resource value: 0x7f08008e + public const int container = 2131230862; + + // aapt resource value: 0x7f080066 + public const int contentPanel = 2131230822; + + // aapt resource value: 0x7f08008f + public const int coordinator = 2131230863; + + // aapt resource value: 0x7f08006d + public const int custom = 2131230829; + + // aapt resource value: 0x7f08006c + public const int customPanel = 2131230828; + + // aapt resource value: 0x7f08007a + public const int decor_content_parent = 2131230842; + + // aapt resource value: 0x7f080060 + public const int default_activity_button = 2131230816; + + // aapt resource value: 0x7f080091 + public const int design_bottom_sheet = 2131230865; + + // aapt resource value: 0x7f080098 + public const int design_menu_item_action_area = 2131230872; + + // aapt resource value: 0x7f080097 + public const int design_menu_item_action_area_stub = 2131230871; + + // aapt resource value: 0x7f080096 + public const int design_menu_item_text = 2131230870; + + // aapt resource value: 0x7f080095 + public const int design_navigation_view = 2131230869; + + // aapt resource value: 0x7f080020 + public const int disableHome = 2131230752; + + // aapt resource value: 0x7f08007e + public const int edit_query = 2131230846; + + // aapt resource value: 0x7f080030 + public const int end = 2131230768; + + // aapt resource value: 0x7f0800ac + public const int end_padder = 2131230892; + + // aapt resource value: 0x7f08003f + public const int enterAlways = 2131230783; + + // aapt resource value: 0x7f080040 + public const int enterAlwaysCollapsed = 2131230784; + + // aapt resource value: 0x7f080041 + public const int exitUntilCollapsed = 2131230785; + + // aapt resource value: 0x7f08005e + public const int expand_activities_button = 2131230814; + + // aapt resource value: 0x7f080072 + public const int expanded_menu = 2131230834; + + // aapt resource value: 0x7f080050 + public const int fill = 2131230800; + + // aapt resource value: 0x7f080051 + public const int fill_horizontal = 2131230801; + + // aapt resource value: 0x7f080048 + public const int fill_vertical = 2131230792; + + // aapt resource value: 0x7f080054 + public const int @fixed = 2131230804; + + // aapt resource value: 0x7f080058 + public const int forever = 2131230808; + + // aapt resource value: 0x7f08008b + public const int gameView1 = 2131230859; + + // aapt resource value: 0x7f08000a + public const int ghost_view = 2131230730; + + // aapt resource value: 0x7f080005 + public const int home = 2131230725; + + // aapt resource value: 0x7f080021 + public const int homeAsUp = 2131230753; + + // aapt resource value: 0x7f080062 + public const int icon = 2131230818; + + // aapt resource value: 0x7f0800ab + public const int icon_group = 2131230891; + + // aapt resource value: 0x7f08003a + public const int ifRoom = 2131230778; + + // aapt resource value: 0x7f08005f + public const int image = 2131230815; + + // aapt resource value: 0x7f0800a7 + public const int info = 2131230887; + + // aapt resource value: 0x7f080059 + public const int italic = 2131230809; + + // aapt resource value: 0x7f080000 + public const int item_touch_helper_previous_elevation = 2131230720; + + // aapt resource value: 0x7f08008d + public const int largeLabel = 2131230861; + + // aapt resource value: 0x7f080049 + public const int left = 2131230793; + + // aapt resource value: 0x7f080017 + public const int line1 = 2131230743; + + // aapt resource value: 0x7f080018 + public const int line3 = 2131230744; + + // aapt resource value: 0x7f08001d + public const int listMode = 2131230749; + + // aapt resource value: 0x7f080061 + public const int list_item = 2131230817; + + // aapt resource value: 0x7f0800af + public const int masked = 2131230895; + + // aapt resource value: 0x7f0800a0 + public const int media_actions = 2131230880; + + // aapt resource value: 0x7f0800ad + public const int message = 2131230893; + + // aapt resource value: 0x7f080031 + public const int middle = 2131230769; + + // aapt resource value: 0x7f080053 + public const int mini = 2131230803; + + // aapt resource value: 0x7f080028 + public const int multiply = 2131230760; + + // aapt resource value: 0x7f080094 + public const int navigation_header_container = 2131230868; + + // aapt resource value: 0x7f08003b + public const int never = 2131230779; + + // aapt resource value: 0x7f080022 + public const int none = 2131230754; + + // aapt resource value: 0x7f08001e + public const int normal = 2131230750; + + // aapt resource value: 0x7f0800a9 + public const int notification_background = 2131230889; + + // aapt resource value: 0x7f0800a3 + public const int notification_main_column = 2131230883; + + // aapt resource value: 0x7f0800a2 + public const int notification_main_column_container = 2131230882; + + // aapt resource value: 0x7f08004c + public const int parallax = 2131230796; + + // aapt resource value: 0x7f080065 + public const int parentPanel = 2131230821; + + // aapt resource value: 0x7f08000b + public const int parent_matrix = 2131230731; + + // aapt resource value: 0x7f08004d + public const int pin = 2131230797; + + // aapt resource value: 0x7f080006 + public const int progress_circular = 2131230726; + + // aapt resource value: 0x7f080007 + public const int progress_horizontal = 2131230727; + + // aapt resource value: 0x7f080075 + public const int radio = 2131230837; + + // aapt resource value: 0x7f08004a + public const int right = 2131230794; + + // aapt resource value: 0x7f0800a8 + public const int right_icon = 2131230888; + + // aapt resource value: 0x7f0800a4 + public const int right_side = 2131230884; + + // aapt resource value: 0x7f08000c + public const int save_image_matrix = 2131230732; + + // aapt resource value: 0x7f08000d + public const int save_non_transition_alpha = 2131230733; + + // aapt resource value: 0x7f08000e + public const int save_scale_type = 2131230734; + + // aapt resource value: 0x7f080029 + public const int screen = 2131230761; + + // aapt resource value: 0x7f080042 + public const int scroll = 2131230786; + + // aapt resource value: 0x7f08006b + public const int scrollIndicatorDown = 2131230827; + + // aapt resource value: 0x7f080067 + public const int scrollIndicatorUp = 2131230823; + + // aapt resource value: 0x7f080068 + public const int scrollView = 2131230824; + + // aapt resource value: 0x7f080055 + public const int scrollable = 2131230805; + + // aapt resource value: 0x7f080080 + public const int search_badge = 2131230848; + + // aapt resource value: 0x7f08007f + public const int search_bar = 2131230847; + + // aapt resource value: 0x7f080081 + public const int search_button = 2131230849; + + // aapt resource value: 0x7f080086 + public const int search_close_btn = 2131230854; + + // aapt resource value: 0x7f080082 + public const int search_edit_frame = 2131230850; + + // aapt resource value: 0x7f080088 + public const int search_go_btn = 2131230856; + + // aapt resource value: 0x7f080083 + public const int search_mag_icon = 2131230851; + + // aapt resource value: 0x7f080084 + public const int search_plate = 2131230852; + + // aapt resource value: 0x7f080085 + public const int search_src_text = 2131230853; + + // aapt resource value: 0x7f080089 + public const int search_voice_btn = 2131230857; + + // aapt resource value: 0x7f08008a + public const int select_dialog_listview = 2131230858; + + // aapt resource value: 0x7f080074 + public const int shortcut = 2131230836; + + // aapt resource value: 0x7f080023 + public const int showCustom = 2131230755; + + // aapt resource value: 0x7f080024 + public const int showHome = 2131230756; + + // aapt resource value: 0x7f080025 + public const int showTitle = 2131230757; + + // aapt resource value: 0x7f08008c + public const int smallLabel = 2131230860; + + // aapt resource value: 0x7f080093 + public const int snackbar_action = 2131230867; + + // aapt resource value: 0x7f080092 + public const int snackbar_text = 2131230866; + + // aapt resource value: 0x7f080043 + public const int snap = 2131230787; + + // aapt resource value: 0x7f080064 + public const int spacer = 2131230820; + + // aapt resource value: 0x7f080008 + public const int split_action_bar = 2131230728; + + // aapt resource value: 0x7f08002a + public const int src_atop = 2131230762; + + // aapt resource value: 0x7f08002b + public const int src_in = 2131230763; + + // aapt resource value: 0x7f08002c + public const int src_over = 2131230764; + + // aapt resource value: 0x7f08004b + public const int start = 2131230795; + + // aapt resource value: 0x7f08009f + public const int status_bar_latest_event_content = 2131230879; + + // aapt resource value: 0x7f080076 + public const int submenuarrow = 2131230838; + + // aapt resource value: 0x7f080087 + public const int submit_area = 2131230855; + + // aapt resource value: 0x7f08001f + public const int tabMode = 2131230751; + + // aapt resource value: 0x7f080019 + public const int tag_transition_group = 2131230745; + + // aapt resource value: 0x7f08001a + public const int text = 2131230746; + + // aapt resource value: 0x7f08001b + public const int text2 = 2131230747; + + // aapt resource value: 0x7f08006a + public const int textSpacerNoButtons = 2131230826; + + // aapt resource value: 0x7f080069 + public const int textSpacerNoTitle = 2131230825; + + // aapt resource value: 0x7f080099 + public const int text_input_password_toggle = 2131230873; + + // aapt resource value: 0x7f080014 + public const int textinput_counter = 2131230740; + + // aapt resource value: 0x7f080015 + public const int textinput_error = 2131230741; + + // aapt resource value: 0x7f0800a5 + public const int time = 2131230885; + + // aapt resource value: 0x7f08001c + public const int title = 2131230748; + + // aapt resource value: 0x7f080071 + public const int titleDividerNoCustom = 2131230833; + + // aapt resource value: 0x7f08006f + public const int title_template = 2131230831; + + // aapt resource value: 0x7f08003e + public const int top = 2131230782; + + // aapt resource value: 0x7f08006e + public const int topPanel = 2131230830; + + // aapt resource value: 0x7f080090 + public const int touch_outside = 2131230864; + + // aapt resource value: 0x7f08000f + public const int transition_current_scene = 2131230735; + + // aapt resource value: 0x7f080010 + public const int transition_layout_save = 2131230736; + + // aapt resource value: 0x7f080011 + public const int transition_position = 2131230737; + + // aapt resource value: 0x7f080012 + public const int transition_scene_layoutid_cache = 2131230738; + + // aapt resource value: 0x7f080013 + public const int transition_transform = 2131230739; + + // aapt resource value: 0x7f08002d + public const int uniform = 2131230765; + + // aapt resource value: 0x7f080009 + public const int up = 2131230729; + + // aapt resource value: 0x7f080026 + public const int useLogo = 2131230758; + + // aapt resource value: 0x7f080016 + public const int view_offset_helper = 2131230742; + + // aapt resource value: 0x7f0800ae + public const int visible = 2131230894; + + // aapt resource value: 0x7f08003c + public const int withText = 2131230780; + + // aapt resource value: 0x7f08002e + public const int wrap_content = 2131230766; + + static Id() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Id() + { + } + } + + public partial class Integer + { + + // aapt resource value: 0x7f0d0000 + public const int abc_config_activityDefaultDur = 2131558400; + + // aapt resource value: 0x7f0d0001 + public const int abc_config_activityShortDur = 2131558401; + + // aapt resource value: 0x7f0d0005 + public const int app_bar_elevation_anim_duration = 2131558405; + + // aapt resource value: 0x7f0d0006 + public const int bottom_sheet_slide_duration = 2131558406; + + // aapt resource value: 0x7f0d0002 + public const int cancel_button_image_alpha = 2131558402; + + // aapt resource value: 0x7f0d0003 + public const int config_tooltipAnimTime = 2131558403; + + // aapt resource value: 0x7f0d0004 + public const int design_snackbar_text_max_lines = 2131558404; + + // aapt resource value: 0x7f0d0007 + public const int hide_password_duration = 2131558407; + + // aapt resource value: 0x7f0d0008 + public const int show_password_duration = 2131558408; + + // aapt resource value: 0x7f0d0009 + public const int status_bar_notification_info_maxnum = 2131558409; + + static Integer() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Integer() + { + } + } + + public partial class Layout + { + + // aapt resource value: 0x7f040000 + public const int abc_action_bar_title_item = 2130968576; + + // aapt resource value: 0x7f040001 + public const int abc_action_bar_up_container = 2130968577; + + // aapt resource value: 0x7f040002 + public const int abc_action_menu_item_layout = 2130968578; + + // aapt resource value: 0x7f040003 + public const int abc_action_menu_layout = 2130968579; + + // aapt resource value: 0x7f040004 + public const int abc_action_mode_bar = 2130968580; + + // aapt resource value: 0x7f040005 + public const int abc_action_mode_close_item_material = 2130968581; + + // aapt resource value: 0x7f040006 + public const int abc_activity_chooser_view = 2130968582; + + // aapt resource value: 0x7f040007 + public const int abc_activity_chooser_view_list_item = 2130968583; + + // aapt resource value: 0x7f040008 + public const int abc_alert_dialog_button_bar_material = 2130968584; + + // aapt resource value: 0x7f040009 + public const int abc_alert_dialog_material = 2130968585; + + // aapt resource value: 0x7f04000a + public const int abc_alert_dialog_title_material = 2130968586; + + // aapt resource value: 0x7f04000b + public const int abc_dialog_title_material = 2130968587; + + // aapt resource value: 0x7f04000c + public const int abc_expanded_menu_layout = 2130968588; + + // aapt resource value: 0x7f04000d + public const int abc_list_menu_item_checkbox = 2130968589; + + // aapt resource value: 0x7f04000e + public const int abc_list_menu_item_icon = 2130968590; + + // aapt resource value: 0x7f04000f + public const int abc_list_menu_item_layout = 2130968591; + + // aapt resource value: 0x7f040010 + public const int abc_list_menu_item_radio = 2130968592; + + // aapt resource value: 0x7f040011 + public const int abc_popup_menu_header_item_layout = 2130968593; + + // aapt resource value: 0x7f040012 + public const int abc_popup_menu_item_layout = 2130968594; + + // aapt resource value: 0x7f040013 + public const int abc_screen_content_include = 2130968595; + + // aapt resource value: 0x7f040014 + public const int abc_screen_simple = 2130968596; + + // aapt resource value: 0x7f040015 + public const int abc_screen_simple_overlay_action_mode = 2130968597; + + // aapt resource value: 0x7f040016 + public const int abc_screen_toolbar = 2130968598; + + // aapt resource value: 0x7f040017 + public const int abc_search_dropdown_item_icons_2line = 2130968599; + + // aapt resource value: 0x7f040018 + public const int abc_search_view = 2130968600; + + // aapt resource value: 0x7f040019 + public const int abc_select_dialog_material = 2130968601; + + // aapt resource value: 0x7f04001a + public const int activity_main = 2130968602; + + // aapt resource value: 0x7f04001b + public const int design_bottom_navigation_item = 2130968603; + + // aapt resource value: 0x7f04001c + public const int design_bottom_sheet_dialog = 2130968604; + + // aapt resource value: 0x7f04001d + public const int design_layout_snackbar = 2130968605; + + // aapt resource value: 0x7f04001e + public const int design_layout_snackbar_include = 2130968606; + + // aapt resource value: 0x7f04001f + public const int design_layout_tab_icon = 2130968607; + + // aapt resource value: 0x7f040020 + public const int design_layout_tab_text = 2130968608; + + // aapt resource value: 0x7f040021 + public const int design_menu_item_action_area = 2130968609; + + // aapt resource value: 0x7f040022 + public const int design_navigation_item = 2130968610; + + // aapt resource value: 0x7f040023 + public const int design_navigation_item_header = 2130968611; + + // aapt resource value: 0x7f040024 + public const int design_navigation_item_separator = 2130968612; + + // aapt resource value: 0x7f040025 + public const int design_navigation_item_subheader = 2130968613; + + // aapt resource value: 0x7f040026 + public const int design_navigation_menu = 2130968614; + + // aapt resource value: 0x7f040027 + public const int design_navigation_menu_item = 2130968615; + + // aapt resource value: 0x7f040028 + public const int design_text_input_password_icon = 2130968616; + + // aapt resource value: 0x7f040029 + public const int notification_action = 2130968617; + + // aapt resource value: 0x7f04002a + public const int notification_action_tombstone = 2130968618; + + // aapt resource value: 0x7f04002b + public const int notification_media_action = 2130968619; + + // aapt resource value: 0x7f04002c + public const int notification_media_cancel_action = 2130968620; + + // aapt resource value: 0x7f04002d + public const int notification_template_big_media = 2130968621; + + // aapt resource value: 0x7f04002e + public const int notification_template_big_media_custom = 2130968622; + + // aapt resource value: 0x7f04002f + public const int notification_template_big_media_narrow = 2130968623; + + // aapt resource value: 0x7f040030 + public const int notification_template_big_media_narrow_custom = 2130968624; + + // aapt resource value: 0x7f040031 + public const int notification_template_custom_big = 2130968625; + + // aapt resource value: 0x7f040032 + public const int notification_template_icon_group = 2130968626; + + // aapt resource value: 0x7f040033 + public const int notification_template_lines_media = 2130968627; + + // aapt resource value: 0x7f040034 + public const int notification_template_media = 2130968628; + + // aapt resource value: 0x7f040035 + public const int notification_template_media_custom = 2130968629; + + // aapt resource value: 0x7f040036 + public const int notification_template_part_chronometer = 2130968630; + + // aapt resource value: 0x7f040037 + public const int notification_template_part_time = 2130968631; + + // aapt resource value: 0x7f040038 + public const int select_dialog_item_material = 2130968632; + + // aapt resource value: 0x7f040039 + public const int select_dialog_multichoice_material = 2130968633; + + // aapt resource value: 0x7f04003a + public const int select_dialog_singlechoice_material = 2130968634; + + // aapt resource value: 0x7f04003b + public const int support_simple_spinner_dropdown_item = 2130968635; + + // aapt resource value: 0x7f04003c + public const int tooltip = 2130968636; + + static Layout() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Layout() + { + } + } + + public partial class Mipmap + { + + // aapt resource value: 0x7f030000 + public const int ic_launcher = 2130903040; + + // aapt resource value: 0x7f030001 + public const int ic_launcher_foreground = 2130903041; + + // aapt resource value: 0x7f030002 + public const int ic_launcher_round = 2130903042; + + static Mipmap() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Mipmap() + { + } + } + + public partial class String + { + + // aapt resource value: 0x7f090000 + public const int abc_action_bar_home_description = 2131296256; + + // aapt resource value: 0x7f090001 + public const int abc_action_bar_up_description = 2131296257; + + // aapt resource value: 0x7f090002 + public const int abc_action_menu_overflow_description = 2131296258; + + // aapt resource value: 0x7f090003 + public const int abc_action_mode_done = 2131296259; + + // aapt resource value: 0x7f090004 + public const int abc_activity_chooser_view_see_all = 2131296260; + + // aapt resource value: 0x7f090005 + public const int abc_activitychooserview_choose_application = 2131296261; + + // aapt resource value: 0x7f090006 + public const int abc_capital_off = 2131296262; + + // aapt resource value: 0x7f090007 + public const int abc_capital_on = 2131296263; + + // aapt resource value: 0x7f090012 + public const int abc_font_family_body_1_material = 2131296274; + + // aapt resource value: 0x7f090013 + public const int abc_font_family_body_2_material = 2131296275; + + // aapt resource value: 0x7f090014 + public const int abc_font_family_button_material = 2131296276; + + // aapt resource value: 0x7f090015 + public const int abc_font_family_caption_material = 2131296277; + + // aapt resource value: 0x7f090016 + public const int abc_font_family_display_1_material = 2131296278; + + // aapt resource value: 0x7f090017 + public const int abc_font_family_display_2_material = 2131296279; + + // aapt resource value: 0x7f090018 + public const int abc_font_family_display_3_material = 2131296280; + + // aapt resource value: 0x7f090019 + public const int abc_font_family_display_4_material = 2131296281; + + // aapt resource value: 0x7f09001a + public const int abc_font_family_headline_material = 2131296282; + + // aapt resource value: 0x7f09001b + public const int abc_font_family_menu_material = 2131296283; + + // aapt resource value: 0x7f09001c + public const int abc_font_family_subhead_material = 2131296284; + + // aapt resource value: 0x7f09001d + public const int abc_font_family_title_material = 2131296285; + + // aapt resource value: 0x7f090008 + public const int abc_search_hint = 2131296264; + + // aapt resource value: 0x7f090009 + public const int abc_searchview_description_clear = 2131296265; + + // aapt resource value: 0x7f09000a + public const int abc_searchview_description_query = 2131296266; + + // aapt resource value: 0x7f09000b + public const int abc_searchview_description_search = 2131296267; + + // aapt resource value: 0x7f09000c + public const int abc_searchview_description_submit = 2131296268; + + // aapt resource value: 0x7f09000d + public const int abc_searchview_description_voice = 2131296269; + + // aapt resource value: 0x7f09000e + public const int abc_shareactionprovider_share_with = 2131296270; + + // aapt resource value: 0x7f09000f + public const int abc_shareactionprovider_share_with_application = 2131296271; + + // aapt resource value: 0x7f090010 + public const int abc_toolbar_collapse_description = 2131296272; + + // aapt resource value: 0x7f090028 + public const int action_settings = 2131296296; + + // aapt resource value: 0x7f090027 + public const int app_name = 2131296295; + + // aapt resource value: 0x7f09001e + public const int appbar_scrolling_view_behavior = 2131296286; + + // aapt resource value: 0x7f09001f + public const int bottom_sheet_behavior = 2131296287; + + // aapt resource value: 0x7f090020 + public const int character_counter_pattern = 2131296288; + + // aapt resource value: 0x7f090021 + public const int password_toggle_content_description = 2131296289; + + // aapt resource value: 0x7f090022 + public const int path_password_eye = 2131296290; + + // aapt resource value: 0x7f090023 + public const int path_password_eye_mask_strike_through = 2131296291; + + // aapt resource value: 0x7f090024 + public const int path_password_eye_mask_visible = 2131296292; + + // aapt resource value: 0x7f090025 + public const int path_password_strike_through = 2131296293; + + // aapt resource value: 0x7f090011 + public const int search_menu_title = 2131296273; + + // aapt resource value: 0x7f090026 + public const int status_bar_notification_info_overflow = 2131296294; + + static String() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private String() + { + } + } + + public partial class Style + { + + // aapt resource value: 0x7f0a0095 + public const int AlertDialog_AppCompat = 2131361941; + + // aapt resource value: 0x7f0a0096 + public const int AlertDialog_AppCompat_Light = 2131361942; + + // aapt resource value: 0x7f0a0097 + public const int Animation_AppCompat_Dialog = 2131361943; + + // aapt resource value: 0x7f0a0098 + public const int Animation_AppCompat_DropDownUp = 2131361944; + + // aapt resource value: 0x7f0a0099 + public const int Animation_AppCompat_Tooltip = 2131361945; + + // aapt resource value: 0x7f0a015f + public const int Animation_Design_BottomSheetDialog = 2131362143; + + // aapt resource value: 0x7f0a0180 + public const int AppTheme = 2131362176; + + // aapt resource value: 0x7f0a009a + public const int Base_AlertDialog_AppCompat = 2131361946; + + // aapt resource value: 0x7f0a009b + public const int Base_AlertDialog_AppCompat_Light = 2131361947; + + // aapt resource value: 0x7f0a009c + public const int Base_Animation_AppCompat_Dialog = 2131361948; + + // aapt resource value: 0x7f0a009d + public const int Base_Animation_AppCompat_DropDownUp = 2131361949; + + // aapt resource value: 0x7f0a009e + public const int Base_Animation_AppCompat_Tooltip = 2131361950; + + // aapt resource value: 0x7f0a009f + public const int Base_DialogWindowTitle_AppCompat = 2131361951; + + // aapt resource value: 0x7f0a00a0 + public const int Base_DialogWindowTitleBackground_AppCompat = 2131361952; + + // aapt resource value: 0x7f0a0039 + public const int Base_TextAppearance_AppCompat = 2131361849; + + // aapt resource value: 0x7f0a003a + public const int Base_TextAppearance_AppCompat_Body1 = 2131361850; + + // aapt resource value: 0x7f0a003b + public const int Base_TextAppearance_AppCompat_Body2 = 2131361851; + + // aapt resource value: 0x7f0a0027 + public const int Base_TextAppearance_AppCompat_Button = 2131361831; + + // aapt resource value: 0x7f0a003c + public const int Base_TextAppearance_AppCompat_Caption = 2131361852; + + // aapt resource value: 0x7f0a003d + public const int Base_TextAppearance_AppCompat_Display1 = 2131361853; + + // aapt resource value: 0x7f0a003e + public const int Base_TextAppearance_AppCompat_Display2 = 2131361854; + + // aapt resource value: 0x7f0a003f + public const int Base_TextAppearance_AppCompat_Display3 = 2131361855; + + // aapt resource value: 0x7f0a0040 + public const int Base_TextAppearance_AppCompat_Display4 = 2131361856; + + // aapt resource value: 0x7f0a0041 + public const int Base_TextAppearance_AppCompat_Headline = 2131361857; + + // aapt resource value: 0x7f0a000b + public const int Base_TextAppearance_AppCompat_Inverse = 2131361803; + + // aapt resource value: 0x7f0a0042 + public const int Base_TextAppearance_AppCompat_Large = 2131361858; + + // aapt resource value: 0x7f0a000c + public const int Base_TextAppearance_AppCompat_Large_Inverse = 2131361804; + + // aapt resource value: 0x7f0a0043 + public const int Base_TextAppearance_AppCompat_Light_Widget_PopupMenu_Large = 2131361859; + + // aapt resource value: 0x7f0a0044 + public const int Base_TextAppearance_AppCompat_Light_Widget_PopupMenu_Small = 2131361860; + + // aapt resource value: 0x7f0a0045 + public const int Base_TextAppearance_AppCompat_Medium = 2131361861; + + // aapt resource value: 0x7f0a000d + public const int Base_TextAppearance_AppCompat_Medium_Inverse = 2131361805; + + // aapt resource value: 0x7f0a0046 + public const int Base_TextAppearance_AppCompat_Menu = 2131361862; + + // aapt resource value: 0x7f0a00a1 + public const int Base_TextAppearance_AppCompat_SearchResult = 2131361953; + + // aapt resource value: 0x7f0a0047 + public const int Base_TextAppearance_AppCompat_SearchResult_Subtitle = 2131361863; + + // aapt resource value: 0x7f0a0048 + public const int Base_TextAppearance_AppCompat_SearchResult_Title = 2131361864; + + // aapt resource value: 0x7f0a0049 + public const int Base_TextAppearance_AppCompat_Small = 2131361865; + + // aapt resource value: 0x7f0a000e + public const int Base_TextAppearance_AppCompat_Small_Inverse = 2131361806; + + // aapt resource value: 0x7f0a004a + public const int Base_TextAppearance_AppCompat_Subhead = 2131361866; + + // aapt resource value: 0x7f0a000f + public const int Base_TextAppearance_AppCompat_Subhead_Inverse = 2131361807; + + // aapt resource value: 0x7f0a004b + public const int Base_TextAppearance_AppCompat_Title = 2131361867; + + // aapt resource value: 0x7f0a0010 + public const int Base_TextAppearance_AppCompat_Title_Inverse = 2131361808; + + // aapt resource value: 0x7f0a00a2 + public const int Base_TextAppearance_AppCompat_Tooltip = 2131361954; + + // aapt resource value: 0x7f0a0086 + public const int Base_TextAppearance_AppCompat_Widget_ActionBar_Menu = 2131361926; + + // aapt resource value: 0x7f0a004c + public const int Base_TextAppearance_AppCompat_Widget_ActionBar_Subtitle = 2131361868; + + // aapt resource value: 0x7f0a004d + public const int Base_TextAppearance_AppCompat_Widget_ActionBar_Subtitle_Inverse = 2131361869; + + // aapt resource value: 0x7f0a004e + public const int Base_TextAppearance_AppCompat_Widget_ActionBar_Title = 2131361870; + + // aapt resource value: 0x7f0a004f + public const int Base_TextAppearance_AppCompat_Widget_ActionBar_Title_Inverse = 2131361871; + + // aapt resource value: 0x7f0a0050 + public const int Base_TextAppearance_AppCompat_Widget_ActionMode_Subtitle = 2131361872; + + // aapt resource value: 0x7f0a0051 + public const int Base_TextAppearance_AppCompat_Widget_ActionMode_Title = 2131361873; + + // aapt resource value: 0x7f0a0052 + public const int Base_TextAppearance_AppCompat_Widget_Button = 2131361874; + + // aapt resource value: 0x7f0a008d + public const int Base_TextAppearance_AppCompat_Widget_Button_Borderless_Colored = 2131361933; + + // aapt resource value: 0x7f0a008e + public const int Base_TextAppearance_AppCompat_Widget_Button_Colored = 2131361934; + + // aapt resource value: 0x7f0a0087 + public const int Base_TextAppearance_AppCompat_Widget_Button_Inverse = 2131361927; + + // aapt resource value: 0x7f0a00a3 + public const int Base_TextAppearance_AppCompat_Widget_DropDownItem = 2131361955; + + // aapt resource value: 0x7f0a0053 + public const int Base_TextAppearance_AppCompat_Widget_PopupMenu_Header = 2131361875; + + // aapt resource value: 0x7f0a0054 + public const int Base_TextAppearance_AppCompat_Widget_PopupMenu_Large = 2131361876; + + // aapt resource value: 0x7f0a0055 + public const int Base_TextAppearance_AppCompat_Widget_PopupMenu_Small = 2131361877; + + // aapt resource value: 0x7f0a0056 + public const int Base_TextAppearance_AppCompat_Widget_Switch = 2131361878; + + // aapt resource value: 0x7f0a0057 + public const int Base_TextAppearance_AppCompat_Widget_TextView_SpinnerItem = 2131361879; + + // aapt resource value: 0x7f0a00a4 + public const int Base_TextAppearance_Widget_AppCompat_ExpandedMenu_Item = 2131361956; + + // aapt resource value: 0x7f0a0058 + public const int Base_TextAppearance_Widget_AppCompat_Toolbar_Subtitle = 2131361880; + + // aapt resource value: 0x7f0a0059 + public const int Base_TextAppearance_Widget_AppCompat_Toolbar_Title = 2131361881; + + // aapt resource value: 0x7f0a005a + public const int Base_Theme_AppCompat = 2131361882; + + // aapt resource value: 0x7f0a00a5 + public const int Base_Theme_AppCompat_CompactMenu = 2131361957; + + // aapt resource value: 0x7f0a0011 + public const int Base_Theme_AppCompat_Dialog = 2131361809; + + // aapt resource value: 0x7f0a0012 + public const int Base_Theme_AppCompat_Dialog_Alert = 2131361810; + + // aapt resource value: 0x7f0a00a6 + public const int Base_Theme_AppCompat_Dialog_FixedSize = 2131361958; + + // aapt resource value: 0x7f0a0013 + public const int Base_Theme_AppCompat_Dialog_MinWidth = 2131361811; + + // aapt resource value: 0x7f0a0001 + public const int Base_Theme_AppCompat_DialogWhenLarge = 2131361793; + + // aapt resource value: 0x7f0a005b + public const int Base_Theme_AppCompat_Light = 2131361883; + + // aapt resource value: 0x7f0a00a7 + public const int Base_Theme_AppCompat_Light_DarkActionBar = 2131361959; + + // aapt resource value: 0x7f0a0014 + public const int Base_Theme_AppCompat_Light_Dialog = 2131361812; + + // aapt resource value: 0x7f0a0015 + public const int Base_Theme_AppCompat_Light_Dialog_Alert = 2131361813; + + // aapt resource value: 0x7f0a00a8 + public const int Base_Theme_AppCompat_Light_Dialog_FixedSize = 2131361960; + + // aapt resource value: 0x7f0a0016 + public const int Base_Theme_AppCompat_Light_Dialog_MinWidth = 2131361814; + + // aapt resource value: 0x7f0a0002 + public const int Base_Theme_AppCompat_Light_DialogWhenLarge = 2131361794; + + // aapt resource value: 0x7f0a00a9 + public const int Base_ThemeOverlay_AppCompat = 2131361961; + + // aapt resource value: 0x7f0a00aa + public const int Base_ThemeOverlay_AppCompat_ActionBar = 2131361962; + + // aapt resource value: 0x7f0a00ab + public const int Base_ThemeOverlay_AppCompat_Dark = 2131361963; + + // aapt resource value: 0x7f0a00ac + public const int Base_ThemeOverlay_AppCompat_Dark_ActionBar = 2131361964; + + // aapt resource value: 0x7f0a0017 + public const int Base_ThemeOverlay_AppCompat_Dialog = 2131361815; + + // aapt resource value: 0x7f0a0018 + public const int Base_ThemeOverlay_AppCompat_Dialog_Alert = 2131361816; + + // aapt resource value: 0x7f0a00ad + public const int Base_ThemeOverlay_AppCompat_Light = 2131361965; + + // aapt resource value: 0x7f0a0019 + public const int Base_V11_Theme_AppCompat_Dialog = 2131361817; + + // aapt resource value: 0x7f0a001a + public const int Base_V11_Theme_AppCompat_Light_Dialog = 2131361818; + + // aapt resource value: 0x7f0a001b + public const int Base_V11_ThemeOverlay_AppCompat_Dialog = 2131361819; + + // aapt resource value: 0x7f0a0023 + public const int Base_V12_Widget_AppCompat_AutoCompleteTextView = 2131361827; + + // aapt resource value: 0x7f0a0024 + public const int Base_V12_Widget_AppCompat_EditText = 2131361828; + + // aapt resource value: 0x7f0a0160 + public const int Base_V14_Widget_Design_AppBarLayout = 2131362144; + + // aapt resource value: 0x7f0a005c + public const int Base_V21_Theme_AppCompat = 2131361884; + + // aapt resource value: 0x7f0a005d + public const int Base_V21_Theme_AppCompat_Dialog = 2131361885; + + // aapt resource value: 0x7f0a005e + public const int Base_V21_Theme_AppCompat_Light = 2131361886; + + // aapt resource value: 0x7f0a005f + public const int Base_V21_Theme_AppCompat_Light_Dialog = 2131361887; + + // aapt resource value: 0x7f0a0060 + public const int Base_V21_ThemeOverlay_AppCompat_Dialog = 2131361888; + + // aapt resource value: 0x7f0a015c + public const int Base_V21_Widget_Design_AppBarLayout = 2131362140; + + // aapt resource value: 0x7f0a0084 + public const int Base_V22_Theme_AppCompat = 2131361924; + + // aapt resource value: 0x7f0a0085 + public const int Base_V22_Theme_AppCompat_Light = 2131361925; + + // aapt resource value: 0x7f0a0088 + public const int Base_V23_Theme_AppCompat = 2131361928; + + // aapt resource value: 0x7f0a0089 + public const int Base_V23_Theme_AppCompat_Light = 2131361929; + + // aapt resource value: 0x7f0a0091 + public const int Base_V26_Theme_AppCompat = 2131361937; + + // aapt resource value: 0x7f0a0092 + public const int Base_V26_Theme_AppCompat_Light = 2131361938; + + // aapt resource value: 0x7f0a0093 + public const int Base_V26_Widget_AppCompat_Toolbar = 2131361939; + + // aapt resource value: 0x7f0a015e + public const int Base_V26_Widget_Design_AppBarLayout = 2131362142; + + // aapt resource value: 0x7f0a00ae + public const int Base_V7_Theme_AppCompat = 2131361966; + + // aapt resource value: 0x7f0a00af + public const int Base_V7_Theme_AppCompat_Dialog = 2131361967; + + // aapt resource value: 0x7f0a00b0 + public const int Base_V7_Theme_AppCompat_Light = 2131361968; + + // aapt resource value: 0x7f0a00b1 + public const int Base_V7_Theme_AppCompat_Light_Dialog = 2131361969; + + // aapt resource value: 0x7f0a00b2 + public const int Base_V7_ThemeOverlay_AppCompat_Dialog = 2131361970; + + // aapt resource value: 0x7f0a00b3 + public const int Base_V7_Widget_AppCompat_AutoCompleteTextView = 2131361971; + + // aapt resource value: 0x7f0a00b4 + public const int Base_V7_Widget_AppCompat_EditText = 2131361972; + + // aapt resource value: 0x7f0a00b5 + public const int Base_V7_Widget_AppCompat_Toolbar = 2131361973; + + // aapt resource value: 0x7f0a00b6 + public const int Base_Widget_AppCompat_ActionBar = 2131361974; + + // aapt resource value: 0x7f0a00b7 + public const int Base_Widget_AppCompat_ActionBar_Solid = 2131361975; + + // aapt resource value: 0x7f0a00b8 + public const int Base_Widget_AppCompat_ActionBar_TabBar = 2131361976; + + // aapt resource value: 0x7f0a0061 + public const int Base_Widget_AppCompat_ActionBar_TabText = 2131361889; + + // aapt resource value: 0x7f0a0062 + public const int Base_Widget_AppCompat_ActionBar_TabView = 2131361890; + + // aapt resource value: 0x7f0a0063 + public const int Base_Widget_AppCompat_ActionButton = 2131361891; + + // aapt resource value: 0x7f0a0064 + public const int Base_Widget_AppCompat_ActionButton_CloseMode = 2131361892; + + // aapt resource value: 0x7f0a0065 + public const int Base_Widget_AppCompat_ActionButton_Overflow = 2131361893; + + // aapt resource value: 0x7f0a00b9 + public const int Base_Widget_AppCompat_ActionMode = 2131361977; + + // aapt resource value: 0x7f0a00ba + public const int Base_Widget_AppCompat_ActivityChooserView = 2131361978; + + // aapt resource value: 0x7f0a0025 + public const int Base_Widget_AppCompat_AutoCompleteTextView = 2131361829; + + // aapt resource value: 0x7f0a0066 + public const int Base_Widget_AppCompat_Button = 2131361894; + + // aapt resource value: 0x7f0a0067 + public const int Base_Widget_AppCompat_Button_Borderless = 2131361895; + + // aapt resource value: 0x7f0a0068 + public const int Base_Widget_AppCompat_Button_Borderless_Colored = 2131361896; + + // aapt resource value: 0x7f0a00bb + public const int Base_Widget_AppCompat_Button_ButtonBar_AlertDialog = 2131361979; + + // aapt resource value: 0x7f0a008a + public const int Base_Widget_AppCompat_Button_Colored = 2131361930; + + // aapt resource value: 0x7f0a0069 + public const int Base_Widget_AppCompat_Button_Small = 2131361897; + + // aapt resource value: 0x7f0a006a + public const int Base_Widget_AppCompat_ButtonBar = 2131361898; + + // aapt resource value: 0x7f0a00bc + public const int Base_Widget_AppCompat_ButtonBar_AlertDialog = 2131361980; + + // aapt resource value: 0x7f0a006b + public const int Base_Widget_AppCompat_CompoundButton_CheckBox = 2131361899; + + // aapt resource value: 0x7f0a006c + public const int Base_Widget_AppCompat_CompoundButton_RadioButton = 2131361900; + + // aapt resource value: 0x7f0a00bd + public const int Base_Widget_AppCompat_CompoundButton_Switch = 2131361981; + + // aapt resource value: 0x7f0a0000 + public const int Base_Widget_AppCompat_DrawerArrowToggle = 2131361792; + + // aapt resource value: 0x7f0a00be + public const int Base_Widget_AppCompat_DrawerArrowToggle_Common = 2131361982; + + // aapt resource value: 0x7f0a006d + public const int Base_Widget_AppCompat_DropDownItem_Spinner = 2131361901; + + // aapt resource value: 0x7f0a0026 + public const int Base_Widget_AppCompat_EditText = 2131361830; + + // aapt resource value: 0x7f0a006e + public const int Base_Widget_AppCompat_ImageButton = 2131361902; + + // aapt resource value: 0x7f0a00bf + public const int Base_Widget_AppCompat_Light_ActionBar = 2131361983; + + // aapt resource value: 0x7f0a00c0 + public const int Base_Widget_AppCompat_Light_ActionBar_Solid = 2131361984; + + // aapt resource value: 0x7f0a00c1 + public const int Base_Widget_AppCompat_Light_ActionBar_TabBar = 2131361985; + + // aapt resource value: 0x7f0a006f + public const int Base_Widget_AppCompat_Light_ActionBar_TabText = 2131361903; + + // aapt resource value: 0x7f0a0070 + public const int Base_Widget_AppCompat_Light_ActionBar_TabText_Inverse = 2131361904; + + // aapt resource value: 0x7f0a0071 + public const int Base_Widget_AppCompat_Light_ActionBar_TabView = 2131361905; + + // aapt resource value: 0x7f0a0072 + public const int Base_Widget_AppCompat_Light_PopupMenu = 2131361906; + + // aapt resource value: 0x7f0a0073 + public const int Base_Widget_AppCompat_Light_PopupMenu_Overflow = 2131361907; + + // aapt resource value: 0x7f0a00c2 + public const int Base_Widget_AppCompat_ListMenuView = 2131361986; + + // aapt resource value: 0x7f0a0074 + public const int Base_Widget_AppCompat_ListPopupWindow = 2131361908; + + // aapt resource value: 0x7f0a0075 + public const int Base_Widget_AppCompat_ListView = 2131361909; + + // aapt resource value: 0x7f0a0076 + public const int Base_Widget_AppCompat_ListView_DropDown = 2131361910; + + // aapt resource value: 0x7f0a0077 + public const int Base_Widget_AppCompat_ListView_Menu = 2131361911; + + // aapt resource value: 0x7f0a0078 + public const int Base_Widget_AppCompat_PopupMenu = 2131361912; + + // aapt resource value: 0x7f0a0079 + public const int Base_Widget_AppCompat_PopupMenu_Overflow = 2131361913; + + // aapt resource value: 0x7f0a00c3 + public const int Base_Widget_AppCompat_PopupWindow = 2131361987; + + // aapt resource value: 0x7f0a001c + public const int Base_Widget_AppCompat_ProgressBar = 2131361820; + + // aapt resource value: 0x7f0a001d + public const int Base_Widget_AppCompat_ProgressBar_Horizontal = 2131361821; + + // aapt resource value: 0x7f0a007a + public const int Base_Widget_AppCompat_RatingBar = 2131361914; + + // aapt resource value: 0x7f0a008b + public const int Base_Widget_AppCompat_RatingBar_Indicator = 2131361931; + + // aapt resource value: 0x7f0a008c + public const int Base_Widget_AppCompat_RatingBar_Small = 2131361932; + + // aapt resource value: 0x7f0a00c4 + public const int Base_Widget_AppCompat_SearchView = 2131361988; + + // aapt resource value: 0x7f0a00c5 + public const int Base_Widget_AppCompat_SearchView_ActionBar = 2131361989; + + // aapt resource value: 0x7f0a007b + public const int Base_Widget_AppCompat_SeekBar = 2131361915; + + // aapt resource value: 0x7f0a00c6 + public const int Base_Widget_AppCompat_SeekBar_Discrete = 2131361990; + + // aapt resource value: 0x7f0a007c + public const int Base_Widget_AppCompat_Spinner = 2131361916; + + // aapt resource value: 0x7f0a0003 + public const int Base_Widget_AppCompat_Spinner_Underlined = 2131361795; + + // aapt resource value: 0x7f0a007d + public const int Base_Widget_AppCompat_TextView_SpinnerItem = 2131361917; + + // aapt resource value: 0x7f0a0094 + public const int Base_Widget_AppCompat_Toolbar = 2131361940; + + // aapt resource value: 0x7f0a007e + public const int Base_Widget_AppCompat_Toolbar_Button_Navigation = 2131361918; + + // aapt resource value: 0x7f0a015d + public const int Base_Widget_Design_AppBarLayout = 2131362141; + + // aapt resource value: 0x7f0a0161 + public const int Base_Widget_Design_TabLayout = 2131362145; + + // aapt resource value: 0x7f0a001e + public const int Platform_AppCompat = 2131361822; + + // aapt resource value: 0x7f0a001f + public const int Platform_AppCompat_Light = 2131361823; + + // aapt resource value: 0x7f0a007f + public const int Platform_ThemeOverlay_AppCompat = 2131361919; + + // aapt resource value: 0x7f0a0080 + public const int Platform_ThemeOverlay_AppCompat_Dark = 2131361920; + + // aapt resource value: 0x7f0a0081 + public const int Platform_ThemeOverlay_AppCompat_Light = 2131361921; + + // aapt resource value: 0x7f0a0020 + public const int Platform_V11_AppCompat = 2131361824; + + // aapt resource value: 0x7f0a0021 + public const int Platform_V11_AppCompat_Light = 2131361825; + + // aapt resource value: 0x7f0a0028 + public const int Platform_V14_AppCompat = 2131361832; + + // aapt resource value: 0x7f0a0029 + public const int Platform_V14_AppCompat_Light = 2131361833; + + // aapt resource value: 0x7f0a0082 + public const int Platform_V21_AppCompat = 2131361922; + + // aapt resource value: 0x7f0a0083 + public const int Platform_V21_AppCompat_Light = 2131361923; + + // aapt resource value: 0x7f0a008f + public const int Platform_V25_AppCompat = 2131361935; + + // aapt resource value: 0x7f0a0090 + public const int Platform_V25_AppCompat_Light = 2131361936; + + // aapt resource value: 0x7f0a0022 + public const int Platform_Widget_AppCompat_Spinner = 2131361826; + + // aapt resource value: 0x7f0a002b + public const int RtlOverlay_DialogWindowTitle_AppCompat = 2131361835; + + // aapt resource value: 0x7f0a002c + public const int RtlOverlay_Widget_AppCompat_ActionBar_TitleItem = 2131361836; + + // aapt resource value: 0x7f0a002d + public const int RtlOverlay_Widget_AppCompat_DialogTitle_Icon = 2131361837; + + // aapt resource value: 0x7f0a002e + public const int RtlOverlay_Widget_AppCompat_PopupMenuItem = 2131361838; + + // aapt resource value: 0x7f0a002f + public const int RtlOverlay_Widget_AppCompat_PopupMenuItem_InternalGroup = 2131361839; + + // aapt resource value: 0x7f0a0030 + public const int RtlOverlay_Widget_AppCompat_PopupMenuItem_Text = 2131361840; + + // aapt resource value: 0x7f0a0031 + public const int RtlOverlay_Widget_AppCompat_Search_DropDown = 2131361841; + + // aapt resource value: 0x7f0a0032 + public const int RtlOverlay_Widget_AppCompat_Search_DropDown_Icon1 = 2131361842; + + // aapt resource value: 0x7f0a0033 + public const int RtlOverlay_Widget_AppCompat_Search_DropDown_Icon2 = 2131361843; + + // aapt resource value: 0x7f0a0034 + public const int RtlOverlay_Widget_AppCompat_Search_DropDown_Query = 2131361844; + + // aapt resource value: 0x7f0a0035 + public const int RtlOverlay_Widget_AppCompat_Search_DropDown_Text = 2131361845; + + // aapt resource value: 0x7f0a0036 + public const int RtlOverlay_Widget_AppCompat_SearchView_MagIcon = 2131361846; + + // aapt resource value: 0x7f0a0037 + public const int RtlUnderlay_Widget_AppCompat_ActionButton = 2131361847; + + // aapt resource value: 0x7f0a0038 + public const int RtlUnderlay_Widget_AppCompat_ActionButton_Overflow = 2131361848; + + // aapt resource value: 0x7f0a00c7 + public const int TextAppearance_AppCompat = 2131361991; + + // aapt resource value: 0x7f0a00c8 + public const int TextAppearance_AppCompat_Body1 = 2131361992; + + // aapt resource value: 0x7f0a00c9 + public const int TextAppearance_AppCompat_Body2 = 2131361993; + + // aapt resource value: 0x7f0a00ca + public const int TextAppearance_AppCompat_Button = 2131361994; + + // aapt resource value: 0x7f0a00cb + public const int TextAppearance_AppCompat_Caption = 2131361995; + + // aapt resource value: 0x7f0a00cc + public const int TextAppearance_AppCompat_Display1 = 2131361996; + + // aapt resource value: 0x7f0a00cd + public const int TextAppearance_AppCompat_Display2 = 2131361997; + + // aapt resource value: 0x7f0a00ce + public const int TextAppearance_AppCompat_Display3 = 2131361998; + + // aapt resource value: 0x7f0a00cf + public const int TextAppearance_AppCompat_Display4 = 2131361999; + + // aapt resource value: 0x7f0a00d0 + public const int TextAppearance_AppCompat_Headline = 2131362000; + + // aapt resource value: 0x7f0a00d1 + public const int TextAppearance_AppCompat_Inverse = 2131362001; + + // aapt resource value: 0x7f0a00d2 + public const int TextAppearance_AppCompat_Large = 2131362002; + + // aapt resource value: 0x7f0a00d3 + public const int TextAppearance_AppCompat_Large_Inverse = 2131362003; + + // aapt resource value: 0x7f0a00d4 + public const int TextAppearance_AppCompat_Light_SearchResult_Subtitle = 2131362004; + + // aapt resource value: 0x7f0a00d5 + public const int TextAppearance_AppCompat_Light_SearchResult_Title = 2131362005; + + // aapt resource value: 0x7f0a00d6 + public const int TextAppearance_AppCompat_Light_Widget_PopupMenu_Large = 2131362006; + + // aapt resource value: 0x7f0a00d7 + public const int TextAppearance_AppCompat_Light_Widget_PopupMenu_Small = 2131362007; + + // aapt resource value: 0x7f0a00d8 + public const int TextAppearance_AppCompat_Medium = 2131362008; + + // aapt resource value: 0x7f0a00d9 + public const int TextAppearance_AppCompat_Medium_Inverse = 2131362009; + + // aapt resource value: 0x7f0a00da + public const int TextAppearance_AppCompat_Menu = 2131362010; + + // aapt resource value: 0x7f0a00db + public const int TextAppearance_AppCompat_SearchResult_Subtitle = 2131362011; + + // aapt resource value: 0x7f0a00dc + public const int TextAppearance_AppCompat_SearchResult_Title = 2131362012; + + // aapt resource value: 0x7f0a00dd + public const int TextAppearance_AppCompat_Small = 2131362013; + + // aapt resource value: 0x7f0a00de + public const int TextAppearance_AppCompat_Small_Inverse = 2131362014; + + // aapt resource value: 0x7f0a00df + public const int TextAppearance_AppCompat_Subhead = 2131362015; + + // aapt resource value: 0x7f0a00e0 + public const int TextAppearance_AppCompat_Subhead_Inverse = 2131362016; + + // aapt resource value: 0x7f0a00e1 + public const int TextAppearance_AppCompat_Title = 2131362017; + + // aapt resource value: 0x7f0a00e2 + public const int TextAppearance_AppCompat_Title_Inverse = 2131362018; + + // aapt resource value: 0x7f0a002a + public const int TextAppearance_AppCompat_Tooltip = 2131361834; + + // aapt resource value: 0x7f0a00e3 + public const int TextAppearance_AppCompat_Widget_ActionBar_Menu = 2131362019; + + // aapt resource value: 0x7f0a00e4 + public const int TextAppearance_AppCompat_Widget_ActionBar_Subtitle = 2131362020; + + // aapt resource value: 0x7f0a00e5 + public const int TextAppearance_AppCompat_Widget_ActionBar_Subtitle_Inverse = 2131362021; + + // aapt resource value: 0x7f0a00e6 + public const int TextAppearance_AppCompat_Widget_ActionBar_Title = 2131362022; + + // aapt resource value: 0x7f0a00e7 + public const int TextAppearance_AppCompat_Widget_ActionBar_Title_Inverse = 2131362023; + + // aapt resource value: 0x7f0a00e8 + public const int TextAppearance_AppCompat_Widget_ActionMode_Subtitle = 2131362024; + + // aapt resource value: 0x7f0a00e9 + public const int TextAppearance_AppCompat_Widget_ActionMode_Subtitle_Inverse = 2131362025; + + // aapt resource value: 0x7f0a00ea + public const int TextAppearance_AppCompat_Widget_ActionMode_Title = 2131362026; + + // aapt resource value: 0x7f0a00eb + public const int TextAppearance_AppCompat_Widget_ActionMode_Title_Inverse = 2131362027; + + // aapt resource value: 0x7f0a00ec + public const int TextAppearance_AppCompat_Widget_Button = 2131362028; + + // aapt resource value: 0x7f0a00ed + public const int TextAppearance_AppCompat_Widget_Button_Borderless_Colored = 2131362029; + + // aapt resource value: 0x7f0a00ee + public const int TextAppearance_AppCompat_Widget_Button_Colored = 2131362030; + + // aapt resource value: 0x7f0a00ef + public const int TextAppearance_AppCompat_Widget_Button_Inverse = 2131362031; + + // aapt resource value: 0x7f0a00f0 + public const int TextAppearance_AppCompat_Widget_DropDownItem = 2131362032; + + // aapt resource value: 0x7f0a00f1 + public const int TextAppearance_AppCompat_Widget_PopupMenu_Header = 2131362033; + + // aapt resource value: 0x7f0a00f2 + public const int TextAppearance_AppCompat_Widget_PopupMenu_Large = 2131362034; + + // aapt resource value: 0x7f0a00f3 + public const int TextAppearance_AppCompat_Widget_PopupMenu_Small = 2131362035; + + // aapt resource value: 0x7f0a00f4 + public const int TextAppearance_AppCompat_Widget_Switch = 2131362036; + + // aapt resource value: 0x7f0a00f5 + public const int TextAppearance_AppCompat_Widget_TextView_SpinnerItem = 2131362037; + + // aapt resource value: 0x7f0a0179 + public const int TextAppearance_Compat_Notification = 2131362169; + + // aapt resource value: 0x7f0a017a + public const int TextAppearance_Compat_Notification_Info = 2131362170; + + // aapt resource value: 0x7f0a0156 + public const int TextAppearance_Compat_Notification_Info_Media = 2131362134; + + // aapt resource value: 0x7f0a017f + public const int TextAppearance_Compat_Notification_Line2 = 2131362175; + + // aapt resource value: 0x7f0a015a + public const int TextAppearance_Compat_Notification_Line2_Media = 2131362138; + + // aapt resource value: 0x7f0a0157 + public const int TextAppearance_Compat_Notification_Media = 2131362135; + + // aapt resource value: 0x7f0a017b + public const int TextAppearance_Compat_Notification_Time = 2131362171; + + // aapt resource value: 0x7f0a0158 + public const int TextAppearance_Compat_Notification_Time_Media = 2131362136; + + // aapt resource value: 0x7f0a017c + public const int TextAppearance_Compat_Notification_Title = 2131362172; + + // aapt resource value: 0x7f0a0159 + public const int TextAppearance_Compat_Notification_Title_Media = 2131362137; + + // aapt resource value: 0x7f0a0162 + public const int TextAppearance_Design_CollapsingToolbar_Expanded = 2131362146; + + // aapt resource value: 0x7f0a0163 + public const int TextAppearance_Design_Counter = 2131362147; + + // aapt resource value: 0x7f0a0164 + public const int TextAppearance_Design_Counter_Overflow = 2131362148; + + // aapt resource value: 0x7f0a0165 + public const int TextAppearance_Design_Error = 2131362149; + + // aapt resource value: 0x7f0a0166 + public const int TextAppearance_Design_Hint = 2131362150; + + // aapt resource value: 0x7f0a0167 + public const int TextAppearance_Design_Snackbar_Message = 2131362151; + + // aapt resource value: 0x7f0a0168 + public const int TextAppearance_Design_Tab = 2131362152; + + // aapt resource value: 0x7f0a00f6 + public const int TextAppearance_Widget_AppCompat_ExpandedMenu_Item = 2131362038; + + // aapt resource value: 0x7f0a00f7 + public const int TextAppearance_Widget_AppCompat_Toolbar_Subtitle = 2131362039; + + // aapt resource value: 0x7f0a00f8 + public const int TextAppearance_Widget_AppCompat_Toolbar_Title = 2131362040; + + // aapt resource value: 0x7f0a00f9 + public const int Theme_AppCompat = 2131362041; + + // aapt resource value: 0x7f0a00fa + public const int Theme_AppCompat_CompactMenu = 2131362042; + + // aapt resource value: 0x7f0a0004 + public const int Theme_AppCompat_DayNight = 2131361796; + + // aapt resource value: 0x7f0a0005 + public const int Theme_AppCompat_DayNight_DarkActionBar = 2131361797; + + // aapt resource value: 0x7f0a0006 + public const int Theme_AppCompat_DayNight_Dialog = 2131361798; + + // aapt resource value: 0x7f0a0007 + public const int Theme_AppCompat_DayNight_Dialog_Alert = 2131361799; + + // aapt resource value: 0x7f0a0008 + public const int Theme_AppCompat_DayNight_Dialog_MinWidth = 2131361800; + + // aapt resource value: 0x7f0a0009 + public const int Theme_AppCompat_DayNight_DialogWhenLarge = 2131361801; + + // aapt resource value: 0x7f0a000a + public const int Theme_AppCompat_DayNight_NoActionBar = 2131361802; + + // aapt resource value: 0x7f0a00fb + public const int Theme_AppCompat_Dialog = 2131362043; + + // aapt resource value: 0x7f0a00fc + public const int Theme_AppCompat_Dialog_Alert = 2131362044; + + // aapt resource value: 0x7f0a00fd + public const int Theme_AppCompat_Dialog_MinWidth = 2131362045; + + // aapt resource value: 0x7f0a00fe + public const int Theme_AppCompat_DialogWhenLarge = 2131362046; + + // aapt resource value: 0x7f0a00ff + public const int Theme_AppCompat_Light = 2131362047; + + // aapt resource value: 0x7f0a0100 + public const int Theme_AppCompat_Light_DarkActionBar = 2131362048; + + // aapt resource value: 0x7f0a0101 + public const int Theme_AppCompat_Light_Dialog = 2131362049; + + // aapt resource value: 0x7f0a0102 + public const int Theme_AppCompat_Light_Dialog_Alert = 2131362050; + + // aapt resource value: 0x7f0a0103 + public const int Theme_AppCompat_Light_Dialog_MinWidth = 2131362051; + + // aapt resource value: 0x7f0a0104 + public const int Theme_AppCompat_Light_DialogWhenLarge = 2131362052; + + // aapt resource value: 0x7f0a0105 + public const int Theme_AppCompat_Light_NoActionBar = 2131362053; + + // aapt resource value: 0x7f0a0106 + public const int Theme_AppCompat_NoActionBar = 2131362054; + + // aapt resource value: 0x7f0a0169 + public const int Theme_Design = 2131362153; + + // aapt resource value: 0x7f0a016a + public const int Theme_Design_BottomSheetDialog = 2131362154; + + // aapt resource value: 0x7f0a016b + public const int Theme_Design_Light = 2131362155; + + // aapt resource value: 0x7f0a016c + public const int Theme_Design_Light_BottomSheetDialog = 2131362156; + + // aapt resource value: 0x7f0a016d + public const int Theme_Design_Light_NoActionBar = 2131362157; + + // aapt resource value: 0x7f0a016e + public const int Theme_Design_NoActionBar = 2131362158; + + // aapt resource value: 0x7f0a0107 + public const int ThemeOverlay_AppCompat = 2131362055; + + // aapt resource value: 0x7f0a0108 + public const int ThemeOverlay_AppCompat_ActionBar = 2131362056; + + // aapt resource value: 0x7f0a0109 + public const int ThemeOverlay_AppCompat_Dark = 2131362057; + + // aapt resource value: 0x7f0a010a + public const int ThemeOverlay_AppCompat_Dark_ActionBar = 2131362058; + + // aapt resource value: 0x7f0a010b + public const int ThemeOverlay_AppCompat_Dialog = 2131362059; + + // aapt resource value: 0x7f0a010c + public const int ThemeOverlay_AppCompat_Dialog_Alert = 2131362060; + + // aapt resource value: 0x7f0a010d + public const int ThemeOverlay_AppCompat_Light = 2131362061; + + // aapt resource value: 0x7f0a010e + public const int Widget_AppCompat_ActionBar = 2131362062; + + // aapt resource value: 0x7f0a010f + public const int Widget_AppCompat_ActionBar_Solid = 2131362063; + + // aapt resource value: 0x7f0a0110 + public const int Widget_AppCompat_ActionBar_TabBar = 2131362064; + + // aapt resource value: 0x7f0a0111 + public const int Widget_AppCompat_ActionBar_TabText = 2131362065; + + // aapt resource value: 0x7f0a0112 + public const int Widget_AppCompat_ActionBar_TabView = 2131362066; + + // aapt resource value: 0x7f0a0113 + public const int Widget_AppCompat_ActionButton = 2131362067; + + // aapt resource value: 0x7f0a0114 + public const int Widget_AppCompat_ActionButton_CloseMode = 2131362068; + + // aapt resource value: 0x7f0a0115 + public const int Widget_AppCompat_ActionButton_Overflow = 2131362069; + + // aapt resource value: 0x7f0a0116 + public const int Widget_AppCompat_ActionMode = 2131362070; + + // aapt resource value: 0x7f0a0117 + public const int Widget_AppCompat_ActivityChooserView = 2131362071; + + // aapt resource value: 0x7f0a0118 + public const int Widget_AppCompat_AutoCompleteTextView = 2131362072; + + // aapt resource value: 0x7f0a0119 + public const int Widget_AppCompat_Button = 2131362073; + + // aapt resource value: 0x7f0a011a + public const int Widget_AppCompat_Button_Borderless = 2131362074; + + // aapt resource value: 0x7f0a011b + public const int Widget_AppCompat_Button_Borderless_Colored = 2131362075; + + // aapt resource value: 0x7f0a011c + public const int Widget_AppCompat_Button_ButtonBar_AlertDialog = 2131362076; + + // aapt resource value: 0x7f0a011d + public const int Widget_AppCompat_Button_Colored = 2131362077; + + // aapt resource value: 0x7f0a011e + public const int Widget_AppCompat_Button_Small = 2131362078; + + // aapt resource value: 0x7f0a011f + public const int Widget_AppCompat_ButtonBar = 2131362079; + + // aapt resource value: 0x7f0a0120 + public const int Widget_AppCompat_ButtonBar_AlertDialog = 2131362080; + + // aapt resource value: 0x7f0a0121 + public const int Widget_AppCompat_CompoundButton_CheckBox = 2131362081; + + // aapt resource value: 0x7f0a0122 + public const int Widget_AppCompat_CompoundButton_RadioButton = 2131362082; + + // aapt resource value: 0x7f0a0123 + public const int Widget_AppCompat_CompoundButton_Switch = 2131362083; + + // aapt resource value: 0x7f0a0124 + public const int Widget_AppCompat_DrawerArrowToggle = 2131362084; + + // aapt resource value: 0x7f0a0125 + public const int Widget_AppCompat_DropDownItem_Spinner = 2131362085; + + // aapt resource value: 0x7f0a0126 + public const int Widget_AppCompat_EditText = 2131362086; + + // aapt resource value: 0x7f0a0127 + public const int Widget_AppCompat_ImageButton = 2131362087; + + // aapt resource value: 0x7f0a0128 + public const int Widget_AppCompat_Light_ActionBar = 2131362088; + + // aapt resource value: 0x7f0a0129 + public const int Widget_AppCompat_Light_ActionBar_Solid = 2131362089; + + // aapt resource value: 0x7f0a012a + public const int Widget_AppCompat_Light_ActionBar_Solid_Inverse = 2131362090; + + // aapt resource value: 0x7f0a012b + public const int Widget_AppCompat_Light_ActionBar_TabBar = 2131362091; + + // aapt resource value: 0x7f0a012c + public const int Widget_AppCompat_Light_ActionBar_TabBar_Inverse = 2131362092; + + // aapt resource value: 0x7f0a012d + public const int Widget_AppCompat_Light_ActionBar_TabText = 2131362093; + + // aapt resource value: 0x7f0a012e + public const int Widget_AppCompat_Light_ActionBar_TabText_Inverse = 2131362094; + + // aapt resource value: 0x7f0a012f + public const int Widget_AppCompat_Light_ActionBar_TabView = 2131362095; + + // aapt resource value: 0x7f0a0130 + public const int Widget_AppCompat_Light_ActionBar_TabView_Inverse = 2131362096; + + // aapt resource value: 0x7f0a0131 + public const int Widget_AppCompat_Light_ActionButton = 2131362097; + + // aapt resource value: 0x7f0a0132 + public const int Widget_AppCompat_Light_ActionButton_CloseMode = 2131362098; + + // aapt resource value: 0x7f0a0133 + public const int Widget_AppCompat_Light_ActionButton_Overflow = 2131362099; + + // aapt resource value: 0x7f0a0134 + public const int Widget_AppCompat_Light_ActionMode_Inverse = 2131362100; + + // aapt resource value: 0x7f0a0135 + public const int Widget_AppCompat_Light_ActivityChooserView = 2131362101; + + // aapt resource value: 0x7f0a0136 + public const int Widget_AppCompat_Light_AutoCompleteTextView = 2131362102; + + // aapt resource value: 0x7f0a0137 + public const int Widget_AppCompat_Light_DropDownItem_Spinner = 2131362103; + + // aapt resource value: 0x7f0a0138 + public const int Widget_AppCompat_Light_ListPopupWindow = 2131362104; + + // aapt resource value: 0x7f0a0139 + public const int Widget_AppCompat_Light_ListView_DropDown = 2131362105; + + // aapt resource value: 0x7f0a013a + public const int Widget_AppCompat_Light_PopupMenu = 2131362106; + + // aapt resource value: 0x7f0a013b + public const int Widget_AppCompat_Light_PopupMenu_Overflow = 2131362107; + + // aapt resource value: 0x7f0a013c + public const int Widget_AppCompat_Light_SearchView = 2131362108; + + // aapt resource value: 0x7f0a013d + public const int Widget_AppCompat_Light_Spinner_DropDown_ActionBar = 2131362109; + + // aapt resource value: 0x7f0a013e + public const int Widget_AppCompat_ListMenuView = 2131362110; + + // aapt resource value: 0x7f0a013f + public const int Widget_AppCompat_ListPopupWindow = 2131362111; + + // aapt resource value: 0x7f0a0140 + public const int Widget_AppCompat_ListView = 2131362112; + + // aapt resource value: 0x7f0a0141 + public const int Widget_AppCompat_ListView_DropDown = 2131362113; + + // aapt resource value: 0x7f0a0142 + public const int Widget_AppCompat_ListView_Menu = 2131362114; + + // aapt resource value: 0x7f0a0143 + public const int Widget_AppCompat_PopupMenu = 2131362115; + + // aapt resource value: 0x7f0a0144 + public const int Widget_AppCompat_PopupMenu_Overflow = 2131362116; + + // aapt resource value: 0x7f0a0145 + public const int Widget_AppCompat_PopupWindow = 2131362117; + + // aapt resource value: 0x7f0a0146 + public const int Widget_AppCompat_ProgressBar = 2131362118; + + // aapt resource value: 0x7f0a0147 + public const int Widget_AppCompat_ProgressBar_Horizontal = 2131362119; + + // aapt resource value: 0x7f0a0148 + public const int Widget_AppCompat_RatingBar = 2131362120; + + // aapt resource value: 0x7f0a0149 + public const int Widget_AppCompat_RatingBar_Indicator = 2131362121; + + // aapt resource value: 0x7f0a014a + public const int Widget_AppCompat_RatingBar_Small = 2131362122; + + // aapt resource value: 0x7f0a014b + public const int Widget_AppCompat_SearchView = 2131362123; + + // aapt resource value: 0x7f0a014c + public const int Widget_AppCompat_SearchView_ActionBar = 2131362124; + + // aapt resource value: 0x7f0a014d + public const int Widget_AppCompat_SeekBar = 2131362125; + + // aapt resource value: 0x7f0a014e + public const int Widget_AppCompat_SeekBar_Discrete = 2131362126; + + // aapt resource value: 0x7f0a014f + public const int Widget_AppCompat_Spinner = 2131362127; + + // aapt resource value: 0x7f0a0150 + public const int Widget_AppCompat_Spinner_DropDown = 2131362128; + + // aapt resource value: 0x7f0a0151 + public const int Widget_AppCompat_Spinner_DropDown_ActionBar = 2131362129; + + // aapt resource value: 0x7f0a0152 + public const int Widget_AppCompat_Spinner_Underlined = 2131362130; + + // aapt resource value: 0x7f0a0153 + public const int Widget_AppCompat_TextView_SpinnerItem = 2131362131; + + // aapt resource value: 0x7f0a0154 + public const int Widget_AppCompat_Toolbar = 2131362132; + + // aapt resource value: 0x7f0a0155 + public const int Widget_AppCompat_Toolbar_Button_Navigation = 2131362133; + + // aapt resource value: 0x7f0a017d + public const int Widget_Compat_NotificationActionContainer = 2131362173; + + // aapt resource value: 0x7f0a017e + public const int Widget_Compat_NotificationActionText = 2131362174; + + // aapt resource value: 0x7f0a016f + public const int Widget_Design_AppBarLayout = 2131362159; + + // aapt resource value: 0x7f0a0170 + public const int Widget_Design_BottomNavigationView = 2131362160; + + // aapt resource value: 0x7f0a0171 + public const int Widget_Design_BottomSheet_Modal = 2131362161; + + // aapt resource value: 0x7f0a0172 + public const int Widget_Design_CollapsingToolbar = 2131362162; + + // aapt resource value: 0x7f0a0173 + public const int Widget_Design_CoordinatorLayout = 2131362163; + + // aapt resource value: 0x7f0a0174 + public const int Widget_Design_FloatingActionButton = 2131362164; + + // aapt resource value: 0x7f0a0175 + public const int Widget_Design_NavigationView = 2131362165; + + // aapt resource value: 0x7f0a0176 + public const int Widget_Design_ScrimInsetsFrameLayout = 2131362166; + + // aapt resource value: 0x7f0a0177 + public const int Widget_Design_Snackbar = 2131362167; + + // aapt resource value: 0x7f0a015b + public const int Widget_Design_TabLayout = 2131362139; + + // aapt resource value: 0x7f0a0178 + public const int Widget_Design_TextInputLayout = 2131362168; + + static Style() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Style() + { + } + } + + public partial class Styleable + { + + public static int[] ActionBar = new int[] { + 2130771978, + 2130771980, + 2130771981, + 2130771982, + 2130771983, + 2130771984, + 2130771985, + 2130771986, + 2130771987, + 2130771988, + 2130771989, + 2130771990, + 2130771991, + 2130771992, + 2130771993, + 2130771994, + 2130771995, + 2130771996, + 2130771997, + 2130771998, + 2130771999, + 2130772000, + 2130772001, + 2130772002, + 2130772003, + 2130772004, + 2130772005, + 2130772006, + 2130772076}; + + // aapt resource value: 10 + public const int ActionBar_background = 10; + + // aapt resource value: 12 + public const int ActionBar_backgroundSplit = 12; + + // aapt resource value: 11 + public const int ActionBar_backgroundStacked = 11; + + // aapt resource value: 21 + public const int ActionBar_contentInsetEnd = 21; + + // aapt resource value: 25 + public const int ActionBar_contentInsetEndWithActions = 25; + + // aapt resource value: 22 + public const int ActionBar_contentInsetLeft = 22; + + // aapt resource value: 23 + public const int ActionBar_contentInsetRight = 23; + + // aapt resource value: 20 + public const int ActionBar_contentInsetStart = 20; + + // aapt resource value: 24 + public const int ActionBar_contentInsetStartWithNavigation = 24; + + // aapt resource value: 13 + public const int ActionBar_customNavigationLayout = 13; + + // aapt resource value: 3 + public const int ActionBar_displayOptions = 3; + + // aapt resource value: 9 + public const int ActionBar_divider = 9; + + // aapt resource value: 26 + public const int ActionBar_elevation = 26; + + // aapt resource value: 0 + public const int ActionBar_height = 0; + + // aapt resource value: 19 + public const int ActionBar_hideOnContentScroll = 19; + + // aapt resource value: 28 + public const int ActionBar_homeAsUpIndicator = 28; + + // aapt resource value: 14 + public const int ActionBar_homeLayout = 14; + + // aapt resource value: 7 + public const int ActionBar_icon = 7; + + // aapt resource value: 16 + public const int ActionBar_indeterminateProgressStyle = 16; + + // aapt resource value: 18 + public const int ActionBar_itemPadding = 18; + + // aapt resource value: 8 + public const int ActionBar_logo = 8; + + // aapt resource value: 2 + public const int ActionBar_navigationMode = 2; + + // aapt resource value: 27 + public const int ActionBar_popupTheme = 27; + + // aapt resource value: 17 + public const int ActionBar_progressBarPadding = 17; + + // aapt resource value: 15 + public const int ActionBar_progressBarStyle = 15; + + // aapt resource value: 4 + public const int ActionBar_subtitle = 4; + + // aapt resource value: 6 + public const int ActionBar_subtitleTextStyle = 6; + + // aapt resource value: 1 + public const int ActionBar_title = 1; + + // aapt resource value: 5 + public const int ActionBar_titleTextStyle = 5; + + public static int[] ActionBarLayout = new int[] { + 16842931}; + + // aapt resource value: 0 + public const int ActionBarLayout_android_layout_gravity = 0; + + public static int[] ActionMenuItemView = new int[] { + 16843071}; + + // aapt resource value: 0 + public const int ActionMenuItemView_android_minWidth = 0; + + public static int[] ActionMenuView; + + public static int[] ActionMode = new int[] { + 2130771978, + 2130771984, + 2130771985, + 2130771989, + 2130771991, + 2130772007}; + + // aapt resource value: 3 + public const int ActionMode_background = 3; + + // aapt resource value: 4 + public const int ActionMode_backgroundSplit = 4; + + // aapt resource value: 5 + public const int ActionMode_closeItemLayout = 5; + + // aapt resource value: 0 + public const int ActionMode_height = 0; + + // aapt resource value: 2 + public const int ActionMode_subtitleTextStyle = 2; + + // aapt resource value: 1 + public const int ActionMode_titleTextStyle = 1; + + public static int[] ActivityChooserView = new int[] { + 2130772008, + 2130772009}; + + // aapt resource value: 1 + public const int ActivityChooserView_expandActivityOverflowButtonDrawable = 1; + + // aapt resource value: 0 + public const int ActivityChooserView_initialActivityCount = 0; + + public static int[] AlertDialog = new int[] { + 16842994, + 2130772010, + 2130772011, + 2130772012, + 2130772013, + 2130772014, + 2130772015}; + + // aapt resource value: 0 + public const int AlertDialog_android_layout = 0; + + // aapt resource value: 1 + public const int AlertDialog_buttonPanelSideLayout = 1; + + // aapt resource value: 5 + public const int AlertDialog_listItemLayout = 5; + + // aapt resource value: 2 + public const int AlertDialog_listLayout = 2; + + // aapt resource value: 3 + public const int AlertDialog_multiChoiceItemLayout = 3; + + // aapt resource value: 6 + public const int AlertDialog_showTitle = 6; + + // aapt resource value: 4 + public const int AlertDialog_singleChoiceItemLayout = 4; + + public static int[] AppBarLayout = new int[] { + 16842964, + 16843919, + 16844096, + 2130772005, + 2130772223}; + + // aapt resource value: 0 + public const int AppBarLayout_android_background = 0; + + // aapt resource value: 2 + public const int AppBarLayout_android_keyboardNavigationCluster = 2; + + // aapt resource value: 1 + public const int AppBarLayout_android_touchscreenBlocksFocus = 1; + + // aapt resource value: 3 + public const int AppBarLayout_elevation = 3; + + // aapt resource value: 4 + public const int AppBarLayout_expanded = 4; + + public static int[] AppBarLayoutStates = new int[] { + 2130772224, + 2130772225}; + + // aapt resource value: 0 + public const int AppBarLayoutStates_state_collapsed = 0; + + // aapt resource value: 1 + public const int AppBarLayoutStates_state_collapsible = 1; + + public static int[] AppBarLayout_Layout = new int[] { + 2130772226, + 2130772227}; + + // aapt resource value: 0 + public const int AppBarLayout_Layout_layout_scrollFlags = 0; + + // aapt resource value: 1 + public const int AppBarLayout_Layout_layout_scrollInterpolator = 1; + + public static int[] AppCompatImageView = new int[] { + 16843033, + 2130772016, + 2130772017, + 2130772018}; + + // aapt resource value: 0 + public const int AppCompatImageView_android_src = 0; + + // aapt resource value: 1 + public const int AppCompatImageView_srcCompat = 1; + + // aapt resource value: 2 + public const int AppCompatImageView_tint = 2; + + // aapt resource value: 3 + public const int AppCompatImageView_tintMode = 3; + + public static int[] AppCompatSeekBar = new int[] { + 16843074, + 2130772019, + 2130772020, + 2130772021}; + + // aapt resource value: 0 + public const int AppCompatSeekBar_android_thumb = 0; + + // aapt resource value: 1 + public const int AppCompatSeekBar_tickMark = 1; + + // aapt resource value: 2 + public const int AppCompatSeekBar_tickMarkTint = 2; + + // aapt resource value: 3 + public const int AppCompatSeekBar_tickMarkTintMode = 3; + + public static int[] AppCompatTextHelper = new int[] { + 16842804, + 16843117, + 16843118, + 16843119, + 16843120, + 16843666, + 16843667}; + + // aapt resource value: 2 + public const int AppCompatTextHelper_android_drawableBottom = 2; + + // aapt resource value: 6 + public const int AppCompatTextHelper_android_drawableEnd = 6; + + // aapt resource value: 3 + public const int AppCompatTextHelper_android_drawableLeft = 3; + + // aapt resource value: 4 + public const int AppCompatTextHelper_android_drawableRight = 4; + + // aapt resource value: 5 + public const int AppCompatTextHelper_android_drawableStart = 5; + + // aapt resource value: 1 + public const int AppCompatTextHelper_android_drawableTop = 1; + + // aapt resource value: 0 + public const int AppCompatTextHelper_android_textAppearance = 0; + + public static int[] AppCompatTextView = new int[] { + 16842804, + 2130772022, + 2130772023, + 2130772024, + 2130772025, + 2130772026, + 2130772027, + 2130772028}; + + // aapt resource value: 0 + public const int AppCompatTextView_android_textAppearance = 0; + + // aapt resource value: 6 + public const int AppCompatTextView_autoSizeMaxTextSize = 6; + + // aapt resource value: 5 + public const int AppCompatTextView_autoSizeMinTextSize = 5; + + // aapt resource value: 4 + public const int AppCompatTextView_autoSizePresetSizes = 4; + + // aapt resource value: 3 + public const int AppCompatTextView_autoSizeStepGranularity = 3; + + // aapt resource value: 2 + public const int AppCompatTextView_autoSizeTextType = 2; + + // aapt resource value: 7 + public const int AppCompatTextView_fontFamily = 7; + + // aapt resource value: 1 + public const int AppCompatTextView_textAllCaps = 1; + + public static int[] AppCompatTheme = new int[] { + 16842839, + 16842926, + 2130772029, + 2130772030, + 2130772031, + 2130772032, + 2130772033, + 2130772034, + 2130772035, + 2130772036, + 2130772037, + 2130772038, + 2130772039, + 2130772040, + 2130772041, + 2130772042, + 2130772043, + 2130772044, + 2130772045, + 2130772046, + 2130772047, + 2130772048, + 2130772049, + 2130772050, + 2130772051, + 2130772052, + 2130772053, + 2130772054, + 2130772055, + 2130772056, + 2130772057, + 2130772058, + 2130772059, + 2130772060, + 2130772061, + 2130772062, + 2130772063, + 2130772064, + 2130772065, + 2130772066, + 2130772067, + 2130772068, + 2130772069, + 2130772070, + 2130772071, + 2130772072, + 2130772073, + 2130772074, + 2130772075, + 2130772076, + 2130772077, + 2130772078, + 2130772079, + 2130772080, + 2130772081, + 2130772082, + 2130772083, + 2130772084, + 2130772085, + 2130772086, + 2130772087, + 2130772088, + 2130772089, + 2130772090, + 2130772091, + 2130772092, + 2130772093, + 2130772094, + 2130772095, + 2130772096, + 2130772097, + 2130772098, + 2130772099, + 2130772100, + 2130772101, + 2130772102, + 2130772103, + 2130772104, + 2130772105, + 2130772106, + 2130772107, + 2130772108, + 2130772109, + 2130772110, + 2130772111, + 2130772112, + 2130772113, + 2130772114, + 2130772115, + 2130772116, + 2130772117, + 2130772118, + 2130772119, + 2130772120, + 2130772121, + 2130772122, + 2130772123, + 2130772124, + 2130772125, + 2130772126, + 2130772127, + 2130772128, + 2130772129, + 2130772130, + 2130772131, + 2130772132, + 2130772133, + 2130772134, + 2130772135, + 2130772136, + 2130772137, + 2130772138, + 2130772139, + 2130772140, + 2130772141, + 2130772142, + 2130772143, + 2130772144, + 2130772145}; + + // aapt resource value: 23 + public const int AppCompatTheme_actionBarDivider = 23; + + // aapt resource value: 24 + public const int AppCompatTheme_actionBarItemBackground = 24; + + // aapt resource value: 17 + public const int AppCompatTheme_actionBarPopupTheme = 17; + + // aapt resource value: 22 + public const int AppCompatTheme_actionBarSize = 22; + + // aapt resource value: 19 + public const int AppCompatTheme_actionBarSplitStyle = 19; + + // aapt resource value: 18 + public const int AppCompatTheme_actionBarStyle = 18; + + // aapt resource value: 13 + public const int AppCompatTheme_actionBarTabBarStyle = 13; + + // aapt resource value: 12 + public const int AppCompatTheme_actionBarTabStyle = 12; + + // aapt resource value: 14 + public const int AppCompatTheme_actionBarTabTextStyle = 14; + + // aapt resource value: 20 + public const int AppCompatTheme_actionBarTheme = 20; + + // aapt resource value: 21 + public const int AppCompatTheme_actionBarWidgetTheme = 21; + + // aapt resource value: 50 + public const int AppCompatTheme_actionButtonStyle = 50; + + // aapt resource value: 46 + public const int AppCompatTheme_actionDropDownStyle = 46; + + // aapt resource value: 25 + public const int AppCompatTheme_actionMenuTextAppearance = 25; + + // aapt resource value: 26 + public const int AppCompatTheme_actionMenuTextColor = 26; + + // aapt resource value: 29 + public const int AppCompatTheme_actionModeBackground = 29; + + // aapt resource value: 28 + public const int AppCompatTheme_actionModeCloseButtonStyle = 28; + + // aapt resource value: 31 + public const int AppCompatTheme_actionModeCloseDrawable = 31; + + // aapt resource value: 33 + public const int AppCompatTheme_actionModeCopyDrawable = 33; + + // aapt resource value: 32 + public const int AppCompatTheme_actionModeCutDrawable = 32; + + // aapt resource value: 37 + public const int AppCompatTheme_actionModeFindDrawable = 37; + + // aapt resource value: 34 + public const int AppCompatTheme_actionModePasteDrawable = 34; + + // aapt resource value: 39 + public const int AppCompatTheme_actionModePopupWindowStyle = 39; + + // aapt resource value: 35 + public const int AppCompatTheme_actionModeSelectAllDrawable = 35; + + // aapt resource value: 36 + public const int AppCompatTheme_actionModeShareDrawable = 36; + + // aapt resource value: 30 + public const int AppCompatTheme_actionModeSplitBackground = 30; + + // aapt resource value: 27 + public const int AppCompatTheme_actionModeStyle = 27; + + // aapt resource value: 38 + public const int AppCompatTheme_actionModeWebSearchDrawable = 38; + + // aapt resource value: 15 + public const int AppCompatTheme_actionOverflowButtonStyle = 15; + + // aapt resource value: 16 + public const int AppCompatTheme_actionOverflowMenuStyle = 16; + + // aapt resource value: 58 + public const int AppCompatTheme_activityChooserViewStyle = 58; + + // aapt resource value: 95 + public const int AppCompatTheme_alertDialogButtonGroupStyle = 95; + + // aapt resource value: 96 + public const int AppCompatTheme_alertDialogCenterButtons = 96; + + // aapt resource value: 94 + public const int AppCompatTheme_alertDialogStyle = 94; + + // aapt resource value: 97 + public const int AppCompatTheme_alertDialogTheme = 97; + + // aapt resource value: 1 + public const int AppCompatTheme_android_windowAnimationStyle = 1; + + // aapt resource value: 0 + public const int AppCompatTheme_android_windowIsFloating = 0; + + // aapt resource value: 102 + public const int AppCompatTheme_autoCompleteTextViewStyle = 102; + + // aapt resource value: 55 + public const int AppCompatTheme_borderlessButtonStyle = 55; + + // aapt resource value: 52 + public const int AppCompatTheme_buttonBarButtonStyle = 52; + + // aapt resource value: 100 + public const int AppCompatTheme_buttonBarNegativeButtonStyle = 100; + + // aapt resource value: 101 + public const int AppCompatTheme_buttonBarNeutralButtonStyle = 101; + + // aapt resource value: 99 + public const int AppCompatTheme_buttonBarPositiveButtonStyle = 99; + + // aapt resource value: 51 + public const int AppCompatTheme_buttonBarStyle = 51; + + // aapt resource value: 103 + public const int AppCompatTheme_buttonStyle = 103; + + // aapt resource value: 104 + public const int AppCompatTheme_buttonStyleSmall = 104; + + // aapt resource value: 105 + public const int AppCompatTheme_checkboxStyle = 105; + + // aapt resource value: 106 + public const int AppCompatTheme_checkedTextViewStyle = 106; + + // aapt resource value: 86 + public const int AppCompatTheme_colorAccent = 86; + + // aapt resource value: 93 + public const int AppCompatTheme_colorBackgroundFloating = 93; + + // aapt resource value: 90 + public const int AppCompatTheme_colorButtonNormal = 90; + + // aapt resource value: 88 + public const int AppCompatTheme_colorControlActivated = 88; + + // aapt resource value: 89 + public const int AppCompatTheme_colorControlHighlight = 89; + + // aapt resource value: 87 + public const int AppCompatTheme_colorControlNormal = 87; + + // aapt resource value: 118 + public const int AppCompatTheme_colorError = 118; + + // aapt resource value: 84 + public const int AppCompatTheme_colorPrimary = 84; + + // aapt resource value: 85 + public const int AppCompatTheme_colorPrimaryDark = 85; + + // aapt resource value: 91 + public const int AppCompatTheme_colorSwitchThumbNormal = 91; + + // aapt resource value: 92 + public const int AppCompatTheme_controlBackground = 92; + + // aapt resource value: 44 + public const int AppCompatTheme_dialogPreferredPadding = 44; + + // aapt resource value: 43 + public const int AppCompatTheme_dialogTheme = 43; + + // aapt resource value: 57 + public const int AppCompatTheme_dividerHorizontal = 57; + + // aapt resource value: 56 + public const int AppCompatTheme_dividerVertical = 56; + + // aapt resource value: 75 + public const int AppCompatTheme_dropDownListViewStyle = 75; + + // aapt resource value: 47 + public const int AppCompatTheme_dropdownListPreferredItemHeight = 47; + + // aapt resource value: 64 + public const int AppCompatTheme_editTextBackground = 64; + + // aapt resource value: 63 + public const int AppCompatTheme_editTextColor = 63; + + // aapt resource value: 107 + public const int AppCompatTheme_editTextStyle = 107; + + // aapt resource value: 49 + public const int AppCompatTheme_homeAsUpIndicator = 49; + + // aapt resource value: 65 + public const int AppCompatTheme_imageButtonStyle = 65; + + // aapt resource value: 83 + public const int AppCompatTheme_listChoiceBackgroundIndicator = 83; + + // aapt resource value: 45 + public const int AppCompatTheme_listDividerAlertDialog = 45; + + // aapt resource value: 115 + public const int AppCompatTheme_listMenuViewStyle = 115; + + // aapt resource value: 76 + public const int AppCompatTheme_listPopupWindowStyle = 76; + + // aapt resource value: 70 + public const int AppCompatTheme_listPreferredItemHeight = 70; + + // aapt resource value: 72 + public const int AppCompatTheme_listPreferredItemHeightLarge = 72; + + // aapt resource value: 71 + public const int AppCompatTheme_listPreferredItemHeightSmall = 71; + + // aapt resource value: 73 + public const int AppCompatTheme_listPreferredItemPaddingLeft = 73; + + // aapt resource value: 74 + public const int AppCompatTheme_listPreferredItemPaddingRight = 74; + + // aapt resource value: 80 + public const int AppCompatTheme_panelBackground = 80; + + // aapt resource value: 82 + public const int AppCompatTheme_panelMenuListTheme = 82; + + // aapt resource value: 81 + public const int AppCompatTheme_panelMenuListWidth = 81; + + // aapt resource value: 61 + public const int AppCompatTheme_popupMenuStyle = 61; + + // aapt resource value: 62 + public const int AppCompatTheme_popupWindowStyle = 62; + + // aapt resource value: 108 + public const int AppCompatTheme_radioButtonStyle = 108; + + // aapt resource value: 109 + public const int AppCompatTheme_ratingBarStyle = 109; + + // aapt resource value: 110 + public const int AppCompatTheme_ratingBarStyleIndicator = 110; + + // aapt resource value: 111 + public const int AppCompatTheme_ratingBarStyleSmall = 111; + + // aapt resource value: 69 + public const int AppCompatTheme_searchViewStyle = 69; + + // aapt resource value: 112 + public const int AppCompatTheme_seekBarStyle = 112; + + // aapt resource value: 53 + public const int AppCompatTheme_selectableItemBackground = 53; + + // aapt resource value: 54 + public const int AppCompatTheme_selectableItemBackgroundBorderless = 54; + + // aapt resource value: 48 + public const int AppCompatTheme_spinnerDropDownItemStyle = 48; + + // aapt resource value: 113 + public const int AppCompatTheme_spinnerStyle = 113; + + // aapt resource value: 114 + public const int AppCompatTheme_switchStyle = 114; + + // aapt resource value: 40 + public const int AppCompatTheme_textAppearanceLargePopupMenu = 40; + + // aapt resource value: 77 + public const int AppCompatTheme_textAppearanceListItem = 77; + + // aapt resource value: 78 + public const int AppCompatTheme_textAppearanceListItemSecondary = 78; + + // aapt resource value: 79 + public const int AppCompatTheme_textAppearanceListItemSmall = 79; + + // aapt resource value: 42 + public const int AppCompatTheme_textAppearancePopupMenuHeader = 42; + + // aapt resource value: 67 + public const int AppCompatTheme_textAppearanceSearchResultSubtitle = 67; + + // aapt resource value: 66 + public const int AppCompatTheme_textAppearanceSearchResultTitle = 66; + + // aapt resource value: 41 + public const int AppCompatTheme_textAppearanceSmallPopupMenu = 41; + + // aapt resource value: 98 + public const int AppCompatTheme_textColorAlertDialogListItem = 98; + + // aapt resource value: 68 + public const int AppCompatTheme_textColorSearchUrl = 68; + + // aapt resource value: 60 + public const int AppCompatTheme_toolbarNavigationButtonStyle = 60; + + // aapt resource value: 59 + public const int AppCompatTheme_toolbarStyle = 59; + + // aapt resource value: 117 + public const int AppCompatTheme_tooltipForegroundColor = 117; + + // aapt resource value: 116 + public const int AppCompatTheme_tooltipFrameBackground = 116; + + // aapt resource value: 2 + public const int AppCompatTheme_windowActionBar = 2; + + // aapt resource value: 4 + public const int AppCompatTheme_windowActionBarOverlay = 4; + + // aapt resource value: 5 + public const int AppCompatTheme_windowActionModeOverlay = 5; + + // aapt resource value: 9 + public const int AppCompatTheme_windowFixedHeightMajor = 9; + + // aapt resource value: 7 + public const int AppCompatTheme_windowFixedHeightMinor = 7; + + // aapt resource value: 6 + public const int AppCompatTheme_windowFixedWidthMajor = 6; + + // aapt resource value: 8 + public const int AppCompatTheme_windowFixedWidthMinor = 8; + + // aapt resource value: 10 + public const int AppCompatTheme_windowMinWidthMajor = 10; + + // aapt resource value: 11 + public const int AppCompatTheme_windowMinWidthMinor = 11; + + // aapt resource value: 3 + public const int AppCompatTheme_windowNoTitle = 3; + + public static int[] BottomNavigationView = new int[] { + 2130772005, + 2130772266, + 2130772267, + 2130772268, + 2130772269}; + + // aapt resource value: 0 + public const int BottomNavigationView_elevation = 0; + + // aapt resource value: 4 + public const int BottomNavigationView_itemBackground = 4; + + // aapt resource value: 2 + public const int BottomNavigationView_itemIconTint = 2; + + // aapt resource value: 3 + public const int BottomNavigationView_itemTextColor = 3; + + // aapt resource value: 1 + public const int BottomNavigationView_menu = 1; + + public static int[] BottomSheetBehavior_Layout = new int[] { + 2130772228, + 2130772229, + 2130772230}; + + // aapt resource value: 1 + public const int BottomSheetBehavior_Layout_behavior_hideable = 1; + + // aapt resource value: 0 + public const int BottomSheetBehavior_Layout_behavior_peekHeight = 0; + + // aapt resource value: 2 + public const int BottomSheetBehavior_Layout_behavior_skipCollapsed = 2; + + public static int[] ButtonBarLayout = new int[] { + 2130772146}; + + // aapt resource value: 0 + public const int ButtonBarLayout_allowStacking = 0; + + public static int[] CollapsingToolbarLayout = new int[] { + 2130771980, + 2130772231, + 2130772232, + 2130772233, + 2130772234, + 2130772235, + 2130772236, + 2130772237, + 2130772238, + 2130772239, + 2130772240, + 2130772241, + 2130772242, + 2130772243, + 2130772244, + 2130772245}; + + // aapt resource value: 13 + public const int CollapsingToolbarLayout_collapsedTitleGravity = 13; + + // aapt resource value: 7 + public const int CollapsingToolbarLayout_collapsedTitleTextAppearance = 7; + + // aapt resource value: 8 + public const int CollapsingToolbarLayout_contentScrim = 8; + + // aapt resource value: 14 + public const int CollapsingToolbarLayout_expandedTitleGravity = 14; + + // aapt resource value: 1 + public const int CollapsingToolbarLayout_expandedTitleMargin = 1; + + // aapt resource value: 5 + public const int CollapsingToolbarLayout_expandedTitleMarginBottom = 5; + + // aapt resource value: 4 + public const int CollapsingToolbarLayout_expandedTitleMarginEnd = 4; + + // aapt resource value: 2 + public const int CollapsingToolbarLayout_expandedTitleMarginStart = 2; + + // aapt resource value: 3 + public const int CollapsingToolbarLayout_expandedTitleMarginTop = 3; + + // aapt resource value: 6 + public const int CollapsingToolbarLayout_expandedTitleTextAppearance = 6; + + // aapt resource value: 12 + public const int CollapsingToolbarLayout_scrimAnimationDuration = 12; + + // aapt resource value: 11 + public const int CollapsingToolbarLayout_scrimVisibleHeightTrigger = 11; + + // aapt resource value: 9 + public const int CollapsingToolbarLayout_statusBarScrim = 9; + + // aapt resource value: 0 + public const int CollapsingToolbarLayout_title = 0; + + // aapt resource value: 15 + public const int CollapsingToolbarLayout_titleEnabled = 15; + + // aapt resource value: 10 + public const int CollapsingToolbarLayout_toolbarId = 10; + + public static int[] CollapsingToolbarLayout_Layout = new int[] { + 2130772246, + 2130772247}; + + // aapt resource value: 0 + public const int CollapsingToolbarLayout_Layout_layout_collapseMode = 0; + + // aapt resource value: 1 + public const int CollapsingToolbarLayout_Layout_layout_collapseParallaxMultiplier = 1; + + public static int[] ColorStateListItem = new int[] { + 16843173, + 16843551, + 2130772147}; + + // aapt resource value: 2 + public const int ColorStateListItem_alpha = 2; + + // aapt resource value: 1 + public const int ColorStateListItem_android_alpha = 1; + + // aapt resource value: 0 + public const int ColorStateListItem_android_color = 0; + + public static int[] CompoundButton = new int[] { + 16843015, + 2130772148, + 2130772149}; + + // aapt resource value: 0 + public const int CompoundButton_android_button = 0; + + // aapt resource value: 1 + public const int CompoundButton_buttonTint = 1; + + // aapt resource value: 2 + public const int CompoundButton_buttonTintMode = 2; + + public static int[] CoordinatorLayout = new int[] { + 2130772248, + 2130772249}; + + // aapt resource value: 0 + public const int CoordinatorLayout_keylines = 0; + + // aapt resource value: 1 + public const int CoordinatorLayout_statusBarBackground = 1; + + public static int[] CoordinatorLayout_Layout = new int[] { + 16842931, + 2130772250, + 2130772251, + 2130772252, + 2130772253, + 2130772254, + 2130772255}; + + // aapt resource value: 0 + public const int CoordinatorLayout_Layout_android_layout_gravity = 0; + + // aapt resource value: 2 + public const int CoordinatorLayout_Layout_layout_anchor = 2; + + // aapt resource value: 4 + public const int CoordinatorLayout_Layout_layout_anchorGravity = 4; + + // aapt resource value: 1 + public const int CoordinatorLayout_Layout_layout_behavior = 1; + + // aapt resource value: 6 + public const int CoordinatorLayout_Layout_layout_dodgeInsetEdges = 6; + + // aapt resource value: 5 + public const int CoordinatorLayout_Layout_layout_insetEdge = 5; + + // aapt resource value: 3 + public const int CoordinatorLayout_Layout_layout_keyline = 3; + + public static int[] DesignTheme = new int[] { + 2130772256, + 2130772257, + 2130772258}; + + // aapt resource value: 0 + public const int DesignTheme_bottomSheetDialogTheme = 0; + + // aapt resource value: 1 + public const int DesignTheme_bottomSheetStyle = 1; + + // aapt resource value: 2 + public const int DesignTheme_textColorError = 2; + + public static int[] DrawerArrowToggle = new int[] { + 2130772150, + 2130772151, + 2130772152, + 2130772153, + 2130772154, + 2130772155, + 2130772156, + 2130772157}; + + // aapt resource value: 4 + public const int DrawerArrowToggle_arrowHeadLength = 4; + + // aapt resource value: 5 + public const int DrawerArrowToggle_arrowShaftLength = 5; + + // aapt resource value: 6 + public const int DrawerArrowToggle_barLength = 6; + + // aapt resource value: 0 + public const int DrawerArrowToggle_color = 0; + + // aapt resource value: 2 + public const int DrawerArrowToggle_drawableSize = 2; + + // aapt resource value: 3 + public const int DrawerArrowToggle_gapBetweenBars = 3; + + // aapt resource value: 1 + public const int DrawerArrowToggle_spinBars = 1; + + // aapt resource value: 7 + public const int DrawerArrowToggle_thickness = 7; + + public static int[] FloatingActionButton = new int[] { + 2130772005, + 2130772221, + 2130772222, + 2130772259, + 2130772260, + 2130772261, + 2130772262, + 2130772263}; + + // aapt resource value: 1 + public const int FloatingActionButton_backgroundTint = 1; + + // aapt resource value: 2 + public const int FloatingActionButton_backgroundTintMode = 2; + + // aapt resource value: 6 + public const int FloatingActionButton_borderWidth = 6; + + // aapt resource value: 0 + public const int FloatingActionButton_elevation = 0; + + // aapt resource value: 4 + public const int FloatingActionButton_fabSize = 4; + + // aapt resource value: 5 + public const int FloatingActionButton_pressedTranslationZ = 5; + + // aapt resource value: 3 + public const int FloatingActionButton_rippleColor = 3; + + // aapt resource value: 7 + public const int FloatingActionButton_useCompatPadding = 7; + + public static int[] FloatingActionButton_Behavior_Layout = new int[] { + 2130772264}; + + // aapt resource value: 0 + public const int FloatingActionButton_Behavior_Layout_behavior_autoHide = 0; + + public static int[] FontFamily = new int[] { + 2130772305, + 2130772306, + 2130772307, + 2130772308, + 2130772309, + 2130772310}; + + // aapt resource value: 0 + public const int FontFamily_fontProviderAuthority = 0; + + // aapt resource value: 3 + public const int FontFamily_fontProviderCerts = 3; + + // aapt resource value: 4 + public const int FontFamily_fontProviderFetchStrategy = 4; + + // aapt resource value: 5 + public const int FontFamily_fontProviderFetchTimeout = 5; + + // aapt resource value: 1 + public const int FontFamily_fontProviderPackage = 1; + + // aapt resource value: 2 + public const int FontFamily_fontProviderQuery = 2; + + public static int[] FontFamilyFont = new int[] { + 16844082, + 16844083, + 16844095, + 2130772311, + 2130772312, + 2130772313}; + + // aapt resource value: 0 + public const int FontFamilyFont_android_font = 0; + + // aapt resource value: 2 + public const int FontFamilyFont_android_fontStyle = 2; + + // aapt resource value: 1 + public const int FontFamilyFont_android_fontWeight = 1; + + // aapt resource value: 4 + public const int FontFamilyFont_font = 4; + + // aapt resource value: 3 + public const int FontFamilyFont_fontStyle = 3; + + // aapt resource value: 5 + public const int FontFamilyFont_fontWeight = 5; + + public static int[] ForegroundLinearLayout = new int[] { + 16843017, + 16843264, + 2130772265}; + + // aapt resource value: 0 + public const int ForegroundLinearLayout_android_foreground = 0; + + // aapt resource value: 1 + public const int ForegroundLinearLayout_android_foregroundGravity = 1; + + // aapt resource value: 2 + public const int ForegroundLinearLayout_foregroundInsidePadding = 2; + + public static int[] LinearLayoutCompat = new int[] { + 16842927, + 16842948, + 16843046, + 16843047, + 16843048, + 2130771988, + 2130772158, + 2130772159, + 2130772160}; + + // aapt resource value: 2 + public const int LinearLayoutCompat_android_baselineAligned = 2; + + // aapt resource value: 3 + public const int LinearLayoutCompat_android_baselineAlignedChildIndex = 3; + + // aapt resource value: 0 + public const int LinearLayoutCompat_android_gravity = 0; + + // aapt resource value: 1 + public const int LinearLayoutCompat_android_orientation = 1; + + // aapt resource value: 4 + public const int LinearLayoutCompat_android_weightSum = 4; + + // aapt resource value: 5 + public const int LinearLayoutCompat_divider = 5; + + // aapt resource value: 8 + public const int LinearLayoutCompat_dividerPadding = 8; + + // aapt resource value: 6 + public const int LinearLayoutCompat_measureWithLargestChild = 6; + + // aapt resource value: 7 + public const int LinearLayoutCompat_showDividers = 7; + + public static int[] LinearLayoutCompat_Layout = new int[] { + 16842931, + 16842996, + 16842997, + 16843137}; + + // aapt resource value: 0 + public const int LinearLayoutCompat_Layout_android_layout_gravity = 0; + + // aapt resource value: 2 + public const int LinearLayoutCompat_Layout_android_layout_height = 2; + + // aapt resource value: 3 + public const int LinearLayoutCompat_Layout_android_layout_weight = 3; + + // aapt resource value: 1 + public const int LinearLayoutCompat_Layout_android_layout_width = 1; + + public static int[] ListPopupWindow = new int[] { + 16843436, + 16843437}; + + // aapt resource value: 0 + public const int ListPopupWindow_android_dropDownHorizontalOffset = 0; + + // aapt resource value: 1 + public const int ListPopupWindow_android_dropDownVerticalOffset = 1; + + public static int[] MenuGroup = new int[] { + 16842766, + 16842960, + 16843156, + 16843230, + 16843231, + 16843232}; + + // aapt resource value: 5 + public const int MenuGroup_android_checkableBehavior = 5; + + // aapt resource value: 0 + public const int MenuGroup_android_enabled = 0; + + // aapt resource value: 1 + public const int MenuGroup_android_id = 1; + + // aapt resource value: 3 + public const int MenuGroup_android_menuCategory = 3; + + // aapt resource value: 4 + public const int MenuGroup_android_orderInCategory = 4; + + // aapt resource value: 2 + public const int MenuGroup_android_visible = 2; + + public static int[] MenuItem = new int[] { + 16842754, + 16842766, + 16842960, + 16843014, + 16843156, + 16843230, + 16843231, + 16843233, + 16843234, + 16843235, + 16843236, + 16843237, + 16843375, + 2130772161, + 2130772162, + 2130772163, + 2130772164, + 2130772165, + 2130772166, + 2130772167, + 2130772168, + 2130772169, + 2130772170}; + + // aapt resource value: 16 + public const int MenuItem_actionLayout = 16; + + // aapt resource value: 18 + public const int MenuItem_actionProviderClass = 18; + + // aapt resource value: 17 + public const int MenuItem_actionViewClass = 17; + + // aapt resource value: 13 + public const int MenuItem_alphabeticModifiers = 13; + + // aapt resource value: 9 + public const int MenuItem_android_alphabeticShortcut = 9; + + // aapt resource value: 11 + public const int MenuItem_android_checkable = 11; + + // aapt resource value: 3 + public const int MenuItem_android_checked = 3; + + // aapt resource value: 1 + public const int MenuItem_android_enabled = 1; + + // aapt resource value: 0 + public const int MenuItem_android_icon = 0; + + // aapt resource value: 2 + public const int MenuItem_android_id = 2; + + // aapt resource value: 5 + public const int MenuItem_android_menuCategory = 5; + + // aapt resource value: 10 + public const int MenuItem_android_numericShortcut = 10; + + // aapt resource value: 12 + public const int MenuItem_android_onClick = 12; + + // aapt resource value: 6 + public const int MenuItem_android_orderInCategory = 6; + + // aapt resource value: 7 + public const int MenuItem_android_title = 7; + + // aapt resource value: 8 + public const int MenuItem_android_titleCondensed = 8; + + // aapt resource value: 4 + public const int MenuItem_android_visible = 4; + + // aapt resource value: 19 + public const int MenuItem_contentDescription = 19; + + // aapt resource value: 21 + public const int MenuItem_iconTint = 21; + + // aapt resource value: 22 + public const int MenuItem_iconTintMode = 22; + + // aapt resource value: 14 + public const int MenuItem_numericModifiers = 14; + + // aapt resource value: 15 + public const int MenuItem_showAsAction = 15; + + // aapt resource value: 20 + public const int MenuItem_tooltipText = 20; + + public static int[] MenuView = new int[] { + 16842926, + 16843052, + 16843053, + 16843054, + 16843055, + 16843056, + 16843057, + 2130772171, + 2130772172}; + + // aapt resource value: 4 + public const int MenuView_android_headerBackground = 4; + + // aapt resource value: 2 + public const int MenuView_android_horizontalDivider = 2; + + // aapt resource value: 5 + public const int MenuView_android_itemBackground = 5; + + // aapt resource value: 6 + public const int MenuView_android_itemIconDisabledAlpha = 6; + + // aapt resource value: 1 + public const int MenuView_android_itemTextAppearance = 1; + + // aapt resource value: 3 + public const int MenuView_android_verticalDivider = 3; + + // aapt resource value: 0 + public const int MenuView_android_windowAnimationStyle = 0; + + // aapt resource value: 7 + public const int MenuView_preserveIconSpacing = 7; + + // aapt resource value: 8 + public const int MenuView_subMenuArrow = 8; + + public static int[] NavigationView = new int[] { + 16842964, + 16842973, + 16843039, + 2130772005, + 2130772266, + 2130772267, + 2130772268, + 2130772269, + 2130772270, + 2130772271}; + + // aapt resource value: 0 + public const int NavigationView_android_background = 0; + + // aapt resource value: 1 + public const int NavigationView_android_fitsSystemWindows = 1; + + // aapt resource value: 2 + public const int NavigationView_android_maxWidth = 2; + + // aapt resource value: 3 + public const int NavigationView_elevation = 3; + + // aapt resource value: 9 + public const int NavigationView_headerLayout = 9; + + // aapt resource value: 7 + public const int NavigationView_itemBackground = 7; + + // aapt resource value: 5 + public const int NavigationView_itemIconTint = 5; + + // aapt resource value: 8 + public const int NavigationView_itemTextAppearance = 8; + + // aapt resource value: 6 + public const int NavigationView_itemTextColor = 6; + + // aapt resource value: 4 + public const int NavigationView_menu = 4; + + public static int[] PopupWindow = new int[] { + 16843126, + 16843465, + 2130772173}; + + // aapt resource value: 1 + public const int PopupWindow_android_popupAnimationStyle = 1; + + // aapt resource value: 0 + public const int PopupWindow_android_popupBackground = 0; + + // aapt resource value: 2 + public const int PopupWindow_overlapAnchor = 2; + + public static int[] PopupWindowBackgroundState = new int[] { + 2130772174}; + + // aapt resource value: 0 + public const int PopupWindowBackgroundState_state_above_anchor = 0; + + public static int[] RecycleListView = new int[] { + 2130772175, + 2130772176}; + + // aapt resource value: 0 + public const int RecycleListView_paddingBottomNoButtons = 0; + + // aapt resource value: 1 + public const int RecycleListView_paddingTopNoTitle = 1; + + public static int[] RecyclerView = new int[] { + 16842948, + 16842993, + 2130771968, + 2130771969, + 2130771970, + 2130771971, + 2130771972, + 2130771973, + 2130771974, + 2130771975, + 2130771976}; + + // aapt resource value: 1 + public const int RecyclerView_android_descendantFocusability = 1; + + // aapt resource value: 0 + public const int RecyclerView_android_orientation = 0; + + // aapt resource value: 6 + public const int RecyclerView_fastScrollEnabled = 6; + + // aapt resource value: 9 + public const int RecyclerView_fastScrollHorizontalThumbDrawable = 9; + + // aapt resource value: 10 + public const int RecyclerView_fastScrollHorizontalTrackDrawable = 10; + + // aapt resource value: 7 + public const int RecyclerView_fastScrollVerticalThumbDrawable = 7; + + // aapt resource value: 8 + public const int RecyclerView_fastScrollVerticalTrackDrawable = 8; + + // aapt resource value: 2 + public const int RecyclerView_layoutManager = 2; + + // aapt resource value: 4 + public const int RecyclerView_reverseLayout = 4; + + // aapt resource value: 3 + public const int RecyclerView_spanCount = 3; + + // aapt resource value: 5 + public const int RecyclerView_stackFromEnd = 5; + + public static int[] ScrimInsetsFrameLayout = new int[] { + 2130772272}; + + // aapt resource value: 0 + public const int ScrimInsetsFrameLayout_insetForeground = 0; + + public static int[] ScrollingViewBehavior_Layout = new int[] { + 2130772273}; + + // aapt resource value: 0 + public const int ScrollingViewBehavior_Layout_behavior_overlapTop = 0; + + public static int[] SearchView = new int[] { + 16842970, + 16843039, + 16843296, + 16843364, + 2130772177, + 2130772178, + 2130772179, + 2130772180, + 2130772181, + 2130772182, + 2130772183, + 2130772184, + 2130772185, + 2130772186, + 2130772187, + 2130772188, + 2130772189}; + + // aapt resource value: 0 + public const int SearchView_android_focusable = 0; + + // aapt resource value: 3 + public const int SearchView_android_imeOptions = 3; + + // aapt resource value: 2 + public const int SearchView_android_inputType = 2; + + // aapt resource value: 1 + public const int SearchView_android_maxWidth = 1; + + // aapt resource value: 8 + public const int SearchView_closeIcon = 8; + + // aapt resource value: 13 + public const int SearchView_commitIcon = 13; + + // aapt resource value: 7 + public const int SearchView_defaultQueryHint = 7; + + // aapt resource value: 9 + public const int SearchView_goIcon = 9; + + // aapt resource value: 5 + public const int SearchView_iconifiedByDefault = 5; + + // aapt resource value: 4 + public const int SearchView_layout = 4; + + // aapt resource value: 15 + public const int SearchView_queryBackground = 15; + + // aapt resource value: 6 + public const int SearchView_queryHint = 6; + + // aapt resource value: 11 + public const int SearchView_searchHintIcon = 11; + + // aapt resource value: 10 + public const int SearchView_searchIcon = 10; + + // aapt resource value: 16 + public const int SearchView_submitBackground = 16; + + // aapt resource value: 14 + public const int SearchView_suggestionRowLayout = 14; + + // aapt resource value: 12 + public const int SearchView_voiceIcon = 12; + + public static int[] SnackbarLayout = new int[] { + 16843039, + 2130772005, + 2130772274}; + + // aapt resource value: 0 + public const int SnackbarLayout_android_maxWidth = 0; + + // aapt resource value: 1 + public const int SnackbarLayout_elevation = 1; + + // aapt resource value: 2 + public const int SnackbarLayout_maxActionInlineWidth = 2; + + public static int[] Spinner = new int[] { + 16842930, + 16843126, + 16843131, + 16843362, + 2130772006}; + + // aapt resource value: 3 + public const int Spinner_android_dropDownWidth = 3; + + // aapt resource value: 0 + public const int Spinner_android_entries = 0; + + // aapt resource value: 1 + public const int Spinner_android_popupBackground = 1; + + // aapt resource value: 2 + public const int Spinner_android_prompt = 2; + + // aapt resource value: 4 + public const int Spinner_popupTheme = 4; + + public static int[] SwitchCompat = new int[] { + 16843044, + 16843045, + 16843074, + 2130772190, + 2130772191, + 2130772192, + 2130772193, + 2130772194, + 2130772195, + 2130772196, + 2130772197, + 2130772198, + 2130772199, + 2130772200}; + + // aapt resource value: 1 + public const int SwitchCompat_android_textOff = 1; + + // aapt resource value: 0 + public const int SwitchCompat_android_textOn = 0; + + // aapt resource value: 2 + public const int SwitchCompat_android_thumb = 2; + + // aapt resource value: 13 + public const int SwitchCompat_showText = 13; + + // aapt resource value: 12 + public const int SwitchCompat_splitTrack = 12; + + // aapt resource value: 10 + public const int SwitchCompat_switchMinWidth = 10; + + // aapt resource value: 11 + public const int SwitchCompat_switchPadding = 11; + + // aapt resource value: 9 + public const int SwitchCompat_switchTextAppearance = 9; + + // aapt resource value: 8 + public const int SwitchCompat_thumbTextPadding = 8; + + // aapt resource value: 3 + public const int SwitchCompat_thumbTint = 3; + + // aapt resource value: 4 + public const int SwitchCompat_thumbTintMode = 4; + + // aapt resource value: 5 + public const int SwitchCompat_track = 5; + + // aapt resource value: 6 + public const int SwitchCompat_trackTint = 6; + + // aapt resource value: 7 + public const int SwitchCompat_trackTintMode = 7; + + public static int[] TabItem = new int[] { + 16842754, + 16842994, + 16843087}; + + // aapt resource value: 0 + public const int TabItem_android_icon = 0; + + // aapt resource value: 1 + public const int TabItem_android_layout = 1; + + // aapt resource value: 2 + public const int TabItem_android_text = 2; + + public static int[] TabLayout = new int[] { + 2130772275, + 2130772276, + 2130772277, + 2130772278, + 2130772279, + 2130772280, + 2130772281, + 2130772282, + 2130772283, + 2130772284, + 2130772285, + 2130772286, + 2130772287, + 2130772288, + 2130772289, + 2130772290}; + + // aapt resource value: 3 + public const int TabLayout_tabBackground = 3; + + // aapt resource value: 2 + public const int TabLayout_tabContentStart = 2; + + // aapt resource value: 5 + public const int TabLayout_tabGravity = 5; + + // aapt resource value: 0 + public const int TabLayout_tabIndicatorColor = 0; + + // aapt resource value: 1 + public const int TabLayout_tabIndicatorHeight = 1; + + // aapt resource value: 7 + public const int TabLayout_tabMaxWidth = 7; + + // aapt resource value: 6 + public const int TabLayout_tabMinWidth = 6; + + // aapt resource value: 4 + public const int TabLayout_tabMode = 4; + + // aapt resource value: 15 + public const int TabLayout_tabPadding = 15; + + // aapt resource value: 14 + public const int TabLayout_tabPaddingBottom = 14; + + // aapt resource value: 13 + public const int TabLayout_tabPaddingEnd = 13; + + // aapt resource value: 11 + public const int TabLayout_tabPaddingStart = 11; + + // aapt resource value: 12 + public const int TabLayout_tabPaddingTop = 12; + + // aapt resource value: 10 + public const int TabLayout_tabSelectedTextColor = 10; + + // aapt resource value: 8 + public const int TabLayout_tabTextAppearance = 8; + + // aapt resource value: 9 + public const int TabLayout_tabTextColor = 9; + + public static int[] TextAppearance = new int[] { + 16842901, + 16842902, + 16842903, + 16842904, + 16842906, + 16842907, + 16843105, + 16843106, + 16843107, + 16843108, + 16843692, + 2130772022, + 2130772028}; + + // aapt resource value: 10 + public const int TextAppearance_android_fontFamily = 10; + + // aapt resource value: 6 + public const int TextAppearance_android_shadowColor = 6; + + // aapt resource value: 7 + public const int TextAppearance_android_shadowDx = 7; + + // aapt resource value: 8 + public const int TextAppearance_android_shadowDy = 8; + + // aapt resource value: 9 + public const int TextAppearance_android_shadowRadius = 9; + + // aapt resource value: 3 + public const int TextAppearance_android_textColor = 3; + + // aapt resource value: 4 + public const int TextAppearance_android_textColorHint = 4; + + // aapt resource value: 5 + public const int TextAppearance_android_textColorLink = 5; + + // aapt resource value: 0 + public const int TextAppearance_android_textSize = 0; + + // aapt resource value: 2 + public const int TextAppearance_android_textStyle = 2; + + // aapt resource value: 1 + public const int TextAppearance_android_typeface = 1; + + // aapt resource value: 12 + public const int TextAppearance_fontFamily = 12; + + // aapt resource value: 11 + public const int TextAppearance_textAllCaps = 11; + + public static int[] TextInputLayout = new int[] { + 16842906, + 16843088, + 2130772291, + 2130772292, + 2130772293, + 2130772294, + 2130772295, + 2130772296, + 2130772297, + 2130772298, + 2130772299, + 2130772300, + 2130772301, + 2130772302, + 2130772303, + 2130772304}; + + // aapt resource value: 1 + public const int TextInputLayout_android_hint = 1; + + // aapt resource value: 0 + public const int TextInputLayout_android_textColorHint = 0; + + // aapt resource value: 6 + public const int TextInputLayout_counterEnabled = 6; + + // aapt resource value: 7 + public const int TextInputLayout_counterMaxLength = 7; + + // aapt resource value: 9 + public const int TextInputLayout_counterOverflowTextAppearance = 9; + + // aapt resource value: 8 + public const int TextInputLayout_counterTextAppearance = 8; + + // aapt resource value: 4 + public const int TextInputLayout_errorEnabled = 4; + + // aapt resource value: 5 + public const int TextInputLayout_errorTextAppearance = 5; + + // aapt resource value: 10 + public const int TextInputLayout_hintAnimationEnabled = 10; + + // aapt resource value: 3 + public const int TextInputLayout_hintEnabled = 3; + + // aapt resource value: 2 + public const int TextInputLayout_hintTextAppearance = 2; + + // aapt resource value: 13 + public const int TextInputLayout_passwordToggleContentDescription = 13; + + // aapt resource value: 12 + public const int TextInputLayout_passwordToggleDrawable = 12; + + // aapt resource value: 11 + public const int TextInputLayout_passwordToggleEnabled = 11; + + // aapt resource value: 14 + public const int TextInputLayout_passwordToggleTint = 14; + + // aapt resource value: 15 + public const int TextInputLayout_passwordToggleTintMode = 15; + + public static int[] Toolbar = new int[] { + 16842927, + 16843072, + 2130771980, + 2130771983, + 2130771987, + 2130771999, + 2130772000, + 2130772001, + 2130772002, + 2130772003, + 2130772004, + 2130772006, + 2130772201, + 2130772202, + 2130772203, + 2130772204, + 2130772205, + 2130772206, + 2130772207, + 2130772208, + 2130772209, + 2130772210, + 2130772211, + 2130772212, + 2130772213, + 2130772214, + 2130772215, + 2130772216, + 2130772217}; + + // aapt resource value: 0 + public const int Toolbar_android_gravity = 0; + + // aapt resource value: 1 + public const int Toolbar_android_minHeight = 1; + + // aapt resource value: 21 + public const int Toolbar_buttonGravity = 21; + + // aapt resource value: 23 + public const int Toolbar_collapseContentDescription = 23; + + // aapt resource value: 22 + public const int Toolbar_collapseIcon = 22; + + // aapt resource value: 6 + public const int Toolbar_contentInsetEnd = 6; + + // aapt resource value: 10 + public const int Toolbar_contentInsetEndWithActions = 10; + + // aapt resource value: 7 + public const int Toolbar_contentInsetLeft = 7; + + // aapt resource value: 8 + public const int Toolbar_contentInsetRight = 8; + + // aapt resource value: 5 + public const int Toolbar_contentInsetStart = 5; + + // aapt resource value: 9 + public const int Toolbar_contentInsetStartWithNavigation = 9; + + // aapt resource value: 4 + public const int Toolbar_logo = 4; + + // aapt resource value: 26 + public const int Toolbar_logoDescription = 26; + + // aapt resource value: 20 + public const int Toolbar_maxButtonHeight = 20; + + // aapt resource value: 25 + public const int Toolbar_navigationContentDescription = 25; + + // aapt resource value: 24 + public const int Toolbar_navigationIcon = 24; + + // aapt resource value: 11 + public const int Toolbar_popupTheme = 11; + + // aapt resource value: 3 + public const int Toolbar_subtitle = 3; + + // aapt resource value: 13 + public const int Toolbar_subtitleTextAppearance = 13; + + // aapt resource value: 28 + public const int Toolbar_subtitleTextColor = 28; + + // aapt resource value: 2 + public const int Toolbar_title = 2; + + // aapt resource value: 14 + public const int Toolbar_titleMargin = 14; + + // aapt resource value: 18 + public const int Toolbar_titleMarginBottom = 18; + + // aapt resource value: 16 + public const int Toolbar_titleMarginEnd = 16; + + // aapt resource value: 15 + public const int Toolbar_titleMarginStart = 15; + + // aapt resource value: 17 + public const int Toolbar_titleMarginTop = 17; + + // aapt resource value: 19 + public const int Toolbar_titleMargins = 19; + + // aapt resource value: 12 + public const int Toolbar_titleTextAppearance = 12; + + // aapt resource value: 27 + public const int Toolbar_titleTextColor = 27; + + public static int[] View = new int[] { + 16842752, + 16842970, + 2130772218, + 2130772219, + 2130772220}; + + // aapt resource value: 1 + public const int View_android_focusable = 1; + + // aapt resource value: 0 + public const int View_android_theme = 0; + + // aapt resource value: 3 + public const int View_paddingEnd = 3; + + // aapt resource value: 2 + public const int View_paddingStart = 2; + + // aapt resource value: 4 + public const int View_theme = 4; + + public static int[] ViewBackgroundHelper = new int[] { + 16842964, + 2130772221, + 2130772222}; + + // aapt resource value: 0 + public const int ViewBackgroundHelper_android_background = 0; + + // aapt resource value: 1 + public const int ViewBackgroundHelper_backgroundTint = 1; + + // aapt resource value: 2 + public const int ViewBackgroundHelper_backgroundTintMode = 2; + + public static int[] ViewStubCompat = new int[] { + 16842960, + 16842994, + 16842995}; + + // aapt resource value: 0 + public const int ViewStubCompat_android_id = 0; + + // aapt resource value: 2 + public const int ViewStubCompat_android_inflatedId = 2; + + // aapt resource value: 1 + public const int ViewStubCompat_android_layout = 1; + + static Styleable() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Styleable() + { + } + } + } +} +#pragma warning restore 1591 diff --git a/osu.Android/Resources/layout/activity_main.axml b/osu.Android/Resources/layout/activity_main.axml new file mode 100644 index 0000000000..fc0060857f --- /dev/null +++ b/osu.Android/Resources/layout/activity_main.axml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml b/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..036d09bc5f --- /dev/null +++ b/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml b/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000..036d09bc5f --- /dev/null +++ b/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/osu.Android/Resources/mipmap-hdpi/ic_launcher.png b/osu.Android/Resources/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..2af076dabd0afe60561f41903cc42931db8d33cd GIT binary patch literal 8828 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 N&GnQ!lI8SQ#{X#Iv!eh2 literal 0 HcmV?d00001 diff --git a/osu.Android/Resources/mipmap-hdpi/ic_launcher_round.png b/osu.Android/Resources/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..2af076dabd0afe60561f41903cc42931db8d33cd GIT binary patch literal 8828 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 LNkvXXu0mjfp$&R0 literal 0 HcmV?d00001 diff --git a/osu.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png b/osu.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..a12b157f00e3022cfe81a1e767525c33f4ed2cf1 GIT binary patch literal 958 zcmeAS@N?(olHy`uVBq!ia0vp^IUvlz1|<8_!p|}=Ft>WTIEGZ*dOIh!!#7am`0)iN z!-Y*JzdqP8rN&2Y&y2(+EtA?m9-5+}#BXAw@$*D;zxcf=lRhYP2`ZYNoGdU|=;=Y1 z!-o@UOzpBVHoTpyopyF#@i)8YcdVaV?2ljDUj6>w?`yyA*Pf5cUSE9b6wq26;8J@~ z){!@7GpTmNE>2kO_POn1zf8`~}P?%{85(;s&nc+C&;t$4D5$+f9? z-8>e~Z&%(_OwrVd==PGc4mhTFjVafjdCqsM|EvEe$2)U;a9s0IGofbtHcpKz;cJR= z`DNzVI-iMtrg<$r*EFejE8l0oMM3e)a|=o;x>Mhk@*n)xx%2Rrt=4TnivwP5zpS-& z@5h3w<{9>vH!6KP74q!po!oh)$BI~jUu}4P|5ofvi@(2i9NyELbZ$qD}PI&+JJ3+^f2=YEuP zjpepXu;`->)%n@lB|b@Iv$k0qhJJp%S?O9t?)zjLwwY?z@=v^12)=lt^ZcwNoye^x z_uu*-x}ntY`mc3S`yMaaHuurqE~e`{G_IsMZdhw*{kDDS9h3WSQa;8d3vwO)d?WE+ z%*LAIs=2#$t=BZmPTP}xMpj0I9ti9_c{r`p zu+;ELV)~|tmk}}-GjAWQO5U<}Lr?bB5UX>pYf5~UOnY%ZTQR${nq6YQOHc15>q%#$ zl8$8k_1fsCw;<~OiJ-OiE?f7RJWt%N`#e!y=2`BhIqju|a?kW5QupmV#wx6HrSs?J z&nJroVy6i|*Og1U`{c;a^^dPvTfNJjdCg1nUS<*OC dK7&Kx57tYsZ49$p7vBM?@pScbS?83{1OVHE%8UR2 literal 0 HcmV?d00001 diff --git a/osu.Android/Resources/mipmap-mdpi/ic_launcher_round.png b/osu.Android/Resources/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..c51990875e404a36964d727d376637c6e6c81305 GIT binary patch literal 5045 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 LNkvXXu0mjfp$&R0 literal 0 HcmV?d00001 diff --git a/osu.Android/Resources/mipmap-xhdpi/ic_launcher.png b/osu.Android/Resources/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4e8d9b598cd21e01bbbca29aa077d99a4dabefe3 GIT binary patch literal 12931 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 b;}yxl--Ss53i@>Q4YQuNcebmsMJN0NT!aL! literal 0 HcmV?d00001 diff --git a/osu.Android/Resources/mipmap-xhdpi/ic_launcher_round.png b/osu.Android/Resources/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..4e8d9b598cd21e01bbbca29aa077d99a4dabefe3 GIT binary patch literal 12931 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 literal 0 HcmV?d00001 diff --git a/osu.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png b/osu.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..2d3354eaf2778d4ae52cf3e1368f131be63ccb62 GIT binary patch literal 21256 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 z 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 z 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 F@PB1o0BisN literal 0 HcmV?d00001 diff --git a/osu.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png b/osu.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..d8b832e7f7a81d3d69ad5c0b3ea88dfd739e36c0 GIT binary patch literal 30047 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 z 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 + osu.Android + Settings + diff --git a/osu.Android/Resources/values/colors.xml b/osu.Android/Resources/values/colors.xml new file mode 100644 index 0000000000..17bb9a9dd1 --- /dev/null +++ b/osu.Android/Resources/values/colors.xml @@ -0,0 +1,6 @@ + + + #2c3e50 + #1B3147 + #3498db + diff --git a/osu.Android/Resources/values/ic_launcher_background.xml b/osu.Android/Resources/values/ic_launcher_background.xml new file mode 100644 index 0000000000..6ec24e6413 --- /dev/null +++ b/osu.Android/Resources/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #2C3E50 + \ No newline at end of file diff --git a/osu.Android/Resources/values/styles.xml b/osu.Android/Resources/values/styles.xml new file mode 100644 index 0000000000..5885930df6 --- /dev/null +++ b/osu.Android/Resources/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/osu.Android/bass.dll b/osu.Android/bass.dll new file mode 100644 index 0000000000000000000000000000000000000000..3cd403c3acbd9e89ad071c5069392ea208baaa38 GIT binary patch literal 210944 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 S(A~_h>u%w{`TPGJ1^#c6n$Mj8 literal 0 HcmV?d00001 diff --git a/osu.Android/lib/arm64-v8a/libbass.so b/osu.Android/lib/arm64-v8a/libbass.so new file mode 100644 index 0000000000000000000000000000000000000000..d5c24b3e4b2918a33153cf6a0561366996be16f9 GIT binary patch literal 308904 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

^m>m@_QI`Gyr3D`HGSXQzB#7TNkp#fsh! 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%0A0ic&Bx^ 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 T?MJo0F6IAiepbA(NM`E) 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 z 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 EAM_!vo&W#< literal 0 HcmV?d00001 diff --git a/osu.Android/lib/armeabi-v7a/libbass_fx.so b/osu.Android/lib/armeabi-v7a/libbass_fx.so new file mode 100644 index 0000000000000000000000000000000000000000..006e2feb30bb005eec727f829cc3965fb136f16c GIT binary patch literal 92176 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 V>7s+MhuN$Sd><=0>Ypgz{{j9{WcmOA literal 0 HcmV?d00001 diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj new file mode 100644 index 0000000000..494ff6044b --- /dev/null +++ b/osu.Android/osu.Android.csproj @@ -0,0 +1,175 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {D1D5F9A8-B40B-40E6-B02F-482D03346D3D} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {122416d6-6b49-4ee2-a1e8-b825f31c79fe} + Library + Properties + osu.Android + osu.Android + 512 + True + Resources\Resource.designer.cs + Resource + Off + false + v8.1 + Properties\AndroidManifest.xml + Resources + Assets + Xamarin.Android.Net.AndroidClientHandler + + + True + portable + False + bin\Debug\ + DEBUG;TRACE + prompt + 4 + True + None + False + false + false + false + CJK;Mideast;Rare;West;Other + + + + True + pdbonly + True + bin\Release\ + TRACE + prompt + 4 + true + False + SdkOnly + True + + + + + + + + + + + + + + + + osu.licenseheader + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + 0.22.0 + + + 1.1.0 + + + 1.0.0-beta0006 + + + 1.0.0-beta0005 + + + + + + {0fd409e8-a359-42bd-bc52-6a4745a122e5} + osu.Framework.Android + + + {0a8ba083-9d08-45ad-8653-796db1653388} + osu.Framework + + + {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} + osu.Game.Resources + + + {58f6c80c-1253-4a0e-a465-b8c85ebeadf3} + osu.Game.Rulesets.Catch + + + {48f4582b-7687-4621-9cbe-5c24197cb536} + osu.Game.Rulesets.Mania + + + {c92a607b-1fdd-4954-9f92-03ff547d9080} + osu.Game.Rulesets.Osu + + + {f167e17a-7de6-4af5-b920-a5112296c695} + osu.Game.Rulesets.Taiko + + + {2a66dd92-adb1-4994-89e2-c94e04acda0d} + osu.Game + + + + + + + + \ No newline at end of file From f083b186638c2859eded8173903cfa30424536a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Dec 2018 17:59:42 +0900 Subject: [PATCH 0313/2854] Update in line with framework changes --- osu.Game/Online/Chat/DrawableLinkCompiler.cs | 11 +++++------ osu.Game/OsuGame.cs | 4 ++-- osu.Game/Overlays/Music/PlaylistItem.cs | 5 ++--- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/osu.Game/Online/Chat/DrawableLinkCompiler.cs b/osu.Game/Online/Chat/DrawableLinkCompiler.cs index de017baf35..2b0a49cb6c 100644 --- a/osu.Game/Online/Chat/DrawableLinkCompiler.cs +++ b/osu.Game/Online/Chat/DrawableLinkCompiler.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -15,20 +14,20 @@ using osuTK; namespace osu.Game.Online.Chat { ///

- /// An invisible drawable that brings multiple pieces together to form a consumable clickable link. + /// An invisible drawable that brings multiple pieces together to form a consumable clickable link. /// public class DrawableLinkCompiler : OsuHoverContainer, IHasTooltip { /// /// Each word part of a chat link (split for word-wrap support). /// - public List Parts; + public List Parts; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parts.Any(d => d.ReceivePositionalInputAt(screenSpacePos)); protected override HoverClickSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new LinkHoverSounds(sampleSet, Parts); - public DrawableLinkCompiler(IEnumerable parts) + public DrawableLinkCompiler(IEnumerable parts) { Parts = parts.ToList(); } @@ -45,9 +44,9 @@ namespace osu.Game.Online.Chat private class LinkHoverSounds : HoverClickSounds { - private readonly List parts; + private readonly List parts; - public LinkHoverSounds(HoverSampleSet sampleSet, List parts) + public LinkHoverSounds(HoverSampleSet sampleSet, List parts) : base(sampleSet) { this.parts = parts; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index cd40d4793a..73ecbafb9e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -553,9 +553,9 @@ namespace osu.Game try { - Logger.Log($"Loading {d}...", LoggingTarget.Debug); + Logger.Log($"Loading {d}...", level: LogLevel.Debug); await LoadComponentAsync(d, add); - Logger.Log($"Loaded {d}!", LoggingTarget.Debug); + Logger.Log($"Loaded {d}!", level: LogLevel.Debug); } catch (OperationCanceledException) { diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index 5d89e53081..40a395535d 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -7,7 +7,6 @@ using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Beatmaps; @@ -26,7 +25,7 @@ namespace osu.Game.Overlays.Music private SpriteIcon handle; private TextFlowContainer text; - private IEnumerable titleSprites; + private IEnumerable titleSprites; private ILocalisedBindableString titleBind; private ILocalisedBindableString artistBind; @@ -58,7 +57,7 @@ namespace osu.Game.Overlays.Music selected = value; FinishTransforms(true); - foreach (SpriteText s in titleSprites) + foreach (Drawable s in titleSprites) s.FadeColour(Selected ? hoverColour : Color4.White, fade_duration); } } From 8907ce3f6300d558ad5490eadf7ee53e0e0df719 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Dec 2018 18:11:04 +0900 Subject: [PATCH 0314/2854] Automatically fix invalid pairing dates on load (based on contained groupings) --- osu.Game.Tournament/TournamentGameBase.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 4938533a9e..5c9bee560e 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -109,7 +109,12 @@ namespace osu.Game.Tournament foreach (var id in group.Pairings) { var found = Ladder.Pairings.FirstOrDefault(p => p.ID == id); - if (found != null) found.Grouping.Value = group; + if (found != null) + { + found.Grouping.Value = group; + if (group.StartDate.Value > found.Date.Value) + found.Date.Value = group.StartDate.Value; + } } Ladder.CurrentMatch.Value = Ladder.Pairings.FirstOrDefault(p => p.Current.Value); From 5d7043156be563cd434e94913f383e2269fa17b2 Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Fri, 14 Dec 2018 09:07:17 -0600 Subject: [PATCH 0315/2854] Attempt to get the icon right. --- osu.Android/Properties/AndroidManifest.xml | 3 +-- osu.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml | 2 +- osu.Android/Resources/values/Strings.xml | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml index 12d1f326b9..4ae7d35c2e 100644 --- a/osu.Android/Properties/AndroidManifest.xml +++ b/osu.Android/Properties/AndroidManifest.xml @@ -4,6 +4,5 @@ - - + \ No newline at end of file diff --git a/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml b/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml index 036d09bc5f..9412204815 100644 --- a/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml +++ b/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/osu.Android/Resources/values/Strings.xml b/osu.Android/Resources/values/Strings.xml index fa7ba67465..21f749f876 100644 --- a/osu.Android/Resources/values/Strings.xml +++ b/osu.Android/Resources/values/Strings.xml @@ -1,4 +1,4 @@ - osu.Android + osu!lazer Settings From 214908aaa024c816032de5df11ed8ff35cbd6407 Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Fri, 14 Dec 2018 09:27:02 -0600 Subject: [PATCH 0316/2854] Add opsu migration, fix font not rendering --- osu.Android/GameView.cs | 3 +-- osu.Android/MainActivity.cs | 2 ++ osu.Android/OsuGameAndroid.cs | 44 ++++++++++++++++++++++++++++++++++ osu.Android/osu.Android.csproj | 5 ++-- 4 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 osu.Android/OsuGameAndroid.cs diff --git a/osu.Android/GameView.cs b/osu.Android/GameView.cs index 649ee0134b..dfae65a2e9 100644 --- a/osu.Android/GameView.cs +++ b/osu.Android/GameView.cs @@ -3,7 +3,6 @@ using Android.Content; using Android.Util; -using osu.Framework; using osu.Framework.Android; using osu.Game; @@ -21,6 +20,6 @@ namespace osu.Android { CreateGame(); } - public override Framework.Game CreateGame() => new OsuGame(); + public override Framework.Game CreateGame() => new OsuGameAndroid(); } } diff --git a/osu.Android/MainActivity.cs b/osu.Android/MainActivity.cs index 3a8a5026bc..ff1fd9eba1 100644 --- a/osu.Android/MainActivity.cs +++ b/osu.Android/MainActivity.cs @@ -5,6 +5,7 @@ using Android.App; using Android.OS; using Android.Runtime; using Android.Widget; +using Android.Views; using Android.Content.PM; namespace osu.Android @@ -17,6 +18,7 @@ namespace osu.Android base.OnCreate(savedInstanceState); // Set our view from the "main" layout resource SetContentView(Resource.Layout.activity_main); + Window.AddFlags(WindowManagerFlags.KeepScreenOn); } public override void OnBackPressed() { diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs new file mode 100644 index 0000000000..e2f016887d --- /dev/null +++ b/osu.Android/OsuGameAndroid.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Android; +using osu.Framework.Platform; +using osu.Game; +using System; +using System.IO; + +namespace osu.Android +{ + internal class OsuGameAndroid : OsuGame + { + public OsuGameAndroid() : base() + { + + } + public override Storage GetStorageForStableInstall() + { + return new OpsuStorage(); + } + + // For better migration from opsu! to osu!lazer (WIP) + private class OpsuStorage : AndroidStorage + { + bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); + + protected override string LocateBasePath() + { + BasePath = base.LocateBasePath(); + string opsuInstallPath = Path.Combine(BasePath, "opsu"); + Console.WriteLine(opsuInstallPath); + if (checkExists(opsuInstallPath)) + return opsuInstallPath; + return null; + } + + public OpsuStorage() : base(string.Empty, null) + { + BasePath = LocateBasePath(); + } + } + } +} diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 494ff6044b..42e472bc83 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -63,6 +63,7 @@ + @@ -120,10 +121,10 @@ 1.1.0 - 1.0.0-beta0006 + 1.0.0-dev000094 - 1.0.0-beta0005 + 1.0.0-dev002278 From a7c82c97416a5f8777299e57715e4a8b7bdcb207 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Dec 2018 16:50:38 +0900 Subject: [PATCH 0317/2854] Split out chat component into reusable piece --- .../Components/MatchChatDisplay.cs | 168 ++---------------- 1 file changed, 13 insertions(+), 155 deletions(-) diff --git a/osu.Game.Tournament/Components/MatchChatDisplay.cs b/osu.Game.Tournament/Components/MatchChatDisplay.cs index 41efd0833b..8eedded0f1 100644 --- a/osu.Game.Tournament/Components/MatchChatDisplay.cs +++ b/osu.Game.Tournament/Components/MatchChatDisplay.cs @@ -1,59 +1,23 @@ // Copyright (c) 2007-2018 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.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; using osu.Game.Tournament.IPC; -using osuTK; using osuTK.Graphics; namespace osu.Game.Tournament.Components { - public class MatchChatDisplay : CompositeDrawable + public class MatchChatDisplay : StandAloneChatDisplay { - private Channel lastChannel; - public readonly Bindable Channel = new Bindable(); - private readonly FillFlowContainer messagesFlow; - - public MatchChatDisplay() - { - CornerRadius = 10; - Masking = true; - - InternalChildren = new Drawable[] - { - new Box - { - Colour = Color4.Black, - Alpha = 0.8f, - RelativeSizeAxes = Axes.Both, - }, - messagesFlow = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - LayoutEasing = Easing.Out, - LayoutDuration = 500, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Direction = FillDirection.Vertical, - }, - }; - - Channel.BindValueChanged(channelChanged); - } - private readonly Bindable chatChannel = new Bindable(); + protected override Drawable CreateMessage(Message message) => new MatchMessage(message); + private ChannelManager manager; [BackgroundDependencyLoader(true)] @@ -92,132 +56,26 @@ namespace osu.Game.Tournament.Components } } - private void channelChanged(Channel channel) + protected class MatchMessage : StandAloneMessage { - if (lastChannel != null) - lastChannel.NewMessagesArrived -= newMessages; - - lastChannel = channel; - messagesFlow.Clear(); - - if (channel == null) return; - - channel.NewMessagesArrived += newMessages; - } - - private void newMessages(IEnumerable messages) - { - var excessChildren = messagesFlow.Children.Count - 10; - if (excessChildren > 0) - { - foreach (var c in messagesFlow.Children.Take(excessChildren)) - c.Expire(); - } - - foreach (var message in messages) - { - var formatted = MessageFormatter.FormatMessage(message); - messagesFlow.Add(new MatchMessage(formatted) { Y = messagesFlow.Height }); - } - } - - private class MatchMessage : CompositeDrawable - { - private readonly Message message; - public MatchMessage(Message message) + : base(message) { - this.message = message; } - private readonly Color4 red = new Color4(186, 0, 18, 255); - private readonly Color4 blue = new Color4(17, 136, 170, 255); - [BackgroundDependencyLoader] private void load(LadderInfo info) { - Circle colourBox; - - Margin = new MarginPadding(3); - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - OsuSpriteText senderText; - InternalChildren = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - Width = 0.2f, - Children = new Drawable[] - { - senderText = new OsuSpriteText - { - Font = @"Exo2.0-Bold", - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Text = message.Sender.ToString() - } - } - }, - new Container - { - Size = new Vector2(8, OsuSpriteText.FONT_SIZE), - Margin = new MarginPadding { Horizontal = 3 }, - Children = new Drawable[] - { - colourBox = new Circle - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(8), - }, - } - }, - new OsuTextFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Width = 0.5f, - Text = message.DisplayContent - } - } - }, - }; - - if (info.CurrentMatch.Value.Team1.Value.Players.Any(u => u.Id == message.Sender.Id)) - { - colourBox.Colour = red; - } - else if (info.CurrentMatch.Value.Team2.Value.Players.Any(u => u.Id == message.Sender.Id)) - { - colourBox.Colour = blue; - } - else if (message.Sender.Colour != null) - { - senderText.Colour = colourBox.Colour = OsuColour.FromHex(message.Sender.Colour); - } + if (info.CurrentMatch.Value.Team1.Value.Players.Any(u => u.Id == Message.Sender.Id)) + ColourBox.Colour = red; + else if (info.CurrentMatch.Value.Team2.Value.Players.Any(u => u.Id == Message.Sender.Id)) + ColourBox.Colour = blue; + else if (Message.Sender.Colour != null) + SenderText.Colour = ColourBox.Colour = OsuColour.FromHex(Message.Sender.Colour); } - } - public void Contract() - { - this.FadeIn(300); - this.MoveToY(0, 500, Easing.OutQuint); - } - - public void Expand() - { - this.FadeOut(200); - this.MoveToY(100, 500, Easing.In); + private readonly Color4 red = new Color4(186, 0, 18, 255); + private readonly Color4 blue = new Color4(17, 136, 170, 255); } } } From 660be6a2a454d950bba18c524ba83b7ca9bef8f8 Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Thu, 20 Dec 2018 09:11:20 -0600 Subject: [PATCH 0318/2854] Remove ability to import from opsu! This has been struck down by @ppy --- osu.Android/GameView.cs | 2 +- osu.Android/OsuGameAndroid.cs | 44 ---------------------- osu.Android/Properties/AndroidManifest.xml | 1 + osu.Android/osu.Android.csproj | 2 +- 4 files changed, 3 insertions(+), 46 deletions(-) delete mode 100644 osu.Android/OsuGameAndroid.cs diff --git a/osu.Android/GameView.cs b/osu.Android/GameView.cs index dfae65a2e9..fc9cdeda53 100644 --- a/osu.Android/GameView.cs +++ b/osu.Android/GameView.cs @@ -20,6 +20,6 @@ namespace osu.Android { CreateGame(); } - public override Framework.Game CreateGame() => new OsuGameAndroid(); + public override Framework.Game CreateGame() => new OsuGame(); } } diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs deleted file mode 100644 index e2f016887d..0000000000 --- a/osu.Android/OsuGameAndroid.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Android; -using osu.Framework.Platform; -using osu.Game; -using System; -using System.IO; - -namespace osu.Android -{ - internal class OsuGameAndroid : OsuGame - { - public OsuGameAndroid() : base() - { - - } - public override Storage GetStorageForStableInstall() - { - return new OpsuStorage(); - } - - // For better migration from opsu! to osu!lazer (WIP) - private class OpsuStorage : AndroidStorage - { - bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); - - protected override string LocateBasePath() - { - BasePath = base.LocateBasePath(); - string opsuInstallPath = Path.Combine(BasePath, "opsu"); - Console.WriteLine(opsuInstallPath); - if (checkExists(opsuInstallPath)) - return opsuInstallPath; - return null; - } - - public OpsuStorage() : base(string.Empty, null) - { - BasePath = LocateBasePath(); - } - } - } -} diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml index 4ae7d35c2e..76cb58969f 100644 --- a/osu.Android/Properties/AndroidManifest.xml +++ b/osu.Android/Properties/AndroidManifest.xml @@ -4,5 +4,6 @@ + \ No newline at end of file diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 42e472bc83..8a481b5239 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -40,6 +40,7 @@ false CJK;Mideast;Rare;West;Other + false True @@ -63,7 +64,6 @@ - From b4fa2d90491066ac834d18be5517de473da0ac05 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sat, 22 Dec 2018 16:51:24 +0100 Subject: [PATCH 0319/2854] WIP --- osu.Game.Tests/Visual/TestCaseUserPanel.cs | 1 + osu.Game.Tests/Visual/TestCaseUserProfile.cs | 15 +- .../Graphics/Containers/OsuHoverContainer.cs | 3 +- osu.Game/Graphics/OsuColour.cs | 10 + .../Overlays/Profile/Header/SupporterIcon.cs | 82 +- osu.Game/Overlays/Profile/ProfileHeader.cs | 1089 +++++++++++++---- osu.Game/Overlays/UserProfileOverlay.cs | 6 +- osu.Game/Users/User.cs | 21 + osu.Game/Users/UserPanel.cs | 4 +- osu.Game/Users/UserStatistics.cs | 18 + osu.sln.DotSettings | 1 + 11 files changed, 981 insertions(+), 269 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseUserPanel.cs b/osu.Game.Tests/Visual/TestCaseUserPanel.cs index a53af247f3..81b7465d47 100644 --- a/osu.Game.Tests/Visual/TestCaseUserPanel.cs +++ b/osu.Game.Tests/Visual/TestCaseUserPanel.cs @@ -38,6 +38,7 @@ namespace osu.Game.Tests.Visual Country = new Country { FlagName = @"AU" }, CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", IsSupporter = true, + SupportLevel = 3, }) { Width = 300 }, }, }); diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs index cb281d045b..ce41bc22ff 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Overlays; @@ -27,7 +28,9 @@ namespace osu.Game.Tests.Visual typeof(UserProfileOverlay), typeof(RankGraph), typeof(LineGraph), - typeof(BadgeContainer) + typeof(BadgeContainer), + typeof(SectionsContainer<>), + typeof(SupporterIcon) }; public TestCaseUserProfile() @@ -58,6 +61,11 @@ namespace osu.Game.Tests.Visual { Ranks = new UserStatistics.UserRanks { Global = 2148, Country = 1 }, PP = 4567.89m, + Level = new UserStatistics.LevelInfo + { + Current = 727, + Progress = 69, + } }, RankHistory = new User.RankHistoryData { @@ -72,7 +80,10 @@ namespace osu.Game.Tests.Visual Description = "Outstanding help by being a voluntary test subject.", ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg" } - } + }, + Title = "osu!volunteer", + Colour = "ff0000", + Achievements = new User.UserAchievement[0], }, false)); checkSupporterTag(false); diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs index af804735a8..418ccac290 100644 --- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs +++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs @@ -33,7 +33,8 @@ namespace osu.Game.Graphics.Containers [BackgroundDependencyLoader] private void load(OsuColour colours) { - HoverColour = colours.Yellow; + if(HoverColour == default) + HoverColour = colours.Yellow; } protected override void LoadComplete() diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index fc627fa501..b069f2bf0e 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -92,5 +92,15 @@ namespace osu.Game.Graphics public readonly Color4 ChatBlue = FromHex(@"17292e"); public readonly Color4 ContextMenuGray = FromHex(@"223034"); + + public readonly Color4 CommunityUserGreenLight = FromHex(@"deff87"); + public readonly Color4 CommunityUserGreen = FromHex(@"05ffa2"); + public readonly Color4 CommunityUserGreenDark = FromHex(@"a6cc00"); + public readonly Color4 CommunityUserGrayGreenLighter = FromHex(@"9ebab1"); + public readonly Color4 CommunityUserGrayGreenLight = FromHex(@"77998e"); + public readonly Color4 CommunityUserGrayGreen = FromHex(@"4e7466"); + public readonly Color4 CommunityUserGrayGreenDark = FromHex(@"33413c"); + public readonly Color4 CommunityUserGrayGreenDarker = FromHex(@"2c3532"); + public readonly Color4 CommunityUserGrayGreenDarkest = FromHex(@"1e2422"); } } diff --git a/osu.Game/Overlays/Profile/Header/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/SupporterIcon.cs index 1325ea4e9a..8b33a60d37 100644 --- a/osu.Game/Overlays/Profile/Header/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/SupporterIcon.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; -using osu.Game.Graphics.Backgrounds; using osuTK; namespace osu.Game.Overlays.Profile.Header @@ -15,50 +14,73 @@ namespace osu.Game.Overlays.Profile.Header public class SupporterIcon : CircularContainer, IHasTooltip { private readonly Box background; + private readonly FillFlowContainer iconContainer; public string TooltipText => "osu!supporter"; + public int SupporterLevel + { + set + { + if (value == 0) + { + Hide(); + } + else + { + Show(); + iconContainer.Clear(); + for (int i = 0; i < value; i++) + { + iconContainer.Add(new SpriteIcon + { + Width = 12, + RelativeSizeAxes = Axes.Y, + Icon = FontAwesome.fa_heart, + }); + } + } + } + } + public SupporterIcon() { Masking = true; + AutoSizeAxes = Axes.X; + Hide(); + Children = new Drawable[] { - new Box { RelativeSizeAxes = Axes.Both }, - new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Scale = new Vector2(0.8f), - Masking = true, - Children = new Drawable[] - { - background = new Box { RelativeSizeAxes = Axes.Both }, - new Triangles - { - TriangleScale = 0.2f, - ColourLight = OsuColour.FromHex(@"ff7db7"), - ColourDark = OsuColour.FromHex(@"de5b95"), - RelativeSizeAxes = Axes.Both, - Velocity = 0.3f, - }, - } - }, - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Icon = FontAwesome.fa_heart, - Scale = new Vector2(0.45f), - } + background = new Box { RelativeSizeAxes = Axes.Both }, + iconContainer = new FillFlowContainer + { + Direction = FillDirection.Horizontal, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Height = 0.6f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } }; } + public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) + { + bool invalid = base.Invalidate(invalidation, source, shallPropagate); + + if ((invalidation & Invalidation.DrawSize) != 0) + { + iconContainer.Padding = new MarginPadding { Horizontal = DrawHeight / 2 }; + } + + return invalid; + } + [BackgroundDependencyLoader] private void load(OsuColour colours) { background.Colour = colours.Pink; + iconContainer.Colour = colours.CommunityUserGrayGreenDark; } } } diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index a8075ec295..78f35b3da8 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -2,52 +2,91 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using osuTK; -using osuTK.Graphics; +using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Extensions; +using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Profile.Components; +using osu.Game.Online.Chat; using osu.Game.Overlays.Profile.Header; +using osu.Game.Scoring; using osu.Game.Users; +using osuTK; namespace osu.Game.Overlays.Profile { public class ProfileHeader : Container { - private readonly LinkFlowContainer infoTextLeft; - private readonly LinkFlowContainer infoTextRight; - private readonly FillFlowContainer scoreText, scoreNumberText; private readonly RankGraph rankGraph; public readonly SupporterIcon SupporterTag; private readonly Container coverContainer; - private readonly Sprite levelBadge; - private readonly SpriteText levelText; - private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA; - private readonly Box colourBar; - private readonly DrawableFlag countryFlag; - private readonly BadgeContainer badgeContainer; + private readonly OsuSpriteText coverInfoText; + private readonly CoverInfoTabControl infoTabControl; - private const float cover_height = 350; - private const float info_height = 150; - private const float info_width = 220; + private readonly Box headerTopBox; + private readonly UpdateableAvatar avatar; + private readonly OsuSpriteText usernameText; + private readonly OsuSpriteText titleText; + private readonly DrawableFlag userFlag; + private readonly OsuSpriteText userCountryText; + private readonly Box userIconSeperatorBox; + private readonly FillFlowContainer userStats; + + private readonly Box headerCenterBox; + private readonly OsuSpriteText followerText; + private readonly ProfileHeaderButton messageButton; + private readonly ProfileHeaderButton expandButton; + private readonly Sprite levelBadgeSprite; + private readonly OsuSpriteText levelBadgeText; + + private readonly Bar levelProgressBar; + private readonly OsuSpriteText levelProgressText; + + private readonly OverlinedInfoContainer hiddenDetailGlobal, hiddenDetailCountry; + + public readonly BindableBool DetailsVisible = new BindableBool(); + + private readonly Box headerDetailBox; + private readonly HasTooltipContainer totalPlayTimeTooltip; + private readonly OverlinedInfoContainer totalPlayTimeInfo, medalInfo, ppInfo; + private readonly Dictionary scoreRankInfos = new Dictionary(); + private readonly OverlinedInfoContainer detailGlobalRank, detailCountryRank; + + private const float cover_height = 150; + private const float cover_info_height = 75; + private const float info_height = 500; private const float avatar_size = 110; - private const float level_position = 30; - private const float level_height = 60; - private const float stats_width = 280; - public ProfileHeader(User user) + [Resolved(CanBeNull = true)] + private ChannelManager channelManager { get; set; } + + [Resolved(CanBeNull = true)] + private UserProfileOverlay userOverlay { get; set; } + + [Resolved(CanBeNull = true)] + private ChatOverlay chatOverlay { get; set; } + + public ProfileHeader() { + Container headerDetailContainer, expandedDetailContainer; + FillFlowContainer hiddenDetailContainer; + SpriteIcon expandButtonIcon; + RelativeSizeAxes = Axes.X; Height = cover_height + info_height; @@ -65,262 +104,491 @@ namespace osu.Game.Overlays.Profile RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.75f)) }, - new Container - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Padding = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN, Bottom = 20, Right = stats_width + UserProfileOverlay.CONTENT_X_MARGIN }, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Children = new Drawable[] - { - new UpdateableAvatar - { - User = user, - Size = new Vector2(avatar_size), - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Masking = true, - CornerRadius = 5, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.25f), - Radius = 4, - }, - }, - new Container - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - X = avatar_size + 10, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - SupporterTag = new SupporterIcon - { - Alpha = 0, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Y = -75, - Size = new Vector2(25, 25) - }, - new FillFlowContainer - { - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Y = -48, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = user.Username, - Font = @"Exo2.0-RegularItalic", - TextSize = 30, - }, - new ExternalLinkButton($@"https://osu.ppy.sh/users/{user.Id}") - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Left = 3, Bottom = 3 }, //To better lineup with the font - }, - } - }, - countryFlag = new DrawableFlag(user.Country) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Width = 30, - Height = 20 - } - } - }, - badgeContainer = new BadgeContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Bottom = 5 }, - Alpha = 0, - }, - } - }, - colourBar = new Box - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - X = UserProfileOverlay.CONTENT_X_MARGIN, - Height = 5, - Width = info_width, - Alpha = 0 - } } }, - new Box // this is a temporary workaround for incorrect masking behaviour of FillMode.Fill used in UserCoverBackground (see https://github.com/ppy/osu-framework/issues/1675) - { - RelativeSizeAxes = Axes.X, - Height = 1, - Y = cover_height, - Colour = OsuColour.Gray(34), - }, - infoTextLeft = new LinkFlowContainer(t => t.TextSize = 14) - { - X = UserProfileOverlay.CONTENT_X_MARGIN, - Y = cover_height + 20, - Width = info_width, - AutoSizeAxes = Axes.Y, - ParagraphSpacing = 0.8f, - LineSpacing = 0.2f - }, - infoTextRight = new LinkFlowContainer(t => - { - t.TextSize = 14; - t.Font = @"Exo2.0-RegularItalic"; - }) - { - X = UserProfileOverlay.CONTENT_X_MARGIN + info_width + 20, - Y = cover_height + 20, - Width = info_width, - AutoSizeAxes = Axes.Y, - ParagraphSpacing = 0.8f, - LineSpacing = 0.2f - }, new Container { - X = -UserProfileOverlay.CONTENT_X_MARGIN, - RelativeSizeAxes = Axes.Y, - Width = stats_width, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, + Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, + Y = cover_height, + Height = cover_info_height, + RelativeSizeAxes = Axes.X, + Anchor = Anchor.TopLeft, + Origin = Anchor.BottomLeft, + Depth = -float.MaxValue, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + new OsuSpriteText + { + Text = "Player ", + Font = "Exo2.0-Regular", + TextSize = 30 + }, + coverInfoText = new OsuSpriteText + { + Text = "Info", + Font = "Exo2.0-Regular", + TextSize = 30 + } + } + }, + infoTabControl = new CoverInfoTabControl + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = cover_info_height - 30, + Margin = new MarginPadding { Left = -UserProfileOverlay.CONTENT_X_MARGIN }, + Padding = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN } + } + } + }, + new FillFlowContainer + { + Margin = new MarginPadding { Top = cover_height }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, Children = new Drawable[] { new Container { RelativeSizeAxes = Axes.X, - Y = level_position, - Height = level_height, + Height = 150, Children = new Drawable[] { - new Box + headerTopBox = new Box { - Colour = Color4.Black.Opacity(0.5f), - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, }, - levelBadge = new Sprite + new FillFlowContainer { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Height = 50, - Width = 50, - Alpha = 0 + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, + Height = avatar_size, + AutoSizeAxes = Axes.X, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Children = new[] + { + avatar = new UpdateableAvatar + { + Size = new Vector2(avatar_size), + Masking = true, + CornerRadius = avatar_size * 0.25f, + }, + new Container + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Padding = new MarginPadding { Left = 10 }, + Children = new Drawable[] + { + usernameText = new OsuSpriteText + { + Font = "Exo2.0-Regular", + TextSize = 24 + }, + new FillFlowContainer + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + titleText = new OsuSpriteText + { + TextSize = 18, + Font = "Exo2.0-Regular" + }, + SupporterTag = new SupporterIcon + { + Height = 20, + Margin = new MarginPadding { Top = 5 } + }, + userIconSeperatorBox = new Box + { + RelativeSizeAxes = Axes.X, + Height = 1.5f, + Margin = new MarginPadding { Top = 10 } + }, + new Container + { + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = 5 }, + Children = new Drawable[] + { + userFlag = new DrawableFlag + { + Size = new Vector2(30, 20) + }, + userCountryText = new OsuSpriteText + { + Font = "Exo2.0-Regular", + TextSize = 17.5f, + Margin = new MarginPadding { Left = 40 }, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + } + } + }, + } + } + } + } + } }, - levelText = new OsuSpriteText + userStats = new FillFlowContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Y = 11, - TextSize = 20 + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Y, + Width = 300, + Margin = new MarginPadding { Right = UserProfileOverlay.CONTENT_X_MARGIN }, + Padding = new MarginPadding { Vertical = 15 }, + Spacing = new Vector2(0, 2) } } }, new Container { RelativeSizeAxes = Axes.X, - Y = cover_height, - Anchor = Anchor.TopCentre, - Origin = Anchor.BottomCentre, - Height = cover_height - level_height - level_position - 5, + Height = 60, Children = new Drawable[] { - new Box + headerCenterBox = new Box { - Colour = Color4.Black.Opacity(0.5f), - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, }, - scoreText = new FillFlowContainer + new FillFlowContainer { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Padding = new MarginPadding { Horizontal = 20, Vertical = 18 }, - Spacing = new Vector2(0, 2) - }, - scoreNumberText = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Padding = new MarginPadding { Horizontal = 20, Vertical = 18 }, - Spacing = new Vector2(0, 2) - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, Direction = FillDirection.Horizontal, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Y = -64, - Spacing = new Vector2(20, 0), - Children = new[] + Padding = new MarginPadding { Vertical = 10 }, + Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, + Spacing = new Vector2(10, 0), + Children = new Drawable[] { - gradeSSPlus = new GradeBadge("SSPlus") { Alpha = 0 }, - gradeSS = new GradeBadge("SS") { Alpha = 0 }, + new ProfileHeaderButton + { + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Direction = FillDirection.Horizontal, + Padding = new MarginPadding { Right = 10 }, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.fa_user, + FillMode = FillMode.Fit, + Size = new Vector2(50, 14) + }, + followerText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + TextSize = 16, + Font = "Exo2.0-Bold" + } + } + } + } + }, + messageButton = new ProfileHeaderButton + { + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.fa_envelope, + FillMode = FillMode.Fit, + Size = new Vector2(50, 14) + }, + } + }, + } }, - new FillFlowContainer + new Container { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Y = -18, - Spacing = new Vector2(20, 0), - Children = new[] + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Y, + Padding = new MarginPadding { Vertical = 10 }, + Width = UserProfileOverlay.CONTENT_X_MARGIN, + Child = expandButton = new ProfileHeaderButton { - gradeSPlus = new GradeBadge("SPlus") { Alpha = 0 }, - gradeS = new GradeBadge("S") { Alpha = 0 }, - gradeA = new GradeBadge("A") { Alpha = 0 }, + RelativeSizeAxes = Axes.Y, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + expandButtonIcon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(20), + Icon = FontAwesome.fa_chevron_up, + }, + } + }, + }, + new Container + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Right = UserProfileOverlay.CONTENT_X_MARGIN }, + Children = new Drawable[] + { + new HasTooltipContainer + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Size = new Vector2(40), + TooltipText = "Level", + Children = new Drawable[] + { + levelBadgeSprite = new Sprite + { + RelativeSizeAxes = Axes.Both, + }, + levelBadgeText = new OsuSpriteText + { + TextSize = 20, + Font = "Exo2.0-Medium", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } + }, + expandedDetailContainer = new HasTooltipContainer + { + TooltipText = "Progress to next level", + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Width = 200, + Height = 6, + Margin = new MarginPadding { Right = 50 }, + Children = new Drawable[] + { + new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Child = levelProgressBar = new Bar + { + RelativeSizeAxes = Axes.Both, + BackgroundColour = Color4.Black, + Direction = BarDirection.LeftToRight, + } + }, + levelProgressText = new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.TopRight, + Font = "Exo2.0-Bold", + TextSize = 12, + } + } + }, + hiddenDetailContainer = new FillFlowContainer + { + Direction = FillDirection.Horizontal, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Width = 200, + AutoSizeAxes = Axes.Y, + Alpha = 0, + Spacing = new Vector2(10, 0), + Margin = new MarginPadding { Right = 50 }, + Children = new[] + { + hiddenDetailGlobal = new OverlinedInfoContainer + { + Title = "Global Ranking" + }, + hiddenDetailCountry = new OverlinedInfoContainer + { + Title = "Country Ranking" + }, + } + } } } } }, - new Container + headerDetailContainer = new Container { RelativeSizeAxes = Axes.X, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Height = info_height - 15, + AutoSizeAxes = Axes.Y, Children = new Drawable[] { - new Box + headerDetailBox = new Box { - Colour = Color4.Black.Opacity(0.25f), - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, }, - rankGraph = new RankGraph + new FillFlowContainer { - RelativeSizeAxes = Axes.Both - } + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10, 0), + Children = new Drawable[] + { + totalPlayTimeTooltip = new HasTooltipContainer + { + AutoSizeAxes = Axes.Both, + TooltipText = "0 hours", + Child = totalPlayTimeInfo = new OverlinedInfoContainer + { + Title = "Total Play Time", + }, + }, + medalInfo = new OverlinedInfoContainer + { + Title = "Medals" + }, + ppInfo = new OverlinedInfoContainer + { + Title = "pp" + }, + } + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Direction = FillDirection.Horizontal, + Children = new[] + { + scoreRankInfos[ScoreRank.XH] = new ScoreRankInfo(ScoreRank.XH), + scoreRankInfos[ScoreRank.X] = new ScoreRankInfo(ScoreRank.X), + scoreRankInfos[ScoreRank.SH] = new ScoreRankInfo(ScoreRank.SH), + scoreRankInfos[ScoreRank.S] = new ScoreRankInfo(ScoreRank.S), + scoreRankInfos[ScoreRank.A] = new ScoreRankInfo(ScoreRank.A), + } + } + } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Right = 130 }, + Children = new Drawable[] + { + rankGraph = new RankGraph + { + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + Width = 130, + Anchor = Anchor.TopRight, + Direction = FillDirection.Vertical, + Padding = new MarginPadding { Horizontal = 10 }, + Spacing = new Vector2(0, 20), + Children = new Drawable[] + { + detailGlobalRank = new OverlinedInfoContainer(true, 110) + { + Title = "Global Ranking" + }, + detailCountryRank = new OverlinedInfoContainer(false, 110) + { + Title = "Country Ranking" + }, + } + } + } + } + } + }, } } } } }; + + infoTabControl.AddItem("Info"); + infoTabControl.AddItem("Modding"); + + DetailsVisible.ValueChanged += newValue => expandButtonIcon.Icon = newValue ? FontAwesome.fa_chevron_down : FontAwesome.fa_chevron_up; + DetailsVisible.ValueChanged += newValue => hiddenDetailContainer.Alpha = newValue ? 1 : 0; + DetailsVisible.ValueChanged += newValue => expandedDetailContainer.Alpha = newValue ? 0 : 1; + DetailsVisible.ValueChanged += newValue => headerDetailContainer.Alpha = newValue ? 0 : 1; } - [BackgroundDependencyLoader] - private void load(TextureStore textures) + [BackgroundDependencyLoader(true)] + private void load(OsuColour colours, TextureStore textures) { - levelBadge.Texture = textures.Get(@"Profile/levelbadge"); + coverInfoText.Colour = colours.CommunityUserGreen; + + infoTabControl.AccentColour = colours.CommunityUserGreen; + + headerTopBox.Colour = colours.CommunityUserGrayGreenDarker; + userCountryText.Colour = colours.CommunityUserGrayGreenLighter; + userIconSeperatorBox.Colour = colours.CommunityUserGrayGreenLighter; + + headerCenterBox.Colour = colours.CommunityUserGrayGreenDark; + levelBadgeSprite.Texture = textures.Get("Profile/levelbadge"); + levelBadgeSprite.Colour = colours.Yellow; + levelProgressBar.AccentColour = colours.Yellow; + + hiddenDetailGlobal.LineColour = colours.Yellow; + hiddenDetailCountry.LineColour = colours.Yellow; + + headerDetailBox.Colour = colours.CommunityUserGrayGreenDarkest; + totalPlayTimeInfo.LineColour = colours.Yellow; + medalInfo.LineColour = colours.GreenLight; + ppInfo.LineColour = colours.Red; + + detailGlobalRank.LineColour = colours.Yellow; + detailCountryRank.LineColour = colours.Yellow; } private User user; public User User { - get { return user; } + get => user; set { user = value; @@ -340,9 +608,79 @@ namespace osu.Game.Overlays.Profile Depth = float.MaxValue, }, coverContainer.Add); - if (user.IsSupporter) - SupporterTag.Show(); + avatar.User = User; + usernameText.Text = user.Username; + userFlag.Country = user.Country; + userCountryText.Text = user.Country?.FullName; + SupporterTag.SupporterLevel = user.SupportLevel; + if(user.Title != null) + titleText.Text = user.Title; + titleText.Colour = OsuColour.FromHex(user.Colour ?? "fff"); + userStats.Add(new UserStatsLine("Ranked Score", user.Statistics.RankedScore.ToString("#,##0"))); + userStats.Add(new UserStatsLine("Hit Accuracy", Math.Round(user.Statistics.Accuracy, 2).ToString("#0.00'%'"))); + userStats.Add(new UserStatsLine("Play Count", user.Statistics.PlayCount.ToString("#,##0"))); + userStats.Add(new UserStatsLine("Total Score", user.Statistics.TotalScore.ToString("#,##0"))); + userStats.Add(new UserStatsLine("Total Hits", user.Statistics.TotalHits.ToString("#,##0"))); + userStats.Add(new UserStatsLine("Maximum Combo", user.Statistics.MaxCombo.ToString("#,##0"))); + userStats.Add(new UserStatsLine("Replays Watched by Others", user.Statistics.ReplaysWatched.ToString("#,##0"))); + + followerText.Text = user.FollowerCount?.Length > 0 ? user.FollowerCount[0].ToString("#,##0") : "0"; + + if (!user.PMFriendsOnly) + messageButton.Action = () => + { + channelManager?.OpenPrivateChannel(user); + userOverlay?.Hide(); + chatOverlay?.Show(); + }; + + expandButton.Action = DetailsVisible.Toggle; + + levelBadgeText.Text = user.Statistics.Level.Current.ToString(); + levelProgressBar.Length = user.Statistics.Level.Progress / 100f; + levelProgressText.Text = user.Statistics.Level.Progress.ToString("0'%'"); + + hiddenDetailGlobal.Content = user.Statistics.Ranks.Global?.ToString("#,##0") ?? "-"; + hiddenDetailCountry.Content = user.Statistics.Ranks.Country?.ToString("#,##0") ?? "-"; + + medalInfo.Content = user.Achievements.Length.ToString(); + ppInfo.Content = user.Statistics.PP?.ToString("#,##0") ?? "0"; + + string formatTime(int? secondsNull) + { + if (secondsNull == null) return "0h 0m"; + + int seconds = secondsNull.Value; + string time = ""; + + int days = seconds / 86400; + seconds -= days * 86400; + if (days > 0) + time += days + "d "; + + int hours = seconds / 3600; + seconds -= hours * 3600; + time += hours + "h "; + + int minutes = seconds / 60; + time += minutes + "m"; + + return time; + } + + totalPlayTimeInfo.Content = formatTime(user.Statistics.PlayTime); + totalPlayTimeTooltip.TooltipText = (user.Statistics.PlayTime ?? 0) / 3600 + " hours"; + + foreach (var scoreRankInfo in scoreRankInfos) + scoreRankInfo.Value.RankCount = user.Statistics.GradesCount.GetForScoreRank(scoreRankInfo.Key); + + detailGlobalRank.Content = user.Statistics.Ranks.Global?.ToString("#,##0") ?? "-"; + detailCountryRank.Content = user.Statistics.Ranks.Country?.ToString("#,##0") ?? "-"; + + rankGraph.User.Value = user; + + /* if (!string.IsNullOrEmpty(user.Colour)) { colourBar.Colour = OsuColour.FromHex(user.Colour); @@ -457,24 +795,315 @@ namespace osu.Game.Overlays.Profile rankGraph.User.Value = user; } - badgeContainer.ShowBadges(user.Badges); + badgeContainer.ShowBadges(user.Badges);*/ } - private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null) + private class CoverInfoTabControl : TabControl { - if (string.IsNullOrEmpty(str)) return; + private readonly Box bar; - infoTextRight.AddIcon(icon); - if (url != null) + private Color4 accentColour; + public Color4 AccentColour { - infoTextRight.AddLink(" " + str, url); - } - else - { - infoTextRight.AddText(" " + str); + get => accentColour; + set + { + if (accentColour == value) return; + + accentColour = value; + + bar.Colour = value; + + foreach (TabItem tabItem in TabContainer) + { + ((CoverInfoTabItem)tabItem).AccentColour = value; + } + } } - infoTextRight.NewLine(); + public MarginPadding Padding + { + set => TabContainer.Padding = value; + get => TabContainer.Padding; + } + + public CoverInfoTabControl() + { + TabContainer.Masking = false; + TabContainer.Spacing = new Vector2(20, 0); + + AddInternal(bar = new Box + { + RelativeSizeAxes = Axes.X, + Height = 2, + Anchor = Anchor.BottomLeft, + Origin = Anchor.CentreLeft + }); + } + + protected override Dropdown CreateDropdown() => null; + + protected override TabItem CreateTabItem(string value) => new CoverInfoTabItem(value) + { + AccentColour = AccentColour + }; + + private class CoverInfoTabItem : TabItem + { + private readonly OsuSpriteText text; + private readonly Drawable bar; + + private Color4 accentColour; + public Color4 AccentColour + { + get => accentColour; + set + { + accentColour = value; + + bar.Colour = value; + if (!Active) text.Colour = value; + } + } + + public CoverInfoTabItem(string value) + : base(value) + { + AutoSizeAxes = Axes.X; + RelativeSizeAxes = Axes.Y; + + Children = new[] + { + text = new OsuSpriteText + { + Margin = new MarginPadding { Bottom = 15 }, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Text = value, + TextSize = 14, + Font = "Exo2.0-Bold", + }, + bar = new Circle + { + RelativeSizeAxes = Axes.X, + Height = 0, + Origin = Anchor.CentreLeft, + Anchor = Anchor.BottomLeft, + }, + new HoverClickSounds() + }; + } + + protected override bool OnHover(HoverEvent e) + { + if (!Active) + onActivated(true); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + + if (!Active) + OnDeactivated(); + } + + protected override void OnActivated() + { + onActivated(); + } + + protected override void OnDeactivated() + { + text.FadeColour(AccentColour, 120, Easing.InQuad); + bar.ResizeHeightTo(0, 120, Easing.InQuad); + text.Font = "Exo2.0-Medium"; + } + + private void onActivated(bool fake = false) + { + text.FadeColour(Color4.White, 120, Easing.InQuad); + bar.ResizeHeightTo(7.5f, 120, Easing.InQuad); + if (!fake) + text.Font = "Exo2.0-Bold"; + } + } + } + + private class UserStatsLine : Container + { + private readonly OsuSpriteText rightText; + + public UserStatsLine(string left, string right) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Children = new Drawable[] + { + new OsuSpriteText + { + TextSize = 15, + Text = left, + Font = "Exo2.0-Medium" + }, + rightText = new OsuSpriteText + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + TextSize = 15, + Text = right, + Font = "Exo2.0-Medium" + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + rightText.Colour = colours.BlueLight; + } + } + + private class ProfileHeaderButton : OsuHoverContainer + { + private readonly Box background; + private readonly Container content; + + protected override Container Content => content; + + protected override IEnumerable EffectTargets => new[] { background }; + + public ProfileHeaderButton() + { + HoverColour = Color4.Black.Opacity(0.75f); + IdleColour = Color4.Black.Opacity(0.7f); + AutoSizeAxes = Axes.X; + + base.Content.Add(new CircularContainer + { + Masking = true, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + content = new Container + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 10 }, + } + } + }); + } + } + + private class HasTooltipContainer : Container, IHasTooltip + { + public string TooltipText { get; set; } + } + + private class OverlinedInfoContainer : CompositeDrawable + { + private readonly Circle line; + private readonly OsuSpriteText title, content; + + public string Title + { + set => title.Text = value; + } + + public string Content + { + set => content.Text = value; + } + + public Color4 LineColour + { + set => line.Colour = value; + } + + public OverlinedInfoContainer(bool big = false, int minimumWidth = 60) + { + AutoSizeAxes = Axes.Both; + InternalChild = new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + line = new Circle + { + RelativeSizeAxes = Axes.X, + Height = 4, + }, + title = new OsuSpriteText + { + Font = "Exo2.0-Bold", + TextSize = big ? 14 : 12, + }, + content = new OsuSpriteText + { + Font = "Exo2.0-Light", + TextSize = big ? 40 : 18, + }, + new Container //Add a minimum size to the FillFlowContainer + { + Width = minimumWidth, + } + } + }; + } + } + + public class ScoreRankInfo : CompositeDrawable + { + private readonly ScoreRank rank; + private readonly Sprite rankSprite; + private readonly OsuSpriteText rankCount; + + public int RankCount + { + set => rankCount.Text = value.ToString("#,##0"); + } + + public ScoreRankInfo(ScoreRank rank) + { + this.rank = rank; + + AutoSizeAxes = Axes.Both; + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + Width = 56, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + rankSprite = new Sprite + { + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit + }, + rankCount = new OsuSpriteText + { + Font = "Exo2.0-Bold", + TextSize = 12, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + rankSprite.Texture = textures.Get($"Grades/{rank.GetDescription()}"); + } } } } diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index c15f464c7c..ca99f07d9b 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays private SectionsContainer sectionsContainer; private ProfileTabControl tabs; - public const float CONTENT_X_MARGIN = 50; + public const float CONTENT_X_MARGIN = 70; public UserProfileOverlay() { @@ -113,12 +113,10 @@ namespace osu.Game.Overlays Colour = OsuColour.Gray(0.2f) }); - Header = new ProfileHeader(user); - Add(sectionsContainer = new SectionsContainer { RelativeSizeAxes = Axes.Both, - ExpandableHeader = Header, + ExpandableHeader = Header = new ProfileHeader(), FixedHeader = tabs, HeaderBackground = new Box { diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index a5d8c03a67..485c953b75 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -59,6 +59,9 @@ namespace osu.Game.Users [JsonProperty(@"is_supporter")] public bool IsSupporter; + [JsonProperty(@"support_level")] + public int SupportLevel; + [JsonProperty(@"is_gmt")] public bool IsGMT; @@ -71,6 +74,9 @@ namespace osu.Game.Users [JsonProperty(@"is_active")] public bool Active; + [JsonProperty(@"pm_friends_only")] + public bool PMFriendsOnly; + [JsonProperty(@"interests")] public string Interests; @@ -104,6 +110,9 @@ namespace osu.Game.Users [JsonProperty(@"post_count")] public int PostCount; + [JsonProperty(@"follower_count")] + public int[] FollowerCount; + [JsonProperty(@"playstyle")] public string[] PlayStyle; @@ -143,6 +152,18 @@ namespace osu.Game.Users [JsonProperty("badges")] public Badge[] Badges; + [JsonProperty("user_achievements")] + public UserAchievement[] Achievements; + + public class UserAchievement + { + [JsonProperty("achieved_at")] + public DateTimeOffset AchievedAt; + + [JsonProperty("achievement_id")] + public int ID; + } + public override string ToString() => Username; /// private double hpMultiplier = 1; - public ManiaScoreProcessor() - { - } - public ManiaScoreProcessor(DrawableRuleset drawableRuleset) : base(drawableRuleset) { @@ -122,8 +82,8 @@ namespace osu.Game.Rulesets.Mania.Scoring } } - protected override double HpFactorFor(Judgement judgement, HitResult result) - => result == HitResult.Miss ? hpMissMultiplier : hpMultiplier; + protected override double HpFactorFor(JudgementResult result) + => result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier; public override HitWindows CreateHitWindows() => new ManiaHitWindows(); } diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index a4975ef3b3..2162663539 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -37,8 +37,6 @@ namespace osu.Game.Rulesets.Osu.Scoring comboResultCounts.Clear(); } - private const double harshness = 0.01; - protected override void ApplyResult(JudgementResult result) { base.ApplyResult(result); @@ -49,9 +47,9 @@ namespace osu.Game.Rulesets.Osu.Scoring comboResultCounts[osuResult.ComboType] = comboResultCounts.GetOrDefault(osuResult.ComboType) + 1; } - protected override double HpFactorFor(Judgement judgement, HitResult result) + protected override double HpFactorFor(JudgementResult result) { - switch (result) + switch (result.Type) { case HitResult.Great: return 10.2 - hpDrainRate; diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index a0055a93aa..b2b866db4b 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -46,8 +46,8 @@ namespace osu.Game.Rulesets.Taiko.Scoring hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120); } - protected override double HpFactorFor(Judgement judgement, HitResult result) - => result == HitResult.Miss ? hpMissMultiplier : hpMultiplier; + protected override double HpFactorFor(JudgementResult result) + => result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier; protected override void Reset(bool storeResults) { diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs index d4ef5750b1..195fe316ac 100644 --- a/osu.Game/Rulesets/Judgements/JudgementResult.cs +++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs @@ -37,6 +37,11 @@ namespace osu.Game.Rulesets.Judgements /// diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index d86f608bd1..3d0127bba4 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -185,8 +185,8 @@ namespace osu.Game.Users { infoContainer.Add(new SupporterIcon { - RelativeSizeAxes = Axes.Y, - Width = 20f, + Height = 20f, + SupporterLevel = user.SupportLevel }); } diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs index f04bfb62bb..c400a3f15b 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -1,7 +1,9 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using Newtonsoft.Json; +using osu.Game.Scoring; namespace osu.Game.Users { @@ -37,6 +39,9 @@ namespace osu.Game.Users [JsonProperty(@"play_count")] public int PlayCount; + [JsonProperty(@"play_time")] + public int? PlayTime; + [JsonProperty(@"total_score")] public long TotalScore; @@ -68,6 +73,19 @@ namespace osu.Game.Users [JsonProperty(@"a")] public int A; + + public int GetForScoreRank(ScoreRank rank) + { + switch (rank) + { + case ScoreRank.XH: return SSPlus; + case ScoreRank.X: return SS; + case ScoreRank.SH: return SPlus; + case ScoreRank.S: return S; + case ScoreRank.A: return A; + default: throw new ArgumentException($"API does not return {rank.ToString()}"); + } + } } public struct UserRanks diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index d6882282e6..170a7bd8c9 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -208,6 +208,7 @@ MD5 NS OS + PM RGB RNG SHA From 2fe80d556844d82c8029d8999a08c8b5166e5094 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sat, 22 Dec 2018 21:50:25 +0100 Subject: [PATCH 0320/2854] Update ProfileHeader to the new design --- .../Visual/TestCaseBadgeContainer.cs | 62 --- osu.Game.Tests/Visual/TestCaseUserProfile.cs | 1 - .../Graphics/Containers/SectionsContainer.cs | 11 + osu.Game/Graphics/UserInterface/LineGraph.cs | 11 +- .../Profile/Components/DrawableJoinDate.cs | 20 - .../Overlays/Profile/Components/GradeBadge.cs | 50 --- .../Overlays/Profile/Header/BadgeContainer.cs | 198 --------- .../Profile/Header/ProfileHeaderTabControl.cs | 149 +++++++ osu.Game/Overlays/Profile/Header/RankGraph.cs | 194 +++++--- osu.Game/Overlays/Profile/ProfileHeader.cs | 419 ++++++++---------- 10 files changed, 480 insertions(+), 635 deletions(-) delete mode 100644 osu.Game.Tests/Visual/TestCaseBadgeContainer.cs delete mode 100644 osu.Game/Overlays/Profile/Components/DrawableJoinDate.cs delete mode 100644 osu.Game/Overlays/Profile/Components/GradeBadge.cs delete mode 100644 osu.Game/Overlays/Profile/Header/BadgeContainer.cs create mode 100644 osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs diff --git a/osu.Game.Tests/Visual/TestCaseBadgeContainer.cs b/osu.Game.Tests/Visual/TestCaseBadgeContainer.cs deleted file mode 100644 index 8177e2e272..0000000000 --- a/osu.Game.Tests/Visual/TestCaseBadgeContainer.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2007-2018 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 NUnit.Framework; -using osu.Framework.Graphics; -using osu.Game.Overlays.Profile.Header; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseBadgeContainer : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(BadgeContainer) }; - - public TestCaseBadgeContainer() - { - BadgeContainer badgeContainer; - - Child = badgeContainer = new BadgeContainer - { - RelativeSizeAxes = Axes.Both - }; - - AddStep("Show 1 badge", () => badgeContainer.ShowBadges(new[] - { - new Badge - { - AwardedAt = DateTimeOffset.Now, - Description = "Appreciates compasses", - ImageUrl = "https://assets.ppy.sh/profile-badges/mg2018-1star.png", - } - })); - - AddStep("Show 2 badges", () => badgeContainer.ShowBadges(new[] - { - new Badge - { - AwardedAt = DateTimeOffset.Now, - Description = "Contributed to osu!lazer testing", - ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.png", - }, - new Badge - { - AwardedAt = DateTimeOffset.Now, - Description = "Appreciates compasses", - ImageUrl = "https://assets.ppy.sh/profile-badges/mg2018-1star.png", - } - })); - - AddStep("Show many badges", () => badgeContainer.ShowBadges(Enumerable.Range(1, 20).Select(i => new Badge - { - AwardedAt = DateTimeOffset.Now, - Description = $"Contributed to osu!lazer testing {i} times", - ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg", - }).ToArray())); - } - } -} diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs index ce41bc22ff..cff55c2506 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs @@ -28,7 +28,6 @@ namespace osu.Game.Tests.Visual typeof(UserProfileOverlay), typeof(RankGraph), typeof(LineGraph), - typeof(BadgeContainer), typeof(SectionsContainer<>), typeof(SupporterIcon) }; diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 36fdbe6e94..f16b5773ea 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -141,6 +141,17 @@ namespace osu.Game.Graphics.Containers public void ScrollToTop() => scrollContainer.ScrollTo(0); + public override void InvalidateFromChild(Invalidation invalidation, Drawable source = null) + { + base.InvalidateFromChild(invalidation, source); + + if ((invalidation & Invalidation.DrawSize) != 0) + { + if (source == ExpandableHeader) //We need to recalculate the positions if the ExpandableHeader changed its size + lastKnownScroll = -1; + } + } + private float lastKnownScroll; protected override void UpdateAfterChildren() { diff --git a/osu.Game/Graphics/UserInterface/LineGraph.cs b/osu.Game/Graphics/UserInterface/LineGraph.cs index c84c500201..c750f7a89d 100644 --- a/osu.Game/Graphics/UserInterface/LineGraph.cs +++ b/osu.Game/Graphics/UserInterface/LineGraph.cs @@ -9,6 +9,7 @@ using osuTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Lines; +using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface { @@ -63,13 +64,19 @@ namespace osu.Game.Graphics.UserInterface } } + public Color4 LineColour + { + get => maskingContainer.Colour; + set => maskingContainer.Colour = value; + } + public LineGraph() { Add(maskingContainer = new Container { Masking = true, RelativeSizeAxes = Axes.Both, - Child = path = new SmoothPath { RelativeSizeAxes = Axes.Both, PathWidth = 1 } + Child = path = new SmoothPath { RelativeSizeAxes = Axes.Both, PathWidth = 1.5f } }); } @@ -103,7 +110,7 @@ namespace osu.Game.Graphics.UserInterface for (int i = 0; i < values.Length; i++) { float x = (i + count - values.Length) / (float)(count - 1) * DrawWidth - 1; - float y = GetYPosition(values[i]) * DrawHeight - 1; + float y = GetYPosition(values[i]) * DrawHeight - path.PathWidth; // the -1 is for inner offset in path (actually -PathWidth) path.AddVertex(new Vector2(x, y)); } diff --git a/osu.Game/Overlays/Profile/Components/DrawableJoinDate.cs b/osu.Game/Overlays/Profile/Components/DrawableJoinDate.cs deleted file mode 100644 index 11ee329f33..0000000000 --- a/osu.Game/Overlays/Profile/Components/DrawableJoinDate.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Profile.Components -{ - public class DrawableJoinDate : DrawableDate - { - public DrawableJoinDate(DateTimeOffset date) - : base(date) - { - } - - protected override string Format() => Text = Date.ToUniversalTime().Year < 2008 ? "Here since the beginning" : $"{Date:MMMM yyyy}"; - - public override string TooltipText => $"{Date:MMMM d, yyyy}"; - } -} diff --git a/osu.Game/Overlays/Profile/Components/GradeBadge.cs b/osu.Game/Overlays/Profile/Components/GradeBadge.cs deleted file mode 100644 index 14a47e8d03..0000000000 --- a/osu.Game/Overlays/Profile/Components/GradeBadge.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Game.Graphics.Sprites; - -namespace osu.Game.Overlays.Profile.Components -{ - public class GradeBadge : Container - { - private const float width = 50; - private readonly string grade; - private readonly Sprite badge; - private readonly SpriteText numberText; - - public int DisplayCount - { - set => numberText.Text = value.ToString(@"#,0"); - } - - public GradeBadge(string grade) - { - this.grade = grade; - Width = width; - Height = 41; - Add(badge = new Sprite - { - Width = width, - Height = 26 - }); - Add(numberText = new OsuSpriteText - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - TextSize = 14, - Font = @"Exo2.0-Bold" - }); - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - badge.Texture = textures.Get($"Grades/{grade}"); - } - } -} diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs deleted file mode 100644 index 06fef22309..0000000000 --- a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Users; -using osuTK; - -namespace osu.Game.Overlays.Profile.Header -{ - public class BadgeContainer : Container - { - private static readonly Vector2 badge_size = new Vector2(86, 40); - private static readonly MarginPadding outer_padding = new MarginPadding(3); - - private OsuSpriteText badgeCountText; - private FillFlowContainer badgeFlowContainer; - private FillFlowContainer outerBadgeContainer; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Child = new Container - { - Masking = true, - CornerRadius = 4, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Gray3 - }, - outerBadgeContainer = new OuterBadgeContainer(onOuterHover, onOuterHoverLost) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Direction = FillDirection.Vertical, - Padding = outer_padding, - Width = DrawableBadge.DRAWABLE_BADGE_SIZE.X + outer_padding.TotalHorizontal, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - badgeCountText = new OsuSpriteText - { - Alpha = 0, - TextSize = 12, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Font = "Exo2.0-Regular" - }, - new Container - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - AutoSizeAxes = Axes.Both, - Child = badgeFlowContainer = new FillFlowContainer - { - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - } - } - } - }, - } - }; - - Scheduler.AddDelayed(rotateBadges, 3000, true); - } - - private void rotateBadges() - { - if (outerBadgeContainer.IsHovered) return; - - visibleBadge = (visibleBadge + 1) % badgeCount; - - badgeFlowContainer.MoveToX(-DrawableBadge.DRAWABLE_BADGE_SIZE.X * visibleBadge, 500, Easing.InOutQuad); - } - - private int visibleBadge; - private int badgeCount; - - public void ShowBadges(Badge[] badges) - { - if (badges == null || badges.Length == 0) - { - Hide(); - return; - } - - badgeCount = badges.Length; - - badgeCountText.FadeTo(badgeCount > 1 ? 1 : 0); - badgeCountText.Text = $"{badges.Length} badges"; - - Show(); - visibleBadge = 0; - - badgeFlowContainer.Clear(); - for (var index = 0; index < badges.Length; index++) - { - int displayIndex = index; - LoadComponentAsync(new DrawableBadge(badges[index]) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }, asyncBadge => - { - badgeFlowContainer.Add(asyncBadge); - - // load in stable order regardless of async load order. - badgeFlowContainer.SetLayoutPosition(asyncBadge, displayIndex); - }); - } - } - - private void onOuterHover() - { - badgeFlowContainer.ClearTransforms(); - badgeFlowContainer.X = 0; - badgeFlowContainer.Direction = FillDirection.Full; - outerBadgeContainer.AutoSizeAxes = Axes.Both; - - badgeFlowContainer.MaximumSize = new Vector2(ChildSize.X, float.MaxValue); - } - - private void onOuterHoverLost() - { - badgeFlowContainer.X = -DrawableBadge.DRAWABLE_BADGE_SIZE.X * visibleBadge; - badgeFlowContainer.Direction = FillDirection.Horizontal; - outerBadgeContainer.AutoSizeAxes = Axes.Y; - outerBadgeContainer.Width = DrawableBadge.DRAWABLE_BADGE_SIZE.X + outer_padding.TotalHorizontal; - } - - private class OuterBadgeContainer : FillFlowContainer - { - private readonly Action hoverAction; - private readonly Action hoverLostAction; - - public OuterBadgeContainer(Action hoverAction, Action hoverLostAction) - { - this.hoverAction = hoverAction; - this.hoverLostAction = hoverLostAction; - } - - protected override bool OnHover(HoverEvent e) - { - hoverAction(); - return true; - } - - protected override void OnHoverLost(HoverLostEvent e) => hoverLostAction(); - } - - private class DrawableBadge : Container, IHasTooltip - { - public static readonly Vector2 DRAWABLE_BADGE_SIZE = badge_size + outer_padding.Total; - - private readonly Badge badge; - - public DrawableBadge(Badge badge) - { - this.badge = badge; - Padding = outer_padding; - Size = DRAWABLE_BADGE_SIZE; - } - - [BackgroundDependencyLoader] - private void load(LargeTextureStore textures) - { - Child = new Sprite - { - FillMode = FillMode.Fit, - RelativeSizeAxes = Axes.Both, - Texture = textures.Get(badge.ImageUrl), - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - Child.FadeInFromZero(200); - } - - public string TooltipText => badge.Description; - } - } -} diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs new file mode 100644 index 0000000000..b067273b04 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs @@ -0,0 +1,149 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Profile +{ + public class ProfileHeaderTabControl : TabControl + { + private readonly Box bar; + + private Color4 accentColour; + + public Color4 AccentColour + { + get => accentColour; + set + { + if (accentColour == value) return; + + accentColour = value; + + bar.Colour = value; + + foreach (TabItem tabItem in TabContainer) + { + ((ProfileHeaderTabItem)tabItem).AccentColour = value; + } + } + } + + public MarginPadding Padding + { + set => TabContainer.Padding = value; + get => TabContainer.Padding; + } + + public ProfileHeaderTabControl() + { + TabContainer.Masking = false; + TabContainer.Spacing = new Vector2(20, 0); + + AddInternal(bar = new Box + { + RelativeSizeAxes = Axes.X, + Height = 2, + Anchor = Anchor.BottomLeft, + Origin = Anchor.CentreLeft + }); + } + + protected override Dropdown CreateDropdown() => null; + + protected override TabItem CreateTabItem(string value) => new ProfileHeaderTabItem(value) + { + AccentColour = AccentColour + }; + + private class ProfileHeaderTabItem : TabItem + { + private readonly OsuSpriteText text; + private readonly Drawable bar; + + private Color4 accentColour; + + public Color4 AccentColour + { + get => accentColour; + set + { + accentColour = value; + + bar.Colour = value; + if (!Active) text.Colour = value; + } + } + + public ProfileHeaderTabItem(string value) + : base(value) + { + AutoSizeAxes = Axes.X; + RelativeSizeAxes = Axes.Y; + + Children = new[] + { + text = new OsuSpriteText + { + Margin = new MarginPadding { Bottom = 15 }, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Text = value, + TextSize = 14, + Font = "Exo2.0-Bold", + }, + bar = new Circle + { + RelativeSizeAxes = Axes.X, + Height = 0, + Origin = Anchor.CentreLeft, + Anchor = Anchor.BottomLeft, + }, + new HoverClickSounds() + }; + } + + protected override bool OnHover(HoverEvent e) + { + if (!Active) + onActivated(true); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + + if (!Active) + OnDeactivated(); + } + + protected override void OnActivated() + { + onActivated(); + } + + protected override void OnDeactivated() + { + text.FadeColour(AccentColour, 120, Easing.InQuad); + bar.ResizeHeightTo(0, 120, Easing.InQuad); + text.Font = "Exo2.0-Medium"; + } + + private void onActivated(bool fake = false) + { + text.FadeColour(Color4.White, 120, Easing.InQuad); + bar.ResizeHeightTo(7.5f, 120, Easing.InQuad); + if (!fake) + text.Font = "Exo2.0-Bold"; + } + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/RankGraph.cs b/osu.Game/Overlays/Profile/Header/RankGraph.cs index f74c8b5069..f67a8e47d4 100644 --- a/osu.Game/Overlays/Profile/Header/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/RankGraph.cs @@ -8,8 +8,8 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -19,19 +19,18 @@ using osuTK; namespace osu.Game.Overlays.Profile.Header { - public class RankGraph : Container + public class RankGraph : Container, IHasCustomTooltip { - private const float primary_textsize = 25; private const float secondary_textsize = 13; private const float padding = 10; private const float fade_duration = 150; private const int ranked_days = 88; - private readonly SpriteText rankText, performanceText, relativeText; private readonly RankChartLineGraph graph; private readonly OsuSpriteText placeholder; private KeyValuePair[] ranks; + private int dayIndex; public Bindable User = new Bindable(); public RankGraph() @@ -44,43 +43,20 @@ namespace osu.Game.Overlays.Profile.Header Anchor = Anchor.Centre, Origin = Anchor.Centre, Text = "No recent plays", - TextSize = 14, - Font = @"Exo2.0-RegularItalic", - }, - rankText = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Font = @"Exo2.0-RegularItalic", - TextSize = primary_textsize - }, - relativeText = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Font = @"Exo2.0-RegularItalic", - Y = 25, - TextSize = secondary_textsize - }, - performanceText = new OsuSpriteText - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Font = @"Exo2.0-RegularItalic", - TextSize = secondary_textsize + TextSize = 12, + Font = @"Exo2.0-Regular", }, graph = new RankChartLineGraph { Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Height = 60, + RelativeSizeAxes = Axes.Both, Y = -secondary_textsize, Alpha = 0, } }; - graph.OnBallMove += showHistoryRankTexts; + graph.OnBallMove += i => dayIndex = i; User.ValueChanged += userChanged; } @@ -88,7 +64,7 @@ namespace osu.Game.Overlays.Profile.Header [BackgroundDependencyLoader] private void load(OsuColour colours) { - graph.Colour = colours.Yellow; + graph.LineColour = colours.Yellow; } private void userChanged(User user) @@ -97,9 +73,6 @@ namespace osu.Game.Overlays.Profile.Header if (user?.Statistics?.Ranks.Global == null) { - rankText.Text = string.Empty; - performanceText.Text = string.Empty; - relativeText.Text = string.Empty; graph.FadeOut(fade_duration, Easing.Out); ranks = null; return; @@ -114,27 +87,9 @@ namespace osu.Game.Overlays.Profile.Header graph.DefaultValueCount = ranks.Length; graph.Values = ranks.Select(x => -(float)Math.Log(x.Value)); - graph.SetStaticBallPosition(); } graph.FadeTo(ranks.Length > 1 ? 1 : 0, fade_duration, Easing.Out); - - updateRankTexts(); - } - - private void updateRankTexts() - { - var user = User.Value; - - performanceText.Text = user.Statistics.PP != null ? $"{user.Statistics.PP:#,0}pp" : string.Empty; - rankText.Text = user.Statistics.Ranks.Global > 0 ? $"#{user.Statistics.Ranks.Global:#,0}" : "no rank"; - relativeText.Text = user.Country != null && user.Statistics.Ranks.Country > 0 ? $"{user.Country.FullName} #{user.Statistics.Ranks.Country:#,0}" : "no rank"; - } - - private void showHistoryRankTexts(int dayIndex) - { - rankText.Text = $"#{ranks[dayIndex].Value:#,0}"; - relativeText.Text = dayIndex + 1 == ranks.Length ? "Now" : $"{ranked_days - ranks[dayIndex].Key} days ago"; } protected override bool OnHover(HoverEvent e) @@ -160,7 +115,6 @@ namespace osu.Game.Overlays.Profile.Header if (ranks?.Length > 1) { graph.HideBall(); - updateRankTexts(); } base.OnHoverLost(e); @@ -168,44 +122,62 @@ namespace osu.Game.Overlays.Profile.Header private class RankChartLineGraph : LineGraph { - private readonly CircularContainer staticBall; private readonly CircularContainer movingBall; + private readonly Box ballBg; + private readonly Box movingBar; public Action OnBallMove; public RankChartLineGraph() { - Add(staticBall = new CircularContainer + Add(movingBar = new Box { - Origin = Anchor.Centre, - Size = new Vector2(8), - Masking = true, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Y, + Width = 1.5f, + Alpha = 0, RelativePositionAxes = Axes.Both, - Child = new Box { RelativeSizeAxes = Axes.Both } }); + Add(movingBall = new CircularContainer { Origin = Anchor.Centre, - Size = new Vector2(8), + Size = new Vector2(18), Alpha = 0, Masking = true, + BorderThickness = 4, RelativePositionAxes = Axes.Both, - Child = new Box { RelativeSizeAxes = Axes.Both } + Child = ballBg = new Box { RelativeSizeAxes = Axes.Both } }); } - public void SetStaticBallPosition() => staticBall.Position = new Vector2(1, GetYPosition(Values.Last())); + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + ballBg.Colour = colours.CommunityUserGrayGreenDarkest; + movingBall.BorderColour = colours.Yellow; + movingBar.Colour = colours.Yellow; + } public void UpdateBallPosition(float mouseXPosition) { int index = calculateIndex(mouseXPosition); movingBall.Position = calculateBallPosition(index); + movingBar.X = movingBall.X; OnBallMove.Invoke(index); } - public void ShowBall() => movingBall.FadeIn(fade_duration); + public void ShowBall() + { + movingBall.FadeIn(fade_duration); + movingBar.FadeIn(fade_duration); + } - public void HideBall() => movingBall.FadeOut(fade_duration); + public void HideBall() + { + movingBall.FadeOut(fade_duration); + movingBar.FadeOut(fade_duration); + } private int calculateIndex(float mouseXPosition) => (int)Math.Round(mouseXPosition / DrawWidth * (DefaultValueCount - 1)); @@ -215,5 +187,97 @@ namespace osu.Game.Overlays.Profile.Header return new Vector2(index / (float)(DefaultValueCount - 1), y); } } + + public string TooltipText => User.Value?.Statistics?.Ranks.Global == null ? "" : $"{ranks[dayIndex].Value:#,##0}|{ranked_days - ranks[dayIndex].Key + 1}"; + + public ITooltip GetCustomTooltip() => new RankGraphTooltip(this); + + public class RankGraphTooltip : VisibilityContainer, ITooltip + { + private readonly RankGraph graph; + private readonly OsuSpriteText globalRankingText, timeText; + private readonly Box background; + + public string TooltipText { get; set; } + + public RankGraphTooltip(RankGraph graph) + { + this.graph = graph; + AutoSizeAxes = Axes.Both; + Masking = true; + CornerRadius = 10; + + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Padding = new MarginPadding(10), + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new OsuSpriteText + { + Font = "Exo2.0-Bold", + TextSize = 12, + Text = "Global Ranking " + }, + globalRankingText = new OsuSpriteText + { + Font = "Exo2.0-Regular", + TextSize = 12, + } + } + }, + timeText = new OsuSpriteText + { + TextSize = 12, + Font = "Exo2.0-Regular" + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.CommunityUserGrayGreenDarker; + } + + public void Refresh() + { + var info = TooltipText.Split('|'); + globalRankingText.Text = info[0]; + timeText.Text = info[1] == "0" ? "now" : $"{info[1]} days ago"; + } + + private bool instantMove = true; + + public void Move(Vector2 pos) + { + if (instantMove) + { + Position = pos; + instantMove = false; + } + else + this.MoveTo(pos, 200, Easing.OutQuint); + } + + protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); + + protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); + } } } diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index b35ae50c5e..6ab178cfe1 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -15,8 +15,6 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -36,7 +34,7 @@ namespace osu.Game.Overlays.Profile public readonly SupporterIcon SupporterTag; private readonly Container coverContainer; private readonly OsuSpriteText coverInfoText; - private readonly CoverInfoTabControl infoTabControl; + private readonly ProfileHeaderTabControl infoTabControl; private readonly Box headerTopBox; private readonly UpdateableAvatar avatar; @@ -67,9 +65,16 @@ namespace osu.Game.Overlays.Profile private readonly Dictionary scoreRankInfos = new Dictionary(); private readonly OverlinedInfoContainer detailGlobalRank, detailCountryRank; + private readonly Box headerBadgeBox; + private readonly FillFlowContainer badgeFlowContainer; + private readonly Container badgeContainer; + + private readonly Box headerBottomBox; + private readonly LinkFlowContainer bottomTopLinkContainer; + private readonly LinkFlowContainer bottomLinkContainer; + private const float cover_height = 150; private const float cover_info_height = 75; - private const float info_height = 500; private const float avatar_size = 110; [Resolved(CanBeNull = true)] @@ -83,12 +88,12 @@ namespace osu.Game.Overlays.Profile public ProfileHeader() { - Container headerDetailContainer, expandedDetailContainer; - FillFlowContainer hiddenDetailContainer; + Container expandedDetailContainer; + FillFlowContainer hiddenDetailContainer, headerDetailContainer; SpriteIcon expandButtonIcon; RelativeSizeAxes = Axes.X; - Height = cover_height + info_height; + AutoSizeAxes = Axes.Y; Children = new Drawable[] { @@ -137,7 +142,7 @@ namespace osu.Game.Overlays.Profile } } }, - infoTabControl = new CoverInfoTabControl + infoTabControl = new ProfileHeaderTabControl { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, @@ -181,7 +186,7 @@ namespace osu.Game.Overlays.Profile Size = new Vector2(avatar_size), Masking = true, CornerRadius = avatar_size * 0.25f, - OpenOnClick = { Value = false }, + OpenOnClick = { Value = false }, }, new Container { @@ -437,7 +442,7 @@ namespace osu.Game.Overlays.Profile } } }, - headerDetailContainer = new Container + new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -447,7 +452,7 @@ namespace osu.Game.Overlays.Profile { RelativeSizeAxes = Axes.Both, }, - new FillFlowContainer + headerDetailContainer = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -539,11 +544,82 @@ namespace osu.Game.Overlays.Profile } } } - } + }, } }, } - } + }, + badgeContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Alpha = 0, + Children = new Drawable[] + { + headerBadgeBox = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new Container //artificial shadow + { + RelativeSizeAxes = Axes.X, + Height = 3, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new ColourInfo + { + TopLeft = Color4.Black.Opacity(0.2f), + TopRight = Color4.Black.Opacity(0.2f), + BottomLeft = Color4.Black.Opacity(0), + BottomRight = Color4.Black.Opacity(0) + } + }, + }, + badgeFlowContainer = new FillFlowContainer + { + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = 5 }, + Spacing = new Vector2(10, 10), + Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 }, + } + } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + headerBottomBox = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 }, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + bottomTopLinkContainer = new LinkFlowContainer(text => text.TextSize = 12) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + bottomLinkContainer = new LinkFlowContainer(text => text.TextSize = 12) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } + } + } + } + }, } } }; @@ -557,6 +633,8 @@ namespace osu.Game.Overlays.Profile DetailsVisible.ValueChanged += newValue => headerDetailContainer.Alpha = newValue ? 0 : 1; } + private Color4 communityUserGrayGreenLighter; + [BackgroundDependencyLoader(true)] private void load(OsuColour colours, TextureStore textures) { @@ -583,9 +661,12 @@ namespace osu.Game.Overlays.Profile detailGlobalRank.LineColour = colours.Yellow; detailCountryRank.LineColour = colours.Yellow; - } - private readonly OsuSpriteText usernameText; + headerBadgeBox.Colour = colours.CommunityUserGrayGreenDarkest; + headerBottomBox.Colour = colours.CommunityUserGrayGreenDarker; + + communityUserGrayGreenLighter = colours.CommunityUserGrayGreenLighter; + } private User user; @@ -683,67 +764,68 @@ namespace osu.Game.Overlays.Profile rankGraph.User.Value = user; - /* - if (!string.IsNullOrEmpty(user.Colour)) + var badges = User.Badges; + if (badges.Length > 0) { - colourBar.Colour = OsuColour.FromHex(user.Colour); - colourBar.Show(); + badgeContainer.Show(); + for (var index = 0; index < badges.Length; index++) + { + int displayIndex = index; + LoadComponentAsync(new DrawableBadge(badges[index]), asyncBadge => + { + badgeFlowContainer.Add(asyncBadge); + + // load in stable order regardless of async load order. + badgeFlowContainer.SetLayoutPosition(asyncBadge, displayIndex); + }); + } } - void boldItalic(SpriteText t) => t.Font = @"Exo2.0-BoldItalic"; - void lightText(SpriteText t) => t.Alpha = 0.8f; - - OsuSpriteText createScoreText(string text) => new OsuSpriteText - { - TextSize = 14, - Text = text - }; - - OsuSpriteText createScoreNumberText(string text) => new OsuSpriteText - { - TextSize = 14, - Font = @"Exo2.0-Bold", - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Text = text - }; - - if (user.Country != null) - { - infoTextLeft.AddText("From ", lightText); - infoTextLeft.AddText(user.Country.FullName, boldItalic); - countryFlag.Country = user.Country; - } - - infoTextLeft.NewParagraph(); + void bold(SpriteText t) => t.Font = @"Exo2.0-Bold"; + void addSpacer(OsuTextFlowContainer textFlow) => textFlow.AddArbitraryDrawable(new Container { Width = 15 }); if (user.JoinDate.ToUniversalTime().Year < 2008) { - infoTextLeft.AddText(new DrawableJoinDate(user.JoinDate), lightText); + bottomTopLinkContainer.AddText("Here since the beginning"); } else { - infoTextLeft.AddText("Joined ", lightText); - infoTextLeft.AddText(new DrawableJoinDate(user.JoinDate), boldItalic); + bottomTopLinkContainer.AddText("Joined "); + bottomTopLinkContainer.AddText(new DrawableDate(user.JoinDate), bold); } + addSpacer(bottomTopLinkContainer); + if (user.LastVisit.HasValue) { - infoTextLeft.NewLine(); - infoTextLeft.AddText("Last seen ", lightText); - infoTextLeft.AddText(new DrawableDate(user.LastVisit.Value), boldItalic); - infoTextLeft.NewParagraph(); + bottomTopLinkContainer.AddText("Last seen "); + bottomTopLinkContainer.AddText(new DrawableDate(user.LastVisit.Value), bold); } - if (user.PlayStyle?.Length > 0) + addSpacer(bottomTopLinkContainer); + + bottomTopLinkContainer.AddText("Contributed "); + bottomTopLinkContainer.AddLink($@"{user.PostCount} forum posts", $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: bold); + + void tryAddInfo(FontAwesome icon, string content, string link = null) { - infoTextLeft.AddText("Plays with ", lightText); - infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic); - } + if (string.IsNullOrEmpty(content)) return; - infoTextLeft.NewLine(); - infoTextLeft.AddText("Contributed ", lightText); - infoTextLeft.AddLink($@"{user.PostCount} forum posts", url: $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: boldItalic); + bottomLinkContainer.AddIcon(icon, text => + { + text.TextSize = 10; + text.Colour = communityUserGrayGreenLighter; + }); + if (link != null) + { + bottomLinkContainer.AddLink(" " + content, link, creationParameters: bold); + } + else + { + bottomLinkContainer.AddText(" " + content, bold); + } + addSpacer(bottomLinkContainer); + } string websiteWithoutProtcol = user.Website; if (!string.IsNullOrEmpty(websiteWithoutProtcol)) @@ -753,185 +835,16 @@ namespace osu.Game.Overlays.Profile websiteWithoutProtcol = websiteWithoutProtcol.Substring(protocolIndex + 2); } - tryAddInfoRightLine(FontAwesome.fa_map_marker, user.Location); - tryAddInfoRightLine(FontAwesome.fa_heart_o, user.Interests); - tryAddInfoRightLine(FontAwesome.fa_suitcase, user.Occupation); - infoTextRight.NewParagraph(); + tryAddInfo(FontAwesome.fa_map_marker, user.Location); + tryAddInfo(FontAwesome.fa_heart_o, user.Interests); + tryAddInfo(FontAwesome.fa_suitcase, user.Occupation); + bottomLinkContainer.NewLine(); if (!string.IsNullOrEmpty(user.Twitter)) - tryAddInfoRightLine(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}"); - tryAddInfoRightLine(FontAwesome.fa_gamepad, user.Discord); - tryAddInfoRightLine(FontAwesome.fa_skype, user.Skype, @"skype:" + user.Skype + @"?chat"); - tryAddInfoRightLine(FontAwesome.fa_lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}"); - tryAddInfoRightLine(FontAwesome.fa_globe, websiteWithoutProtcol, user.Website); - - if (user.Statistics != null) - { - levelBadge.Show(); - levelText.Text = user.Statistics.Level.Current.ToString(); - - scoreText.Add(createScoreText("Ranked Score")); - scoreNumberText.Add(createScoreNumberText(user.Statistics.RankedScore.ToString(@"#,0"))); - scoreText.Add(createScoreText("Accuracy")); - scoreNumberText.Add(createScoreNumberText($"{user.Statistics.Accuracy:0.##}%")); - scoreText.Add(createScoreText("Play Count")); - scoreNumberText.Add(createScoreNumberText(user.Statistics.PlayCount.ToString(@"#,0"))); - scoreText.Add(createScoreText("Total Score")); - scoreNumberText.Add(createScoreNumberText(user.Statistics.TotalScore.ToString(@"#,0"))); - scoreText.Add(createScoreText("Total Hits")); - scoreNumberText.Add(createScoreNumberText(user.Statistics.TotalHits.ToString(@"#,0"))); - scoreText.Add(createScoreText("Max Combo")); - scoreNumberText.Add(createScoreNumberText(user.Statistics.MaxCombo.ToString(@"#,0"))); - scoreText.Add(createScoreText("Replays Watched by Others")); - scoreNumberText.Add(createScoreNumberText(user.Statistics.ReplaysWatched.ToString(@"#,0"))); - - gradeSSPlus.DisplayCount = user.Statistics.GradesCount.SSPlus; - gradeSSPlus.Show(); - gradeSS.DisplayCount = user.Statistics.GradesCount.SS; - gradeSS.Show(); - gradeSPlus.DisplayCount = user.Statistics.GradesCount.SPlus; - gradeSPlus.Show(); - gradeS.DisplayCount = user.Statistics.GradesCount.S; - gradeS.Show(); - gradeA.DisplayCount = user.Statistics.GradesCount.A; - gradeA.Show(); - - rankGraph.User.Value = user; - } - - badgeContainer.ShowBadges(user.Badges);*/ - } - - private class CoverInfoTabControl : TabControl - { - private readonly Box bar; - - private Color4 accentColour; - public Color4 AccentColour - { - get => accentColour; - set - { - if (accentColour == value) return; - - accentColour = value; - - bar.Colour = value; - - foreach (TabItem tabItem in TabContainer) - { - ((CoverInfoTabItem)tabItem).AccentColour = value; - } - } - } - - public MarginPadding Padding - { - set => TabContainer.Padding = value; - get => TabContainer.Padding; - } - - public CoverInfoTabControl() - { - TabContainer.Masking = false; - TabContainer.Spacing = new Vector2(20, 0); - - AddInternal(bar = new Box - { - RelativeSizeAxes = Axes.X, - Height = 2, - Anchor = Anchor.BottomLeft, - Origin = Anchor.CentreLeft - }); - } - - protected override Dropdown CreateDropdown() => null; - - protected override TabItem CreateTabItem(string value) => new CoverInfoTabItem(value) - { - AccentColour = AccentColour - }; - - private class CoverInfoTabItem : TabItem - { - private readonly OsuSpriteText text; - private readonly Drawable bar; - - private Color4 accentColour; - public Color4 AccentColour - { - get => accentColour; - set - { - accentColour = value; - - bar.Colour = value; - if (!Active) text.Colour = value; - } - } - - public CoverInfoTabItem(string value) - : base(value) - { - AutoSizeAxes = Axes.X; - RelativeSizeAxes = Axes.Y; - - Children = new[] - { - text = new OsuSpriteText - { - Margin = new MarginPadding { Bottom = 15 }, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Text = value, - TextSize = 14, - Font = "Exo2.0-Bold", - }, - bar = new Circle - { - RelativeSizeAxes = Axes.X, - Height = 0, - Origin = Anchor.CentreLeft, - Anchor = Anchor.BottomLeft, - }, - new HoverClickSounds() - }; - } - - protected override bool OnHover(HoverEvent e) - { - if (!Active) - onActivated(true); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - base.OnHoverLost(e); - - if (!Active) - OnDeactivated(); - } - - protected override void OnActivated() - { - onActivated(); - } - - protected override void OnDeactivated() - { - text.FadeColour(AccentColour, 120, Easing.InQuad); - bar.ResizeHeightTo(0, 120, Easing.InQuad); - text.Font = "Exo2.0-Medium"; - } - - private void onActivated(bool fake = false) - { - text.FadeColour(Color4.White, 120, Easing.InQuad); - bar.ResizeHeightTo(7.5f, 120, Easing.InQuad); - if (!fake) - text.Font = "Exo2.0-Bold"; - } - } + tryAddInfo(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}"); + tryAddInfo(FontAwesome.fa_gamepad, user.Discord); //todo: update fontawesome to include discord logo + tryAddInfo(FontAwesome.fa_skype, user.Skype, @"skype:" + user.Skype + @"?chat"); + tryAddInfo(FontAwesome.fa_lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}"); + tryAddInfo(FontAwesome.fa_link, websiteWithoutProtcol, user.Website); } private class UserStatsLine : Container @@ -1108,5 +1021,37 @@ namespace osu.Game.Overlays.Profile rankSprite.Texture = textures.Get($"Grades/{rank.GetDescription()}"); } } + + private class DrawableBadge : CompositeDrawable, IHasTooltip + { + public static readonly Vector2 DRAWABLE_BADGE_SIZE = new Vector2(86, 40); + + private readonly Badge badge; + + public DrawableBadge(Badge badge) + { + this.badge = badge; + Size = DRAWABLE_BADGE_SIZE; + } + + [BackgroundDependencyLoader] + private void load(LargeTextureStore textures) + { + InternalChild = new Sprite + { + FillMode = FillMode.Fit, + RelativeSizeAxes = Axes.Both, + Texture = textures.Get(badge.ImageUrl), + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + InternalChild.FadeInFromZero(200); + } + + public string TooltipText => badge.Description; + } } } From da99161736610b1dba44962f8e03bd87a7efd462 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sat, 22 Dec 2018 22:31:11 +0100 Subject: [PATCH 0321/2854] add some missing stuff --- osu.Game/Graphics/UserInterface/LineGraph.cs | 1 - .../Overlays/Profile/Header/SupporterIcon.cs | 1 - osu.Game/Overlays/Profile/ProfileHeader.cs | 44 ++++++++++++++++--- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/LineGraph.cs b/osu.Game/Graphics/UserInterface/LineGraph.cs index c750f7a89d..4948c0d7f4 100644 --- a/osu.Game/Graphics/UserInterface/LineGraph.cs +++ b/osu.Game/Graphics/UserInterface/LineGraph.cs @@ -111,7 +111,6 @@ namespace osu.Game.Graphics.UserInterface { float x = (i + count - values.Length) / (float)(count - 1) * DrawWidth - 1; float y = GetYPosition(values[i]) * DrawHeight - path.PathWidth; - // the -1 is for inner offset in path (actually -PathWidth) path.AddVertex(new Vector2(x, y)); } } diff --git a/osu.Game/Overlays/Profile/Header/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/SupporterIcon.cs index 8b33a60d37..03135824de 100644 --- a/osu.Game/Overlays/Profile/Header/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/SupporterIcon.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; -using osuTK; namespace osu.Game.Overlays.Profile.Header { diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 6ab178cfe1..1a51ab4cb6 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Extensions; @@ -39,6 +40,7 @@ namespace osu.Game.Overlays.Profile private readonly Box headerTopBox; private readonly UpdateableAvatar avatar; private readonly OsuSpriteText usernameText; + private readonly ExternalLinkButton openUserExternally; private readonly OsuSpriteText titleText; private readonly DrawableFlag userFlag; private readonly OsuSpriteText userCountryText; @@ -76,6 +78,13 @@ namespace osu.Game.Overlays.Profile private const float cover_height = 150; private const float cover_info_height = 75; private const float avatar_size = 110; + private static readonly Dictionary play_styles = new Dictionary + { + {"keyboard", "Keyboard"}, + {"mouse", "Mouse"}, + {"tablet", "Tablet"}, + {"touch", "Touch Screen"}, + }; [Resolved(CanBeNull = true)] private ChannelManager channelManager { get; set; } @@ -195,10 +204,24 @@ namespace osu.Game.Overlays.Profile Padding = new MarginPadding { Left = 10 }, Children = new Drawable[] { - usernameText = new OsuSpriteText + new FillFlowContainer { - Font = "Exo2.0-Regular", - TextSize = 24 + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + usernameText = new OsuSpriteText + { + Font = "Exo2.0-Regular", + TextSize = 24 + }, + openUserExternally = new ExternalLinkButton + { + Margin = new MarginPadding { Left = 5 }, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + } }, new FillFlowContainer { @@ -694,6 +717,7 @@ namespace osu.Game.Overlays.Profile avatar.User = User; usernameText.Text = user.Username; + openUserExternally.Link = $@"https://osu.ppy.sh/users/{user.Id}"; userFlag.Country = user.Country; userCountryText.Text = user.Country?.FullName; SupporterTag.SupporterLevel = user.SupportLevel; @@ -796,16 +820,24 @@ namespace osu.Game.Overlays.Profile addSpacer(bottomTopLinkContainer); + if (user.PlayStyle?.Length > 0) + { + bottomTopLinkContainer.AddText("Plays with "); + bottomTopLinkContainer.AddText(string.Join(", ", user.PlayStyle.Select(style => play_styles[style])), bold); + + addSpacer(bottomTopLinkContainer); + } + if (user.LastVisit.HasValue) { bottomTopLinkContainer.AddText("Last seen "); bottomTopLinkContainer.AddText(new DrawableDate(user.LastVisit.Value), bold); + + addSpacer(bottomTopLinkContainer); } - addSpacer(bottomTopLinkContainer); - bottomTopLinkContainer.AddText("Contributed "); - bottomTopLinkContainer.AddLink($@"{user.PostCount} forum posts", $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: bold); + bottomTopLinkContainer.AddLink($@"{user.PostCount:#,##0} forum posts", $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: bold); void tryAddInfo(FontAwesome icon, string content, string link = null) { From 360c17e2c76b43525f67b159ff5c3652248543e6 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sat, 22 Dec 2018 22:50:19 +0100 Subject: [PATCH 0322/2854] appease CodeFactor and AppVeyor --- osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs | 2 +- osu.Game/Overlays/Profile/Header/RankGraph.cs | 6 ++---- osu.Game/Overlays/Profile/ProfileHeader.cs | 1 - 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs index b067273b04..2576d627ea 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs +++ b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics.UserInterface; using osuTK; using osuTK.Graphics; -namespace osu.Game.Overlays.Profile +namespace osu.Game.Overlays.Profile.Header { public class ProfileHeaderTabControl : TabControl { diff --git a/osu.Game/Overlays/Profile/Header/RankGraph.cs b/osu.Game/Overlays/Profile/Header/RankGraph.cs index f67a8e47d4..76a72fe420 100644 --- a/osu.Game/Overlays/Profile/Header/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/RankGraph.cs @@ -190,19 +190,17 @@ namespace osu.Game.Overlays.Profile.Header public string TooltipText => User.Value?.Statistics?.Ranks.Global == null ? "" : $"{ranks[dayIndex].Value:#,##0}|{ranked_days - ranks[dayIndex].Key + 1}"; - public ITooltip GetCustomTooltip() => new RankGraphTooltip(this); + public ITooltip GetCustomTooltip() => new RankGraphTooltip(); public class RankGraphTooltip : VisibilityContainer, ITooltip { - private readonly RankGraph graph; private readonly OsuSpriteText globalRankingText, timeText; private readonly Box background; public string TooltipText { get; set; } - public RankGraphTooltip(RankGraph graph) + public RankGraphTooltip() { - this.graph = graph; AutoSizeAxes = Axes.Both; Masking = true; CornerRadius = 10; diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 1a51ab4cb6..77074abd09 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -353,7 +353,6 @@ namespace osu.Game.Overlays.Profile }, } }, - } }, new Container From e05fbd4136e338537e4ec6f9d64f958413b0f705 Mon Sep 17 00:00:00 2001 From: jorolf Date: Tue, 25 Dec 2018 01:09:49 +0100 Subject: [PATCH 0323/2854] address some comments and improve ui --- .../Overlays/Profile/Header/ProfileHeaderTabControl.cs | 2 +- osu.Game/Overlays/Profile/Header/RankGraph.cs | 2 ++ osu.Game/Overlays/Profile/ProfileHeader.cs | 10 +++++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs index 2576d627ea..db8a0b594c 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs +++ b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs @@ -38,8 +38,8 @@ namespace osu.Game.Overlays.Profile.Header public MarginPadding Padding { - set => TabContainer.Padding = value; get => TabContainer.Padding; + set => TabContainer.Padding = value; } public ProfileHeaderTabControl() diff --git a/osu.Game/Overlays/Profile/Header/RankGraph.cs b/osu.Game/Overlays/Profile/Header/RankGraph.cs index 76a72fe420..e681d2701a 100644 --- a/osu.Game/Overlays/Profile/Header/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/RankGraph.cs @@ -234,6 +234,8 @@ namespace osu.Game.Overlays.Profile.Header { Font = "Exo2.0-Regular", TextSize = 12, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, } } }, diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 77074abd09..7429a65210 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -20,6 +20,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; using osu.Game.Online.Chat; using osu.Game.Overlays.Profile.Header; using osu.Game.Scoring; @@ -95,6 +96,9 @@ namespace osu.Game.Overlays.Profile [Resolved(CanBeNull = true)] private ChatOverlay chatOverlay { get; set; } + [Resolved] + private APIAccess apiAccess { get; set; } + public ProfileHeader() { Container expandedDetailContainer; @@ -340,6 +344,7 @@ namespace osu.Game.Overlays.Profile }, messageButton = new ProfileHeaderButton { + Alpha = 0, RelativeSizeAxes = Axes.Y, Children = new Drawable[] { @@ -734,13 +739,16 @@ namespace osu.Game.Overlays.Profile followerText.Text = user.FollowerCount?.Length > 0 ? user.FollowerCount[0].ToString("#,##0") : "0"; - if (!user.PMFriendsOnly) + if (!user.PMFriendsOnly && apiAccess.LocalUser.Value.Id != user.Id) + { + messageButton.Show(); messageButton.Action = () => { channelManager?.OpenPrivateChannel(user); userOverlay?.Hide(); chatOverlay?.Show(); }; + } expandButton.Action = DetailsVisible.Toggle; From a33a1458a58bc7f65eb281d06c9691316b0e308f Mon Sep 17 00:00:00 2001 From: jorolf Date: Fri, 4 Jan 2019 22:52:00 +0100 Subject: [PATCH 0324/2854] update design and make play styles an enum --- osu.Game/Overlays/Profile/ProfileHeader.cs | 24 ++++++++++----------- osu.Game/Users/User.cs | 25 ++++++++++++++++++++-- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 7429a65210..e6d892f0b7 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -75,6 +75,7 @@ namespace osu.Game.Overlays.Profile private readonly Box headerBottomBox; private readonly LinkFlowContainer bottomTopLinkContainer; private readonly LinkFlowContainer bottomLinkContainer; + private Color4 linkBlue; private const float cover_height = 150; private const float cover_info_height = 75; @@ -693,6 +694,7 @@ namespace osu.Game.Overlays.Profile headerBottomBox.Colour = colours.CommunityUserGrayGreenDarker; communityUserGrayGreenLighter = colours.CommunityUserGrayGreenLighter; + linkBlue = colours.BlueLight; } private User user; @@ -827,10 +829,10 @@ namespace osu.Game.Overlays.Profile addSpacer(bottomTopLinkContainer); - if (user.PlayStyle?.Length > 0) + if (user.PlayStyles?.Length > 0) { bottomTopLinkContainer.AddText("Plays with "); - bottomTopLinkContainer.AddText(string.Join(", ", user.PlayStyle.Select(style => play_styles[style])), bold); + bottomTopLinkContainer.AddText(string.Join(", ", user.PlayStyles.Select(style => style.GetDescription())), bold); addSpacer(bottomTopLinkContainer); } @@ -857,7 +859,11 @@ namespace osu.Game.Overlays.Profile }); if (link != null) { - bottomLinkContainer.AddLink(" " + content, link, creationParameters: bold); + bottomLinkContainer.AddLink(" " + content, link, creationParameters: text => + { + bold(text); + text.Colour = linkBlue; + }); } else { @@ -888,8 +894,6 @@ namespace osu.Game.Overlays.Profile private class UserStatsLine : Container { - private readonly OsuSpriteText rightText; - public UserStatsLine(string left, string right) { RelativeSizeAxes = Axes.X; @@ -902,22 +906,16 @@ namespace osu.Game.Overlays.Profile Text = left, Font = "Exo2.0-Medium" }, - rightText = new OsuSpriteText + new OsuSpriteText { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, TextSize = 15, Text = right, - Font = "Exo2.0-Medium" + Font = "Exo2.0-Bold" }, }; } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - rightText.Colour = colours.BlueLight; - } } private class ProfileHeaderButton : OsuHoverContainer diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 485c953b75..1005b094aa 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -2,7 +2,11 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.ComponentModel; +using System.Linq; +using System.Security.Cryptography; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using osu.Framework.Configuration; namespace osu.Game.Users @@ -113,8 +117,13 @@ namespace osu.Game.Users [JsonProperty(@"follower_count")] public int[] FollowerCount; - [JsonProperty(@"playstyle")] - public string[] PlayStyle; + [JsonProperty] + private string[] playstyle + { + set { PlayStyles = value?.Select(str => Enum.Parse(typeof(PlayStyle), str, true)).Cast().ToArray(); } + } + + public PlayStyle[] PlayStyles; [JsonProperty(@"playmode")] public string PlayMode; @@ -174,5 +183,17 @@ namespace osu.Game.Users Username = "system", Id = 0 }; + + public enum PlayStyle + { + [Description("Keyboard")] + Keyboard, + [Description("Mouse")] + Mouse, + [Description("Tablet")] + Tablet, + [Description("Touch Screen")] + Touch, + } } } From 8bab87c072e96ef0ca2784d5ad6716ef7709994e Mon Sep 17 00:00:00 2001 From: jorolf Date: Fri, 4 Jan 2019 23:09:21 +0100 Subject: [PATCH 0325/2854] update resources and remove unused usings --- osu-resources | 2 +- osu.Game/Users/User.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu-resources b/osu-resources index 9880089b4e..677897728f 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 9880089b4e8fcd78d68f30c8a40d43bf8dccca86 +Subproject commit 677897728f4332fa1200e0280ca02c4b987c6c47 diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 1005b094aa..f90781df77 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -4,9 +4,7 @@ using System; using System.ComponentModel; using System.Linq; -using System.Security.Cryptography; using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using osu.Framework.Configuration; namespace osu.Game.Users From ddce608f97977d5dd6b5dd7ebf8eaff4ac9cda6e Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Sat, 5 Jan 2019 22:15:09 -0600 Subject: [PATCH 0326/2854] Setup osu.Android solution and its ruleset tests --- osu.Android.sln | 124 ++++++++++++++++++ osu.Android/GameView.cs | 25 ---- osu.Android/OsuGameActivity.cs | 17 +++ osu.Android/osu.Android.csproj | 23 +--- .../Assets/AboutAssets.txt | 19 +++ .../MainActivity.cs | 14 +- .../Properties/AndroidManifest.xml | 9 ++ .../Properties/AssemblyInfo.cs | 30 +++++ .../Resources/AboutResources.txt | 0 .../Resources/Resource.designer.cs | 112 ++++++++++++++++ .../Resources/layout/activity_main.axml | 6 - .../mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../Resources/mipmap-hdpi/ic_launcher.png | Bin 0 -> 1634 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 1441 bytes .../mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 3552 bytes .../Resources/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1362 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 958 bytes .../mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2413 bytes .../Resources/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 2307 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 2056 bytes .../mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 4858 bytes .../Resources/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 3871 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 3403 bytes .../mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 8001 bytes .../Resources/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 5016 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 4889 bytes .../mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 10893 bytes .../Resources/values/Strings.xml | 4 + .../Resources/values/colors.xml | 6 + .../values/ic_launcher_background.xml | 4 + .../Resources/values/styles.xml | 0 ...u.Game.Rulesets.Catch.Tests.Android.csproj | 108 +++++++++++++++ .../Assets/AboutAssets.txt | 19 +++ .../MainActivity.cs | 19 +++ .../Properties/AndroidManifest.xml | 9 ++ .../Properties/AssemblyInfo.cs | 30 +++++ .../Resources/AboutResources.txt | 44 +++++++ .../Resources/Resource.designer.cs | 112 ++++++++++++++++ .../Resources/layout/activity_main.axml | 7 + .../mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../Resources/mipmap-hdpi/ic_launcher.png | Bin 0 -> 1634 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 1441 bytes .../mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 3552 bytes .../Resources/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1362 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 958 bytes .../mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2413 bytes .../Resources/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 2307 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 2056 bytes .../mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 4858 bytes .../Resources/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 3871 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 3403 bytes .../mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 8001 bytes .../Resources/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 5016 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 4889 bytes .../mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 10893 bytes .../Resources/values/Strings.xml | 4 + .../Resources/values/colors.xml | 6 + .../values/ic_launcher_background.xml | 4 + .../Resources/values/styles.xml | 11 ++ ...u.Game.Rulesets.Mania.Tests.Android.csproj | 105 +++++++++++++++ .../Assets/AboutAssets.txt | 19 +++ .../MainActivity.cs | 19 +++ .../Properties/AndroidManifest.xml | 9 ++ .../Properties/AssemblyInfo.cs | 30 +++++ .../Resources/AboutResources.txt | 44 +++++++ .../Resources/Resource.designer.cs | 112 ++++++++++++++++ .../Resources/layout/activity_main.axml | 7 + .../mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../Resources/mipmap-hdpi/ic_launcher.png | Bin 0 -> 1634 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 1441 bytes .../mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 3552 bytes .../Resources/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1362 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 958 bytes .../mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2413 bytes .../Resources/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 2307 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 2056 bytes .../mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 4858 bytes .../Resources/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 3871 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 3403 bytes .../mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 8001 bytes .../Resources/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 5016 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 4889 bytes .../mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 10893 bytes .../Resources/values/Strings.xml | 2 +- .../Resources/values/colors.xml | 6 + .../values/ic_launcher_background.xml | 4 + .../Resources/values/styles.xml | 11 ++ ...osu.Game.Rulesets.Osu.Tests.Android.csproj | 105 +++++++++++++++ .../Assets/AboutAssets.txt | 19 +++ .../MainActivity.cs | 19 +++ .../Properties/AndroidManifest.xml | 9 ++ .../Properties/AssemblyInfo.cs | 30 +++++ .../Resources/AboutResources.txt | 44 +++++++ .../Resources/Resource.designer.cs | 112 ++++++++++++++++ .../Resources/layout/activity_main.axml | 7 + .../mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../Resources/mipmap-hdpi/ic_launcher.png | Bin 0 -> 1634 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 1441 bytes .../mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 3552 bytes .../Resources/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1362 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 958 bytes .../mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2413 bytes .../Resources/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 2307 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 2056 bytes .../mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 4858 bytes .../Resources/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 3871 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 3403 bytes .../mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 8001 bytes .../Resources/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 5016 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 4889 bytes .../mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 10893 bytes .../Resources/values/Strings.xml | 4 + .../Resources/values/colors.xml | 6 + .../values/ic_launcher_background.xml | 4 + .../Resources/values/styles.xml | 11 ++ ...u.Game.Rulesets.Taiko.Tests.Android.csproj | 105 +++++++++++++++ 120 files changed, 1591 insertions(+), 58 deletions(-) create mode 100644 osu.Android.sln delete mode 100644 osu.Android/GameView.cs create mode 100644 osu.Android/OsuGameActivity.cs create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Assets/AboutAssets.txt rename {osu.Android => osu.Game.Rulesets.Catch.Tests.Android}/MainActivity.cs (61%) create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Properties/AssemblyInfo.cs rename {osu.Android => osu.Game.Rulesets.Catch.Tests.Android}/Resources/AboutResources.txt (100%) create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/Resource.designer.cs rename {osu.Android => osu.Game.Rulesets.Catch.Tests.Android}/Resources/layout/activity_main.axml (59%) create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-mdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xhdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/values/Strings.xml create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/values/colors.xml create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/values/ic_launcher_background.xml rename {osu.Android => osu.Game.Rulesets.Catch.Tests.Android}/Resources/values/styles.xml (100%) create mode 100644 osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Assets/AboutAssets.txt create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Properties/AssemblyInfo.cs create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/AboutResources.txt create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/Resource.designer.cs create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/layout/activity_main.axml create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-mdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xhdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/values/Strings.xml create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/values/colors.xml create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/values/ic_launcher_background.xml create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/values/styles.xml create mode 100644 osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Assets/AboutAssets.txt create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Properties/AssemblyInfo.cs create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/AboutResources.txt create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/Resource.designer.cs create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/layout/activity_main.axml create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-mdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xhdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png rename {osu.Android => osu.Game.Rulesets.Osu.Tests.Android}/Resources/values/Strings.xml (51%) create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/values/colors.xml create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/values/ic_launcher_background.xml create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/values/styles.xml create mode 100644 osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Assets/AboutAssets.txt create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Properties/AssemblyInfo.cs create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/AboutResources.txt create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/Resource.designer.cs create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/layout/activity_main.axml create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-mdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xhdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher.png create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/Strings.xml create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/colors.xml create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/ic_launcher_background.xml create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/styles.xml create mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj diff --git a/osu.Android.sln b/osu.Android.sln new file mode 100644 index 0000000000..11c0f3cb27 --- /dev/null +++ b/osu.Android.sln @@ -0,0 +1,124 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2006 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game", "osu.Game\osu.Game.csproj", "{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Resources", "osu-resources\osu.Game.Resources\osu.Game.Resources.csproj", "{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Osu", "osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj", "{C92A607B-1FDD-4954-9F92-03FF547D9080}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Catch", "osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj", "{58F6C80C-1253-4A0E-A465-B8C85EBEADF3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Taiko", "osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj", "{F167E17A-7DE6-4AF5-B920-A5112296C695}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Mania", "osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj", "{48F4582B-7687-4621-9CBE-5C24197CB536}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Android", "osu.Android\osu.Android.csproj", "{D1D5F9A8-B40B-40E6-B02F-482D03346D3D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Catch.Tests.Android", "osu.Game.Rulesets.Catch.Tests.Android\osu.Game.Rulesets.Catch.Tests.Android.csproj", "{C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Mania.Tests.Android", "osu.Game.Rulesets.Mania.Tests.Android\osu.Game.Rulesets.Mania.Tests.Android.csproj", "{531F1092-DB27-445D-AA33-2A77C7187C99}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Osu.Tests.Android", "osu.Game.Rulesets.Osu.Tests.Android\osu.Game.Rulesets.Osu.Tests.Android.csproj", "{90CAB706-39CB-4B93-9629-3218A6FF8E9B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Taiko.Tests.Android", "osu.Game.Rulesets.Taiko.Tests.Android\osu.Game.Rulesets.Taiko.Tests.Android.csproj", "{3701A0A1-8476-42C6-B5C4-D24129B4A484}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.Build.0 = Release|Any CPU + {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Release|Any CPU.Build.0 = Release|Any CPU + {C92A607B-1FDD-4954-9F92-03FF547D9080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C92A607B-1FDD-4954-9F92-03FF547D9080}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C92A607B-1FDD-4954-9F92-03FF547D9080}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C92A607B-1FDD-4954-9F92-03FF547D9080}.Release|Any CPU.Build.0 = Release|Any CPU + {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Release|Any CPU.Build.0 = Release|Any CPU + {F167E17A-7DE6-4AF5-B920-A5112296C695}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F167E17A-7DE6-4AF5-B920-A5112296C695}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F167E17A-7DE6-4AF5-B920-A5112296C695}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F167E17A-7DE6-4AF5-B920-A5112296C695}.Release|Any CPU.Build.0 = Release|Any CPU + {48F4582B-7687-4621-9CBE-5C24197CB536}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48F4582B-7687-4621-9CBE-5C24197CB536}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.Build.0 = Release|Any CPU + {D1D5F9A8-B40B-40E6-B02F-482D03346D3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1D5F9A8-B40B-40E6-B02F-482D03346D3D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1D5F9A8-B40B-40E6-B02F-482D03346D3D}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {D1D5F9A8-B40B-40E6-B02F-482D03346D3D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1D5F9A8-B40B-40E6-B02F-482D03346D3D}.Release|Any CPU.Build.0 = Release|Any CPU + {D1D5F9A8-B40B-40E6-B02F-482D03346D3D}.Release|Any CPU.Deploy.0 = Release|Any CPU + {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}.Release|Any CPU.Build.0 = Release|Any CPU + {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}.Release|Any CPU.Deploy.0 = Release|Any CPU + {531F1092-DB27-445D-AA33-2A77C7187C99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {531F1092-DB27-445D-AA33-2A77C7187C99}.Debug|Any CPU.Build.0 = Debug|Any CPU + {531F1092-DB27-445D-AA33-2A77C7187C99}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {531F1092-DB27-445D-AA33-2A77C7187C99}.Release|Any CPU.ActiveCfg = Release|Any CPU + {531F1092-DB27-445D-AA33-2A77C7187C99}.Release|Any CPU.Build.0 = Release|Any CPU + {531F1092-DB27-445D-AA33-2A77C7187C99}.Release|Any CPU.Deploy.0 = Release|Any CPU + {90CAB706-39CB-4B93-9629-3218A6FF8E9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {90CAB706-39CB-4B93-9629-3218A6FF8E9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {90CAB706-39CB-4B93-9629-3218A6FF8E9B}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {90CAB706-39CB-4B93-9629-3218A6FF8E9B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {90CAB706-39CB-4B93-9629-3218A6FF8E9B}.Release|Any CPU.Build.0 = Release|Any CPU + {90CAB706-39CB-4B93-9629-3218A6FF8E9B}.Release|Any CPU.Deploy.0 = Release|Any CPU + {3701A0A1-8476-42C6-B5C4-D24129B4A484}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3701A0A1-8476-42C6-B5C4-D24129B4A484}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3701A0A1-8476-42C6-B5C4-D24129B4A484}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {3701A0A1-8476-42C6-B5C4-D24129B4A484}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3701A0A1-8476-42C6-B5C4-D24129B4A484}.Release|Any CPU.Build.0 = Release|Any CPU + {3701A0A1-8476-42C6-B5C4-D24129B4A484}.Release|Any CPU.Deploy.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {671B0BEC-2403-45B0-9357-2C97CC517668} + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + Policies = $0 + $0.TextStylePolicy = $1 + $1.EolMarker = Windows + $1.inheritsSet = VisualStudio + $1.inheritsScope = text/plain + $1.scope = text/x-csharp + $0.CSharpFormattingPolicy = $2 + $2.IndentSwitchSection = True + $2.NewLinesForBracesInProperties = True + $2.NewLinesForBracesInAccessors = True + $2.NewLinesForBracesInAnonymousMethods = True + $2.NewLinesForBracesInControlBlocks = True + $2.NewLinesForBracesInAnonymousTypes = True + $2.NewLinesForBracesInObjectCollectionArrayInitializers = True + $2.NewLinesForBracesInLambdaExpressionBody = True + $2.NewLineForElse = True + $2.NewLineForCatch = True + $2.NewLineForFinally = True + $2.NewLineForMembersInObjectInit = True + $2.NewLineForMembersInAnonymousTypes = True + $2.NewLineForClausesInQuery = True + $2.SpacingAfterMethodDeclarationName = False + $2.SpaceAfterMethodCallName = False + $2.SpaceBeforeOpenSquareBracket = False + $2.inheritsSet = Mono + $2.inheritsScope = text/x-csharp + $2.scope = text/x-csharp + EndGlobalSection +EndGlobal diff --git a/osu.Android/GameView.cs b/osu.Android/GameView.cs deleted file mode 100644 index fc9cdeda53..0000000000 --- a/osu.Android/GameView.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using Android.Content; -using Android.Util; -using osu.Framework.Android; -using osu.Game; - -namespace osu.Android -{ - public class GameView : AndroidGameView - { - public GameView(Context context, IAttributeSet attrs) : - base(context, attrs) - { - CreateGame(); - } - - public GameView(Context context) : base(context) - { - CreateGame(); - } - public override Framework.Game CreateGame() => new OsuGame(); - } -} diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs new file mode 100644 index 0000000000..08f2707044 --- /dev/null +++ b/osu.Android/OsuGameActivity.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Android.App; +using Android.Content.PM; +using osu.Framework.Android; +using osu.Game; + +namespace osu.Android +{ + [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] + public class OsuGameActivity : AndroidGameActivity + { + protected override Framework.Game CreateGame() + => new OsuGame(); + } +} diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 8a481b5239..daff6da961 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -62,8 +62,7 @@ - - + @@ -83,18 +82,12 @@ PreserveNewest - - - Designer - - - @@ -114,6 +107,12 @@ + + 0.0.7879 + + + 0.0.7879 + 0.22.0 @@ -129,14 +128,6 @@ - - {0fd409e8-a359-42bd-bc52-6a4745a122e5} - osu.Framework.Android - - - {0a8ba083-9d08-45ad-8653-796db1653388} - osu.Framework - {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} osu.Game.Resources diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Assets/AboutAssets.txt b/osu.Game.Rulesets.Catch.Tests.Android/Assets/AboutAssets.txt new file mode 100644 index 0000000000..b0633374bd --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests.Android/Assets/AboutAssets.txt @@ -0,0 +1,19 @@ +Any raw assets you want to be deployed with your application can be placed in +this directory (and child directories) and given a Build Action of "AndroidAsset". + +These files will be deployed with you package and will be accessible using Android's +AssetManager, like this: + +public class ReadAsset : Activity +{ + protected override void OnCreate (Bundle bundle) + { + base.OnCreate (bundle); + + InputStream input = Assets.Open ("my_asset.txt"); + } +} + +Additionally, some Android functions will automatically load asset files: + +Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); \ No newline at end of file diff --git a/osu.Android/MainActivity.cs b/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs similarity index 61% rename from osu.Android/MainActivity.cs rename to osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs index ff1fd9eba1..34f10dd16b 100644 --- a/osu.Android/MainActivity.cs +++ b/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs @@ -3,26 +3,20 @@ using Android.App; using Android.OS; +using Android.Support.V7.App; using Android.Runtime; using Android.Widget; -using Android.Views; -using Android.Content.PM; -namespace osu.Android +namespace osu.Game.Rulesets.Catch.Tests.Android { - [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true, ScreenOrientation = ScreenOrientation.Landscape, SupportsPictureInPicture = false)] - public class MainActivity : Activity + [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)] + public class MainActivity : AppCompatActivity { protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); // Set our view from the "main" layout resource SetContentView(Resource.Layout.activity_main); - Window.AddFlags(WindowManagerFlags.KeepScreenOn); - } - public override void OnBackPressed() - { - } } } diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml new file mode 100644 index 0000000000..6c8c0935b2 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Catch.Tests.Android/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..1b1fd64fb5 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests.Android/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Android.App; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("osu.Game.Rulesets.Catch.Tests.Android")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("osu.Game.Rulesets.Catch.Tests.Android")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Android/Resources/AboutResources.txt b/osu.Game.Rulesets.Catch.Tests.Android/Resources/AboutResources.txt similarity index 100% rename from osu.Android/Resources/AboutResources.txt rename to osu.Game.Rulesets.Catch.Tests.Android/Resources/AboutResources.txt diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/Resource.designer.cs b/osu.Game.Rulesets.Catch.Tests.Android/Resources/Resource.designer.cs new file mode 100644 index 0000000000..a19fe391a8 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests.Android/Resources/Resource.designer.cs @@ -0,0 +1,112 @@ +#pragma warning disable 1591 +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +[assembly: global::Android.Runtime.ResourceDesignerAttribute("osu.Game.Rulesets.Catch.Tests.Android.Resource", IsApplication=true)] + +namespace osu.Game.Rulesets.Catch.Tests.Android +{ + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] + public partial class Resource + { + + static Resource() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + public static void UpdateIdValues() + { + } + + public partial class Attribute + { + + static Attribute() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Attribute() + { + } + } + + public partial class Id + { + + // aapt resource value: 0x7f050000 + public const int myButton = 2131034112; + + static Id() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Id() + { + } + } + + public partial class Layout + { + + // aapt resource value: 0x7f030000 + public const int Main = 2130903040; + + static Layout() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Layout() + { + } + } + + public partial class Mipmap + { + + // aapt resource value: 0x7f020000 + public const int Icon = 2130837504; + + static Mipmap() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Mipmap() + { + } + } + + public partial class String + { + + // aapt resource value: 0x7f040001 + public const int app_name = 2130968577; + + // aapt resource value: 0x7f040000 + public const int hello = 2130968576; + + static String() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private String() + { + } + } + } +} +#pragma warning restore 1591 diff --git a/osu.Android/Resources/layout/activity_main.axml b/osu.Game.Rulesets.Catch.Tests.Android/Resources/layout/activity_main.axml similarity index 59% rename from osu.Android/Resources/layout/activity_main.axml rename to osu.Game.Rulesets.Catch.Tests.Android/Resources/layout/activity_main.axml index fc0060857f..ff7a60eb50 100644 --- a/osu.Android/Resources/layout/activity_main.axml +++ b/osu.Game.Rulesets.Catch.Tests.Android/Resources/layout/activity_main.axml @@ -4,10 +4,4 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> - \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..036d09bc5f --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000..036d09bc5f --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..2531cb31efc3a0a3de6113ab9c7845dc1d9654e4 GIT binary patch literal 1634 zcmV-o2A%ndP)B+Z3$1(8#|f~9B42Y^N-3=o2YCq0YUY$Zu=pM;#hG{lHi%n~Vh z1d1vN#EDO19X?u$`cV z!a}AKG@Bb*#1cdYg8af_;jP69b`k%G1n?0=F^8bI^o>wg-vEliK^U}y^!D|^p|ax; zC|pK=f+FHp!RUAhtlpGGUxJb|wm^5! z<1r%$<$TR02wajxKZ4MiR#aAxDLE(##UNyD|ABr4WoGRF*?@e^2|~Hq(gurSSJH*;Q~5lw{J5A_(PCXBWhzZE${qgzv0{dk-F( z1<}>r181tLiEla&f1j&?p2xjbfp2cTt-c1Ox~?9EhK9`cJ9Vatf)loIoQ@#h&}cIGD>Z#QLE}&(bMo@7Ff|7f#Nm^$PJpVcbj+v~K7wfVwF}=) zRQsc+`=A-+C)vrRvaIC-5u>|;3h z*G4-u#RI<_vuSN~vZ6{|I~q5FFk3%de#+*>UFG>&bq6~ zUEMZ~FIOmFO=kA^5rkp-Msw?^63xvdXVZ-rv@{6{iVO}M!}^Je%2BPbi+(L<5<%~h z2v^D+f<|j%7~cJjOzg*!GPQ{%uE{i%YgcZhuZh{yNlQ}RhaU1jd=K+AopVKP+D}&} zZ3y$llqZiln=Z_A$!qzkGbX0D{?l(v5@1|`QyCvCnQ`eKI>|zj_zo%y#fKf85VhQ} zP)y&j4P*nR3q{-o35iV6nx7QDqq<;WDVIt}|N%`!dgv*y3va8eLNj zU9x(?ieweHfQ*yXk8|=ssZ~qJEz^QoKJ|iGa>ge_Vm_8l}S+UvJ{8g4jr+o#aTSFsz1W;PDP zW765JXGU#3JL>SlIl3NRV2{7B2dLO1cIP)a4ZRYL|MBD36O1#oSgAf}APz5@;x=_U-<=y)Py7*}O5(uu7BL_eLe6Ek7pH|G zMq)FrF1EFq&yruS5b=F=w)fVVoPd(oeRyTFym_Uwyn~L=OL(O?cf^2L5R(SmjORx6 z%nmZf^W=3pkvT*>@osUNi>DULH1hL;y`JGQX$onRCr_U0=H~Viodq!<7Q{3rPk~{G gu#IhOV;e2n|1(WJB~7~kivR!s07*qoM6N<$g7lUVaR2}S literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..7a859c25556af7a2e46e22a2220eaded55628e9f GIT binary patch literal 1441 zcma)+`#%#30L6FjMQg%tuA0%p#0??L`*E=rD#U2F4L5n@F+O9Sp;(QwEQy7+?sX?r zCWN(!Hg`+j5k8*H@|yQEtnAi*(D{7M`Tlf1=eKjq)BUsp2nqrK01B=yNUv`!`EH=x zx8$xJQUd^Fuec%|(TT&0V}4orr_==mmCnEuzD+ff8Pg>pJRqsWsD{#?eGPaCu0(sEH_2RG@<6-Nt<8 ztPMUmmAz9Ga$23Y9~p9dqJSgJJ#Jk_r@o13^%d-Xf46i+Lrmz3 zy9(DUDVXj;Zny7nO+yn&W2flEX=C!8&D0zI`G# z8;XmlonoghgRFUY*$+7pPLa}Uy)onw>TT9t(FTV6#BV8&lXWDPRvQW_n~xZ|yLcZjX>m$Eaf1)dwXS`&E^ zkNjO;%;fWywchc=+w4utQ0Vbn%B>b~yy4I#D{?1!P`$P>Wdo+ljCo(tYia04JTc=$$u+IbzDVPFYpm8+AQj+ zGKH zfS{{hN%W)kF+(26oZpkURD5Q_G_z97F6{Jval+TOj-;5y)*Rdo3a$^^k~q5gpTzmp1q@+2X9O z;_VUF>;s~C1~gpFrFoh?{aQ|LlBIYz!z^P~lndX5-ES)p#+9GW*|-WBTzQ*&gKOE` zM##bUaWl`6rZBXw0!~_oUhf+H$tNc@lLZCj0bZT^KSo@C|P?7YR8dP0se1jj z9aA0|7MONf(ZYaLZs$s}r*05fx25-iN6mZe_*Rq%uyz(+^-k;t`!R`?uf~rn#1ZC7 zuv3}UrmMzcBbo4jym@fS5%I+G`GJIC1s$)MQs3Vhld?a2U;w}$@V%dC@%qpO7+3#$ N&GnQ!lI8SQ#{X#Iv!eh2 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..b8d35b3a1cfe5308bb099a5a1429555c41417e36 GIT binary patch literal 3552 zcmV<64IlD}P)o8zx62qSGZVDjFcw zmxU;G#z^HzQ!GXJ-*69pbEeNn;$q%9`<^_ve6S+hkfX>pEmUTks+2m@VN4e=-BfB# zcQM@~beFnE|8|&qR$IOR+Cm@fKKV*xuU`Zdvl=LK4a4vxD=}@uREG)CWaLRqJ5ybP zu6!%iC+?fAzSb|q<0OVH@(J1H8ThTgk0;W=21TJYwd22S48?0q?Ql<_H9oW?Q#<^| zeirUq0oDLxz*ubc^EioOzd5Deq{k}q4=YI_6Qm}Lx&A|+|0D}zEJqe60pgP7hwE|CF z@#G3rLLN!=hY3#Mncm#=bNubjDVN#!%R!#+yMuUTdtd@=nOZsg2kv6qi*x zzDFd9=@0{x|A>LZ;?=}}RP0ia7?F(2EK$;G^~ix^1(KmvlA1T%Me0V!5Mp(azrt*g z`GKR#Hle}^)6nEOi&5p=B`&3>XD&k7hNpOg6rWXgIVwRD#GYff08(lhSI*BM130r6 ztwLvix`bL=@1gtm@4J-l-fc!-e{&2~Oqs{qaK~p9f7wxs>V|45HOAS_daGw5xEuU;CIJ+92}tg z4<4ZP8$L$Eb4K%sldwI?Dr*+0^Cav!^8yGXz0q0enY&~)R;yOG00dN1dkvL6IfJJZ zVXu}^_&HPQzwpQx>^t=9m8u@|rU zGZkWRl_Ic3Qgwcn12rQ-p|)rUPVR0xZ|g z#6I?<=DMiep91ftqa7MkB{^?D-ZoQ_q4o#Zz5>gjTpeUp0 z3G@w~C|7{qc>5!&4by(n%Jp`iuf291jemANFJmoJ=kLN8bXoMLmT3fvj9{#fSNW<} zPWfc?!`YwgG7Mhr!;M=hJH@mEk5k`p+aWlYYie<%{DirkwsaCDMRv!-QbfD`F`U&* zo>5d65*-)D#>B#V$@hY}ZNj;cW4C_i&aXIcn%mJeYW9gE&#PbekM-NS=wn4l1Pv@ zMzD%cy$ABGjazr~@-TOPy^E&IU2N`Sc+MEK;iFAm2A0h&E$DX(ms?2dx_7F01)(i1 zt(1M_?Cw+ZHd@;uW{XK*Y{?Ju0ch5um8c1;jWfXy;v{GISLTsgmo00A* z8#H~vA1NDj?m{&xWtC4M{&ANL0wWz5DipHQ4JPOCWyT?wRHhZzZ zeZJFjg#>%C8}$u6=EclzKE2=~#v<4nARyoPtdc`q14SwhI__K?1o_n~Yb@iSRqNli zs3kSrZnRJbh=V@m8MSxBLHE(SRrcc`CQy{7<{rUV_*?AJCSmpCIGg>1Pb59_r4>#^ z(nn96vdGRMk_L&gj-oWj!lL9s60`o2)KQE1 zB&*KmVz3NtmJIw>|N6;iRC%JSJZi=ZuUXilH+U`xaL>hXvZ^UVLRHpEz@n>UwO_O{ zvxM&!UB21;HmhtN?84Q$8@99YqbIS1J!uhfSMyjD;F8UQWTYp=gUt@U%M2UX5p%4Kzf zcJbV2CClLAM^#U{Xz6L zJdsKRtEu5+&Ybs{fi3b28WN?!`q@NF5kI%@$vey#&m~jmHwA`7A1U07i4e+zpQNm|hsmsx_shxjsk(;ai>lwhlEheA0qLHoISKxd?ut+1!iOjA0S8%WxDr|ybBIOiWdU3lm z`-eQ?oQ5>5uzjd7ej1)jB$<=TK2p#pFi;o>wmV#sI7_BxK%(~=dnzy;Aqovnm`E`X z<`57N71R@7aPSTY2!M`7!(!s5%GHI9gb|Mfi808OJ5S4R8Y+~7+uvURZz0;p z$0s#rxNa}R6fBi{*o(kCWK;@xicx9yVII?fSHiQ~j)?aO3JQYL#1XJ5KSG|e0(*zs zOa;K*K(T=V9)Oo{S<-6w00i(zcy;?%WAK3C1Mvl$9;N=lVFfV>njP|tB6AU(uC?@> z>XDSeeB2Vo7A9ow#Js=(UMbBR<;r{YlREwU{QN+-qoC#%8Y!79O45D}o{p&oU}|T; z>W*ZQ?|P6=Q;;J~SYlu-7;}g~TnRh?FN7zL`Pd01O}@Uq@HG|@9IGE37W1SqA>&g? zTHZBSPGLzE$?Ht!kDJ76DBvsz?sa_Jgn8b?lwYVN8t5Cwz+*wV0=BG(XdZfBYHVG7 zgM)+piP`~Bia~<{b0Q>(OJWkWdn9S2YM^=t1#;S6S%7Af;8{qR!SG`HQiJ>24Sho2 zL}ElRCX5X{JPMx?>I+FAk*G-6f(-`qF+V?Th(J13AWvQ!t;+aJJVO7iBze?19H-RE z(+le5=|zn+71YB$_zj+cXCrYNXbXK1X@NeYU<{IQJ~|&+Vuu8n20(yGz=FMhv2fZG zydQSKNf0W)qyvJ7=KBu`Edqjn!#(_43OobPk~Yv*0DY05b$~lvw>!Y<4{sZy*+GK_ z4fXQ!4TV}T0S=6OG@&SRFASc6XQ2&|l>WaZP#hR`YNGwS5C*yUv?lc$Zn7uu(=Jd zBQr(wEwogv4g_{iFq~uA3k~Z|L@DvE#_JQ>CKxj(Q|L@;_pg7{hnT!9|ZQb+#ochnl1kg9D@G4hNk|1@c1c) z{PkOR|2qXG{Wo$7`M-9{ZVdTtdk+0Kb_u1e2S8@7a?0x`-IJ*AtKYskrENiB%2SAk%zG8F7zQf=Uw)BkpfBE_?MDjX& z@xO&fB(T^G|G)3ZNu2smpTF|o#wUh09?%1ZEU4JTml;2Q`T9S*q6Mrzuc{3gQ-A*d z{Q2vDYEeB{thm1G|F`eoaq0)fT1(#ya4b^Y1D+8X|DV5nO|V2c3(TM(uHGc5|Nf&V|J{K3i0U2yrD0-<#2-I@{x5Ip1M7*&D*x{joegF;bWbC? z(kra(q`n6-N}I4|UUdBS-G~1{3Hjh;&W{YUBz~nhg z|9eJe{4Z(f##+{cVkED+{l6Db&737`v6TNa;pIQg8*`u<_1?qB7^TPOFJHjLD9$4G z$4`iwAE;_BU%Le^B3KtGndh}^?w7N zp&3LI9GX_%Z^hMgm2i3hX^M$M&D3?3wyocP$TZWyV~|^v4II`1-Ns4G92qkYkC3*q zq5Vcp3$J%tR^A_hzW)HC>4{->YFc`|Q_{EF#LX=TNWTIEGZ*dOIh!!#7am`0)iN z!-Y*JzdqP8rN&2Y&y2(+EtA?m9-5+}#BXAw@$*D;zxcf=lRhYP2`ZYNoGdU|=;=Y1 z!-o@UOzpBVHoTpyopyF#@i)8YcdVaV?2ljDUj6>w?`yyA*Pf5cUSE9b6wq26;8J@~ z){!@7GpTmNE>2kO_POn1zf8`~}P?%{85(;s&nc+C&;t$4D5$+f9? z-8>e~Z&%(_OwrVd==PGc4mhTFjVafjdCqsM|EvEe$2)U;a9s0IGofbtHcpKz;cJR= z`DNzVI-iMtrg<$r*EFejE8l0oMM3e)a|=o;x>Mhk@*n)xx%2Rrt=4TnivwP5zpS-& z@5h3w<{9>vH!6KP74q!po!oh)$BI~jUu}4P|5ofvi@(2i9NyELbZ$qD}PI&+JJ3+^f2=YEuP zjpepXu;`->)%n@lB|b@Iv$k0qhJJp%S?O9t?)zjLwwY?z@=v^12)=lt^ZcwNoye^x z_uu*-x}ntY`mc3S`yMaaHuurqE~e`{G_IsMZdhw*{kDDS9h3WSQa;8d3vwO)d?WE+ z%*LAIs=2#$t=BZmPTP}xMpj0I9ti9_c{r`p zu+;ELV)~|tmk}}-GjAWQO5U<}Lr?bB5UX>pYf5~UOnY%ZTQR${nq6YQOHc15>q%#$ zl8$8k_1fsCw;<~OiJ-OiE?f7RJWt%N`#e!y=2`BhIqju|a?kW5QupmV#wx6HrSs?J z&nJroVy6i|*Og1U`{c;a^^dPvTfNJjdCg1nUS<*OC dK7&Kx57tYsZ49$p7vBM?@pScbS?83{1OVHE%8UR2 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..8f56909cddfa86f1387074bf43003f36d6e67be1 GIT binary patch literal 2413 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 b;}yxl--Ss53i@>Q4YQuNcebmsMJN0NT!aL! literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..9737d79c0492b2c8a811621660eba33e79fab959 GIT binary patch literal 4858 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..9133e31b43252d00767a6a3806df9ba68de2d265 GIT binary patch literal 3871 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..c3ae5f5ccdecc01a9b17a2a0c2b1bb20602f0151 GIT binary patch literal 8001 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 z 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 F@PB1o0BisN literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..ef89bd5215ffcc38c68b119a7495a77a7084543b GIT binary patch literal 10893 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/Strings.xml b/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/Strings.xml new file mode 100644 index 0000000000..f800d6eb45 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/Strings.xml @@ -0,0 +1,4 @@ + + osu.Game.Rulesets.Catch.Tests.Android + Settings + diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/colors.xml b/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/colors.xml new file mode 100644 index 0000000000..17bb9a9dd1 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/colors.xml @@ -0,0 +1,6 @@ + + + #2c3e50 + #1B3147 + #3498db + diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/ic_launcher_background.xml b/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/ic_launcher_background.xml new file mode 100644 index 0000000000..6ec24e6413 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #2C3E50 + \ No newline at end of file diff --git a/osu.Android/Resources/values/styles.xml b/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/styles.xml similarity index 100% rename from osu.Android/Resources/values/styles.xml rename to osu.Game.Rulesets.Catch.Tests.Android/Resources/values/styles.xml diff --git a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj new file mode 100644 index 0000000000..d1e62c46a1 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj @@ -0,0 +1,108 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {122416d6-6b49-4ee2-a1e8-b825f31c79fe} + Library + Properties + osu.Game.Rulesets.Catch.Tests.Android + osu.Game.Rulesets.Catch.Tests.Android + 512 + True + Resources\Resource.designer.cs + Resource + Off + false + v8.1 + Properties\AndroidManifest.xml + Resources + Assets + Xamarin.Android.Net.AndroidClientHandler + + + True + portable + False + bin\Debug\ + DEBUG;TRACE + prompt + 4 + True + None + False + + + True + pdbonly + True + bin\Release\ + TRACE + prompt + 4 + true + False + SdkOnly + True + + + + + + + + + + + + + + + osu.licenseheader + + + + + + + + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Assets/AboutAssets.txt b/osu.Game.Rulesets.Mania.Tests.Android/Assets/AboutAssets.txt new file mode 100644 index 0000000000..b0633374bd --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests.Android/Assets/AboutAssets.txt @@ -0,0 +1,19 @@ +Any raw assets you want to be deployed with your application can be placed in +this directory (and child directories) and given a Build Action of "AndroidAsset". + +These files will be deployed with you package and will be accessible using Android's +AssetManager, like this: + +public class ReadAsset : Activity +{ + protected override void OnCreate (Bundle bundle) + { + base.OnCreate (bundle); + + InputStream input = Assets.Open ("my_asset.txt"); + } +} + +Additionally, some Android functions will automatically load asset files: + +Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs new file mode 100644 index 0000000000..09fc7ce770 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs @@ -0,0 +1,19 @@ +using Android.App; +using Android.OS; +using Android.Support.V7.App; +using Android.Runtime; +using Android.Widget; + +namespace osu.Game.Rulesets.Mania.Tests.Android +{ + [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)] + public class MainActivity : AppCompatActivity + { + protected override void OnCreate(Bundle savedInstanceState) + { + base.OnCreate(savedInstanceState); + // Set our view from the "main" layout resource + SetContentView(Resource.Layout.activity_main); + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml new file mode 100644 index 0000000000..6ffcfe16f7 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Mania.Tests.Android/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..468676e282 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests.Android/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Android.App; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("osu.Game.Rulesets.Mania.Tests.Android")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("osu.Game.Rulesets.Mania.Tests.Android")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/AboutResources.txt b/osu.Game.Rulesets.Mania.Tests.Android/Resources/AboutResources.txt new file mode 100644 index 0000000000..c2bca974c4 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests.Android/Resources/AboutResources.txt @@ -0,0 +1,44 @@ +Images, layout descriptions, binary blobs and string dictionaries can be included +in your application as resource files. Various Android APIs are designed to +operate on the resource IDs instead of dealing with images, strings or binary blobs +directly. + +For example, a sample Android app that contains a user interface layout (main.axml), +an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) +would keep its resources in the "Resources" directory of the application: + +Resources/ + drawable/ + icon.png + + layout/ + main.axml + + values/ + strings.xml + +In order to get the build system to recognize Android resources, set the build action to +"AndroidResource". The native Android APIs do not operate directly with filenames, but +instead operate on resource IDs. When you compile an Android application that uses resources, +the build system will package the resources for distribution and generate a class called "R" +(this is an Android convention) that contains the tokens for each one of the resources +included. For example, for the above Resources layout, this is what the R class would expose: + +public class R { + public class drawable { + public const int icon = 0x123; + } + + public class layout { + public const int main = 0x456; + } + + public class strings { + public const int first_string = 0xabc; + public const int second_string = 0xbcd; + } +} + +You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main +to reference the layout/main.axml file, or R.strings.first_string to reference the first +string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/Resource.designer.cs b/osu.Game.Rulesets.Mania.Tests.Android/Resources/Resource.designer.cs new file mode 100644 index 0000000000..c27ae8a832 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests.Android/Resources/Resource.designer.cs @@ -0,0 +1,112 @@ +#pragma warning disable 1591 +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +[assembly: global::Android.Runtime.ResourceDesignerAttribute("osu.Game.Rulesets.Mania.Tests.Android.Resource", IsApplication=true)] + +namespace osu.Game.Rulesets.Mania.Tests.Android +{ + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] + public partial class Resource + { + + static Resource() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + public static void UpdateIdValues() + { + } + + public partial class Attribute + { + + static Attribute() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Attribute() + { + } + } + + public partial class Id + { + + // aapt resource value: 0x7f050000 + public const int myButton = 2131034112; + + static Id() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Id() + { + } + } + + public partial class Layout + { + + // aapt resource value: 0x7f030000 + public const int Main = 2130903040; + + static Layout() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Layout() + { + } + } + + public partial class Mipmap + { + + // aapt resource value: 0x7f020000 + public const int Icon = 2130837504; + + static Mipmap() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Mipmap() + { + } + } + + public partial class String + { + + // aapt resource value: 0x7f040001 + public const int app_name = 2130968577; + + // aapt resource value: 0x7f040000 + public const int hello = 2130968576; + + static String() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private String() + { + } + } + } +} +#pragma warning restore 1591 diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/layout/activity_main.axml b/osu.Game.Rulesets.Mania.Tests.Android/Resources/layout/activity_main.axml new file mode 100644 index 0000000000..ff7a60eb50 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests.Android/Resources/layout/activity_main.axml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..036d09bc5f --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000..036d09bc5f --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..2531cb31efc3a0a3de6113ab9c7845dc1d9654e4 GIT binary patch literal 1634 zcmV-o2A%ndP)B+Z3$1(8#|f~9B42Y^N-3=o2YCq0YUY$Zu=pM;#hG{lHi%n~Vh z1d1vN#EDO19X?u$`cV z!a}AKG@Bb*#1cdYg8af_;jP69b`k%G1n?0=F^8bI^o>wg-vEliK^U}y^!D|^p|ax; zC|pK=f+FHp!RUAhtlpGGUxJb|wm^5! z<1r%$<$TR02wajxKZ4MiR#aAxDLE(##UNyD|ABr4WoGRF*?@e^2|~Hq(gurSSJH*;Q~5lw{J5A_(PCXBWhzZE${qgzv0{dk-F( z1<}>r181tLiEla&f1j&?p2xjbfp2cTt-c1Ox~?9EhK9`cJ9Vatf)loIoQ@#h&}cIGD>Z#QLE}&(bMo@7Ff|7f#Nm^$PJpVcbj+v~K7wfVwF}=) zRQsc+`=A-+C)vrRvaIC-5u>|;3h z*G4-u#RI<_vuSN~vZ6{|I~q5FFk3%de#+*>UFG>&bq6~ zUEMZ~FIOmFO=kA^5rkp-Msw?^63xvdXVZ-rv@{6{iVO}M!}^Je%2BPbi+(L<5<%~h z2v^D+f<|j%7~cJjOzg*!GPQ{%uE{i%YgcZhuZh{yNlQ}RhaU1jd=K+AopVKP+D}&} zZ3y$llqZiln=Z_A$!qzkGbX0D{?l(v5@1|`QyCvCnQ`eKI>|zj_zo%y#fKf85VhQ} zP)y&j4P*nR3q{-o35iV6nx7QDqq<;WDVIt}|N%`!dgv*y3va8eLNj zU9x(?ieweHfQ*yXk8|=ssZ~qJEz^QoKJ|iGa>ge_Vm_8l}S+UvJ{8g4jr+o#aTSFsz1W;PDP zW765JXGU#3JL>SlIl3NRV2{7B2dLO1cIP)a4ZRYL|MBD36O1#oSgAf}APz5@;x=_U-<=y)Py7*}O5(uu7BL_eLe6Ek7pH|G zMq)FrF1EFq&yruS5b=F=w)fVVoPd(oeRyTFym_Uwyn~L=OL(O?cf^2L5R(SmjORx6 z%nmZf^W=3pkvT*>@osUNi>DULH1hL;y`JGQX$onRCr_U0=H~Viodq!<7Q{3rPk~{G gu#IhOV;e2n|1(WJB~7~kivR!s07*qoM6N<$g7lUVaR2}S literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..7a859c25556af7a2e46e22a2220eaded55628e9f GIT binary patch literal 1441 zcma)+`#%#30L6FjMQg%tuA0%p#0??L`*E=rD#U2F4L5n@F+O9Sp;(QwEQy7+?sX?r zCWN(!Hg`+j5k8*H@|yQEtnAi*(D{7M`Tlf1=eKjq)BUsp2nqrK01B=yNUv`!`EH=x zx8$xJQUd^Fuec%|(TT&0V}4orr_==mmCnEuzD+ff8Pg>pJRqsWsD{#?eGPaCu0(sEH_2RG@<6-Nt<8 ztPMUmmAz9Ga$23Y9~p9dqJSgJJ#Jk_r@o13^%d-Xf46i+Lrmz3 zy9(DUDVXj;Zny7nO+yn&W2flEX=C!8&D0zI`G# z8;XmlonoghgRFUY*$+7pPLa}Uy)onw>TT9t(FTV6#BV8&lXWDPRvQW_n~xZ|yLcZjX>m$Eaf1)dwXS`&E^ zkNjO;%;fWywchc=+w4utQ0Vbn%B>b~yy4I#D{?1!P`$P>Wdo+ljCo(tYia04JTc=$$u+IbzDVPFYpm8+AQj+ zGKH zfS{{hN%W)kF+(26oZpkURD5Q_G_z97F6{Jval+TOj-;5y)*Rdo3a$^^k~q5gpTzmp1q@+2X9O z;_VUF>;s~C1~gpFrFoh?{aQ|LlBIYz!z^P~lndX5-ES)p#+9GW*|-WBTzQ*&gKOE` zM##bUaWl`6rZBXw0!~_oUhf+H$tNc@lLZCj0bZT^KSo@C|P?7YR8dP0se1jj z9aA0|7MONf(ZYaLZs$s}r*05fx25-iN6mZe_*Rq%uyz(+^-k;t`!R`?uf~rn#1ZC7 zuv3}UrmMzcBbo4jym@fS5%I+G`GJIC1s$)MQs3Vhld?a2U;w}$@V%dC@%qpO7+3#$ N&GnQ!lI8SQ#{X#Iv!eh2 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..b8d35b3a1cfe5308bb099a5a1429555c41417e36 GIT binary patch literal 3552 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..8f56909cddfa86f1387074bf43003f36d6e67be1 GIT binary patch literal 2413 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 b;}yxl--Ss53i@>Q4YQuNcebmsMJN0NT!aL! literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..9737d79c0492b2c8a811621660eba33e79fab959 GIT binary patch literal 4858 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..9133e31b43252d00767a6a3806df9ba68de2d265 GIT binary patch literal 3871 zcmZ{n_dgVX|Hs|AGwx({M)uw%qjECNKBIFkWs_{OPG-hgIT;-yd++QJvW2om=tM|& z%H9e2)c5=2=ka+x9`E=2^#{BjugCKpi$>{Of^a}6C@3!JA~i98FX7+NQ2pIx?Ufb^ z3VM>RrkZg8anp*{)c6w{ua@Q=_bH*Cuxq%LI*7AGBwto)H-4!zzcekaq&2morKG}n zDqW!T*L~Hk*w&fLWkN_%TRacHzZw}4ksU%uD{7=< z4l@F>pf_Cn{g0o4;i*1H;#1e1-8Sexy}Xv7sq#ll}DbR&61Jz5)YqB}ZOJOXIqaqfl-_k@P*k!*Y-1 zd(EHAJP_%kR{C}E1hMnU!7Nn5&Xc@ zOW#dX-a7S(bXQ1)GD`E2+dA)roFGLZ$YG!>vm17Q#~qSAB*6DaQd9MaCo|S}wqb6S9B=T`wCw7@qZA zHbS^wMo*b2CVh9inNqd!C^;{$*8EGWf1W{RE8+5O2vQgbd8Q|#Z&D)~7#LW|`W&2L z_SyasQE5fzr8$fM0Zn_(DI~(K;s=4IGw}=5`M4LXXw%?Zd&A4B^1?jOnMXtv(4tuj zATG@Fl~sFhQWT1;`B1D2SSa~}-c~CzLg>+!-;3#7J?rnfA!~pBo zKQ;tVz*}4Grw3mfA+SZK^Sp%H{@X6r2psg~wG{kKWi$fIuTaUYJFc+AxB^Hw2(({r z_$0>HdR@Wy8L4?wi;8`FQFPbpt2#h8fmG`&B8tlM5!2hu3~W9;Mqv1GU+Z^bFm_b1!BHQjAzk$7fP& z^+rYz zVHe?I`XfV!78$8wvEthV$qSmS@AMbm$$^&CjwO*XiO*z1y?$BvZ^Zy5u4Q%*GwkuJ zdFhfDJOt}_7~rgd?V5#_fpC@U$k32TWQE{Z8>ywyPzxH=>)UDGWYnmX(Fb+@_3Ou~ zQDTc)-$8tyLf$*#c|I%opcN|Iwpi0aok4zEm|`s&mJ65u`O9-E$2vwO(g>l&pPd{? zI9B0e|2d$nht>or~UhZeZIs-;+8ZZsPv$1!{ zYkPAaeuiW<{zM*KV2e#>&FcN2K4-DYi+?kum$EY&dVq(b3UTbt^ZQoV{Tc2LA1UkH zBDgQD|M3jlVG2yoaJX%Fc+A2)TcRrG(d02quX~s4`tA9wYJVi4r|&{VIdWAu+b+UA z#D3m-q-AvGK>23Q=g)azqn6sg=~2SRnnXB}qwnBEf5Uu;3xhb1FkS2>9B6<#$v z+I*^>7jCs&{@h8Xi&E&$>jvHrN8I$!dUD8y^dULVQL)&{Q)}2As z6ZABSIMYqKkCm6M88j7N7xMEnC=gP0B;)u<9N5J_^%K> z*Az(p>9S5q8>$rgQhLa55;4pZ@2)^uB#99mJgk77uj5uN@6N-r{5Kqr_FZfZn6e>E zMKrwhrfKE?wa}r(M@=2{P1P+!6EZHVN8En4Y$L|dv>Hq!)_bP6R<9P9Z+s)zWA1ZLM5a4U@vGOf?w{MXFOt75#wAKL`?v{8Z z2$CP5w&Nu%jIM|Y`!>T(^5aPpEoX`FS-)HwHbD2~koRV8oR{Pw_kcl$MO)6=mgjSH zJOy6jb(-j$fYY8!!fUd0a{B6GJg=I-%O55W&rE6;7-8tgVgNNM$J3gSXW1RDNrc`< z#EedInYups6;GLd*K%^%^(uFYd}~YO@Pn8*O${mw51{s)%zn$Xe8Tw$jrbimPq!j@ z*0hIk!_i#DC*e{3zI}+oXk5SK3{#2$i0fjXjyAD@XI7?hYbeL?%@JI|d{iPK+D;kU zAGrkYsTV4sy%%Fpsx5N3qUfu8zQb<=cHoraH_Wcb!Be`WTwXmH$d*nUW=?wA`7A*o z<$A_%p{1zExsocwhl5+^BZ7UC(?%+H-|=fBd84jpK2*0vZeZ@aHO+a=(5;8Fo1F*_ z7RSB%61GElZ1qOkvK)2fds zr|EHY#3AP!54Lr49m8x=u<$D_mjj);=htK~crq~|t5E*iV`o5kN?WK~+ZqF}?4J$H zv}QvA=s4<%i2K&VtXgZaO8Ms1*eS~zW+p=i7$u=S>f_zrw*1VNnSd%QD5Ld9GloR@ z!RGDZ;LYg)_qUoX6EbZ+bRpGHNO_Amy#j~eears);u62C)Pop$=F&pnhKuVt<9*Lb z?nVO)Ox`p6+Av1SIzi?lPB(g!XG2>cRqRKpF!pYXQbOkpo6~W zr&=N0>J^NPXAK2RFFNLfEK14=LkgiktE^_fHiodhKBaCS?pvH=RXEy7)7Ti}-?jEIQaxkB@s8-7H- zP;(ydFBF&_M6q_x@*Z^2#u{9pR5^)lPzX{gM$vuoWl3qjG#5OA%3@B`+&<>FRM^PC zWW9q9)v=x=jPRaaR^-m!qmI4WkhVcz@g9E%FIcZE>S&@yl_Km=!FC07xZifd9I{B-wJj#*1$wX$TWLs} zW>O+MrpYyMN_z+l7V6hGU1{?UzdbnDyiF1yiScCsbS&~iYSa2Dxvf%yF1Ht2_{bD)hkvE@C;YuC|PRtV+*rJ3zu@>WdieCbY z?L^FvNcnD!@PR3HUfFE^DlHs`fbA*K=ESgH0kVN(Z1z9DXjS&W6nWMJh5SO~{z05N z<{!_&82``b;~4+n|06yAf6#}v1q4#xD5R7rz%^dWXP=7mZKrFXMV3LOsc-r0Lk^B* z*yW56L{@?c^6?B*`jZ<~_QxMRW>kP5*-MV8m7gjrZoRXShrUmLUhI4a(VdYLK&55r zU17e^C&gz4hl7mom-*BpFI2V{+7D6eAZ|2Ia^Vg3{euGU;>50HzV8hj<1S`qAmbwK zgfaxem$ENrvVy=#$6Q$PJ?>joXo~5|7K;K?OOeXFuh!s`y~S?fuBg-`eZ<(kO5=j5+?q5CtBYHR53EePl$zzHN=tqL zAT0t%Q#&;$Lw9BKz-ifw&RNE#LZ zm*Y}tqURdR>_s30cr0Kmm)t7#DrItL=Pr-fY-&x>r8OIyN>b?!<#VU$BR9WtYus|C zlb3z7)3d0E&l3aF=W^2M+}x|R0NK52~QqMAdhKneJ)#) zT7732cAbz3<9Y0*qG%PU`g=RHJ)IFk*+PLD`Ld=IP?Njd>VtWBR4-Ck3Hv18U0)!W|c+cna{BX_>&pGEgpL3q?d1PmE6?8)S1P>1n$m*K8 zJrB=+%>Ow8{6`kgrK{~n_TQ|`%^YJ!R>os1-7RDQVJEyvrcBr0ehYLHwGuyhJjGN~ zQXoUXRri!muH=&aB?U>1OjA+1iSjX(KbG?{YAz~fDVtjrlxYNBasKe~oczl_x-QJz zn1EG=Of|76+r|3xXyZ;!Z#<{CvwOP))l;nhw({7K_y2yigJ{x8djHV!Bv%QD>fEfn zfz7)UQ4*qUMrsKoLSX)X$^#u-A&fe$U;?hE?p+_>xKL~AEW=Jiw}Ig1U5_U2-(%P{ zVuCJ~0vp6K{QrLUB2JkBR01uDv@prICoZtsfk#L4hb)YP$ub z2f9S)(JaQXb)^RXnn$j9bIlTy>rIX8d>-`yHuPE_>g`J>+u2H@?_8)`5+VCZ zJ))x}d%#qT1tl9I{o=s%XS2qeFG8n-U=;5i1zPYMWY#Ugl?PL<R0Zs;GS;0v_6v|OQ7krpYk?2}6+_J=VtUfeH}yzAF?`>jymCe2|@ zE_!x#kL0VTIc#d=NsJts=|t#hKG7`BXUl1oZJd_+s<~+jSG10sdI~p`>Jt@dIcTpk z(+P)ir{VKA-gi;l0w;XuaaL!nE0S~vh;JiqLTbE!c-KbPyJn}btB~-;)~zTHI%j4>7N~5ed{XR z@TZds;|W5p9zFJm>%npX+g!M9-SBG5(G~tQGju$$?s0-M z8i{z)9_@-4y_s8w1hG#2@)W_Gy`H>H z1(d8CvggX8%}7F>|ssPHeOOsARfk+ZD^pYf)6t1o(2N$(!|C3zU zKVISCDIohzMA{jmuTCd^jW{UlZ$_&zLFp%t%IE;0FwLK?#ax}NpTM<$q)21(kCO9! zGpf@W(epS!5)H+%??hxpeW;?j?=^Kx@14o;v>D$b zP3}=kUhhy?LR;HsWjGv4-gwx;eMyAYB>R4dzEaq-um1|WJnV8v=BH2uq{=Ra}$`B~FqCs(3MAh~Os%v8)w@H|$ zg_VdKV5wp)xMzX1n-Aq)qtzsSvg8&rYXn#G^LI*Y0sB7>ahs^vmy6?mVu=E+y!JAN z5Rs7_hhWn4Qq_83d83=(=BI7B;w7}P(UN8DBje-KB^6X-(dB&4#=Gk3w33Z^13Vz^+onWncA9w z(g&H0obtZ)6)!pW`V<`$gqKxoEgjz&DqaANl+$flu$NrTO{3h64C%W0B;?ouck96dmECiAOSgLnquRi9Ym#7^c6o~jg+`g&QG`y*p>^QNEFvFbx#g?K>dd!xLd zU!VLLVCqKEaYcdFkz(29DqDUND9U`_MP5;~M8NDZJ{He zk;dXH>Gi=$mAUP>>#=XK+FLL<+9m%$bTL7G$*)s0vPk|*NW^D;OB0FWJfG;aDGZh45jcb_Cddp0TATTx{GhEf+8 z3l`4EwxKT|wDEFu&Myr;v?plbH}IOkcsT!?;7kqVc;2d18*~;A#|N$}@zDiw&S#j=gj`+r|E;^PI_ZH=jFp;u-UdtX}q` zj-?WO|B5n$u>6n*B%x9^s1-Kn{cc?G1k-7&_ zwLF-TR~=5;R@=Z2NwwPKCSgF7O1wGY-E8<5&pZ7LU!^fnH;;349_Fiq9MLPqL(a(1 zsJU#*xX>qFWvC{9H`&spGA2)U=!YvASswAtl)`#Cl6djQ)aS#)TQu(&_ZlpyGBU-6 zwwZrgbwTZOwC5=DeSszp9I!ofeq!n(g&FKS(1Nw?A9sU4Xo@8?jg}jHWSc;ah7@UF z!a6IuaM)$~{`s-R$Bkjl%MTJAEUX{;0kXY4gfi>o{;XVoaP-18)r%V-8@eao=x#;V z&_;=bQT9U+Y2#e!85O7%wlOF^fRGsaHY|A~NbO_jj3r2x#>t<5>fN6oxdPwT)wY@k zjG*q7<$OBOx{2Jc{J{y5j(4mUq)3g63bh^BLnu=PtaH8mc*y65raYYl^^Np@Ai-Zc zkTIC6gZl)25##?-#KR`pzbe_6H{51vh|TX@ZD9!ks)+YKQ!R0du6^#S+~RdCJoWy7aJfJRHzVpyJev>2KCjz-n}~JO-6wq?+T3 zD((}AdNA$siA#~3{9V3}&=P7T~8-+~>bR`# zRZ&K76n;#4L<`&WSZl%QoU8^V&8PZb#MOy#SEuqXEy72o-RWQLim{Eou}@A*-=?qF zjh$uG)&yVg!V35577^rL==DB-34u*!*^Oy22FV_Ip<+%Rr=v3Zcn?7BGD!C$9;oz* zt$J0B^1P_&>J^z1UJ8#GKNY literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..c3ae5f5ccdecc01a9b17a2a0c2b1bb20602f0151 GIT binary patch literal 8001 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 z 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 F@PB1o0BisN literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..ef89bd5215ffcc38c68b119a7495a77a7084543b GIT binary patch literal 10893 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/Strings.xml b/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/Strings.xml new file mode 100644 index 0000000000..6ab86b7262 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/Strings.xml @@ -0,0 +1,4 @@ + + osu.Game.Rulesets.Mania.Tests.Android + Settings + diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/colors.xml b/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/colors.xml new file mode 100644 index 0000000000..17bb9a9dd1 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/colors.xml @@ -0,0 +1,6 @@ + + + #2c3e50 + #1B3147 + #3498db + diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/ic_launcher_background.xml b/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/ic_launcher_background.xml new file mode 100644 index 0000000000..6ec24e6413 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #2C3E50 + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/styles.xml b/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/styles.xml new file mode 100644 index 0000000000..5885930df6 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj new file mode 100644 index 0000000000..5fbee695b4 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj @@ -0,0 +1,105 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {531F1092-DB27-445D-AA33-2A77C7187C99} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {122416d6-6b49-4ee2-a1e8-b825f31c79fe} + Library + Properties + osu.Game.Rulesets.Mania.Tests.Android + osu.Game.Rulesets.Mania.Tests.Android + 512 + True + Resources\Resource.designer.cs + Resource + Off + false + v8.1 + Properties\AndroidManifest.xml + Resources + Assets + Xamarin.Android.Net.AndroidClientHandler + + + True + portable + False + bin\Debug\ + DEBUG;TRACE + prompt + 4 + True + None + False + + + True + pdbonly + True + bin\Release\ + TRACE + prompt + 4 + true + False + SdkOnly + True + + + + + + + + + + + + + + + + + + + + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Assets/AboutAssets.txt b/osu.Game.Rulesets.Osu.Tests.Android/Assets/AboutAssets.txt new file mode 100644 index 0000000000..b0633374bd --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests.Android/Assets/AboutAssets.txt @@ -0,0 +1,19 @@ +Any raw assets you want to be deployed with your application can be placed in +this directory (and child directories) and given a Build Action of "AndroidAsset". + +These files will be deployed with you package and will be accessible using Android's +AssetManager, like this: + +public class ReadAsset : Activity +{ + protected override void OnCreate (Bundle bundle) + { + base.OnCreate (bundle); + + InputStream input = Assets.Open ("my_asset.txt"); + } +} + +Additionally, some Android functions will automatically load asset files: + +Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs new file mode 100644 index 0000000000..77a75f0d67 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs @@ -0,0 +1,19 @@ +using Android.App; +using Android.OS; +using Android.Support.V7.App; +using Android.Runtime; +using Android.Widget; + +namespace osu.Game.Rulesets.Osu.Tests.Android +{ + [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)] + public class MainActivity : AppCompatActivity + { + protected override void OnCreate(Bundle savedInstanceState) + { + base.OnCreate(savedInstanceState); + // Set our view from the "main" layout resource + SetContentView(Resource.Layout.activity_main); + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml new file mode 100644 index 0000000000..05da9d02f7 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Osu.Tests.Android/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..0e43fb25cf --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests.Android/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Android.App; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("osu.Game.Rulesets.Osu.Tests.Android")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("osu.Game.Rulesets.Osu.Tests.Android")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/AboutResources.txt b/osu.Game.Rulesets.Osu.Tests.Android/Resources/AboutResources.txt new file mode 100644 index 0000000000..c2bca974c4 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests.Android/Resources/AboutResources.txt @@ -0,0 +1,44 @@ +Images, layout descriptions, binary blobs and string dictionaries can be included +in your application as resource files. Various Android APIs are designed to +operate on the resource IDs instead of dealing with images, strings or binary blobs +directly. + +For example, a sample Android app that contains a user interface layout (main.axml), +an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) +would keep its resources in the "Resources" directory of the application: + +Resources/ + drawable/ + icon.png + + layout/ + main.axml + + values/ + strings.xml + +In order to get the build system to recognize Android resources, set the build action to +"AndroidResource". The native Android APIs do not operate directly with filenames, but +instead operate on resource IDs. When you compile an Android application that uses resources, +the build system will package the resources for distribution and generate a class called "R" +(this is an Android convention) that contains the tokens for each one of the resources +included. For example, for the above Resources layout, this is what the R class would expose: + +public class R { + public class drawable { + public const int icon = 0x123; + } + + public class layout { + public const int main = 0x456; + } + + public class strings { + public const int first_string = 0xabc; + public const int second_string = 0xbcd; + } +} + +You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main +to reference the layout/main.axml file, or R.strings.first_string to reference the first +string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/Resource.designer.cs b/osu.Game.Rulesets.Osu.Tests.Android/Resources/Resource.designer.cs new file mode 100644 index 0000000000..f229c3f960 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests.Android/Resources/Resource.designer.cs @@ -0,0 +1,112 @@ +#pragma warning disable 1591 +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +[assembly: global::Android.Runtime.ResourceDesignerAttribute("osu.Game.Rulesets.Osu.Tests.Android.Resource", IsApplication=true)] + +namespace osu.Game.Rulesets.Osu.Tests.Android +{ + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] + public partial class Resource + { + + static Resource() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + public static void UpdateIdValues() + { + } + + public partial class Attribute + { + + static Attribute() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Attribute() + { + } + } + + public partial class Id + { + + // aapt resource value: 0x7f050000 + public const int myButton = 2131034112; + + static Id() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Id() + { + } + } + + public partial class Layout + { + + // aapt resource value: 0x7f030000 + public const int Main = 2130903040; + + static Layout() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Layout() + { + } + } + + public partial class Mipmap + { + + // aapt resource value: 0x7f020000 + public const int Icon = 2130837504; + + static Mipmap() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Mipmap() + { + } + } + + public partial class String + { + + // aapt resource value: 0x7f040001 + public const int app_name = 2130968577; + + // aapt resource value: 0x7f040000 + public const int hello = 2130968576; + + static String() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private String() + { + } + } + } +} +#pragma warning restore 1591 diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/layout/activity_main.axml b/osu.Game.Rulesets.Osu.Tests.Android/Resources/layout/activity_main.axml new file mode 100644 index 0000000000..ff7a60eb50 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests.Android/Resources/layout/activity_main.axml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..036d09bc5f --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000..036d09bc5f --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..2531cb31efc3a0a3de6113ab9c7845dc1d9654e4 GIT binary patch literal 1634 zcmV-o2A%ndP)B+Z3$1(8#|f~9B42Y^N-3=o2YCq0YUY$Zu=pM;#hG{lHi%n~Vh z1d1vN#EDO19X?u$`cV z!a}AKG@Bb*#1cdYg8af_;jP69b`k%G1n?0=F^8bI^o>wg-vEliK^U}y^!D|^p|ax; zC|pK=f+FHp!RUAhtlpGGUxJb|wm^5! z<1r%$<$TR02wajxKZ4MiR#aAxDLE(##UNyD|ABr4WoGRF*?@e^2|~Hq(gurSSJH*;Q~5lw{J5A_(PCXBWhzZE${qgzv0{dk-F( z1<}>r181tLiEla&f1j&?p2xjbfp2cTt-c1Ox~?9EhK9`cJ9Vatf)loIoQ@#h&}cIGD>Z#QLE}&(bMo@7Ff|7f#Nm^$PJpVcbj+v~K7wfVwF}=) zRQsc+`=A-+C)vrRvaIC-5u>|;3h z*G4-u#RI<_vuSN~vZ6{|I~q5FFk3%de#+*>UFG>&bq6~ zUEMZ~FIOmFO=kA^5rkp-Msw?^63xvdXVZ-rv@{6{iVO}M!}^Je%2BPbi+(L<5<%~h z2v^D+f<|j%7~cJjOzg*!GPQ{%uE{i%YgcZhuZh{yNlQ}RhaU1jd=K+AopVKP+D}&} zZ3y$llqZiln=Z_A$!qzkGbX0D{?l(v5@1|`QyCvCnQ`eKI>|zj_zo%y#fKf85VhQ} zP)y&j4P*nR3q{-o35iV6nx7QDqq<;WDVIt}|N%`!dgv*y3va8eLNj zU9x(?ieweHfQ*yXk8|=ssZ~qJEz^QoKJ|iGa>ge_Vm_8l}S+UvJ{8g4jr+o#aTSFsz1W;PDP zW765JXGU#3JL>SlIl3NRV2{7B2dLO1cIP)a4ZRYL|MBD36O1#oSgAf}APz5@;x=_U-<=y)Py7*}O5(uu7BL_eLe6Ek7pH|G zMq)FrF1EFq&yruS5b=F=w)fVVoPd(oeRyTFym_Uwyn~L=OL(O?cf^2L5R(SmjORx6 z%nmZf^W=3pkvT*>@osUNi>DULH1hL;y`JGQX$onRCr_U0=H~Viodq!<7Q{3rPk~{G gu#IhOV;e2n|1(WJB~7~kivR!s07*qoM6N<$g7lUVaR2}S literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..7a859c25556af7a2e46e22a2220eaded55628e9f GIT binary patch literal 1441 zcma)+`#%#30L6FjMQg%tuA0%p#0??L`*E=rD#U2F4L5n@F+O9Sp;(QwEQy7+?sX?r zCWN(!Hg`+j5k8*H@|yQEtnAi*(D{7M`Tlf1=eKjq)BUsp2nqrK01B=yNUv`!`EH=x zx8$xJQUd^Fuec%|(TT&0V}4orr_==mmCnEuzD+ff8Pg>pJRqsWsD{#?eGPaCu0(sEH_2RG@<6-Nt<8 ztPMUmmAz9Ga$23Y9~p9dqJSgJJ#Jk_r@o13^%d-Xf46i+Lrmz3 zy9(DUDVXj;Zny7nO+yn&W2flEX=C!8&D0zI`G# z8;XmlonoghgRFUY*$+7pPLa}Uy)onw>TT9t(FTV6#BV8&lXWDPRvQW_n~xZ|yLcZjX>m$Eaf1)dwXS`&E^ zkNjO;%;fWywchc=+w4utQ0Vbn%B>b~yy4I#D{?1!P`$P>Wdo+ljCo(tYia04JTc=$$u+IbzDVPFYpm8+AQj+ zGKH zfS{{hN%W)kF+(26oZpkURD5Q_G_z97F6{Jval+TOj-;5y)*Rdo3a$^^k~q5gpTzmp1q@+2X9O z;_VUF>;s~C1~gpFrFoh?{aQ|LlBIYz!z^P~lndX5-ES)p#+9GW*|-WBTzQ*&gKOE` zM##bUaWl`6rZBXw0!~_oUhf+H$tNc@lLZCj0bZT^KSo@C|P?7YR8dP0se1jj z9aA0|7MONf(ZYaLZs$s}r*05fx25-iN6mZe_*Rq%uyz(+^-k;t`!R`?uf~rn#1ZC7 zuv3}UrmMzcBbo4jym@fS5%I+G`GJIC1s$)MQs3Vhld?a2U;w}$@V%dC@%qpO7+3#$ N&GnQ!lI8SQ#{X#Iv!eh2 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..b8d35b3a1cfe5308bb099a5a1429555c41417e36 GIT binary patch literal 3552 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..8f56909cddfa86f1387074bf43003f36d6e67be1 GIT binary patch literal 2413 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 b;}yxl--Ss53i@>Q4YQuNcebmsMJN0NT!aL! literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..9737d79c0492b2c8a811621660eba33e79fab959 GIT binary patch literal 4858 zcmVxlCBHiW_rSgI3_J^MKwHqJEz|i*Sg*YtOHn%!8|O@U|xT*V!1aH) zx9aT)+OT1e6*I^fro))}A|t%nqOC49C*uh}iznRD0RVt(Fkci3aF-cE^~v-{jirSe z8y+KDRrXqA%?3VAUmJ!e`Y4{{Db{MI)J1oI-WfBjRTVY1Q!rK-v!l86id7G;UWZ3x z7~0LnZOuZ2xjo$KBiYmM_`2d z5?SVjnV>hVk!Z_9*%?FywwjSrU-z}DU~qVkNCML#z4GhV z_dS*4ib?_|4A~&o6c6ZDCNLfVt@G)TDg@Pe&InwDu_Y44rH_jqbYt zQQk%w?14PLdL_onhlQI!tDo8~G_ws5=fN6HW6)RMZ1xE78Tw}PR+Lk5El;CNtD@BG z@-c!)0b@`g>cgGvV&(C9t(F;co=4};U+^dfw6xu|4X@RormvOYhELMs z#n0=>EFFekYFvrh+S)vl0br1y$L?uHF?ZLL#>k8mg*7cHSw;nCRmALvD)pwhLaqK` zH{FAdpJ?$&@EJOEIG%e~S}30iDZGsfvTJYqebn^#ei9&%5{a3h)`)uHexhMfx2GC}a7&+PSj;~z&<#NnP097H+5#qe^HCa1jY34dHKXo8 zyY}pNY0`(An$dSZ{AfkZ$4_A9@iVII_BL<*2^~Fl!lh?HY6o9?8_(#NGRALVO#8VI z9n&Hr&MA(;4gAX2_<|07{q29d4A%Yse8#Sg>u#G&F@_8Hz`UC4@30;drblKka481` z?((Z|zQ@@uWsI@Bpz3gpTq7nHw%?y+JiTRw)x(8QKjZG6LV@5aU|(2+QR(aE^IiQA zbbY#Ry<58f_jBjbjM>lIwKaI;ZD{|mhuvbp&fR-a)yVM<(;)5!g71B_7Ufosrv7ZTPIz#p-Luu#-A?Iq&cPX$ zzM1o0ayvrq*fGO)ASt78v{QGK(f{&-ng{so_ts*sjO@u0Q~!L6QwtMIG_TAibnspej~MaY~_~X)&16cA3OA}Uc)}S zZIuHg0l)fIxZO8!t8bb(l>-Cnku0bDbBiIiN=wjhmPbZL24MzlVdpYjrNWx)(Pv+N zBWOAR3??M;Y<>CqF?UmT!q$5#$Hw0_5S%iz0WXT*1g|T5HRZin>UI=?a+d@J@ z!s*q|QbSDkGb%|Ptu~nUaAClGGv)}o`WafkaSJLkjkN=I!IBjnQqbDkiW**Ov@?)k zGq(Qtv*2Socm6z@IOPdFd$xCn2c|3a@PedtiB%Y-T!Ns zB*nm2J}l((;v)h?(g?ET>{yU|?VjUA$|Z5Ar4z zy&(!+?I)a55qI7%Xw>;RW~l8%Ar-Om{WT5^Y~x$+J4{7<@%1J_QxP{h$Tzu?ijZcP zKq?}fVC`eW07@i+F8B>mD^4f z)ZCiSzUcJ1kJo--m#qXTfHz@!FdhAeQdfr()df(n8{lw5hWt__$<&YXgbf+9gAJMc zW<2fEh74^Wt)GRe=bqeL_c`r8F zZ%NkP(2@K3Gurh1b{rks2WKzipslrswj^bFgIglwlMH~dvpP|4vRM$R(A9m*hXM4a z{4CC!@(@?pZpuIQ%!_Vq%1@oy;BZ@V_r3$1Hs$Z-xhbElE&Cp0JBVQHxI|GZmG;L! z!cy}pUl5`!WzA<_x?Ps?(38*EwFT+}D%{)w4WeKG+_o)f-(4r+oe$Td9FAov)Yh)P z4vEusup1UeF!pl7fNJ<-5Wab=5QSObu{0lZy)X+3VhwhMS;IIMX0@RgaIog6Fbk?C zTx|!ur{OpMjaOloqObP-sLfq@n$Z3)UV(sl1(Orr_5onOR78jzqW7(*JljLXv( z@h(qS6x5&?Y5JXjX{Y+%Mhyk@@83TeKfIkwUdT~|ykpm%Uc~^Yq_8a%b~pV1Kc(8z zoqm3P3c4D?#dpPGV`HIoB1)QRoC#7O#GxDz9Gw!NHm6%&QMzz}Dm~%)iV{ zGPeP+B$&E(5j7MN5)+rJ)D3A8;w8Q8Ui6aQr~h3q$V+_zR@JpD!O z6@t8|oswO4Y(T`I62MR_7K=EYk`fUS0Y|&XC1n`qz>CL1NP%Y`Rj{AeQ3cHE2i+g9 z$XNi`5e&JWnnKxva6i8wwX9(94k6-#zI|8+z44N)E#Bqp8<0hBzPP9Rok_u<_*BiE zpx1Fxs=hMmM6B-%{ zA2dja5v#^23aZ50BUK|xXAp(ZNxW`U&_!XEVU zV=I}8Hxwt!nhV$vjJo7JX>U56>IHQz@}zXb3SyKmUA_mmg3DQhUCz8!fC<4Spew($ z;e$P^5VEzFCeakFf!%)Me)ZWyyPbef8C|hjw-#fOPGdr0)8${-=*QRtI6OT$v*@eK zi3wKVrx$(=1tndn_noPttFW$%gmXQxy3=ANthcD6zW40_8=X((d6Lp}-{86D0tN(& zZvEtyH_Ip|VaiO>7(QVPGkrcnp8}qJ7#~Vh7lPV>GV>&s(e3sxEJ25Ufq{YWg(3I~ zU4}R<|4n&8b;l=6`T`RyF%KQ(#w&8b;KGpu5;Awcp8UKO#RMXPAPH&lO6_b}ZskR& zg{195@012Qu|}yJD!-GOQ*kj)rU6$ojja60o(A8hpey)lFE0@=K^2{-xJ8;-yobph z^)_i>uX^gpvCN{qQFM@{qUQ*6_423>yD?RDp(2q8PKHwW2Z!m!s={|bY(W~B4{CZc zBgoh~q*j(U7>QN+?}>s2z^;~p%x!?DfzM_FxM6|*{{Hd!XA1bo10~8y5>4?As19Hv zXJVxP@Fdrg9#hA8pGcxH?u+Cm=y&w<~fq{a`3jA*+9(;bhBKtXM zc3BhSDM86L(XTyXBiK5gjD@OThB3w~vQ@?l6Mli8uULbAMT{ygP>eX7*m2G=arDK$ZBF}Q^?qZJyqqn zs*>=^35vw}6AZKrL^?D)sxnTNIS&VL+rdVVNZLw8F)D#!iaU&9?q|O7!fuc02hQ(- zzF`b;shJHS;gMBD-N@*%QeKXzH>ez!B4=8E21biSp%TJ~G+$re+-R|EVxl_lZE05N zewrCWSdzj1Rt=>p+F4)5ZfAgH|Bktj4K}mVfzc4B;J)@jpU^iRLmpZ2GJ0&3x(V#= z$hNy|1Bh}U=v3lSfND}<5Hf;-29ykx$R{Nza~qR044YE3%a6(Os;LcbSgo`tWz85z zM6Y}k^$a{K&#$=z^*PCz#!b*R^Z|WApR`-)l>%cSdOonz`u#q}hyd`Xv7U{CH=~GD zr~w#EIbjjeb+AI?Q?+vvl=*LnGxVQHGK)8-Xv==V%sG^rS9w&PS9u%={+*grehB`C zwp4sK%tv;}Pv(A9KbA_?6$<gpmV|K5zk3V^6LOr zItEUINek*iBnmPHhK5%JV^9ZN9bXRw|Aya*M8O8Qhuo_nI$cfLl0w_GVWsqY5b3*L zUsE+)7~w;7ZhxW%!r+Bw@V#kOMM+39QCTtqD3F3ha`Lwn`d*O)o`p8Z%h6$^?f#@M zpUWM1R~X_)cHscHP`c6}I0E!FfNDe0@HbM85K5l$Cv98-oF_vVruYz*(T{-2Cg%4( gUP6AytBbGy15leQhEvp{>;M1&07*qoM6N<$g7ZLQy#N3J literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..9133e31b43252d00767a6a3806df9ba68de2d265 GIT binary patch literal 3871 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..c3ae5f5ccdecc01a9b17a2a0c2b1bb20602f0151 GIT binary patch literal 8001 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 z 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 F@PB1o0BisN literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..ef89bd5215ffcc38c68b119a7495a77a7084543b GIT binary patch literal 10893 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 literal 0 HcmV?d00001 diff --git a/osu.Android/Resources/values/Strings.xml b/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/Strings.xml similarity index 51% rename from osu.Android/Resources/values/Strings.xml rename to osu.Game.Rulesets.Osu.Tests.Android/Resources/values/Strings.xml index 21f749f876..8c47838dfb 100644 --- a/osu.Android/Resources/values/Strings.xml +++ b/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/Strings.xml @@ -1,4 +1,4 @@ - osu!lazer + osu.Game.Rulesets.Osu.Tests.Android Settings diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/colors.xml b/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/colors.xml new file mode 100644 index 0000000000..17bb9a9dd1 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/colors.xml @@ -0,0 +1,6 @@ + + + #2c3e50 + #1B3147 + #3498db + diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/ic_launcher_background.xml b/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/ic_launcher_background.xml new file mode 100644 index 0000000000..6ec24e6413 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #2C3E50 + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/styles.xml b/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/styles.xml new file mode 100644 index 0000000000..5885930df6 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj new file mode 100644 index 0000000000..7eeee4fd80 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj @@ -0,0 +1,105 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {90CAB706-39CB-4B93-9629-3218A6FF8E9B} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {122416d6-6b49-4ee2-a1e8-b825f31c79fe} + Library + Properties + osu.Game.Rulesets.Osu.Tests.Android + osu.Game.Rulesets.Osu.Tests.Android + 512 + True + Resources\Resource.designer.cs + Resource + Off + false + v8.1 + Properties\AndroidManifest.xml + Resources + Assets + Xamarin.Android.Net.AndroidClientHandler + + + True + portable + False + bin\Debug\ + DEBUG;TRACE + prompt + 4 + True + None + False + + + True + pdbonly + True + bin\Release\ + TRACE + prompt + 4 + true + False + SdkOnly + True + + + + + + + + + + + + + + + + + + + + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Assets/AboutAssets.txt b/osu.Game.Rulesets.Taiko.Tests.Android/Assets/AboutAssets.txt new file mode 100644 index 0000000000..b0633374bd --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Assets/AboutAssets.txt @@ -0,0 +1,19 @@ +Any raw assets you want to be deployed with your application can be placed in +this directory (and child directories) and given a Build Action of "AndroidAsset". + +These files will be deployed with you package and will be accessible using Android's +AssetManager, like this: + +public class ReadAsset : Activity +{ + protected override void OnCreate (Bundle bundle) + { + base.OnCreate (bundle); + + InputStream input = Assets.Open ("my_asset.txt"); + } +} + +Additionally, some Android functions will automatically load asset files: + +Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs new file mode 100644 index 0000000000..8ceca4d436 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs @@ -0,0 +1,19 @@ +using Android.App; +using Android.OS; +using Android.Support.V7.App; +using Android.Runtime; +using Android.Widget; + +namespace osu.Game.Rulesets.Taiko.Tests.Android +{ + [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)] + public class MainActivity : AppCompatActivity + { + protected override void OnCreate(Bundle savedInstanceState) + { + base.OnCreate(savedInstanceState); + // Set our view from the "main" layout resource + SetContentView(Resource.Layout.activity_main); + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml new file mode 100644 index 0000000000..9b5e759012 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..28f5d292ab --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Android.App; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("osu.Game.Rulesets.Taiko.Tests.Android")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("osu.Game.Rulesets.Taiko.Tests.Android")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/AboutResources.txt b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/AboutResources.txt new file mode 100644 index 0000000000..c2bca974c4 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/AboutResources.txt @@ -0,0 +1,44 @@ +Images, layout descriptions, binary blobs and string dictionaries can be included +in your application as resource files. Various Android APIs are designed to +operate on the resource IDs instead of dealing with images, strings or binary blobs +directly. + +For example, a sample Android app that contains a user interface layout (main.axml), +an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) +would keep its resources in the "Resources" directory of the application: + +Resources/ + drawable/ + icon.png + + layout/ + main.axml + + values/ + strings.xml + +In order to get the build system to recognize Android resources, set the build action to +"AndroidResource". The native Android APIs do not operate directly with filenames, but +instead operate on resource IDs. When you compile an Android application that uses resources, +the build system will package the resources for distribution and generate a class called "R" +(this is an Android convention) that contains the tokens for each one of the resources +included. For example, for the above Resources layout, this is what the R class would expose: + +public class R { + public class drawable { + public const int icon = 0x123; + } + + public class layout { + public const int main = 0x456; + } + + public class strings { + public const int first_string = 0xabc; + public const int second_string = 0xbcd; + } +} + +You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main +to reference the layout/main.axml file, or R.strings.first_string to reference the first +string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/Resource.designer.cs b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/Resource.designer.cs new file mode 100644 index 0000000000..d3709d3cdb --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/Resource.designer.cs @@ -0,0 +1,112 @@ +#pragma warning disable 1591 +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +[assembly: global::Android.Runtime.ResourceDesignerAttribute("osu.Game.Rulesets.Taiko.Tests.Android.Resource", IsApplication=true)] + +namespace osu.Game.Rulesets.Taiko.Tests.Android +{ + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] + public partial class Resource + { + + static Resource() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + public static void UpdateIdValues() + { + } + + public partial class Attribute + { + + static Attribute() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Attribute() + { + } + } + + public partial class Id + { + + // aapt resource value: 0x7f050000 + public const int myButton = 2131034112; + + static Id() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Id() + { + } + } + + public partial class Layout + { + + // aapt resource value: 0x7f030000 + public const int Main = 2130903040; + + static Layout() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Layout() + { + } + } + + public partial class Mipmap + { + + // aapt resource value: 0x7f020000 + public const int Icon = 2130837504; + + static Mipmap() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private Mipmap() + { + } + } + + public partial class String + { + + // aapt resource value: 0x7f040001 + public const int app_name = 2130968577; + + // aapt resource value: 0x7f040000 + public const int hello = 2130968576; + + static String() + { + global::Android.Runtime.ResourceIdManager.UpdateIdValues(); + } + + private String() + { + } + } + } +} +#pragma warning restore 1591 diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/layout/activity_main.axml b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/layout/activity_main.axml new file mode 100644 index 0000000000..ff7a60eb50 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/layout/activity_main.axml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..036d09bc5f --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000..036d09bc5f --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..2531cb31efc3a0a3de6113ab9c7845dc1d9654e4 GIT binary patch literal 1634 zcmV-o2A%ndP)B+Z3$1(8#|f~9B42Y^N-3=o2YCq0YUY$Zu=pM;#hG{lHi%n~Vh z1d1vN#EDO19X?u$`cV z!a}AKG@Bb*#1cdYg8af_;jP69b`k%G1n?0=F^8bI^o>wg-vEliK^U}y^!D|^p|ax; zC|pK=f+FHp!RUAhtlpGGUxJb|wm^5! z<1r%$<$TR02wajxKZ4MiR#aAxDLE(##UNyD|ABr4WoGRF*?@e^2|~Hq(gurSSJH*;Q~5lw{J5A_(PCXBWhzZE${qgzv0{dk-F( z1<}>r181tLiEla&f1j&?p2xjbfp2cTt-c1Ox~?9EhK9`cJ9Vatf)loIoQ@#h&}cIGD>Z#QLE}&(bMo@7Ff|7f#Nm^$PJpVcbj+v~K7wfVwF}=) zRQsc+`=A-+C)vrRvaIC-5u>|;3h z*G4-u#RI<_vuSN~vZ6{|I~q5FFk3%de#+*>UFG>&bq6~ zUEMZ~FIOmFO=kA^5rkp-Msw?^63xvdXVZ-rv@{6{iVO}M!}^Je%2BPbi+(L<5<%~h z2v^D+f<|j%7~cJjOzg*!GPQ{%uE{i%YgcZhuZh{yNlQ}RhaU1jd=K+AopVKP+D}&} zZ3y$llqZiln=Z_A$!qzkGbX0D{?l(v5@1|`QyCvCnQ`eKI>|zj_zo%y#fKf85VhQ} zP)y&j4P*nR3q{-o35iV6nx7QDqq<;WDVIt}|N%`!dgv*y3va8eLNj zU9x(?ieweHfQ*yXk8|=ssZ~qJEz^QoKJ|iGa>ge_Vm_8l}S+UvJ{8g4jr+o#aTSFsz1W;PDP zW765JXGU#3JL>SlIl3NRV2{7B2dLO1cIP)a4ZRYL|MBD36O1#oSgAf}APz5@;x=_U-<=y)Py7*}O5(uu7BL_eLe6Ek7pH|G zMq)FrF1EFq&yruS5b=F=w)fVVoPd(oeRyTFym_Uwyn~L=OL(O?cf^2L5R(SmjORx6 z%nmZf^W=3pkvT*>@osUNi>DULH1hL;y`JGQX$onRCr_U0=H~Viodq!<7Q{3rPk~{G gu#IhOV;e2n|1(WJB~7~kivR!s07*qoM6N<$g7lUVaR2}S literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..7a859c25556af7a2e46e22a2220eaded55628e9f GIT binary patch literal 1441 zcma)+`#%#30L6FjMQg%tuA0%p#0??L`*E=rD#U2F4L5n@F+O9Sp;(QwEQy7+?sX?r zCWN(!Hg`+j5k8*H@|yQEtnAi*(D{7M`Tlf1=eKjq)BUsp2nqrK01B=yNUv`!`EH=x zx8$xJQUd^Fuec%|(TT&0V}4orr_==mmCnEuzD+ff8Pg>pJRqsWsD{#?eGPaCu0(sEH_2RG@<6-Nt<8 ztPMUmmAz9Ga$23Y9~p9dqJSgJJ#Jk_r@o13^%d-Xf46i+Lrmz3 zy9(DUDVXj;Zny7nO+yn&W2flEX=C!8&D0zI`G# z8;XmlonoghgRFUY*$+7pPLa}Uy)onw>TT9t(FTV6#BV8&lXWDPRvQW_n~xZ|yLcZjX>m$Eaf1)dwXS`&E^ zkNjO;%;fWywchc=+w4utQ0Vbn%B>b~yy4I#D{?1!P`$P>Wdo+ljCo(tYia04JTc=$$u+IbzDVPFYpm8+AQj+ zGKH zfS{{hN%W)kF+(26oZpkURD5Q_G_z97F6{Jval+TOj-;5y)*Rdo3a$^^k~q5gpTzmp1q@+2X9O z;_VUF>;s~C1~gpFrFoh?{aQ|LlBIYz!z^P~lndX5-ES)p#+9GW*|-WBTzQ*&gKOE` zM##bUaWl`6rZBXw0!~_oUhf+H$tNc@lLZCj0bZT^KSo@C|P?7YR8dP0se1jj z9aA0|7MONf(ZYaLZs$s}r*05fx25-iN6mZe_*Rq%uyz(+^-k;t`!R`?uf~rn#1ZC7 zuv3}UrmMzcBbo4jym@fS5%I+G`GJIC1s$)MQs3Vhld?a2U;w}$@V%dC@%qpO7+3#$ N&GnQ!lI8SQ#{X#Iv!eh2 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..b8d35b3a1cfe5308bb099a5a1429555c41417e36 GIT binary patch literal 3552 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..8f56909cddfa86f1387074bf43003f36d6e67be1 GIT binary patch literal 2413 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 b;}yxl--Ss53i@>Q4YQuNcebmsMJN0NT!aL! literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..9737d79c0492b2c8a811621660eba33e79fab959 GIT binary patch literal 4858 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..9133e31b43252d00767a6a3806df9ba68de2d265 GIT binary patch literal 3871 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..c3ae5f5ccdecc01a9b17a2a0c2b1bb20602f0151 GIT binary patch literal 8001 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 z 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 F@PB1o0BisN literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..ef89bd5215ffcc38c68b119a7495a77a7084543b GIT binary patch literal 10893 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/Strings.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/Strings.xml new file mode 100644 index 0000000000..12965f132d --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/Strings.xml @@ -0,0 +1,4 @@ + + osu.Game.Rulesets.Taiko.Tests.Android + Settings + diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/colors.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/colors.xml new file mode 100644 index 0000000000..17bb9a9dd1 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/colors.xml @@ -0,0 +1,6 @@ + + + #2c3e50 + #1B3147 + #3498db + diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/ic_launcher_background.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/ic_launcher_background.xml new file mode 100644 index 0000000000..6ec24e6413 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #2C3E50 + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/styles.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/styles.xml new file mode 100644 index 0000000000..5885930df6 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj new file mode 100644 index 0000000000..62c2cefe06 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj @@ -0,0 +1,105 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {3701A0A1-8476-42C6-B5C4-D24129B4A484} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {122416d6-6b49-4ee2-a1e8-b825f31c79fe} + Library + Properties + osu.Game.Rulesets.Taiko.Tests.Android + osu.Game.Rulesets.Taiko.Tests.Android + 512 + True + Resources\Resource.designer.cs + Resource + Off + false + v8.1 + Properties\AndroidManifest.xml + Resources + Assets + Xamarin.Android.Net.AndroidClientHandler + + + True + portable + False + bin\Debug\ + DEBUG;TRACE + prompt + 4 + True + None + False + + + True + pdbonly + True + bin\Release\ + TRACE + prompt + 4 + true + False + SdkOnly + True + + + + + + + + + + + + + + + + + + + + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From e5dd95198a283282936bb76aa463869c84cd22a2 Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Sat, 5 Jan 2019 23:17:55 -0600 Subject: [PATCH 0327/2854] Cleanup project files with common props --- osu.Android.props | 60 ++++++++++ osu.Android/Assets/AboutAssets.txt | 19 --- osu.Android/osu.Android.csproj | 67 +---------- .../Assets/AboutAssets.txt | 19 --- .../MainActivity.cs | 19 ++- .../Resources/AboutResources.txt | 44 ------- .../Resources/Resource.designer.cs | 112 ------------------ .../Resources/layout/activity_main.axml | 7 -- .../mipmap-anydpi-v26/ic_launcher.xml | 5 - .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 - .../Resources/mipmap-hdpi/ic_launcher.png | Bin 1634 -> 0 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 1441 -> 0 bytes .../mipmap-hdpi/ic_launcher_round.png | Bin 3552 -> 0 bytes .../Resources/mipmap-mdpi/ic_launcher.png | Bin 1362 -> 0 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 958 -> 0 bytes .../mipmap-mdpi/ic_launcher_round.png | Bin 2413 -> 0 bytes .../Resources/mipmap-xhdpi/ic_launcher.png | Bin 2307 -> 0 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 2056 -> 0 bytes .../mipmap-xhdpi/ic_launcher_round.png | Bin 4858 -> 0 bytes .../Resources/mipmap-xxhdpi/ic_launcher.png | Bin 3871 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 3403 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_round.png | Bin 8001 -> 0 bytes .../Resources/mipmap-xxxhdpi/ic_launcher.png | Bin 5016 -> 0 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 4889 -> 0 bytes .../mipmap-xxxhdpi/ic_launcher_round.png | Bin 10893 -> 0 bytes .../Resources/values/Strings.xml | 4 - .../Resources/values/colors.xml | 6 - .../values/ic_launcher_background.xml | 4 - .../Resources/values/styles.xml | 11 -- ...u.Game.Rulesets.Catch.Tests.Android.csproj | 82 +++---------- .../Assets/AboutAssets.txt | 19 --- .../MainActivity.cs | 26 ++-- .../Resources/AboutResources.txt | 44 ------- .../Resources/Resource.designer.cs | 112 ------------------ .../Resources/layout/activity_main.axml | 7 -- .../mipmap-anydpi-v26/ic_launcher.xml | 5 - .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 - .../Resources/mipmap-hdpi/ic_launcher.png | Bin 1634 -> 0 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 1441 -> 0 bytes .../mipmap-hdpi/ic_launcher_round.png | Bin 3552 -> 0 bytes .../Resources/mipmap-mdpi/ic_launcher.png | Bin 1362 -> 0 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 958 -> 0 bytes .../mipmap-mdpi/ic_launcher_round.png | Bin 2413 -> 0 bytes .../Resources/mipmap-xhdpi/ic_launcher.png | Bin 2307 -> 0 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 2056 -> 0 bytes .../mipmap-xhdpi/ic_launcher_round.png | Bin 4858 -> 0 bytes .../Resources/mipmap-xxhdpi/ic_launcher.png | Bin 3871 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 3403 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_round.png | Bin 8001 -> 0 bytes .../Resources/mipmap-xxxhdpi/ic_launcher.png | Bin 5016 -> 0 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 4889 -> 0 bytes .../mipmap-xxxhdpi/ic_launcher_round.png | Bin 10893 -> 0 bytes .../Resources/values/Strings.xml | 4 - .../Resources/values/colors.xml | 6 - .../values/ic_launcher_background.xml | 4 - .../Resources/values/styles.xml | 11 -- ...u.Game.Rulesets.Mania.Tests.Android.csproj | 85 +++---------- .../Assets/AboutAssets.txt | 19 --- .../MainActivity.cs | 26 ++-- .../Resources/AboutResources.txt | 44 ------- .../Resources/Resource.designer.cs | 112 ------------------ .../Resources/layout/activity_main.axml | 7 -- .../mipmap-anydpi-v26/ic_launcher.xml | 5 - .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 - .../Resources/mipmap-hdpi/ic_launcher.png | Bin 1634 -> 0 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 1441 -> 0 bytes .../mipmap-hdpi/ic_launcher_round.png | Bin 3552 -> 0 bytes .../Resources/mipmap-mdpi/ic_launcher.png | Bin 1362 -> 0 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 958 -> 0 bytes .../mipmap-mdpi/ic_launcher_round.png | Bin 2413 -> 0 bytes .../Resources/mipmap-xhdpi/ic_launcher.png | Bin 2307 -> 0 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 2056 -> 0 bytes .../mipmap-xhdpi/ic_launcher_round.png | Bin 4858 -> 0 bytes .../Resources/mipmap-xxhdpi/ic_launcher.png | Bin 3871 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 3403 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_round.png | Bin 8001 -> 0 bytes .../Resources/mipmap-xxxhdpi/ic_launcher.png | Bin 5016 -> 0 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 4889 -> 0 bytes .../mipmap-xxxhdpi/ic_launcher_round.png | Bin 10893 -> 0 bytes .../Resources/values/Strings.xml | 4 - .../Resources/values/colors.xml | 6 - .../values/ic_launcher_background.xml | 4 - .../Resources/values/styles.xml | 11 -- ...osu.Game.Rulesets.Osu.Tests.Android.csproj | 85 +++---------- .../Assets/AboutAssets.txt | 19 --- .../MainActivity.cs | 26 ++-- .../Resources/AboutResources.txt | 44 ------- .../Resources/Resource.designer.cs | 112 ------------------ .../Resources/layout/activity_main.axml | 7 -- .../mipmap-anydpi-v26/ic_launcher.xml | 5 - .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 - .../Resources/mipmap-hdpi/ic_launcher.png | Bin 1634 -> 0 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 1441 -> 0 bytes .../mipmap-hdpi/ic_launcher_round.png | Bin 3552 -> 0 bytes .../Resources/mipmap-mdpi/ic_launcher.png | Bin 1362 -> 0 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 958 -> 0 bytes .../mipmap-mdpi/ic_launcher_round.png | Bin 2413 -> 0 bytes .../Resources/mipmap-xhdpi/ic_launcher.png | Bin 2307 -> 0 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 2056 -> 0 bytes .../mipmap-xhdpi/ic_launcher_round.png | Bin 4858 -> 0 bytes .../Resources/mipmap-xxhdpi/ic_launcher.png | Bin 3871 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 3403 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_round.png | Bin 8001 -> 0 bytes .../Resources/mipmap-xxxhdpi/ic_launcher.png | Bin 5016 -> 0 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 4889 -> 0 bytes .../mipmap-xxxhdpi/ic_launcher_round.png | Bin 10893 -> 0 bytes .../Resources/values/Strings.xml | 4 - .../Resources/values/colors.xml | 6 - .../values/ic_launcher_background.xml | 4 - .../Resources/values/styles.xml | 11 -- ...u.Game.Rulesets.Taiko.Tests.Android.csproj | 86 ++++---------- 111 files changed, 179 insertions(+), 1270 deletions(-) create mode 100644 osu.Android.props delete mode 100644 osu.Android/Assets/AboutAssets.txt delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Assets/AboutAssets.txt delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/AboutResources.txt delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/Resource.designer.cs delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/layout/activity_main.axml delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-mdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xhdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/values/Strings.xml delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/values/colors.xml delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/values/ic_launcher_background.xml delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Resources/values/styles.xml delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Assets/AboutAssets.txt delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/AboutResources.txt delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/Resource.designer.cs delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/layout/activity_main.axml delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-mdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xhdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/values/Strings.xml delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/values/colors.xml delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/values/ic_launcher_background.xml delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Resources/values/styles.xml delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Assets/AboutAssets.txt delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/AboutResources.txt delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/Resource.designer.cs delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/layout/activity_main.axml delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-mdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xhdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/values/Strings.xml delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/values/colors.xml delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/values/ic_launcher_background.xml delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Resources/values/styles.xml delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Assets/AboutAssets.txt delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/AboutResources.txt delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/Resource.designer.cs delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/layout/activity_main.axml delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-mdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xhdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/Strings.xml delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/colors.xml delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/ic_launcher_background.xml delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/styles.xml diff --git a/osu.Android.props b/osu.Android.props new file mode 100644 index 0000000000..4c89408203 --- /dev/null +++ b/osu.Android.props @@ -0,0 +1,60 @@ + + + True + portable + False + bin\Debug\ + DEBUG;TRACE + prompt + 4 + True + None + False + false + false + false + CJK;Mideast;Rare;West;Other + + false + + + True + pdbonly + True + bin\Release\ + TRACE + prompt + 4 + true + False + SdkOnly + True + CJK;Mideast;Rare;West;Other + + + + + + + + + + 0.0.7879 + + + 0.0.7879 + + + 0.22.0 + + + 1.1.0 + + + 1.0.0-dev000094 + + + 1.0.0-dev002278 + + + \ No newline at end of file diff --git a/osu.Android/Assets/AboutAssets.txt b/osu.Android/Assets/AboutAssets.txt deleted file mode 100644 index b0633374bd..0000000000 --- a/osu.Android/Assets/AboutAssets.txt +++ /dev/null @@ -1,19 +0,0 @@ -Any raw assets you want to be deployed with your application can be placed in -this directory (and child directories) and given a Build Action of "AndroidAsset". - -These files will be deployed with you package and will be accessible using Android's -AssetManager, like this: - -public class ReadAsset : Activity -{ - protected override void OnCreate (Bundle bundle) - { - base.OnCreate (bundle); - - InputStream input = Assets.Open ("my_asset.txt"); - } -} - -Additionally, some Android functions will automatically load asset files: - -Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); \ No newline at end of file diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index daff6da961..646ed71d44 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -24,43 +24,7 @@ Assets Xamarin.Android.Net.AndroidClientHandler - - True - portable - False - bin\Debug\ - DEBUG;TRACE - prompt - 4 - True - None - False - false - false - false - CJK;Mideast;Rare;West;Other - - false - - - True - pdbonly - True - bin\Release\ - TRACE - prompt - 4 - true - False - SdkOnly - True - - - - - - - + @@ -83,7 +47,6 @@ PreserveNewest - @@ -106,27 +69,6 @@ - - - 0.0.7879 - - - 0.0.7879 - - - 0.22.0 - - - 1.1.0 - - - 1.0.0-dev000094 - - - 1.0.0-dev002278 - - - {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} @@ -157,11 +99,4 @@ - \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Assets/AboutAssets.txt b/osu.Game.Rulesets.Catch.Tests.Android/Assets/AboutAssets.txt deleted file mode 100644 index b0633374bd..0000000000 --- a/osu.Game.Rulesets.Catch.Tests.Android/Assets/AboutAssets.txt +++ /dev/null @@ -1,19 +0,0 @@ -Any raw assets you want to be deployed with your application can be placed in -this directory (and child directories) and given a Build Action of "AndroidAsset". - -These files will be deployed with you package and will be accessible using Android's -AssetManager, like this: - -public class ReadAsset : Activity -{ - protected override void OnCreate (Bundle bundle) - { - base.OnCreate (bundle); - - InputStream input = Assets.Open ("my_asset.txt"); - } -} - -Additionally, some Android functions will automatically load asset files: - -Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs index 34f10dd16b..4b13b86ca2 100644 --- a/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs +++ b/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs @@ -2,21 +2,16 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using Android.App; -using Android.OS; -using Android.Support.V7.App; -using Android.Runtime; -using Android.Widget; +using Android.Content.PM; +using osu.Framework.Android; +using osu.Game.Tests; namespace osu.Game.Rulesets.Catch.Tests.Android { - [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)] - public class MainActivity : AppCompatActivity + [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] + public class MainActivity : AndroidGameActivity { - protected override void OnCreate(Bundle savedInstanceState) - { - base.OnCreate(savedInstanceState); - // Set our view from the "main" layout resource - SetContentView(Resource.Layout.activity_main); - } + protected override Framework.Game CreateGame() + => new OsuTestBrowser(); } } diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/AboutResources.txt b/osu.Game.Rulesets.Catch.Tests.Android/Resources/AboutResources.txt deleted file mode 100644 index c2bca974c4..0000000000 --- a/osu.Game.Rulesets.Catch.Tests.Android/Resources/AboutResources.txt +++ /dev/null @@ -1,44 +0,0 @@ -Images, layout descriptions, binary blobs and string dictionaries can be included -in your application as resource files. Various Android APIs are designed to -operate on the resource IDs instead of dealing with images, strings or binary blobs -directly. - -For example, a sample Android app that contains a user interface layout (main.axml), -an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) -would keep its resources in the "Resources" directory of the application: - -Resources/ - drawable/ - icon.png - - layout/ - main.axml - - values/ - strings.xml - -In order to get the build system to recognize Android resources, set the build action to -"AndroidResource". The native Android APIs do not operate directly with filenames, but -instead operate on resource IDs. When you compile an Android application that uses resources, -the build system will package the resources for distribution and generate a class called "R" -(this is an Android convention) that contains the tokens for each one of the resources -included. For example, for the above Resources layout, this is what the R class would expose: - -public class R { - public class drawable { - public const int icon = 0x123; - } - - public class layout { - public const int main = 0x456; - } - - public class strings { - public const int first_string = 0xabc; - public const int second_string = 0xbcd; - } -} - -You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main -to reference the layout/main.axml file, or R.strings.first_string to reference the first -string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/Resource.designer.cs b/osu.Game.Rulesets.Catch.Tests.Android/Resources/Resource.designer.cs deleted file mode 100644 index a19fe391a8..0000000000 --- a/osu.Game.Rulesets.Catch.Tests.Android/Resources/Resource.designer.cs +++ /dev/null @@ -1,112 +0,0 @@ -#pragma warning disable 1591 -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -[assembly: global::Android.Runtime.ResourceDesignerAttribute("osu.Game.Rulesets.Catch.Tests.Android.Resource", IsApplication=true)] - -namespace osu.Game.Rulesets.Catch.Tests.Android -{ - - - [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] - public partial class Resource - { - - static Resource() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - public static void UpdateIdValues() - { - } - - public partial class Attribute - { - - static Attribute() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Attribute() - { - } - } - - public partial class Id - { - - // aapt resource value: 0x7f050000 - public const int myButton = 2131034112; - - static Id() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Id() - { - } - } - - public partial class Layout - { - - // aapt resource value: 0x7f030000 - public const int Main = 2130903040; - - static Layout() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Layout() - { - } - } - - public partial class Mipmap - { - - // aapt resource value: 0x7f020000 - public const int Icon = 2130837504; - - static Mipmap() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Mipmap() - { - } - } - - public partial class String - { - - // aapt resource value: 0x7f040001 - public const int app_name = 2130968577; - - // aapt resource value: 0x7f040000 - public const int hello = 2130968576; - - static String() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private String() - { - } - } - } -} -#pragma warning restore 1591 diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/layout/activity_main.axml b/osu.Game.Rulesets.Catch.Tests.Android/Resources/layout/activity_main.axml deleted file mode 100644 index ff7a60eb50..0000000000 --- a/osu.Game.Rulesets.Catch.Tests.Android/Resources/layout/activity_main.axml +++ /dev/null @@ -1,7 +0,0 @@ - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 036d09bc5f..0000000000 --- a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 036d09bc5f..0000000000 --- a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 2531cb31efc3a0a3de6113ab9c7845dc1d9654e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1634 zcmV-o2A%ndP)B+Z3$1(8#|f~9B42Y^N-3=o2YCq0YUY$Zu=pM;#hG{lHi%n~Vh z1d1vN#EDO19X?u$`cV z!a}AKG@Bb*#1cdYg8af_;jP69b`k%G1n?0=F^8bI^o>wg-vEliK^U}y^!D|^p|ax; zC|pK=f+FHp!RUAhtlpGGUxJb|wm^5! z<1r%$<$TR02wajxKZ4MiR#aAxDLE(##UNyD|ABr4WoGRF*?@e^2|~Hq(gurSSJH*;Q~5lw{J5A_(PCXBWhzZE${qgzv0{dk-F( z1<}>r181tLiEla&f1j&?p2xjbfp2cTt-c1Ox~?9EhK9`cJ9Vatf)loIoQ@#h&}cIGD>Z#QLE}&(bMo@7Ff|7f#Nm^$PJpVcbj+v~K7wfVwF}=) zRQsc+`=A-+C)vrRvaIC-5u>|;3h z*G4-u#RI<_vuSN~vZ6{|I~q5FFk3%de#+*>UFG>&bq6~ zUEMZ~FIOmFO=kA^5rkp-Msw?^63xvdXVZ-rv@{6{iVO}M!}^Je%2BPbi+(L<5<%~h z2v^D+f<|j%7~cJjOzg*!GPQ{%uE{i%YgcZhuZh{yNlQ}RhaU1jd=K+AopVKP+D}&} zZ3y$llqZiln=Z_A$!qzkGbX0D{?l(v5@1|`QyCvCnQ`eKI>|zj_zo%y#fKf85VhQ} zP)y&j4P*nR3q{-o35iV6nx7QDqq<;WDVIt}|N%`!dgv*y3va8eLNj zU9x(?ieweHfQ*yXk8|=ssZ~qJEz^QoKJ|iGa>ge_Vm_8l}S+UvJ{8g4jr+o#aTSFsz1W;PDP zW765JXGU#3JL>SlIl3NRV2{7B2dLO1cIP)a4ZRYL|MBD36O1#oSgAf}APz5@;x=_U-<=y)Py7*}O5(uu7BL_eLe6Ek7pH|G zMq)FrF1EFq&yruS5b=F=w)fVVoPd(oeRyTFym_Uwyn~L=OL(O?cf^2L5R(SmjORx6 z%nmZf^W=3pkvT*>@osUNi>DULH1hL;y`JGQX$onRCr_U0=H~Viodq!<7Q{3rPk~{G gu#IhOV;e2n|1(WJB~7~kivR!s07*qoM6N<$g7lUVaR2}S diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png deleted file mode 100644 index 7a859c25556af7a2e46e22a2220eaded55628e9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1441 zcma)+`#%#30L6FjMQg%tuA0%p#0??L`*E=rD#U2F4L5n@F+O9Sp;(QwEQy7+?sX?r zCWN(!Hg`+j5k8*H@|yQEtnAi*(D{7M`Tlf1=eKjq)BUsp2nqrK01B=yNUv`!`EH=x zx8$xJQUd^Fuec%|(TT&0V}4orr_==mmCnEuzD+ff8Pg>pJRqsWsD{#?eGPaCu0(sEH_2RG@<6-Nt<8 ztPMUmmAz9Ga$23Y9~p9dqJSgJJ#Jk_r@o13^%d-Xf46i+Lrmz3 zy9(DUDVXj;Zny7nO+yn&W2flEX=C!8&D0zI`G# z8;XmlonoghgRFUY*$+7pPLa}Uy)onw>TT9t(FTV6#BV8&lXWDPRvQW_n~xZ|yLcZjX>m$Eaf1)dwXS`&E^ zkNjO;%;fWywchc=+w4utQ0Vbn%B>b~yy4I#D{?1!P`$P>Wdo+ljCo(tYia04JTc=$$u+IbzDVPFYpm8+AQj+ zGKH zfS{{hN%W)kF+(26oZpkURD5Q_G_z97F6{Jval+TOj-;5y)*Rdo3a$^^k~q5gpTzmp1q@+2X9O z;_VUF>;s~C1~gpFrFoh?{aQ|LlBIYz!z^P~lndX5-ES)p#+9GW*|-WBTzQ*&gKOE` zM##bUaWl`6rZBXw0!~_oUhf+H$tNc@lLZCj0bZT^KSo@C|P?7YR8dP0se1jj z9aA0|7MONf(ZYaLZs$s}r*05fx25-iN6mZe_*Rq%uyz(+^-k;t`!R`?uf~rn#1ZC7 zuv3}UrmMzcBbo4jym@fS5%I+G`GJIC1s$)MQs3Vhld?a2U;w}$@V%dC@%qpO7+3#$ N&GnQ!lI8SQ#{X#Iv!eh2 diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index b8d35b3a1cfe5308bb099a5a1429555c41417e36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3552 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 diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 8f56909cddfa86f1387074bf43003f36d6e67be1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2413 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 b;}yxl--Ss53i@>Q4YQuNcebmsMJN0NT!aL! diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 9737d79c0492b2c8a811621660eba33e79fab959..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4858 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 diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 9133e31b43252d00767a6a3806df9ba68de2d265..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3871 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 diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index c3ae5f5ccdecc01a9b17a2a0c2b1bb20602f0151..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8001 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 z 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 F@PB1o0BisN diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Catch.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index ef89bd5215ffcc38c68b119a7495a77a7084543b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10893 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 diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/Strings.xml b/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/Strings.xml deleted file mode 100644 index f800d6eb45..0000000000 --- a/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/Strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - osu.Game.Rulesets.Catch.Tests.Android - Settings - diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/colors.xml b/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/colors.xml deleted file mode 100644 index 17bb9a9dd1..0000000000 --- a/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/colors.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - #2c3e50 - #1B3147 - #3498db - diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/ic_launcher_background.xml b/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/ic_launcher_background.xml deleted file mode 100644 index 6ec24e6413..0000000000 --- a/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #2C3E50 - \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/styles.xml b/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/styles.xml deleted file mode 100644 index 5885930df6..0000000000 --- a/osu.Game.Rulesets.Catch.Tests.Android/Resources/values/styles.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj index d1e62c46a1..82c171cb46 100644 --- a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj +++ b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj @@ -24,85 +24,35 @@ Assets Xamarin.Android.Net.AndroidClientHandler - - True - portable - False - bin\Debug\ - DEBUG;TRACE - prompt - 4 - True - None - False - - - True - pdbonly - True - bin\Release\ - TRACE - prompt - 4 - true - False - SdkOnly - True - - - - - - - + - osu.licenseheader - - - - Designer - - - - - - - - - - - - - - - - - - - - - - + + %(RecursiveDir)%(Filename)%(Extension) + - + + {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} + osu.Game.Resources + + + {58f6c80c-1253-4a0e-a465-b8c85ebeadf3} + osu.Game.Rulesets.Catch + + + {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D} + osu.Game + - \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Assets/AboutAssets.txt b/osu.Game.Rulesets.Mania.Tests.Android/Assets/AboutAssets.txt deleted file mode 100644 index b0633374bd..0000000000 --- a/osu.Game.Rulesets.Mania.Tests.Android/Assets/AboutAssets.txt +++ /dev/null @@ -1,19 +0,0 @@ -Any raw assets you want to be deployed with your application can be placed in -this directory (and child directories) and given a Build Action of "AndroidAsset". - -These files will be deployed with you package and will be accessible using Android's -AssetManager, like this: - -public class ReadAsset : Activity -{ - protected override void OnCreate (Bundle bundle) - { - base.OnCreate (bundle); - - InputStream input = Assets.Open ("my_asset.txt"); - } -} - -Additionally, some Android functions will automatically load asset files: - -Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs index 09fc7ce770..3c0de47654 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs +++ b/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs @@ -1,19 +1,17 @@ -using Android.App; -using Android.OS; -using Android.Support.V7.App; -using Android.Runtime; -using Android.Widget; +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Android.App; +using Android.Content.PM; +using osu.Framework.Android; +using osu.Game.Tests; namespace osu.Game.Rulesets.Mania.Tests.Android { - [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)] - public class MainActivity : AppCompatActivity + [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] + public class MainActivity : AndroidGameActivity { - protected override void OnCreate(Bundle savedInstanceState) - { - base.OnCreate(savedInstanceState); - // Set our view from the "main" layout resource - SetContentView(Resource.Layout.activity_main); - } + protected override Framework.Game CreateGame() + => new OsuTestBrowser(); } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/AboutResources.txt b/osu.Game.Rulesets.Mania.Tests.Android/Resources/AboutResources.txt deleted file mode 100644 index c2bca974c4..0000000000 --- a/osu.Game.Rulesets.Mania.Tests.Android/Resources/AboutResources.txt +++ /dev/null @@ -1,44 +0,0 @@ -Images, layout descriptions, binary blobs and string dictionaries can be included -in your application as resource files. Various Android APIs are designed to -operate on the resource IDs instead of dealing with images, strings or binary blobs -directly. - -For example, a sample Android app that contains a user interface layout (main.axml), -an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) -would keep its resources in the "Resources" directory of the application: - -Resources/ - drawable/ - icon.png - - layout/ - main.axml - - values/ - strings.xml - -In order to get the build system to recognize Android resources, set the build action to -"AndroidResource". The native Android APIs do not operate directly with filenames, but -instead operate on resource IDs. When you compile an Android application that uses resources, -the build system will package the resources for distribution and generate a class called "R" -(this is an Android convention) that contains the tokens for each one of the resources -included. For example, for the above Resources layout, this is what the R class would expose: - -public class R { - public class drawable { - public const int icon = 0x123; - } - - public class layout { - public const int main = 0x456; - } - - public class strings { - public const int first_string = 0xabc; - public const int second_string = 0xbcd; - } -} - -You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main -to reference the layout/main.axml file, or R.strings.first_string to reference the first -string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/Resource.designer.cs b/osu.Game.Rulesets.Mania.Tests.Android/Resources/Resource.designer.cs deleted file mode 100644 index c27ae8a832..0000000000 --- a/osu.Game.Rulesets.Mania.Tests.Android/Resources/Resource.designer.cs +++ /dev/null @@ -1,112 +0,0 @@ -#pragma warning disable 1591 -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -[assembly: global::Android.Runtime.ResourceDesignerAttribute("osu.Game.Rulesets.Mania.Tests.Android.Resource", IsApplication=true)] - -namespace osu.Game.Rulesets.Mania.Tests.Android -{ - - - [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] - public partial class Resource - { - - static Resource() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - public static void UpdateIdValues() - { - } - - public partial class Attribute - { - - static Attribute() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Attribute() - { - } - } - - public partial class Id - { - - // aapt resource value: 0x7f050000 - public const int myButton = 2131034112; - - static Id() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Id() - { - } - } - - public partial class Layout - { - - // aapt resource value: 0x7f030000 - public const int Main = 2130903040; - - static Layout() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Layout() - { - } - } - - public partial class Mipmap - { - - // aapt resource value: 0x7f020000 - public const int Icon = 2130837504; - - static Mipmap() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Mipmap() - { - } - } - - public partial class String - { - - // aapt resource value: 0x7f040001 - public const int app_name = 2130968577; - - // aapt resource value: 0x7f040000 - public const int hello = 2130968576; - - static String() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private String() - { - } - } - } -} -#pragma warning restore 1591 diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/layout/activity_main.axml b/osu.Game.Rulesets.Mania.Tests.Android/Resources/layout/activity_main.axml deleted file mode 100644 index ff7a60eb50..0000000000 --- a/osu.Game.Rulesets.Mania.Tests.Android/Resources/layout/activity_main.axml +++ /dev/null @@ -1,7 +0,0 @@ - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 036d09bc5f..0000000000 --- a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 036d09bc5f..0000000000 --- a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 2531cb31efc3a0a3de6113ab9c7845dc1d9654e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1634 zcmV-o2A%ndP)B+Z3$1(8#|f~9B42Y^N-3=o2YCq0YUY$Zu=pM;#hG{lHi%n~Vh z1d1vN#EDO19X?u$`cV z!a}AKG@Bb*#1cdYg8af_;jP69b`k%G1n?0=F^8bI^o>wg-vEliK^U}y^!D|^p|ax; zC|pK=f+FHp!RUAhtlpGGUxJb|wm^5! z<1r%$<$TR02wajxKZ4MiR#aAxDLE(##UNyD|ABr4WoGRF*?@e^2|~Hq(gurSSJH*;Q~5lw{J5A_(PCXBWhzZE${qgzv0{dk-F( z1<}>r181tLiEla&f1j&?p2xjbfp2cTt-c1Ox~?9EhK9`cJ9Vatf)loIoQ@#h&}cIGD>Z#QLE}&(bMo@7Ff|7f#Nm^$PJpVcbj+v~K7wfVwF}=) zRQsc+`=A-+C)vrRvaIC-5u>|;3h z*G4-u#RI<_vuSN~vZ6{|I~q5FFk3%de#+*>UFG>&bq6~ zUEMZ~FIOmFO=kA^5rkp-Msw?^63xvdXVZ-rv@{6{iVO}M!}^Je%2BPbi+(L<5<%~h z2v^D+f<|j%7~cJjOzg*!GPQ{%uE{i%YgcZhuZh{yNlQ}RhaU1jd=K+AopVKP+D}&} zZ3y$llqZiln=Z_A$!qzkGbX0D{?l(v5@1|`QyCvCnQ`eKI>|zj_zo%y#fKf85VhQ} zP)y&j4P*nR3q{-o35iV6nx7QDqq<;WDVIt}|N%`!dgv*y3va8eLNj zU9x(?ieweHfQ*yXk8|=ssZ~qJEz^QoKJ|iGa>ge_Vm_8l}S+UvJ{8g4jr+o#aTSFsz1W;PDP zW765JXGU#3JL>SlIl3NRV2{7B2dLO1cIP)a4ZRYL|MBD36O1#oSgAf}APz5@;x=_U-<=y)Py7*}O5(uu7BL_eLe6Ek7pH|G zMq)FrF1EFq&yruS5b=F=w)fVVoPd(oeRyTFym_Uwyn~L=OL(O?cf^2L5R(SmjORx6 z%nmZf^W=3pkvT*>@osUNi>DULH1hL;y`JGQX$onRCr_U0=H~Viodq!<7Q{3rPk~{G gu#IhOV;e2n|1(WJB~7~kivR!s07*qoM6N<$g7lUVaR2}S diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png deleted file mode 100644 index 7a859c25556af7a2e46e22a2220eaded55628e9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1441 zcma)+`#%#30L6FjMQg%tuA0%p#0??L`*E=rD#U2F4L5n@F+O9Sp;(QwEQy7+?sX?r zCWN(!Hg`+j5k8*H@|yQEtnAi*(D{7M`Tlf1=eKjq)BUsp2nqrK01B=yNUv`!`EH=x zx8$xJQUd^Fuec%|(TT&0V}4orr_==mmCnEuzD+ff8Pg>pJRqsWsD{#?eGPaCu0(sEH_2RG@<6-Nt<8 ztPMUmmAz9Ga$23Y9~p9dqJSgJJ#Jk_r@o13^%d-Xf46i+Lrmz3 zy9(DUDVXj;Zny7nO+yn&W2flEX=C!8&D0zI`G# z8;XmlonoghgRFUY*$+7pPLa}Uy)onw>TT9t(FTV6#BV8&lXWDPRvQW_n~xZ|yLcZjX>m$Eaf1)dwXS`&E^ zkNjO;%;fWywchc=+w4utQ0Vbn%B>b~yy4I#D{?1!P`$P>Wdo+ljCo(tYia04JTc=$$u+IbzDVPFYpm8+AQj+ zGKH zfS{{hN%W)kF+(26oZpkURD5Q_G_z97F6{Jval+TOj-;5y)*Rdo3a$^^k~q5gpTzmp1q@+2X9O z;_VUF>;s~C1~gpFrFoh?{aQ|LlBIYz!z^P~lndX5-ES)p#+9GW*|-WBTzQ*&gKOE` zM##bUaWl`6rZBXw0!~_oUhf+H$tNc@lLZCj0bZT^KSo@C|P?7YR8dP0se1jj z9aA0|7MONf(ZYaLZs$s}r*05fx25-iN6mZe_*Rq%uyz(+^-k;t`!R`?uf~rn#1ZC7 zuv3}UrmMzcBbo4jym@fS5%I+G`GJIC1s$)MQs3Vhld?a2U;w}$@V%dC@%qpO7+3#$ N&GnQ!lI8SQ#{X#Iv!eh2 diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index b8d35b3a1cfe5308bb099a5a1429555c41417e36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3552 zcmV<64IlD}P)o8zx62qSGZVDjFcw zmxU;G#z^HzQ!GXJ-*69pbEeNn;$q%9`<^_ve6S+hkfX>pEmUTks+2m@VN4e=-BfB# zcQM@~beFnE|8|&qR$IOR+Cm@fKKV*xuU`Zdvl=LK4a4vxD=}@uREG)CWaLRqJ5ybP zu6!%iC+?fAzSb|q<0OVH@(J1H8ThTgk0;W=21TJYwd22S48?0q?Ql<_H9oW?Q#<^| zeirUq0oDLxz*ubc^EioOzd5Deq{k}q4=YI_6Qm}Lx&A|+|0D}zEJqe60pgP7hwE|CF z@#G3rLLN!=hY3#Mncm#=bNubjDVN#!%R!#+yMuUTdtd@=nOZsg2kv6qi*x zzDFd9=@0{x|A>LZ;?=}}RP0ia7?F(2EK$;G^~ix^1(KmvlA1T%Me0V!5Mp(azrt*g z`GKR#Hle}^)6nEOi&5p=B`&3>XD&k7hNpOg6rWXgIVwRD#GYff08(lhSI*BM130r6 ztwLvix`bL=@1gtm@4J-l-fc!-e{&2~Oqs{qaK~p9f7wxs>V|45HOAS_daGw5xEuU;CIJ+92}tg z4<4ZP8$L$Eb4K%sldwI?Dr*+0^Cav!^8yGXz0q0enY&~)R;yOG00dN1dkvL6IfJJZ zVXu}^_&HPQzwpQx>^t=9m8u@|rU zGZkWRl_Ic3Qgwcn12rQ-p|)rUPVR0xZ|g z#6I?<=DMiep91ftqa7MkB{^?D-ZoQ_q4o#Zz5>gjTpeUp0 z3G@w~C|7{qc>5!&4by(n%Jp`iuf291jemANFJmoJ=kLN8bXoMLmT3fvj9{#fSNW<} zPWfc?!`YwgG7Mhr!;M=hJH@mEk5k`p+aWlYYie<%{DirkwsaCDMRv!-QbfD`F`U&* zo>5d65*-)D#>B#V$@hY}ZNj;cW4C_i&aXIcn%mJeYW9gE&#PbekM-NS=wn4l1Pv@ zMzD%cy$ABGjazr~@-TOPy^E&IU2N`Sc+MEK;iFAm2A0h&E$DX(ms?2dx_7F01)(i1 zt(1M_?Cw+ZHd@;uW{XK*Y{?Ju0ch5um8c1;jWfXy;v{GISLTsgmo00A* z8#H~vA1NDj?m{&xWtC4M{&ANL0wWz5DipHQ4JPOCWyT?wRHhZzZ zeZJFjg#>%C8}$u6=EclzKE2=~#v<4nARyoPtdc`q14SwhI__K?1o_n~Yb@iSRqNli zs3kSrZnRJbh=V@m8MSxBLHE(SRrcc`CQy{7<{rUV_*?AJCSmpCIGg>1Pb59_r4>#^ z(nn96vdGRMk_L&gj-oWj!lL9s60`o2)KQE1 zB&*KmVz3NtmJIw>|N6;iRC%JSJZi=ZuUXilH+U`xaL>hXvZ^UVLRHpEz@n>UwO_O{ zvxM&!UB21;HmhtN?84Q$8@99YqbIS1J!uhfSMyjD;F8UQWTYp=gUt@U%M2UX5p%4Kzf zcJbV2CClLAM^#U{Xz6L zJdsKRtEu5+&Ybs{fi3b28WN?!`q@NF5kI%@$vey#&m~jmHwA`7A1U07i4e+zpQNm|hsmsx_shxjsk(;ai>lwhlEheA0qLHoISKxd?ut+1!iOjA0S8%WxDr|ybBIOiWdU3lm z`-eQ?oQ5>5uzjd7ej1)jB$<=TK2p#pFi;o>wmV#sI7_BxK%(~=dnzy;Aqovnm`E`X z<`57N71R@7aPSTY2!M`7!(!s5%GHI9gb|Mfi808OJ5S4R8Y+~7+uvURZz0;p z$0s#rxNa}R6fBi{*o(kCWK;@xicx9yVII?fSHiQ~j)?aO3JQYL#1XJ5KSG|e0(*zs zOa;K*K(T=V9)Oo{S<-6w00i(zcy;?%WAK3C1Mvl$9;N=lVFfV>njP|tB6AU(uC?@> z>XDSeeB2Vo7A9ow#Js=(UMbBR<;r{YlREwU{QN+-qoC#%8Y!79O45D}o{p&oU}|T; z>W*ZQ?|P6=Q;;J~SYlu-7;}g~TnRh?FN7zL`Pd01O}@Uq@HG|@9IGE37W1SqA>&g? zTHZBSPGLzE$?Ht!kDJ76DBvsz?sa_Jgn8b?lwYVN8t5Cwz+*wV0=BG(XdZfBYHVG7 zgM)+piP`~Bia~<{b0Q>(OJWkWdn9S2YM^=t1#;S6S%7Af;8{qR!SG`HQiJ>24Sho2 zL}ElRCX5X{JPMx?>I+FAk*G-6f(-`qF+V?Th(J13AWvQ!t;+aJJVO7iBze?19H-RE z(+le5=|zn+71YB$_zj+cXCrYNXbXK1X@NeYU<{IQJ~|&+Vuu8n20(yGz=FMhv2fZG zydQSKNf0W)qyvJ7=KBu`Edqjn!#(_43OobPk~Yv*0DY05b$~lvw>!Y<4{sZy*+GK_ z4fXQ!4TV}T0S=6OG@&SRFASc6XQ2&|l>WaZP#hR`YNGwS5C*yUv?lc$Zn7uu(=Jd zBQr(wEwogv4g_{iFq~uA3k~Z|L@DvE#_JQ>CKxj(Q|L@;_pg7{hnT!9|ZQb+#ochnl1kg9D@G4hNk|1@c1c) z{PkOR|2qXG{Wo$7`M-9{ZVdTtdk+0Kb_u1e2S8@7a?0x`-IJ*AtKYskrENiB%2SAk%zG8F7zQf=Uw)BkpfBE_?MDjX& z@xO&fB(T^G|G)3ZNu2smpTF|o#wUh09?%1ZEU4JTml;2Q`T9S*q6Mrzuc{3gQ-A*d z{Q2vDYEeB{thm1G|F`eoaq0)fT1(#ya4b^Y1D+8X|DV5nO|V2c3(TM(uHGc5|Nf&V|J{K3i0U2yrD0-<#2-I@{x5Ip1M7*&D*x{joegF;bWbC? z(kra(q`n6-N}I4|UUdBS-G~1{3Hjh;&W{YUBz~nhg z|9eJe{4Z(f##+{cVkED+{l6Db&737`v6TNa;pIQg8*`u<_1?qB7^TPOFJHjLD9$4G z$4`iwAE;_BU%Le^B3KtGndh}^?w7N zp&3LI9GX_%Z^hMgm2i3hX^M$M&D3?3wyocP$TZWyV~|^v4II`1-Ns4G92qkYkC3*q zq5Vcp3$J%tR^A_hzW)HC>4{->YFc`|Q_{EF#LX=TNWTIEGZ*dOIh!!#7am`0)iN z!-Y*JzdqP8rN&2Y&y2(+EtA?m9-5+}#BXAw@$*D;zxcf=lRhYP2`ZYNoGdU|=;=Y1 z!-o@UOzpBVHoTpyopyF#@i)8YcdVaV?2ljDUj6>w?`yyA*Pf5cUSE9b6wq26;8J@~ z){!@7GpTmNE>2kO_POn1zf8`~}P?%{85(;s&nc+C&;t$4D5$+f9? z-8>e~Z&%(_OwrVd==PGc4mhTFjVafjdCqsM|EvEe$2)U;a9s0IGofbtHcpKz;cJR= z`DNzVI-iMtrg<$r*EFejE8l0oMM3e)a|=o;x>Mhk@*n)xx%2Rrt=4TnivwP5zpS-& z@5h3w<{9>vH!6KP74q!po!oh)$BI~jUu}4P|5ofvi@(2i9NyELbZ$qD}PI&+JJ3+^f2=YEuP zjpepXu;`->)%n@lB|b@Iv$k0qhJJp%S?O9t?)zjLwwY?z@=v^12)=lt^ZcwNoye^x z_uu*-x}ntY`mc3S`yMaaHuurqE~e`{G_IsMZdhw*{kDDS9h3WSQa;8d3vwO)d?WE+ z%*LAIs=2#$t=BZmPTP}xMpj0I9ti9_c{r`p zu+;ELV)~|tmk}}-GjAWQO5U<}Lr?bB5UX>pYf5~UOnY%ZTQR${nq6YQOHc15>q%#$ zl8$8k_1fsCw;<~OiJ-OiE?f7RJWt%N`#e!y=2`BhIqju|a?kW5QupmV#wx6HrSs?J z&nJroVy6i|*Og1U`{c;a^^dPvTfNJjdCg1nUS<*OC dK7&Kx57tYsZ49$p7vBM?@pScbS?83{1OVHE%8UR2 diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 8f56909cddfa86f1387074bf43003f36d6e67be1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2413 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 b;}yxl--Ss53i@>Q4YQuNcebmsMJN0NT!aL! diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 9737d79c0492b2c8a811621660eba33e79fab959..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4858 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 diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 9133e31b43252d00767a6a3806df9ba68de2d265..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3871 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 diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index c3ae5f5ccdecc01a9b17a2a0c2b1bb20602f0151..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8001 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 z 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 F@PB1o0BisN diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Mania.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index ef89bd5215ffcc38c68b119a7495a77a7084543b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10893 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 diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/Strings.xml b/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/Strings.xml deleted file mode 100644 index 6ab86b7262..0000000000 --- a/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/Strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - osu.Game.Rulesets.Mania.Tests.Android - Settings - diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/colors.xml b/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/colors.xml deleted file mode 100644 index 17bb9a9dd1..0000000000 --- a/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/colors.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - #2c3e50 - #1B3147 - #3498db - diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/ic_launcher_background.xml b/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/ic_launcher_background.xml deleted file mode 100644 index 6ec24e6413..0000000000 --- a/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #2C3E50 - \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/styles.xml b/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/styles.xml deleted file mode 100644 index 5885930df6..0000000000 --- a/osu.Game.Rulesets.Mania.Tests.Android/Resources/values/styles.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj index 5fbee695b4..a761f2569a 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj +++ b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj @@ -24,82 +24,35 @@ Assets Xamarin.Android.Net.AndroidClientHandler - - True - portable - False - bin\Debug\ - DEBUG;TRACE - prompt - 4 - True - None - False - - - True - pdbonly - True - bin\Release\ - TRACE - prompt - 4 - true - False - SdkOnly - True - - - - - - - + - - + + osu.licenseheader + - - - Designer - - - - - - - - - - - - - - - - - - - - - - + + %(RecursiveDir)%(Filename)%(Extension) + - + + {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} + osu.Game.Resources + + + {48f4582b-7687-4621-9cbe-5c24197cb536} + osu.Game.Rulesets.Mania + + + {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D} + osu.Game + - \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Assets/AboutAssets.txt b/osu.Game.Rulesets.Osu.Tests.Android/Assets/AboutAssets.txt deleted file mode 100644 index b0633374bd..0000000000 --- a/osu.Game.Rulesets.Osu.Tests.Android/Assets/AboutAssets.txt +++ /dev/null @@ -1,19 +0,0 @@ -Any raw assets you want to be deployed with your application can be placed in -this directory (and child directories) and given a Build Action of "AndroidAsset". - -These files will be deployed with you package and will be accessible using Android's -AssetManager, like this: - -public class ReadAsset : Activity -{ - protected override void OnCreate (Bundle bundle) - { - base.OnCreate (bundle); - - InputStream input = Assets.Open ("my_asset.txt"); - } -} - -Additionally, some Android functions will automatically load asset files: - -Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs index 77a75f0d67..622eadef70 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs +++ b/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs @@ -1,19 +1,17 @@ -using Android.App; -using Android.OS; -using Android.Support.V7.App; -using Android.Runtime; -using Android.Widget; +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Android.App; +using Android.Content.PM; +using osu.Framework.Android; +using osu.Game.Tests; namespace osu.Game.Rulesets.Osu.Tests.Android { - [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)] - public class MainActivity : AppCompatActivity + [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] + public class MainActivity : AndroidGameActivity { - protected override void OnCreate(Bundle savedInstanceState) - { - base.OnCreate(savedInstanceState); - // Set our view from the "main" layout resource - SetContentView(Resource.Layout.activity_main); - } + protected override Framework.Game CreateGame() + => new OsuTestBrowser(); } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/AboutResources.txt b/osu.Game.Rulesets.Osu.Tests.Android/Resources/AboutResources.txt deleted file mode 100644 index c2bca974c4..0000000000 --- a/osu.Game.Rulesets.Osu.Tests.Android/Resources/AboutResources.txt +++ /dev/null @@ -1,44 +0,0 @@ -Images, layout descriptions, binary blobs and string dictionaries can be included -in your application as resource files. Various Android APIs are designed to -operate on the resource IDs instead of dealing with images, strings or binary blobs -directly. - -For example, a sample Android app that contains a user interface layout (main.axml), -an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) -would keep its resources in the "Resources" directory of the application: - -Resources/ - drawable/ - icon.png - - layout/ - main.axml - - values/ - strings.xml - -In order to get the build system to recognize Android resources, set the build action to -"AndroidResource". The native Android APIs do not operate directly with filenames, but -instead operate on resource IDs. When you compile an Android application that uses resources, -the build system will package the resources for distribution and generate a class called "R" -(this is an Android convention) that contains the tokens for each one of the resources -included. For example, for the above Resources layout, this is what the R class would expose: - -public class R { - public class drawable { - public const int icon = 0x123; - } - - public class layout { - public const int main = 0x456; - } - - public class strings { - public const int first_string = 0xabc; - public const int second_string = 0xbcd; - } -} - -You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main -to reference the layout/main.axml file, or R.strings.first_string to reference the first -string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/Resource.designer.cs b/osu.Game.Rulesets.Osu.Tests.Android/Resources/Resource.designer.cs deleted file mode 100644 index f229c3f960..0000000000 --- a/osu.Game.Rulesets.Osu.Tests.Android/Resources/Resource.designer.cs +++ /dev/null @@ -1,112 +0,0 @@ -#pragma warning disable 1591 -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -[assembly: global::Android.Runtime.ResourceDesignerAttribute("osu.Game.Rulesets.Osu.Tests.Android.Resource", IsApplication=true)] - -namespace osu.Game.Rulesets.Osu.Tests.Android -{ - - - [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] - public partial class Resource - { - - static Resource() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - public static void UpdateIdValues() - { - } - - public partial class Attribute - { - - static Attribute() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Attribute() - { - } - } - - public partial class Id - { - - // aapt resource value: 0x7f050000 - public const int myButton = 2131034112; - - static Id() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Id() - { - } - } - - public partial class Layout - { - - // aapt resource value: 0x7f030000 - public const int Main = 2130903040; - - static Layout() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Layout() - { - } - } - - public partial class Mipmap - { - - // aapt resource value: 0x7f020000 - public const int Icon = 2130837504; - - static Mipmap() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Mipmap() - { - } - } - - public partial class String - { - - // aapt resource value: 0x7f040001 - public const int app_name = 2130968577; - - // aapt resource value: 0x7f040000 - public const int hello = 2130968576; - - static String() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private String() - { - } - } - } -} -#pragma warning restore 1591 diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/layout/activity_main.axml b/osu.Game.Rulesets.Osu.Tests.Android/Resources/layout/activity_main.axml deleted file mode 100644 index ff7a60eb50..0000000000 --- a/osu.Game.Rulesets.Osu.Tests.Android/Resources/layout/activity_main.axml +++ /dev/null @@ -1,7 +0,0 @@ - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 036d09bc5f..0000000000 --- a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 036d09bc5f..0000000000 --- a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 2531cb31efc3a0a3de6113ab9c7845dc1d9654e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1634 zcmV-o2A%ndP)B+Z3$1(8#|f~9B42Y^N-3=o2YCq0YUY$Zu=pM;#hG{lHi%n~Vh z1d1vN#EDO19X?u$`cV z!a}AKG@Bb*#1cdYg8af_;jP69b`k%G1n?0=F^8bI^o>wg-vEliK^U}y^!D|^p|ax; zC|pK=f+FHp!RUAhtlpGGUxJb|wm^5! z<1r%$<$TR02wajxKZ4MiR#aAxDLE(##UNyD|ABr4WoGRF*?@e^2|~Hq(gurSSJH*;Q~5lw{J5A_(PCXBWhzZE${qgzv0{dk-F( z1<}>r181tLiEla&f1j&?p2xjbfp2cTt-c1Ox~?9EhK9`cJ9Vatf)loIoQ@#h&}cIGD>Z#QLE}&(bMo@7Ff|7f#Nm^$PJpVcbj+v~K7wfVwF}=) zRQsc+`=A-+C)vrRvaIC-5u>|;3h z*G4-u#RI<_vuSN~vZ6{|I~q5FFk3%de#+*>UFG>&bq6~ zUEMZ~FIOmFO=kA^5rkp-Msw?^63xvdXVZ-rv@{6{iVO}M!}^Je%2BPbi+(L<5<%~h z2v^D+f<|j%7~cJjOzg*!GPQ{%uE{i%YgcZhuZh{yNlQ}RhaU1jd=K+AopVKP+D}&} zZ3y$llqZiln=Z_A$!qzkGbX0D{?l(v5@1|`QyCvCnQ`eKI>|zj_zo%y#fKf85VhQ} zP)y&j4P*nR3q{-o35iV6nx7QDqq<;WDVIt}|N%`!dgv*y3va8eLNj zU9x(?ieweHfQ*yXk8|=ssZ~qJEz^QoKJ|iGa>ge_Vm_8l}S+UvJ{8g4jr+o#aTSFsz1W;PDP zW765JXGU#3JL>SlIl3NRV2{7B2dLO1cIP)a4ZRYL|MBD36O1#oSgAf}APz5@;x=_U-<=y)Py7*}O5(uu7BL_eLe6Ek7pH|G zMq)FrF1EFq&yruS5b=F=w)fVVoPd(oeRyTFym_Uwyn~L=OL(O?cf^2L5R(SmjORx6 z%nmZf^W=3pkvT*>@osUNi>DULH1hL;y`JGQX$onRCr_U0=H~Viodq!<7Q{3rPk~{G gu#IhOV;e2n|1(WJB~7~kivR!s07*qoM6N<$g7lUVaR2}S diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png deleted file mode 100644 index 7a859c25556af7a2e46e22a2220eaded55628e9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1441 zcma)+`#%#30L6FjMQg%tuA0%p#0??L`*E=rD#U2F4L5n@F+O9Sp;(QwEQy7+?sX?r zCWN(!Hg`+j5k8*H@|yQEtnAi*(D{7M`Tlf1=eKjq)BUsp2nqrK01B=yNUv`!`EH=x zx8$xJQUd^Fuec%|(TT&0V}4orr_==mmCnEuzD+ff8Pg>pJRqsWsD{#?eGPaCu0(sEH_2RG@<6-Nt<8 ztPMUmmAz9Ga$23Y9~p9dqJSgJJ#Jk_r@o13^%d-Xf46i+Lrmz3 zy9(DUDVXj;Zny7nO+yn&W2flEX=C!8&D0zI`G# z8;XmlonoghgRFUY*$+7pPLa}Uy)onw>TT9t(FTV6#BV8&lXWDPRvQW_n~xZ|yLcZjX>m$Eaf1)dwXS`&E^ zkNjO;%;fWywchc=+w4utQ0Vbn%B>b~yy4I#D{?1!P`$P>Wdo+ljCo(tYia04JTc=$$u+IbzDVPFYpm8+AQj+ zGKH zfS{{hN%W)kF+(26oZpkURD5Q_G_z97F6{Jval+TOj-;5y)*Rdo3a$^^k~q5gpTzmp1q@+2X9O z;_VUF>;s~C1~gpFrFoh?{aQ|LlBIYz!z^P~lndX5-ES)p#+9GW*|-WBTzQ*&gKOE` zM##bUaWl`6rZBXw0!~_oUhf+H$tNc@lLZCj0bZT^KSo@C|P?7YR8dP0se1jj z9aA0|7MONf(ZYaLZs$s}r*05fx25-iN6mZe_*Rq%uyz(+^-k;t`!R`?uf~rn#1ZC7 zuv3}UrmMzcBbo4jym@fS5%I+G`GJIC1s$)MQs3Vhld?a2U;w}$@V%dC@%qpO7+3#$ N&GnQ!lI8SQ#{X#Iv!eh2 diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index b8d35b3a1cfe5308bb099a5a1429555c41417e36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3552 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 diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 8f56909cddfa86f1387074bf43003f36d6e67be1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2413 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 b;}yxl--Ss53i@>Q4YQuNcebmsMJN0NT!aL! diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 9737d79c0492b2c8a811621660eba33e79fab959..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4858 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 diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 9133e31b43252d00767a6a3806df9ba68de2d265..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3871 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 diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index c3ae5f5ccdecc01a9b17a2a0c2b1bb20602f0151..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8001 zcmV-HAHLv;P)_otvA^2tyUR8VoCfH?7Uf~Y8h zGGvL!9~U1e2+EQ@WE5!2`JeaRb4v*AP1@XhlD4_e^FD<(x#OJQec#_Z&U@V4T!-s$ z9j?Q5xDMCRfsbx(Zj;?X1`i(Golm&WvEOkWT@EAwg5u(04-gg*b^)Q=wdZqzt5X5S z3@E&xRqAU4(t6iMrj`y!NG~3kqBiu;%rFkf27!OW@8ECn8ThO4HTO;#7xy{;~-`#PSee#+yl`$7 zsLK|B`URc=p2hMdam~0$z)>3q=>?G-oqR?n&P@dVyd_S<+u&%Xj+V7fH_Q{po6c#f1Tbw|%*|St=SEuXXwPQvs;F+N*+6v& zkIGS=8;n&;W7y>ag7A-w!kVPC!v1S4JS!J)TIEOFIQ3rxW7krsqtmA#u9&R4Ay`gb z(K=n%T(#4z;juGa*V5Q_dcLDB>_6S5b%fDI*u>4?G*GAIMVyzVRuA^V55I_W&0So_ z?m#5#@*8Uw%Vd?_ozm6kh@LvXJd~7GxJ;G^CQWUu{Z64R4)0XtntK~kATU^H+D^c8 z$u;=`ixI{YgUC>`Lsn3k+$l5>_W&w=jT%4PK^J%^fyih&sMJ+tbZ8JYn=PYBg&*pu z3p}(zRC`R3SDx7+%^8RK)Pkyn^uoFWF7P)0TEDbH=%m>4xeM{1Dq*;BhR7 zR0aLE%d(6S9mK_F16jmX-{=C5qlF!NRYBGF5=p+Vvj-cwP3%~$8xBY7p`fb-9)Y#aFnwpwAl)ydj$3Pl0ek#%w z51>+@mReAKLYiq%I18yZ<2|M|G!vun*52{p6m;a+@eT(ZOF41!6dE_>89JuSh)r33 z`35{^-5t({xYA0jBB#*iJ*5L~K|BBWv%`ajlRbO)V^e%54N~2p($^q)UfEL?rNoXQ z%_@UQN1OM6x_^G|JDmnRAPo%-43En$9Ylo>r502nnWnhdQ6S>fo;$vw?`YTbTtDU^ zbm+*jP6Z&4bLY>ak$3%@nkiH2%D3P-^rUXeu9&X6`)Hf4tkQw#tCj0IBx$xqR(|^( z(qlKDjw$Ph6ghn+P}V|h!z8t#EFRy;3A1h&bcpk~Dd?XwXFDZ$K;YRPe(YIFh5Fc( z{rP(^XJ)J^JN;zjs>jaI){f-zdLwI2BW-GSncYwsaxP zspxKfGjY!Em&bMRq8Bi%L(`s{$B@m=4xmey8qf>#7ox0^fm8@}O0TM>#54m9Ld~c+ z_cWtvF>UQrIrI*+W9RNp4<1eq9y)@mhL53^=1}C8eaXg#L^5NX_EGDrOU%})BU;?& zgC)y4Epcv5KKp7F()J!qgHT^i$*)AxOhZ2rwGgL$>OP~rUcLWK_o5T0PIoErfE+!3 z0*$(V5)A+~GFm97Y=tOV$b$P&4I1johoTj$*LOMaaPs4?+mVJE7pg!BYJG{|T8Q(! z)W+Jmw6)KJlb=Cn&zGwnS);jE(y!@=IfB$9)QGN1`8o z{I$!1hZ6{0^c^yqN?b^(>w8L~%9gQlApt-{RGGWVQ2PLF?K6AcLUi%sr7jO3kOl89 z65EV1bDLUFjij35$uQ?yt=3bBoEL}(cHK$e9y&b<%dZ>VDf3>htLBsDDFFu*Z zK*D7DXFTUVX7g_!_fhC73^d8Jrepw`_s&Ny;8+x&ee~IKW^BYK)0Ie~&aZ&Ew~I^@ z71kY-t7mAMuUqeXlqvhPC!e%y&tGWg?rUY=fkWa(kum9oR76YH27!#bJs=wU&|~70 zX?;JGoK^e^%)LEkj8R_^YPCN`<~Ca7Ij`?^*lpin*CakV<3+{<0`atz>fvKW&E~J( zuo?Bcer$`^2APEK?fm)rcAx*-jXxk`%?MG+G-Jkc%YF-#NJ86f#yIn()HO$*#g8~+ zd1&e^yWRFDpP$EDs6Jxs!|3o);rZ3kV<*tf_e|t{MsUe5UcA`uYh1i^2|YG*j@Vj= zi3!E2^|kFbW8_O7Se;FyWxk4PZxkfo_2=FL%xVX|V*EL8yeGI8dh`8HnR=zxu3K^4 z?Tl%)_d2`(+RtcMvCWuNQ}`lapgjQM)RvdpSi6pf_mx@PA3gQr0)c{Wjp+6NF6Irs zL820t0ST#n`V1b$3tBcTaZ!+L{k*q75;0p3-dHV?<@DZ+G2q({GsfnWwM#`kaZCYc%YN);0tcIqxe~S22_Zd4^oi;xE1y)TF?#>ouYjo{^wp6J+R<)CHpf3u?96tF8RUGgV(bi-!3c zdDjGVQiNZ-uoCj zdR)5-_0QpRkGlU+{2ctxXOD)n>egdY{@AQnuoE&sl;o-+x6i@Q*jNe6gKVf1BC4vp zOk0}Gwr3HKK=&SaEBblcZ=$CG{@AmZ_bmmE^2rw~+swfr;K}Fd0YBNiRs3oK2wU)Z zfOe%dbma{aSyqwFQEBoa52dc}AhRtbMKNEmzV!jaA!yXp%z6DiUbnZ;;MQK@8%U zubLa~M8}Swq?pY7GXf1rV4q zDDOy2*FVX`1Z@Ej`H(mM;!9!?XmG7R`QjVuMe^@0{(|={Egv!(ZToGPb?t*S6=*EJ zXME$mPXviEwMEu#`agjy7uhPsq)g*mj8kQsE6;EsU+lsy5eqy%VPk*szNA#H3k8P;B3WV8iMG zAL^kt)NB&Ngu&|4_1|xGSWV69_22V)EKm*b{nlSvJqKtgcm}@jL*0&}mLNe1FtolA zVy-dJ4}}J*4Yk|F0MNAO=Gs*gBLs-XjGM}PkM}t8}FKMRr@^9KDXTW zAKvc(e>&#`OOPOJ@$RCfcK2Ou29U1riIBMDG`5$JbpUzAD6}c~i)VxkB0?pg*yW^c zk)411#duwO3EsJHf7opHKKS%2-U)%AAx*d4mMA&&6A&VpsMM984UbRJ+6*8`iZ&f< zpn4$zG;YdFr|PT$T4??|A2W4Gt@dFYcq=-5^f=?T4;}p=Z>`VMFD`Jpwfm3Fd_|bD zj$VB)^h`*}2W;>Hhy)S66Vyl(v3 zes{u#pHRRiR5~LjS*f=g3*rEjpvuYW3IJl_CfMWRyKh*F1;uWBpMls?ef@<_3m|1) z`6ZhGMAVbFM46p|zj$6q08M%3Wv6Uhz*mX^=56VUHB55{i0`!OUG^J+R<7OTbkAq4 zO0o?csJ>@{3{03eRx_Sf0Td<6QsFQEBcvBL`d^dL1p(@Tg%a?ppcf&ZX}a<538(>U zsk7(Kq4Ai*wN|zP0v+?~FF2PLx^LnPdjZtMm9~b(DRONFP=quUYN3w`2_R^cuvWp1r77NM)G6)s7O_B`3T0Al^c^ zUw2%amEW;*530U?EU!C1_pJ{d{(PIZ{LIVQ+M3FcX-jrtOhglGbhnlZgRTsrDt*mH zF#vSa-H$l*ErsHJSm4J8f*0q%+hSc1@S(TfU&5<}Du&)J=z6oZ%JGw@(3tU$37Slm zW)*M6n1~?QaJN!Wp9micNiC@QM2vC{i10e9VJ4W*d2fGcwHxdq9)LsP7GGf+WcsJi zp6@VI4LQ6#!HVqJ-ib*W1}NtUCD`BxP)tlr5BxJ&*{kwpvFd@~E#3XsKI(%DM3`?$ zFjN@YvVQB!Z@y)AN9614=!llY!0q_fr?scy6fEsYNY_K#yI_J1-g1s^5{U$sa0I~~ z3SyPCLVN{Q63~20;aWh9`OFWj-#TQ2c|CLHEEAUCU2lfnej!()S`!G7%&`(NZ(m7k z6^c{kJ`I>?3xEQpS%zU^uE>D5lxFyU>(ASHOE{pyur0yBH5)hct_m%{f1_DA2V>cH z$Zf(G)%U7Ev9gRYfC-xbB$LU2X$QolXbOZ*s9MS$k zpR6s}?;Q{TF(5y(x0uz{solwkBUAO&E5u&f3|;8O~Zm}gs8jmZc&?sLfy}ZJH^Pb-rBLkukEGEX2zm!X9k1Z~ZXG;?s)mi>UrdO>Yw!B41@A8A?MzlV><+YT z$1cI255`Q49zh&|R_ZEHbaKW$fCYjHcN@ENFhn{iB1V>lPj;L}k08i137M@2jRt#e z@h#!08F3dndCGng58cW5R)qpkr_P)sIDlrp{Dvr7AaFS_Sx)a$A<=P0zyb*(cC)p; z3y`HiEU~EtRcpi~(&pK3AcH~;F1vnfIByu?lP`r?9Si4JzG^+Msf6o6j!Lkw#4p=X zaotU#%mtIeU?b4b;x3+G!PBh`ZSJ~oBJ0)h2fLM#V{x|~T*y<~OO zMN4bH?5VNl%kYC1dT`Ryf~?4eY&&#&6`K286+q0dLXs5iTyUmBLqh{?CD6@0C^9k< zJhAYYl>3$m>pnTQ5Y|;+t{BGCaai!ltmr(bY{MwMUvH_a_CZ+~zKvvYA*2M^>5@Bhzq3R_;9V4J5SzJXynm~-ra z1+>?EU1i4n{h8h{39{^>*SI_h4FCaIT=M10F1KI&wQXhAGX1PY-|mtj&)WB4uJN4r zw8wl|ly@*hDkegrtWXv7yGV1}Z%9<`bAp~ijuKeZC`7Lxn`(cwC6~gY69&LsySaq~ zwb%P+2f}NR?(97eEtgnp$Y&o&QGX>+3sz(6Igj(@UEM_kk_GW0l$9dCBnHN=P}ghmhLG zA~MY&G`>e*V6IYEegJNSMs%8S>w6DE|6TM&rzX^3y1rh$LG-cYmMtf1iVpb(1n7zO z2^Ye3x4L43AT>EQC1(P#cZgup(n7EYg}vE&XU})RuF@2^Pm?0I4~k4mdjjTCZ0%#g zg_sn79F`P$cJa5YDXVRu1tM_kouN&P81m{{A2M}O;)2K2z-*$Dmj6AT!&EYt!D4Wq zRy{I5Kffr58HB`2`zdu5=V|82p#92bp6v)as{FqDPv+TZq%36F#q~iw8R9Gz%k$#X zLQKuHkB?6x{;5n<>z;%#I4uAHxx8=UbWwLYq%GhaOu=q@hRDPj=17rSh9vTg=V0#0 z9C9_!?rszgP7C?4EkAsq1-?p}S@<<{a-ijvL3_HTD^^q4u#SeTT(?P(rck!zyAo8o zwJ>L7?n232Qqexw5NfRXqFE9akT1{ey&vjHXn_dSJ=8yUbgv9nqrd`3vB9H;y}vYu zgFZg~g>1b~j~E)n*&3k^;!IggqUvTvUPTjaKJ?LNUolbYj--viU58Gw&_cLO#45w9 z)_G}5n|j8{#uC$&#IE-epEz4HWsr0W^Y-?Zfm%#Z{T2X3{>u!4xy|m!J z=;P0qcL;%AiZ_gTNc3?b(dNr?%zI*FnJ>T`k+}+M<96O+n=&XsVs0!gF+KkS*sPUi zl$z^r2#fnVf@F$VnrdmflzDwoTuRQTFgIk5dOFf{wPwl!*g6tsDM)%^rePHjHrgO^ ziDjyy0>!I!>+qaplDUZ`bLBA8)shx+zp{?ZCjo3M7L7F1xP^^Wn;J*}%O%vnV`_jG zI5Dl)&#(;&J15NC1e>KRy16;YVa|s_F+r0;l-f5SAU`>)=yw;08~`3>yY7NN@EjOm zF36mOIs@;q#)lxH8BT~=s()~JiA+{ih(L6BLQ5NochXGG(Ac`bGtW^AAry) z6?UnR%hl&|(cveUthm(N)jt0IMKFe5UjAvMmtnY>x7DFFPivaUlf)t*kr#(Sq=Nhm z@S+&G<|$cr@mb>PU*?LwUBGGX8h;taMye@18!1bl1!D$dM_$A@GNwH`BY0X0HbOKs zgw36KEASwsgBlJFi!;Tmd#!`aF}Gx>tC}@4bJYl%8MIEkI&VX8So8p5veIGfNd7T| zjHyRwGF!G(GzJpFmxu=h)Gz=kD@vL+DOppv58Qn-PwjG701^uvHm*aq+(t>6h67Pa zsZ)uUl}^Sgk&IoSBPt4=1wUG$Gcu36~g<6p#jS)g^iQrNL##*8D&T?#xc@giT6C62PtMw;NBF?CSO zBF`?pz(%n-7q*U6K6ZF*!*Lu&;{eZrXN^zI`8>F1bpIB#P81m{-_Fi=+NzDbN$et= zykWqNGQi!3K@5pZ7%oZ8`64;Hh9nrj5m?`E(04)p87N^SnGNfnx4FotD zWDFE!Ov1?+d3RN0&|r>#v;h2b=t;_{D^lE#SWrZD(iW$8p+q! zS0A06_BgDr8GL(MhT&@Us}qG!F2bR05nRG6sHK znd`Jy8+i~_?N17!qFD~$m11VvG+4BOk#WOf<(gNM()B;dv?cWnm>A7ux(ZO-+s}c@ zUJhk`4sy;Wj?Zv_;WQ0^My4&ThkJy34UCiwhkGaS9Ac^%jgv^8HIzKNx0!qH0*?Sd zA{vR|Nce5_WYj&p!H|g#i;f==Bg=RxA+6W?E)yuEDR}T08@#;#3pNuhw;6vgL?{&ioX%xV=lSZOt^QVRTX9$hXam}3pm09 z$%hPX2&r?Cu=yV^m4#M<3Ci{h3hf&aFTW>7p_v<(n!8G>G48^q<1|bxXesb`7+_(u zazzu>Srta(7;2gCLU%6!s3NZq)-WZfr5T1@ajCjha7}#ed;J1K%ZaARvd}gvlDm?S zX9;m>9C|?VB4PVL;+aH~Tu|~AFg0tYW&o0dW%lJSoTj#=tw0jQ^IDY22NdY1oFf%0}#JFNJg9 zb4`bH!nr*>Jo3r4vdFbLO~ZjEncQnMx%VLQEM6|)&;?R=;*oG#DaZ^=kQ;)Pmr97A zz~q@}C`(Xf6Ah6Ilkel>UxKwpMPNvHbwEgX4G8=jeg}Ue0LcS$Y4&|Hu&^422*hrb zj|K`T5 zvEu&kr?~JYsHgmN0NIn2aTn+aRJ9k!PJ8U-hv4^jUYrdmS}_oGTBmMTI8(8 z03a};B0~PpXcIa4tdx8=ft)LroI8SCE0|onhYK_v7fjvBqPuoO{)9hqzzQR# zC4vyzNCF0Pi6noEAfF9014WI zV2uq3g6f^x2G7c=p@RHqN*TgM%4|`s^UtkutYSaPk<{TxQ5pftG4D{HdAqOLZ#1v_ ze9M+5dsmQgQfV0(U&(S!!AFzvis49pCTa?3*#F3|c3c({E49|qiLo*tWAg7N2r?$H zceChvA3_;lB9B|DgITla;p_)_r>v>z1zcg0vl49vG;Ili>b(32*1hN??A7sM@$nr4 z8!M}P<^@Xi%U%oe11bF}T`A`>43CK-Qz^~WSp-#Hv2Q9-9^X94+}vz@Y^)g{BUOYV z_|+d(CAi?WUj6zyz~}lnkBZ=80;M3*LU zHGMlZ?()$(qVAfc|G0}(d&tSfx)|^Mu2H_=kb4o=Ap3@`Lp&B)cL!~H9PI7w*YctI zQdh5sK=8^5AG8P>#9Vyr+q9%EwH3HQk{XQFUw1_hfFE3734S2!^#qIgdS@@Q{Gn}V z&i9cg|N4u1hekL~)kUtMXQYP=0K1b;zvVq4 zRb1r#*7T38ib@M@JD6D*ec@F^uyytIxz!L&dH3FxrvZWb8BV**eALkmeW5?93@}@n z4gNan2F?-Ie_od^USuAI0%QWj1;%?cUgs$RzY?UxLayXoAPU~f29Th25OmAI z06!5@vgYvOQk6;7bal;{!x-3L@ZzNh{0cx{9p0)g1j+z7i}n8i$po2mA$9%`)fE!Czt%i%kp_d^qH20s4XnQst#a^y8a7?M5z z*L>NT7jYu?ICpgEQUYh_OrrtIc)wKx1p6)`I=;61<0)vR1JCOJwvBjC!)Mv`b#ol9Akg)gKB^lewze1bTfSn@{B`u_A zN)PUeMM_x{I^}mc;UI<%**ErSWv7bWZqZOYaL!Vhe~kgeP$S=_d##+rr~Y2Hh1>Lf zY=aYSLIB5kY+Q46%@wn%6eSeDTv`P&y|-w1o@Q>{3O~TqAV%Mfc7n9fmZEe)q(iKx^n9(NLb73Fz+c+s z!>K-8XvAo7Xl~E$nxjkY=8*HY3k8UR*tK@ktoRk(m_t4G*)CvnEHo5Mv^lI*I$~VT zuH0CQ&e0+^wcyj7d5)_2{MUw8@JEb14uhKmP;dz#w@0mHpB@zWPB$AE8802Ak?aBk z1M!fDJDr>(_(|mFqjVXEY-2j@TGY<*rK|h113ZR$)F9b)LOQJZhEwYNf%4CFbZX7r zL16#j)!2N6%HO@+Vja^$%=71~T?~9Gg$KI>#Wwff2WtS32+6IQEv;R6a?Q?f&t~sy z^?UKhaZ#>^yY+4h*)R!0Fyiwv!ursg*ef5>>?IAD*ns7x&BkByqWr2RWnuEC)*Vud z`9a0}20fROX5f7JsQ%t$N;zJM+&`J&In$Q}u+M=I{b7@g!`prSoyZpQ9TV;3(@D1e z%BI66KJyYBWhq#q@AQ!=m9Nvfnq z-SG?FyKF)enqlGZ8yZrUBOey84zNfN!yy;zjn1@HJvxz3-Fp z@Tz6QUll*eYHc^+v(f|F6?U8_{nr~jaIG0W?B=i6B3RcSto*bvBsbTM=A9BU-3Ah8 zNi`l$9?&GMo=FEwRv_xSgyGZtj9#@e-B5nrpw{?~zkgz73X_}cv)*W^Rr8w)YwNHc z*5Nn6f`7FA!KOwX(rWwMR7CG2XjL0w!d?(-NK_z;CDgW!? zm{={qDnSAQe=8Vg-umXT=L(@JFv-`qNgoa*CdglVGRag)CSpU(wYQsW`&k0q_mT*%_hS-?>#U4EO z2MC~jQ3U6aUEVZn`ZAr-q_#O-3f;~=QSZ=x?WSyg+?f9&^TYDzkb6XdslA>n+|$$Y z#wjomIx&A!XAHF_GVmq|e@koN>Yw2r^&$^Gl_#ddWR=6%jFpj99RV`jcPw{gQUrpP z&}y~JthsyUaj=yQDO|`!1pHEh$z()Rxx-4E66v=_sVbSZ*qEz&S3yM0K3<= zl(AIalVLR~ZN4IX$r$zP!ZB`rtk!neSg;~!`TZzT`@!UHZQV6$;7SKpBW2rrUV6x# zmbf#hIQ8SB>u=fyo$!2K@J^E%%R8%^DUW6^Ebq2+fLvKX@){F7?rY$=jVkSNr#m^S zUpAC=E)0=|)VsRj1l+j|KCG0J1K2@28(?-SzJW8yW`-j@8fz?sRj+*;$DojX-q@wYb}{2W8MP`wCr zpMJgOGt1}UL%B`+e1=bS5ru|!T&(Bpqim_)`YyB+;aZ#ewM>398;>NO39z+)EM@9I zzqa%gS5q)4Ws**y4RgHdAlxy?P#N69EqQ~}t7qX#A{`ZoNn=1A+!}QMkw>!0732x3 z`%S`@brK1YzOF-F&+{yjtW_BZrcDAx(tO-GN;yTY1tuOT<*hG12+Xe>ynLs0qchz{ z`%mg>lPr;0bC~$^CnR=xKR;P3OfpfJ$f|c)lUs?S0JW(^)lwEvC4)e}5}SI^v{!1$ zjqz@CVW6_>%7&F`sY3xz9P-J|lBlF}so2Y{lOpC+^`4$YhDLpp3!lSk@7KlW@%84X z*IvEA!*PC8@8D;8o1-I7vgw9B2}E<;Gq@mSZ&q9x(yG-(0CRJ;r zbr$E?ta2}89WD9k`z^Rc!N4GdALcn;R6#TJ15qv>piYcX@`jjXw~iJvrTm)BH$ zb%K;N2--lOR@QBD`&ZF+4es%d!air^&5bM>hfj5->g#UzXEdTl_hyn zIkQLs>{x-PlSZZM!^euTA~#MxCZTd_Kbjkq`Dn%=#g_vd*TXIuYU@v(d_{kZ;gK)u zziBr#l9lQ0LjnAl*orcD2VJ5{3NMwFco~orS-1~*AxKWOzTLAVmkWPoR%xPGNdu_q zz;1sj4r&=@sDnZO$2EB8H~guAjJd#c{W^O({#pLgMS7mAt2DrusXx<^*a&kdXI-_Y z_9j_9_oo7Ni?ojhH{T{3!6L3yVd(f2Q0Zr`E!UF-##p;v7n$b-e;v^A-o+ab? zlVwJ*Qt6gkF!g%V9M;PT-|U= znQZgx^I%KEj2c)s_Obx$c&fXdCv3`UHn5IUlIGXDmDJu$E7UeYpf5^wf`~WfT87s{$hui5G`USZ+r7zlb|e z{ZrEYyI`t?3$8$w!SQh-JJib09-`-O7ZU4W&ZGTrlS_{>=JI+%v?F3Tq4~1)esPKE zOiQEtW@?$T*;OTKv!Sl$WxW~6_9*!_N!^2IYUo+ypU1@6-e{dt%xSFE+(Fb`n{t+) z$HuFNv2x025j(+st&hXUa}gE1f(XrQ=B;Jhk8HVYcyj)MC0D)AaFV7l_3cKkrp89u z(05Bo#PXm6x=Pa_jB9=7rv$M%r5HsdnqMzLuKQArS-14ABcqZOrYyX~mfY?EWt(fm z(L+_F&V`mRF)}iS^LN5w6g}wbzz9&?o&7$8Y%p%*CHR^I$9f1*yUyH}zB4^i`c9)n z^IWRH4CDIwFT)hq3)>yRq6eP@ro(m*m$s4>KJU-QgKcLrPB2?_UE8C%l~~G<7O(TM zW$LTyd`im-CExf(S*NOi-sw_1p>6i4+&79YR+?)afxX5n4mIp$-P0wan9u#)Ul4SvZ5P^5 z*}dWjId8T<(NSMTCXWyZOnb$5cGAW?f`MWbibU$G>fOxR97aMitp0yYMP)?= z1O$K<=BD-n0)n+a_A!yelXun{$^rsE|6^eacZ`@^o{6gUa>5DRGx2`<)%*{W-(fiE zKNZgd&b|Bnp~hRX`A=CwbJ~tFFaEyeo|pUP4EcicV1wv|i;gmvUVb}SdG@R=&h?^h z3PSUksrkt}uuFf~%EQT?&f}||K|(rx9lY30_TJXsozA%7iJ(FQFNgw*A)ZB;o5OXk z2W9E{7_j|*?Y#`4wVAHYryQ%j!apO!ra!3)N5t{n=S%-`Z&9H|1ggSHaeG=c{YVqE z0nrZ>c$u-m#RjYlJ1__6P(^4W9s;ScgAR=zMOIH2>yAx`HB{r5^EgmL@|bsD=u7Gu zgacoB7^h};0J>#HNEt$s)qtqv*4c|ndX;#H76lzv<;Vxk6@#g{Gq4d5%WWY>Gi3f= zIKV2{dnC-DVoc|KC3NFn1|W?&GD3yrhBQpQn1h|7bczqvxu=CR)Jw7gbC+QwvaIEW zC>4WTKfgc&MmiUJlQ7QQ7}Hg!Ap(tTH@Vv9u#mW7!+x8dHoaYZt4=L{l<%ypU!D4= zAS@TennL1&=;?wmIgrc5%GX_FM5SRm$E04c%mXlGjC)%@wcw!V01?0j7n9{7EPdk=@ym z$AP&CIX2?G3azQ~&F_9DKcX+*Yo?D#h zeA!&ib)-h(S91c||CGiw5S6!M8UOe&d_fPoP1qgv7Ba~8Q*sj)a{=i8HuEbZsa{lu zz-=@kWR7|Y?HSQ%0n!>w;F9us#<{QLC86YcoYnBR1owfTyprh81G;RrC}Esl?1HMv zyb`o29Syq=(7zTFAfx&e4fE$uUZg#Gbh>4=KVyZb+cw~u&Y>qu?u{B68uE``QQG9r zmop-I-|3yLz{~j*d`H3pl^lfgr7-YvghZHlBpOn-tQ_R`!kd!$ea{=!*s5=R#cH z-w1Iv^D>#dtn;Vvc&R1_74NQLpe(P71gUjM=#4Y)q2ZEHM?~zI{U!rX9NTM&AWKD& zRIFnXMQePHcG5+0TeG)#;q}O}4)o5u8|2r*dn4MHKJkvE;lc?nL07p4^g0(ti$qOd z7G<#R+0qe+BXeJs7NmU%6*9-tL`>&b9%g`^JST1Uz_w8UNEKy?+`vpqU{b|pHs`^^ zOy72g#If!7q-y?+iQ`q2vKU=#xG*JW@36RQJ+$r7Kl0zN1}?qeOpvO-=|iob7Q=kZ z&;#HH%r!#0!Y3I8jiWidEi*IP7UD6bbASGI7)sp(zbVzYY8zrxL3tuVe`^QbFHLY! zu#-^Bj5!U65BGn8)`lVC>Y&Zf8rlFtB_ z)|g__N9i>0a%zB+Q*h3cNW}I$Tg3Lki5X{!^g@UdZ2)-J_jP}rAEQ0G?Yy7+Nv*sq z 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 F@PB1o0BisN diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Osu.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index ef89bd5215ffcc38c68b119a7495a77a7084543b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10893 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 diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/Strings.xml b/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/Strings.xml deleted file mode 100644 index 8c47838dfb..0000000000 --- a/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/Strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - osu.Game.Rulesets.Osu.Tests.Android - Settings - diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/colors.xml b/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/colors.xml deleted file mode 100644 index 17bb9a9dd1..0000000000 --- a/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/colors.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - #2c3e50 - #1B3147 - #3498db - diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/ic_launcher_background.xml b/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/ic_launcher_background.xml deleted file mode 100644 index 6ec24e6413..0000000000 --- a/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #2C3E50 - \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/styles.xml b/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/styles.xml deleted file mode 100644 index 5885930df6..0000000000 --- a/osu.Game.Rulesets.Osu.Tests.Android/Resources/values/styles.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj index 7eeee4fd80..9b2ab38309 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj +++ b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj @@ -24,82 +24,35 @@ Assets Xamarin.Android.Net.AndroidClientHandler - - True - portable - False - bin\Debug\ - DEBUG;TRACE - prompt - 4 - True - None - False - - - True - pdbonly - True - bin\Release\ - TRACE - prompt - 4 - true - False - SdkOnly - True - - - - - - - + - - + + osu.licenseheader + - - - Designer - - - - - - - - - - - - - - - - - - - - - - + + %(RecursiveDir)%(Filename)%(Extension) + - + + {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} + osu.Game.Resources + + + {c92a607b-1fdd-4954-9f92-03ff547d9080} + osu.Game.Rulesets.Osu + + + {2a66dd92-adb1-4994-89e2-c94e04acda0d} + osu.Game + - \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Assets/AboutAssets.txt b/osu.Game.Rulesets.Taiko.Tests.Android/Assets/AboutAssets.txt deleted file mode 100644 index b0633374bd..0000000000 --- a/osu.Game.Rulesets.Taiko.Tests.Android/Assets/AboutAssets.txt +++ /dev/null @@ -1,19 +0,0 @@ -Any raw assets you want to be deployed with your application can be placed in -this directory (and child directories) and given a Build Action of "AndroidAsset". - -These files will be deployed with you package and will be accessible using Android's -AssetManager, like this: - -public class ReadAsset : Activity -{ - protected override void OnCreate (Bundle bundle) - { - base.OnCreate (bundle); - - InputStream input = Assets.Open ("my_asset.txt"); - } -} - -Additionally, some Android functions will automatically load asset files: - -Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs index 8ceca4d436..0c64cbfc5d 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs +++ b/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs @@ -1,19 +1,17 @@ -using Android.App; -using Android.OS; -using Android.Support.V7.App; -using Android.Runtime; -using Android.Widget; +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Android.App; +using Android.Content.PM; +using osu.Framework.Android; +using osu.Game.Tests; namespace osu.Game.Rulesets.Taiko.Tests.Android { - [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)] - public class MainActivity : AppCompatActivity + [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] + public class MainActivity : AndroidGameActivity { - protected override void OnCreate(Bundle savedInstanceState) - { - base.OnCreate(savedInstanceState); - // Set our view from the "main" layout resource - SetContentView(Resource.Layout.activity_main); - } + protected override Framework.Game CreateGame() + => new OsuTestBrowser(); } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/AboutResources.txt b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/AboutResources.txt deleted file mode 100644 index c2bca974c4..0000000000 --- a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/AboutResources.txt +++ /dev/null @@ -1,44 +0,0 @@ -Images, layout descriptions, binary blobs and string dictionaries can be included -in your application as resource files. Various Android APIs are designed to -operate on the resource IDs instead of dealing with images, strings or binary blobs -directly. - -For example, a sample Android app that contains a user interface layout (main.axml), -an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) -would keep its resources in the "Resources" directory of the application: - -Resources/ - drawable/ - icon.png - - layout/ - main.axml - - values/ - strings.xml - -In order to get the build system to recognize Android resources, set the build action to -"AndroidResource". The native Android APIs do not operate directly with filenames, but -instead operate on resource IDs. When you compile an Android application that uses resources, -the build system will package the resources for distribution and generate a class called "R" -(this is an Android convention) that contains the tokens for each one of the resources -included. For example, for the above Resources layout, this is what the R class would expose: - -public class R { - public class drawable { - public const int icon = 0x123; - } - - public class layout { - public const int main = 0x456; - } - - public class strings { - public const int first_string = 0xabc; - public const int second_string = 0xbcd; - } -} - -You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main -to reference the layout/main.axml file, or R.strings.first_string to reference the first -string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/Resource.designer.cs b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/Resource.designer.cs deleted file mode 100644 index d3709d3cdb..0000000000 --- a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/Resource.designer.cs +++ /dev/null @@ -1,112 +0,0 @@ -#pragma warning disable 1591 -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -[assembly: global::Android.Runtime.ResourceDesignerAttribute("osu.Game.Rulesets.Taiko.Tests.Android.Resource", IsApplication=true)] - -namespace osu.Game.Rulesets.Taiko.Tests.Android -{ - - - [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] - public partial class Resource - { - - static Resource() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - public static void UpdateIdValues() - { - } - - public partial class Attribute - { - - static Attribute() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Attribute() - { - } - } - - public partial class Id - { - - // aapt resource value: 0x7f050000 - public const int myButton = 2131034112; - - static Id() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Id() - { - } - } - - public partial class Layout - { - - // aapt resource value: 0x7f030000 - public const int Main = 2130903040; - - static Layout() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Layout() - { - } - } - - public partial class Mipmap - { - - // aapt resource value: 0x7f020000 - public const int Icon = 2130837504; - - static Mipmap() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Mipmap() - { - } - } - - public partial class String - { - - // aapt resource value: 0x7f040001 - public const int app_name = 2130968577; - - // aapt resource value: 0x7f040000 - public const int hello = 2130968576; - - static String() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private String() - { - } - } - } -} -#pragma warning restore 1591 diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/layout/activity_main.axml b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/layout/activity_main.axml deleted file mode 100644 index ff7a60eb50..0000000000 --- a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/layout/activity_main.axml +++ /dev/null @@ -1,7 +0,0 @@ - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 036d09bc5f..0000000000 --- a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 036d09bc5f..0000000000 --- a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 2531cb31efc3a0a3de6113ab9c7845dc1d9654e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1634 zcmV-o2A%ndP)B+Z3$1(8#|f~9B42Y^N-3=o2YCq0YUY$Zu=pM;#hG{lHi%n~Vh z1d1vN#EDO19X?u$`cV z!a}AKG@Bb*#1cdYg8af_;jP69b`k%G1n?0=F^8bI^o>wg-vEliK^U}y^!D|^p|ax; zC|pK=f+FHp!RUAhtlpGGUxJb|wm^5! z<1r%$<$TR02wajxKZ4MiR#aAxDLE(##UNyD|ABr4WoGRF*?@e^2|~Hq(gurSSJH*;Q~5lw{J5A_(PCXBWhzZE${qgzv0{dk-F( z1<}>r181tLiEla&f1j&?p2xjbfp2cTt-c1Ox~?9EhK9`cJ9Vatf)loIoQ@#h&}cIGD>Z#QLE}&(bMo@7Ff|7f#Nm^$PJpVcbj+v~K7wfVwF}=) zRQsc+`=A-+C)vrRvaIC-5u>|;3h z*G4-u#RI<_vuSN~vZ6{|I~q5FFk3%de#+*>UFG>&bq6~ zUEMZ~FIOmFO=kA^5rkp-Msw?^63xvdXVZ-rv@{6{iVO}M!}^Je%2BPbi+(L<5<%~h z2v^D+f<|j%7~cJjOzg*!GPQ{%uE{i%YgcZhuZh{yNlQ}RhaU1jd=K+AopVKP+D}&} zZ3y$llqZiln=Z_A$!qzkGbX0D{?l(v5@1|`QyCvCnQ`eKI>|zj_zo%y#fKf85VhQ} zP)y&j4P*nR3q{-o35iV6nx7QDqq<;WDVIt}|N%`!dgv*y3va8eLNj zU9x(?ieweHfQ*yXk8|=ssZ~qJEz^QoKJ|iGa>ge_Vm_8l}S+UvJ{8g4jr+o#aTSFsz1W;PDP zW765JXGU#3JL>SlIl3NRV2{7B2dLO1cIP)a4ZRYL|MBD36O1#oSgAf}APz5@;x=_U-<=y)Py7*}O5(uu7BL_eLe6Ek7pH|G zMq)FrF1EFq&yruS5b=F=w)fVVoPd(oeRyTFym_Uwyn~L=OL(O?cf^2L5R(SmjORx6 z%nmZf^W=3pkvT*>@osUNi>DULH1hL;y`JGQX$onRCr_U0=H~Viodq!<7Q{3rPk~{G gu#IhOV;e2n|1(WJB~7~kivR!s07*qoM6N<$g7lUVaR2}S diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png deleted file mode 100644 index 7a859c25556af7a2e46e22a2220eaded55628e9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1441 zcma)+`#%#30L6FjMQg%tuA0%p#0??L`*E=rD#U2F4L5n@F+O9Sp;(QwEQy7+?sX?r zCWN(!Hg`+j5k8*H@|yQEtnAi*(D{7M`Tlf1=eKjq)BUsp2nqrK01B=yNUv`!`EH=x zx8$xJQUd^Fuec%|(TT&0V}4orr_==mmCnEuzD+ff8Pg>pJRqsWsD{#?eGPaCu0(sEH_2RG@<6-Nt<8 ztPMUmmAz9Ga$23Y9~p9dqJSgJJ#Jk_r@o13^%d-Xf46i+Lrmz3 zy9(DUDVXj;Zny7nO+yn&W2flEX=C!8&D0zI`G# z8;XmlonoghgRFUY*$+7pPLa}Uy)onw>TT9t(FTV6#BV8&lXWDPRvQW_n~xZ|yLcZjX>m$Eaf1)dwXS`&E^ zkNjO;%;fWywchc=+w4utQ0Vbn%B>b~yy4I#D{?1!P`$P>Wdo+ljCo(tYia04JTc=$$u+IbzDVPFYpm8+AQj+ zGKH zfS{{hN%W)kF+(26oZpkURD5Q_G_z97F6{Jval+TOj-;5y)*Rdo3a$^^k~q5gpTzmp1q@+2X9O z;_VUF>;s~C1~gpFrFoh?{aQ|LlBIYz!z^P~lndX5-ES)p#+9GW*|-WBTzQ*&gKOE` zM##bUaWl`6rZBXw0!~_oUhf+H$tNc@lLZCj0bZT^KSo@C|P?7YR8dP0se1jj z9aA0|7MONf(ZYaLZs$s}r*05fx25-iN6mZe_*Rq%uyz(+^-k;t`!R`?uf~rn#1ZC7 zuv3}UrmMzcBbo4jym@fS5%I+G`GJIC1s$)MQs3Vhld?a2U;w}$@V%dC@%qpO7+3#$ N&GnQ!lI8SQ#{X#Iv!eh2 diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index b8d35b3a1cfe5308bb099a5a1429555c41417e36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3552 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 diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 8f56909cddfa86f1387074bf43003f36d6e67be1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2413 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 b;}yxl--Ss53i@>Q4YQuNcebmsMJN0NT!aL! diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 9737d79c0492b2c8a811621660eba33e79fab959..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4858 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 diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 9133e31b43252d00767a6a3806df9ba68de2d265..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3871 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 diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index c3ae5f5ccdecc01a9b17a2a0c2b1bb20602f0151..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8001 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 z 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 F@PB1o0BisN diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index ef89bd5215ffcc38c68b119a7495a77a7084543b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10893 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 diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/Strings.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/Strings.xml deleted file mode 100644 index 12965f132d..0000000000 --- a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/Strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - osu.Game.Rulesets.Taiko.Tests.Android - Settings - diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/colors.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/colors.xml deleted file mode 100644 index 17bb9a9dd1..0000000000 --- a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/colors.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - #2c3e50 - #1B3147 - #3498db - diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/ic_launcher_background.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/ic_launcher_background.xml deleted file mode 100644 index 6ec24e6413..0000000000 --- a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #2C3E50 - \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/styles.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/styles.xml deleted file mode 100644 index 5885930df6..0000000000 --- a/osu.Game.Rulesets.Taiko.Tests.Android/Resources/values/styles.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj index 62c2cefe06..0174be9451 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj +++ b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj @@ -24,82 +24,38 @@ Assets Xamarin.Android.Net.AndroidClientHandler - - True - portable - False - bin\Debug\ - DEBUG;TRACE - prompt - 4 - True - None - False - - - True - pdbonly - True - bin\Release\ - TRACE - prompt - 4 - true - False - SdkOnly - True - - - - - - - + - - + + osu.licenseheader + - - - Designer - - - - - - - - - - - - - - - - - - - - - - + + %(RecursiveDir)%(Filename)%(Extension) + + + + {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} + osu.Game.Resources + + + {f167e17a-7de6-4af5-b920-a5112296c695} + osu.Game.Rulesets.Taiko + + + {2a66dd92-adb1-4994-89e2-c94e04acda0d} + osu.Game + + - \ No newline at end of file From 5fdf4ee67b3c4e3c8c97625cdbf858c3e92faeec Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Sat, 5 Jan 2019 23:18:12 -0600 Subject: [PATCH 0328/2854] Add Android's VisualTests project --- osu.Android.sln | 8 ++ osu.Game.Tests.Android/MainActivity.cs | 16 ++++ .../Properties/AndroidManifest.xml | 9 ++ .../Properties/AssemblyInfo.cs | 30 ++++++ .../osu.Game.Tests.Android.csproj | 91 +++++++++++++++++++ 5 files changed, 154 insertions(+) create mode 100644 osu.Game.Tests.Android/MainActivity.cs create mode 100644 osu.Game.Tests.Android/Properties/AndroidManifest.xml create mode 100644 osu.Game.Tests.Android/Properties/AssemblyInfo.cs create mode 100644 osu.Game.Tests.Android/osu.Game.Tests.Android.csproj diff --git a/osu.Android.sln b/osu.Android.sln index 11c0f3cb27..d9c6ffc939 100644 --- a/osu.Android.sln +++ b/osu.Android.sln @@ -25,6 +25,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Osu.Tests EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Taiko.Tests.Android", "osu.Game.Rulesets.Taiko.Tests.Android\osu.Game.Rulesets.Taiko.Tests.Android.csproj", "{3701A0A1-8476-42C6-B5C4-D24129B4A484}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tests.Android", "osu.Game.Tests.Android\osu.Game.Tests.Android.csproj", "{5CC222DC-5716-4499-B897-DCBDDA4A5CF9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -85,6 +87,12 @@ Global {3701A0A1-8476-42C6-B5C4-D24129B4A484}.Release|Any CPU.ActiveCfg = Release|Any CPU {3701A0A1-8476-42C6-B5C4-D24129B4A484}.Release|Any CPU.Build.0 = Release|Any CPU {3701A0A1-8476-42C6-B5C4-D24129B4A484}.Release|Any CPU.Deploy.0 = Release|Any CPU + {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}.Release|Any CPU.Build.0 = Release|Any CPU + {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}.Release|Any CPU.Deploy.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/osu.Game.Tests.Android/MainActivity.cs b/osu.Game.Tests.Android/MainActivity.cs new file mode 100644 index 0000000000..58375c699e --- /dev/null +++ b/osu.Game.Tests.Android/MainActivity.cs @@ -0,0 +1,16 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Android.App; +using Android.Content.PM; +using osu.Framework.Android; + +namespace osu.Game.Tests.Android +{ + [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] + public class MainActivity : AndroidGameActivity + { + protected override Framework.Game CreateGame() + => new OsuTestBrowser(); + } +} diff --git a/osu.Game.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Tests.Android/Properties/AndroidManifest.xml new file mode 100644 index 0000000000..d4d822c7b0 --- /dev/null +++ b/osu.Game.Tests.Android/Properties/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/osu.Game.Tests.Android/Properties/AssemblyInfo.cs b/osu.Game.Tests.Android/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..dfa3e0197b --- /dev/null +++ b/osu.Game.Tests.Android/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Android.App; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("osu.Game.Tests.Android")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("osu.Game.Tests.Android")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj new file mode 100644 index 0000000000..4f5b0e68d4 --- /dev/null +++ b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj @@ -0,0 +1,91 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {5CC222DC-5716-4499-B897-DCBDDA4A5CF9} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {122416d6-6b49-4ee2-a1e8-b825f31c79fe} + Library + Properties + osu.Game.Tests.Android + osu.Game.Tests.Android + 512 + True + Resources\Resource.designer.cs + Resource + Off + false + v8.1 + Properties\AndroidManifest.xml + Resources + Assets + Xamarin.Android.Net.AndroidClientHandler + + + + + + + + + osu.licenseheader + + + + + + %(RecursiveDir)%(Filename)%(Extension) + + + %(RecursiveDir)%(Filename)%(Extension) + + + %(RecursiveDir)%(Filename)%(Extension) + + + %(RecursiveDir)%(Filename)%(Extension) + + + %(RecursiveDir)%(Filename)%(Extension) + + + %(RecursiveDir)%(Filename)%(Extension) + + + %(RecursiveDir)%(Filename)%(Extension) + + + %(RecursiveDir)%(Filename)%(Extension) + + + + + {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} + osu.Game.Resources + + + {58f6c80c-1253-4a0e-a465-b8c85ebeadf3} + osu.Game.Rulesets.Catch + + + {48f4582b-7687-4621-9cbe-5c24197cb536} + osu.Game.Rulesets.Mania + + + {c92a607b-1fdd-4954-9f92-03ff547d9080} + osu.Game.Rulesets.Osu + + + {f167e17a-7de6-4af5-b920-a5112296c695} + osu.Game.Rulesets.Taiko + + + {2a66dd92-adb1-4994-89e2-c94e04acda0d} + osu.Game + + + + \ No newline at end of file From 8159905ca038994432454cb55233a416cc5236bd Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Sat, 5 Jan 2019 23:48:57 -0600 Subject: [PATCH 0329/2854] Code File Sanity --- .gitignore | 1 + osu.Android/Properties/AssemblyInfo.cs | 33 ------------------- osu.Android/osu.Android.csproj | 1 - .../Properties/AssemblyInfo.cs | 30 ----------------- ...u.Game.Rulesets.Catch.Tests.Android.csproj | 1 - .../Properties/AssemblyInfo.cs | 30 ----------------- ...u.Game.Rulesets.Mania.Tests.Android.csproj | 1 - .../Properties/AssemblyInfo.cs | 30 ----------------- ...osu.Game.Rulesets.Osu.Tests.Android.csproj | 1 - .../Properties/AssemblyInfo.cs | 30 ----------------- ...u.Game.Rulesets.Taiko.Tests.Android.csproj | 1 - .../Properties/AndroidManifest.xml | 12 +++---- .../Properties/AssemblyInfo.cs | 30 ----------------- .../osu.Game.Tests.Android.csproj | 8 +++-- osu.Game/Properties/AssemblyInfo.cs | 1 + 15 files changed, 12 insertions(+), 198 deletions(-) delete mode 100644 osu.Android/Properties/AssemblyInfo.cs delete mode 100644 osu.Game.Rulesets.Catch.Tests.Android/Properties/AssemblyInfo.cs delete mode 100644 osu.Game.Rulesets.Mania.Tests.Android/Properties/AssemblyInfo.cs delete mode 100644 osu.Game.Rulesets.Osu.Tests.Android/Properties/AssemblyInfo.cs delete mode 100644 osu.Game.Rulesets.Taiko.Tests.Android/Properties/AssemblyInfo.cs delete mode 100644 osu.Game.Tests.Android/Properties/AssemblyInfo.cs diff --git a/.gitignore b/.gitignore index f95a04e517..21dbf45618 100644 --- a/.gitignore +++ b/.gitignore @@ -196,6 +196,7 @@ ClientBin/ *.publishsettings node_modules/ orleans.codegen.cs +Resource.designer.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) diff --git a/osu.Android/Properties/AssemblyInfo.cs b/osu.Android/Properties/AssemblyInfo.cs deleted file mode 100644 index d222a06ee0..0000000000 --- a/osu.Android/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Android.App; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("osu.Android")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("osu.Android")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 646ed71d44..4a1dccd758 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -28,7 +28,6 @@ - diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Catch.Tests.Android/Properties/AssemblyInfo.cs deleted file mode 100644 index 1b1fd64fb5..0000000000 --- a/osu.Game.Rulesets.Catch.Tests.Android/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Android.App; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("osu.Game.Rulesets.Catch.Tests.Android")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("osu.Game.Rulesets.Catch.Tests.Android")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj index 82c171cb46..e4140374cc 100644 --- a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj +++ b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj @@ -27,7 +27,6 @@ - diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Mania.Tests.Android/Properties/AssemblyInfo.cs deleted file mode 100644 index 468676e282..0000000000 --- a/osu.Game.Rulesets.Mania.Tests.Android/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Android.App; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("osu.Game.Rulesets.Mania.Tests.Android")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("osu.Game.Rulesets.Mania.Tests.Android")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj index a761f2569a..8b2cc5f55b 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj +++ b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj @@ -27,7 +27,6 @@ - diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Osu.Tests.Android/Properties/AssemblyInfo.cs deleted file mode 100644 index 0e43fb25cf..0000000000 --- a/osu.Game.Rulesets.Osu.Tests.Android/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Android.App; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("osu.Game.Rulesets.Osu.Tests.Android")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("osu.Game.Rulesets.Osu.Tests.Android")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj index 9b2ab38309..a8d6b631cc 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj +++ b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj @@ -27,7 +27,6 @@ - diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AssemblyInfo.cs deleted file mode 100644 index 28f5d292ab..0000000000 --- a/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Android.App; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("osu.Game.Rulesets.Taiko.Tests.Android")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("osu.Game.Rulesets.Taiko.Tests.Android")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj index 0174be9451..2dc5cc37c1 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj +++ b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj @@ -27,7 +27,6 @@ - diff --git a/osu.Game.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Tests.Android/Properties/AndroidManifest.xml index d4d822c7b0..fda3bdafab 100644 --- a/osu.Game.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Tests.Android/Properties/AndroidManifest.xml @@ -1,9 +1,5 @@  - - - - - + + + + \ No newline at end of file diff --git a/osu.Game.Tests.Android/Properties/AssemblyInfo.cs b/osu.Game.Tests.Android/Properties/AssemblyInfo.cs deleted file mode 100644 index dfa3e0197b..0000000000 --- a/osu.Game.Tests.Android/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Android.App; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("osu.Game.Tests.Android")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("osu.Game.Tests.Android")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj index 4f5b0e68d4..8a8a2b6733 100644 --- a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj +++ b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj @@ -10,7 +10,7 @@ {122416d6-6b49-4ee2-a1e8-b825f31c79fe} Library Properties - osu.Game.Tests.Android + osu.Game.Tests osu.Game.Tests.Android 512 True @@ -27,7 +27,6 @@ - @@ -87,5 +86,10 @@ osu.Game + + + 2.0.0 + + \ No newline at end of file diff --git a/osu.Game/Properties/AssemblyInfo.cs b/osu.Game/Properties/AssemblyInfo.cs index df9045b802..3a283b29d5 100644 --- a/osu.Game/Properties/AssemblyInfo.cs +++ b/osu.Game/Properties/AssemblyInfo.cs @@ -9,3 +9,4 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("osu.Game.Tests")] [assembly: InternalsVisibleTo("osu.Game.Tests.Dynamic")] +[assembly: InternalsVisibleTo("osu.Game.Tests.Android")] From 47268898bec498d8e165e26c84fd59315a2ac44d Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Sun, 6 Jan 2019 00:04:25 -0600 Subject: [PATCH 0330/2854] Expose internals for Android test projects --- osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs | 1 + osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs | 1 + osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs | 1 + osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs | 1 + 4 files changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs index 045d0824c6..7dfb32cade 100644 --- a/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs @@ -9,3 +9,4 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests")] [assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests.Dynamic")] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests.Android")] diff --git a/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs index 76ccfe324b..743cec5ee1 100644 --- a/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs @@ -9,3 +9,4 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests")] [assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests.Dynamic")] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests.Android")] diff --git a/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs index b61c53be6a..5a41b60a03 100644 --- a/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs @@ -9,3 +9,4 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests")] [assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests.Dynamic")] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests.Android")] diff --git a/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs index 26f70bc927..ae7a113f7e 100644 --- a/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs @@ -9,3 +9,4 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests")] [assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests.Dynamic")] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests.Android")] From bf78c93346ff6c54c6e96311b824735f953a8a0a Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Sun, 6 Jan 2019 00:06:00 -0600 Subject: [PATCH 0331/2854] Cleanup Android manifest files --- .../Properties/AndroidManifest.xml | 3 +-- .../Properties/AndroidManifest.xml | 3 +-- .../Properties/AndroidManifest.xml | 3 +-- .../Properties/AndroidManifest.xml | 3 +-- osu.Game.Tests.Android/Properties/AndroidManifest.xml | 2 +- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml index 6c8c0935b2..e22c571991 100644 --- a/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml @@ -4,6 +4,5 @@ android:versionName="1.0" package="osu.Game.Rulesets.Catch.Tests.Android.osu.Game.Rulesets.Catch.Tests.Android"> - - + diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml index 6ffcfe16f7..a36d279391 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml @@ -4,6 +4,5 @@ android:versionName="1.0" package="osu.Game.Rulesets.Mania.Tests.Android.osu.Game.Rulesets.Mania.Tests.Android"> - - + diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml index 05da9d02f7..a10763a5ed 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml @@ -4,6 +4,5 @@ android:versionName="1.0" package="osu.Game.Rulesets.Osu.Tests.Android.osu.Game.Rulesets.Osu.Tests.Android"> - - + diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml index 9b5e759012..d9df277133 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml @@ -4,6 +4,5 @@ android:versionName="1.0" package="osu.Game.Rulesets.Taiko.Tests.Android.osu.Game.Rulesets.Taiko.Tests.Android"> - - + diff --git a/osu.Game.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Tests.Android/Properties/AndroidManifest.xml index fda3bdafab..20060728da 100644 --- a/osu.Game.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Tests.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file From c2a52b56e7e0b04cdcffb3e3543a46ee8b40577e Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Sun, 6 Jan 2019 12:01:50 -0600 Subject: [PATCH 0332/2854] Set app name and reset default namespace --- .../Properties/AndroidManifest.xml | 11 ++++------- .../osu.Game.Rulesets.Catch.Tests.Android.csproj | 2 +- .../Properties/AndroidManifest.xml | 11 ++++------- .../osu.Game.Rulesets.Mania.Tests.Android.csproj | 2 +- .../Properties/AndroidManifest.xml | 11 ++++------- .../osu.Game.Rulesets.Osu.Tests.Android.csproj | 2 +- .../Properties/AndroidManifest.xml | 11 ++++------- .../osu.Game.Rulesets.Taiko.Tests.Android.csproj | 2 +- osu.Game.Tests.Android/Properties/AndroidManifest.xml | 2 +- 9 files changed, 21 insertions(+), 33 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml index e22c571991..b04b0718f5 100644 --- a/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml @@ -1,8 +1,5 @@  - - - - + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj index e4140374cc..591f4cbc31 100644 --- a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj +++ b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj @@ -10,7 +10,7 @@ {122416d6-6b49-4ee2-a1e8-b825f31c79fe} Library Properties - osu.Game.Rulesets.Catch.Tests.Android + osu.Game.Rulesets.Catch.Tests osu.Game.Rulesets.Catch.Tests.Android 512 True diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml index a36d279391..c315581606 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml @@ -1,8 +1,5 @@  - - - - + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj index 8b2cc5f55b..5faf9f747d 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj +++ b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj @@ -10,7 +10,7 @@ {122416d6-6b49-4ee2-a1e8-b825f31c79fe} Library Properties - osu.Game.Rulesets.Mania.Tests.Android + osu.Game.Rulesets.Mania.Tests osu.Game.Rulesets.Mania.Tests.Android 512 True diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml index a10763a5ed..dac9c19477 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml @@ -1,8 +1,5 @@  - - - - + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj index a8d6b631cc..f99aa61f01 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj +++ b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj @@ -10,7 +10,7 @@ {122416d6-6b49-4ee2-a1e8-b825f31c79fe} Library Properties - osu.Game.Rulesets.Osu.Tests.Android + osu.Game.Rulesets.Osu.Tests osu.Game.Rulesets.Osu.Tests.Android 512 True diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml index d9df277133..f731042a4c 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml @@ -1,8 +1,5 @@  - - - - + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj index 2dc5cc37c1..defeab5a64 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj +++ b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj @@ -10,7 +10,7 @@ {122416d6-6b49-4ee2-a1e8-b825f31c79fe} Library Properties - osu.Game.Rulesets.Taiko.Tests.Android + osu.Game.Rulesets.Taiko.Tests osu.Game.Rulesets.Taiko.Tests.Android 512 True diff --git a/osu.Game.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Tests.Android/Properties/AndroidManifest.xml index 20060728da..146f96c2a3 100644 --- a/osu.Game.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Tests.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file From 48686bdb301ba2876324393ecfa18ac6383539cd Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Sun, 6 Jan 2019 18:18:11 -0600 Subject: [PATCH 0333/2854] Fix osu.Android Android manifest --- osu.Android/Properties/AndroidManifest.xml | 2 +- osu.Android/Resources/Resource.designer.cs | 6424 +------------------- 2 files changed, 15 insertions(+), 6411 deletions(-) diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml index 76cb58969f..bb5f9b751e 100644 --- a/osu.Android/Properties/AndroidManifest.xml +++ b/osu.Android/Properties/AndroidManifest.xml @@ -5,5 +5,5 @@ - + \ No newline at end of file diff --git a/osu.Android/Resources/Resource.designer.cs b/osu.Android/Resources/Resource.designer.cs index ed8407c130..c02df072f1 100644 --- a/osu.Android/Resources/Resource.designer.cs +++ b/osu.Android/Resources/Resource.designer.cs @@ -28,1124 +28,9 @@ namespace osu.Android { } - public partial class Animation - { - - // aapt resource value: 0x7f050000 - public const int abc_fade_in = 2131034112; - - // aapt resource value: 0x7f050001 - public const int abc_fade_out = 2131034113; - - // aapt resource value: 0x7f050002 - public const int abc_grow_fade_in_from_bottom = 2131034114; - - // aapt resource value: 0x7f050003 - public const int abc_popup_enter = 2131034115; - - // aapt resource value: 0x7f050004 - public const int abc_popup_exit = 2131034116; - - // aapt resource value: 0x7f050005 - public const int abc_shrink_fade_out_from_bottom = 2131034117; - - // aapt resource value: 0x7f050006 - public const int abc_slide_in_bottom = 2131034118; - - // aapt resource value: 0x7f050007 - public const int abc_slide_in_top = 2131034119; - - // aapt resource value: 0x7f050008 - public const int abc_slide_out_bottom = 2131034120; - - // aapt resource value: 0x7f050009 - public const int abc_slide_out_top = 2131034121; - - // aapt resource value: 0x7f05000a - public const int design_bottom_sheet_slide_in = 2131034122; - - // aapt resource value: 0x7f05000b - public const int design_bottom_sheet_slide_out = 2131034123; - - // aapt resource value: 0x7f05000c - public const int design_snackbar_in = 2131034124; - - // aapt resource value: 0x7f05000d - public const int design_snackbar_out = 2131034125; - - // aapt resource value: 0x7f05000e - public const int tooltip_enter = 2131034126; - - // aapt resource value: 0x7f05000f - public const int tooltip_exit = 2131034127; - - static Animation() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Animation() - { - } - } - - public partial class Animator - { - - // aapt resource value: 0x7f060000 - public const int design_appbar_state_list_animator = 2131099648; - - static Animator() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Animator() - { - } - } - public partial class Attribute { - // aapt resource value: 0x7f010052 - public const int actionBarDivider = 2130772050; - - // aapt resource value: 0x7f010053 - public const int actionBarItemBackground = 2130772051; - - // aapt resource value: 0x7f01004c - public const int actionBarPopupTheme = 2130772044; - - // aapt resource value: 0x7f010051 - public const int actionBarSize = 2130772049; - - // aapt resource value: 0x7f01004e - public const int actionBarSplitStyle = 2130772046; - - // aapt resource value: 0x7f01004d - public const int actionBarStyle = 2130772045; - - // aapt resource value: 0x7f010048 - public const int actionBarTabBarStyle = 2130772040; - - // aapt resource value: 0x7f010047 - public const int actionBarTabStyle = 2130772039; - - // aapt resource value: 0x7f010049 - public const int actionBarTabTextStyle = 2130772041; - - // aapt resource value: 0x7f01004f - public const int actionBarTheme = 2130772047; - - // aapt resource value: 0x7f010050 - public const int actionBarWidgetTheme = 2130772048; - - // aapt resource value: 0x7f01006d - public const int actionButtonStyle = 2130772077; - - // aapt resource value: 0x7f010069 - public const int actionDropDownStyle = 2130772073; - - // aapt resource value: 0x7f0100c4 - public const int actionLayout = 2130772164; - - // aapt resource value: 0x7f010054 - public const int actionMenuTextAppearance = 2130772052; - - // aapt resource value: 0x7f010055 - public const int actionMenuTextColor = 2130772053; - - // aapt resource value: 0x7f010058 - public const int actionModeBackground = 2130772056; - - // aapt resource value: 0x7f010057 - public const int actionModeCloseButtonStyle = 2130772055; - - // aapt resource value: 0x7f01005a - public const int actionModeCloseDrawable = 2130772058; - - // aapt resource value: 0x7f01005c - public const int actionModeCopyDrawable = 2130772060; - - // aapt resource value: 0x7f01005b - public const int actionModeCutDrawable = 2130772059; - - // aapt resource value: 0x7f010060 - public const int actionModeFindDrawable = 2130772064; - - // aapt resource value: 0x7f01005d - public const int actionModePasteDrawable = 2130772061; - - // aapt resource value: 0x7f010062 - public const int actionModePopupWindowStyle = 2130772066; - - // aapt resource value: 0x7f01005e - public const int actionModeSelectAllDrawable = 2130772062; - - // aapt resource value: 0x7f01005f - public const int actionModeShareDrawable = 2130772063; - - // aapt resource value: 0x7f010059 - public const int actionModeSplitBackground = 2130772057; - - // aapt resource value: 0x7f010056 - public const int actionModeStyle = 2130772054; - - // aapt resource value: 0x7f010061 - public const int actionModeWebSearchDrawable = 2130772065; - - // aapt resource value: 0x7f01004a - public const int actionOverflowButtonStyle = 2130772042; - - // aapt resource value: 0x7f01004b - public const int actionOverflowMenuStyle = 2130772043; - - // aapt resource value: 0x7f0100c6 - public const int actionProviderClass = 2130772166; - - // aapt resource value: 0x7f0100c5 - public const int actionViewClass = 2130772165; - - // aapt resource value: 0x7f010075 - public const int activityChooserViewStyle = 2130772085; - - // aapt resource value: 0x7f01009a - public const int alertDialogButtonGroupStyle = 2130772122; - - // aapt resource value: 0x7f01009b - public const int alertDialogCenterButtons = 2130772123; - - // aapt resource value: 0x7f010099 - public const int alertDialogStyle = 2130772121; - - // aapt resource value: 0x7f01009c - public const int alertDialogTheme = 2130772124; - - // aapt resource value: 0x7f0100b2 - public const int allowStacking = 2130772146; - - // aapt resource value: 0x7f0100b3 - public const int alpha = 2130772147; - - // aapt resource value: 0x7f0100c1 - public const int alphabeticModifiers = 2130772161; - - // aapt resource value: 0x7f0100ba - public const int arrowHeadLength = 2130772154; - - // aapt resource value: 0x7f0100bb - public const int arrowShaftLength = 2130772155; - - // aapt resource value: 0x7f0100a1 - public const int autoCompleteTextViewStyle = 2130772129; - - // aapt resource value: 0x7f01003b - public const int autoSizeMaxTextSize = 2130772027; - - // aapt resource value: 0x7f01003a - public const int autoSizeMinTextSize = 2130772026; - - // aapt resource value: 0x7f010039 - public const int autoSizePresetSizes = 2130772025; - - // aapt resource value: 0x7f010038 - public const int autoSizeStepGranularity = 2130772024; - - // aapt resource value: 0x7f010037 - public const int autoSizeTextType = 2130772023; - - // aapt resource value: 0x7f010015 - public const int background = 2130771989; - - // aapt resource value: 0x7f010017 - public const int backgroundSplit = 2130771991; - - // aapt resource value: 0x7f010016 - public const int backgroundStacked = 2130771990; - - // aapt resource value: 0x7f0100fd - public const int backgroundTint = 2130772221; - - // aapt resource value: 0x7f0100fe - public const int backgroundTintMode = 2130772222; - - // aapt resource value: 0x7f0100bc - public const int barLength = 2130772156; - - // aapt resource value: 0x7f010128 - public const int behavior_autoHide = 2130772264; - - // aapt resource value: 0x7f010105 - public const int behavior_hideable = 2130772229; - - // aapt resource value: 0x7f010131 - public const int behavior_overlapTop = 2130772273; - - // aapt resource value: 0x7f010104 - public const int behavior_peekHeight = 2130772228; - - // aapt resource value: 0x7f010106 - public const int behavior_skipCollapsed = 2130772230; - - // aapt resource value: 0x7f010126 - public const int borderWidth = 2130772262; - - // aapt resource value: 0x7f010072 - public const int borderlessButtonStyle = 2130772082; - - // aapt resource value: 0x7f010120 - public const int bottomSheetDialogTheme = 2130772256; - - // aapt resource value: 0x7f010121 - public const int bottomSheetStyle = 2130772257; - - // aapt resource value: 0x7f01006f - public const int buttonBarButtonStyle = 2130772079; - - // aapt resource value: 0x7f01009f - public const int buttonBarNegativeButtonStyle = 2130772127; - - // aapt resource value: 0x7f0100a0 - public const int buttonBarNeutralButtonStyle = 2130772128; - - // aapt resource value: 0x7f01009e - public const int buttonBarPositiveButtonStyle = 2130772126; - - // aapt resource value: 0x7f01006e - public const int buttonBarStyle = 2130772078; - - // aapt resource value: 0x7f0100f2 - public const int buttonGravity = 2130772210; - - // aapt resource value: 0x7f01002a - public const int buttonPanelSideLayout = 2130772010; - - // aapt resource value: 0x7f0100a2 - public const int buttonStyle = 2130772130; - - // aapt resource value: 0x7f0100a3 - public const int buttonStyleSmall = 2130772131; - - // aapt resource value: 0x7f0100b4 - public const int buttonTint = 2130772148; - - // aapt resource value: 0x7f0100b5 - public const int buttonTintMode = 2130772149; - - // aapt resource value: 0x7f0100a4 - public const int checkboxStyle = 2130772132; - - // aapt resource value: 0x7f0100a5 - public const int checkedTextViewStyle = 2130772133; - - // aapt resource value: 0x7f0100d5 - public const int closeIcon = 2130772181; - - // aapt resource value: 0x7f010027 - public const int closeItemLayout = 2130772007; - - // aapt resource value: 0x7f0100f4 - public const int collapseContentDescription = 2130772212; - - // aapt resource value: 0x7f0100f3 - public const int collapseIcon = 2130772211; - - // aapt resource value: 0x7f010113 - public const int collapsedTitleGravity = 2130772243; - - // aapt resource value: 0x7f01010d - public const int collapsedTitleTextAppearance = 2130772237; - - // aapt resource value: 0x7f0100b6 - public const int color = 2130772150; - - // aapt resource value: 0x7f010091 - public const int colorAccent = 2130772113; - - // aapt resource value: 0x7f010098 - public const int colorBackgroundFloating = 2130772120; - - // aapt resource value: 0x7f010095 - public const int colorButtonNormal = 2130772117; - - // aapt resource value: 0x7f010093 - public const int colorControlActivated = 2130772115; - - // aapt resource value: 0x7f010094 - public const int colorControlHighlight = 2130772116; - - // aapt resource value: 0x7f010092 - public const int colorControlNormal = 2130772114; - - // aapt resource value: 0x7f0100b1 - public const int colorError = 2130772145; - - // aapt resource value: 0x7f01008f - public const int colorPrimary = 2130772111; - - // aapt resource value: 0x7f010090 - public const int colorPrimaryDark = 2130772112; - - // aapt resource value: 0x7f010096 - public const int colorSwitchThumbNormal = 2130772118; - - // aapt resource value: 0x7f0100da - public const int commitIcon = 2130772186; - - // aapt resource value: 0x7f0100c7 - public const int contentDescription = 2130772167; - - // aapt resource value: 0x7f010020 - public const int contentInsetEnd = 2130772000; - - // aapt resource value: 0x7f010024 - public const int contentInsetEndWithActions = 2130772004; - - // aapt resource value: 0x7f010021 - public const int contentInsetLeft = 2130772001; - - // aapt resource value: 0x7f010022 - public const int contentInsetRight = 2130772002; - - // aapt resource value: 0x7f01001f - public const int contentInsetStart = 2130771999; - - // aapt resource value: 0x7f010023 - public const int contentInsetStartWithNavigation = 2130772003; - - // aapt resource value: 0x7f01010e - public const int contentScrim = 2130772238; - - // aapt resource value: 0x7f010097 - public const int controlBackground = 2130772119; - - // aapt resource value: 0x7f010147 - public const int counterEnabled = 2130772295; - - // aapt resource value: 0x7f010148 - public const int counterMaxLength = 2130772296; - - // aapt resource value: 0x7f01014a - public const int counterOverflowTextAppearance = 2130772298; - - // aapt resource value: 0x7f010149 - public const int counterTextAppearance = 2130772297; - - // aapt resource value: 0x7f010018 - public const int customNavigationLayout = 2130771992; - - // aapt resource value: 0x7f0100d4 - public const int defaultQueryHint = 2130772180; - - // aapt resource value: 0x7f010067 - public const int dialogPreferredPadding = 2130772071; - - // aapt resource value: 0x7f010066 - public const int dialogTheme = 2130772070; - - // aapt resource value: 0x7f01000e - public const int displayOptions = 2130771982; - - // aapt resource value: 0x7f010014 - public const int divider = 2130771988; - - // aapt resource value: 0x7f010074 - public const int dividerHorizontal = 2130772084; - - // aapt resource value: 0x7f0100c0 - public const int dividerPadding = 2130772160; - - // aapt resource value: 0x7f010073 - public const int dividerVertical = 2130772083; - - // aapt resource value: 0x7f0100b8 - public const int drawableSize = 2130772152; - - // aapt resource value: 0x7f010009 - public const int drawerArrowStyle = 2130771977; - - // aapt resource value: 0x7f010086 - public const int dropDownListViewStyle = 2130772102; - - // aapt resource value: 0x7f01006a - public const int dropdownListPreferredItemHeight = 2130772074; - - // aapt resource value: 0x7f01007b - public const int editTextBackground = 2130772091; - - // aapt resource value: 0x7f01007a - public const int editTextColor = 2130772090; - - // aapt resource value: 0x7f0100a6 - public const int editTextStyle = 2130772134; - - // aapt resource value: 0x7f010025 - public const int elevation = 2130772005; - - // aapt resource value: 0x7f010145 - public const int errorEnabled = 2130772293; - - // aapt resource value: 0x7f010146 - public const int errorTextAppearance = 2130772294; - - // aapt resource value: 0x7f010029 - public const int expandActivityOverflowButtonDrawable = 2130772009; - - // aapt resource value: 0x7f0100ff - public const int expanded = 2130772223; - - // aapt resource value: 0x7f010114 - public const int expandedTitleGravity = 2130772244; - - // aapt resource value: 0x7f010107 - public const int expandedTitleMargin = 2130772231; - - // aapt resource value: 0x7f01010b - public const int expandedTitleMarginBottom = 2130772235; - - // aapt resource value: 0x7f01010a - public const int expandedTitleMarginEnd = 2130772234; - - // aapt resource value: 0x7f010108 - public const int expandedTitleMarginStart = 2130772232; - - // aapt resource value: 0x7f010109 - public const int expandedTitleMarginTop = 2130772233; - - // aapt resource value: 0x7f01010c - public const int expandedTitleTextAppearance = 2130772236; - - // aapt resource value: 0x7f010124 - public const int fabSize = 2130772260; - - // aapt resource value: 0x7f010004 - public const int fastScrollEnabled = 2130771972; - - // aapt resource value: 0x7f010007 - public const int fastScrollHorizontalThumbDrawable = 2130771975; - - // aapt resource value: 0x7f010008 - public const int fastScrollHorizontalTrackDrawable = 2130771976; - - // aapt resource value: 0x7f010005 - public const int fastScrollVerticalThumbDrawable = 2130771973; - - // aapt resource value: 0x7f010006 - public const int fastScrollVerticalTrackDrawable = 2130771974; - - // aapt resource value: 0x7f010158 - public const int font = 2130772312; - - // aapt resource value: 0x7f01003c - public const int fontFamily = 2130772028; - - // aapt resource value: 0x7f010151 - public const int fontProviderAuthority = 2130772305; - - // aapt resource value: 0x7f010154 - public const int fontProviderCerts = 2130772308; - - // aapt resource value: 0x7f010155 - public const int fontProviderFetchStrategy = 2130772309; - - // aapt resource value: 0x7f010156 - public const int fontProviderFetchTimeout = 2130772310; - - // aapt resource value: 0x7f010152 - public const int fontProviderPackage = 2130772306; - - // aapt resource value: 0x7f010153 - public const int fontProviderQuery = 2130772307; - - // aapt resource value: 0x7f010157 - public const int fontStyle = 2130772311; - - // aapt resource value: 0x7f010159 - public const int fontWeight = 2130772313; - - // aapt resource value: 0x7f010129 - public const int foregroundInsidePadding = 2130772265; - - // aapt resource value: 0x7f0100b9 - public const int gapBetweenBars = 2130772153; - - // aapt resource value: 0x7f0100d6 - public const int goIcon = 2130772182; - - // aapt resource value: 0x7f01012f - public const int headerLayout = 2130772271; - - // aapt resource value: 0x7f01000a - public const int height = 2130771978; - - // aapt resource value: 0x7f01001e - public const int hideOnContentScroll = 2130771998; - - // aapt resource value: 0x7f01014b - public const int hintAnimationEnabled = 2130772299; - - // aapt resource value: 0x7f010144 - public const int hintEnabled = 2130772292; - - // aapt resource value: 0x7f010143 - public const int hintTextAppearance = 2130772291; - - // aapt resource value: 0x7f01006c - public const int homeAsUpIndicator = 2130772076; - - // aapt resource value: 0x7f010019 - public const int homeLayout = 2130771993; - - // aapt resource value: 0x7f010012 - public const int icon = 2130771986; - - // aapt resource value: 0x7f0100c9 - public const int iconTint = 2130772169; - - // aapt resource value: 0x7f0100ca - public const int iconTintMode = 2130772170; - - // aapt resource value: 0x7f0100d2 - public const int iconifiedByDefault = 2130772178; - - // aapt resource value: 0x7f01007c - public const int imageButtonStyle = 2130772092; - - // aapt resource value: 0x7f01001b - public const int indeterminateProgressStyle = 2130771995; - - // aapt resource value: 0x7f010028 - public const int initialActivityCount = 2130772008; - - // aapt resource value: 0x7f010130 - public const int insetForeground = 2130772272; - - // aapt resource value: 0x7f01000b - public const int isLightTheme = 2130771979; - - // aapt resource value: 0x7f01012d - public const int itemBackground = 2130772269; - - // aapt resource value: 0x7f01012b - public const int itemIconTint = 2130772267; - - // aapt resource value: 0x7f01001d - public const int itemPadding = 2130771997; - - // aapt resource value: 0x7f01012e - public const int itemTextAppearance = 2130772270; - - // aapt resource value: 0x7f01012c - public const int itemTextColor = 2130772268; - - // aapt resource value: 0x7f010118 - public const int keylines = 2130772248; - - // aapt resource value: 0x7f0100d1 - public const int layout = 2130772177; - - // aapt resource value: 0x7f010000 - public const int layoutManager = 2130771968; - - // aapt resource value: 0x7f01011b - public const int layout_anchor = 2130772251; - - // aapt resource value: 0x7f01011d - public const int layout_anchorGravity = 2130772253; - - // aapt resource value: 0x7f01011a - public const int layout_behavior = 2130772250; - - // aapt resource value: 0x7f010116 - public const int layout_collapseMode = 2130772246; - - // aapt resource value: 0x7f010117 - public const int layout_collapseParallaxMultiplier = 2130772247; - - // aapt resource value: 0x7f01011f - public const int layout_dodgeInsetEdges = 2130772255; - - // aapt resource value: 0x7f01011e - public const int layout_insetEdge = 2130772254; - - // aapt resource value: 0x7f01011c - public const int layout_keyline = 2130772252; - - // aapt resource value: 0x7f010102 - public const int layout_scrollFlags = 2130772226; - - // aapt resource value: 0x7f010103 - public const int layout_scrollInterpolator = 2130772227; - - // aapt resource value: 0x7f01008e - public const int listChoiceBackgroundIndicator = 2130772110; - - // aapt resource value: 0x7f010068 - public const int listDividerAlertDialog = 2130772072; - - // aapt resource value: 0x7f01002e - public const int listItemLayout = 2130772014; - - // aapt resource value: 0x7f01002b - public const int listLayout = 2130772011; - - // aapt resource value: 0x7f0100ae - public const int listMenuViewStyle = 2130772142; - - // aapt resource value: 0x7f010087 - public const int listPopupWindowStyle = 2130772103; - - // aapt resource value: 0x7f010081 - public const int listPreferredItemHeight = 2130772097; - - // aapt resource value: 0x7f010083 - public const int listPreferredItemHeightLarge = 2130772099; - - // aapt resource value: 0x7f010082 - public const int listPreferredItemHeightSmall = 2130772098; - - // aapt resource value: 0x7f010084 - public const int listPreferredItemPaddingLeft = 2130772100; - - // aapt resource value: 0x7f010085 - public const int listPreferredItemPaddingRight = 2130772101; - - // aapt resource value: 0x7f010013 - public const int logo = 2130771987; - - // aapt resource value: 0x7f0100f7 - public const int logoDescription = 2130772215; - - // aapt resource value: 0x7f010132 - public const int maxActionInlineWidth = 2130772274; - - // aapt resource value: 0x7f0100f1 - public const int maxButtonHeight = 2130772209; - - // aapt resource value: 0x7f0100be - public const int measureWithLargestChild = 2130772158; - - // aapt resource value: 0x7f01012a - public const int menu = 2130772266; - - // aapt resource value: 0x7f01002c - public const int multiChoiceItemLayout = 2130772012; - - // aapt resource value: 0x7f0100f6 - public const int navigationContentDescription = 2130772214; - - // aapt resource value: 0x7f0100f5 - public const int navigationIcon = 2130772213; - - // aapt resource value: 0x7f01000d - public const int navigationMode = 2130771981; - - // aapt resource value: 0x7f0100c2 - public const int numericModifiers = 2130772162; - - // aapt resource value: 0x7f0100cd - public const int overlapAnchor = 2130772173; - - // aapt resource value: 0x7f0100cf - public const int paddingBottomNoButtons = 2130772175; - - // aapt resource value: 0x7f0100fb - public const int paddingEnd = 2130772219; - - // aapt resource value: 0x7f0100fa - public const int paddingStart = 2130772218; - - // aapt resource value: 0x7f0100d0 - public const int paddingTopNoTitle = 2130772176; - - // aapt resource value: 0x7f01008b - public const int panelBackground = 2130772107; - - // aapt resource value: 0x7f01008d - public const int panelMenuListTheme = 2130772109; - - // aapt resource value: 0x7f01008c - public const int panelMenuListWidth = 2130772108; - - // aapt resource value: 0x7f01014e - public const int passwordToggleContentDescription = 2130772302; - - // aapt resource value: 0x7f01014d - public const int passwordToggleDrawable = 2130772301; - - // aapt resource value: 0x7f01014c - public const int passwordToggleEnabled = 2130772300; - - // aapt resource value: 0x7f01014f - public const int passwordToggleTint = 2130772303; - - // aapt resource value: 0x7f010150 - public const int passwordToggleTintMode = 2130772304; - - // aapt resource value: 0x7f010078 - public const int popupMenuStyle = 2130772088; - - // aapt resource value: 0x7f010026 - public const int popupTheme = 2130772006; - - // aapt resource value: 0x7f010079 - public const int popupWindowStyle = 2130772089; - - // aapt resource value: 0x7f0100cb - public const int preserveIconSpacing = 2130772171; - - // aapt resource value: 0x7f010125 - public const int pressedTranslationZ = 2130772261; - - // aapt resource value: 0x7f01001c - public const int progressBarPadding = 2130771996; - - // aapt resource value: 0x7f01001a - public const int progressBarStyle = 2130771994; - - // aapt resource value: 0x7f0100dc - public const int queryBackground = 2130772188; - - // aapt resource value: 0x7f0100d3 - public const int queryHint = 2130772179; - - // aapt resource value: 0x7f0100a7 - public const int radioButtonStyle = 2130772135; - - // aapt resource value: 0x7f0100a8 - public const int ratingBarStyle = 2130772136; - - // aapt resource value: 0x7f0100a9 - public const int ratingBarStyleIndicator = 2130772137; - - // aapt resource value: 0x7f0100aa - public const int ratingBarStyleSmall = 2130772138; - - // aapt resource value: 0x7f010002 - public const int reverseLayout = 2130771970; - - // aapt resource value: 0x7f010123 - public const int rippleColor = 2130772259; - - // aapt resource value: 0x7f010112 - public const int scrimAnimationDuration = 2130772242; - - // aapt resource value: 0x7f010111 - public const int scrimVisibleHeightTrigger = 2130772241; - - // aapt resource value: 0x7f0100d8 - public const int searchHintIcon = 2130772184; - - // aapt resource value: 0x7f0100d7 - public const int searchIcon = 2130772183; - - // aapt resource value: 0x7f010080 - public const int searchViewStyle = 2130772096; - - // aapt resource value: 0x7f0100ab - public const int seekBarStyle = 2130772139; - - // aapt resource value: 0x7f010070 - public const int selectableItemBackground = 2130772080; - - // aapt resource value: 0x7f010071 - public const int selectableItemBackgroundBorderless = 2130772081; - - // aapt resource value: 0x7f0100c3 - public const int showAsAction = 2130772163; - - // aapt resource value: 0x7f0100bf - public const int showDividers = 2130772159; - - // aapt resource value: 0x7f0100e8 - public const int showText = 2130772200; - - // aapt resource value: 0x7f01002f - public const int showTitle = 2130772015; - - // aapt resource value: 0x7f01002d - public const int singleChoiceItemLayout = 2130772013; - - // aapt resource value: 0x7f010001 - public const int spanCount = 2130771969; - - // aapt resource value: 0x7f0100b7 - public const int spinBars = 2130772151; - - // aapt resource value: 0x7f01006b - public const int spinnerDropDownItemStyle = 2130772075; - - // aapt resource value: 0x7f0100ac - public const int spinnerStyle = 2130772140; - - // aapt resource value: 0x7f0100e7 - public const int splitTrack = 2130772199; - - // aapt resource value: 0x7f010030 - public const int srcCompat = 2130772016; - - // aapt resource value: 0x7f010003 - public const int stackFromEnd = 2130771971; - - // aapt resource value: 0x7f0100ce - public const int state_above_anchor = 2130772174; - - // aapt resource value: 0x7f010100 - public const int state_collapsed = 2130772224; - - // aapt resource value: 0x7f010101 - public const int state_collapsible = 2130772225; - - // aapt resource value: 0x7f010119 - public const int statusBarBackground = 2130772249; - - // aapt resource value: 0x7f01010f - public const int statusBarScrim = 2130772239; - - // aapt resource value: 0x7f0100cc - public const int subMenuArrow = 2130772172; - - // aapt resource value: 0x7f0100dd - public const int submitBackground = 2130772189; - - // aapt resource value: 0x7f01000f - public const int subtitle = 2130771983; - - // aapt resource value: 0x7f0100ea - public const int subtitleTextAppearance = 2130772202; - - // aapt resource value: 0x7f0100f9 - public const int subtitleTextColor = 2130772217; - - // aapt resource value: 0x7f010011 - public const int subtitleTextStyle = 2130771985; - - // aapt resource value: 0x7f0100db - public const int suggestionRowLayout = 2130772187; - - // aapt resource value: 0x7f0100e5 - public const int switchMinWidth = 2130772197; - - // aapt resource value: 0x7f0100e6 - public const int switchPadding = 2130772198; - - // aapt resource value: 0x7f0100ad - public const int switchStyle = 2130772141; - - // aapt resource value: 0x7f0100e4 - public const int switchTextAppearance = 2130772196; - - // aapt resource value: 0x7f010136 - public const int tabBackground = 2130772278; - - // aapt resource value: 0x7f010135 - public const int tabContentStart = 2130772277; - - // aapt resource value: 0x7f010138 - public const int tabGravity = 2130772280; - - // aapt resource value: 0x7f010133 - public const int tabIndicatorColor = 2130772275; - - // aapt resource value: 0x7f010134 - public const int tabIndicatorHeight = 2130772276; - - // aapt resource value: 0x7f01013a - public const int tabMaxWidth = 2130772282; - - // aapt resource value: 0x7f010139 - public const int tabMinWidth = 2130772281; - - // aapt resource value: 0x7f010137 - public const int tabMode = 2130772279; - - // aapt resource value: 0x7f010142 - public const int tabPadding = 2130772290; - - // aapt resource value: 0x7f010141 - public const int tabPaddingBottom = 2130772289; - - // aapt resource value: 0x7f010140 - public const int tabPaddingEnd = 2130772288; - - // aapt resource value: 0x7f01013e - public const int tabPaddingStart = 2130772286; - - // aapt resource value: 0x7f01013f - public const int tabPaddingTop = 2130772287; - - // aapt resource value: 0x7f01013d - public const int tabSelectedTextColor = 2130772285; - - // aapt resource value: 0x7f01013b - public const int tabTextAppearance = 2130772283; - - // aapt resource value: 0x7f01013c - public const int tabTextColor = 2130772284; - - // aapt resource value: 0x7f010036 - public const int textAllCaps = 2130772022; - - // aapt resource value: 0x7f010063 - public const int textAppearanceLargePopupMenu = 2130772067; - - // aapt resource value: 0x7f010088 - public const int textAppearanceListItem = 2130772104; - - // aapt resource value: 0x7f010089 - public const int textAppearanceListItemSecondary = 2130772105; - - // aapt resource value: 0x7f01008a - public const int textAppearanceListItemSmall = 2130772106; - - // aapt resource value: 0x7f010065 - public const int textAppearancePopupMenuHeader = 2130772069; - - // aapt resource value: 0x7f01007e - public const int textAppearanceSearchResultSubtitle = 2130772094; - - // aapt resource value: 0x7f01007d - public const int textAppearanceSearchResultTitle = 2130772093; - - // aapt resource value: 0x7f010064 - public const int textAppearanceSmallPopupMenu = 2130772068; - - // aapt resource value: 0x7f01009d - public const int textColorAlertDialogListItem = 2130772125; - - // aapt resource value: 0x7f010122 - public const int textColorError = 2130772258; - - // aapt resource value: 0x7f01007f - public const int textColorSearchUrl = 2130772095; - - // aapt resource value: 0x7f0100fc - public const int theme = 2130772220; - - // aapt resource value: 0x7f0100bd - public const int thickness = 2130772157; - - // aapt resource value: 0x7f0100e3 - public const int thumbTextPadding = 2130772195; - - // aapt resource value: 0x7f0100de - public const int thumbTint = 2130772190; - - // aapt resource value: 0x7f0100df - public const int thumbTintMode = 2130772191; - - // aapt resource value: 0x7f010033 - public const int tickMark = 2130772019; - - // aapt resource value: 0x7f010034 - public const int tickMarkTint = 2130772020; - - // aapt resource value: 0x7f010035 - public const int tickMarkTintMode = 2130772021; - - // aapt resource value: 0x7f010031 - public const int tint = 2130772017; - - // aapt resource value: 0x7f010032 - public const int tintMode = 2130772018; - - // aapt resource value: 0x7f01000c - public const int title = 2130771980; - - // aapt resource value: 0x7f010115 - public const int titleEnabled = 2130772245; - - // aapt resource value: 0x7f0100eb - public const int titleMargin = 2130772203; - - // aapt resource value: 0x7f0100ef - public const int titleMarginBottom = 2130772207; - - // aapt resource value: 0x7f0100ed - public const int titleMarginEnd = 2130772205; - - // aapt resource value: 0x7f0100ec - public const int titleMarginStart = 2130772204; - - // aapt resource value: 0x7f0100ee - public const int titleMarginTop = 2130772206; - - // aapt resource value: 0x7f0100f0 - public const int titleMargins = 2130772208; - - // aapt resource value: 0x7f0100e9 - public const int titleTextAppearance = 2130772201; - - // aapt resource value: 0x7f0100f8 - public const int titleTextColor = 2130772216; - - // aapt resource value: 0x7f010010 - public const int titleTextStyle = 2130771984; - - // aapt resource value: 0x7f010110 - public const int toolbarId = 2130772240; - - // aapt resource value: 0x7f010077 - public const int toolbarNavigationButtonStyle = 2130772087; - - // aapt resource value: 0x7f010076 - public const int toolbarStyle = 2130772086; - - // aapt resource value: 0x7f0100b0 - public const int tooltipForegroundColor = 2130772144; - - // aapt resource value: 0x7f0100af - public const int tooltipFrameBackground = 2130772143; - - // aapt resource value: 0x7f0100c8 - public const int tooltipText = 2130772168; - - // aapt resource value: 0x7f0100e0 - public const int track = 2130772192; - - // aapt resource value: 0x7f0100e1 - public const int trackTint = 2130772193; - - // aapt resource value: 0x7f0100e2 - public const int trackTintMode = 2130772194; - - // aapt resource value: 0x7f010127 - public const int useCompatPadding = 2130772263; - - // aapt resource value: 0x7f0100d9 - public const int voiceIcon = 2130772185; - - // aapt resource value: 0x7f01003d - public const int windowActionBar = 2130772029; - - // aapt resource value: 0x7f01003f - public const int windowActionBarOverlay = 2130772031; - - // aapt resource value: 0x7f010040 - public const int windowActionModeOverlay = 2130772032; - - // aapt resource value: 0x7f010044 - public const int windowFixedHeightMajor = 2130772036; - - // aapt resource value: 0x7f010042 - public const int windowFixedHeightMinor = 2130772034; - - // aapt resource value: 0x7f010041 - public const int windowFixedWidthMajor = 2130772033; - - // aapt resource value: 0x7f010043 - public const int windowFixedWidthMinor = 2130772035; - - // aapt resource value: 0x7f010045 - public const int windowMinWidthMajor = 2130772037; - - // aapt resource value: 0x7f010046 - public const int windowMinWidthMinor = 2130772038; - - // aapt resource value: 0x7f01003e - public const int windowNoTitle = 2130772030; - static Attribute() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); @@ -1156,333 +41,20 @@ namespace osu.Android } } - public partial class Boolean - { - - // aapt resource value: 0x7f0b0000 - public const int abc_action_bar_embed_tabs = 2131427328; - - // aapt resource value: 0x7f0b0001 - public const int abc_allow_stacked_button_bar = 2131427329; - - // aapt resource value: 0x7f0b0002 - public const int abc_config_actionMenuItemAllCaps = 2131427330; - - // aapt resource value: 0x7f0b0003 - public const int abc_config_closeDialogWhenTouchOutside = 2131427331; - - // aapt resource value: 0x7f0b0004 - public const int abc_config_showMenuShortcutsWhenKeyboardPresent = 2131427332; - - static Boolean() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Boolean() - { - } - } - public partial class Color { - // aapt resource value: 0x7f0c004b - public const int abc_background_cache_hint_selector_material_dark = 2131492939; + // aapt resource value: 0x7f030002 + public const int colorAccent = 2130903042; - // aapt resource value: 0x7f0c004c - public const int abc_background_cache_hint_selector_material_light = 2131492940; + // aapt resource value: 0x7f030000 + public const int colorPrimary = 2130903040; - // aapt resource value: 0x7f0c004d - public const int abc_btn_colored_borderless_text_material = 2131492941; + // aapt resource value: 0x7f030001 + public const int colorPrimaryDark = 2130903041; - // aapt resource value: 0x7f0c004e - public const int abc_btn_colored_text_material = 2131492942; - - // aapt resource value: 0x7f0c004f - public const int abc_color_highlight_material = 2131492943; - - // aapt resource value: 0x7f0c0050 - public const int abc_hint_foreground_material_dark = 2131492944; - - // aapt resource value: 0x7f0c0051 - public const int abc_hint_foreground_material_light = 2131492945; - - // aapt resource value: 0x7f0c0000 - public const int abc_input_method_navigation_guard = 2131492864; - - // aapt resource value: 0x7f0c0052 - public const int abc_primary_text_disable_only_material_dark = 2131492946; - - // aapt resource value: 0x7f0c0053 - public const int abc_primary_text_disable_only_material_light = 2131492947; - - // aapt resource value: 0x7f0c0054 - public const int abc_primary_text_material_dark = 2131492948; - - // aapt resource value: 0x7f0c0055 - public const int abc_primary_text_material_light = 2131492949; - - // aapt resource value: 0x7f0c0056 - public const int abc_search_url_text = 2131492950; - - // aapt resource value: 0x7f0c0001 - public const int abc_search_url_text_normal = 2131492865; - - // aapt resource value: 0x7f0c0002 - public const int abc_search_url_text_pressed = 2131492866; - - // aapt resource value: 0x7f0c0003 - public const int abc_search_url_text_selected = 2131492867; - - // aapt resource value: 0x7f0c0057 - public const int abc_secondary_text_material_dark = 2131492951; - - // aapt resource value: 0x7f0c0058 - public const int abc_secondary_text_material_light = 2131492952; - - // aapt resource value: 0x7f0c0059 - public const int abc_tint_btn_checkable = 2131492953; - - // aapt resource value: 0x7f0c005a - public const int abc_tint_default = 2131492954; - - // aapt resource value: 0x7f0c005b - public const int abc_tint_edittext = 2131492955; - - // aapt resource value: 0x7f0c005c - public const int abc_tint_seek_thumb = 2131492956; - - // aapt resource value: 0x7f0c005d - public const int abc_tint_spinner = 2131492957; - - // aapt resource value: 0x7f0c005e - public const int abc_tint_switch_track = 2131492958; - - // aapt resource value: 0x7f0c0004 - public const int accent_material_dark = 2131492868; - - // aapt resource value: 0x7f0c0005 - public const int accent_material_light = 2131492869; - - // aapt resource value: 0x7f0c0006 - public const int background_floating_material_dark = 2131492870; - - // aapt resource value: 0x7f0c0007 - public const int background_floating_material_light = 2131492871; - - // aapt resource value: 0x7f0c0008 - public const int background_material_dark = 2131492872; - - // aapt resource value: 0x7f0c0009 - public const int background_material_light = 2131492873; - - // aapt resource value: 0x7f0c000a - public const int bright_foreground_disabled_material_dark = 2131492874; - - // aapt resource value: 0x7f0c000b - public const int bright_foreground_disabled_material_light = 2131492875; - - // aapt resource value: 0x7f0c000c - public const int bright_foreground_inverse_material_dark = 2131492876; - - // aapt resource value: 0x7f0c000d - public const int bright_foreground_inverse_material_light = 2131492877; - - // aapt resource value: 0x7f0c000e - public const int bright_foreground_material_dark = 2131492878; - - // aapt resource value: 0x7f0c000f - public const int bright_foreground_material_light = 2131492879; - - // aapt resource value: 0x7f0c0010 - public const int button_material_dark = 2131492880; - - // aapt resource value: 0x7f0c0011 - public const int button_material_light = 2131492881; - - // aapt resource value: 0x7f0c0049 - public const int colorAccent = 2131492937; - - // aapt resource value: 0x7f0c0047 - public const int colorPrimary = 2131492935; - - // aapt resource value: 0x7f0c0048 - public const int colorPrimaryDark = 2131492936; - - // aapt resource value: 0x7f0c003c - public const int design_bottom_navigation_shadow_color = 2131492924; - - // aapt resource value: 0x7f0c005f - public const int design_error = 2131492959; - - // aapt resource value: 0x7f0c003d - public const int design_fab_shadow_end_color = 2131492925; - - // aapt resource value: 0x7f0c003e - public const int design_fab_shadow_mid_color = 2131492926; - - // aapt resource value: 0x7f0c003f - public const int design_fab_shadow_start_color = 2131492927; - - // aapt resource value: 0x7f0c0040 - public const int design_fab_stroke_end_inner_color = 2131492928; - - // aapt resource value: 0x7f0c0041 - public const int design_fab_stroke_end_outer_color = 2131492929; - - // aapt resource value: 0x7f0c0042 - public const int design_fab_stroke_top_inner_color = 2131492930; - - // aapt resource value: 0x7f0c0043 - public const int design_fab_stroke_top_outer_color = 2131492931; - - // aapt resource value: 0x7f0c0044 - public const int design_snackbar_background_color = 2131492932; - - // aapt resource value: 0x7f0c0060 - public const int design_tint_password_toggle = 2131492960; - - // aapt resource value: 0x7f0c0012 - public const int dim_foreground_disabled_material_dark = 2131492882; - - // aapt resource value: 0x7f0c0013 - public const int dim_foreground_disabled_material_light = 2131492883; - - // aapt resource value: 0x7f0c0014 - public const int dim_foreground_material_dark = 2131492884; - - // aapt resource value: 0x7f0c0015 - public const int dim_foreground_material_light = 2131492885; - - // aapt resource value: 0x7f0c0016 - public const int error_color_material = 2131492886; - - // aapt resource value: 0x7f0c0017 - public const int foreground_material_dark = 2131492887; - - // aapt resource value: 0x7f0c0018 - public const int foreground_material_light = 2131492888; - - // aapt resource value: 0x7f0c0019 - public const int highlighted_text_material_dark = 2131492889; - - // aapt resource value: 0x7f0c001a - public const int highlighted_text_material_light = 2131492890; - - // aapt resource value: 0x7f0c004a - public const int ic_launcher_background = 2131492938; - - // aapt resource value: 0x7f0c001b - public const int material_blue_grey_800 = 2131492891; - - // aapt resource value: 0x7f0c001c - public const int material_blue_grey_900 = 2131492892; - - // aapt resource value: 0x7f0c001d - public const int material_blue_grey_950 = 2131492893; - - // aapt resource value: 0x7f0c001e - public const int material_deep_teal_200 = 2131492894; - - // aapt resource value: 0x7f0c001f - public const int material_deep_teal_500 = 2131492895; - - // aapt resource value: 0x7f0c0020 - public const int material_grey_100 = 2131492896; - - // aapt resource value: 0x7f0c0021 - public const int material_grey_300 = 2131492897; - - // aapt resource value: 0x7f0c0022 - public const int material_grey_50 = 2131492898; - - // aapt resource value: 0x7f0c0023 - public const int material_grey_600 = 2131492899; - - // aapt resource value: 0x7f0c0024 - public const int material_grey_800 = 2131492900; - - // aapt resource value: 0x7f0c0025 - public const int material_grey_850 = 2131492901; - - // aapt resource value: 0x7f0c0026 - public const int material_grey_900 = 2131492902; - - // aapt resource value: 0x7f0c0045 - public const int notification_action_color_filter = 2131492933; - - // aapt resource value: 0x7f0c0046 - public const int notification_icon_bg_color = 2131492934; - - // aapt resource value: 0x7f0c003b - public const int notification_material_background_media_default_color = 2131492923; - - // aapt resource value: 0x7f0c0027 - public const int primary_dark_material_dark = 2131492903; - - // aapt resource value: 0x7f0c0028 - public const int primary_dark_material_light = 2131492904; - - // aapt resource value: 0x7f0c0029 - public const int primary_material_dark = 2131492905; - - // aapt resource value: 0x7f0c002a - public const int primary_material_light = 2131492906; - - // aapt resource value: 0x7f0c002b - public const int primary_text_default_material_dark = 2131492907; - - // aapt resource value: 0x7f0c002c - public const int primary_text_default_material_light = 2131492908; - - // aapt resource value: 0x7f0c002d - public const int primary_text_disabled_material_dark = 2131492909; - - // aapt resource value: 0x7f0c002e - public const int primary_text_disabled_material_light = 2131492910; - - // aapt resource value: 0x7f0c002f - public const int ripple_material_dark = 2131492911; - - // aapt resource value: 0x7f0c0030 - public const int ripple_material_light = 2131492912; - - // aapt resource value: 0x7f0c0031 - public const int secondary_text_default_material_dark = 2131492913; - - // aapt resource value: 0x7f0c0032 - public const int secondary_text_default_material_light = 2131492914; - - // aapt resource value: 0x7f0c0033 - public const int secondary_text_disabled_material_dark = 2131492915; - - // aapt resource value: 0x7f0c0034 - public const int secondary_text_disabled_material_light = 2131492916; - - // aapt resource value: 0x7f0c0035 - public const int switch_thumb_disabled_material_dark = 2131492917; - - // aapt resource value: 0x7f0c0036 - public const int switch_thumb_disabled_material_light = 2131492918; - - // aapt resource value: 0x7f0c0061 - public const int switch_thumb_material_dark = 2131492961; - - // aapt resource value: 0x7f0c0062 - public const int switch_thumb_material_light = 2131492962; - - // aapt resource value: 0x7f0c0037 - public const int switch_thumb_normal_material_dark = 2131492919; - - // aapt resource value: 0x7f0c0038 - public const int switch_thumb_normal_material_light = 2131492920; - - // aapt resource value: 0x7f0c0039 - public const int tooltip_background_dark = 2131492921; - - // aapt resource value: 0x7f0c003a - public const int tooltip_background_light = 2131492922; + // aapt resource value: 0x7f030003 + public const int ic_launcher_background = 2130903043; static Color() { @@ -1494,1624 +66,17 @@ namespace osu.Android } } - public partial class Dimension - { - - // aapt resource value: 0x7f070012 - public const int abc_action_bar_content_inset_material = 2131165202; - - // aapt resource value: 0x7f070013 - public const int abc_action_bar_content_inset_with_nav = 2131165203; - - // aapt resource value: 0x7f070007 - public const int abc_action_bar_default_height_material = 2131165191; - - // aapt resource value: 0x7f070014 - public const int abc_action_bar_default_padding_end_material = 2131165204; - - // aapt resource value: 0x7f070015 - public const int abc_action_bar_default_padding_start_material = 2131165205; - - // aapt resource value: 0x7f070017 - public const int abc_action_bar_elevation_material = 2131165207; - - // aapt resource value: 0x7f070018 - public const int abc_action_bar_icon_vertical_padding_material = 2131165208; - - // aapt resource value: 0x7f070019 - public const int abc_action_bar_overflow_padding_end_material = 2131165209; - - // aapt resource value: 0x7f07001a - public const int abc_action_bar_overflow_padding_start_material = 2131165210; - - // aapt resource value: 0x7f070008 - public const int abc_action_bar_progress_bar_size = 2131165192; - - // aapt resource value: 0x7f07001b - public const int abc_action_bar_stacked_max_height = 2131165211; - - // aapt resource value: 0x7f07001c - public const int abc_action_bar_stacked_tab_max_width = 2131165212; - - // aapt resource value: 0x7f07001d - public const int abc_action_bar_subtitle_bottom_margin_material = 2131165213; - - // aapt resource value: 0x7f07001e - public const int abc_action_bar_subtitle_top_margin_material = 2131165214; - - // aapt resource value: 0x7f07001f - public const int abc_action_button_min_height_material = 2131165215; - - // aapt resource value: 0x7f070020 - public const int abc_action_button_min_width_material = 2131165216; - - // aapt resource value: 0x7f070021 - public const int abc_action_button_min_width_overflow_material = 2131165217; - - // aapt resource value: 0x7f070006 - public const int abc_alert_dialog_button_bar_height = 2131165190; - - // aapt resource value: 0x7f070022 - public const int abc_button_inset_horizontal_material = 2131165218; - - // aapt resource value: 0x7f070023 - public const int abc_button_inset_vertical_material = 2131165219; - - // aapt resource value: 0x7f070024 - public const int abc_button_padding_horizontal_material = 2131165220; - - // aapt resource value: 0x7f070025 - public const int abc_button_padding_vertical_material = 2131165221; - - // aapt resource value: 0x7f070026 - public const int abc_cascading_menus_min_smallest_width = 2131165222; - - // aapt resource value: 0x7f07000b - public const int abc_config_prefDialogWidth = 2131165195; - - // aapt resource value: 0x7f070027 - public const int abc_control_corner_material = 2131165223; - - // aapt resource value: 0x7f070028 - public const int abc_control_inset_material = 2131165224; - - // aapt resource value: 0x7f070029 - public const int abc_control_padding_material = 2131165225; - - // aapt resource value: 0x7f07000c - public const int abc_dialog_fixed_height_major = 2131165196; - - // aapt resource value: 0x7f07000d - public const int abc_dialog_fixed_height_minor = 2131165197; - - // aapt resource value: 0x7f07000e - public const int abc_dialog_fixed_width_major = 2131165198; - - // aapt resource value: 0x7f07000f - public const int abc_dialog_fixed_width_minor = 2131165199; - - // aapt resource value: 0x7f07002a - public const int abc_dialog_list_padding_bottom_no_buttons = 2131165226; - - // aapt resource value: 0x7f07002b - public const int abc_dialog_list_padding_top_no_title = 2131165227; - - // aapt resource value: 0x7f070010 - public const int abc_dialog_min_width_major = 2131165200; - - // aapt resource value: 0x7f070011 - public const int abc_dialog_min_width_minor = 2131165201; - - // aapt resource value: 0x7f07002c - public const int abc_dialog_padding_material = 2131165228; - - // aapt resource value: 0x7f07002d - public const int abc_dialog_padding_top_material = 2131165229; - - // aapt resource value: 0x7f07002e - public const int abc_dialog_title_divider_material = 2131165230; - - // aapt resource value: 0x7f07002f - public const int abc_disabled_alpha_material_dark = 2131165231; - - // aapt resource value: 0x7f070030 - public const int abc_disabled_alpha_material_light = 2131165232; - - // aapt resource value: 0x7f070031 - public const int abc_dropdownitem_icon_width = 2131165233; - - // aapt resource value: 0x7f070032 - public const int abc_dropdownitem_text_padding_left = 2131165234; - - // aapt resource value: 0x7f070033 - public const int abc_dropdownitem_text_padding_right = 2131165235; - - // aapt resource value: 0x7f070034 - public const int abc_edit_text_inset_bottom_material = 2131165236; - - // aapt resource value: 0x7f070035 - public const int abc_edit_text_inset_horizontal_material = 2131165237; - - // aapt resource value: 0x7f070036 - public const int abc_edit_text_inset_top_material = 2131165238; - - // aapt resource value: 0x7f070037 - public const int abc_floating_window_z = 2131165239; - - // aapt resource value: 0x7f070038 - public const int abc_list_item_padding_horizontal_material = 2131165240; - - // aapt resource value: 0x7f070039 - public const int abc_panel_menu_list_width = 2131165241; - - // aapt resource value: 0x7f07003a - public const int abc_progress_bar_height_material = 2131165242; - - // aapt resource value: 0x7f07003b - public const int abc_search_view_preferred_height = 2131165243; - - // aapt resource value: 0x7f07003c - public const int abc_search_view_preferred_width = 2131165244; - - // aapt resource value: 0x7f07003d - public const int abc_seekbar_track_background_height_material = 2131165245; - - // aapt resource value: 0x7f07003e - public const int abc_seekbar_track_progress_height_material = 2131165246; - - // aapt resource value: 0x7f07003f - public const int abc_select_dialog_padding_start_material = 2131165247; - - // aapt resource value: 0x7f070016 - public const int abc_switch_padding = 2131165206; - - // aapt resource value: 0x7f070040 - public const int abc_text_size_body_1_material = 2131165248; - - // aapt resource value: 0x7f070041 - public const int abc_text_size_body_2_material = 2131165249; - - // aapt resource value: 0x7f070042 - public const int abc_text_size_button_material = 2131165250; - - // aapt resource value: 0x7f070043 - public const int abc_text_size_caption_material = 2131165251; - - // aapt resource value: 0x7f070044 - public const int abc_text_size_display_1_material = 2131165252; - - // aapt resource value: 0x7f070045 - public const int abc_text_size_display_2_material = 2131165253; - - // aapt resource value: 0x7f070046 - public const int abc_text_size_display_3_material = 2131165254; - - // aapt resource value: 0x7f070047 - public const int abc_text_size_display_4_material = 2131165255; - - // aapt resource value: 0x7f070048 - public const int abc_text_size_headline_material = 2131165256; - - // aapt resource value: 0x7f070049 - public const int abc_text_size_large_material = 2131165257; - - // aapt resource value: 0x7f07004a - public const int abc_text_size_medium_material = 2131165258; - - // aapt resource value: 0x7f07004b - public const int abc_text_size_menu_header_material = 2131165259; - - // aapt resource value: 0x7f07004c - public const int abc_text_size_menu_material = 2131165260; - - // aapt resource value: 0x7f07004d - public const int abc_text_size_small_material = 2131165261; - - // aapt resource value: 0x7f07004e - public const int abc_text_size_subhead_material = 2131165262; - - // aapt resource value: 0x7f070009 - public const int abc_text_size_subtitle_material_toolbar = 2131165193; - - // aapt resource value: 0x7f07004f - public const int abc_text_size_title_material = 2131165263; - - // aapt resource value: 0x7f07000a - public const int abc_text_size_title_material_toolbar = 2131165194; - - // aapt resource value: 0x7f07008b - public const int compat_button_inset_horizontal_material = 2131165323; - - // aapt resource value: 0x7f07008c - public const int compat_button_inset_vertical_material = 2131165324; - - // aapt resource value: 0x7f07008d - public const int compat_button_padding_horizontal_material = 2131165325; - - // aapt resource value: 0x7f07008e - public const int compat_button_padding_vertical_material = 2131165326; - - // aapt resource value: 0x7f07008f - public const int compat_control_corner_material = 2131165327; - - // aapt resource value: 0x7f070069 - public const int design_appbar_elevation = 2131165289; - - // aapt resource value: 0x7f07006a - public const int design_bottom_navigation_active_item_max_width = 2131165290; - - // aapt resource value: 0x7f07006b - public const int design_bottom_navigation_active_text_size = 2131165291; - - // aapt resource value: 0x7f07006c - public const int design_bottom_navigation_elevation = 2131165292; - - // aapt resource value: 0x7f07006d - public const int design_bottom_navigation_height = 2131165293; - - // aapt resource value: 0x7f07006e - public const int design_bottom_navigation_item_max_width = 2131165294; - - // aapt resource value: 0x7f07006f - public const int design_bottom_navigation_item_min_width = 2131165295; - - // aapt resource value: 0x7f070070 - public const int design_bottom_navigation_margin = 2131165296; - - // aapt resource value: 0x7f070071 - public const int design_bottom_navigation_shadow_height = 2131165297; - - // aapt resource value: 0x7f070072 - public const int design_bottom_navigation_text_size = 2131165298; - - // aapt resource value: 0x7f070073 - public const int design_bottom_sheet_modal_elevation = 2131165299; - - // aapt resource value: 0x7f070074 - public const int design_bottom_sheet_peek_height_min = 2131165300; - - // aapt resource value: 0x7f070075 - public const int design_fab_border_width = 2131165301; - - // aapt resource value: 0x7f070076 - public const int design_fab_elevation = 2131165302; - - // aapt resource value: 0x7f070077 - public const int design_fab_image_size = 2131165303; - - // aapt resource value: 0x7f070078 - public const int design_fab_size_mini = 2131165304; - - // aapt resource value: 0x7f070079 - public const int design_fab_size_normal = 2131165305; - - // aapt resource value: 0x7f07007a - public const int design_fab_translation_z_pressed = 2131165306; - - // aapt resource value: 0x7f07007b - public const int design_navigation_elevation = 2131165307; - - // aapt resource value: 0x7f07007c - public const int design_navigation_icon_padding = 2131165308; - - // aapt resource value: 0x7f07007d - public const int design_navigation_icon_size = 2131165309; - - // aapt resource value: 0x7f070061 - public const int design_navigation_max_width = 2131165281; - - // aapt resource value: 0x7f07007e - public const int design_navigation_padding_bottom = 2131165310; - - // aapt resource value: 0x7f07007f - public const int design_navigation_separator_vertical_padding = 2131165311; - - // aapt resource value: 0x7f070062 - public const int design_snackbar_action_inline_max_width = 2131165282; - - // aapt resource value: 0x7f070063 - public const int design_snackbar_background_corner_radius = 2131165283; - - // aapt resource value: 0x7f070080 - public const int design_snackbar_elevation = 2131165312; - - // aapt resource value: 0x7f070064 - public const int design_snackbar_extra_spacing_horizontal = 2131165284; - - // aapt resource value: 0x7f070065 - public const int design_snackbar_max_width = 2131165285; - - // aapt resource value: 0x7f070066 - public const int design_snackbar_min_width = 2131165286; - - // aapt resource value: 0x7f070081 - public const int design_snackbar_padding_horizontal = 2131165313; - - // aapt resource value: 0x7f070082 - public const int design_snackbar_padding_vertical = 2131165314; - - // aapt resource value: 0x7f070067 - public const int design_snackbar_padding_vertical_2lines = 2131165287; - - // aapt resource value: 0x7f070083 - public const int design_snackbar_text_size = 2131165315; - - // aapt resource value: 0x7f070084 - public const int design_tab_max_width = 2131165316; - - // aapt resource value: 0x7f070068 - public const int design_tab_scrollable_min_width = 2131165288; - - // aapt resource value: 0x7f070085 - public const int design_tab_text_size = 2131165317; - - // aapt resource value: 0x7f070086 - public const int design_tab_text_size_2line = 2131165318; - - // aapt resource value: 0x7f070050 - public const int disabled_alpha_material_dark = 2131165264; - - // aapt resource value: 0x7f070051 - public const int disabled_alpha_material_light = 2131165265; - - // aapt resource value: 0x7f070000 - public const int fastscroll_default_thickness = 2131165184; - - // aapt resource value: 0x7f070001 - public const int fastscroll_margin = 2131165185; - - // aapt resource value: 0x7f070002 - public const int fastscroll_minimum_range = 2131165186; - - // aapt resource value: 0x7f070052 - public const int highlight_alpha_material_colored = 2131165266; - - // aapt resource value: 0x7f070053 - public const int highlight_alpha_material_dark = 2131165267; - - // aapt resource value: 0x7f070054 - public const int highlight_alpha_material_light = 2131165268; - - // aapt resource value: 0x7f070055 - public const int hint_alpha_material_dark = 2131165269; - - // aapt resource value: 0x7f070056 - public const int hint_alpha_material_light = 2131165270; - - // aapt resource value: 0x7f070057 - public const int hint_pressed_alpha_material_dark = 2131165271; - - // aapt resource value: 0x7f070058 - public const int hint_pressed_alpha_material_light = 2131165272; - - // aapt resource value: 0x7f070003 - public const int item_touch_helper_max_drag_scroll_per_frame = 2131165187; - - // aapt resource value: 0x7f070004 - public const int item_touch_helper_swipe_escape_max_velocity = 2131165188; - - // aapt resource value: 0x7f070005 - public const int item_touch_helper_swipe_escape_velocity = 2131165189; - - // aapt resource value: 0x7f070090 - public const int notification_action_icon_size = 2131165328; - - // aapt resource value: 0x7f070091 - public const int notification_action_text_size = 2131165329; - - // aapt resource value: 0x7f070092 - public const int notification_big_circle_margin = 2131165330; - - // aapt resource value: 0x7f070088 - public const int notification_content_margin_start = 2131165320; - - // aapt resource value: 0x7f070093 - public const int notification_large_icon_height = 2131165331; - - // aapt resource value: 0x7f070094 - public const int notification_large_icon_width = 2131165332; - - // aapt resource value: 0x7f070089 - public const int notification_main_column_padding_top = 2131165321; - - // aapt resource value: 0x7f07008a - public const int notification_media_narrow_margin = 2131165322; - - // aapt resource value: 0x7f070095 - public const int notification_right_icon_size = 2131165333; - - // aapt resource value: 0x7f070087 - public const int notification_right_side_padding_top = 2131165319; - - // aapt resource value: 0x7f070096 - public const int notification_small_icon_background_padding = 2131165334; - - // aapt resource value: 0x7f070097 - public const int notification_small_icon_size_as_large = 2131165335; - - // aapt resource value: 0x7f070098 - public const int notification_subtext_size = 2131165336; - - // aapt resource value: 0x7f070099 - public const int notification_top_pad = 2131165337; - - // aapt resource value: 0x7f07009a - public const int notification_top_pad_large_text = 2131165338; - - // aapt resource value: 0x7f070059 - public const int tooltip_corner_radius = 2131165273; - - // aapt resource value: 0x7f07005a - public const int tooltip_horizontal_padding = 2131165274; - - // aapt resource value: 0x7f07005b - public const int tooltip_margin = 2131165275; - - // aapt resource value: 0x7f07005c - public const int tooltip_precise_anchor_extra_offset = 2131165276; - - // aapt resource value: 0x7f07005d - public const int tooltip_precise_anchor_threshold = 2131165277; - - // aapt resource value: 0x7f07005e - public const int tooltip_vertical_padding = 2131165278; - - // aapt resource value: 0x7f07005f - public const int tooltip_y_offset_non_touch = 2131165279; - - // aapt resource value: 0x7f070060 - public const int tooltip_y_offset_touch = 2131165280; - - static Dimension() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Dimension() - { - } - } - - public partial class Drawable - { - - // aapt resource value: 0x7f020000 - public const int abc_ab_share_pack_mtrl_alpha = 2130837504; - - // aapt resource value: 0x7f020001 - public const int abc_action_bar_item_background_material = 2130837505; - - // aapt resource value: 0x7f020002 - public const int abc_btn_borderless_material = 2130837506; - - // aapt resource value: 0x7f020003 - public const int abc_btn_check_material = 2130837507; - - // aapt resource value: 0x7f020004 - public const int abc_btn_check_to_on_mtrl_000 = 2130837508; - - // aapt resource value: 0x7f020005 - public const int abc_btn_check_to_on_mtrl_015 = 2130837509; - - // aapt resource value: 0x7f020006 - public const int abc_btn_colored_material = 2130837510; - - // aapt resource value: 0x7f020007 - public const int abc_btn_default_mtrl_shape = 2130837511; - - // aapt resource value: 0x7f020008 - public const int abc_btn_radio_material = 2130837512; - - // aapt resource value: 0x7f020009 - public const int abc_btn_radio_to_on_mtrl_000 = 2130837513; - - // aapt resource value: 0x7f02000a - public const int abc_btn_radio_to_on_mtrl_015 = 2130837514; - - // aapt resource value: 0x7f02000b - public const int abc_btn_switch_to_on_mtrl_00001 = 2130837515; - - // aapt resource value: 0x7f02000c - public const int abc_btn_switch_to_on_mtrl_00012 = 2130837516; - - // aapt resource value: 0x7f02000d - public const int abc_cab_background_internal_bg = 2130837517; - - // aapt resource value: 0x7f02000e - public const int abc_cab_background_top_material = 2130837518; - - // aapt resource value: 0x7f02000f - public const int abc_cab_background_top_mtrl_alpha = 2130837519; - - // aapt resource value: 0x7f020010 - public const int abc_control_background_material = 2130837520; - - // aapt resource value: 0x7f020011 - public const int abc_dialog_material_background = 2130837521; - - // aapt resource value: 0x7f020012 - public const int abc_edit_text_material = 2130837522; - - // aapt resource value: 0x7f020013 - public const int abc_ic_ab_back_material = 2130837523; - - // aapt resource value: 0x7f020014 - public const int abc_ic_arrow_drop_right_black_24dp = 2130837524; - - // aapt resource value: 0x7f020015 - public const int abc_ic_clear_material = 2130837525; - - // aapt resource value: 0x7f020016 - public const int abc_ic_commit_search_api_mtrl_alpha = 2130837526; - - // aapt resource value: 0x7f020017 - public const int abc_ic_go_search_api_material = 2130837527; - - // aapt resource value: 0x7f020018 - public const int abc_ic_menu_copy_mtrl_am_alpha = 2130837528; - - // aapt resource value: 0x7f020019 - public const int abc_ic_menu_cut_mtrl_alpha = 2130837529; - - // aapt resource value: 0x7f02001a - public const int abc_ic_menu_overflow_material = 2130837530; - - // aapt resource value: 0x7f02001b - public const int abc_ic_menu_paste_mtrl_am_alpha = 2130837531; - - // aapt resource value: 0x7f02001c - public const int abc_ic_menu_selectall_mtrl_alpha = 2130837532; - - // aapt resource value: 0x7f02001d - public const int abc_ic_menu_share_mtrl_alpha = 2130837533; - - // aapt resource value: 0x7f02001e - public const int abc_ic_search_api_material = 2130837534; - - // aapt resource value: 0x7f02001f - public const int abc_ic_star_black_16dp = 2130837535; - - // aapt resource value: 0x7f020020 - public const int abc_ic_star_black_36dp = 2130837536; - - // aapt resource value: 0x7f020021 - public const int abc_ic_star_black_48dp = 2130837537; - - // aapt resource value: 0x7f020022 - public const int abc_ic_star_half_black_16dp = 2130837538; - - // aapt resource value: 0x7f020023 - public const int abc_ic_star_half_black_36dp = 2130837539; - - // aapt resource value: 0x7f020024 - public const int abc_ic_star_half_black_48dp = 2130837540; - - // aapt resource value: 0x7f020025 - public const int abc_ic_voice_search_api_material = 2130837541; - - // aapt resource value: 0x7f020026 - public const int abc_item_background_holo_dark = 2130837542; - - // aapt resource value: 0x7f020027 - public const int abc_item_background_holo_light = 2130837543; - - // aapt resource value: 0x7f020028 - public const int abc_list_divider_mtrl_alpha = 2130837544; - - // aapt resource value: 0x7f020029 - public const int abc_list_focused_holo = 2130837545; - - // aapt resource value: 0x7f02002a - public const int abc_list_longpressed_holo = 2130837546; - - // aapt resource value: 0x7f02002b - public const int abc_list_pressed_holo_dark = 2130837547; - - // aapt resource value: 0x7f02002c - public const int abc_list_pressed_holo_light = 2130837548; - - // aapt resource value: 0x7f02002d - public const int abc_list_selector_background_transition_holo_dark = 2130837549; - - // aapt resource value: 0x7f02002e - public const int abc_list_selector_background_transition_holo_light = 2130837550; - - // aapt resource value: 0x7f02002f - public const int abc_list_selector_disabled_holo_dark = 2130837551; - - // aapt resource value: 0x7f020030 - public const int abc_list_selector_disabled_holo_light = 2130837552; - - // aapt resource value: 0x7f020031 - public const int abc_list_selector_holo_dark = 2130837553; - - // aapt resource value: 0x7f020032 - public const int abc_list_selector_holo_light = 2130837554; - - // aapt resource value: 0x7f020033 - public const int abc_menu_hardkey_panel_mtrl_mult = 2130837555; - - // aapt resource value: 0x7f020034 - public const int abc_popup_background_mtrl_mult = 2130837556; - - // aapt resource value: 0x7f020035 - public const int abc_ratingbar_indicator_material = 2130837557; - - // aapt resource value: 0x7f020036 - public const int abc_ratingbar_material = 2130837558; - - // aapt resource value: 0x7f020037 - public const int abc_ratingbar_small_material = 2130837559; - - // aapt resource value: 0x7f020038 - public const int abc_scrubber_control_off_mtrl_alpha = 2130837560; - - // aapt resource value: 0x7f020039 - public const int abc_scrubber_control_to_pressed_mtrl_000 = 2130837561; - - // aapt resource value: 0x7f02003a - public const int abc_scrubber_control_to_pressed_mtrl_005 = 2130837562; - - // aapt resource value: 0x7f02003b - public const int abc_scrubber_primary_mtrl_alpha = 2130837563; - - // aapt resource value: 0x7f02003c - public const int abc_scrubber_track_mtrl_alpha = 2130837564; - - // aapt resource value: 0x7f02003d - public const int abc_seekbar_thumb_material = 2130837565; - - // aapt resource value: 0x7f02003e - public const int abc_seekbar_tick_mark_material = 2130837566; - - // aapt resource value: 0x7f02003f - public const int abc_seekbar_track_material = 2130837567; - - // aapt resource value: 0x7f020040 - public const int abc_spinner_mtrl_am_alpha = 2130837568; - - // aapt resource value: 0x7f020041 - public const int abc_spinner_textfield_background_material = 2130837569; - - // aapt resource value: 0x7f020042 - public const int abc_switch_thumb_material = 2130837570; - - // aapt resource value: 0x7f020043 - public const int abc_switch_track_mtrl_alpha = 2130837571; - - // aapt resource value: 0x7f020044 - public const int abc_tab_indicator_material = 2130837572; - - // aapt resource value: 0x7f020045 - public const int abc_tab_indicator_mtrl_alpha = 2130837573; - - // aapt resource value: 0x7f020046 - public const int abc_text_cursor_material = 2130837574; - - // aapt resource value: 0x7f020047 - public const int abc_text_select_handle_left_mtrl_dark = 2130837575; - - // aapt resource value: 0x7f020048 - public const int abc_text_select_handle_left_mtrl_light = 2130837576; - - // aapt resource value: 0x7f020049 - public const int abc_text_select_handle_middle_mtrl_dark = 2130837577; - - // aapt resource value: 0x7f02004a - public const int abc_text_select_handle_middle_mtrl_light = 2130837578; - - // aapt resource value: 0x7f02004b - public const int abc_text_select_handle_right_mtrl_dark = 2130837579; - - // aapt resource value: 0x7f02004c - public const int abc_text_select_handle_right_mtrl_light = 2130837580; - - // aapt resource value: 0x7f02004d - public const int abc_textfield_activated_mtrl_alpha = 2130837581; - - // aapt resource value: 0x7f02004e - public const int abc_textfield_default_mtrl_alpha = 2130837582; - - // aapt resource value: 0x7f02004f - public const int abc_textfield_search_activated_mtrl_alpha = 2130837583; - - // aapt resource value: 0x7f020050 - public const int abc_textfield_search_default_mtrl_alpha = 2130837584; - - // aapt resource value: 0x7f020051 - public const int abc_textfield_search_material = 2130837585; - - // aapt resource value: 0x7f020052 - public const int abc_vector_test = 2130837586; - - // aapt resource value: 0x7f020053 - public const int avd_hide_password = 2130837587; - - // aapt resource value: 0x7f02006a - public const int avd_hide_password_1 = 2130837610; - - // aapt resource value: 0x7f02006b - public const int avd_hide_password_2 = 2130837611; - - // aapt resource value: 0x7f02006c - public const int avd_hide_password_3 = 2130837612; - - // aapt resource value: 0x7f020054 - public const int avd_show_password = 2130837588; - - // aapt resource value: 0x7f02006d - public const int avd_show_password_1 = 2130837613; - - // aapt resource value: 0x7f02006e - public const int avd_show_password_2 = 2130837614; - - // aapt resource value: 0x7f02006f - public const int avd_show_password_3 = 2130837615; - - // aapt resource value: 0x7f020055 - public const int design_bottom_navigation_item_background = 2130837589; - - // aapt resource value: 0x7f020056 - public const int design_fab_background = 2130837590; - - // aapt resource value: 0x7f020057 - public const int design_ic_visibility = 2130837591; - - // aapt resource value: 0x7f020058 - public const int design_ic_visibility_off = 2130837592; - - // aapt resource value: 0x7f020059 - public const int design_password_eye = 2130837593; - - // aapt resource value: 0x7f02005a - public const int design_snackbar_background = 2130837594; - - // aapt resource value: 0x7f02005b - public const int navigation_empty_icon = 2130837595; - - // aapt resource value: 0x7f02005c - public const int notification_action_background = 2130837596; - - // aapt resource value: 0x7f02005d - public const int notification_bg = 2130837597; - - // aapt resource value: 0x7f02005e - public const int notification_bg_low = 2130837598; - - // aapt resource value: 0x7f02005f - public const int notification_bg_low_normal = 2130837599; - - // aapt resource value: 0x7f020060 - public const int notification_bg_low_pressed = 2130837600; - - // aapt resource value: 0x7f020061 - public const int notification_bg_normal = 2130837601; - - // aapt resource value: 0x7f020062 - public const int notification_bg_normal_pressed = 2130837602; - - // aapt resource value: 0x7f020063 - public const int notification_icon_background = 2130837603; - - // aapt resource value: 0x7f020068 - public const int notification_template_icon_bg = 2130837608; - - // aapt resource value: 0x7f020069 - public const int notification_template_icon_low_bg = 2130837609; - - // aapt resource value: 0x7f020064 - public const int notification_tile_bg = 2130837604; - - // aapt resource value: 0x7f020065 - public const int notify_panel_notification_icon_bg = 2130837605; - - // aapt resource value: 0x7f020066 - public const int tooltip_frame_dark = 2130837606; - - // aapt resource value: 0x7f020067 - public const int tooltip_frame_light = 2130837607; - - static Drawable() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Drawable() - { - } - } - - public partial class Id - { - - // aapt resource value: 0x7f080032 - public const int ALT = 2131230770; - - // aapt resource value: 0x7f080033 - public const int CTRL = 2131230771; - - // aapt resource value: 0x7f080034 - public const int FUNCTION = 2131230772; - - // aapt resource value: 0x7f080035 - public const int META = 2131230773; - - // aapt resource value: 0x7f080036 - public const int SHIFT = 2131230774; - - // aapt resource value: 0x7f080037 - public const int SYM = 2131230775; - - // aapt resource value: 0x7f08009d - public const int action0 = 2131230877; - - // aapt resource value: 0x7f08007c - public const int action_bar = 2131230844; - - // aapt resource value: 0x7f080001 - public const int action_bar_activity_content = 2131230721; - - // aapt resource value: 0x7f08007b - public const int action_bar_container = 2131230843; - - // aapt resource value: 0x7f080077 - public const int action_bar_root = 2131230839; - - // aapt resource value: 0x7f080002 - public const int action_bar_spinner = 2131230722; - - // aapt resource value: 0x7f08005b - public const int action_bar_subtitle = 2131230811; - - // aapt resource value: 0x7f08005a - public const int action_bar_title = 2131230810; - - // aapt resource value: 0x7f08009a - public const int action_container = 2131230874; - - // aapt resource value: 0x7f08007d - public const int action_context_bar = 2131230845; - - // aapt resource value: 0x7f0800a1 - public const int action_divider = 2131230881; - - // aapt resource value: 0x7f08009b - public const int action_image = 2131230875; - - // aapt resource value: 0x7f080003 - public const int action_menu_divider = 2131230723; - - // aapt resource value: 0x7f080004 - public const int action_menu_presenter = 2131230724; - - // aapt resource value: 0x7f080079 - public const int action_mode_bar = 2131230841; - - // aapt resource value: 0x7f080078 - public const int action_mode_bar_stub = 2131230840; - - // aapt resource value: 0x7f08005c - public const int action_mode_close_button = 2131230812; - - // aapt resource value: 0x7f08009c - public const int action_text = 2131230876; - - // aapt resource value: 0x7f0800aa - public const int actions = 2131230890; - - // aapt resource value: 0x7f08005d - public const int activity_chooser_view_content = 2131230813; - - // aapt resource value: 0x7f080027 - public const int add = 2131230759; - - // aapt resource value: 0x7f080070 - public const int alertTitle = 2131230832; - - // aapt resource value: 0x7f080052 - public const int all = 2131230802; - - // aapt resource value: 0x7f080038 - public const int always = 2131230776; - - // aapt resource value: 0x7f080056 - public const int async = 2131230806; - - // aapt resource value: 0x7f080044 - public const int auto = 2131230788; - - // aapt resource value: 0x7f08002f - public const int beginning = 2131230767; - - // aapt resource value: 0x7f080057 - public const int blocking = 2131230807; - - // aapt resource value: 0x7f08003d - public const int bottom = 2131230781; - - // aapt resource value: 0x7f080063 - public const int buttonPanel = 2131230819; - - // aapt resource value: 0x7f08009e - public const int cancel_action = 2131230878; - - // aapt resource value: 0x7f080045 - public const int center = 2131230789; - - // aapt resource value: 0x7f080046 - public const int center_horizontal = 2131230790; - - // aapt resource value: 0x7f080047 - public const int center_vertical = 2131230791; - - // aapt resource value: 0x7f080073 - public const int checkbox = 2131230835; - - // aapt resource value: 0x7f0800a6 - public const int chronometer = 2131230886; - - // aapt resource value: 0x7f08004e - public const int clip_horizontal = 2131230798; - - // aapt resource value: 0x7f08004f - public const int clip_vertical = 2131230799; - - // aapt resource value: 0x7f080039 - public const int collapseActionView = 2131230777; - - // aapt resource value: 0x7f08008e - public const int container = 2131230862; - - // aapt resource value: 0x7f080066 - public const int contentPanel = 2131230822; - - // aapt resource value: 0x7f08008f - public const int coordinator = 2131230863; - - // aapt resource value: 0x7f08006d - public const int custom = 2131230829; - - // aapt resource value: 0x7f08006c - public const int customPanel = 2131230828; - - // aapt resource value: 0x7f08007a - public const int decor_content_parent = 2131230842; - - // aapt resource value: 0x7f080060 - public const int default_activity_button = 2131230816; - - // aapt resource value: 0x7f080091 - public const int design_bottom_sheet = 2131230865; - - // aapt resource value: 0x7f080098 - public const int design_menu_item_action_area = 2131230872; - - // aapt resource value: 0x7f080097 - public const int design_menu_item_action_area_stub = 2131230871; - - // aapt resource value: 0x7f080096 - public const int design_menu_item_text = 2131230870; - - // aapt resource value: 0x7f080095 - public const int design_navigation_view = 2131230869; - - // aapt resource value: 0x7f080020 - public const int disableHome = 2131230752; - - // aapt resource value: 0x7f08007e - public const int edit_query = 2131230846; - - // aapt resource value: 0x7f080030 - public const int end = 2131230768; - - // aapt resource value: 0x7f0800ac - public const int end_padder = 2131230892; - - // aapt resource value: 0x7f08003f - public const int enterAlways = 2131230783; - - // aapt resource value: 0x7f080040 - public const int enterAlwaysCollapsed = 2131230784; - - // aapt resource value: 0x7f080041 - public const int exitUntilCollapsed = 2131230785; - - // aapt resource value: 0x7f08005e - public const int expand_activities_button = 2131230814; - - // aapt resource value: 0x7f080072 - public const int expanded_menu = 2131230834; - - // aapt resource value: 0x7f080050 - public const int fill = 2131230800; - - // aapt resource value: 0x7f080051 - public const int fill_horizontal = 2131230801; - - // aapt resource value: 0x7f080048 - public const int fill_vertical = 2131230792; - - // aapt resource value: 0x7f080054 - public const int @fixed = 2131230804; - - // aapt resource value: 0x7f080058 - public const int forever = 2131230808; - - // aapt resource value: 0x7f08008b - public const int gameView1 = 2131230859; - - // aapt resource value: 0x7f08000a - public const int ghost_view = 2131230730; - - // aapt resource value: 0x7f080005 - public const int home = 2131230725; - - // aapt resource value: 0x7f080021 - public const int homeAsUp = 2131230753; - - // aapt resource value: 0x7f080062 - public const int icon = 2131230818; - - // aapt resource value: 0x7f0800ab - public const int icon_group = 2131230891; - - // aapt resource value: 0x7f08003a - public const int ifRoom = 2131230778; - - // aapt resource value: 0x7f08005f - public const int image = 2131230815; - - // aapt resource value: 0x7f0800a7 - public const int info = 2131230887; - - // aapt resource value: 0x7f080059 - public const int italic = 2131230809; - - // aapt resource value: 0x7f080000 - public const int item_touch_helper_previous_elevation = 2131230720; - - // aapt resource value: 0x7f08008d - public const int largeLabel = 2131230861; - - // aapt resource value: 0x7f080049 - public const int left = 2131230793; - - // aapt resource value: 0x7f080017 - public const int line1 = 2131230743; - - // aapt resource value: 0x7f080018 - public const int line3 = 2131230744; - - // aapt resource value: 0x7f08001d - public const int listMode = 2131230749; - - // aapt resource value: 0x7f080061 - public const int list_item = 2131230817; - - // aapt resource value: 0x7f0800af - public const int masked = 2131230895; - - // aapt resource value: 0x7f0800a0 - public const int media_actions = 2131230880; - - // aapt resource value: 0x7f0800ad - public const int message = 2131230893; - - // aapt resource value: 0x7f080031 - public const int middle = 2131230769; - - // aapt resource value: 0x7f080053 - public const int mini = 2131230803; - - // aapt resource value: 0x7f080028 - public const int multiply = 2131230760; - - // aapt resource value: 0x7f080094 - public const int navigation_header_container = 2131230868; - - // aapt resource value: 0x7f08003b - public const int never = 2131230779; - - // aapt resource value: 0x7f080022 - public const int none = 2131230754; - - // aapt resource value: 0x7f08001e - public const int normal = 2131230750; - - // aapt resource value: 0x7f0800a9 - public const int notification_background = 2131230889; - - // aapt resource value: 0x7f0800a3 - public const int notification_main_column = 2131230883; - - // aapt resource value: 0x7f0800a2 - public const int notification_main_column_container = 2131230882; - - // aapt resource value: 0x7f08004c - public const int parallax = 2131230796; - - // aapt resource value: 0x7f080065 - public const int parentPanel = 2131230821; - - // aapt resource value: 0x7f08000b - public const int parent_matrix = 2131230731; - - // aapt resource value: 0x7f08004d - public const int pin = 2131230797; - - // aapt resource value: 0x7f080006 - public const int progress_circular = 2131230726; - - // aapt resource value: 0x7f080007 - public const int progress_horizontal = 2131230727; - - // aapt resource value: 0x7f080075 - public const int radio = 2131230837; - - // aapt resource value: 0x7f08004a - public const int right = 2131230794; - - // aapt resource value: 0x7f0800a8 - public const int right_icon = 2131230888; - - // aapt resource value: 0x7f0800a4 - public const int right_side = 2131230884; - - // aapt resource value: 0x7f08000c - public const int save_image_matrix = 2131230732; - - // aapt resource value: 0x7f08000d - public const int save_non_transition_alpha = 2131230733; - - // aapt resource value: 0x7f08000e - public const int save_scale_type = 2131230734; - - // aapt resource value: 0x7f080029 - public const int screen = 2131230761; - - // aapt resource value: 0x7f080042 - public const int scroll = 2131230786; - - // aapt resource value: 0x7f08006b - public const int scrollIndicatorDown = 2131230827; - - // aapt resource value: 0x7f080067 - public const int scrollIndicatorUp = 2131230823; - - // aapt resource value: 0x7f080068 - public const int scrollView = 2131230824; - - // aapt resource value: 0x7f080055 - public const int scrollable = 2131230805; - - // aapt resource value: 0x7f080080 - public const int search_badge = 2131230848; - - // aapt resource value: 0x7f08007f - public const int search_bar = 2131230847; - - // aapt resource value: 0x7f080081 - public const int search_button = 2131230849; - - // aapt resource value: 0x7f080086 - public const int search_close_btn = 2131230854; - - // aapt resource value: 0x7f080082 - public const int search_edit_frame = 2131230850; - - // aapt resource value: 0x7f080088 - public const int search_go_btn = 2131230856; - - // aapt resource value: 0x7f080083 - public const int search_mag_icon = 2131230851; - - // aapt resource value: 0x7f080084 - public const int search_plate = 2131230852; - - // aapt resource value: 0x7f080085 - public const int search_src_text = 2131230853; - - // aapt resource value: 0x7f080089 - public const int search_voice_btn = 2131230857; - - // aapt resource value: 0x7f08008a - public const int select_dialog_listview = 2131230858; - - // aapt resource value: 0x7f080074 - public const int shortcut = 2131230836; - - // aapt resource value: 0x7f080023 - public const int showCustom = 2131230755; - - // aapt resource value: 0x7f080024 - public const int showHome = 2131230756; - - // aapt resource value: 0x7f080025 - public const int showTitle = 2131230757; - - // aapt resource value: 0x7f08008c - public const int smallLabel = 2131230860; - - // aapt resource value: 0x7f080093 - public const int snackbar_action = 2131230867; - - // aapt resource value: 0x7f080092 - public const int snackbar_text = 2131230866; - - // aapt resource value: 0x7f080043 - public const int snap = 2131230787; - - // aapt resource value: 0x7f080064 - public const int spacer = 2131230820; - - // aapt resource value: 0x7f080008 - public const int split_action_bar = 2131230728; - - // aapt resource value: 0x7f08002a - public const int src_atop = 2131230762; - - // aapt resource value: 0x7f08002b - public const int src_in = 2131230763; - - // aapt resource value: 0x7f08002c - public const int src_over = 2131230764; - - // aapt resource value: 0x7f08004b - public const int start = 2131230795; - - // aapt resource value: 0x7f08009f - public const int status_bar_latest_event_content = 2131230879; - - // aapt resource value: 0x7f080076 - public const int submenuarrow = 2131230838; - - // aapt resource value: 0x7f080087 - public const int submit_area = 2131230855; - - // aapt resource value: 0x7f08001f - public const int tabMode = 2131230751; - - // aapt resource value: 0x7f080019 - public const int tag_transition_group = 2131230745; - - // aapt resource value: 0x7f08001a - public const int text = 2131230746; - - // aapt resource value: 0x7f08001b - public const int text2 = 2131230747; - - // aapt resource value: 0x7f08006a - public const int textSpacerNoButtons = 2131230826; - - // aapt resource value: 0x7f080069 - public const int textSpacerNoTitle = 2131230825; - - // aapt resource value: 0x7f080099 - public const int text_input_password_toggle = 2131230873; - - // aapt resource value: 0x7f080014 - public const int textinput_counter = 2131230740; - - // aapt resource value: 0x7f080015 - public const int textinput_error = 2131230741; - - // aapt resource value: 0x7f0800a5 - public const int time = 2131230885; - - // aapt resource value: 0x7f08001c - public const int title = 2131230748; - - // aapt resource value: 0x7f080071 - public const int titleDividerNoCustom = 2131230833; - - // aapt resource value: 0x7f08006f - public const int title_template = 2131230831; - - // aapt resource value: 0x7f08003e - public const int top = 2131230782; - - // aapt resource value: 0x7f08006e - public const int topPanel = 2131230830; - - // aapt resource value: 0x7f080090 - public const int touch_outside = 2131230864; - - // aapt resource value: 0x7f08000f - public const int transition_current_scene = 2131230735; - - // aapt resource value: 0x7f080010 - public const int transition_layout_save = 2131230736; - - // aapt resource value: 0x7f080011 - public const int transition_position = 2131230737; - - // aapt resource value: 0x7f080012 - public const int transition_scene_layoutid_cache = 2131230738; - - // aapt resource value: 0x7f080013 - public const int transition_transform = 2131230739; - - // aapt resource value: 0x7f08002d - public const int uniform = 2131230765; - - // aapt resource value: 0x7f080009 - public const int up = 2131230729; - - // aapt resource value: 0x7f080026 - public const int useLogo = 2131230758; - - // aapt resource value: 0x7f080016 - public const int view_offset_helper = 2131230742; - - // aapt resource value: 0x7f0800ae - public const int visible = 2131230894; - - // aapt resource value: 0x7f08003c - public const int withText = 2131230780; - - // aapt resource value: 0x7f08002e - public const int wrap_content = 2131230766; - - static Id() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Id() - { - } - } - - public partial class Integer - { - - // aapt resource value: 0x7f0d0000 - public const int abc_config_activityDefaultDur = 2131558400; - - // aapt resource value: 0x7f0d0001 - public const int abc_config_activityShortDur = 2131558401; - - // aapt resource value: 0x7f0d0005 - public const int app_bar_elevation_anim_duration = 2131558405; - - // aapt resource value: 0x7f0d0006 - public const int bottom_sheet_slide_duration = 2131558406; - - // aapt resource value: 0x7f0d0002 - public const int cancel_button_image_alpha = 2131558402; - - // aapt resource value: 0x7f0d0003 - public const int config_tooltipAnimTime = 2131558403; - - // aapt resource value: 0x7f0d0004 - public const int design_snackbar_text_max_lines = 2131558404; - - // aapt resource value: 0x7f0d0007 - public const int hide_password_duration = 2131558407; - - // aapt resource value: 0x7f0d0008 - public const int show_password_duration = 2131558408; - - // aapt resource value: 0x7f0d0009 - public const int status_bar_notification_info_maxnum = 2131558409; - - static Integer() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Integer() - { - } - } - - public partial class Layout - { - - // aapt resource value: 0x7f040000 - public const int abc_action_bar_title_item = 2130968576; - - // aapt resource value: 0x7f040001 - public const int abc_action_bar_up_container = 2130968577; - - // aapt resource value: 0x7f040002 - public const int abc_action_menu_item_layout = 2130968578; - - // aapt resource value: 0x7f040003 - public const int abc_action_menu_layout = 2130968579; - - // aapt resource value: 0x7f040004 - public const int abc_action_mode_bar = 2130968580; - - // aapt resource value: 0x7f040005 - public const int abc_action_mode_close_item_material = 2130968581; - - // aapt resource value: 0x7f040006 - public const int abc_activity_chooser_view = 2130968582; - - // aapt resource value: 0x7f040007 - public const int abc_activity_chooser_view_list_item = 2130968583; - - // aapt resource value: 0x7f040008 - public const int abc_alert_dialog_button_bar_material = 2130968584; - - // aapt resource value: 0x7f040009 - public const int abc_alert_dialog_material = 2130968585; - - // aapt resource value: 0x7f04000a - public const int abc_alert_dialog_title_material = 2130968586; - - // aapt resource value: 0x7f04000b - public const int abc_dialog_title_material = 2130968587; - - // aapt resource value: 0x7f04000c - public const int abc_expanded_menu_layout = 2130968588; - - // aapt resource value: 0x7f04000d - public const int abc_list_menu_item_checkbox = 2130968589; - - // aapt resource value: 0x7f04000e - public const int abc_list_menu_item_icon = 2130968590; - - // aapt resource value: 0x7f04000f - public const int abc_list_menu_item_layout = 2130968591; - - // aapt resource value: 0x7f040010 - public const int abc_list_menu_item_radio = 2130968592; - - // aapt resource value: 0x7f040011 - public const int abc_popup_menu_header_item_layout = 2130968593; - - // aapt resource value: 0x7f040012 - public const int abc_popup_menu_item_layout = 2130968594; - - // aapt resource value: 0x7f040013 - public const int abc_screen_content_include = 2130968595; - - // aapt resource value: 0x7f040014 - public const int abc_screen_simple = 2130968596; - - // aapt resource value: 0x7f040015 - public const int abc_screen_simple_overlay_action_mode = 2130968597; - - // aapt resource value: 0x7f040016 - public const int abc_screen_toolbar = 2130968598; - - // aapt resource value: 0x7f040017 - public const int abc_search_dropdown_item_icons_2line = 2130968599; - - // aapt resource value: 0x7f040018 - public const int abc_search_view = 2130968600; - - // aapt resource value: 0x7f040019 - public const int abc_select_dialog_material = 2130968601; - - // aapt resource value: 0x7f04001a - public const int activity_main = 2130968602; - - // aapt resource value: 0x7f04001b - public const int design_bottom_navigation_item = 2130968603; - - // aapt resource value: 0x7f04001c - public const int design_bottom_sheet_dialog = 2130968604; - - // aapt resource value: 0x7f04001d - public const int design_layout_snackbar = 2130968605; - - // aapt resource value: 0x7f04001e - public const int design_layout_snackbar_include = 2130968606; - - // aapt resource value: 0x7f04001f - public const int design_layout_tab_icon = 2130968607; - - // aapt resource value: 0x7f040020 - public const int design_layout_tab_text = 2130968608; - - // aapt resource value: 0x7f040021 - public const int design_menu_item_action_area = 2130968609; - - // aapt resource value: 0x7f040022 - public const int design_navigation_item = 2130968610; - - // aapt resource value: 0x7f040023 - public const int design_navigation_item_header = 2130968611; - - // aapt resource value: 0x7f040024 - public const int design_navigation_item_separator = 2130968612; - - // aapt resource value: 0x7f040025 - public const int design_navigation_item_subheader = 2130968613; - - // aapt resource value: 0x7f040026 - public const int design_navigation_menu = 2130968614; - - // aapt resource value: 0x7f040027 - public const int design_navigation_menu_item = 2130968615; - - // aapt resource value: 0x7f040028 - public const int design_text_input_password_icon = 2130968616; - - // aapt resource value: 0x7f040029 - public const int notification_action = 2130968617; - - // aapt resource value: 0x7f04002a - public const int notification_action_tombstone = 2130968618; - - // aapt resource value: 0x7f04002b - public const int notification_media_action = 2130968619; - - // aapt resource value: 0x7f04002c - public const int notification_media_cancel_action = 2130968620; - - // aapt resource value: 0x7f04002d - public const int notification_template_big_media = 2130968621; - - // aapt resource value: 0x7f04002e - public const int notification_template_big_media_custom = 2130968622; - - // aapt resource value: 0x7f04002f - public const int notification_template_big_media_narrow = 2130968623; - - // aapt resource value: 0x7f040030 - public const int notification_template_big_media_narrow_custom = 2130968624; - - // aapt resource value: 0x7f040031 - public const int notification_template_custom_big = 2130968625; - - // aapt resource value: 0x7f040032 - public const int notification_template_icon_group = 2130968626; - - // aapt resource value: 0x7f040033 - public const int notification_template_lines_media = 2130968627; - - // aapt resource value: 0x7f040034 - public const int notification_template_media = 2130968628; - - // aapt resource value: 0x7f040035 - public const int notification_template_media_custom = 2130968629; - - // aapt resource value: 0x7f040036 - public const int notification_template_part_chronometer = 2130968630; - - // aapt resource value: 0x7f040037 - public const int notification_template_part_time = 2130968631; - - // aapt resource value: 0x7f040038 - public const int select_dialog_item_material = 2130968632; - - // aapt resource value: 0x7f040039 - public const int select_dialog_multichoice_material = 2130968633; - - // aapt resource value: 0x7f04003a - public const int select_dialog_singlechoice_material = 2130968634; - - // aapt resource value: 0x7f04003b - public const int support_simple_spinner_dropdown_item = 2130968635; - - // aapt resource value: 0x7f04003c - public const int tooltip = 2130968636; - - static Layout() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Layout() - { - } - } - public partial class Mipmap { - // aapt resource value: 0x7f030000 - public const int ic_launcher = 2130903040; + // aapt resource value: 0x7f020000 + public const int ic_launcher = 2130837504; - // aapt resource value: 0x7f030001 - public const int ic_launcher_foreground = 2130903041; + // aapt resource value: 0x7f020001 + public const int ic_launcher_foreground = 2130837505; - // aapt resource value: 0x7f030002 - public const int ic_launcher_round = 2130903042; + // aapt resource value: 0x7f020002 + public const int ic_launcher_round = 2130837506; static Mipmap() { @@ -3122,3367 +87,6 @@ namespace osu.Android { } } - - public partial class String - { - - // aapt resource value: 0x7f090000 - public const int abc_action_bar_home_description = 2131296256; - - // aapt resource value: 0x7f090001 - public const int abc_action_bar_up_description = 2131296257; - - // aapt resource value: 0x7f090002 - public const int abc_action_menu_overflow_description = 2131296258; - - // aapt resource value: 0x7f090003 - public const int abc_action_mode_done = 2131296259; - - // aapt resource value: 0x7f090004 - public const int abc_activity_chooser_view_see_all = 2131296260; - - // aapt resource value: 0x7f090005 - public const int abc_activitychooserview_choose_application = 2131296261; - - // aapt resource value: 0x7f090006 - public const int abc_capital_off = 2131296262; - - // aapt resource value: 0x7f090007 - public const int abc_capital_on = 2131296263; - - // aapt resource value: 0x7f090012 - public const int abc_font_family_body_1_material = 2131296274; - - // aapt resource value: 0x7f090013 - public const int abc_font_family_body_2_material = 2131296275; - - // aapt resource value: 0x7f090014 - public const int abc_font_family_button_material = 2131296276; - - // aapt resource value: 0x7f090015 - public const int abc_font_family_caption_material = 2131296277; - - // aapt resource value: 0x7f090016 - public const int abc_font_family_display_1_material = 2131296278; - - // aapt resource value: 0x7f090017 - public const int abc_font_family_display_2_material = 2131296279; - - // aapt resource value: 0x7f090018 - public const int abc_font_family_display_3_material = 2131296280; - - // aapt resource value: 0x7f090019 - public const int abc_font_family_display_4_material = 2131296281; - - // aapt resource value: 0x7f09001a - public const int abc_font_family_headline_material = 2131296282; - - // aapt resource value: 0x7f09001b - public const int abc_font_family_menu_material = 2131296283; - - // aapt resource value: 0x7f09001c - public const int abc_font_family_subhead_material = 2131296284; - - // aapt resource value: 0x7f09001d - public const int abc_font_family_title_material = 2131296285; - - // aapt resource value: 0x7f090008 - public const int abc_search_hint = 2131296264; - - // aapt resource value: 0x7f090009 - public const int abc_searchview_description_clear = 2131296265; - - // aapt resource value: 0x7f09000a - public const int abc_searchview_description_query = 2131296266; - - // aapt resource value: 0x7f09000b - public const int abc_searchview_description_search = 2131296267; - - // aapt resource value: 0x7f09000c - public const int abc_searchview_description_submit = 2131296268; - - // aapt resource value: 0x7f09000d - public const int abc_searchview_description_voice = 2131296269; - - // aapt resource value: 0x7f09000e - public const int abc_shareactionprovider_share_with = 2131296270; - - // aapt resource value: 0x7f09000f - public const int abc_shareactionprovider_share_with_application = 2131296271; - - // aapt resource value: 0x7f090010 - public const int abc_toolbar_collapse_description = 2131296272; - - // aapt resource value: 0x7f090028 - public const int action_settings = 2131296296; - - // aapt resource value: 0x7f090027 - public const int app_name = 2131296295; - - // aapt resource value: 0x7f09001e - public const int appbar_scrolling_view_behavior = 2131296286; - - // aapt resource value: 0x7f09001f - public const int bottom_sheet_behavior = 2131296287; - - // aapt resource value: 0x7f090020 - public const int character_counter_pattern = 2131296288; - - // aapt resource value: 0x7f090021 - public const int password_toggle_content_description = 2131296289; - - // aapt resource value: 0x7f090022 - public const int path_password_eye = 2131296290; - - // aapt resource value: 0x7f090023 - public const int path_password_eye_mask_strike_through = 2131296291; - - // aapt resource value: 0x7f090024 - public const int path_password_eye_mask_visible = 2131296292; - - // aapt resource value: 0x7f090025 - public const int path_password_strike_through = 2131296293; - - // aapt resource value: 0x7f090011 - public const int search_menu_title = 2131296273; - - // aapt resource value: 0x7f090026 - public const int status_bar_notification_info_overflow = 2131296294; - - static String() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private String() - { - } - } - - public partial class Style - { - - // aapt resource value: 0x7f0a0095 - public const int AlertDialog_AppCompat = 2131361941; - - // aapt resource value: 0x7f0a0096 - public const int AlertDialog_AppCompat_Light = 2131361942; - - // aapt resource value: 0x7f0a0097 - public const int Animation_AppCompat_Dialog = 2131361943; - - // aapt resource value: 0x7f0a0098 - public const int Animation_AppCompat_DropDownUp = 2131361944; - - // aapt resource value: 0x7f0a0099 - public const int Animation_AppCompat_Tooltip = 2131361945; - - // aapt resource value: 0x7f0a015f - public const int Animation_Design_BottomSheetDialog = 2131362143; - - // aapt resource value: 0x7f0a0180 - public const int AppTheme = 2131362176; - - // aapt resource value: 0x7f0a009a - public const int Base_AlertDialog_AppCompat = 2131361946; - - // aapt resource value: 0x7f0a009b - public const int Base_AlertDialog_AppCompat_Light = 2131361947; - - // aapt resource value: 0x7f0a009c - public const int Base_Animation_AppCompat_Dialog = 2131361948; - - // aapt resource value: 0x7f0a009d - public const int Base_Animation_AppCompat_DropDownUp = 2131361949; - - // aapt resource value: 0x7f0a009e - public const int Base_Animation_AppCompat_Tooltip = 2131361950; - - // aapt resource value: 0x7f0a009f - public const int Base_DialogWindowTitle_AppCompat = 2131361951; - - // aapt resource value: 0x7f0a00a0 - public const int Base_DialogWindowTitleBackground_AppCompat = 2131361952; - - // aapt resource value: 0x7f0a0039 - public const int Base_TextAppearance_AppCompat = 2131361849; - - // aapt resource value: 0x7f0a003a - public const int Base_TextAppearance_AppCompat_Body1 = 2131361850; - - // aapt resource value: 0x7f0a003b - public const int Base_TextAppearance_AppCompat_Body2 = 2131361851; - - // aapt resource value: 0x7f0a0027 - public const int Base_TextAppearance_AppCompat_Button = 2131361831; - - // aapt resource value: 0x7f0a003c - public const int Base_TextAppearance_AppCompat_Caption = 2131361852; - - // aapt resource value: 0x7f0a003d - public const int Base_TextAppearance_AppCompat_Display1 = 2131361853; - - // aapt resource value: 0x7f0a003e - public const int Base_TextAppearance_AppCompat_Display2 = 2131361854; - - // aapt resource value: 0x7f0a003f - public const int Base_TextAppearance_AppCompat_Display3 = 2131361855; - - // aapt resource value: 0x7f0a0040 - public const int Base_TextAppearance_AppCompat_Display4 = 2131361856; - - // aapt resource value: 0x7f0a0041 - public const int Base_TextAppearance_AppCompat_Headline = 2131361857; - - // aapt resource value: 0x7f0a000b - public const int Base_TextAppearance_AppCompat_Inverse = 2131361803; - - // aapt resource value: 0x7f0a0042 - public const int Base_TextAppearance_AppCompat_Large = 2131361858; - - // aapt resource value: 0x7f0a000c - public const int Base_TextAppearance_AppCompat_Large_Inverse = 2131361804; - - // aapt resource value: 0x7f0a0043 - public const int Base_TextAppearance_AppCompat_Light_Widget_PopupMenu_Large = 2131361859; - - // aapt resource value: 0x7f0a0044 - public const int Base_TextAppearance_AppCompat_Light_Widget_PopupMenu_Small = 2131361860; - - // aapt resource value: 0x7f0a0045 - public const int Base_TextAppearance_AppCompat_Medium = 2131361861; - - // aapt resource value: 0x7f0a000d - public const int Base_TextAppearance_AppCompat_Medium_Inverse = 2131361805; - - // aapt resource value: 0x7f0a0046 - public const int Base_TextAppearance_AppCompat_Menu = 2131361862; - - // aapt resource value: 0x7f0a00a1 - public const int Base_TextAppearance_AppCompat_SearchResult = 2131361953; - - // aapt resource value: 0x7f0a0047 - public const int Base_TextAppearance_AppCompat_SearchResult_Subtitle = 2131361863; - - // aapt resource value: 0x7f0a0048 - public const int Base_TextAppearance_AppCompat_SearchResult_Title = 2131361864; - - // aapt resource value: 0x7f0a0049 - public const int Base_TextAppearance_AppCompat_Small = 2131361865; - - // aapt resource value: 0x7f0a000e - public const int Base_TextAppearance_AppCompat_Small_Inverse = 2131361806; - - // aapt resource value: 0x7f0a004a - public const int Base_TextAppearance_AppCompat_Subhead = 2131361866; - - // aapt resource value: 0x7f0a000f - public const int Base_TextAppearance_AppCompat_Subhead_Inverse = 2131361807; - - // aapt resource value: 0x7f0a004b - public const int Base_TextAppearance_AppCompat_Title = 2131361867; - - // aapt resource value: 0x7f0a0010 - public const int Base_TextAppearance_AppCompat_Title_Inverse = 2131361808; - - // aapt resource value: 0x7f0a00a2 - public const int Base_TextAppearance_AppCompat_Tooltip = 2131361954; - - // aapt resource value: 0x7f0a0086 - public const int Base_TextAppearance_AppCompat_Widget_ActionBar_Menu = 2131361926; - - // aapt resource value: 0x7f0a004c - public const int Base_TextAppearance_AppCompat_Widget_ActionBar_Subtitle = 2131361868; - - // aapt resource value: 0x7f0a004d - public const int Base_TextAppearance_AppCompat_Widget_ActionBar_Subtitle_Inverse = 2131361869; - - // aapt resource value: 0x7f0a004e - public const int Base_TextAppearance_AppCompat_Widget_ActionBar_Title = 2131361870; - - // aapt resource value: 0x7f0a004f - public const int Base_TextAppearance_AppCompat_Widget_ActionBar_Title_Inverse = 2131361871; - - // aapt resource value: 0x7f0a0050 - public const int Base_TextAppearance_AppCompat_Widget_ActionMode_Subtitle = 2131361872; - - // aapt resource value: 0x7f0a0051 - public const int Base_TextAppearance_AppCompat_Widget_ActionMode_Title = 2131361873; - - // aapt resource value: 0x7f0a0052 - public const int Base_TextAppearance_AppCompat_Widget_Button = 2131361874; - - // aapt resource value: 0x7f0a008d - public const int Base_TextAppearance_AppCompat_Widget_Button_Borderless_Colored = 2131361933; - - // aapt resource value: 0x7f0a008e - public const int Base_TextAppearance_AppCompat_Widget_Button_Colored = 2131361934; - - // aapt resource value: 0x7f0a0087 - public const int Base_TextAppearance_AppCompat_Widget_Button_Inverse = 2131361927; - - // aapt resource value: 0x7f0a00a3 - public const int Base_TextAppearance_AppCompat_Widget_DropDownItem = 2131361955; - - // aapt resource value: 0x7f0a0053 - public const int Base_TextAppearance_AppCompat_Widget_PopupMenu_Header = 2131361875; - - // aapt resource value: 0x7f0a0054 - public const int Base_TextAppearance_AppCompat_Widget_PopupMenu_Large = 2131361876; - - // aapt resource value: 0x7f0a0055 - public const int Base_TextAppearance_AppCompat_Widget_PopupMenu_Small = 2131361877; - - // aapt resource value: 0x7f0a0056 - public const int Base_TextAppearance_AppCompat_Widget_Switch = 2131361878; - - // aapt resource value: 0x7f0a0057 - public const int Base_TextAppearance_AppCompat_Widget_TextView_SpinnerItem = 2131361879; - - // aapt resource value: 0x7f0a00a4 - public const int Base_TextAppearance_Widget_AppCompat_ExpandedMenu_Item = 2131361956; - - // aapt resource value: 0x7f0a0058 - public const int Base_TextAppearance_Widget_AppCompat_Toolbar_Subtitle = 2131361880; - - // aapt resource value: 0x7f0a0059 - public const int Base_TextAppearance_Widget_AppCompat_Toolbar_Title = 2131361881; - - // aapt resource value: 0x7f0a005a - public const int Base_Theme_AppCompat = 2131361882; - - // aapt resource value: 0x7f0a00a5 - public const int Base_Theme_AppCompat_CompactMenu = 2131361957; - - // aapt resource value: 0x7f0a0011 - public const int Base_Theme_AppCompat_Dialog = 2131361809; - - // aapt resource value: 0x7f0a0012 - public const int Base_Theme_AppCompat_Dialog_Alert = 2131361810; - - // aapt resource value: 0x7f0a00a6 - public const int Base_Theme_AppCompat_Dialog_FixedSize = 2131361958; - - // aapt resource value: 0x7f0a0013 - public const int Base_Theme_AppCompat_Dialog_MinWidth = 2131361811; - - // aapt resource value: 0x7f0a0001 - public const int Base_Theme_AppCompat_DialogWhenLarge = 2131361793; - - // aapt resource value: 0x7f0a005b - public const int Base_Theme_AppCompat_Light = 2131361883; - - // aapt resource value: 0x7f0a00a7 - public const int Base_Theme_AppCompat_Light_DarkActionBar = 2131361959; - - // aapt resource value: 0x7f0a0014 - public const int Base_Theme_AppCompat_Light_Dialog = 2131361812; - - // aapt resource value: 0x7f0a0015 - public const int Base_Theme_AppCompat_Light_Dialog_Alert = 2131361813; - - // aapt resource value: 0x7f0a00a8 - public const int Base_Theme_AppCompat_Light_Dialog_FixedSize = 2131361960; - - // aapt resource value: 0x7f0a0016 - public const int Base_Theme_AppCompat_Light_Dialog_MinWidth = 2131361814; - - // aapt resource value: 0x7f0a0002 - public const int Base_Theme_AppCompat_Light_DialogWhenLarge = 2131361794; - - // aapt resource value: 0x7f0a00a9 - public const int Base_ThemeOverlay_AppCompat = 2131361961; - - // aapt resource value: 0x7f0a00aa - public const int Base_ThemeOverlay_AppCompat_ActionBar = 2131361962; - - // aapt resource value: 0x7f0a00ab - public const int Base_ThemeOverlay_AppCompat_Dark = 2131361963; - - // aapt resource value: 0x7f0a00ac - public const int Base_ThemeOverlay_AppCompat_Dark_ActionBar = 2131361964; - - // aapt resource value: 0x7f0a0017 - public const int Base_ThemeOverlay_AppCompat_Dialog = 2131361815; - - // aapt resource value: 0x7f0a0018 - public const int Base_ThemeOverlay_AppCompat_Dialog_Alert = 2131361816; - - // aapt resource value: 0x7f0a00ad - public const int Base_ThemeOverlay_AppCompat_Light = 2131361965; - - // aapt resource value: 0x7f0a0019 - public const int Base_V11_Theme_AppCompat_Dialog = 2131361817; - - // aapt resource value: 0x7f0a001a - public const int Base_V11_Theme_AppCompat_Light_Dialog = 2131361818; - - // aapt resource value: 0x7f0a001b - public const int Base_V11_ThemeOverlay_AppCompat_Dialog = 2131361819; - - // aapt resource value: 0x7f0a0023 - public const int Base_V12_Widget_AppCompat_AutoCompleteTextView = 2131361827; - - // aapt resource value: 0x7f0a0024 - public const int Base_V12_Widget_AppCompat_EditText = 2131361828; - - // aapt resource value: 0x7f0a0160 - public const int Base_V14_Widget_Design_AppBarLayout = 2131362144; - - // aapt resource value: 0x7f0a005c - public const int Base_V21_Theme_AppCompat = 2131361884; - - // aapt resource value: 0x7f0a005d - public const int Base_V21_Theme_AppCompat_Dialog = 2131361885; - - // aapt resource value: 0x7f0a005e - public const int Base_V21_Theme_AppCompat_Light = 2131361886; - - // aapt resource value: 0x7f0a005f - public const int Base_V21_Theme_AppCompat_Light_Dialog = 2131361887; - - // aapt resource value: 0x7f0a0060 - public const int Base_V21_ThemeOverlay_AppCompat_Dialog = 2131361888; - - // aapt resource value: 0x7f0a015c - public const int Base_V21_Widget_Design_AppBarLayout = 2131362140; - - // aapt resource value: 0x7f0a0084 - public const int Base_V22_Theme_AppCompat = 2131361924; - - // aapt resource value: 0x7f0a0085 - public const int Base_V22_Theme_AppCompat_Light = 2131361925; - - // aapt resource value: 0x7f0a0088 - public const int Base_V23_Theme_AppCompat = 2131361928; - - // aapt resource value: 0x7f0a0089 - public const int Base_V23_Theme_AppCompat_Light = 2131361929; - - // aapt resource value: 0x7f0a0091 - public const int Base_V26_Theme_AppCompat = 2131361937; - - // aapt resource value: 0x7f0a0092 - public const int Base_V26_Theme_AppCompat_Light = 2131361938; - - // aapt resource value: 0x7f0a0093 - public const int Base_V26_Widget_AppCompat_Toolbar = 2131361939; - - // aapt resource value: 0x7f0a015e - public const int Base_V26_Widget_Design_AppBarLayout = 2131362142; - - // aapt resource value: 0x7f0a00ae - public const int Base_V7_Theme_AppCompat = 2131361966; - - // aapt resource value: 0x7f0a00af - public const int Base_V7_Theme_AppCompat_Dialog = 2131361967; - - // aapt resource value: 0x7f0a00b0 - public const int Base_V7_Theme_AppCompat_Light = 2131361968; - - // aapt resource value: 0x7f0a00b1 - public const int Base_V7_Theme_AppCompat_Light_Dialog = 2131361969; - - // aapt resource value: 0x7f0a00b2 - public const int Base_V7_ThemeOverlay_AppCompat_Dialog = 2131361970; - - // aapt resource value: 0x7f0a00b3 - public const int Base_V7_Widget_AppCompat_AutoCompleteTextView = 2131361971; - - // aapt resource value: 0x7f0a00b4 - public const int Base_V7_Widget_AppCompat_EditText = 2131361972; - - // aapt resource value: 0x7f0a00b5 - public const int Base_V7_Widget_AppCompat_Toolbar = 2131361973; - - // aapt resource value: 0x7f0a00b6 - public const int Base_Widget_AppCompat_ActionBar = 2131361974; - - // aapt resource value: 0x7f0a00b7 - public const int Base_Widget_AppCompat_ActionBar_Solid = 2131361975; - - // aapt resource value: 0x7f0a00b8 - public const int Base_Widget_AppCompat_ActionBar_TabBar = 2131361976; - - // aapt resource value: 0x7f0a0061 - public const int Base_Widget_AppCompat_ActionBar_TabText = 2131361889; - - // aapt resource value: 0x7f0a0062 - public const int Base_Widget_AppCompat_ActionBar_TabView = 2131361890; - - // aapt resource value: 0x7f0a0063 - public const int Base_Widget_AppCompat_ActionButton = 2131361891; - - // aapt resource value: 0x7f0a0064 - public const int Base_Widget_AppCompat_ActionButton_CloseMode = 2131361892; - - // aapt resource value: 0x7f0a0065 - public const int Base_Widget_AppCompat_ActionButton_Overflow = 2131361893; - - // aapt resource value: 0x7f0a00b9 - public const int Base_Widget_AppCompat_ActionMode = 2131361977; - - // aapt resource value: 0x7f0a00ba - public const int Base_Widget_AppCompat_ActivityChooserView = 2131361978; - - // aapt resource value: 0x7f0a0025 - public const int Base_Widget_AppCompat_AutoCompleteTextView = 2131361829; - - // aapt resource value: 0x7f0a0066 - public const int Base_Widget_AppCompat_Button = 2131361894; - - // aapt resource value: 0x7f0a0067 - public const int Base_Widget_AppCompat_Button_Borderless = 2131361895; - - // aapt resource value: 0x7f0a0068 - public const int Base_Widget_AppCompat_Button_Borderless_Colored = 2131361896; - - // aapt resource value: 0x7f0a00bb - public const int Base_Widget_AppCompat_Button_ButtonBar_AlertDialog = 2131361979; - - // aapt resource value: 0x7f0a008a - public const int Base_Widget_AppCompat_Button_Colored = 2131361930; - - // aapt resource value: 0x7f0a0069 - public const int Base_Widget_AppCompat_Button_Small = 2131361897; - - // aapt resource value: 0x7f0a006a - public const int Base_Widget_AppCompat_ButtonBar = 2131361898; - - // aapt resource value: 0x7f0a00bc - public const int Base_Widget_AppCompat_ButtonBar_AlertDialog = 2131361980; - - // aapt resource value: 0x7f0a006b - public const int Base_Widget_AppCompat_CompoundButton_CheckBox = 2131361899; - - // aapt resource value: 0x7f0a006c - public const int Base_Widget_AppCompat_CompoundButton_RadioButton = 2131361900; - - // aapt resource value: 0x7f0a00bd - public const int Base_Widget_AppCompat_CompoundButton_Switch = 2131361981; - - // aapt resource value: 0x7f0a0000 - public const int Base_Widget_AppCompat_DrawerArrowToggle = 2131361792; - - // aapt resource value: 0x7f0a00be - public const int Base_Widget_AppCompat_DrawerArrowToggle_Common = 2131361982; - - // aapt resource value: 0x7f0a006d - public const int Base_Widget_AppCompat_DropDownItem_Spinner = 2131361901; - - // aapt resource value: 0x7f0a0026 - public const int Base_Widget_AppCompat_EditText = 2131361830; - - // aapt resource value: 0x7f0a006e - public const int Base_Widget_AppCompat_ImageButton = 2131361902; - - // aapt resource value: 0x7f0a00bf - public const int Base_Widget_AppCompat_Light_ActionBar = 2131361983; - - // aapt resource value: 0x7f0a00c0 - public const int Base_Widget_AppCompat_Light_ActionBar_Solid = 2131361984; - - // aapt resource value: 0x7f0a00c1 - public const int Base_Widget_AppCompat_Light_ActionBar_TabBar = 2131361985; - - // aapt resource value: 0x7f0a006f - public const int Base_Widget_AppCompat_Light_ActionBar_TabText = 2131361903; - - // aapt resource value: 0x7f0a0070 - public const int Base_Widget_AppCompat_Light_ActionBar_TabText_Inverse = 2131361904; - - // aapt resource value: 0x7f0a0071 - public const int Base_Widget_AppCompat_Light_ActionBar_TabView = 2131361905; - - // aapt resource value: 0x7f0a0072 - public const int Base_Widget_AppCompat_Light_PopupMenu = 2131361906; - - // aapt resource value: 0x7f0a0073 - public const int Base_Widget_AppCompat_Light_PopupMenu_Overflow = 2131361907; - - // aapt resource value: 0x7f0a00c2 - public const int Base_Widget_AppCompat_ListMenuView = 2131361986; - - // aapt resource value: 0x7f0a0074 - public const int Base_Widget_AppCompat_ListPopupWindow = 2131361908; - - // aapt resource value: 0x7f0a0075 - public const int Base_Widget_AppCompat_ListView = 2131361909; - - // aapt resource value: 0x7f0a0076 - public const int Base_Widget_AppCompat_ListView_DropDown = 2131361910; - - // aapt resource value: 0x7f0a0077 - public const int Base_Widget_AppCompat_ListView_Menu = 2131361911; - - // aapt resource value: 0x7f0a0078 - public const int Base_Widget_AppCompat_PopupMenu = 2131361912; - - // aapt resource value: 0x7f0a0079 - public const int Base_Widget_AppCompat_PopupMenu_Overflow = 2131361913; - - // aapt resource value: 0x7f0a00c3 - public const int Base_Widget_AppCompat_PopupWindow = 2131361987; - - // aapt resource value: 0x7f0a001c - public const int Base_Widget_AppCompat_ProgressBar = 2131361820; - - // aapt resource value: 0x7f0a001d - public const int Base_Widget_AppCompat_ProgressBar_Horizontal = 2131361821; - - // aapt resource value: 0x7f0a007a - public const int Base_Widget_AppCompat_RatingBar = 2131361914; - - // aapt resource value: 0x7f0a008b - public const int Base_Widget_AppCompat_RatingBar_Indicator = 2131361931; - - // aapt resource value: 0x7f0a008c - public const int Base_Widget_AppCompat_RatingBar_Small = 2131361932; - - // aapt resource value: 0x7f0a00c4 - public const int Base_Widget_AppCompat_SearchView = 2131361988; - - // aapt resource value: 0x7f0a00c5 - public const int Base_Widget_AppCompat_SearchView_ActionBar = 2131361989; - - // aapt resource value: 0x7f0a007b - public const int Base_Widget_AppCompat_SeekBar = 2131361915; - - // aapt resource value: 0x7f0a00c6 - public const int Base_Widget_AppCompat_SeekBar_Discrete = 2131361990; - - // aapt resource value: 0x7f0a007c - public const int Base_Widget_AppCompat_Spinner = 2131361916; - - // aapt resource value: 0x7f0a0003 - public const int Base_Widget_AppCompat_Spinner_Underlined = 2131361795; - - // aapt resource value: 0x7f0a007d - public const int Base_Widget_AppCompat_TextView_SpinnerItem = 2131361917; - - // aapt resource value: 0x7f0a0094 - public const int Base_Widget_AppCompat_Toolbar = 2131361940; - - // aapt resource value: 0x7f0a007e - public const int Base_Widget_AppCompat_Toolbar_Button_Navigation = 2131361918; - - // aapt resource value: 0x7f0a015d - public const int Base_Widget_Design_AppBarLayout = 2131362141; - - // aapt resource value: 0x7f0a0161 - public const int Base_Widget_Design_TabLayout = 2131362145; - - // aapt resource value: 0x7f0a001e - public const int Platform_AppCompat = 2131361822; - - // aapt resource value: 0x7f0a001f - public const int Platform_AppCompat_Light = 2131361823; - - // aapt resource value: 0x7f0a007f - public const int Platform_ThemeOverlay_AppCompat = 2131361919; - - // aapt resource value: 0x7f0a0080 - public const int Platform_ThemeOverlay_AppCompat_Dark = 2131361920; - - // aapt resource value: 0x7f0a0081 - public const int Platform_ThemeOverlay_AppCompat_Light = 2131361921; - - // aapt resource value: 0x7f0a0020 - public const int Platform_V11_AppCompat = 2131361824; - - // aapt resource value: 0x7f0a0021 - public const int Platform_V11_AppCompat_Light = 2131361825; - - // aapt resource value: 0x7f0a0028 - public const int Platform_V14_AppCompat = 2131361832; - - // aapt resource value: 0x7f0a0029 - public const int Platform_V14_AppCompat_Light = 2131361833; - - // aapt resource value: 0x7f0a0082 - public const int Platform_V21_AppCompat = 2131361922; - - // aapt resource value: 0x7f0a0083 - public const int Platform_V21_AppCompat_Light = 2131361923; - - // aapt resource value: 0x7f0a008f - public const int Platform_V25_AppCompat = 2131361935; - - // aapt resource value: 0x7f0a0090 - public const int Platform_V25_AppCompat_Light = 2131361936; - - // aapt resource value: 0x7f0a0022 - public const int Platform_Widget_AppCompat_Spinner = 2131361826; - - // aapt resource value: 0x7f0a002b - public const int RtlOverlay_DialogWindowTitle_AppCompat = 2131361835; - - // aapt resource value: 0x7f0a002c - public const int RtlOverlay_Widget_AppCompat_ActionBar_TitleItem = 2131361836; - - // aapt resource value: 0x7f0a002d - public const int RtlOverlay_Widget_AppCompat_DialogTitle_Icon = 2131361837; - - // aapt resource value: 0x7f0a002e - public const int RtlOverlay_Widget_AppCompat_PopupMenuItem = 2131361838; - - // aapt resource value: 0x7f0a002f - public const int RtlOverlay_Widget_AppCompat_PopupMenuItem_InternalGroup = 2131361839; - - // aapt resource value: 0x7f0a0030 - public const int RtlOverlay_Widget_AppCompat_PopupMenuItem_Text = 2131361840; - - // aapt resource value: 0x7f0a0031 - public const int RtlOverlay_Widget_AppCompat_Search_DropDown = 2131361841; - - // aapt resource value: 0x7f0a0032 - public const int RtlOverlay_Widget_AppCompat_Search_DropDown_Icon1 = 2131361842; - - // aapt resource value: 0x7f0a0033 - public const int RtlOverlay_Widget_AppCompat_Search_DropDown_Icon2 = 2131361843; - - // aapt resource value: 0x7f0a0034 - public const int RtlOverlay_Widget_AppCompat_Search_DropDown_Query = 2131361844; - - // aapt resource value: 0x7f0a0035 - public const int RtlOverlay_Widget_AppCompat_Search_DropDown_Text = 2131361845; - - // aapt resource value: 0x7f0a0036 - public const int RtlOverlay_Widget_AppCompat_SearchView_MagIcon = 2131361846; - - // aapt resource value: 0x7f0a0037 - public const int RtlUnderlay_Widget_AppCompat_ActionButton = 2131361847; - - // aapt resource value: 0x7f0a0038 - public const int RtlUnderlay_Widget_AppCompat_ActionButton_Overflow = 2131361848; - - // aapt resource value: 0x7f0a00c7 - public const int TextAppearance_AppCompat = 2131361991; - - // aapt resource value: 0x7f0a00c8 - public const int TextAppearance_AppCompat_Body1 = 2131361992; - - // aapt resource value: 0x7f0a00c9 - public const int TextAppearance_AppCompat_Body2 = 2131361993; - - // aapt resource value: 0x7f0a00ca - public const int TextAppearance_AppCompat_Button = 2131361994; - - // aapt resource value: 0x7f0a00cb - public const int TextAppearance_AppCompat_Caption = 2131361995; - - // aapt resource value: 0x7f0a00cc - public const int TextAppearance_AppCompat_Display1 = 2131361996; - - // aapt resource value: 0x7f0a00cd - public const int TextAppearance_AppCompat_Display2 = 2131361997; - - // aapt resource value: 0x7f0a00ce - public const int TextAppearance_AppCompat_Display3 = 2131361998; - - // aapt resource value: 0x7f0a00cf - public const int TextAppearance_AppCompat_Display4 = 2131361999; - - // aapt resource value: 0x7f0a00d0 - public const int TextAppearance_AppCompat_Headline = 2131362000; - - // aapt resource value: 0x7f0a00d1 - public const int TextAppearance_AppCompat_Inverse = 2131362001; - - // aapt resource value: 0x7f0a00d2 - public const int TextAppearance_AppCompat_Large = 2131362002; - - // aapt resource value: 0x7f0a00d3 - public const int TextAppearance_AppCompat_Large_Inverse = 2131362003; - - // aapt resource value: 0x7f0a00d4 - public const int TextAppearance_AppCompat_Light_SearchResult_Subtitle = 2131362004; - - // aapt resource value: 0x7f0a00d5 - public const int TextAppearance_AppCompat_Light_SearchResult_Title = 2131362005; - - // aapt resource value: 0x7f0a00d6 - public const int TextAppearance_AppCompat_Light_Widget_PopupMenu_Large = 2131362006; - - // aapt resource value: 0x7f0a00d7 - public const int TextAppearance_AppCompat_Light_Widget_PopupMenu_Small = 2131362007; - - // aapt resource value: 0x7f0a00d8 - public const int TextAppearance_AppCompat_Medium = 2131362008; - - // aapt resource value: 0x7f0a00d9 - public const int TextAppearance_AppCompat_Medium_Inverse = 2131362009; - - // aapt resource value: 0x7f0a00da - public const int TextAppearance_AppCompat_Menu = 2131362010; - - // aapt resource value: 0x7f0a00db - public const int TextAppearance_AppCompat_SearchResult_Subtitle = 2131362011; - - // aapt resource value: 0x7f0a00dc - public const int TextAppearance_AppCompat_SearchResult_Title = 2131362012; - - // aapt resource value: 0x7f0a00dd - public const int TextAppearance_AppCompat_Small = 2131362013; - - // aapt resource value: 0x7f0a00de - public const int TextAppearance_AppCompat_Small_Inverse = 2131362014; - - // aapt resource value: 0x7f0a00df - public const int TextAppearance_AppCompat_Subhead = 2131362015; - - // aapt resource value: 0x7f0a00e0 - public const int TextAppearance_AppCompat_Subhead_Inverse = 2131362016; - - // aapt resource value: 0x7f0a00e1 - public const int TextAppearance_AppCompat_Title = 2131362017; - - // aapt resource value: 0x7f0a00e2 - public const int TextAppearance_AppCompat_Title_Inverse = 2131362018; - - // aapt resource value: 0x7f0a002a - public const int TextAppearance_AppCompat_Tooltip = 2131361834; - - // aapt resource value: 0x7f0a00e3 - public const int TextAppearance_AppCompat_Widget_ActionBar_Menu = 2131362019; - - // aapt resource value: 0x7f0a00e4 - public const int TextAppearance_AppCompat_Widget_ActionBar_Subtitle = 2131362020; - - // aapt resource value: 0x7f0a00e5 - public const int TextAppearance_AppCompat_Widget_ActionBar_Subtitle_Inverse = 2131362021; - - // aapt resource value: 0x7f0a00e6 - public const int TextAppearance_AppCompat_Widget_ActionBar_Title = 2131362022; - - // aapt resource value: 0x7f0a00e7 - public const int TextAppearance_AppCompat_Widget_ActionBar_Title_Inverse = 2131362023; - - // aapt resource value: 0x7f0a00e8 - public const int TextAppearance_AppCompat_Widget_ActionMode_Subtitle = 2131362024; - - // aapt resource value: 0x7f0a00e9 - public const int TextAppearance_AppCompat_Widget_ActionMode_Subtitle_Inverse = 2131362025; - - // aapt resource value: 0x7f0a00ea - public const int TextAppearance_AppCompat_Widget_ActionMode_Title = 2131362026; - - // aapt resource value: 0x7f0a00eb - public const int TextAppearance_AppCompat_Widget_ActionMode_Title_Inverse = 2131362027; - - // aapt resource value: 0x7f0a00ec - public const int TextAppearance_AppCompat_Widget_Button = 2131362028; - - // aapt resource value: 0x7f0a00ed - public const int TextAppearance_AppCompat_Widget_Button_Borderless_Colored = 2131362029; - - // aapt resource value: 0x7f0a00ee - public const int TextAppearance_AppCompat_Widget_Button_Colored = 2131362030; - - // aapt resource value: 0x7f0a00ef - public const int TextAppearance_AppCompat_Widget_Button_Inverse = 2131362031; - - // aapt resource value: 0x7f0a00f0 - public const int TextAppearance_AppCompat_Widget_DropDownItem = 2131362032; - - // aapt resource value: 0x7f0a00f1 - public const int TextAppearance_AppCompat_Widget_PopupMenu_Header = 2131362033; - - // aapt resource value: 0x7f0a00f2 - public const int TextAppearance_AppCompat_Widget_PopupMenu_Large = 2131362034; - - // aapt resource value: 0x7f0a00f3 - public const int TextAppearance_AppCompat_Widget_PopupMenu_Small = 2131362035; - - // aapt resource value: 0x7f0a00f4 - public const int TextAppearance_AppCompat_Widget_Switch = 2131362036; - - // aapt resource value: 0x7f0a00f5 - public const int TextAppearance_AppCompat_Widget_TextView_SpinnerItem = 2131362037; - - // aapt resource value: 0x7f0a0179 - public const int TextAppearance_Compat_Notification = 2131362169; - - // aapt resource value: 0x7f0a017a - public const int TextAppearance_Compat_Notification_Info = 2131362170; - - // aapt resource value: 0x7f0a0156 - public const int TextAppearance_Compat_Notification_Info_Media = 2131362134; - - // aapt resource value: 0x7f0a017f - public const int TextAppearance_Compat_Notification_Line2 = 2131362175; - - // aapt resource value: 0x7f0a015a - public const int TextAppearance_Compat_Notification_Line2_Media = 2131362138; - - // aapt resource value: 0x7f0a0157 - public const int TextAppearance_Compat_Notification_Media = 2131362135; - - // aapt resource value: 0x7f0a017b - public const int TextAppearance_Compat_Notification_Time = 2131362171; - - // aapt resource value: 0x7f0a0158 - public const int TextAppearance_Compat_Notification_Time_Media = 2131362136; - - // aapt resource value: 0x7f0a017c - public const int TextAppearance_Compat_Notification_Title = 2131362172; - - // aapt resource value: 0x7f0a0159 - public const int TextAppearance_Compat_Notification_Title_Media = 2131362137; - - // aapt resource value: 0x7f0a0162 - public const int TextAppearance_Design_CollapsingToolbar_Expanded = 2131362146; - - // aapt resource value: 0x7f0a0163 - public const int TextAppearance_Design_Counter = 2131362147; - - // aapt resource value: 0x7f0a0164 - public const int TextAppearance_Design_Counter_Overflow = 2131362148; - - // aapt resource value: 0x7f0a0165 - public const int TextAppearance_Design_Error = 2131362149; - - // aapt resource value: 0x7f0a0166 - public const int TextAppearance_Design_Hint = 2131362150; - - // aapt resource value: 0x7f0a0167 - public const int TextAppearance_Design_Snackbar_Message = 2131362151; - - // aapt resource value: 0x7f0a0168 - public const int TextAppearance_Design_Tab = 2131362152; - - // aapt resource value: 0x7f0a00f6 - public const int TextAppearance_Widget_AppCompat_ExpandedMenu_Item = 2131362038; - - // aapt resource value: 0x7f0a00f7 - public const int TextAppearance_Widget_AppCompat_Toolbar_Subtitle = 2131362039; - - // aapt resource value: 0x7f0a00f8 - public const int TextAppearance_Widget_AppCompat_Toolbar_Title = 2131362040; - - // aapt resource value: 0x7f0a00f9 - public const int Theme_AppCompat = 2131362041; - - // aapt resource value: 0x7f0a00fa - public const int Theme_AppCompat_CompactMenu = 2131362042; - - // aapt resource value: 0x7f0a0004 - public const int Theme_AppCompat_DayNight = 2131361796; - - // aapt resource value: 0x7f0a0005 - public const int Theme_AppCompat_DayNight_DarkActionBar = 2131361797; - - // aapt resource value: 0x7f0a0006 - public const int Theme_AppCompat_DayNight_Dialog = 2131361798; - - // aapt resource value: 0x7f0a0007 - public const int Theme_AppCompat_DayNight_Dialog_Alert = 2131361799; - - // aapt resource value: 0x7f0a0008 - public const int Theme_AppCompat_DayNight_Dialog_MinWidth = 2131361800; - - // aapt resource value: 0x7f0a0009 - public const int Theme_AppCompat_DayNight_DialogWhenLarge = 2131361801; - - // aapt resource value: 0x7f0a000a - public const int Theme_AppCompat_DayNight_NoActionBar = 2131361802; - - // aapt resource value: 0x7f0a00fb - public const int Theme_AppCompat_Dialog = 2131362043; - - // aapt resource value: 0x7f0a00fc - public const int Theme_AppCompat_Dialog_Alert = 2131362044; - - // aapt resource value: 0x7f0a00fd - public const int Theme_AppCompat_Dialog_MinWidth = 2131362045; - - // aapt resource value: 0x7f0a00fe - public const int Theme_AppCompat_DialogWhenLarge = 2131362046; - - // aapt resource value: 0x7f0a00ff - public const int Theme_AppCompat_Light = 2131362047; - - // aapt resource value: 0x7f0a0100 - public const int Theme_AppCompat_Light_DarkActionBar = 2131362048; - - // aapt resource value: 0x7f0a0101 - public const int Theme_AppCompat_Light_Dialog = 2131362049; - - // aapt resource value: 0x7f0a0102 - public const int Theme_AppCompat_Light_Dialog_Alert = 2131362050; - - // aapt resource value: 0x7f0a0103 - public const int Theme_AppCompat_Light_Dialog_MinWidth = 2131362051; - - // aapt resource value: 0x7f0a0104 - public const int Theme_AppCompat_Light_DialogWhenLarge = 2131362052; - - // aapt resource value: 0x7f0a0105 - public const int Theme_AppCompat_Light_NoActionBar = 2131362053; - - // aapt resource value: 0x7f0a0106 - public const int Theme_AppCompat_NoActionBar = 2131362054; - - // aapt resource value: 0x7f0a0169 - public const int Theme_Design = 2131362153; - - // aapt resource value: 0x7f0a016a - public const int Theme_Design_BottomSheetDialog = 2131362154; - - // aapt resource value: 0x7f0a016b - public const int Theme_Design_Light = 2131362155; - - // aapt resource value: 0x7f0a016c - public const int Theme_Design_Light_BottomSheetDialog = 2131362156; - - // aapt resource value: 0x7f0a016d - public const int Theme_Design_Light_NoActionBar = 2131362157; - - // aapt resource value: 0x7f0a016e - public const int Theme_Design_NoActionBar = 2131362158; - - // aapt resource value: 0x7f0a0107 - public const int ThemeOverlay_AppCompat = 2131362055; - - // aapt resource value: 0x7f0a0108 - public const int ThemeOverlay_AppCompat_ActionBar = 2131362056; - - // aapt resource value: 0x7f0a0109 - public const int ThemeOverlay_AppCompat_Dark = 2131362057; - - // aapt resource value: 0x7f0a010a - public const int ThemeOverlay_AppCompat_Dark_ActionBar = 2131362058; - - // aapt resource value: 0x7f0a010b - public const int ThemeOverlay_AppCompat_Dialog = 2131362059; - - // aapt resource value: 0x7f0a010c - public const int ThemeOverlay_AppCompat_Dialog_Alert = 2131362060; - - // aapt resource value: 0x7f0a010d - public const int ThemeOverlay_AppCompat_Light = 2131362061; - - // aapt resource value: 0x7f0a010e - public const int Widget_AppCompat_ActionBar = 2131362062; - - // aapt resource value: 0x7f0a010f - public const int Widget_AppCompat_ActionBar_Solid = 2131362063; - - // aapt resource value: 0x7f0a0110 - public const int Widget_AppCompat_ActionBar_TabBar = 2131362064; - - // aapt resource value: 0x7f0a0111 - public const int Widget_AppCompat_ActionBar_TabText = 2131362065; - - // aapt resource value: 0x7f0a0112 - public const int Widget_AppCompat_ActionBar_TabView = 2131362066; - - // aapt resource value: 0x7f0a0113 - public const int Widget_AppCompat_ActionButton = 2131362067; - - // aapt resource value: 0x7f0a0114 - public const int Widget_AppCompat_ActionButton_CloseMode = 2131362068; - - // aapt resource value: 0x7f0a0115 - public const int Widget_AppCompat_ActionButton_Overflow = 2131362069; - - // aapt resource value: 0x7f0a0116 - public const int Widget_AppCompat_ActionMode = 2131362070; - - // aapt resource value: 0x7f0a0117 - public const int Widget_AppCompat_ActivityChooserView = 2131362071; - - // aapt resource value: 0x7f0a0118 - public const int Widget_AppCompat_AutoCompleteTextView = 2131362072; - - // aapt resource value: 0x7f0a0119 - public const int Widget_AppCompat_Button = 2131362073; - - // aapt resource value: 0x7f0a011a - public const int Widget_AppCompat_Button_Borderless = 2131362074; - - // aapt resource value: 0x7f0a011b - public const int Widget_AppCompat_Button_Borderless_Colored = 2131362075; - - // aapt resource value: 0x7f0a011c - public const int Widget_AppCompat_Button_ButtonBar_AlertDialog = 2131362076; - - // aapt resource value: 0x7f0a011d - public const int Widget_AppCompat_Button_Colored = 2131362077; - - // aapt resource value: 0x7f0a011e - public const int Widget_AppCompat_Button_Small = 2131362078; - - // aapt resource value: 0x7f0a011f - public const int Widget_AppCompat_ButtonBar = 2131362079; - - // aapt resource value: 0x7f0a0120 - public const int Widget_AppCompat_ButtonBar_AlertDialog = 2131362080; - - // aapt resource value: 0x7f0a0121 - public const int Widget_AppCompat_CompoundButton_CheckBox = 2131362081; - - // aapt resource value: 0x7f0a0122 - public const int Widget_AppCompat_CompoundButton_RadioButton = 2131362082; - - // aapt resource value: 0x7f0a0123 - public const int Widget_AppCompat_CompoundButton_Switch = 2131362083; - - // aapt resource value: 0x7f0a0124 - public const int Widget_AppCompat_DrawerArrowToggle = 2131362084; - - // aapt resource value: 0x7f0a0125 - public const int Widget_AppCompat_DropDownItem_Spinner = 2131362085; - - // aapt resource value: 0x7f0a0126 - public const int Widget_AppCompat_EditText = 2131362086; - - // aapt resource value: 0x7f0a0127 - public const int Widget_AppCompat_ImageButton = 2131362087; - - // aapt resource value: 0x7f0a0128 - public const int Widget_AppCompat_Light_ActionBar = 2131362088; - - // aapt resource value: 0x7f0a0129 - public const int Widget_AppCompat_Light_ActionBar_Solid = 2131362089; - - // aapt resource value: 0x7f0a012a - public const int Widget_AppCompat_Light_ActionBar_Solid_Inverse = 2131362090; - - // aapt resource value: 0x7f0a012b - public const int Widget_AppCompat_Light_ActionBar_TabBar = 2131362091; - - // aapt resource value: 0x7f0a012c - public const int Widget_AppCompat_Light_ActionBar_TabBar_Inverse = 2131362092; - - // aapt resource value: 0x7f0a012d - public const int Widget_AppCompat_Light_ActionBar_TabText = 2131362093; - - // aapt resource value: 0x7f0a012e - public const int Widget_AppCompat_Light_ActionBar_TabText_Inverse = 2131362094; - - // aapt resource value: 0x7f0a012f - public const int Widget_AppCompat_Light_ActionBar_TabView = 2131362095; - - // aapt resource value: 0x7f0a0130 - public const int Widget_AppCompat_Light_ActionBar_TabView_Inverse = 2131362096; - - // aapt resource value: 0x7f0a0131 - public const int Widget_AppCompat_Light_ActionButton = 2131362097; - - // aapt resource value: 0x7f0a0132 - public const int Widget_AppCompat_Light_ActionButton_CloseMode = 2131362098; - - // aapt resource value: 0x7f0a0133 - public const int Widget_AppCompat_Light_ActionButton_Overflow = 2131362099; - - // aapt resource value: 0x7f0a0134 - public const int Widget_AppCompat_Light_ActionMode_Inverse = 2131362100; - - // aapt resource value: 0x7f0a0135 - public const int Widget_AppCompat_Light_ActivityChooserView = 2131362101; - - // aapt resource value: 0x7f0a0136 - public const int Widget_AppCompat_Light_AutoCompleteTextView = 2131362102; - - // aapt resource value: 0x7f0a0137 - public const int Widget_AppCompat_Light_DropDownItem_Spinner = 2131362103; - - // aapt resource value: 0x7f0a0138 - public const int Widget_AppCompat_Light_ListPopupWindow = 2131362104; - - // aapt resource value: 0x7f0a0139 - public const int Widget_AppCompat_Light_ListView_DropDown = 2131362105; - - // aapt resource value: 0x7f0a013a - public const int Widget_AppCompat_Light_PopupMenu = 2131362106; - - // aapt resource value: 0x7f0a013b - public const int Widget_AppCompat_Light_PopupMenu_Overflow = 2131362107; - - // aapt resource value: 0x7f0a013c - public const int Widget_AppCompat_Light_SearchView = 2131362108; - - // aapt resource value: 0x7f0a013d - public const int Widget_AppCompat_Light_Spinner_DropDown_ActionBar = 2131362109; - - // aapt resource value: 0x7f0a013e - public const int Widget_AppCompat_ListMenuView = 2131362110; - - // aapt resource value: 0x7f0a013f - public const int Widget_AppCompat_ListPopupWindow = 2131362111; - - // aapt resource value: 0x7f0a0140 - public const int Widget_AppCompat_ListView = 2131362112; - - // aapt resource value: 0x7f0a0141 - public const int Widget_AppCompat_ListView_DropDown = 2131362113; - - // aapt resource value: 0x7f0a0142 - public const int Widget_AppCompat_ListView_Menu = 2131362114; - - // aapt resource value: 0x7f0a0143 - public const int Widget_AppCompat_PopupMenu = 2131362115; - - // aapt resource value: 0x7f0a0144 - public const int Widget_AppCompat_PopupMenu_Overflow = 2131362116; - - // aapt resource value: 0x7f0a0145 - public const int Widget_AppCompat_PopupWindow = 2131362117; - - // aapt resource value: 0x7f0a0146 - public const int Widget_AppCompat_ProgressBar = 2131362118; - - // aapt resource value: 0x7f0a0147 - public const int Widget_AppCompat_ProgressBar_Horizontal = 2131362119; - - // aapt resource value: 0x7f0a0148 - public const int Widget_AppCompat_RatingBar = 2131362120; - - // aapt resource value: 0x7f0a0149 - public const int Widget_AppCompat_RatingBar_Indicator = 2131362121; - - // aapt resource value: 0x7f0a014a - public const int Widget_AppCompat_RatingBar_Small = 2131362122; - - // aapt resource value: 0x7f0a014b - public const int Widget_AppCompat_SearchView = 2131362123; - - // aapt resource value: 0x7f0a014c - public const int Widget_AppCompat_SearchView_ActionBar = 2131362124; - - // aapt resource value: 0x7f0a014d - public const int Widget_AppCompat_SeekBar = 2131362125; - - // aapt resource value: 0x7f0a014e - public const int Widget_AppCompat_SeekBar_Discrete = 2131362126; - - // aapt resource value: 0x7f0a014f - public const int Widget_AppCompat_Spinner = 2131362127; - - // aapt resource value: 0x7f0a0150 - public const int Widget_AppCompat_Spinner_DropDown = 2131362128; - - // aapt resource value: 0x7f0a0151 - public const int Widget_AppCompat_Spinner_DropDown_ActionBar = 2131362129; - - // aapt resource value: 0x7f0a0152 - public const int Widget_AppCompat_Spinner_Underlined = 2131362130; - - // aapt resource value: 0x7f0a0153 - public const int Widget_AppCompat_TextView_SpinnerItem = 2131362131; - - // aapt resource value: 0x7f0a0154 - public const int Widget_AppCompat_Toolbar = 2131362132; - - // aapt resource value: 0x7f0a0155 - public const int Widget_AppCompat_Toolbar_Button_Navigation = 2131362133; - - // aapt resource value: 0x7f0a017d - public const int Widget_Compat_NotificationActionContainer = 2131362173; - - // aapt resource value: 0x7f0a017e - public const int Widget_Compat_NotificationActionText = 2131362174; - - // aapt resource value: 0x7f0a016f - public const int Widget_Design_AppBarLayout = 2131362159; - - // aapt resource value: 0x7f0a0170 - public const int Widget_Design_BottomNavigationView = 2131362160; - - // aapt resource value: 0x7f0a0171 - public const int Widget_Design_BottomSheet_Modal = 2131362161; - - // aapt resource value: 0x7f0a0172 - public const int Widget_Design_CollapsingToolbar = 2131362162; - - // aapt resource value: 0x7f0a0173 - public const int Widget_Design_CoordinatorLayout = 2131362163; - - // aapt resource value: 0x7f0a0174 - public const int Widget_Design_FloatingActionButton = 2131362164; - - // aapt resource value: 0x7f0a0175 - public const int Widget_Design_NavigationView = 2131362165; - - // aapt resource value: 0x7f0a0176 - public const int Widget_Design_ScrimInsetsFrameLayout = 2131362166; - - // aapt resource value: 0x7f0a0177 - public const int Widget_Design_Snackbar = 2131362167; - - // aapt resource value: 0x7f0a015b - public const int Widget_Design_TabLayout = 2131362139; - - // aapt resource value: 0x7f0a0178 - public const int Widget_Design_TextInputLayout = 2131362168; - - static Style() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Style() - { - } - } - - public partial class Styleable - { - - public static int[] ActionBar = new int[] { - 2130771978, - 2130771980, - 2130771981, - 2130771982, - 2130771983, - 2130771984, - 2130771985, - 2130771986, - 2130771987, - 2130771988, - 2130771989, - 2130771990, - 2130771991, - 2130771992, - 2130771993, - 2130771994, - 2130771995, - 2130771996, - 2130771997, - 2130771998, - 2130771999, - 2130772000, - 2130772001, - 2130772002, - 2130772003, - 2130772004, - 2130772005, - 2130772006, - 2130772076}; - - // aapt resource value: 10 - public const int ActionBar_background = 10; - - // aapt resource value: 12 - public const int ActionBar_backgroundSplit = 12; - - // aapt resource value: 11 - public const int ActionBar_backgroundStacked = 11; - - // aapt resource value: 21 - public const int ActionBar_contentInsetEnd = 21; - - // aapt resource value: 25 - public const int ActionBar_contentInsetEndWithActions = 25; - - // aapt resource value: 22 - public const int ActionBar_contentInsetLeft = 22; - - // aapt resource value: 23 - public const int ActionBar_contentInsetRight = 23; - - // aapt resource value: 20 - public const int ActionBar_contentInsetStart = 20; - - // aapt resource value: 24 - public const int ActionBar_contentInsetStartWithNavigation = 24; - - // aapt resource value: 13 - public const int ActionBar_customNavigationLayout = 13; - - // aapt resource value: 3 - public const int ActionBar_displayOptions = 3; - - // aapt resource value: 9 - public const int ActionBar_divider = 9; - - // aapt resource value: 26 - public const int ActionBar_elevation = 26; - - // aapt resource value: 0 - public const int ActionBar_height = 0; - - // aapt resource value: 19 - public const int ActionBar_hideOnContentScroll = 19; - - // aapt resource value: 28 - public const int ActionBar_homeAsUpIndicator = 28; - - // aapt resource value: 14 - public const int ActionBar_homeLayout = 14; - - // aapt resource value: 7 - public const int ActionBar_icon = 7; - - // aapt resource value: 16 - public const int ActionBar_indeterminateProgressStyle = 16; - - // aapt resource value: 18 - public const int ActionBar_itemPadding = 18; - - // aapt resource value: 8 - public const int ActionBar_logo = 8; - - // aapt resource value: 2 - public const int ActionBar_navigationMode = 2; - - // aapt resource value: 27 - public const int ActionBar_popupTheme = 27; - - // aapt resource value: 17 - public const int ActionBar_progressBarPadding = 17; - - // aapt resource value: 15 - public const int ActionBar_progressBarStyle = 15; - - // aapt resource value: 4 - public const int ActionBar_subtitle = 4; - - // aapt resource value: 6 - public const int ActionBar_subtitleTextStyle = 6; - - // aapt resource value: 1 - public const int ActionBar_title = 1; - - // aapt resource value: 5 - public const int ActionBar_titleTextStyle = 5; - - public static int[] ActionBarLayout = new int[] { - 16842931}; - - // aapt resource value: 0 - public const int ActionBarLayout_android_layout_gravity = 0; - - public static int[] ActionMenuItemView = new int[] { - 16843071}; - - // aapt resource value: 0 - public const int ActionMenuItemView_android_minWidth = 0; - - public static int[] ActionMenuView; - - public static int[] ActionMode = new int[] { - 2130771978, - 2130771984, - 2130771985, - 2130771989, - 2130771991, - 2130772007}; - - // aapt resource value: 3 - public const int ActionMode_background = 3; - - // aapt resource value: 4 - public const int ActionMode_backgroundSplit = 4; - - // aapt resource value: 5 - public const int ActionMode_closeItemLayout = 5; - - // aapt resource value: 0 - public const int ActionMode_height = 0; - - // aapt resource value: 2 - public const int ActionMode_subtitleTextStyle = 2; - - // aapt resource value: 1 - public const int ActionMode_titleTextStyle = 1; - - public static int[] ActivityChooserView = new int[] { - 2130772008, - 2130772009}; - - // aapt resource value: 1 - public const int ActivityChooserView_expandActivityOverflowButtonDrawable = 1; - - // aapt resource value: 0 - public const int ActivityChooserView_initialActivityCount = 0; - - public static int[] AlertDialog = new int[] { - 16842994, - 2130772010, - 2130772011, - 2130772012, - 2130772013, - 2130772014, - 2130772015}; - - // aapt resource value: 0 - public const int AlertDialog_android_layout = 0; - - // aapt resource value: 1 - public const int AlertDialog_buttonPanelSideLayout = 1; - - // aapt resource value: 5 - public const int AlertDialog_listItemLayout = 5; - - // aapt resource value: 2 - public const int AlertDialog_listLayout = 2; - - // aapt resource value: 3 - public const int AlertDialog_multiChoiceItemLayout = 3; - - // aapt resource value: 6 - public const int AlertDialog_showTitle = 6; - - // aapt resource value: 4 - public const int AlertDialog_singleChoiceItemLayout = 4; - - public static int[] AppBarLayout = new int[] { - 16842964, - 16843919, - 16844096, - 2130772005, - 2130772223}; - - // aapt resource value: 0 - public const int AppBarLayout_android_background = 0; - - // aapt resource value: 2 - public const int AppBarLayout_android_keyboardNavigationCluster = 2; - - // aapt resource value: 1 - public const int AppBarLayout_android_touchscreenBlocksFocus = 1; - - // aapt resource value: 3 - public const int AppBarLayout_elevation = 3; - - // aapt resource value: 4 - public const int AppBarLayout_expanded = 4; - - public static int[] AppBarLayoutStates = new int[] { - 2130772224, - 2130772225}; - - // aapt resource value: 0 - public const int AppBarLayoutStates_state_collapsed = 0; - - // aapt resource value: 1 - public const int AppBarLayoutStates_state_collapsible = 1; - - public static int[] AppBarLayout_Layout = new int[] { - 2130772226, - 2130772227}; - - // aapt resource value: 0 - public const int AppBarLayout_Layout_layout_scrollFlags = 0; - - // aapt resource value: 1 - public const int AppBarLayout_Layout_layout_scrollInterpolator = 1; - - public static int[] AppCompatImageView = new int[] { - 16843033, - 2130772016, - 2130772017, - 2130772018}; - - // aapt resource value: 0 - public const int AppCompatImageView_android_src = 0; - - // aapt resource value: 1 - public const int AppCompatImageView_srcCompat = 1; - - // aapt resource value: 2 - public const int AppCompatImageView_tint = 2; - - // aapt resource value: 3 - public const int AppCompatImageView_tintMode = 3; - - public static int[] AppCompatSeekBar = new int[] { - 16843074, - 2130772019, - 2130772020, - 2130772021}; - - // aapt resource value: 0 - public const int AppCompatSeekBar_android_thumb = 0; - - // aapt resource value: 1 - public const int AppCompatSeekBar_tickMark = 1; - - // aapt resource value: 2 - public const int AppCompatSeekBar_tickMarkTint = 2; - - // aapt resource value: 3 - public const int AppCompatSeekBar_tickMarkTintMode = 3; - - public static int[] AppCompatTextHelper = new int[] { - 16842804, - 16843117, - 16843118, - 16843119, - 16843120, - 16843666, - 16843667}; - - // aapt resource value: 2 - public const int AppCompatTextHelper_android_drawableBottom = 2; - - // aapt resource value: 6 - public const int AppCompatTextHelper_android_drawableEnd = 6; - - // aapt resource value: 3 - public const int AppCompatTextHelper_android_drawableLeft = 3; - - // aapt resource value: 4 - public const int AppCompatTextHelper_android_drawableRight = 4; - - // aapt resource value: 5 - public const int AppCompatTextHelper_android_drawableStart = 5; - - // aapt resource value: 1 - public const int AppCompatTextHelper_android_drawableTop = 1; - - // aapt resource value: 0 - public const int AppCompatTextHelper_android_textAppearance = 0; - - public static int[] AppCompatTextView = new int[] { - 16842804, - 2130772022, - 2130772023, - 2130772024, - 2130772025, - 2130772026, - 2130772027, - 2130772028}; - - // aapt resource value: 0 - public const int AppCompatTextView_android_textAppearance = 0; - - // aapt resource value: 6 - public const int AppCompatTextView_autoSizeMaxTextSize = 6; - - // aapt resource value: 5 - public const int AppCompatTextView_autoSizeMinTextSize = 5; - - // aapt resource value: 4 - public const int AppCompatTextView_autoSizePresetSizes = 4; - - // aapt resource value: 3 - public const int AppCompatTextView_autoSizeStepGranularity = 3; - - // aapt resource value: 2 - public const int AppCompatTextView_autoSizeTextType = 2; - - // aapt resource value: 7 - public const int AppCompatTextView_fontFamily = 7; - - // aapt resource value: 1 - public const int AppCompatTextView_textAllCaps = 1; - - public static int[] AppCompatTheme = new int[] { - 16842839, - 16842926, - 2130772029, - 2130772030, - 2130772031, - 2130772032, - 2130772033, - 2130772034, - 2130772035, - 2130772036, - 2130772037, - 2130772038, - 2130772039, - 2130772040, - 2130772041, - 2130772042, - 2130772043, - 2130772044, - 2130772045, - 2130772046, - 2130772047, - 2130772048, - 2130772049, - 2130772050, - 2130772051, - 2130772052, - 2130772053, - 2130772054, - 2130772055, - 2130772056, - 2130772057, - 2130772058, - 2130772059, - 2130772060, - 2130772061, - 2130772062, - 2130772063, - 2130772064, - 2130772065, - 2130772066, - 2130772067, - 2130772068, - 2130772069, - 2130772070, - 2130772071, - 2130772072, - 2130772073, - 2130772074, - 2130772075, - 2130772076, - 2130772077, - 2130772078, - 2130772079, - 2130772080, - 2130772081, - 2130772082, - 2130772083, - 2130772084, - 2130772085, - 2130772086, - 2130772087, - 2130772088, - 2130772089, - 2130772090, - 2130772091, - 2130772092, - 2130772093, - 2130772094, - 2130772095, - 2130772096, - 2130772097, - 2130772098, - 2130772099, - 2130772100, - 2130772101, - 2130772102, - 2130772103, - 2130772104, - 2130772105, - 2130772106, - 2130772107, - 2130772108, - 2130772109, - 2130772110, - 2130772111, - 2130772112, - 2130772113, - 2130772114, - 2130772115, - 2130772116, - 2130772117, - 2130772118, - 2130772119, - 2130772120, - 2130772121, - 2130772122, - 2130772123, - 2130772124, - 2130772125, - 2130772126, - 2130772127, - 2130772128, - 2130772129, - 2130772130, - 2130772131, - 2130772132, - 2130772133, - 2130772134, - 2130772135, - 2130772136, - 2130772137, - 2130772138, - 2130772139, - 2130772140, - 2130772141, - 2130772142, - 2130772143, - 2130772144, - 2130772145}; - - // aapt resource value: 23 - public const int AppCompatTheme_actionBarDivider = 23; - - // aapt resource value: 24 - public const int AppCompatTheme_actionBarItemBackground = 24; - - // aapt resource value: 17 - public const int AppCompatTheme_actionBarPopupTheme = 17; - - // aapt resource value: 22 - public const int AppCompatTheme_actionBarSize = 22; - - // aapt resource value: 19 - public const int AppCompatTheme_actionBarSplitStyle = 19; - - // aapt resource value: 18 - public const int AppCompatTheme_actionBarStyle = 18; - - // aapt resource value: 13 - public const int AppCompatTheme_actionBarTabBarStyle = 13; - - // aapt resource value: 12 - public const int AppCompatTheme_actionBarTabStyle = 12; - - // aapt resource value: 14 - public const int AppCompatTheme_actionBarTabTextStyle = 14; - - // aapt resource value: 20 - public const int AppCompatTheme_actionBarTheme = 20; - - // aapt resource value: 21 - public const int AppCompatTheme_actionBarWidgetTheme = 21; - - // aapt resource value: 50 - public const int AppCompatTheme_actionButtonStyle = 50; - - // aapt resource value: 46 - public const int AppCompatTheme_actionDropDownStyle = 46; - - // aapt resource value: 25 - public const int AppCompatTheme_actionMenuTextAppearance = 25; - - // aapt resource value: 26 - public const int AppCompatTheme_actionMenuTextColor = 26; - - // aapt resource value: 29 - public const int AppCompatTheme_actionModeBackground = 29; - - // aapt resource value: 28 - public const int AppCompatTheme_actionModeCloseButtonStyle = 28; - - // aapt resource value: 31 - public const int AppCompatTheme_actionModeCloseDrawable = 31; - - // aapt resource value: 33 - public const int AppCompatTheme_actionModeCopyDrawable = 33; - - // aapt resource value: 32 - public const int AppCompatTheme_actionModeCutDrawable = 32; - - // aapt resource value: 37 - public const int AppCompatTheme_actionModeFindDrawable = 37; - - // aapt resource value: 34 - public const int AppCompatTheme_actionModePasteDrawable = 34; - - // aapt resource value: 39 - public const int AppCompatTheme_actionModePopupWindowStyle = 39; - - // aapt resource value: 35 - public const int AppCompatTheme_actionModeSelectAllDrawable = 35; - - // aapt resource value: 36 - public const int AppCompatTheme_actionModeShareDrawable = 36; - - // aapt resource value: 30 - public const int AppCompatTheme_actionModeSplitBackground = 30; - - // aapt resource value: 27 - public const int AppCompatTheme_actionModeStyle = 27; - - // aapt resource value: 38 - public const int AppCompatTheme_actionModeWebSearchDrawable = 38; - - // aapt resource value: 15 - public const int AppCompatTheme_actionOverflowButtonStyle = 15; - - // aapt resource value: 16 - public const int AppCompatTheme_actionOverflowMenuStyle = 16; - - // aapt resource value: 58 - public const int AppCompatTheme_activityChooserViewStyle = 58; - - // aapt resource value: 95 - public const int AppCompatTheme_alertDialogButtonGroupStyle = 95; - - // aapt resource value: 96 - public const int AppCompatTheme_alertDialogCenterButtons = 96; - - // aapt resource value: 94 - public const int AppCompatTheme_alertDialogStyle = 94; - - // aapt resource value: 97 - public const int AppCompatTheme_alertDialogTheme = 97; - - // aapt resource value: 1 - public const int AppCompatTheme_android_windowAnimationStyle = 1; - - // aapt resource value: 0 - public const int AppCompatTheme_android_windowIsFloating = 0; - - // aapt resource value: 102 - public const int AppCompatTheme_autoCompleteTextViewStyle = 102; - - // aapt resource value: 55 - public const int AppCompatTheme_borderlessButtonStyle = 55; - - // aapt resource value: 52 - public const int AppCompatTheme_buttonBarButtonStyle = 52; - - // aapt resource value: 100 - public const int AppCompatTheme_buttonBarNegativeButtonStyle = 100; - - // aapt resource value: 101 - public const int AppCompatTheme_buttonBarNeutralButtonStyle = 101; - - // aapt resource value: 99 - public const int AppCompatTheme_buttonBarPositiveButtonStyle = 99; - - // aapt resource value: 51 - public const int AppCompatTheme_buttonBarStyle = 51; - - // aapt resource value: 103 - public const int AppCompatTheme_buttonStyle = 103; - - // aapt resource value: 104 - public const int AppCompatTheme_buttonStyleSmall = 104; - - // aapt resource value: 105 - public const int AppCompatTheme_checkboxStyle = 105; - - // aapt resource value: 106 - public const int AppCompatTheme_checkedTextViewStyle = 106; - - // aapt resource value: 86 - public const int AppCompatTheme_colorAccent = 86; - - // aapt resource value: 93 - public const int AppCompatTheme_colorBackgroundFloating = 93; - - // aapt resource value: 90 - public const int AppCompatTheme_colorButtonNormal = 90; - - // aapt resource value: 88 - public const int AppCompatTheme_colorControlActivated = 88; - - // aapt resource value: 89 - public const int AppCompatTheme_colorControlHighlight = 89; - - // aapt resource value: 87 - public const int AppCompatTheme_colorControlNormal = 87; - - // aapt resource value: 118 - public const int AppCompatTheme_colorError = 118; - - // aapt resource value: 84 - public const int AppCompatTheme_colorPrimary = 84; - - // aapt resource value: 85 - public const int AppCompatTheme_colorPrimaryDark = 85; - - // aapt resource value: 91 - public const int AppCompatTheme_colorSwitchThumbNormal = 91; - - // aapt resource value: 92 - public const int AppCompatTheme_controlBackground = 92; - - // aapt resource value: 44 - public const int AppCompatTheme_dialogPreferredPadding = 44; - - // aapt resource value: 43 - public const int AppCompatTheme_dialogTheme = 43; - - // aapt resource value: 57 - public const int AppCompatTheme_dividerHorizontal = 57; - - // aapt resource value: 56 - public const int AppCompatTheme_dividerVertical = 56; - - // aapt resource value: 75 - public const int AppCompatTheme_dropDownListViewStyle = 75; - - // aapt resource value: 47 - public const int AppCompatTheme_dropdownListPreferredItemHeight = 47; - - // aapt resource value: 64 - public const int AppCompatTheme_editTextBackground = 64; - - // aapt resource value: 63 - public const int AppCompatTheme_editTextColor = 63; - - // aapt resource value: 107 - public const int AppCompatTheme_editTextStyle = 107; - - // aapt resource value: 49 - public const int AppCompatTheme_homeAsUpIndicator = 49; - - // aapt resource value: 65 - public const int AppCompatTheme_imageButtonStyle = 65; - - // aapt resource value: 83 - public const int AppCompatTheme_listChoiceBackgroundIndicator = 83; - - // aapt resource value: 45 - public const int AppCompatTheme_listDividerAlertDialog = 45; - - // aapt resource value: 115 - public const int AppCompatTheme_listMenuViewStyle = 115; - - // aapt resource value: 76 - public const int AppCompatTheme_listPopupWindowStyle = 76; - - // aapt resource value: 70 - public const int AppCompatTheme_listPreferredItemHeight = 70; - - // aapt resource value: 72 - public const int AppCompatTheme_listPreferredItemHeightLarge = 72; - - // aapt resource value: 71 - public const int AppCompatTheme_listPreferredItemHeightSmall = 71; - - // aapt resource value: 73 - public const int AppCompatTheme_listPreferredItemPaddingLeft = 73; - - // aapt resource value: 74 - public const int AppCompatTheme_listPreferredItemPaddingRight = 74; - - // aapt resource value: 80 - public const int AppCompatTheme_panelBackground = 80; - - // aapt resource value: 82 - public const int AppCompatTheme_panelMenuListTheme = 82; - - // aapt resource value: 81 - public const int AppCompatTheme_panelMenuListWidth = 81; - - // aapt resource value: 61 - public const int AppCompatTheme_popupMenuStyle = 61; - - // aapt resource value: 62 - public const int AppCompatTheme_popupWindowStyle = 62; - - // aapt resource value: 108 - public const int AppCompatTheme_radioButtonStyle = 108; - - // aapt resource value: 109 - public const int AppCompatTheme_ratingBarStyle = 109; - - // aapt resource value: 110 - public const int AppCompatTheme_ratingBarStyleIndicator = 110; - - // aapt resource value: 111 - public const int AppCompatTheme_ratingBarStyleSmall = 111; - - // aapt resource value: 69 - public const int AppCompatTheme_searchViewStyle = 69; - - // aapt resource value: 112 - public const int AppCompatTheme_seekBarStyle = 112; - - // aapt resource value: 53 - public const int AppCompatTheme_selectableItemBackground = 53; - - // aapt resource value: 54 - public const int AppCompatTheme_selectableItemBackgroundBorderless = 54; - - // aapt resource value: 48 - public const int AppCompatTheme_spinnerDropDownItemStyle = 48; - - // aapt resource value: 113 - public const int AppCompatTheme_spinnerStyle = 113; - - // aapt resource value: 114 - public const int AppCompatTheme_switchStyle = 114; - - // aapt resource value: 40 - public const int AppCompatTheme_textAppearanceLargePopupMenu = 40; - - // aapt resource value: 77 - public const int AppCompatTheme_textAppearanceListItem = 77; - - // aapt resource value: 78 - public const int AppCompatTheme_textAppearanceListItemSecondary = 78; - - // aapt resource value: 79 - public const int AppCompatTheme_textAppearanceListItemSmall = 79; - - // aapt resource value: 42 - public const int AppCompatTheme_textAppearancePopupMenuHeader = 42; - - // aapt resource value: 67 - public const int AppCompatTheme_textAppearanceSearchResultSubtitle = 67; - - // aapt resource value: 66 - public const int AppCompatTheme_textAppearanceSearchResultTitle = 66; - - // aapt resource value: 41 - public const int AppCompatTheme_textAppearanceSmallPopupMenu = 41; - - // aapt resource value: 98 - public const int AppCompatTheme_textColorAlertDialogListItem = 98; - - // aapt resource value: 68 - public const int AppCompatTheme_textColorSearchUrl = 68; - - // aapt resource value: 60 - public const int AppCompatTheme_toolbarNavigationButtonStyle = 60; - - // aapt resource value: 59 - public const int AppCompatTheme_toolbarStyle = 59; - - // aapt resource value: 117 - public const int AppCompatTheme_tooltipForegroundColor = 117; - - // aapt resource value: 116 - public const int AppCompatTheme_tooltipFrameBackground = 116; - - // aapt resource value: 2 - public const int AppCompatTheme_windowActionBar = 2; - - // aapt resource value: 4 - public const int AppCompatTheme_windowActionBarOverlay = 4; - - // aapt resource value: 5 - public const int AppCompatTheme_windowActionModeOverlay = 5; - - // aapt resource value: 9 - public const int AppCompatTheme_windowFixedHeightMajor = 9; - - // aapt resource value: 7 - public const int AppCompatTheme_windowFixedHeightMinor = 7; - - // aapt resource value: 6 - public const int AppCompatTheme_windowFixedWidthMajor = 6; - - // aapt resource value: 8 - public const int AppCompatTheme_windowFixedWidthMinor = 8; - - // aapt resource value: 10 - public const int AppCompatTheme_windowMinWidthMajor = 10; - - // aapt resource value: 11 - public const int AppCompatTheme_windowMinWidthMinor = 11; - - // aapt resource value: 3 - public const int AppCompatTheme_windowNoTitle = 3; - - public static int[] BottomNavigationView = new int[] { - 2130772005, - 2130772266, - 2130772267, - 2130772268, - 2130772269}; - - // aapt resource value: 0 - public const int BottomNavigationView_elevation = 0; - - // aapt resource value: 4 - public const int BottomNavigationView_itemBackground = 4; - - // aapt resource value: 2 - public const int BottomNavigationView_itemIconTint = 2; - - // aapt resource value: 3 - public const int BottomNavigationView_itemTextColor = 3; - - // aapt resource value: 1 - public const int BottomNavigationView_menu = 1; - - public static int[] BottomSheetBehavior_Layout = new int[] { - 2130772228, - 2130772229, - 2130772230}; - - // aapt resource value: 1 - public const int BottomSheetBehavior_Layout_behavior_hideable = 1; - - // aapt resource value: 0 - public const int BottomSheetBehavior_Layout_behavior_peekHeight = 0; - - // aapt resource value: 2 - public const int BottomSheetBehavior_Layout_behavior_skipCollapsed = 2; - - public static int[] ButtonBarLayout = new int[] { - 2130772146}; - - // aapt resource value: 0 - public const int ButtonBarLayout_allowStacking = 0; - - public static int[] CollapsingToolbarLayout = new int[] { - 2130771980, - 2130772231, - 2130772232, - 2130772233, - 2130772234, - 2130772235, - 2130772236, - 2130772237, - 2130772238, - 2130772239, - 2130772240, - 2130772241, - 2130772242, - 2130772243, - 2130772244, - 2130772245}; - - // aapt resource value: 13 - public const int CollapsingToolbarLayout_collapsedTitleGravity = 13; - - // aapt resource value: 7 - public const int CollapsingToolbarLayout_collapsedTitleTextAppearance = 7; - - // aapt resource value: 8 - public const int CollapsingToolbarLayout_contentScrim = 8; - - // aapt resource value: 14 - public const int CollapsingToolbarLayout_expandedTitleGravity = 14; - - // aapt resource value: 1 - public const int CollapsingToolbarLayout_expandedTitleMargin = 1; - - // aapt resource value: 5 - public const int CollapsingToolbarLayout_expandedTitleMarginBottom = 5; - - // aapt resource value: 4 - public const int CollapsingToolbarLayout_expandedTitleMarginEnd = 4; - - // aapt resource value: 2 - public const int CollapsingToolbarLayout_expandedTitleMarginStart = 2; - - // aapt resource value: 3 - public const int CollapsingToolbarLayout_expandedTitleMarginTop = 3; - - // aapt resource value: 6 - public const int CollapsingToolbarLayout_expandedTitleTextAppearance = 6; - - // aapt resource value: 12 - public const int CollapsingToolbarLayout_scrimAnimationDuration = 12; - - // aapt resource value: 11 - public const int CollapsingToolbarLayout_scrimVisibleHeightTrigger = 11; - - // aapt resource value: 9 - public const int CollapsingToolbarLayout_statusBarScrim = 9; - - // aapt resource value: 0 - public const int CollapsingToolbarLayout_title = 0; - - // aapt resource value: 15 - public const int CollapsingToolbarLayout_titleEnabled = 15; - - // aapt resource value: 10 - public const int CollapsingToolbarLayout_toolbarId = 10; - - public static int[] CollapsingToolbarLayout_Layout = new int[] { - 2130772246, - 2130772247}; - - // aapt resource value: 0 - public const int CollapsingToolbarLayout_Layout_layout_collapseMode = 0; - - // aapt resource value: 1 - public const int CollapsingToolbarLayout_Layout_layout_collapseParallaxMultiplier = 1; - - public static int[] ColorStateListItem = new int[] { - 16843173, - 16843551, - 2130772147}; - - // aapt resource value: 2 - public const int ColorStateListItem_alpha = 2; - - // aapt resource value: 1 - public const int ColorStateListItem_android_alpha = 1; - - // aapt resource value: 0 - public const int ColorStateListItem_android_color = 0; - - public static int[] CompoundButton = new int[] { - 16843015, - 2130772148, - 2130772149}; - - // aapt resource value: 0 - public const int CompoundButton_android_button = 0; - - // aapt resource value: 1 - public const int CompoundButton_buttonTint = 1; - - // aapt resource value: 2 - public const int CompoundButton_buttonTintMode = 2; - - public static int[] CoordinatorLayout = new int[] { - 2130772248, - 2130772249}; - - // aapt resource value: 0 - public const int CoordinatorLayout_keylines = 0; - - // aapt resource value: 1 - public const int CoordinatorLayout_statusBarBackground = 1; - - public static int[] CoordinatorLayout_Layout = new int[] { - 16842931, - 2130772250, - 2130772251, - 2130772252, - 2130772253, - 2130772254, - 2130772255}; - - // aapt resource value: 0 - public const int CoordinatorLayout_Layout_android_layout_gravity = 0; - - // aapt resource value: 2 - public const int CoordinatorLayout_Layout_layout_anchor = 2; - - // aapt resource value: 4 - public const int CoordinatorLayout_Layout_layout_anchorGravity = 4; - - // aapt resource value: 1 - public const int CoordinatorLayout_Layout_layout_behavior = 1; - - // aapt resource value: 6 - public const int CoordinatorLayout_Layout_layout_dodgeInsetEdges = 6; - - // aapt resource value: 5 - public const int CoordinatorLayout_Layout_layout_insetEdge = 5; - - // aapt resource value: 3 - public const int CoordinatorLayout_Layout_layout_keyline = 3; - - public static int[] DesignTheme = new int[] { - 2130772256, - 2130772257, - 2130772258}; - - // aapt resource value: 0 - public const int DesignTheme_bottomSheetDialogTheme = 0; - - // aapt resource value: 1 - public const int DesignTheme_bottomSheetStyle = 1; - - // aapt resource value: 2 - public const int DesignTheme_textColorError = 2; - - public static int[] DrawerArrowToggle = new int[] { - 2130772150, - 2130772151, - 2130772152, - 2130772153, - 2130772154, - 2130772155, - 2130772156, - 2130772157}; - - // aapt resource value: 4 - public const int DrawerArrowToggle_arrowHeadLength = 4; - - // aapt resource value: 5 - public const int DrawerArrowToggle_arrowShaftLength = 5; - - // aapt resource value: 6 - public const int DrawerArrowToggle_barLength = 6; - - // aapt resource value: 0 - public const int DrawerArrowToggle_color = 0; - - // aapt resource value: 2 - public const int DrawerArrowToggle_drawableSize = 2; - - // aapt resource value: 3 - public const int DrawerArrowToggle_gapBetweenBars = 3; - - // aapt resource value: 1 - public const int DrawerArrowToggle_spinBars = 1; - - // aapt resource value: 7 - public const int DrawerArrowToggle_thickness = 7; - - public static int[] FloatingActionButton = new int[] { - 2130772005, - 2130772221, - 2130772222, - 2130772259, - 2130772260, - 2130772261, - 2130772262, - 2130772263}; - - // aapt resource value: 1 - public const int FloatingActionButton_backgroundTint = 1; - - // aapt resource value: 2 - public const int FloatingActionButton_backgroundTintMode = 2; - - // aapt resource value: 6 - public const int FloatingActionButton_borderWidth = 6; - - // aapt resource value: 0 - public const int FloatingActionButton_elevation = 0; - - // aapt resource value: 4 - public const int FloatingActionButton_fabSize = 4; - - // aapt resource value: 5 - public const int FloatingActionButton_pressedTranslationZ = 5; - - // aapt resource value: 3 - public const int FloatingActionButton_rippleColor = 3; - - // aapt resource value: 7 - public const int FloatingActionButton_useCompatPadding = 7; - - public static int[] FloatingActionButton_Behavior_Layout = new int[] { - 2130772264}; - - // aapt resource value: 0 - public const int FloatingActionButton_Behavior_Layout_behavior_autoHide = 0; - - public static int[] FontFamily = new int[] { - 2130772305, - 2130772306, - 2130772307, - 2130772308, - 2130772309, - 2130772310}; - - // aapt resource value: 0 - public const int FontFamily_fontProviderAuthority = 0; - - // aapt resource value: 3 - public const int FontFamily_fontProviderCerts = 3; - - // aapt resource value: 4 - public const int FontFamily_fontProviderFetchStrategy = 4; - - // aapt resource value: 5 - public const int FontFamily_fontProviderFetchTimeout = 5; - - // aapt resource value: 1 - public const int FontFamily_fontProviderPackage = 1; - - // aapt resource value: 2 - public const int FontFamily_fontProviderQuery = 2; - - public static int[] FontFamilyFont = new int[] { - 16844082, - 16844083, - 16844095, - 2130772311, - 2130772312, - 2130772313}; - - // aapt resource value: 0 - public const int FontFamilyFont_android_font = 0; - - // aapt resource value: 2 - public const int FontFamilyFont_android_fontStyle = 2; - - // aapt resource value: 1 - public const int FontFamilyFont_android_fontWeight = 1; - - // aapt resource value: 4 - public const int FontFamilyFont_font = 4; - - // aapt resource value: 3 - public const int FontFamilyFont_fontStyle = 3; - - // aapt resource value: 5 - public const int FontFamilyFont_fontWeight = 5; - - public static int[] ForegroundLinearLayout = new int[] { - 16843017, - 16843264, - 2130772265}; - - // aapt resource value: 0 - public const int ForegroundLinearLayout_android_foreground = 0; - - // aapt resource value: 1 - public const int ForegroundLinearLayout_android_foregroundGravity = 1; - - // aapt resource value: 2 - public const int ForegroundLinearLayout_foregroundInsidePadding = 2; - - public static int[] LinearLayoutCompat = new int[] { - 16842927, - 16842948, - 16843046, - 16843047, - 16843048, - 2130771988, - 2130772158, - 2130772159, - 2130772160}; - - // aapt resource value: 2 - public const int LinearLayoutCompat_android_baselineAligned = 2; - - // aapt resource value: 3 - public const int LinearLayoutCompat_android_baselineAlignedChildIndex = 3; - - // aapt resource value: 0 - public const int LinearLayoutCompat_android_gravity = 0; - - // aapt resource value: 1 - public const int LinearLayoutCompat_android_orientation = 1; - - // aapt resource value: 4 - public const int LinearLayoutCompat_android_weightSum = 4; - - // aapt resource value: 5 - public const int LinearLayoutCompat_divider = 5; - - // aapt resource value: 8 - public const int LinearLayoutCompat_dividerPadding = 8; - - // aapt resource value: 6 - public const int LinearLayoutCompat_measureWithLargestChild = 6; - - // aapt resource value: 7 - public const int LinearLayoutCompat_showDividers = 7; - - public static int[] LinearLayoutCompat_Layout = new int[] { - 16842931, - 16842996, - 16842997, - 16843137}; - - // aapt resource value: 0 - public const int LinearLayoutCompat_Layout_android_layout_gravity = 0; - - // aapt resource value: 2 - public const int LinearLayoutCompat_Layout_android_layout_height = 2; - - // aapt resource value: 3 - public const int LinearLayoutCompat_Layout_android_layout_weight = 3; - - // aapt resource value: 1 - public const int LinearLayoutCompat_Layout_android_layout_width = 1; - - public static int[] ListPopupWindow = new int[] { - 16843436, - 16843437}; - - // aapt resource value: 0 - public const int ListPopupWindow_android_dropDownHorizontalOffset = 0; - - // aapt resource value: 1 - public const int ListPopupWindow_android_dropDownVerticalOffset = 1; - - public static int[] MenuGroup = new int[] { - 16842766, - 16842960, - 16843156, - 16843230, - 16843231, - 16843232}; - - // aapt resource value: 5 - public const int MenuGroup_android_checkableBehavior = 5; - - // aapt resource value: 0 - public const int MenuGroup_android_enabled = 0; - - // aapt resource value: 1 - public const int MenuGroup_android_id = 1; - - // aapt resource value: 3 - public const int MenuGroup_android_menuCategory = 3; - - // aapt resource value: 4 - public const int MenuGroup_android_orderInCategory = 4; - - // aapt resource value: 2 - public const int MenuGroup_android_visible = 2; - - public static int[] MenuItem = new int[] { - 16842754, - 16842766, - 16842960, - 16843014, - 16843156, - 16843230, - 16843231, - 16843233, - 16843234, - 16843235, - 16843236, - 16843237, - 16843375, - 2130772161, - 2130772162, - 2130772163, - 2130772164, - 2130772165, - 2130772166, - 2130772167, - 2130772168, - 2130772169, - 2130772170}; - - // aapt resource value: 16 - public const int MenuItem_actionLayout = 16; - - // aapt resource value: 18 - public const int MenuItem_actionProviderClass = 18; - - // aapt resource value: 17 - public const int MenuItem_actionViewClass = 17; - - // aapt resource value: 13 - public const int MenuItem_alphabeticModifiers = 13; - - // aapt resource value: 9 - public const int MenuItem_android_alphabeticShortcut = 9; - - // aapt resource value: 11 - public const int MenuItem_android_checkable = 11; - - // aapt resource value: 3 - public const int MenuItem_android_checked = 3; - - // aapt resource value: 1 - public const int MenuItem_android_enabled = 1; - - // aapt resource value: 0 - public const int MenuItem_android_icon = 0; - - // aapt resource value: 2 - public const int MenuItem_android_id = 2; - - // aapt resource value: 5 - public const int MenuItem_android_menuCategory = 5; - - // aapt resource value: 10 - public const int MenuItem_android_numericShortcut = 10; - - // aapt resource value: 12 - public const int MenuItem_android_onClick = 12; - - // aapt resource value: 6 - public const int MenuItem_android_orderInCategory = 6; - - // aapt resource value: 7 - public const int MenuItem_android_title = 7; - - // aapt resource value: 8 - public const int MenuItem_android_titleCondensed = 8; - - // aapt resource value: 4 - public const int MenuItem_android_visible = 4; - - // aapt resource value: 19 - public const int MenuItem_contentDescription = 19; - - // aapt resource value: 21 - public const int MenuItem_iconTint = 21; - - // aapt resource value: 22 - public const int MenuItem_iconTintMode = 22; - - // aapt resource value: 14 - public const int MenuItem_numericModifiers = 14; - - // aapt resource value: 15 - public const int MenuItem_showAsAction = 15; - - // aapt resource value: 20 - public const int MenuItem_tooltipText = 20; - - public static int[] MenuView = new int[] { - 16842926, - 16843052, - 16843053, - 16843054, - 16843055, - 16843056, - 16843057, - 2130772171, - 2130772172}; - - // aapt resource value: 4 - public const int MenuView_android_headerBackground = 4; - - // aapt resource value: 2 - public const int MenuView_android_horizontalDivider = 2; - - // aapt resource value: 5 - public const int MenuView_android_itemBackground = 5; - - // aapt resource value: 6 - public const int MenuView_android_itemIconDisabledAlpha = 6; - - // aapt resource value: 1 - public const int MenuView_android_itemTextAppearance = 1; - - // aapt resource value: 3 - public const int MenuView_android_verticalDivider = 3; - - // aapt resource value: 0 - public const int MenuView_android_windowAnimationStyle = 0; - - // aapt resource value: 7 - public const int MenuView_preserveIconSpacing = 7; - - // aapt resource value: 8 - public const int MenuView_subMenuArrow = 8; - - public static int[] NavigationView = new int[] { - 16842964, - 16842973, - 16843039, - 2130772005, - 2130772266, - 2130772267, - 2130772268, - 2130772269, - 2130772270, - 2130772271}; - - // aapt resource value: 0 - public const int NavigationView_android_background = 0; - - // aapt resource value: 1 - public const int NavigationView_android_fitsSystemWindows = 1; - - // aapt resource value: 2 - public const int NavigationView_android_maxWidth = 2; - - // aapt resource value: 3 - public const int NavigationView_elevation = 3; - - // aapt resource value: 9 - public const int NavigationView_headerLayout = 9; - - // aapt resource value: 7 - public const int NavigationView_itemBackground = 7; - - // aapt resource value: 5 - public const int NavigationView_itemIconTint = 5; - - // aapt resource value: 8 - public const int NavigationView_itemTextAppearance = 8; - - // aapt resource value: 6 - public const int NavigationView_itemTextColor = 6; - - // aapt resource value: 4 - public const int NavigationView_menu = 4; - - public static int[] PopupWindow = new int[] { - 16843126, - 16843465, - 2130772173}; - - // aapt resource value: 1 - public const int PopupWindow_android_popupAnimationStyle = 1; - - // aapt resource value: 0 - public const int PopupWindow_android_popupBackground = 0; - - // aapt resource value: 2 - public const int PopupWindow_overlapAnchor = 2; - - public static int[] PopupWindowBackgroundState = new int[] { - 2130772174}; - - // aapt resource value: 0 - public const int PopupWindowBackgroundState_state_above_anchor = 0; - - public static int[] RecycleListView = new int[] { - 2130772175, - 2130772176}; - - // aapt resource value: 0 - public const int RecycleListView_paddingBottomNoButtons = 0; - - // aapt resource value: 1 - public const int RecycleListView_paddingTopNoTitle = 1; - - public static int[] RecyclerView = new int[] { - 16842948, - 16842993, - 2130771968, - 2130771969, - 2130771970, - 2130771971, - 2130771972, - 2130771973, - 2130771974, - 2130771975, - 2130771976}; - - // aapt resource value: 1 - public const int RecyclerView_android_descendantFocusability = 1; - - // aapt resource value: 0 - public const int RecyclerView_android_orientation = 0; - - // aapt resource value: 6 - public const int RecyclerView_fastScrollEnabled = 6; - - // aapt resource value: 9 - public const int RecyclerView_fastScrollHorizontalThumbDrawable = 9; - - // aapt resource value: 10 - public const int RecyclerView_fastScrollHorizontalTrackDrawable = 10; - - // aapt resource value: 7 - public const int RecyclerView_fastScrollVerticalThumbDrawable = 7; - - // aapt resource value: 8 - public const int RecyclerView_fastScrollVerticalTrackDrawable = 8; - - // aapt resource value: 2 - public const int RecyclerView_layoutManager = 2; - - // aapt resource value: 4 - public const int RecyclerView_reverseLayout = 4; - - // aapt resource value: 3 - public const int RecyclerView_spanCount = 3; - - // aapt resource value: 5 - public const int RecyclerView_stackFromEnd = 5; - - public static int[] ScrimInsetsFrameLayout = new int[] { - 2130772272}; - - // aapt resource value: 0 - public const int ScrimInsetsFrameLayout_insetForeground = 0; - - public static int[] ScrollingViewBehavior_Layout = new int[] { - 2130772273}; - - // aapt resource value: 0 - public const int ScrollingViewBehavior_Layout_behavior_overlapTop = 0; - - public static int[] SearchView = new int[] { - 16842970, - 16843039, - 16843296, - 16843364, - 2130772177, - 2130772178, - 2130772179, - 2130772180, - 2130772181, - 2130772182, - 2130772183, - 2130772184, - 2130772185, - 2130772186, - 2130772187, - 2130772188, - 2130772189}; - - // aapt resource value: 0 - public const int SearchView_android_focusable = 0; - - // aapt resource value: 3 - public const int SearchView_android_imeOptions = 3; - - // aapt resource value: 2 - public const int SearchView_android_inputType = 2; - - // aapt resource value: 1 - public const int SearchView_android_maxWidth = 1; - - // aapt resource value: 8 - public const int SearchView_closeIcon = 8; - - // aapt resource value: 13 - public const int SearchView_commitIcon = 13; - - // aapt resource value: 7 - public const int SearchView_defaultQueryHint = 7; - - // aapt resource value: 9 - public const int SearchView_goIcon = 9; - - // aapt resource value: 5 - public const int SearchView_iconifiedByDefault = 5; - - // aapt resource value: 4 - public const int SearchView_layout = 4; - - // aapt resource value: 15 - public const int SearchView_queryBackground = 15; - - // aapt resource value: 6 - public const int SearchView_queryHint = 6; - - // aapt resource value: 11 - public const int SearchView_searchHintIcon = 11; - - // aapt resource value: 10 - public const int SearchView_searchIcon = 10; - - // aapt resource value: 16 - public const int SearchView_submitBackground = 16; - - // aapt resource value: 14 - public const int SearchView_suggestionRowLayout = 14; - - // aapt resource value: 12 - public const int SearchView_voiceIcon = 12; - - public static int[] SnackbarLayout = new int[] { - 16843039, - 2130772005, - 2130772274}; - - // aapt resource value: 0 - public const int SnackbarLayout_android_maxWidth = 0; - - // aapt resource value: 1 - public const int SnackbarLayout_elevation = 1; - - // aapt resource value: 2 - public const int SnackbarLayout_maxActionInlineWidth = 2; - - public static int[] Spinner = new int[] { - 16842930, - 16843126, - 16843131, - 16843362, - 2130772006}; - - // aapt resource value: 3 - public const int Spinner_android_dropDownWidth = 3; - - // aapt resource value: 0 - public const int Spinner_android_entries = 0; - - // aapt resource value: 1 - public const int Spinner_android_popupBackground = 1; - - // aapt resource value: 2 - public const int Spinner_android_prompt = 2; - - // aapt resource value: 4 - public const int Spinner_popupTheme = 4; - - public static int[] SwitchCompat = new int[] { - 16843044, - 16843045, - 16843074, - 2130772190, - 2130772191, - 2130772192, - 2130772193, - 2130772194, - 2130772195, - 2130772196, - 2130772197, - 2130772198, - 2130772199, - 2130772200}; - - // aapt resource value: 1 - public const int SwitchCompat_android_textOff = 1; - - // aapt resource value: 0 - public const int SwitchCompat_android_textOn = 0; - - // aapt resource value: 2 - public const int SwitchCompat_android_thumb = 2; - - // aapt resource value: 13 - public const int SwitchCompat_showText = 13; - - // aapt resource value: 12 - public const int SwitchCompat_splitTrack = 12; - - // aapt resource value: 10 - public const int SwitchCompat_switchMinWidth = 10; - - // aapt resource value: 11 - public const int SwitchCompat_switchPadding = 11; - - // aapt resource value: 9 - public const int SwitchCompat_switchTextAppearance = 9; - - // aapt resource value: 8 - public const int SwitchCompat_thumbTextPadding = 8; - - // aapt resource value: 3 - public const int SwitchCompat_thumbTint = 3; - - // aapt resource value: 4 - public const int SwitchCompat_thumbTintMode = 4; - - // aapt resource value: 5 - public const int SwitchCompat_track = 5; - - // aapt resource value: 6 - public const int SwitchCompat_trackTint = 6; - - // aapt resource value: 7 - public const int SwitchCompat_trackTintMode = 7; - - public static int[] TabItem = new int[] { - 16842754, - 16842994, - 16843087}; - - // aapt resource value: 0 - public const int TabItem_android_icon = 0; - - // aapt resource value: 1 - public const int TabItem_android_layout = 1; - - // aapt resource value: 2 - public const int TabItem_android_text = 2; - - public static int[] TabLayout = new int[] { - 2130772275, - 2130772276, - 2130772277, - 2130772278, - 2130772279, - 2130772280, - 2130772281, - 2130772282, - 2130772283, - 2130772284, - 2130772285, - 2130772286, - 2130772287, - 2130772288, - 2130772289, - 2130772290}; - - // aapt resource value: 3 - public const int TabLayout_tabBackground = 3; - - // aapt resource value: 2 - public const int TabLayout_tabContentStart = 2; - - // aapt resource value: 5 - public const int TabLayout_tabGravity = 5; - - // aapt resource value: 0 - public const int TabLayout_tabIndicatorColor = 0; - - // aapt resource value: 1 - public const int TabLayout_tabIndicatorHeight = 1; - - // aapt resource value: 7 - public const int TabLayout_tabMaxWidth = 7; - - // aapt resource value: 6 - public const int TabLayout_tabMinWidth = 6; - - // aapt resource value: 4 - public const int TabLayout_tabMode = 4; - - // aapt resource value: 15 - public const int TabLayout_tabPadding = 15; - - // aapt resource value: 14 - public const int TabLayout_tabPaddingBottom = 14; - - // aapt resource value: 13 - public const int TabLayout_tabPaddingEnd = 13; - - // aapt resource value: 11 - public const int TabLayout_tabPaddingStart = 11; - - // aapt resource value: 12 - public const int TabLayout_tabPaddingTop = 12; - - // aapt resource value: 10 - public const int TabLayout_tabSelectedTextColor = 10; - - // aapt resource value: 8 - public const int TabLayout_tabTextAppearance = 8; - - // aapt resource value: 9 - public const int TabLayout_tabTextColor = 9; - - public static int[] TextAppearance = new int[] { - 16842901, - 16842902, - 16842903, - 16842904, - 16842906, - 16842907, - 16843105, - 16843106, - 16843107, - 16843108, - 16843692, - 2130772022, - 2130772028}; - - // aapt resource value: 10 - public const int TextAppearance_android_fontFamily = 10; - - // aapt resource value: 6 - public const int TextAppearance_android_shadowColor = 6; - - // aapt resource value: 7 - public const int TextAppearance_android_shadowDx = 7; - - // aapt resource value: 8 - public const int TextAppearance_android_shadowDy = 8; - - // aapt resource value: 9 - public const int TextAppearance_android_shadowRadius = 9; - - // aapt resource value: 3 - public const int TextAppearance_android_textColor = 3; - - // aapt resource value: 4 - public const int TextAppearance_android_textColorHint = 4; - - // aapt resource value: 5 - public const int TextAppearance_android_textColorLink = 5; - - // aapt resource value: 0 - public const int TextAppearance_android_textSize = 0; - - // aapt resource value: 2 - public const int TextAppearance_android_textStyle = 2; - - // aapt resource value: 1 - public const int TextAppearance_android_typeface = 1; - - // aapt resource value: 12 - public const int TextAppearance_fontFamily = 12; - - // aapt resource value: 11 - public const int TextAppearance_textAllCaps = 11; - - public static int[] TextInputLayout = new int[] { - 16842906, - 16843088, - 2130772291, - 2130772292, - 2130772293, - 2130772294, - 2130772295, - 2130772296, - 2130772297, - 2130772298, - 2130772299, - 2130772300, - 2130772301, - 2130772302, - 2130772303, - 2130772304}; - - // aapt resource value: 1 - public const int TextInputLayout_android_hint = 1; - - // aapt resource value: 0 - public const int TextInputLayout_android_textColorHint = 0; - - // aapt resource value: 6 - public const int TextInputLayout_counterEnabled = 6; - - // aapt resource value: 7 - public const int TextInputLayout_counterMaxLength = 7; - - // aapt resource value: 9 - public const int TextInputLayout_counterOverflowTextAppearance = 9; - - // aapt resource value: 8 - public const int TextInputLayout_counterTextAppearance = 8; - - // aapt resource value: 4 - public const int TextInputLayout_errorEnabled = 4; - - // aapt resource value: 5 - public const int TextInputLayout_errorTextAppearance = 5; - - // aapt resource value: 10 - public const int TextInputLayout_hintAnimationEnabled = 10; - - // aapt resource value: 3 - public const int TextInputLayout_hintEnabled = 3; - - // aapt resource value: 2 - public const int TextInputLayout_hintTextAppearance = 2; - - // aapt resource value: 13 - public const int TextInputLayout_passwordToggleContentDescription = 13; - - // aapt resource value: 12 - public const int TextInputLayout_passwordToggleDrawable = 12; - - // aapt resource value: 11 - public const int TextInputLayout_passwordToggleEnabled = 11; - - // aapt resource value: 14 - public const int TextInputLayout_passwordToggleTint = 14; - - // aapt resource value: 15 - public const int TextInputLayout_passwordToggleTintMode = 15; - - public static int[] Toolbar = new int[] { - 16842927, - 16843072, - 2130771980, - 2130771983, - 2130771987, - 2130771999, - 2130772000, - 2130772001, - 2130772002, - 2130772003, - 2130772004, - 2130772006, - 2130772201, - 2130772202, - 2130772203, - 2130772204, - 2130772205, - 2130772206, - 2130772207, - 2130772208, - 2130772209, - 2130772210, - 2130772211, - 2130772212, - 2130772213, - 2130772214, - 2130772215, - 2130772216, - 2130772217}; - - // aapt resource value: 0 - public const int Toolbar_android_gravity = 0; - - // aapt resource value: 1 - public const int Toolbar_android_minHeight = 1; - - // aapt resource value: 21 - public const int Toolbar_buttonGravity = 21; - - // aapt resource value: 23 - public const int Toolbar_collapseContentDescription = 23; - - // aapt resource value: 22 - public const int Toolbar_collapseIcon = 22; - - // aapt resource value: 6 - public const int Toolbar_contentInsetEnd = 6; - - // aapt resource value: 10 - public const int Toolbar_contentInsetEndWithActions = 10; - - // aapt resource value: 7 - public const int Toolbar_contentInsetLeft = 7; - - // aapt resource value: 8 - public const int Toolbar_contentInsetRight = 8; - - // aapt resource value: 5 - public const int Toolbar_contentInsetStart = 5; - - // aapt resource value: 9 - public const int Toolbar_contentInsetStartWithNavigation = 9; - - // aapt resource value: 4 - public const int Toolbar_logo = 4; - - // aapt resource value: 26 - public const int Toolbar_logoDescription = 26; - - // aapt resource value: 20 - public const int Toolbar_maxButtonHeight = 20; - - // aapt resource value: 25 - public const int Toolbar_navigationContentDescription = 25; - - // aapt resource value: 24 - public const int Toolbar_navigationIcon = 24; - - // aapt resource value: 11 - public const int Toolbar_popupTheme = 11; - - // aapt resource value: 3 - public const int Toolbar_subtitle = 3; - - // aapt resource value: 13 - public const int Toolbar_subtitleTextAppearance = 13; - - // aapt resource value: 28 - public const int Toolbar_subtitleTextColor = 28; - - // aapt resource value: 2 - public const int Toolbar_title = 2; - - // aapt resource value: 14 - public const int Toolbar_titleMargin = 14; - - // aapt resource value: 18 - public const int Toolbar_titleMarginBottom = 18; - - // aapt resource value: 16 - public const int Toolbar_titleMarginEnd = 16; - - // aapt resource value: 15 - public const int Toolbar_titleMarginStart = 15; - - // aapt resource value: 17 - public const int Toolbar_titleMarginTop = 17; - - // aapt resource value: 19 - public const int Toolbar_titleMargins = 19; - - // aapt resource value: 12 - public const int Toolbar_titleTextAppearance = 12; - - // aapt resource value: 27 - public const int Toolbar_titleTextColor = 27; - - public static int[] View = new int[] { - 16842752, - 16842970, - 2130772218, - 2130772219, - 2130772220}; - - // aapt resource value: 1 - public const int View_android_focusable = 1; - - // aapt resource value: 0 - public const int View_android_theme = 0; - - // aapt resource value: 3 - public const int View_paddingEnd = 3; - - // aapt resource value: 2 - public const int View_paddingStart = 2; - - // aapt resource value: 4 - public const int View_theme = 4; - - public static int[] ViewBackgroundHelper = new int[] { - 16842964, - 2130772221, - 2130772222}; - - // aapt resource value: 0 - public const int ViewBackgroundHelper_android_background = 0; - - // aapt resource value: 1 - public const int ViewBackgroundHelper_backgroundTint = 1; - - // aapt resource value: 2 - public const int ViewBackgroundHelper_backgroundTintMode = 2; - - public static int[] ViewStubCompat = new int[] { - 16842960, - 16842994, - 16842995}; - - // aapt resource value: 0 - public const int ViewStubCompat_android_id = 0; - - // aapt resource value: 2 - public const int ViewStubCompat_android_inflatedId = 2; - - // aapt resource value: 1 - public const int ViewStubCompat_android_layout = 1; - - static Styleable() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Styleable() - { - } - } } } #pragma warning restore 1591 From fc6f484244d97c44bcc5003ce37a2e94e32c1635 Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Sun, 6 Jan 2019 21:53:54 -0600 Subject: [PATCH 0334/2854] Use dev build of framework, make some fixes --- osu.Game.Tests/Visual/TestCaseChatLink.cs | 2 +- .../Visual/TestCaseLoungeRoomsContainer.cs | 4 ++-- .../Visual/TestCaseMatchSettingsOverlay.cs | 2 +- osu.Game/Online/Chat/ChannelManager.cs | 8 ++++---- osu.Game/Online/Multiplayer/PlaylistItem.cs | 4 ++-- osu.Game/Online/Multiplayer/Room.cs | 2 +- .../Settings/Sections/Graphics/LayoutSettings.cs | 14 +++++++------- osu.Game/Screens/Multi/IRoomManager.cs | 2 +- .../Multi/Lounge/Components/RoomsContainer.cs | 2 +- osu.Game/Screens/Multi/RoomBindings.cs | 2 +- osu.Game/Screens/Multi/RoomManager.cs | 4 ++-- osu.Game/osu.Game.csproj | 2 +- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChatLink.cs b/osu.Game.Tests/Visual/TestCaseChatLink.cs index 61c2f47e7d..8aa3283af7 100644 --- a/osu.Game.Tests/Visual/TestCaseChatLink.cs +++ b/osu.Game.Tests/Visual/TestCaseChatLink.cs @@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual linkColour = colours.Blue; var chatManager = new ChannelManager(); - BindableCollection availableChannels = (BindableCollection)chatManager.AvailableChannels; + BindableList availableChannels = (BindableList)chatManager.AvailableChannels; availableChannels.Add(new Channel { Name = "#english"}); availableChannels.Add(new Channel { Name = "#japanese" }); Dependencies.Cache(chatManager); diff --git a/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs index 3e9f2fb3a4..6b5bc875f1 100644 --- a/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs @@ -73,8 +73,8 @@ namespace osu.Game.Tests.Visual { public event Action RoomsUpdated; - public readonly BindableCollection Rooms = new BindableCollection(); - IBindableCollection IRoomManager.Rooms => Rooms; + public readonly BindableList Rooms = new BindableList(); + IBindableList IRoomManager.Rooms => Rooms; public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) => Rooms.Add(room); diff --git a/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs index 7fb9d4dded..6f084def48 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs @@ -138,7 +138,7 @@ namespace osu.Game.Tests.Visual public event Action RoomsUpdated; - public IBindableCollection Rooms { get; } = null; + public IBindableList Rooms { get; } = null; public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) { diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 4241b47cd3..d5deda960c 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -29,8 +29,8 @@ namespace osu.Game.Online.Chat @"#lobby" }; - private readonly BindableCollection availableChannels = new BindableCollection(); - private readonly BindableCollection joinedChannels = new BindableCollection(); + private readonly BindableList availableChannels = new BindableList(); + private readonly BindableList joinedChannels = new BindableList(); /// public int MaxNumericResult => NumericResultFor(MaxResult); + /// + /// The health increase for the maximum achievable result. + /// + public double MaxHealthIncrease => HealthIncreaseFor(MaxResult); + /// /// Retrieves the numeric score representation of a . /// diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 0fddb19a6c..1a682080bf 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -206,6 +206,9 @@ namespace osu.Game.Rulesets.Scoring private double baseScore; private double bonusScore; + private double rollingHp; + private double rollingMaxHp; + protected ScoreProcessor() { } @@ -332,6 +335,9 @@ namespace osu.Game.Rulesets.Scoring baseScore += result.Judgement.NumericResultFor(result); rollingMaxBaseScore += result.Judgement.MaxNumericResult; } + + rollingHp += HpFactorFor(result.Judgement, result.Type) * result.Judgement.HealthIncreaseFor(result); + rollingMaxHp += HpFactorFor(result.Judgement, result.Judgement.MaxResult) * result.Judgement.MaxHealthIncrease; } /// @@ -356,8 +362,13 @@ namespace osu.Game.Rulesets.Scoring baseScore -= result.Judgement.NumericResultFor(result); rollingMaxBaseScore -= result.Judgement.MaxNumericResult; } + + rollingHp -= HpFactorFor(result.Judgement, result.Type) * result.Judgement.HealthIncreaseFor(result); + rollingMaxHp -= HpFactorFor(result.Judgement, result.Judgement.MaxResult) * result.Judgement.MaxHealthIncrease; } + protected virtual double HpFactorFor(Judgement judgement, HitResult result) => 1; + private void updateScore() { if (rollingMaxBaseScore != 0) From d7919544fe8e5e6f78fb04834c44fbb7ff721b89 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Apr 2019 16:59:14 +0900 Subject: [PATCH 0494/2854] Implement approximate hp increase for catch --- .../Judgements/CatchBananaJudgement.cs | 2 +- .../Judgements/CatchDropletJudgement.cs | 4 ++-- .../Judgements/CatchJudgement.cs | 4 ++-- .../Judgements/CatchTinyDropletJudgement.cs | 2 +- .../Scoring/CatchScoreProcessor.cs | 20 ++++++++----------- 5 files changed, 14 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs index 4e64753a65..31f825c3ef 100644 --- a/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs +++ b/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Judgements default: return 0; case HitResult.Perfect: - return 8; + return 0.08; } } diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs index 2598dee156..f03897d611 100644 --- a/osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs +++ b/osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs @@ -23,9 +23,9 @@ namespace osu.Game.Rulesets.Catch.Judgements switch (result) { default: - return 0; + return base.HealthIncreaseFor(result); case HitResult.Perfect: - return 7; + return 0.07; } } } diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs index 5d7ef04dd2..abc38fe258 100644 --- a/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs +++ b/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs @@ -27,9 +27,9 @@ namespace osu.Game.Rulesets.Catch.Judgements switch (result) { default: - return 0; + return -0.02; case HitResult.Perfect: - return 10.2; + return 0.01; } } diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchTinyDropletJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchTinyDropletJudgement.cs index b8c51b7b60..bd1a74bc14 100644 --- a/osu.Game.Rulesets.Catch/Judgements/CatchTinyDropletJudgement.cs +++ b/osu.Game.Rulesets.Catch/Judgements/CatchTinyDropletJudgement.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.Judgements default: return 0; case HitResult.Perfect: - return 4; + return 0.004; } } } diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index af614f95d0..40585f2054 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Judgements; @@ -27,20 +26,17 @@ namespace osu.Game.Rulesets.Catch.Scoring hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; } - private const double harshness = 0.01; - - protected override void ApplyResult(JudgementResult result) + protected override double HpFactorFor(Judgement judgement, HitResult result) { - base.ApplyResult(result); - - if (result.Type == HitResult.Miss) + switch (result) { - if (!result.Judgement.IsBonus) - Health.Value -= hpDrainRate * (harshness * 2); - return; + case HitResult.Miss when judgement.IsBonus: + return 0; + case HitResult.Miss: + return hpDrainRate; + default: + return 10 - hpDrainRate; // Award less HP as drain rate is increased } - - Health.Value += Math.Max(result.Judgement.HealthIncreaseFor(result) - hpDrainRate, 0) * harshness; } public override HitWindows CreateHitWindows() => new CatchHitWindows(); From 4c5f41e40f1646df96c8fe1b87cafbfe5e775749 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Apr 2019 17:04:18 +0900 Subject: [PATCH 0495/2854] Implement hp increase for mania --- .../Judgements/HoldNoteTickJudgement.cs | 11 ++++++ .../Judgements/ManiaJudgement.cs | 21 ++++++++++ .../Scoring/ManiaScoreProcessor.cs | 38 +------------------ 3 files changed, 34 insertions(+), 36 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs index 015eb1310e..48c2eb547b 100644 --- a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs +++ b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs @@ -10,5 +10,16 @@ namespace osu.Game.Rulesets.Mania.Judgements public override bool AffectsCombo => false; protected override int NumericResultFor(HitResult result) => 20; + + protected override double HealthIncreaseFor(HitResult result) + { + switch (result) + { + case HitResult.Miss: + return 0; + default: + return 0.040; + } + } } } diff --git a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs index b6fb37f054..0548dc9eed 100644 --- a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs +++ b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs @@ -25,5 +25,26 @@ namespace osu.Game.Rulesets.Mania.Judgements return 300; } } + + protected override double HealthIncreaseFor(HitResult result) + { + switch (result) + { + case HitResult.Miss: + return -0.125; + case HitResult.Meh: + return 0.005; + case HitResult.Ok: + return 0.010; + case HitResult.Good: + return 0.035; + case HitResult.Great: + return 0.055; + case HitResult.Perfect: + return 0.065; + default: + return 0; + } + } } } diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index 5c914d8eac..75a73614f0 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -122,42 +122,8 @@ namespace osu.Game.Rulesets.Mania.Scoring } } - protected override void ApplyResult(JudgementResult result) - { - base.ApplyResult(result); - - bool isTick = result.Judgement is HoldNoteTickJudgement; - - if (isTick) - { - if (result.IsHit) - Health.Value += hpMultiplier * hp_increase_tick; - } - else - { - switch (result.Type) - { - case HitResult.Miss: - Health.Value += hpMissMultiplier * hp_increase_miss; - break; - case HitResult.Meh: - Health.Value += hpMultiplier * hp_increase_bad; - break; - case HitResult.Ok: - Health.Value += hpMultiplier * hp_increase_ok; - break; - case HitResult.Good: - Health.Value += hpMultiplier * hp_increase_good; - break; - case HitResult.Great: - Health.Value += hpMultiplier * hp_increase_great; - break; - case HitResult.Perfect: - Health.Value += hpMultiplier * hp_increase_perfect; - break; - } - } - } + protected override double HpFactorFor(Judgement judgement, HitResult result) + => result == HitResult.Miss ? hpMissMultiplier : hpMultiplier; public override HitWindows CreateHitWindows() => new ManiaHitWindows(); } From b59f23d0944a5aa512c2bf3096d4ef7390f32e72 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Apr 2019 17:04:46 +0900 Subject: [PATCH 0496/2854] Implement hp increase for taiko --- .../Scoring/TaikoScoreProcessor.cs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index 442cca49f8..a0055a93aa 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -46,19 +46,8 @@ namespace osu.Game.Rulesets.Taiko.Scoring hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120); } - protected override void ApplyResult(JudgementResult result) - { - base.ApplyResult(result); - - double hpIncrease = result.Judgement.HealthIncreaseFor(result); - - if (result.Type == HitResult.Miss) - hpIncrease *= hpMissMultiplier; - else - hpIncrease *= hpMultiplier; - - Health.Value += hpIncrease; - } + protected override double HpFactorFor(Judgement judgement, HitResult result) + => result == HitResult.Miss ? hpMissMultiplier : hpMultiplier; protected override void Reset(bool storeResults) { From 034643b8356bf2325d207ec0aab91179f402f3c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Apr 2019 17:06:01 +0900 Subject: [PATCH 0497/2854] Fix pause tests --- osu.Game/Rulesets/UI/Playfield.cs | 5 +++++ osu.Game/Screens/Play/GameplayClockContainer.cs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 13689153f0..a99c16a610 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -68,7 +68,12 @@ namespace osu.Game.Rulesets.UI { Cursor = CreateCursor(); if (Cursor != null) + { + // initial showing of the cursor will be handed by MenuCursorContainer (via DrawableRuleset's IProvideCursor implementation). + Cursor.Hide(); + AddInternal(Cursor); + } } /// diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 29974b728e..3654dc679c 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -123,7 +123,7 @@ namespace osu.Game.Screens.Play { // Seeking the decoupled clock to its current time ensures that its source clock will be seeked to the same time // This accounts for the audio clock source potentially taking time to enter a completely stopped state - adjustableClock.Seek(adjustableClock.CurrentTime); + adjustableClock.Seek(GameplayClock.CurrentTime); adjustableClock.Start(); IsPaused.Value = false; } From b3c496d72cb6d3a381098f357edcfd940c12a8ce Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Apr 2019 17:06:40 +0900 Subject: [PATCH 0498/2854] Remove delay on entering player --- osu.Game/Screens/Play/GameplayClockContainer.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 3654dc679c..2d1ba3f2ab 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -110,11 +110,8 @@ namespace osu.Game.Screens.Play adjustableClock.ChangeSource(sourceClock); updateRate(); - this.Delay(750).Schedule(() => - { - if (!IsPaused.Value) - Start(); - }); + if (!IsPaused.Value) + Start(); }); }); } From 78c844e25930af377c3c28162c58551d03c374b8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Apr 2019 17:24:22 +0900 Subject: [PATCH 0499/2854] Make catch provide some HP at DrainRate=10 --- osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 40585f2054..13c5028b23 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Catch.Scoring case HitResult.Miss: return hpDrainRate; default: - return 10 - hpDrainRate; // Award less HP as drain rate is increased + return 10.2 - hpDrainRate; // Award less HP as drain rate is increased } } From 144e6012dc333153c7e41ad37df544a23ea4b827 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Apr 2019 17:24:42 +0900 Subject: [PATCH 0500/2854] Implement hp increase for osu! --- .../Judgements/OsuJudgement.cs | 15 +++++++++++ .../Scoring/OsuScoreProcessor.cs | 25 ++++++++++--------- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs index 81fedf9f4a..7a98c5003d 100644 --- a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs @@ -24,5 +24,20 @@ namespace osu.Game.Rulesets.Osu.Judgements return 300; } } + + protected override double HealthIncreaseFor(HitResult result) + { + switch (result) + { + case HitResult.Miss: + return -0.02; + case HitResult.Meh: + case HitResult.Good: + case HitResult.Great: + return 0.01; + default: + return 0; + } + } } } diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index 2c8bf11016..a4975ef3b3 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -47,28 +47,29 @@ namespace osu.Game.Rulesets.Osu.Scoring if (result.Type != HitResult.None) comboResultCounts[osuResult.ComboType] = comboResultCounts.GetOrDefault(osuResult.ComboType) + 1; + } - switch (result.Type) + protected override double HpFactorFor(Judgement judgement, HitResult result) + { + switch (result) { case HitResult.Great: - Health.Value += (10.2 - hpDrainRate) * harshness; - break; + return 10.2 - hpDrainRate; case HitResult.Good: - Health.Value += (8 - hpDrainRate) * harshness; - break; + return 8 - hpDrainRate; case HitResult.Meh: - Health.Value += (4 - hpDrainRate) * harshness; - break; + return 4 - hpDrainRate; - /*case HitResult.SliderTick: - Health.Value += Math.Max(7 - hpDrainRate, 0) * 0.01; - break;*/ + // case HitResult.SliderTick: + // return Math.Max(7 - hpDrainRate, 0) * 0.01; case HitResult.Miss: - Health.Value -= hpDrainRate * (harshness * 2); - break; + return hpDrainRate; + + default: + return 0; } } From 4edb17a88a9ac82ac06fb2c7a172e4ad4bc0dc2c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Apr 2019 17:51:43 +0900 Subject: [PATCH 0501/2854] Make hp work + cleanup --- .../Scoring/CatchScoreProcessor.cs | 6 +-- .../Scoring/ManiaScoreProcessor.cs | 44 +------------------ .../Scoring/OsuScoreProcessor.cs | 6 +-- .../Scoring/TaikoScoreProcessor.cs | 4 +- .../Rulesets/Judgements/JudgementResult.cs | 5 +++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 13 ++---- 6 files changed, 17 insertions(+), 61 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 13c5028b23..778a7426aa 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -26,12 +26,10 @@ namespace osu.Game.Rulesets.Catch.Scoring hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; } - protected override double HpFactorFor(Judgement judgement, HitResult result) + protected override double HpFactorFor(JudgementResult result) { - switch (result) + switch (result.Type) { - case HitResult.Miss when judgement.IsBonus: - return 0; case HitResult.Miss: return hpDrainRate; default: diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index 75a73614f0..ee4618e5c2 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -3,7 +3,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; @@ -28,36 +27,6 @@ namespace osu.Game.Rulesets.Mania.Scoring /// private const double hp_multiplier_max = 1; - /// - /// The default BAD hit HP increase. - /// - private const double hp_increase_bad = 0.005; - - /// - /// The default OK hit HP increase. - /// - private const double hp_increase_ok = 0.010; - - /// - /// The default GOOD hit HP increase. - /// - private const double hp_increase_good = 0.035; - - /// - /// The default tick hit HP increase. - /// - private const double hp_increase_tick = 0.040; - - /// - /// The default GREAT hit HP increase. - /// - private const double hp_increase_great = 0.055; - - /// - /// The default PERFECT hit HP increase. - /// - private const double hp_increase_perfect = 0.065; - /// /// The MISS HP multiplier at OD = 0. /// @@ -73,11 +42,6 @@ namespace osu.Game.Rulesets.Mania.Scoring /// private const double hp_multiplier_miss_max = 1; - /// - /// The default MISS HP increase. - /// - private const double hp_increase_miss = -0.125; - /// /// The MISS HP multiplier. This is multiplied to the miss hp increase. /// @@ -88,10 +52,6 @@ namespace osu.Game.Rulesets.Mania.Scoring /// /// The currently opened channel @@ -40,12 +40,12 @@ namespace osu.Game.Online.Chat /// /// The Channels the player has joined /// - public IBindableCollection JoinedChannels => joinedChannels; + public IBindableList JoinedChannels => joinedChannels; /// /// The channels available for the player to join /// - public IBindableCollection AvailableChannels => availableChannels; + public IBindableList AvailableChannels => availableChannels; private IAPIProvider api; diff --git a/osu.Game/Online/Multiplayer/PlaylistItem.cs b/osu.Game/Online/Multiplayer/PlaylistItem.cs index 4155121bdf..63b5b95b9c 100644 --- a/osu.Game/Online/Multiplayer/PlaylistItem.cs +++ b/osu.Game/Online/Multiplayer/PlaylistItem.cs @@ -37,10 +37,10 @@ namespace osu.Game.Online.Multiplayer public RulesetInfo Ruleset { get; set; } [JsonIgnore] - public readonly BindableCollection AllowedMods = new BindableCollection(); + public readonly BindableList AllowedMods = new BindableList(); [JsonIgnore] - public readonly BindableCollection RequiredMods = new BindableCollection(); + public readonly BindableList RequiredMods = new BindableList(); [JsonProperty("beatmap")] private APIBeatmap apiBeatmap { get; set; } diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 448f5ced91..5273c7acfb 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -24,7 +24,7 @@ namespace osu.Game.Online.Multiplayer public Bindable Host { get; private set; } = new Bindable(); [JsonProperty("playlist")] - public BindableCollection Playlist { get; set; } = new BindableCollection(); + public BindableList Playlist { get; set; } = new BindableList(); [JsonProperty("channel_id")] public Bindable ChannelId { get; private set; } = new Bindable(); diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 685244e06b..c1fefb7290 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private FillFlowContainer letterboxSettings; - private Bindable letterboxing; + //private Bindable letterboxing; private Bindable sizeFullscreen; private OsuGameBase game; @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { this.game = game; - letterboxing = config.GetBindable(FrameworkSetting.Letterboxing); + //letterboxing = config.GetBindable(FrameworkSetting.Letterboxing); sizeFullscreen = config.GetBindable(FrameworkSetting.SizeFullscreen); Container resolutionSettingsContainer; @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics new SettingsCheckbox { LabelText = "Letterboxing", - Bindable = letterboxing, + //Bindable = letterboxing, }, letterboxSettings = new FillFlowContainer { @@ -68,13 +68,13 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics new SettingsSlider { LabelText = "Horizontal position", - Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionX), + //Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionX), KeyboardStep = 0.01f }, new SettingsSlider { LabelText = "Vertical position", - Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionY), + //Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionY), KeyboardStep = 0.01f }, } @@ -105,14 +105,14 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }, true); } - letterboxing.BindValueChanged(isVisible => + /*letterboxing.BindValueChanged(isVisible => { letterboxSettings.ClearTransforms(); letterboxSettings.AutoSizeAxes = isVisible ? Axes.Y : Axes.None; if (!isVisible) letterboxSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint); - }, true); + }, true);*/ } private IReadOnlyList getResolutions() diff --git a/osu.Game/Screens/Multi/IRoomManager.cs b/osu.Game/Screens/Multi/IRoomManager.cs index f0dbcb0e71..6af8a35208 100644 --- a/osu.Game/Screens/Multi/IRoomManager.cs +++ b/osu.Game/Screens/Multi/IRoomManager.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.Multi /// /// All the active s. /// - IBindableCollection Rooms { get; } + IBindableList Rooms { get; } /// /// Creates a new . diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs index 5133e96a52..4ad8154090 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components private readonly Bindable selectedRoom = new Bindable(); public IBindable SelectedRoom => selectedRoom; - private readonly IBindableCollection rooms = new BindableCollection(); + private readonly IBindableList rooms = new BindableList(); private readonly FillFlowContainer roomFlow; public IReadOnlyList Rooms => roomFlow; diff --git a/osu.Game/Screens/Multi/RoomBindings.cs b/osu.Game/Screens/Multi/RoomBindings.cs index dc2547268d..cdbb6dbea6 100644 --- a/osu.Game/Screens/Multi/RoomBindings.cs +++ b/osu.Game/Screens/Multi/RoomBindings.cs @@ -86,7 +86,7 @@ namespace osu.Game.Screens.Multi public readonly Bindable Host = new Bindable(); public readonly Bindable Status = new Bindable(); public readonly Bindable Type = new Bindable(); - public readonly BindableCollection Playlist = new BindableCollection(); + public readonly BindableList Playlist = new BindableList(); public readonly Bindable> Participants = new Bindable>(); public readonly Bindable ParticipantCount = new Bindable(); public readonly Bindable MaxParticipants = new Bindable(); diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index fab19c3fd7..1f95401905 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -21,8 +21,8 @@ namespace osu.Game.Screens.Multi { public event Action RoomsUpdated; - private readonly BindableCollection rooms = new BindableCollection(); - public IBindableCollection Rooms => rooms; + private readonly BindableList rooms = new BindableList(); + public IBindableList Rooms => rooms; private Room currentRoom; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 103c7c20d6..4069ac01d0 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,7 @@ - + From 8c94ea3c35337a145855b8e3db3d885bed31b215 Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Tue, 8 Jan 2019 18:14:34 -0600 Subject: [PATCH 0335/2854] Update NuGet packages --- osu.Android.props | 8 ++++---- osu.Game/osu.Game.csproj | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 4c89408203..a662855fc1 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -39,10 +39,10 @@ - 0.0.7879 + 0.0.7939 - 0.0.7879 + 0.0.7939 0.22.0 @@ -51,10 +51,10 @@ 1.1.0 - 1.0.0-dev000094 + 1.0.0-dev000096 - 1.0.0-dev002278 + 1.0.0-dev002315 \ No newline at end of file diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4069ac01d0..ce6e035b1c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,7 @@ - + From 6624a91697ccb26f9e5b9053d8f3aca631f30765 Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Tue, 8 Jan 2019 21:36:22 -0600 Subject: [PATCH 0336/2854] Remove Resource.designer.cs --- osu.Android/Resources/Resource.designer.cs | 92 ---------------------- 1 file changed, 92 deletions(-) delete mode 100644 osu.Android/Resources/Resource.designer.cs diff --git a/osu.Android/Resources/Resource.designer.cs b/osu.Android/Resources/Resource.designer.cs deleted file mode 100644 index c02df072f1..0000000000 --- a/osu.Android/Resources/Resource.designer.cs +++ /dev/null @@ -1,92 +0,0 @@ -#pragma warning disable 1591 -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -[assembly: global::Android.Runtime.ResourceDesignerAttribute("osu.Android.Resource", IsApplication=true)] - -namespace osu.Android -{ - - - [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] - public partial class Resource - { - - static Resource() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - public static void UpdateIdValues() - { - } - - public partial class Attribute - { - - static Attribute() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Attribute() - { - } - } - - public partial class Color - { - - // aapt resource value: 0x7f030002 - public const int colorAccent = 2130903042; - - // aapt resource value: 0x7f030000 - public const int colorPrimary = 2130903040; - - // aapt resource value: 0x7f030001 - public const int colorPrimaryDark = 2130903041; - - // aapt resource value: 0x7f030003 - public const int ic_launcher_background = 2130903043; - - static Color() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Color() - { - } - } - - public partial class Mipmap - { - - // aapt resource value: 0x7f020000 - public const int ic_launcher = 2130837504; - - // aapt resource value: 0x7f020001 - public const int ic_launcher_foreground = 2130837505; - - // aapt resource value: 0x7f020002 - public const int ic_launcher_round = 2130837506; - - static Mipmap() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Mipmap() - { - } - } - } -} -#pragma warning restore 1591 From b18eebfd70757c3fbab997f0d487de00e7ec4489 Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Wed, 9 Jan 2019 19:31:42 -0600 Subject: [PATCH 0337/2854] Get icon working for osu.Android --- osu.Android/Properties/AndroidManifest.xml | 2 +- osu.Android/Resources/drawable/lazer.png | Bin 0 -> 39498 bytes .../mipmap-anydpi-v26/ic_launcher.xml | 5 ---- .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 ---- .../Resources/mipmap-hdpi/ic_launcher.png | Bin 8828 -> 0 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 1441 -> 0 bytes .../mipmap-hdpi/ic_launcher_round.png | Bin 8828 -> 0 bytes .../Resources/mipmap-mdpi/ic_launcher.png | Bin 5045 -> 0 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 958 -> 0 bytes .../mipmap-mdpi/ic_launcher_round.png | Bin 5045 -> 0 bytes .../Resources/mipmap-xhdpi/ic_launcher.png | Bin 12931 -> 0 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 2056 -> 0 bytes .../mipmap-xhdpi/ic_launcher_round.png | Bin 12931 -> 0 bytes .../Resources/mipmap-xxhdpi/ic_launcher.png | Bin 21256 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 3403 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_round.png | Bin 21256 -> 0 bytes .../Resources/mipmap-xxxhdpi/ic_launcher.png | Bin 30047 -> 0 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 4889 -> 0 bytes .../mipmap-xxxhdpi/ic_launcher_round.png | Bin 30047 -> 0 bytes osu.Android/Resources/values/colors.xml | 6 ----- .../values/ic_launcher_background.xml | 4 --- osu.Android/osu.Android.csproj | 23 +----------------- 22 files changed, 2 insertions(+), 43 deletions(-) create mode 100644 osu.Android/Resources/drawable/lazer.png delete mode 100644 osu.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 osu.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 osu.Android/Resources/mipmap-hdpi/ic_launcher.png delete mode 100644 osu.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png delete mode 100644 osu.Android/Resources/mipmap-hdpi/ic_launcher_round.png delete mode 100644 osu.Android/Resources/mipmap-mdpi/ic_launcher.png delete mode 100644 osu.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png delete mode 100644 osu.Android/Resources/mipmap-mdpi/ic_launcher_round.png delete mode 100644 osu.Android/Resources/mipmap-xhdpi/ic_launcher.png delete mode 100644 osu.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png delete mode 100644 osu.Android/Resources/mipmap-xhdpi/ic_launcher_round.png delete mode 100644 osu.Android/Resources/mipmap-xxhdpi/ic_launcher.png delete mode 100644 osu.Android/Resources/mipmap-xxhdpi/ic_launcher_foreground.png delete mode 100644 osu.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png delete mode 100644 osu.Android/Resources/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 osu.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png delete mode 100644 osu.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png delete mode 100644 osu.Android/Resources/values/colors.xml delete mode 100644 osu.Android/Resources/values/ic_launcher_background.xml diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml index bb5f9b751e..9b337eec6c 100644 --- a/osu.Android/Properties/AndroidManifest.xml +++ b/osu.Android/Properties/AndroidManifest.xml @@ -5,5 +5,5 @@ - + \ No newline at end of file diff --git a/osu.Android/Resources/drawable/lazer.png b/osu.Android/Resources/drawable/lazer.png new file mode 100644 index 0000000000000000000000000000000000000000..075a8e7184ad508cedd8fe9ae549d8cea696b51a GIT binary patch literal 39498 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 X{r?ZpP_kcw2Ot$CjR&O)ra}J$c_$^Y literal 0 HcmV?d00001 diff --git a/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml b/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 9412204815..0000000000 --- a/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml b/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 036d09bc5f..0000000000 --- a/osu.Android/Resources/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/osu.Android/Resources/mipmap-hdpi/ic_launcher.png b/osu.Android/Resources/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 2af076dabd0afe60561f41903cc42931db8d33cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8828 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 N&GnQ!lI8SQ#{X#Iv!eh2 diff --git a/osu.Android/Resources/mipmap-hdpi/ic_launcher_round.png b/osu.Android/Resources/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 2af076dabd0afe60561f41903cc42931db8d33cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8828 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 LNkvXXu0mjfp$&R0 diff --git a/osu.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png b/osu.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png deleted file mode 100644 index a12b157f00e3022cfe81a1e767525c33f4ed2cf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 958 zcmeAS@N?(olHy`uVBq!ia0vp^IUvlz1|<8_!p|}=Ft>WTIEGZ*dOIh!!#7am`0)iN z!-Y*JzdqP8rN&2Y&y2(+EtA?m9-5+}#BXAw@$*D;zxcf=lRhYP2`ZYNoGdU|=;=Y1 z!-o@UOzpBVHoTpyopyF#@i)8YcdVaV?2ljDUj6>w?`yyA*Pf5cUSE9b6wq26;8J@~ z){!@7GpTmNE>2kO_POn1zf8`~}P?%{85(;s&nc+C&;t$4D5$+f9? z-8>e~Z&%(_OwrVd==PGc4mhTFjVafjdCqsM|EvEe$2)U;a9s0IGofbtHcpKz;cJR= z`DNzVI-iMtrg<$r*EFejE8l0oMM3e)a|=o;x>Mhk@*n)xx%2Rrt=4TnivwP5zpS-& z@5h3w<{9>vH!6KP74q!po!oh)$BI~jUu}4P|5ofvi@(2i9NyELbZ$qD}PI&+JJ3+^f2=YEuP zjpepXu;`->)%n@lB|b@Iv$k0qhJJp%S?O9t?)zjLwwY?z@=v^12)=lt^ZcwNoye^x z_uu*-x}ntY`mc3S`yMaaHuurqE~e`{G_IsMZdhw*{kDDS9h3WSQa;8d3vwO)d?WE+ z%*LAIs=2#$t=BZmPTP}xMpj0I9ti9_c{r`p zu+;ELV)~|tmk}}-GjAWQO5U<}Lr?bB5UX>pYf5~UOnY%ZTQR${nq6YQOHc15>q%#$ zl8$8k_1fsCw;<~OiJ-OiE?f7RJWt%N`#e!y=2`BhIqju|a?kW5QupmV#wx6HrSs?J z&nJroVy6i|*Og1U`{c;a^^dPvTfNJjdCg1nUS<*OC dK7&Kx57tYsZ49$p7vBM?@pScbS?83{1OVHE%8UR2 diff --git a/osu.Android/Resources/mipmap-mdpi/ic_launcher_round.png b/osu.Android/Resources/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index c51990875e404a36964d727d376637c6e6c81305..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5045 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 LNkvXXu0mjfp$&R0 diff --git a/osu.Android/Resources/mipmap-xhdpi/ic_launcher.png b/osu.Android/Resources/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 4e8d9b598cd21e01bbbca29aa077d99a4dabefe3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12931 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 b;}yxl--Ss53i@>Q4YQuNcebmsMJN0NT!aL! diff --git a/osu.Android/Resources/mipmap-xhdpi/ic_launcher_round.png b/osu.Android/Resources/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 4e8d9b598cd21e01bbbca29aa077d99a4dabefe3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12931 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 diff --git a/osu.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png b/osu.Android/Resources/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 2d3354eaf2778d4ae52cf3e1368f131be63ccb62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21256 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 z zW!Y4e)%RYT^_-LjKO7sDq@y#bSRDP2yai<}1^=fvWF^G_1>}U{ zNVCJ?Ap0_y`O~xlm^hmjdS8W(MUf9?{|iEvE?G7{Of4opq3Isf#T@uk&TU?_zzOhr zJdEvnG&G9dAH9U(H;$v>&JC!)adjFc$$slr;Qq)pkXO$`_~}Ey(Qee(To~MO2&Xw< z$!Bh5cy?(?Edrmr5vhNC7NKA4hI`dKxR*3CUm=;haad=iR9&#ULLT}bZl zK>a;i;QH`}^mhZhPN3s~XW<(M+Tn1AH>^bST^r$^H;FsvsmCvYJ6MR-enTsouU$lah$?c|)HZegXSW+Q$95xxOEdrZ?>qrd zTtNNp>lkK9Trd?FHh!TWeb2m&iCt&lNN{NM24Gigtk|Cc@aUaU@~(`rOxD6&bGT;m z2~9hRnveI2X+br7f3OYm z6-^Kx+lSuA-o_bC$C5AH0g6+tyMr2X|iU?g&Zl7GCAdn&;5{^I!MsXg>EkgZ&JMtNv>UEdy%;&w ziQ&f&;KZKOsJVR|=D&XvZ2qF;c@>v>Y%f%t4S(J)FIv#u;smC2~Ep3FiwTZ>;UGr+Q z^qZgvR1rp-b0P+JTR*A~cVcMoDTJQfkE*K{UlIjK5{&yqFPfJxMquMo*j6?{qH@V; z*s5s)i}nREalRknbNvXn_aWXHWUEGv)5`{Oxx`p3L(KC?7U*$Y2_bV!&DTzs#v(Cf zM+)!>O^UOorbfxD04|q{jKYFzIB9t}G>&L!1Y)YZez})hB#0v4^ich(4HNBSm^e9r zpl|@XPk}POhAD$@RSVn;>$9RiG^0{^C2gMfUWIty2yBZB({s#~xaQYj#h-s1BK0oL zV}7xM`k^sQT$vTq(wp*V&Q)B(PVO_ zGpd(H=;bgKVAT2`tpE-OV|gm907(^zNR)^}3q?Wm%1j!j&9#tw=ujMRD0ZY&jm3wf zV-twI-GTo7=P}@Z8*;rD_C@vZu4smLaT6R()oD02U*&?m>f+D*X`#aQ+HAKiIy{PS z=OD(<^kV!>4`LmI(1H;LUX;MYBiUeAJdj<4%oSnfGj&GPHM0m-A?n4738O@jAn-|M zN~am3bt9Lf#Y<9N*=EaL9y3<}ITsr=MTo0jHyn>ko!nv+B^I7uH~-Hn!9?bil+#uy z^GnWZ)6aoZRVbVQ#p#9a(2>$qB&mx^cLb4hCs>aF&TV6>gR8X>o@LGOE@_0lu4u>D zIhW9}3kV%Phj9AX$o8x}%ZS_h4a z^GOj>CeIEPo10uXjLb?k+6*xs9@WVD(#C3lO?Ea`pPNXsB$36_FA%(211p9Ja2%3} z6ynh+!pHg%Ja7TMY6`wix=V6GR(?#! zJZgSOIQfsZE{0*%!t8H+=&bC&{#kep6u{uQ7kaFY&$zVA{BIck(5U(*cd^{oZ2 zRZkb|SQI2IQeo1ASP%%KLAA=Vz-h&x2W%=6{mrXJ3nRvsmP{d)NFX{q297eTMVdIz zxo|+$+Qn@9Z55pbClhIU`9hNGvr0VXCUD2}9NQl(50LV)r!>;7125h}p=tXr(an$fnDZJKIyRZ$IphnO2Q*1SS9 z%ple9EmZ0P<8`u}!KIeONSINCaQPbb%~H~EWXrsmVfW9u6oN?zcB=L(N|Trfhr?Ok za#{hx;V{`0se%eXuvu25aOoZZi`WPpMvr$Q!mCiDanm+enQy>YmQ%){)ht`y{FmB^ zGDn!w>PeL~lweIHRLyjdjpG>qt(tbf^|!{V$pl6(qOxu+foqoG%D=ont4YPBkd9xz ziJm8SLvkqwWLhx!Hu=eD*9@OF3pk|6ywHrO3+vl~d zwnuVgWF#w*kXC?LEJkReWFbm)0v_?QfVs{26%oRpa3Y3ZK8EpM?=fa0&2VZt-JnF_ zFgZ(cCgr}RvJ4u=n|-*f)sW{{1(?8dSxywu4HCzv)y{}ept9S+n49(HCMc^~*vfFJ z6`=0Am2f&7NOcC0>V9&B`b;cZjWlXu@TYaRUieZbob?HTodWuZGm@hfwX#n|-|$ z;$Gg&Hg+4MQKX0}glGdhhN1S1LLD53Iv!>0*=%)?1;*%6xcL=LA{vs3q?sVIo}tca zhYd*hr8b2)Mk;L6I5@q9dxn`39UdOe0u-ecAdyHAty`o}O#q`GB%b_BeBU(CWt$B8 zU#1S~Q~t=i^CX6y20Uv!NK{KMxU&1B8lr-yHnfsC1!|)_D!z(g` zd2ZIfOP*iF20L+j5;sqw-ePJZfpBjK!To0#!&}RTuD@i-u=D@2 zo~4c1*d>PyX;BT^V0O<*4UQw#Jp#337|9F6Mo~Ex%UFRxVvQz0X&S6QWnq(QieZT^ zwa_*5HVKlqAduOT(B>*l001BWNklcs5PtRuA}<_gg9%02rLxEn zRfCmEUowZn5}J|#(OG-~ibkU&xsCYJ3NSuCP6|+zV1@Oryc$=fktG!JxCu6iI+k<3?Pk&~A!_6Z683 z^T_3(01ThzR!TBaL!r>HMc`~n`f2p{_b123#)gPVxR3~5rx9w?-dRgPZZ{GNrN(-L zh+Y_kv}!&xjUs(PjGpeo$cYYw&-5bR9b!(UOimUjL6Kc>tf_-{>lJWsUCDX^Uk*Y& zhy$O!5$-L^5P9w>A_qH=3`Y?kR}tQG0lmA=qR*qS)~l{%P4KO3F-;}oT~khD{8$I< zvX~|~E1FcIRlsD1D4Ec94uq`$4%x6wib!v7??5ON%KA}S0f;lbv$M00_<;%uTLJ7M zFfnuN_(d0U$<{Mktn#7+F`2^PD@PD)?`HOV^!y;SP}o3Haw4h}1va+>LQ02JoSZETb!hTaLH=i#uS@F|hM4!?4o)d9$Y z2%k-X&EbdS7FiB}SR<%?lH;1itT~*#a4M^E=)?tF_}Qxny?FwQK6e|OAHQkNpUBv; z4)p)z6%05PjNN?==6~Q?$ZpdK6gb#6ErxvMJVak_V~W7{jKZe4;Bk2v%sY`xAvO?0 zr1JzuUOo=R)9e0WMzv8=rY2M38R zS<7XO&J@9=K=3=Db(X}(cv(okXoitb;Nf?8;CHx<3@4d%Nex;w4p3Xr4GZDEeGQb= zQwj58Bjf0Jd?yB;+zb0q0!z1UK=qA9`&Cs$$iZFliCZ!J=w1XLdJ|(u&ZFt>O{m*6 z$rnn|AIEK1uzmp1mrfx1#%bu(Y}+P6QtWWr?eOVd#@Imvm$>%TRI=>YhDBXM^%Oj# z%e;6>pc8AgB&NG?7LlQ`v9aFVpJXXOFc|DM%f+Ux@}S;ORD~2*yQllfWhJ`FHk?lk zPArS$S7!sCY)aemM!2@GVjU;BJiV~`-a3kopTEKQ`#nh!bz9e>_Di>bt1@QciLOD2 zb{R@lQI?sKB_13{xO)hJ)eBh(WYzuG!=@-u9^HeH6N6~)d>SK%+tG5*7ARGdew+|+ z8eJ~e&S$GZZ0~vK<545QC-4wu8#578B|5&-i8f1zct&lssHwio3AwJM=R7bmF)@() zVU_~4x3~Av4+|*(+o{2iD5v9c$OTR-W}>Uul1{S-CBvwQMw(Xz*R5B;b;I(!J+mfy zhH(BDuOs-<5xA2A0*V)&>zAPF3){e%9b6_l25|J-{{_im!^*GTQ?ZIM>N#-oA0ETv z&)trBcNwhHF6v+@@{FR9$j5@u9Kqou7tnmqW;EP%Rn|;SO}xE}YhmBK1mRyFLE=c4 z!Io{4(#gTuNIA6TnUdMCyBz)~%0<{j2@1vkYl@ep#$vI~&d$y$EV@|=@ZyUv4vvqH zUvRtK%W{91DN?-$unC3-ULpd~>I-o32(paA87;%{&H0)og$VjUFbC(BWpHg<$zTt; zLW860*L%_R=uU7wW2p1^p$LFu!y;6D=@!-x3??1n*?)c-wlg6(R_1B5P=9M77TQ0ANS70=@w zKYsl5zJ2?q%-31#`|$8^;!~gc)U|;?U}M1xfR595P&0 zKE&QSi-ALDU|UcFhiUs#w@aczQQP`pr=oY84HIoW7&v$aDP4nmQ9~AjlhxMFCq}#$ z@J<6c2{r&y<8)#*SRPAQ?7_A8Xw?&0wHO*t1ei7R2fZauvwfs*f-SG!#f7_{`h4a z`Rj)<($+QQ9%@&*V*0(M|@acm{Sk0h0z;)*u1Qs>H(I3URe|{RHrze9E&h0DVy6+lj5)UD+ zBH;I-IZy}gP!CT2lJt*$^rP=9#uTlN#}Gf%!Latz zK_lp9tfqO$=`&SynVW^E8A&hV_@sEgZE+nu_iTXgqnjWvsLpGQM}|gl`p3_r`)98~ z?hB(fP|ez;67!fEiNU_Pm8}>;2HoJ9e}5h$kL^Q^I{-=G;e6j(I5$l8?xLm?vAykR z`q&oMu86w7k3W3~F1H)Lt*ZiK9A!Gq^8eDVTBzZ1?p zp+f=zIyNq5I3aB!id4@C^n_}}*voiT)<7=wey5uZnF$3xZ{&_Xqsf%Du1)Dkxg_v6 zFJ-1|AsHMTeD&*J|N2iuq0kh~oHBCT+S)o*RXtYlh%~b=tN|Zz=3Oos)ri?Ol5R9p z!%2vuy3*a_nb3gOjFLqHd9n7OHE#It-;A0+xeM;?SAfsc7@8sqozK6GgMa!Hj6QV` zb&?Bpb+zm|84E->2FI3VsQSXz^vq9k{#S2c_?LT7HgM)*qlP6CeDt1u0&c`4Dtg0%{bFo^YPS-9qm!`TRLK)vkoI#YS zYr6ZrYfK)48lYo2ucyJkJ!_`v zdUhXr$wYE^pokLV-S^(TJ_{H(_}oEo7ea8aZb=6V;+;dlSR6bl06)0^vq%ncY<7oO zR0eD|uZClOE%eia7<}pQ#`w!9g#+Xz zbw(jY&cQPs-|56!9V#izDOW_ewyr?+|9B7lpS&K@G)WS2T+ja3%Q*g54ZA)jQ#S&l*Mn^tSPk-J;H_XtG4q$cx7s6FCKauQ%%Y&WZHENDrKB9o z`9~jpblSj9-VwNe|Nevd+REn&*{hsWHj$x*Sj!u2g*4o1q*p03h|Si#&IUcj=54Lh zcY7K-coql$*T19p$FIN_=Fm`A$7*Uhi%3g1x?w6C7oq0Mx3Ol}mIRNsqy76k;7N$^ zc)if0Mwc1y``2ZiqyOoB(A$G>wA7}1cTv(U*&Sql6WRD#AxD8{PBNiS6Fo*FLo$uX zrdRwf2;E~CdU0|UplFZlt}CGj>=1PxwKa7}?rz8F2XnqKSxb zX*H;+u13AXi^z+|ap)UA!G))HO`8y-pa)F^m+x;3=H=%l`y-ydO=~b?|w;tYxZ}(J7HL zy02lZcNk;mCgCF^ZJjv%50Aq&p}`mMu>nm>vHQ-eAk|MIL}{PVS5H9UMTjj`kOGFC zng~rm?He;*s=#G&@zYXH3c)2b#JeXuc}h#`nd9rS*)jg=2}F7`zH6z5W9tg2@dWGJ zTw7Jm8fl;Xx978Z{ff)$kn$@A6Glo+uW)pBr`>_Z`UZH%G}a;W&|f@+;bVCNCQPUu zlziP{#+zE&d(qOBW>$~rQiC6y87UIYJQ7U!GdtS+KuL8NyLa#2-QC?i?ZxI*fTKr` zV&~4CZxuT@ar4M48nP;)Xi%3D>(9**%vj8sM&}u29D07alT~izO3y2YaQM$3VzzG; zXJ;KDWl1($gigIyUJ(&(>%qxy{~RNyyAU1-;tXYZ`y%l9eMUYl9EWXnGh8>W$lAAm z$3DoRI3$k?Ld#?@Ezvt-Fg4pGgQS+rHi0OsPHkHgox$|;ljjG()wqqi*P$o|UpSa` zF2~I)AS|e6*Fw*-#^cBMGly~hKVD(q(*QjCPfua|Lg6HFks!T9;W&#b+>Rp#wFFA4U`X6>xw zDsN0uZ|yC+Tn@%g93Sx&t_5>B24Nh6kz_y!aR`JCbZaCuqq%3gw|@Wb9*ySgo*4-_ z=Bt`QNYgz%-T&ivexD@|0#!|L?l+&nrH>AiGVf^k2i?%V`vlo#WbqJTb{BU61*?Df zP;t_r$rfHH@p-W{s7hlibK}7I*=L^}N6qR)NN9wgI&}&sKyRu~(3VXkQbTBhs6?|y zE+lg)qg_BupOg5oK@Uuy7uvUd75=lMsS{3b{unO(pPj_!oseG$7PEzf+V}+exYF;? z#9_NVaN*^haQ-)+g1Xf~`~<8bMZ2FeXo-ESieTLCuWJI=-!Fl|pa;sT1-5j^6!W(A z6KQ|J{Go@pDl&rXEK$*K?}vbx5To@hFjI2LwW3zp;NLEidLs#FqZ@uSo5cP@-T-Lz zTF5h5p?`M`G=6v(F1)rIT$SfR$7qCIE`bU>xqs*z6pV))KLZ`_9)Xk3{t)aFkp6gqwS^y%7U?@2fTcI?=3 z-syCnNcAD?P2+%>G7dtX0I*SEPr1ls7l_GL2QPh#etvV8c{x1Tp5i$e$ccGfDO$h+}$v-<_nN0iFW3Yv@)7RU3;p4bzx~R zLmKQOv&{ZhyU0;Q#j0%5!h^;nBR1vux`#rPnwtxO!~O*fmG-!yd;f6jR9`U-ROLnB z>K`DIPk1j4poY#5j)L=xIxuQ=A`epP%sqZ*i^&Ydx%oh!?}76#ehlZ{+zY-z52;4& z+kXm78XcgvfE+ymX)HuKv`T>!Nb2P!9H2MnC6#`XaPs8IqeqS$Nh%|surTG+sZ-9n zy1L4Wii#g4I{_w1&T`D7w_~FoBi7H25>GfC+al>6a|(a z-wLkN%@7>)lO2g=7|1nPglLLNCA$uf^hxjlTD1o93kqP+IS73_tH9OR0cx57tuq9A zlSzIZFz)giO-2C^LVy(#nEa57Ni`#-oHfF8{P^)otddQJq;bd(Km72BoJM)w{K=r3 zm@9baX+o~=5D8N48IxxKvJ+W(Efi=ikYlrv^KuFYfB1Y6v{2-Dq)Vg$%&X^;*ti6Y ztaR_G2CbI^hSdPCrh%Pa6#l!vdjLY+4pQAoR{ALWIg+RfWoUg(Lt#mEz8RR39OCR^ zjgZC}AiF^1_tD-|0G1WyL@Pk**G7R1wWt%IWMSb+a*!uxBKC+E<>rI>f*q)H-C!^p zMao1O=ON!yMfw&p&ieB__?&LwORPXoPAO-N<fVq%&|XFR=5cfa&|g9p9eg z=fHQm74%3dfJ_B818AlWIciwrgY+YWe2RjYDq2$dAPy2VLjvvB!U-{q5NS{hM5Jgm zn#|xn(Fgf8QtE<~zSy^dM^}eKcrn$M)m$}^eQzeaX@EOJt-G78GC_ z#B5rs6RD>cfNE+kDV|D~Ek*7wAV@WZQkL9i6(NCf+OT{!XfXpGf$qvP5WLU@dXq7n zrq`EE2sfDEnD@4z1B|swArk+O*#YRG&?Jcn4S2vCSr>Fu0Wesk<`a5&v8UJjji3z{ z3Dfsxkb*YO>m&CwTiNz>mJ_uY+>oMlQmuco7ccpA)bJOHBF&vZ3i(pO>-AP{-@ZL% z9t%>6;sL;c0|zcOG&JllEiHXA+52z{XBJKZ|G7?3aUi=>EE3W#fRg~d{zP`8QZ3tz zK>={y0Exy}R?ZoAHvfPN?E6oGfntOR7_L-$HBfm&MOvKC5220$BJd_LR76LChzX<; zo-~>)lsiCpiNb$p8K4Tyz+dPj6AG(5HN9@K^Vnuj8Cqp&71-`u0N%50K>0$#WV#N0 zs}#{y^CwjD35ot&UCo4KBbr2i%qZbIwp->X% zDmIF!Xz*U@0DJuva9r*L_r-Q#o1I|PYs8I@0;Aao_S08Mzi>{3Mwo6|Ay}5rhQaMM zz^m0M_W6-Rvc3SRSWZfaO1TJ|%|>>P$Y;!Z{Xoqw22~UvQv#gZwr$%%xr?N%{=9eI zdFO9WJn_U~tJS(H+551OT(xL244%CTD%9Fw(xb_UHy9%wUpZoEau8Xun9TRUEUW&G z8K7S@ZP@Rk0VjcU|1n%^wb_W@B$R82 zApvg?V0ICxN>YbDaYO#-qmQ;bok??F!ytQeeB+Hb&>2o0SJi1iw{$x2VgAtUV3>oH z1w;>cR2qzdD5q*%UIvi|$j;|lsTZ+JZ_neq>V8I*R;{sO4#;>oDJ||B74HoW2Q;?|C zvB@eaA;l#r-M~}pP#)=AC)mFlUiOZ=jQQSMg)FjvY;w7hYEOnC5yxx2z0ha3L%;ay z?dk^aj9gGnkY)S3wr<_}XS?05=!?pp07zQ&+H0@<1prdEOHx`j7=BnLL^=W)I;>Kg zV-{EcM9x_#G6WakNb`qz&HNa;6vyRua2!2Pl5LWZoNVx7y>+wz2Q4PT6qBRz#JAA% z4A>8xf`EIN019ZQ6@u}uTZF|Zi#u{QffL{9aFAv!9Qb{GebC#{1p{mdRNs>q`RVHF z+K#@e(#uj5AK15V-_=8h4!tYqy}DZ`0Xw&Z*w7h4M$$_O_yiS^Ff73aLYG%XDd^|L zZojztW7Vlfr%fx_M#b|1XrVwZt#3&I@k5ZRvHPpo^~tni4(T0G@+?M^=$0@+FhUqv zC2_QdIIygk1G)*act2flz4g{R?d|P~S4qVIvR7Vt<bc|0z;46A8v@0MqKZ0NB=TrI%j%6#Zg3?=>u+ z1?tiQflWZ+A|5GelqqEMP6k3?SbP&$R>rPAb`;V0rimn=lC9K9uUXP8jxm8$5hbVc zA%zjqi6`Atv>N&kA&tuLE-0K_IS*D4Vq;RSlHj@-F%wV9QnFFSsQ^;LyK&icFfNd1 z`!_T+eE9n7ucz!@OoX&1z}H`Y-Tj9@{NYtmSw1N+b85P89`G7kb~rBF1vG+T&qTlo zjR%6@whvbl!E)ywEO+(;2|dR1NuqnA<4**h-1Qg}2n*nVSM2Ky)kO)1YISTOX+1=f}Th4p6b0>lB2Q!52Etq;B@{vieEMQnmT?$}C#r|bIeDm*b!wuW+2$>fS#(0(0`?a>BkUR*P;dQq-LVfNV0k(^`y%ShC641b?rh~hXjMc>J1w<{Ez15X8CE~ILJue zucoG^W%=^umg3^#rK#SF9GFB|=1eO|Z&T{Tn@^@<4UtGz=8y=qiY9{r8_B(`cIZEG z0kn<~sa2uk*>GZc0UXkY<~B&}em}=QtXqp+=m{(yowgAY-?BS^?2) zjz?V?2fnx7dh6d`fBp62nO;gpb%HLJEA)$B{NfFc<7Cxdv$Kl9aL-(kyOWFS56Fqs z$c0?THyTm{h*qP8Jc|tq3^p)nMP8aX>pw41K}LlRu>>M+nLVTE3V~+bmMBZNI96~9*DaHMu?>UX9|*ULA(E-W`ad#!V4e8#~*+En-4$yFl9N8 zTx3P!+wFFT8X6kTuUoh7dkn+GSDlCe+Ii>{`_HxmH{c;evG~A4S#*4}nL~hx#{Fmz z%5Jo+6ReKp7^L+G1W4wX!b%G>3)Z{K$@i9kgMaXN190uv1SJ{KuZW2jNp?A5S(E=l z9v={Ey28yUn5Quqh|?~Ys}qmMZaujzj&Wb15CjL@z?M&hynkIjj94rg8XFtmfB4~t zUvfAcS#uJzrg4Ay^5uT3)!KFY?YE=2MoLcx#UfO>2JoG15w=1qc?>+vXDLTPFb&67 z4x&5N@OoH@v^B}`B$6Ulv*Onbu{6Ns9FlRygq7dv0sp`txT~)K^twnWQ(6e(b;k+6 zQ>E2wK?YwiNUjI38z--#@2^sU4<|qd_ZLeY)FnBP|KEN{6!>N0?8c27fBN^o|2; zUiq)T{`$=Q_uoImXf!63T9tqr1DrnuzOz@w8kyKj!Uu#QNVC-nUXMqXZJ*BSsI*f} z3VS9U{GxAzW&@ezcrJ{rctgPYDbmcMBQFYAS`%ili$=EC9Vk&hnTTxb^@K+V4I8$k6VXeBtWE$Bn&>@UU0oN~tXZ=R0Ax}4 zqRs$ri@x(60*Mnne5J)z3Rf#E(jp9DUZLiFH0x9%K^$L(Ap=OxM8sVyG9Q3(dRD`d zFsbyy|1+Y$5YH5hVUx}Pgl;^8Iv?WHWAauml_f@@upx)0An)-z!2D0Q$o{O}-roQB z&_fSB+uq*pmhpDQ$od38)YjH^Xf&FuOP4NPi3??__oIoRE1Lj8yNhItsW3x=FxaqN z4ii(nD3XS&&*%wm#|L&{rD8lIoR-00*lCn|y)j`+@#n0NTxF@ohY=lOIT>OiNDctG z|7j^$SI(9F8G%6HzLPdN}%R>z9D-o&`!iqVw5jpWV21>(;L`zStxfu?YZz?N*1S5(NbX zNtNd%;3QznCXmo5*$EgLtU0zJX`BeB5W|XO3(m66STaZtnuI;$geQl67qNUG8NpYo z~%iH3TGt-hco7r#Efd z^p}h;GzCU<0yv#c|Ce8WS##fg_m!B<=2>!%L4F-wc?kp^E@0XQ2|ICg?Yv6VjzV7` zG=h4=@gRjglR#jfh=>gxONzkd$PvQy+7gJlA`TPG8ad#d@GP(c|{ zJSSyeL*gKVQE-SQ^8s!DR3aeK>K}JyRDgK9053y?Iv(&CRxH=ZCK$0=G;*gym3fq@ z&HFFk2g?s*88wojrl#iAwQJY@Z@b-|oY+5081V@J0?YKZhK7dPb?eqGq$o-jy)Vgj zPA?!l`+Q?Bkrs`n#l}bs*#$<)Att5HDTE%R)!*y&X2(Z~SI2|*M!`T)D31Vxr@lY6X?To?fq4(N(0JRH|4pa$bQ$ zJff|Ql)&Au3UZ)XI0^Fp`*P68qcRDjqoZT@Bab}t)8ogFx5zvsYZ%oDfT*snZt3su zzp!%U%G&`z7U3g;%FzRq<^c?N0ooj5{QyOZyl~)qyxy>rm~D`-r>Ik#3R2^U6t0!? z8dm2NZ;(pQz*#R~KZeZ`f zFbt!}YohXv05^{#y8z!l01V0x(EvVQc6I@20HN(a(h(*~jOql_?7NkE4x-N~HS@@Q zNkw~D>4|=fFY@K{fzRUwcXuBI7y#Ys*({UUpH)>=t%?uM zGOp_cz~^-4%$c@h$BrFezI^!vlgXqgZbnR^+2a9n^#EN?pq+m50E1j+yMVOXlX{;F z^lgQgwlR;Jz7EJ|Gi5ub}aLq-u(Tv}pNy?aw zw@;64=9h<-mtTJQnT;DazT-tlZ46<|OaKroIYt==$HL)o9DVAkr=EK0rI+?`95?1vI>yulAY=i-VCdk%gJ(NCJ8LQ`Dkkdn zdikwGBG5Kvrxk$iwyB^puw)X1`dz@|jzA}#2og0oI=YlJyRhZ7R{DJj8UCHNEq}e4sOwbcK&2AuAL9Ydv68JP5B8~Ib@>m;K75h zZP>8kxm~+6z31A%UDXKO z#cp7oK|vf3rGzx`O{CF+R8~Fcbf#qrVf89G23LFR$qj^n(zC#n*npiq5w!D4Ks`lv z6-rVJ4i0{`dGqF1e*4?s9tZ}5uIqNw(!jTB0*F+O>ih1yZ_bM^zWA@RX3csS&Gu}Z znCo{z=t4IH>N|kD)D7Guh7>o*O z)I{%0Gh;lkWyPSGKAt2HvxJU=2M@me+;h*pQ&m;fBJ#tI`Bgs>-?j+=;w0d6a&oNC zJoC(|C!c)szgn%X02>pdbu|7s6IEX6MsN6x<;2?<# z#q>W*gFP4`Ha(6#bnaC~7MMaC=w=oX${A*I9?({K%*!$nxOnm6j?J4l|Nh-~-#r!z zg@R%P`CB#k;afTZB0(=)xbUV;n>Ic0;DZl74gfc0`vb5+pS#*mCPb*EAGjVT@cnM! z-F{(0;LgB^#J2de3|@aFKn@|l-v_Qimynqy4s1wJdqmD#H3KTM7U*0fsEci&nVL`R zc}xgm2sqn7bLY)B-~8h%ue`GF>eZ{A-^M{7iEsM^0MU_WSFc_@^O3WYXnjE-h1!8`|7K&ZmX`YzA8GoBi^(= z()dT103gx4vn;D!yLRn@pZw$}4=rB2_+9{zUuK;ykl5Gh1s->bYaZb7HQ)x$;U$v- zO($eUBg%}Z=px$ZVcA$|lBh)GNF)N#&_qUxGH8G@vOuA5goy>ts3z5ywACOqo@9Bq zi3gw0SGQ-+o=;wT?X?|8jvTolnmXS~b0;4DQ6_){GMreJwLJRhqw^no?6Eb=mMyzy zBzJ_QK-)aP3ABKL(2%W;lR)CR#KAANc#`T$G^a30r(pycsq~Xk8Nu)O*Y4S~XUEp9 zTX*l@zrP+(JTomJ*BiJVzz3zKD9ZA{0}m|Oy?gg&p66@%8%TwK)9F0AWy_YI-*Lws zNZ_gaHsp$KATen1rCYps@tiGNw*2Jk)vNn?o*%d|6^5bx?Afz_e*XFA*H525eTrC# zOJ_?#Xe{F&%>+sWY3pW-i;IgMdE}9Kt5&UAzGTUgWemg2_%1x?IPlo*_S2P>m0#@M zz5CGi?c3{}PG^tk*nVeBo$ty7hy*RJaN4`>x@+RnrAsT;u3fvVtgH;FTBePi%N!8| zg27C;Ooikcut5q}Ken#bd5IeYf( zx!T&=lhxJLwFeFyxZKp#)Wvb!;CIc$Nrf8|AQn`FnZ)Gh=NsqGpFe);)TyQO=FOWi zfByW^Y15`nH5!d5a8-Z}WY_Upu}G++udlE9!i5Ws$B!SsaOTXJ3zsinZmh1Z?smJ~ zZm~Wm^p6y~&n9k6faFj`ILJ77=$xD!Lw@?=|meSLdh zUtd>OS683Y>GX)#NkyFF#-LAv8xueoB25QM9M~F_N~Kk+)q0!FW+*8sF;1E^$y`)a zWX{dawV2IjlTN2I>U25-%d$F#VKfZGs2Sue6cGpn&^g6_d_JGg?eTbAZnt}|zrWvM zx7!EW+S(k=&CSl%)>h}>;2=&2T-m+i>dr+x#~XuPA#O~7>_EDYs)&gJ5lJK};`i|e zYvk`FQzi1n#rfL}WC1rO!03UJdB1NA@Ef>+8yE!u!2buUUY=rm%N67R0000YMP)?= z1O$K<=BD-n0)n+a_A!yelXun{$^rsE|6^eacZ`@^o{6gUa>5DRGx2`<)%*{W-(fiE zKNZgd&b|Bnp~hRX`A=CwbJ~tFFaEyeo|pUP4EcicV1wv|i;gmvUVb}SdG@R=&h?^h z3PSUksrkt}uuFf~%EQT?&f}||K|(rx9lY30_TJXsozA%7iJ(FQFNgw*A)ZB;o5OXk z2W9E{7_j|*?Y#`4wVAHYryQ%j!apO!ra!3)N5t{n=S%-`Z&9H|1ggSHaeG=c{YVqE z0nrZ>c$u-m#RjYlJ1__6P(^4W9s;ScgAR=zMOIH2>yAx`HB{r5^EgmL@|bsD=u7Gu zgacoB7^h};0J>#HNEt$s)qtqv*4c|ndX;#H76lzv<;Vxk6@#g{Gq4d5%WWY>Gi3f= zIKV2{dnC-DVoc|KC3NFn1|W?&GD3yrhBQpQn1h|7bczqvxu=CR)Jw7gbC+QwvaIEW zC>4WTKfgc&MmiUJlQ7QQ7}Hg!Ap(tTH@Vv9u#mW7!+x8dHoaYZt4=L{l<%ypU!D4= zAS@TennL1&=;?wmIgrc5%GX_FM5SRm$E04c%mXlGjC)%@wcw!V01?0j7n9{7EPdk=@ym z$AP&CIX2?G3azQ~&F_9DKcX+*Yo?D#h zeA!&ib)-h(S91c||CGiw5S6!M8UOe&d_fPoP1qgv7Ba~8Q*sj)a{=i8HuEbZsa{lu zz-=@kWR7|Y?HSQ%0n!>w;F9us#<{QLC86YcoYnBR1owfTyprh81G;RrC}Esl?1HMv zyb`o29Syq=(7zTFAfx&e4fE$uUZg#Gbh>4=KVyZb+cw~u&Y>qu?u{B68uE``QQG9r zmop-I-|3yLz{~j*d`H3pl^lfgr7-YvghZHlBpOn-tQ_R`!kd!$ea{=!*s5=R#cH z-w1Iv^D>#dtn;Vvc&R1_74NQLpe(P71gUjM=#4Y)q2ZEHM?~zI{U!rX9NTM&AWKD& zRIFnXMQePHcG5+0TeG)#;q}O}4)o5u8|2r*dn4MHKJkvE;lc?nL07p4^g0(ti$qOd z7G<#R+0qe+BXeJs7NmU%6*9-tL`>&b9%g`^JST1Uz_w8UNEKy?+`vpqU{b|pHs`^^ zOy72g#If!7q-y?+iQ`q2vKU=#xG*JW@36RQJ+$r7Kl0zN1}?qeOpvO-=|iob7Q=kZ z&;#HH%r!#0!Y3I8jiWidEi*IP7UD6bbASGI7)sp(zbVzYY8zrxL3tuVe`^QbFHLY! zu#-^Bj5!U65BGn8)`lVC>Y&Zf8rlFtB_ z)|g__N9i>0a%zB+Q*h3cNW}I$Tg3Lki5X{!^g@UdZ2)-J_jP}rAEQ0G?Yy7+Nv*sq z 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 F@PB1o0BisN diff --git a/osu.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png b/osu.Android/Resources/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index d8b832e7f7a81d3d69ad5c0b3ea88dfd739e36c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30047 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 z 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 - - #2c3e50 - #1B3147 - #3498db - diff --git a/osu.Android/Resources/values/ic_launcher_background.xml b/osu.Android/Resources/values/ic_launcher_background.xml deleted file mode 100644 index 6ec24e6413..0000000000 --- a/osu.Android/Resources/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #2C3E50 - \ No newline at end of file diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 4a1dccd758..bfde981207 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -27,7 +27,6 @@ - @@ -47,27 +46,6 @@ - - - - - - - - - - - - - - - - - - - - - {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} @@ -96,6 +74,7 @@ + \ No newline at end of file From 6ba7e2b670da39a224b1ee9428553683485651a3 Mon Sep 17 00:00:00 2001 From: jorolf Date: Fri, 11 Jan 2019 01:12:19 +0100 Subject: [PATCH 0338/2854] split the profile header into several components --- osu.Game.Tests/Visual/TestCaseUserProfile.cs | 76 +- .../Visual/TestCaseUserProfileHeader.cs | 76 ++ .../Profile/Header/BottomHeaderContainer.cs | 160 ++++ .../Profile/Header/CenterHeaderContainer.cs | 321 +++++++ .../Profile/Header/DetailHeaderContainer.cs | 238 +++++ .../Profile/Header/MedalHeaderContainer.cs | 132 +++ .../Profile/Header/TopHeaderContainer.cs | 210 +++++ osu.Game/Overlays/Profile/ProfileHeader.cs | 892 +----------------- 8 files changed, 1197 insertions(+), 908 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs create mode 100644 osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs create mode 100644 osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs create mode 100644 osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs create mode 100644 osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs create mode 100644 osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs index cff55c2506..3753328427 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs @@ -32,6 +32,44 @@ namespace osu.Game.Tests.Visual typeof(SupporterIcon) }; + public static readonly User TEST_USER = new User + { + Username = @"Somebody", + Id = 1, + Country = new Country { FullName = @"Alien" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg", + JoinDate = DateTimeOffset.Now.AddDays(-1), + LastVisit = DateTimeOffset.Now, + ProfileOrder = new[] { "me" }, + Statistics = new UserStatistics + { + Ranks = new UserStatistics.UserRanks { Global = 2148, Country = 1 }, + PP = 4567.89m, + Level = new UserStatistics.LevelInfo + { + Current = 727, + Progress = 69, + } + }, + RankHistory = new User.RankHistoryData + { + Mode = @"osu", + Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray() + }, + Badges = new[] + { + new Badge + { + AwardedAt = DateTimeOffset.FromUnixTimeSeconds(1505741569), + Description = "Outstanding help by being a voluntary test subject.", + ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg" + } + }, + Title = "osu!volunteer", + Colour = "ff0000", + Achievements = new User.UserAchievement[0], + }; + public TestCaseUserProfile() { Add(profile = new TestUserProfileOverlay()); @@ -47,43 +85,7 @@ namespace osu.Game.Tests.Visual { base.LoadComplete(); - AddStep("Show offline dummy", () => profile.ShowUser(new User - { - Username = @"Somebody", - Id = 1, - Country = new Country { FullName = @"Alien" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg", - JoinDate = DateTimeOffset.Now.AddDays(-1), - LastVisit = DateTimeOffset.Now, - ProfileOrder = new[] { "me" }, - Statistics = new UserStatistics - { - Ranks = new UserStatistics.UserRanks { Global = 2148, Country = 1 }, - PP = 4567.89m, - Level = new UserStatistics.LevelInfo - { - Current = 727, - Progress = 69, - } - }, - RankHistory = new User.RankHistoryData - { - Mode = @"osu", - Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray() - }, - Badges = new[] - { - new Badge - { - AwardedAt = DateTimeOffset.FromUnixTimeSeconds(1505741569), - Description = "Outstanding help by being a voluntary test subject.", - ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg" - } - }, - Title = "osu!volunteer", - Colour = "ff0000", - Achievements = new User.UserAchievement[0], - }, false)); + AddStep("Show offline dummy", () => profile.ShowUser(TEST_USER, false)); checkSupporterTag(false); diff --git a/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs b/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs new file mode 100644 index 0000000000..c80227f149 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs @@ -0,0 +1,76 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Profile; +using osu.Game.Overlays.Profile.Header; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseUserProfileHeader : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ProfileHeader), + typeof(RankGraph), + typeof(LineGraph), + typeof(SupporterIcon) + }; + + [Resolved] + private APIAccess api { get; set; } + + private readonly ProfileHeader header; + + public TestCaseUserProfileHeader() + { + header = new ProfileHeader(); + Add(header); + + AddStep("Show offline dummy", () => header.User = TestCaseUserProfile.TEST_USER); + + AddStep("Show null dummy", () => header.User = new User + { + Username = "Null" + }); + + addOnlineStep("Show ppy", new User + { + Username = @"peppy", + Id = 2, + IsSupporter = true, + Country = new Country { FullName = @"Australia", FlagName = @"AU" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg" + }); + + addOnlineStep("Show flyte", new User + { + Username = @"flyte", + Id = 3103765, + Country = new Country { FullName = @"Japan", FlagName = @"JP" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" + }); + } + + private void addOnlineStep(string name, User fallback) + { + AddStep(name, () => + { + if (api.IsLoggedIn) + { + var request = new GetUserRequest(fallback.Id); + request.Success += user => header.User = user; + api.Queue(request); + } + else + header.User = fallback; + }); + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs new file mode 100644 index 0000000000..a3a6447a9f --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -0,0 +1,160 @@ +// Copyright (c) 2007-2018 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; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Users; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Profile.Header +{ + public class BottomHeaderContainer : Container + { + private LinkFlowContainer bottomTopLinkContainer; + private LinkFlowContainer bottomLinkContainer; + private Color4 linkBlue, communityUserGrayGreenLighter; + + private User user; + public User User + { + get => user; + set + { + if (user == value) return; + user = value; + updateDisplay(); + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.CommunityUserGrayGreenDarker, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 }, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + bottomTopLinkContainer = new LinkFlowContainer(text => text.TextSize = 12) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + bottomLinkContainer = new LinkFlowContainer(text => text.TextSize = 12) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } + } + } + }; + + linkBlue = colours.BlueLight; + communityUserGrayGreenLighter = colours.CommunityUserGrayGreenLighter; + } + + private void updateDisplay() + { + void bold(SpriteText t) => t.Font = @"Exo2.0-Bold"; + void addSpacer(OsuTextFlowContainer textFlow) => textFlow.AddArbitraryDrawable(new Container { Width = 15 }); + + bottomTopLinkContainer.Clear(); + bottomLinkContainer.Clear(); + + if (user == null) return; + + if (user.JoinDate.ToUniversalTime().Year < 2008) + { + bottomTopLinkContainer.AddText("Here since the beginning"); + } + else + { + bottomTopLinkContainer.AddText("Joined "); + bottomTopLinkContainer.AddText(new DrawableDate(user.JoinDate), bold); + } + + addSpacer(bottomTopLinkContainer); + + if (user.PlayStyles?.Length > 0) + { + bottomTopLinkContainer.AddText("Plays with "); + bottomTopLinkContainer.AddText(string.Join(", ", user.PlayStyles.Select(style => style.GetDescription())), bold); + + addSpacer(bottomTopLinkContainer); + } + + if (user.LastVisit.HasValue) + { + bottomTopLinkContainer.AddText("Last seen "); + bottomTopLinkContainer.AddText(new DrawableDate(user.LastVisit.Value), bold); + + addSpacer(bottomTopLinkContainer); + } + + bottomTopLinkContainer.AddText("Contributed "); + bottomTopLinkContainer.AddLink($@"{user.PostCount:#,##0} forum posts", $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: bold); + + void tryAddInfo(FontAwesome icon, string content, string link = null) + { + if (string.IsNullOrEmpty(content)) return; + + bottomLinkContainer.AddIcon(icon, text => + { + text.TextSize = 10; + text.Colour = communityUserGrayGreenLighter; + }); + if (link != null) + { + bottomLinkContainer.AddLink(" " + content, link, creationParameters: text => + { + bold(text); + text.Colour = linkBlue; + }); + } + else + { + bottomLinkContainer.AddText(" " + content, bold); + } + addSpacer(bottomLinkContainer); + } + + string websiteWithoutProtcol = user.Website; + if (!string.IsNullOrEmpty(websiteWithoutProtcol)) + { + int protocolIndex = websiteWithoutProtcol.IndexOf("//", StringComparison.Ordinal); + if (protocolIndex >= 0) + websiteWithoutProtcol = websiteWithoutProtcol.Substring(protocolIndex + 2); + } + + tryAddInfo(FontAwesome.fa_map_marker, user.Location); + tryAddInfo(FontAwesome.fa_heart_o, user.Interests); + tryAddInfo(FontAwesome.fa_suitcase, user.Occupation); + bottomLinkContainer.NewLine(); + if (!string.IsNullOrEmpty(user.Twitter)) + tryAddInfo(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}"); + tryAddInfo(FontAwesome.fa_gamepad, user.Discord); //todo: update fontawesome to include discord logo + tryAddInfo(FontAwesome.fa_skype, user.Skype, @"skype:" + user.Skype + @"?chat"); + tryAddInfo(FontAwesome.fa_lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}"); + tryAddInfo(FontAwesome.fa_link, websiteWithoutProtcol, user.Website); + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs new file mode 100644 index 0000000000..9005fd5ab2 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs @@ -0,0 +1,321 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Online.Chat; +using osu.Game.Users; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Profile.Header +{ + public class CenterHeaderContainer : Container + { + public readonly BindableBool DetailsVisible = new BindableBool(); + + private OsuSpriteText followerText; + private ProfileHeaderButton messageButton; + private OsuSpriteText levelBadgeText; + + private Bar levelProgressBar; + private OsuSpriteText levelProgressText; + + private ProfileHeader.OverlinedInfoContainer hiddenDetailGlobal, hiddenDetailCountry; + + [Resolved(CanBeNull = true)] + private ChannelManager channelManager { get; set; } + + [Resolved(CanBeNull = true)] + private UserProfileOverlay userOverlay { get; set; } + + [Resolved(CanBeNull = true)] + private ChatOverlay chatOverlay { get; set; } + + [Resolved] + private APIAccess apiAccess { get; set; } + + private User user; + public User User + { + get => user; + set + { + if (user == value) return; + user = value; + updateDisplay(); + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, TextureStore textures) + { + Container hiddenDetailContainer, expandedDetailContainer; + SpriteIcon expandButtonIcon; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.CommunityUserGrayGreenDark + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Padding = new MarginPadding { Vertical = 10 }, + Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, + Spacing = new Vector2(10, 0), + Children = new Drawable[] + { + new ProfileHeaderButton + { + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Direction = FillDirection.Horizontal, + Padding = new MarginPadding { Right = 10 }, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.fa_user, + FillMode = FillMode.Fit, + Size = new Vector2(50, 14) + }, + followerText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + TextSize = 16, + Font = "Exo2.0-Bold" + } + } + } + } + }, + messageButton = new ProfileHeaderButton + { + Alpha = 0, + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.fa_envelope, + FillMode = FillMode.Fit, + Size = new Vector2(50, 14) + }, + } + }, + } + }, + new Container + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Y, + Padding = new MarginPadding { Vertical = 10 }, + Width = UserProfileOverlay.CONTENT_X_MARGIN, + Child = new ProfileHeaderButton + { + RelativeSizeAxes = Axes.Y, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = DetailsVisible.Toggle, + Children = new Drawable[] + { + expandButtonIcon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(20), + Icon = FontAwesome.fa_chevron_up, + }, + } + }, + }, + new Container + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Right = UserProfileOverlay.CONTENT_X_MARGIN }, + Children = new Drawable[] + { + new ProfileHeader.HasTooltipContainer + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Size = new Vector2(40), + TooltipText = "Level", + Children = new Drawable[] + { + new Sprite + { + RelativeSizeAxes = Axes.Both, + Texture = textures.Get("Profile/levelbadge"), + Colour = colours.Yellow, + }, + levelBadgeText = new OsuSpriteText + { + TextSize = 20, + Font = "Exo2.0-Medium", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } + }, + expandedDetailContainer = new ProfileHeader.HasTooltipContainer + { + TooltipText = "Progress to next level", + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Width = 200, + Height = 6, + Margin = new MarginPadding { Right = 50 }, + Children = new Drawable[] + { + new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Child = levelProgressBar = new Bar + { + RelativeSizeAxes = Axes.Both, + BackgroundColour = Color4.Black, + Direction = BarDirection.LeftToRight, + AccentColour = colours.Yellow + } + }, + levelProgressText = new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.TopRight, + Font = "Exo2.0-Bold", + TextSize = 12, + } + } + }, + hiddenDetailContainer = new FillFlowContainer + { + Direction = FillDirection.Horizontal, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Width = 200, + AutoSizeAxes = Axes.Y, + Alpha = 0, + Spacing = new Vector2(10, 0), + Margin = new MarginPadding { Right = 50 }, + Children = new[] + { + hiddenDetailGlobal = new ProfileHeader.OverlinedInfoContainer + { + Title = "Global Ranking", + LineColour = colours.Yellow + }, + hiddenDetailCountry = new ProfileHeader.OverlinedInfoContainer + { + Title = "Country Ranking", + LineColour = colours.Yellow + }, + } + } + } + } + }; + + DetailsVisible.ValueChanged += newValue => expandButtonIcon.Icon = newValue ? FontAwesome.fa_chevron_down : FontAwesome.fa_chevron_up; + DetailsVisible.ValueChanged += newValue => hiddenDetailContainer.Alpha = newValue ? 1 : 0; + DetailsVisible.ValueChanged += newValue => expandedDetailContainer.Alpha = newValue ? 0 : 1; + } + + private void updateDisplay() + { + followerText.Text = user.FollowerCount?.Length > 0 ? user.FollowerCount[0].ToString("#,##0") : "0"; + + if (!user.PMFriendsOnly && apiAccess.LocalUser.Value.Id != user.Id) + { + messageButton.Show(); + messageButton.Action = () => + { + channelManager?.OpenPrivateChannel(user); + userOverlay?.Hide(); + chatOverlay?.Show(); + }; + } + else + { + messageButton.Hide(); + } + + levelBadgeText.Text = user.Statistics?.Level.Current.ToString() ?? "0"; + levelProgressBar.Length = user.Statistics?.Level.Progress / 100f ?? 0; + levelProgressText.Text = user.Statistics?.Level.Progress.ToString("0'%'"); + + hiddenDetailGlobal.Content = user?.Statistics?.Ranks.Global?.ToString("#,##0") ?? "-"; + hiddenDetailCountry.Content = user?.Statistics?.Ranks.Country?.ToString("#,##0") ?? "-"; + + } + + private class ProfileHeaderButton : OsuHoverContainer + { + private readonly Box background; + private readonly Container content; + + protected override Container Content => content; + + protected override IEnumerable EffectTargets => new[] { background }; + + public ProfileHeaderButton() + { + HoverColour = Color4.Black.Opacity(0.75f); + IdleColour = Color4.Black.Opacity(0.7f); + AutoSizeAxes = Axes.X; + + base.Content.Add(new CircularContainer + { + Masking = true, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + content = new Container + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 10 }, + } + } + }); + } + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs new file mode 100644 index 0000000000..678fc2ddb7 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -0,0 +1,238 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// 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; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Scoring; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Overlays.Profile.Header +{ + public class DetailHeaderContainer : Container + { + private ProfileHeader.HasTooltipContainer totalPlayTimeTooltip; + private ProfileHeader.OverlinedInfoContainer totalPlayTimeInfo, medalInfo, ppInfo; + private readonly Dictionary scoreRankInfos = new Dictionary(); + private ProfileHeader.OverlinedInfoContainer detailGlobalRank, detailCountryRank; + private RankGraph rankGraph; + + private User user; + public User User + { + get => user; + set + { + if (user == value) return; + user = value; + updateDisplay(); + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.CommunityUserGrayGreenDarkest, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10, 0), + Children = new Drawable[] + { + totalPlayTimeTooltip = new ProfileHeader.HasTooltipContainer + { + AutoSizeAxes = Axes.Both, + TooltipText = "0 hours", + Child = totalPlayTimeInfo = new ProfileHeader.OverlinedInfoContainer + { + Title = "Total Play Time", + LineColour = colours.Yellow, + }, + }, + medalInfo = new ProfileHeader.OverlinedInfoContainer + { + Title = "Medals", + LineColour = colours.GreenLight, + }, + ppInfo = new ProfileHeader.OverlinedInfoContainer + { + Title = "pp", + LineColour = colours.Red, + }, + } + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Direction = FillDirection.Horizontal, + Children = new[] + { + scoreRankInfos[ScoreRank.XH] = new ScoreRankInfo(ScoreRank.XH), + scoreRankInfos[ScoreRank.X] = new ScoreRankInfo(ScoreRank.X), + scoreRankInfos[ScoreRank.SH] = new ScoreRankInfo(ScoreRank.SH), + scoreRankInfos[ScoreRank.S] = new ScoreRankInfo(ScoreRank.S), + scoreRankInfos[ScoreRank.A] = new ScoreRankInfo(ScoreRank.A), + } + } + } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Right = 130 }, + Children = new Drawable[] + { + rankGraph = new RankGraph + { + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + Width = 130, + Anchor = Anchor.TopRight, + Direction = FillDirection.Vertical, + Padding = new MarginPadding { Horizontal = 10 }, + Spacing = new Vector2(0, 20), + Children = new Drawable[] + { + detailGlobalRank = new ProfileHeader.OverlinedInfoContainer(true, 110) + { + Title = "Global Ranking", + LineColour = colours.Yellow, + }, + detailCountryRank = new ProfileHeader.OverlinedInfoContainer(false, 110) + { + Title = "Country Ranking", + LineColour = colours.Yellow, + }, + } + } + } + }, + } + }, + }; + } + + private void updateDisplay() + { + medalInfo.Content = user?.Achievements?.Length.ToString() ?? "0"; + ppInfo.Content = user?.Statistics?.PP?.ToString("#,##0") ?? "0"; + + string formatTime(int? secondsNull) + { + if (secondsNull == null) return "0h 0m"; + + int seconds = secondsNull.Value; + string time = ""; + + int days = seconds / 86400; + seconds -= days * 86400; + if (days > 0) + time += days + "d "; + + int hours = seconds / 3600; + seconds -= hours * 3600; + time += hours + "h "; + + int minutes = seconds / 60; + time += minutes + "m"; + + return time; + } + + totalPlayTimeInfo.Content = formatTime(user?.Statistics?.PlayTime); + totalPlayTimeTooltip.TooltipText = (user?.Statistics?.PlayTime ?? 0) / 3600 + " hours"; + + foreach (var scoreRankInfo in scoreRankInfos) + scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount.GetForScoreRank(scoreRankInfo.Key) ?? 0; + + detailGlobalRank.Content = user?.Statistics?.Ranks.Global?.ToString("#,##0") ?? "-"; + detailCountryRank.Content = user?.Statistics?.Ranks.Country?.ToString("#,##0") ?? "-"; + + rankGraph.User.Value = user; + } + + private class ScoreRankInfo : CompositeDrawable + { + private readonly ScoreRank rank; + private readonly Sprite rankSprite; + private readonly OsuSpriteText rankCount; + + public int RankCount + { + set => rankCount.Text = value.ToString("#,##0"); + } + + public ScoreRankInfo(ScoreRank rank) + { + this.rank = rank; + + AutoSizeAxes = Axes.Both; + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + Width = 56, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + rankSprite = new Sprite + { + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit + }, + rankCount = new OsuSpriteText + { + Font = "Exo2.0-Bold", + TextSize = 12, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + rankSprite.Texture = textures.Get($"Grades/{rank.GetDescription()}"); + } + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs new file mode 100644 index 0000000000..74f1e2f689 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs @@ -0,0 +1,132 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Graphics; +using osu.Game.Users; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Profile.Header +{ + public class MedalHeaderContainer : Container + { + private FillFlowContainer badgeFlowContainer; + + private User user; + public User User + { + get => user; + set + { + if (user == value) return; + user = value; + updateDisplay(); + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Alpha = 0; + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.CommunityUserGrayGreenDarkest, + }, + new Container //artificial shadow + { + RelativeSizeAxes = Axes.X, + Height = 3, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new ColourInfo + { + TopLeft = Color4.Black.Opacity(0.2f), + TopRight = Color4.Black.Opacity(0.2f), + BottomLeft = Color4.Black.Opacity(0), + BottomRight = Color4.Black.Opacity(0) + } + }, + }, + badgeFlowContainer = new FillFlowContainer + { + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = 5 }, + Spacing = new Vector2(10, 10), + Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 }, + } + }; + } + + private void updateDisplay() + { + var badges = User.Badges; + badgeFlowContainer.Clear(); + if (badges?.Length > 0) + { + Show(); + for (var index = 0; index < badges.Length; index++) + { + int displayIndex = index; + LoadComponentAsync(new DrawableBadge(badges[index]), asyncBadge => + { + badgeFlowContainer.Add(asyncBadge); + + // load in stable order regardless of async load order. + badgeFlowContainer.SetLayoutPosition(asyncBadge, displayIndex); + }); + } + } + else + { + Hide(); + } + } + + private class DrawableBadge : CompositeDrawable, IHasTooltip + { + public static readonly Vector2 DRAWABLE_BADGE_SIZE = new Vector2(86, 40); + + private readonly Badge badge; + + public DrawableBadge(Badge badge) + { + this.badge = badge; + Size = DRAWABLE_BADGE_SIZE; + } + + [BackgroundDependencyLoader] + private void load(LargeTextureStore textures) + { + InternalChild = new Sprite + { + FillMode = FillMode.Fit, + RelativeSizeAxes = Axes.Both, + Texture = textures.Get(badge.ImageUrl), + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + InternalChild.FadeInFromZero(200); + } + + public string TooltipText => badge.Description; + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs new file mode 100644 index 0000000000..b003f08b95 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -0,0 +1,210 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Overlays.Profile.Header +{ + public class TopHeaderContainer : Container + { + public SupporterIcon SupporterTag; + private UpdateableAvatar avatar; + private OsuSpriteText usernameText; + private ExternalLinkButton openUserExternally; + private OsuSpriteText titleText; + private DrawableFlag userFlag; + private OsuSpriteText userCountryText; + private FillFlowContainer userStats; + + private const float avatar_size = 110; + + private User user; + public User User + { + get => user; + set + { + if (user == value) return; + user = value; + updateDisplay(); + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.CommunityUserGrayGreenDarker, + }, + new FillFlowContainer + { + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, + Height = avatar_size, + AutoSizeAxes = Axes.X, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Children = new[] + { + avatar = new UpdateableAvatar + { + Size = new Vector2(avatar_size), + Masking = true, + CornerRadius = avatar_size * 0.25f, + OpenOnClick = { Value = false }, + }, + new Container + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Padding = new MarginPadding { Left = 10 }, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + usernameText = new OsuSpriteText + { + Font = "Exo2.0-Regular", + TextSize = 24 + }, + openUserExternally = new ExternalLinkButton + { + Margin = new MarginPadding { Left = 5 }, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + } + }, + new FillFlowContainer + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + titleText = new OsuSpriteText + { + TextSize = 18, + Font = "Exo2.0-Regular" + }, + SupporterTag = new SupporterIcon + { + Height = 20, + Margin = new MarginPadding { Top = 5 } + }, + new Box + { + RelativeSizeAxes = Axes.X, + Height = 1.5f, + Margin = new MarginPadding { Top = 10 }, + Colour = colours.CommunityUserGrayGreenLighter, + }, + new Container + { + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = 5 }, + Children = new Drawable[] + { + userFlag = new DrawableFlag + { + Size = new Vector2(30, 20) + }, + userCountryText = new OsuSpriteText + { + Font = "Exo2.0-Regular", + TextSize = 17.5f, + Margin = new MarginPadding { Left = 40 }, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Colour = colours.CommunityUserGrayGreenLighter, + } + } + }, + } + } + } + } + } + }, + userStats = new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Y, + Width = 300, + Margin = new MarginPadding { Right = UserProfileOverlay.CONTENT_X_MARGIN }, + Padding = new MarginPadding { Vertical = 15 }, + Spacing = new Vector2(0, 2) + } + }; + } + + private void updateDisplay() + { + avatar.User = User; + usernameText.Text = user.Username; + openUserExternally.Link = $@"https://osu.ppy.sh/users/{user.Id}"; + userFlag.Country = user.Country; + userCountryText.Text = user.Country?.FullName ?? "Alien"; + SupporterTag.SupporterLevel = user.SupportLevel; + titleText.Text = user.Title; + titleText.Colour = OsuColour.FromHex(user.Colour ?? "fff"); + + userStats.Clear(); + if (user.Statistics != null) + { + userStats.Add(new UserStatsLine("Ranked Score", user.Statistics.RankedScore.ToString("#,##0"))); + userStats.Add(new UserStatsLine("Hit Accuracy", Math.Round(user.Statistics.Accuracy, 2).ToString("#0.00'%'"))); + userStats.Add(new UserStatsLine("Play Count", user.Statistics.PlayCount.ToString("#,##0"))); + userStats.Add(new UserStatsLine("Total Score", user.Statistics.TotalScore.ToString("#,##0"))); + userStats.Add(new UserStatsLine("Total Hits", user.Statistics.TotalHits.ToString("#,##0"))); + userStats.Add(new UserStatsLine("Maximum Combo", user.Statistics.MaxCombo.ToString("#,##0"))); + userStats.Add(new UserStatsLine("Replays Watched by Others", user.Statistics.ReplaysWatched.ToString("#,##0"))); + } + } + + private class UserStatsLine : Container + { + public UserStatsLine(string left, string right) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Children = new Drawable[] + { + new OsuSpriteText + { + TextSize = 15, + Text = left, + Font = "Exo2.0-Medium" + }, + new OsuSpriteText + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + TextSize = 15, + Text = right, + Font = "Exo2.0-Bold" + }, + }; + } + } + } +} diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index e6d892f0b7..8709640b48 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -1,12 +1,8 @@ // Copyright (c) 2007-2018 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; using osu.Framework.Configuration; -using osu.Framework.Extensions; using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -14,91 +10,32 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; -using osu.Game.Online.Chat; using osu.Game.Overlays.Profile.Header; -using osu.Game.Scoring; using osu.Game.Users; -using osuTK; namespace osu.Game.Overlays.Profile { public class ProfileHeader : Container { - private readonly RankGraph rankGraph; - - public readonly SupporterIcon SupporterTag; private readonly Container coverContainer; private readonly OsuSpriteText coverInfoText; private readonly ProfileHeaderTabControl infoTabControl; - private readonly Box headerTopBox; - private readonly UpdateableAvatar avatar; - private readonly OsuSpriteText usernameText; - private readonly ExternalLinkButton openUserExternally; - private readonly OsuSpriteText titleText; - private readonly DrawableFlag userFlag; - private readonly OsuSpriteText userCountryText; - private readonly Box userIconSeperatorBox; - private readonly FillFlowContainer userStats; - - private readonly Box headerCenterBox; - private readonly OsuSpriteText followerText; - private readonly ProfileHeaderButton messageButton; - private readonly ProfileHeaderButton expandButton; - private readonly Sprite levelBadgeSprite; - private readonly OsuSpriteText levelBadgeText; - - private readonly Bar levelProgressBar; - private readonly OsuSpriteText levelProgressText; - - private readonly OverlinedInfoContainer hiddenDetailGlobal, hiddenDetailCountry; + private readonly TopHeaderContainer topHeaderContainer; + public SupporterIcon SupporterTag => topHeaderContainer.SupporterTag; + private readonly CenterHeaderContainer centerHeaderContainer; public readonly BindableBool DetailsVisible = new BindableBool(); - private readonly Box headerDetailBox; - private readonly HasTooltipContainer totalPlayTimeTooltip; - private readonly OverlinedInfoContainer totalPlayTimeInfo, medalInfo, ppInfo; - private readonly Dictionary scoreRankInfos = new Dictionary(); - private readonly OverlinedInfoContainer detailGlobalRank, detailCountryRank; - - private readonly Box headerBadgeBox; - private readonly FillFlowContainer badgeFlowContainer; - private readonly Container badgeContainer; - - private readonly Box headerBottomBox; - private readonly LinkFlowContainer bottomTopLinkContainer; - private readonly LinkFlowContainer bottomLinkContainer; - private Color4 linkBlue; + private readonly DetailHeaderContainer detailHeaderContainer; + private readonly MedalHeaderContainer medalHeaderContainer; + private readonly BottomHeaderContainer bottomHeaderContainer; private const float cover_height = 150; private const float cover_info_height = 75; - private const float avatar_size = 110; - private static readonly Dictionary play_styles = new Dictionary - { - {"keyboard", "Keyboard"}, - {"mouse", "Mouse"}, - {"tablet", "Tablet"}, - {"touch", "Touch Screen"}, - }; - - [Resolved(CanBeNull = true)] - private ChannelManager channelManager { get; set; } - - [Resolved(CanBeNull = true)] - private UserProfileOverlay userOverlay { get; set; } - - [Resolved(CanBeNull = true)] - private ChatOverlay chatOverlay { get; set; } - - [Resolved] - private APIAccess apiAccess { get; set; } public ProfileHeader() { @@ -175,478 +112,30 @@ namespace osu.Game.Overlays.Profile Direction = FillDirection.Vertical, Children = new Drawable[] { - new Container + topHeaderContainer = new TopHeaderContainer { RelativeSizeAxes = Axes.X, Height = 150, - Children = new Drawable[] - { - headerTopBox = new Box - { - RelativeSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - Direction = FillDirection.Horizontal, - Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, - Height = avatar_size, - AutoSizeAxes = Axes.X, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Children = new[] - { - avatar = new UpdateableAvatar - { - Size = new Vector2(avatar_size), - Masking = true, - CornerRadius = avatar_size * 0.25f, - OpenOnClick = { Value = false }, - }, - new Container - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Padding = new MarginPadding { Left = 10 }, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - usernameText = new OsuSpriteText - { - Font = "Exo2.0-Regular", - TextSize = 24 - }, - openUserExternally = new ExternalLinkButton - { - Margin = new MarginPadding { Left = 5 }, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - } - }, - new FillFlowContainer - { - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - titleText = new OsuSpriteText - { - TextSize = 18, - Font = "Exo2.0-Regular" - }, - SupporterTag = new SupporterIcon - { - Height = 20, - Margin = new MarginPadding { Top = 5 } - }, - userIconSeperatorBox = new Box - { - RelativeSizeAxes = Axes.X, - Height = 1.5f, - Margin = new MarginPadding { Top = 10 } - }, - new Container - { - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = 5 }, - Children = new Drawable[] - { - userFlag = new DrawableFlag - { - Size = new Vector2(30, 20) - }, - userCountryText = new OsuSpriteText - { - Font = "Exo2.0-Regular", - TextSize = 17.5f, - Margin = new MarginPadding { Left = 40 }, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - } - } - }, - } - } - } - } - } - }, - userStats = new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Y, - Width = 300, - Margin = new MarginPadding { Right = UserProfileOverlay.CONTENT_X_MARGIN }, - Padding = new MarginPadding { Vertical = 15 }, - Spacing = new Vector2(0, 2) - } - } }, - new Container + centerHeaderContainer = new CenterHeaderContainer { RelativeSizeAxes = Axes.X, Height = 60, - Children = new Drawable[] - { - headerCenterBox = new Box - { - RelativeSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Padding = new MarginPadding { Vertical = 10 }, - Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, - Spacing = new Vector2(10, 0), - Children = new Drawable[] - { - new ProfileHeaderButton - { - RelativeSizeAxes = Axes.Y, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Direction = FillDirection.Horizontal, - Padding = new MarginPadding { Right = 10 }, - Children = new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.fa_user, - FillMode = FillMode.Fit, - Size = new Vector2(50, 14) - }, - followerText = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - TextSize = 16, - Font = "Exo2.0-Bold" - } - } - } - } - }, - messageButton = new ProfileHeaderButton - { - Alpha = 0, - RelativeSizeAxes = Axes.Y, - Children = new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.fa_envelope, - FillMode = FillMode.Fit, - Size = new Vector2(50, 14) - }, - } - }, - } - }, - new Container - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Y, - Padding = new MarginPadding { Vertical = 10 }, - Width = UserProfileOverlay.CONTENT_X_MARGIN, - Child = expandButton = new ProfileHeaderButton - { - RelativeSizeAxes = Axes.Y, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Children = new Drawable[] - { - expandButtonIcon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(20), - Icon = FontAwesome.fa_chevron_up, - }, - } - }, - }, - new Container - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Right = UserProfileOverlay.CONTENT_X_MARGIN }, - Children = new Drawable[] - { - new HasTooltipContainer - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Size = new Vector2(40), - TooltipText = "Level", - Children = new Drawable[] - { - levelBadgeSprite = new Sprite - { - RelativeSizeAxes = Axes.Both, - }, - levelBadgeText = new OsuSpriteText - { - TextSize = 20, - Font = "Exo2.0-Medium", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } - } - }, - expandedDetailContainer = new HasTooltipContainer - { - TooltipText = "Progress to next level", - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Width = 200, - Height = 6, - Margin = new MarginPadding { Right = 50 }, - Children = new Drawable[] - { - new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Child = levelProgressBar = new Bar - { - RelativeSizeAxes = Axes.Both, - BackgroundColour = Color4.Black, - Direction = BarDirection.LeftToRight, - } - }, - levelProgressText = new OsuSpriteText - { - Anchor = Anchor.BottomRight, - Origin = Anchor.TopRight, - Font = "Exo2.0-Bold", - TextSize = 12, - } - } - }, - hiddenDetailContainer = new FillFlowContainer - { - Direction = FillDirection.Horizontal, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Width = 200, - AutoSizeAxes = Axes.Y, - Alpha = 0, - Spacing = new Vector2(10, 0), - Margin = new MarginPadding { Right = 50 }, - Children = new[] - { - hiddenDetailGlobal = new OverlinedInfoContainer - { - Title = "Global Ranking" - }, - hiddenDetailCountry = new OverlinedInfoContainer - { - Title = "Country Ranking" - }, - } - } - } - } - } }, - new Container + detailHeaderContainer = new DetailHeaderContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - headerDetailBox = new Box - { - RelativeSizeAxes = Axes.Both, - }, - headerDetailContainer = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 }, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 20), - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10, 0), - Children = new Drawable[] - { - totalPlayTimeTooltip = new HasTooltipContainer - { - AutoSizeAxes = Axes.Both, - TooltipText = "0 hours", - Child = totalPlayTimeInfo = new OverlinedInfoContainer - { - Title = "Total Play Time", - }, - }, - medalInfo = new OverlinedInfoContainer - { - Title = "Medals" - }, - ppInfo = new OverlinedInfoContainer - { - Title = "pp" - }, - } - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Direction = FillDirection.Horizontal, - Children = new[] - { - scoreRankInfos[ScoreRank.XH] = new ScoreRankInfo(ScoreRank.XH), - scoreRankInfos[ScoreRank.X] = new ScoreRankInfo(ScoreRank.X), - scoreRankInfos[ScoreRank.SH] = new ScoreRankInfo(ScoreRank.SH), - scoreRankInfos[ScoreRank.S] = new ScoreRankInfo(ScoreRank.S), - scoreRankInfos[ScoreRank.A] = new ScoreRankInfo(ScoreRank.A), - } - } - } - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Right = 130 }, - Children = new Drawable[] - { - rankGraph = new RankGraph - { - RelativeSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - Width = 130, - Anchor = Anchor.TopRight, - Direction = FillDirection.Vertical, - Padding = new MarginPadding { Horizontal = 10 }, - Spacing = new Vector2(0, 20), - Children = new Drawable[] - { - detailGlobalRank = new OverlinedInfoContainer(true, 110) - { - Title = "Global Ranking" - }, - detailCountryRank = new OverlinedInfoContainer(false, 110) - { - Title = "Country Ranking" - }, - } - } - } - }, - } - }, - } }, - badgeContainer = new Container + medalHeaderContainer = new MedalHeaderContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Alpha = 0, - Children = new Drawable[] - { - headerBadgeBox = new Box - { - RelativeSizeAxes = Axes.Both, - }, - new Container //artificial shadow - { - RelativeSizeAxes = Axes.X, - Height = 3, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = new ColourInfo - { - TopLeft = Color4.Black.Opacity(0.2f), - TopRight = Color4.Black.Opacity(0.2f), - BottomLeft = Color4.Black.Opacity(0), - BottomRight = Color4.Black.Opacity(0) - } - }, - }, - badgeFlowContainer = new FillFlowContainer - { - Direction = FillDirection.Full, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Top = 5 }, - Spacing = new Vector2(10, 10), - Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 }, - } - } }, - new Container + bottomHeaderContainer = new BottomHeaderContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - headerBottomBox = new Box - { - RelativeSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 }, - Spacing = new Vector2(0, 10), - Children = new Drawable[] - { - bottomTopLinkContainer = new LinkFlowContainer(text => text.TextSize = 12) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, - bottomLinkContainer = new LinkFlowContainer(text => text.TextSize = 12) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - } - } - } - } }, } } @@ -655,46 +144,16 @@ namespace osu.Game.Overlays.Profile infoTabControl.AddItem("Info"); infoTabControl.AddItem("Modding"); - DetailsVisible.ValueChanged += newValue => expandButtonIcon.Icon = newValue ? FontAwesome.fa_chevron_down : FontAwesome.fa_chevron_up; - DetailsVisible.ValueChanged += newValue => hiddenDetailContainer.Alpha = newValue ? 1 : 0; - DetailsVisible.ValueChanged += newValue => expandedDetailContainer.Alpha = newValue ? 0 : 1; - DetailsVisible.ValueChanged += newValue => headerDetailContainer.Alpha = newValue ? 0 : 1; + centerHeaderContainer.DetailsVisible.BindTo(DetailsVisible); + DetailsVisible.ValueChanged += newValue => detailHeaderContainer.Alpha = newValue ? 0 : 1; } - private Color4 communityUserGrayGreenLighter; - - [BackgroundDependencyLoader(true)] + [BackgroundDependencyLoader] private void load(OsuColour colours, TextureStore textures) { coverInfoText.Colour = colours.CommunityUserGreen; infoTabControl.AccentColour = colours.CommunityUserGreen; - - headerTopBox.Colour = colours.CommunityUserGrayGreenDarker; - userCountryText.Colour = colours.CommunityUserGrayGreenLighter; - userIconSeperatorBox.Colour = colours.CommunityUserGrayGreenLighter; - - headerCenterBox.Colour = colours.CommunityUserGrayGreenDark; - levelBadgeSprite.Texture = textures.Get("Profile/levelbadge"); - levelBadgeSprite.Colour = colours.Yellow; - levelProgressBar.AccentColour = colours.Yellow; - - hiddenDetailGlobal.LineColour = colours.Yellow; - hiddenDetailCountry.LineColour = colours.Yellow; - - headerDetailBox.Colour = colours.CommunityUserGrayGreenDarkest; - totalPlayTimeInfo.LineColour = colours.Yellow; - medalInfo.LineColour = colours.GreenLight; - ppInfo.LineColour = colours.Red; - - detailGlobalRank.LineColour = colours.Yellow; - detailCountryRank.LineColour = colours.Yellow; - - headerBadgeBox.Colour = colours.CommunityUserGrayGreenDarkest; - headerBottomBox.Colour = colours.CommunityUserGrayGreenDarker; - - communityUserGrayGreenLighter = colours.CommunityUserGrayGreenLighter; - linkBlue = colours.BlueLight; } private User user; @@ -704,13 +163,15 @@ namespace osu.Game.Overlays.Profile get => user; set { - user = value; - loadUser(); + medalHeaderContainer.User = detailHeaderContainer.User = bottomHeaderContainer.User = + centerHeaderContainer.User = topHeaderContainer.User = user = value; + updateDisplay(); } } - private void loadUser() + private void updateDisplay() { + coverContainer.RemoveAll(d => d is UserCoverBackground); LoadComponentAsync(new UserCoverBackground(user) { RelativeSizeAxes = Axes.Both, @@ -720,247 +181,14 @@ namespace osu.Game.Overlays.Profile OnLoadComplete = d => d.FadeInFromZero(200), Depth = float.MaxValue, }, coverContainer.Add); - - avatar.User = User; - usernameText.Text = user.Username; - openUserExternally.Link = $@"https://osu.ppy.sh/users/{user.Id}"; - userFlag.Country = user.Country; - userCountryText.Text = user.Country?.FullName; - SupporterTag.SupporterLevel = user.SupportLevel; - if(user.Title != null) - titleText.Text = user.Title; - titleText.Colour = OsuColour.FromHex(user.Colour ?? "fff"); - - userStats.Add(new UserStatsLine("Ranked Score", user.Statistics.RankedScore.ToString("#,##0"))); - userStats.Add(new UserStatsLine("Hit Accuracy", Math.Round(user.Statistics.Accuracy, 2).ToString("#0.00'%'"))); - userStats.Add(new UserStatsLine("Play Count", user.Statistics.PlayCount.ToString("#,##0"))); - userStats.Add(new UserStatsLine("Total Score", user.Statistics.TotalScore.ToString("#,##0"))); - userStats.Add(new UserStatsLine("Total Hits", user.Statistics.TotalHits.ToString("#,##0"))); - userStats.Add(new UserStatsLine("Maximum Combo", user.Statistics.MaxCombo.ToString("#,##0"))); - userStats.Add(new UserStatsLine("Replays Watched by Others", user.Statistics.ReplaysWatched.ToString("#,##0"))); - - followerText.Text = user.FollowerCount?.Length > 0 ? user.FollowerCount[0].ToString("#,##0") : "0"; - - if (!user.PMFriendsOnly && apiAccess.LocalUser.Value.Id != user.Id) - { - messageButton.Show(); - messageButton.Action = () => - { - channelManager?.OpenPrivateChannel(user); - userOverlay?.Hide(); - chatOverlay?.Show(); - }; - } - - expandButton.Action = DetailsVisible.Toggle; - - levelBadgeText.Text = user.Statistics.Level.Current.ToString(); - levelProgressBar.Length = user.Statistics.Level.Progress / 100f; - levelProgressText.Text = user.Statistics.Level.Progress.ToString("0'%'"); - - hiddenDetailGlobal.Content = user.Statistics.Ranks.Global?.ToString("#,##0") ?? "-"; - hiddenDetailCountry.Content = user.Statistics.Ranks.Country?.ToString("#,##0") ?? "-"; - - medalInfo.Content = user.Achievements.Length.ToString(); - ppInfo.Content = user.Statistics.PP?.ToString("#,##0") ?? "0"; - - string formatTime(int? secondsNull) - { - if (secondsNull == null) return "0h 0m"; - - int seconds = secondsNull.Value; - string time = ""; - - int days = seconds / 86400; - seconds -= days * 86400; - if (days > 0) - time += days + "d "; - - int hours = seconds / 3600; - seconds -= hours * 3600; - time += hours + "h "; - - int minutes = seconds / 60; - time += minutes + "m"; - - return time; - } - - totalPlayTimeInfo.Content = formatTime(user.Statistics.PlayTime); - totalPlayTimeTooltip.TooltipText = (user.Statistics.PlayTime ?? 0) / 3600 + " hours"; - - foreach (var scoreRankInfo in scoreRankInfos) - scoreRankInfo.Value.RankCount = user.Statistics.GradesCount.GetForScoreRank(scoreRankInfo.Key); - - detailGlobalRank.Content = user.Statistics.Ranks.Global?.ToString("#,##0") ?? "-"; - detailCountryRank.Content = user.Statistics.Ranks.Country?.ToString("#,##0") ?? "-"; - - rankGraph.User.Value = user; - - var badges = User.Badges; - if (badges.Length > 0) - { - badgeContainer.Show(); - for (var index = 0; index < badges.Length; index++) - { - int displayIndex = index; - LoadComponentAsync(new DrawableBadge(badges[index]), asyncBadge => - { - badgeFlowContainer.Add(asyncBadge); - - // load in stable order regardless of async load order. - badgeFlowContainer.SetLayoutPosition(asyncBadge, displayIndex); - }); - } - } - - void bold(SpriteText t) => t.Font = @"Exo2.0-Bold"; - void addSpacer(OsuTextFlowContainer textFlow) => textFlow.AddArbitraryDrawable(new Container { Width = 15 }); - - if (user.JoinDate.ToUniversalTime().Year < 2008) - { - bottomTopLinkContainer.AddText("Here since the beginning"); - } - else - { - bottomTopLinkContainer.AddText("Joined "); - bottomTopLinkContainer.AddText(new DrawableDate(user.JoinDate), bold); - } - - addSpacer(bottomTopLinkContainer); - - if (user.PlayStyles?.Length > 0) - { - bottomTopLinkContainer.AddText("Plays with "); - bottomTopLinkContainer.AddText(string.Join(", ", user.PlayStyles.Select(style => style.GetDescription())), bold); - - addSpacer(bottomTopLinkContainer); - } - - if (user.LastVisit.HasValue) - { - bottomTopLinkContainer.AddText("Last seen "); - bottomTopLinkContainer.AddText(new DrawableDate(user.LastVisit.Value), bold); - - addSpacer(bottomTopLinkContainer); - } - - bottomTopLinkContainer.AddText("Contributed "); - bottomTopLinkContainer.AddLink($@"{user.PostCount:#,##0} forum posts", $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: bold); - - void tryAddInfo(FontAwesome icon, string content, string link = null) - { - if (string.IsNullOrEmpty(content)) return; - - bottomLinkContainer.AddIcon(icon, text => - { - text.TextSize = 10; - text.Colour = communityUserGrayGreenLighter; - }); - if (link != null) - { - bottomLinkContainer.AddLink(" " + content, link, creationParameters: text => - { - bold(text); - text.Colour = linkBlue; - }); - } - else - { - bottomLinkContainer.AddText(" " + content, bold); - } - addSpacer(bottomLinkContainer); - } - - string websiteWithoutProtcol = user.Website; - if (!string.IsNullOrEmpty(websiteWithoutProtcol)) - { - int protocolIndex = websiteWithoutProtcol.IndexOf("//", StringComparison.Ordinal); - if (protocolIndex >= 0) - websiteWithoutProtcol = websiteWithoutProtcol.Substring(protocolIndex + 2); - } - - tryAddInfo(FontAwesome.fa_map_marker, user.Location); - tryAddInfo(FontAwesome.fa_heart_o, user.Interests); - tryAddInfo(FontAwesome.fa_suitcase, user.Occupation); - bottomLinkContainer.NewLine(); - if (!string.IsNullOrEmpty(user.Twitter)) - tryAddInfo(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}"); - tryAddInfo(FontAwesome.fa_gamepad, user.Discord); //todo: update fontawesome to include discord logo - tryAddInfo(FontAwesome.fa_skype, user.Skype, @"skype:" + user.Skype + @"?chat"); - tryAddInfo(FontAwesome.fa_lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}"); - tryAddInfo(FontAwesome.fa_link, websiteWithoutProtcol, user.Website); } - private class UserStatsLine : Container - { - public UserStatsLine(string left, string right) - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Children = new Drawable[] - { - new OsuSpriteText - { - TextSize = 15, - Text = left, - Font = "Exo2.0-Medium" - }, - new OsuSpriteText - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - TextSize = 15, - Text = right, - Font = "Exo2.0-Bold" - }, - }; - } - } - - private class ProfileHeaderButton : OsuHoverContainer - { - private readonly Box background; - private readonly Container content; - - protected override Container Content => content; - - protected override IEnumerable EffectTargets => new[] { background }; - - public ProfileHeaderButton() - { - HoverColour = Color4.Black.Opacity(0.75f); - IdleColour = Color4.Black.Opacity(0.7f); - AutoSizeAxes = Axes.X; - - base.Content.Add(new CircularContainer - { - Masking = true, - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - }, - content = new Container - { - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = 10 }, - } - } - }); - } - } - - private class HasTooltipContainer : Container, IHasTooltip + public class HasTooltipContainer : Container, IHasTooltip { public string TooltipText { get; set; } } - private class OverlinedInfoContainer : CompositeDrawable + public class OverlinedInfoContainer : CompositeDrawable { private readonly Circle line; private readonly OsuSpriteText title, content; @@ -1012,83 +240,5 @@ namespace osu.Game.Overlays.Profile }; } } - - public class ScoreRankInfo : CompositeDrawable - { - private readonly ScoreRank rank; - private readonly Sprite rankSprite; - private readonly OsuSpriteText rankCount; - - public int RankCount - { - set => rankCount.Text = value.ToString("#,##0"); - } - - public ScoreRankInfo(ScoreRank rank) - { - this.rank = rank; - - AutoSizeAxes = Axes.Both; - InternalChild = new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - Width = 56, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - rankSprite = new Sprite - { - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit - }, - rankCount = new OsuSpriteText - { - Font = "Exo2.0-Bold", - TextSize = 12, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre - } - } - }; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - rankSprite.Texture = textures.Get($"Grades/{rank.GetDescription()}"); - } - } - - private class DrawableBadge : CompositeDrawable, IHasTooltip - { - public static readonly Vector2 DRAWABLE_BADGE_SIZE = new Vector2(86, 40); - - private readonly Badge badge; - - public DrawableBadge(Badge badge) - { - this.badge = badge; - Size = DRAWABLE_BADGE_SIZE; - } - - [BackgroundDependencyLoader] - private void load(LargeTextureStore textures) - { - InternalChild = new Sprite - { - FillMode = FillMode.Fit, - RelativeSizeAxes = Axes.Both, - Texture = textures.Get(badge.ImageUrl), - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - InternalChild.FadeInFromZero(200); - } - - public string TooltipText => badge.Description; - } } } From 14d2ed7085a82ab9ba5d6c91a2aefd6cec4b9471 Mon Sep 17 00:00:00 2001 From: jorolf Date: Tue, 22 Jan 2019 19:50:42 +0100 Subject: [PATCH 0339/2854] fix codefactor issue that I didn't notice and has been there for 12 days --- osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs index 9005fd5ab2..9be1eb4326 100644 --- a/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs @@ -278,7 +278,6 @@ namespace osu.Game.Overlays.Profile.Header hiddenDetailGlobal.Content = user?.Statistics?.Ranks.Global?.ToString("#,##0") ?? "-"; hiddenDetailCountry.Content = user?.Statistics?.Ranks.Country?.ToString("#,##0") ?? "-"; - } private class ProfileHeaderButton : OsuHoverContainer From 648ed0e4677356a2b07721696ee3eece95fbe8af Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 27 Jan 2019 23:45:00 +0100 Subject: [PATCH 0340/2854] update license headers --- osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs | 4 ++-- osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs | 4 ++-- osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs | 4 ++-- osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs | 4 ++-- osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs | 4 ++-- osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs | 4 ++-- osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs b/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs index c80227f149..6f164890f9 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index a3a6447a9f..df0409272f 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Linq; diff --git a/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs index 9be1eb4326..30671487d3 100644 --- a/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; using osu.Framework.Allocation; diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 678fc2ddb7..124299b0ba 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; using osu.Framework.Allocation; diff --git a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs index 74f1e2f689..5a54270b80 100644 --- a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs index db8a0b594c..fd7124f20f 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs +++ b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index b003f08b95..4186d08729 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using osu.Framework.Allocation; From c62848df5ae98a42add6352833ed5d085b2af2cd Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Wed, 30 Jan 2019 18:07:32 -0600 Subject: [PATCH 0341/2854] Update license header --- osu.Android/OsuGameActivity.cs | 16 +++++++++++++--- .../MainActivity.cs | 4 ++-- .../MainActivity.cs | 4 ++-- .../MainActivity.cs | 4 ++-- .../MainActivity.cs | 4 ++-- osu.Game.Tests.Android/MainActivity.cs | 6 +++--- 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 08f2707044..130144d417 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -1,17 +1,27 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using Android.App; using Android.Content.PM; +using Android.OS; +using Android.Views; using osu.Framework.Android; using osu.Game; namespace osu.Android { - [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] + [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = true)] public class OsuGameActivity : AndroidGameActivity { protected override Framework.Game CreateGame() => new OsuGame(); + + protected override void OnCreate(Bundle savedInstanceState) + { + base.OnCreate(savedInstanceState); + + Window.AddFlags(WindowManagerFlags.Fullscreen); + Window.AddFlags(WindowManagerFlags.KeepScreenOn); + } } } diff --git a/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs index 4b13b86ca2..8430a88f15 100644 --- a/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs +++ b/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using Android.App; using Android.Content.PM; diff --git a/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs index 3c0de47654..0f1d6756bc 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs +++ b/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using Android.App; using Android.Content.PM; diff --git a/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs index 622eadef70..d6f2ec546d 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs +++ b/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using Android.App; using Android.Content.PM; diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs index 0c64cbfc5d..cc92ad7080 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs +++ b/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using Android.App; using Android.Content.PM; diff --git a/osu.Game.Tests.Android/MainActivity.cs b/osu.Game.Tests.Android/MainActivity.cs index 58375c699e..6f68c372ef 100644 --- a/osu.Game.Tests.Android/MainActivity.cs +++ b/osu.Game.Tests.Android/MainActivity.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using Android.App; using Android.Content.PM; @@ -7,7 +7,7 @@ using osu.Framework.Android; namespace osu.Game.Tests.Android { - [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] + [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = true)] public class MainActivity : AndroidGameActivity { protected override Framework.Game CreateGame() From cb23918512a1c8e7d2d65aa5170336a3567381e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Feb 2019 11:24:43 +0900 Subject: [PATCH 0342/2854] Changes for mania world cup --- osu.Game.Tournament/Components/SongBar.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 14a01aebdf..fc75bd59a4 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -190,7 +190,12 @@ namespace osu.Game.Tournament.Components Anchor = Anchor.CentreLeft, Origin = Anchor.TopLeft }, - new DiffPiece(("CS", $"{beatmap.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"), ("AR", $"{ar:0.#}{srExtra}"), ("OD", $"{beatmap.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}")) + new DiffPiece( + //("CS", $"{beatmap.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"), + //("AR", $"{ar:0.#}{srExtra}"), + ("OD", $"{beatmap.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}"), + ("HP", $"{beatmap.BaseDifficulty.DrainRate:0.#}{hardRockExtra}") + ) { Anchor = Anchor.CentreRight, Origin = Anchor.BottomRight From 5048f425d4b5fa37881fea1ae0c648ef441ca8ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Feb 2019 18:36:15 +0900 Subject: [PATCH 0343/2854] Add ability to reset bracket --- .../Screens/Ladder/Components/MatchPairing.cs | 9 +++++++++ osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index b3adc3b49b..7d6acc5bd2 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -110,5 +110,14 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Team1Score.Value = 0; Team2Score.Value = 0; } + + public void Reset() + { + CancelMatchStart(); + Team1.Value = null; + Team2.Value = null; + Completed.Value = false; + PicksBans.Clear(); + } } } diff --git a/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs index adc880e524..e3cb1473be 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs @@ -77,6 +77,11 @@ namespace osu.Game.Tournament.Screens.Ladder var pos = PairingsContainer.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); AddPairing(new MatchPairing { Position = { Value = new Point((int)pos.X, (int)pos.Y) } }); }), + new OsuMenuItem("Reset teams", MenuItemType.Destructive, () => + { + foreach (var p in PairingsContainer) + p.Pairing.Reset(); + }) }; } } From 2a9da602cb388028dbed0973b35c952093ea8567 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Feb 2019 13:07:57 +0900 Subject: [PATCH 0344/2854] Use nuget packages again --- osu.Game/osu.Game.csproj | 4 +--- osu.sln | 12 ------------ 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3b1b276130..fd79f65989 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,13 +15,11 @@ + - - - diff --git a/osu.sln b/osu.sln index 59b85c8936..688339fab5 100644 --- a/osu.sln +++ b/osu.sln @@ -29,10 +29,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tournament", "osu. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tournament.Tests", "osu.Game.Tournament.Tests\osu.Game.Tournament.Tests.csproj", "{5789E78D-38F9-4072-AB7B-978F34B2C17F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework", "..\osu-framework\osu.Framework\osu.Framework.csproj", "{7A69A230-45A1-4444-8C43-A582E4F48C1E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework.NativeLibs", "..\osu-framework\osu.Framework.NativeLibs\osu.Framework.NativeLibs.csproj", "{C3ECD150-D109-453F-9625-518B282CF745}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -91,14 +87,6 @@ Global {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Debug|Any CPU.Build.0 = Debug|Any CPU {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Release|Any CPU.ActiveCfg = Release|Any CPU {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Release|Any CPU.Build.0 = Release|Any CPU - {7A69A230-45A1-4444-8C43-A582E4F48C1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7A69A230-45A1-4444-8C43-A582E4F48C1E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7A69A230-45A1-4444-8C43-A582E4F48C1E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7A69A230-45A1-4444-8C43-A582E4F48C1E}.Release|Any CPU.Build.0 = Release|Any CPU - {C3ECD150-D109-453F-9625-518B282CF745}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C3ECD150-D109-453F-9625-518B282CF745}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C3ECD150-D109-453F-9625-518B282CF745}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C3ECD150-D109-453F-9625-518B282CF745}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 61e6285f7a601bbf5d292216ca04d640b8a6bc4f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Feb 2019 15:20:22 +0900 Subject: [PATCH 0345/2854] Update paths --- osu.Game.Tournament/IPC/FileBasedIPC.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 83dbe11ffa..7f8ed0cb3d 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -141,12 +141,12 @@ namespace osu.Game.Tournament.IPC { try { - stableInstallPath = "E:\\osu!tourney"; + stableInstallPath = "G:\\My Drive\\Main\\osu!tourney"; if (checkExists(stableInstallPath)) return stableInstallPath; - stableInstallPath = "E:\\osu!mappool"; + stableInstallPath = "G:\\My Drive\\Main\\osu!mappool"; if (checkExists(stableInstallPath)) return stableInstallPath; From fcab21908bd02a73effc030849e9df6f75ad8dbe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 17 Feb 2019 18:20:03 +0900 Subject: [PATCH 0346/2854] Update in line with screen changes --- osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs | 9 +-------- osu.Game.Tournament/Screens/TournamentScreen.cs | 9 +++++++-- osu.Game.Tournament/TournamentGame.cs | 3 ++- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index 66e539af25..f68adabb4c 100644 --- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -13,11 +13,9 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Logging; using osu.Framework.Platform; -using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Screens; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Drawings.Components; using osuTK; @@ -25,14 +23,10 @@ using osuTK.Graphics; namespace osu.Game.Tournament.Screens.Drawings { - public class DrawingsScreen : OsuScreen + public class DrawingsScreen : CompositeDrawable { private const string results_filename = "drawings_results.txt"; - public override bool HideOverlaysOnEnter => true; - - protected override BackgroundScreen CreateBackground() => null; - private ScrollingTeamContainer teamsContainer; private GroupContainer groupsContainer; private OsuSpriteText fullTeamNameText; @@ -59,7 +53,6 @@ namespace osu.Game.Tournament.Screens.Drawings if (!TeamList.Teams.Any()) { - this.Exit(); return; } diff --git a/osu.Game.Tournament/Screens/TournamentScreen.cs b/osu.Game.Tournament/Screens/TournamentScreen.cs index b440b8e796..e830509db9 100644 --- a/osu.Game.Tournament/Screens/TournamentScreen.cs +++ b/osu.Game.Tournament/Screens/TournamentScreen.cs @@ -3,15 +3,20 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Screens; +using osu.Framework.Graphics.Containers; namespace osu.Game.Tournament.Screens { - public class TournamentScreen : OsuScreen + public class TournamentScreen : CompositeDrawable { [Resolved] protected LadderInfo LadderInfo { get; private set; } + public TournamentScreen() + { + RelativeSizeAxes = Axes.Both; + } + public override void Hide() { this.FadeOut(200); diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index f970700fc5..711ecc5ae9 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; +using osu.Framework.Screens; using osu.Game.Graphics.Cursor; using osu.Game.Tournament.Screens; @@ -16,7 +17,7 @@ namespace osu.Game.Tournament Add(new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, - Child = new TournamentSceneManager() + Child = new ScreenStack(new TournamentSceneManager()) { RelativeSizeAxes = Axes.Both } }); MenuCursorContainer.Cursor.Alpha = 0; From 6ea1ed8d04addf7e04044fa6de531f773dd455bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 17 Feb 2019 19:48:54 +0900 Subject: [PATCH 0347/2854] Fix unnecessary texture atlas generation --- .../Components/TournamentBeatmapPanel.cs | 2 +- .../Screens/Gameplay/Components/MatchHeader.cs | 3 +-- .../Screens/Gameplay/GameplayScreen.cs | 3 +-- .../Ladder/Components/DrawableMatchPairing.cs | 13 +++++++++++-- .../Screens/Showcase/ShowcaseScreen.cs | 3 +-- .../Screens/TeamIntro/TeamIntroScreen.cs | 3 +-- .../Screens/TeamWin/TeamWinScreen.cs | 3 +-- 7 files changed, 17 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index ae2e2b5160..748f9fbdce 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -140,7 +140,7 @@ namespace osu.Game.Tournament.Components if (!string.IsNullOrEmpty(mods)) AddInternal(new Sprite { - Texture = new TextureStore(new TextureLoaderStore(new StorageBackedResourceStore(storage))).Get($"mods/{mods}"), + Texture = new LargeTextureStore(new TextureLoaderStore(new StorageBackedResourceStore(storage))).Get($"mods/{mods}"), Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Margin = new MarginPadding(20), diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index 210b42a4c8..25f7be1739 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -6,7 +6,6 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -22,7 +21,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components public class MatchHeader : Container { [BackgroundDependencyLoader] - private void load(LadderInfo ladder, TextureStore textures) + private void load(LadderInfo ladder) { RelativeSizeAxes = Axes.X; Height = 95; diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 277d2aad6d..ad0ef0f521 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -6,7 +6,6 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Textures; using osu.Framework.Threading; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; @@ -37,7 +36,7 @@ namespace osu.Game.Tournament.Screens.Gameplay private TournamentSceneManager sceneManager { get; set; } [BackgroundDependencyLoader] - private void load(LadderInfo ladder, TextureStore textures, MatchIPCInfo ipc, MatchChatDisplay chat) + private void load(LadderInfo ladder, MatchIPCInfo ipc, MatchChatDisplay chat) { this.chat = chat; this.ipc = ipc; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index db942c6e4c..58eab5bc0a 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -18,6 +18,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public class DrawableMatchPairing : CompositeDrawable { public readonly MatchPairing Pairing; + private readonly bool editor; protected readonly FillFlowContainer Flow; private readonly Drawable selectionBox; private readonly Drawable currentMatchSelectionBox; @@ -26,9 +27,14 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [Resolved(CanBeNull = true)] private LadderEditorInfo editorInfo { get; set; } - public DrawableMatchPairing(MatchPairing pairing) + [Resolved(CanBeNull = true)] + private LadderInfo ladderInfo { get; set; } + + + public DrawableMatchPairing(MatchPairing pairing, bool editor = false) { Pairing = pairing; + this.editor = editor; AutoSizeAxes = Axes.Both; @@ -109,7 +115,10 @@ namespace osu.Game.Tournament.Screens.Ladder.Components if (selected) { selectionBox.Show(); - editorInfo.Selected.Value = Pairing; + if (editor) + editorInfo.Selected.Value = Pairing; + else + ladderInfo.CurrentMatch.Value = Pairing; } else selectionBox.Hide(); diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index 7690ff4b97..65369fa6da 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -2,14 +2,13 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Graphics.Textures; namespace osu.Game.Tournament.Screens.Showcase { public class ShowcaseScreen : BeatmapInfoScreen { [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load() { AddInternal(new TournamentLogo()); } diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index 55af305045..dc79d4aab0 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Video; using osu.Framework.Platform; using osu.Game.Graphics; @@ -25,7 +24,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro private readonly Bindable currentMatch = new Bindable(); [BackgroundDependencyLoader] - private void load(TextureStore textures, LadderInfo ladder, Storage storage) + private void load(LadderInfo ladder, Storage storage) { RelativeSizeAxes = Axes.Both; diff --git a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs index fbb622e16f..2e2c8e5020 100644 --- a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs +++ b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Video; using osu.Framework.Platform; using osu.Game.Graphics; @@ -29,7 +28,7 @@ namespace osu.Game.Tournament.Screens.TeamWin private VideoSprite redWinVideo; [BackgroundDependencyLoader] - private void load(TextureStore textures, LadderInfo ladder, Storage storage) + private void load(LadderInfo ladder, Storage storage) { RelativeSizeAxes = Axes.Both; From ef321b41035e38dbfba2087c830f2b7d9ef97ded Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 17 Feb 2019 19:48:58 +0900 Subject: [PATCH 0348/2854] Fix regressed test --- osu.Game.Tournament.Tests/TestCaseDrawings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament.Tests/TestCaseDrawings.cs b/osu.Game.Tournament.Tests/TestCaseDrawings.cs index d957c792f7..f5f5615bf6 100644 --- a/osu.Game.Tournament.Tests/TestCaseDrawings.cs +++ b/osu.Game.Tournament.Tests/TestCaseDrawings.cs @@ -13,7 +13,7 @@ namespace osu.Game.Tournament.Tests { public TestCaseDrawings() { - LoadScreen(new DrawingsScreen + Add(new DrawingsScreen { TeamList = new TestTeamList(), }); From e7668a749bce0cbcbf8b49c3f8f720f0b1847a5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 17 Feb 2019 19:49:10 +0900 Subject: [PATCH 0349/2854] Bind to correct target (hacky) --- osu.Game.Tournament/Screens/Ladder/LadderScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs index 549f12f769..36baaf0b39 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -64,7 +64,7 @@ namespace osu.Game.Tournament.Screens.Ladder protected virtual void AddPairing(MatchPairing pairing) { - PairingsContainer.Add(new DrawableMatchPairing(pairing)); + PairingsContainer.Add(new DrawableMatchPairing(pairing, this is LadderEditorScreen)); } private Cached layout = new Cached(); From 389632d9325dc078605e2dfefcec03b4def93fcd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Mar 2019 13:40:43 +0900 Subject: [PATCH 0350/2854] Fix bindable changes --- osu.Game.Tournament/Components/DateTextBox.cs | 7 +++--- .../Components/MatchChatDisplay.cs | 8 +++---- .../Components/TournamentBeatmapPanel.cs | 6 ++--- osu.Game.Tournament/IPC/MatchIPCInfo.cs | 2 +- osu.Game.Tournament/LadderInfo.cs | 2 +- .../Screens/BeatmapInfoScreen.cs | 9 ++++---- .../Gameplay/Components/MatchHeader.cs | 14 ++++++------ .../Gameplay/Components/MatchScoreDisplay.cs | 2 +- .../Screens/Gameplay/GameplayScreen.cs | 20 ++++++++--------- .../Ladder/Components/DrawableMatchPairing.cs | 21 +++++++++--------- .../Ladder/Components/DrawableMatchTeam.cs | 16 +++++++------- .../Ladder/Components/LadderEditorInfo.cs | 2 +- .../Ladder/Components/LadderEditorSettings.cs | 22 +++++++++---------- .../Screens/Ladder/Components/MatchPairing.cs | 8 +++---- .../Ladder/Components/TournamentGrouping.cs | 2 +- .../Screens/Ladder/LadderScreen.cs | 6 ++--- .../Screens/MapPool/MapPoolScreen.cs | 14 ++++++------ .../Screens/Schedule/ScheduleScreen.cs | 10 ++++----- .../Screens/TeamIntro/TeamIntroScreen.cs | 12 +++++----- .../Screens/TeamWin/TeamWinScreen.cs | 6 ++--- osu.Game.Tournament/TournamentGameBase.cs | 1 + 21 files changed, 96 insertions(+), 94 deletions(-) diff --git a/osu.Game.Tournament/Components/DateTextBox.cs b/osu.Game.Tournament/Components/DateTextBox.cs index 171196f348..0a2485e4f3 100644 --- a/osu.Game.Tournament/Components/DateTextBox.cs +++ b/osu.Game.Tournament/Components/DateTextBox.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; @@ -12,13 +12,12 @@ namespace osu.Game.Tournament.Components { public new Bindable Bindable { - get { return bindable; } - + get => bindable; set { bindable = value; bindable.BindValueChanged(dto => - base.Bindable.Value = dto.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"), true); + base.Bindable.Value = dto.NewValue.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"), true); } } diff --git a/osu.Game.Tournament/Components/MatchChatDisplay.cs b/osu.Game.Tournament/Components/MatchChatDisplay.cs index f96db186ea..33c544f83d 100644 --- a/osu.Game.Tournament/Components/MatchChatDisplay.cs +++ b/osu.Game.Tournament/Components/MatchChatDisplay.cs @@ -3,7 +3,7 @@ using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Game.Online.Chat; using osu.Game.Overlays.Chat; using osu.Game.Tournament.IPC; @@ -25,12 +25,12 @@ namespace osu.Game.Tournament.Components if (ipc != null) { chatChannel.BindTo(ipc.ChatChannel); - chatChannel.BindValueChanged(channelString => + chatChannel.BindValueChanged(c => { - if (string.IsNullOrWhiteSpace(channelString)) + if (string.IsNullOrWhiteSpace(c.NewValue)) return; - int id = int.Parse(channelString); + int id = int.Parse(c.NewValue); if (id <= 0) return; diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 748f9fbdce..94aa86d5cb 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -6,7 +6,7 @@ using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -148,9 +148,9 @@ namespace osu.Game.Tournament.Components }); } - private void matchChanged(MatchPairing match) + private void matchChanged(ValueChangedEvent pairing) { - match.PicksBans.CollectionChanged += picksBansOnCollectionChanged; + pairing.NewValue.PicksBans.CollectionChanged += picksBansOnCollectionChanged; updateState(); } diff --git a/osu.Game.Tournament/IPC/MatchIPCInfo.cs b/osu.Game.Tournament/IPC/MatchIPCInfo.cs index be31df8009..cd57756f60 100644 --- a/osu.Game.Tournament/IPC/MatchIPCInfo.cs +++ b/osu.Game.Tournament/IPC/MatchIPCInfo.cs @@ -1,7 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; diff --git a/osu.Game.Tournament/LadderInfo.cs b/osu.Game.Tournament/LadderInfo.cs index c433987491..83d0f76020 100644 --- a/osu.Game.Tournament/LadderInfo.cs +++ b/osu.Game.Tournament/LadderInfo.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using Newtonsoft.Json; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; diff --git a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs index 0b37e2fab1..b0aadc7e9e 100644 --- a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs +++ b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; @@ -30,15 +31,15 @@ namespace osu.Game.Tournament.Screens ipc.Mods.BindValueChanged(modsChanged, true); } - private void modsChanged(LegacyMods mods) + private void modsChanged(ValueChangedEvent mods) { - SongBar.Mods = mods; + SongBar.Mods = mods.NewValue; } - private void beatmapChanged(BeatmapInfo beatmap) + private void beatmapChanged(ValueChangedEvent beatmap) { SongBar.FadeInFromZero(300, Easing.OutQuint); - SongBar.Beatmap = beatmap; + SongBar.Beatmap = beatmap.NewValue; } } } diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index 25f7be1739..364d437619 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -73,13 +73,13 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components currentMatch.BindTo(ladder.CurrentMatch); } - private void matchChanged(MatchPairing match) + private void matchChanged(ValueChangedEvent match) { currentTeamScore.UnbindBindings(); - currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.Team1Score : match.Team2Score); + currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1Score : match.NewValue.Team2Score); currentTeam.UnbindBindings(); - currentTeam.BindTo(teamColour == TeamColour.Red ? match.Team1 : match.Team2); + currentTeam.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1 : match.NewValue.Team2); // team may change to same team, which means score is not in a good state. // thus we handle this manually. @@ -144,7 +144,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components currentTeamScore.BindTo(score); } - private void scoreChanged(int? score) => counter.CountStars = score ?? 0; + private void scoreChanged(ValueChangedEvent score) => counter.CountStars = score.NewValue ?? 0; } private class TeamDisplay : DrawableTournamentTeam @@ -204,7 +204,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components currentMatch.BindTo(ladder.CurrentMatch); } - private void matchChanged(MatchPairing match) + private void matchChanged(ValueChangedEvent match) { InternalChildren = new Drawable[] { @@ -218,7 +218,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components Anchor = Anchor.Centre, Origin = Anchor.Centre, Colour = Color4.White, - Text = match.Grouping.Value?.Name.Value ?? "Unknown Grouping", + Text = match.NewValue.Grouping.Value?.Name.Value ?? "Unknown Grouping", Font = "Aquatico-Regular", TextSize = 18, }, diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs index b4eb9ef239..30cd2b2e85 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs @@ -3,7 +3,7 @@ using System; using osu.Framework.Allocation; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index ad0ef0f521..eb33c2fbdb 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -120,10 +120,10 @@ namespace osu.Game.Tournament.Screens.Gameplay State.BindTo(ipc.State); State.BindValueChanged(stateChanged, true); - currentMatch.BindValueChanged(m => warmup.Value = m.Team1Score + m.Team2Score == 0); + currentMatch.BindValueChanged(m => warmup.Value = m.NewValue.Team1Score.Value + m.NewValue.Team2Score.Value == 0); currentMatch.BindTo(ladder.CurrentMatch); - warmup.BindValueChanged(w => warmupButton.Alpha = !w ? 0.5f : 1, true); + warmup.BindValueChanged(w => warmupButton.Alpha = !w.NewValue ? 0.5f : 1, true); } private ScheduledDelegate scheduledOperation; @@ -132,15 +132,15 @@ namespace osu.Game.Tournament.Screens.Gameplay private TourneyState lastState; - private void stateChanged(TourneyState state) + private void stateChanged(ValueChangedEvent state) { try { - if (state == TourneyState.Ranking) + if (state.NewValue == TourneyState.Ranking) { if (warmup.Value) return; - if (ipc.Score1 > ipc.Score2) + if (ipc.Score1.Value > ipc.Score2.Value) currentMatch.Value.Team1Score.Value++; else currentMatch.Value.Team2Score.Value++; @@ -167,16 +167,16 @@ namespace osu.Game.Tournament.Screens.Gameplay chat.Contract(); } - switch (state) + switch (state.NewValue) { case TourneyState.Idle: contract(); if (lastState == TourneyState.Ranking && !warmup.Value) { - if (currentMatch.Value?.Completed == true) + if (currentMatch.Value?.Completed.Value == true) scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(TeamWinScreen)); }, 4000); - else if (currentMatch.Value?.Completed == false) + else if (currentMatch.Value?.Completed.Value == false) scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(MapPoolScreen)); }, 4000); } @@ -192,7 +192,7 @@ namespace osu.Game.Tournament.Screens.Gameplay } finally { - lastState = state; + lastState = state.NewValue; } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 58eab5bc0a..d993fbda20 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -87,7 +87,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components pairing.Position.BindValueChanged(pos => { if (IsDragged) return; - Position = new Vector2(pos.X, pos.Y); + + Position = new Vector2(pos.NewValue.X, pos.NewValue.Y); }, true); updateTeams(); @@ -127,7 +128,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private void updateProgression() { - if (!Pairing.Completed) + if (!Pairing.Completed.Value) { // ensure we clear any of our teams from our progression. // this is not pretty logic but should suffice for now. @@ -177,10 +178,10 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { if (Pairing.Grouping.Value == null) return; - var instaWinAmount = Pairing.Grouping.Value.BestOf / 2; + var instaWinAmount = Pairing.Grouping.Value.BestOf.Value / 2; - Pairing.Completed.Value = Pairing.Grouping.Value.BestOf > 0 - && (Pairing.Team1Score + Pairing.Team2Score >= Pairing.Grouping.Value.BestOf || Pairing.Team1Score > instaWinAmount || Pairing.Team2Score > instaWinAmount); + Pairing.Completed.Value = Pairing.Grouping.Value.BestOf.Value > 0 + && (Pairing.Team1Score.Value + Pairing.Team2Score.Value >= Pairing.Grouping.Value.BestOf.Value || Pairing.Team1Score.Value > instaWinAmount || Pairing.Team2Score.Value > instaWinAmount); } protected override void LoadComplete() @@ -193,7 +194,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components globalSelection = editorInfo.Selected.GetBoundCopy(); globalSelection.BindValueChanged(s => { - if (s != Pairing) Selected = false; + if (s.NewValue != Pairing) Selected = false; }); } } @@ -216,14 +217,14 @@ namespace osu.Game.Tournament.Screens.Ladder.Components var team2Match = conditional.Acronyms.Contains(Pairing.Team2Acronym); if (team1Match && team2Match) - Pairing.Date.Value = conditional.Date; + Pairing.Date.Value = conditional.Date.Value; } } Flow.Children = new[] { - new DrawableMatchTeam(Pairing.Team1, Pairing, Pairing.Losers), - new DrawableMatchTeam(Pairing.Team2, Pairing, Pairing.Losers) + new DrawableMatchTeam(Pairing.Team1.Value, Pairing, Pairing.Losers.Value), + new DrawableMatchTeam(Pairing.Team2.Value, Pairing, Pairing.Losers.Value) }; SchedulerAfterChildren.Add(() => Scheduler.Add(updateProgression)); diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index 461283ffed..63d15781ca 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -3,7 +3,7 @@ using System; using osu.Framework.Allocation; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; @@ -52,7 +52,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [Resolved(CanBeNull = true)] private LadderEditorInfo editorInfo { get; set; } - public DrawableMatchTeam(Bindable team, MatchPairing pairing, bool losers) + public DrawableMatchTeam(TournamentTeam team, MatchPairing pairing, bool losers) : base(team) { this.pairing = pairing; @@ -74,8 +74,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components isWinner = () => pairing.Winner == Team; completed.BindTo(pairing.Completed); - if (team.Value != null) - score.BindTo(team.Value == pairing.Team1.Value ? pairing.Team1Score : pairing.Team2Score); + if (team != null) + score.BindTo(team == pairing.Team1.Value ? pairing.Team1Score : pairing.Team2Score); } } @@ -133,7 +133,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components score.BindValueChanged(val => { - scoreText.Text = val?.ToString() ?? string.Empty; + scoreText.Text = val.NewValue?.ToString() ?? string.Empty; updateWinStyle(); }, true); } @@ -155,7 +155,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { pairing.StartMatch(); } - else if (!pairing.Completed) + else if (!pairing.Completed.Value) score.Value++; } else @@ -164,7 +164,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components // don't allow changing scores if the match has a progression. can cause large data loss return false; - if (pairing.Completed && pairing.Winner != Team) + if (pairing.Completed.Value && pairing.Winner != Team) // don't allow changing scores from the non-winner return false; @@ -179,7 +179,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private void updateWinStyle() { - bool winner = completed && isWinner?.Invoke() == true; + bool winner = completed.Value && isWinner?.Invoke() == true; background.FadeColour(winner ? colourWinner : colourNormal, winner ? 500 : 0, Easing.OutQuint); diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs index 1fd9455195..6979aae295 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs @@ -1,7 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Configuration; +using osu.Framework.Bindables; namespace osu.Game.Tournament.Screens.Ladder.Components { diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index bb80a845e3..f0559e0266 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -4,7 +4,7 @@ using System; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -93,11 +93,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components editorInfo.Selected.ValueChanged += selection => { - textboxTeam1.Text = selection?.Team1.Value?.Acronym; - textboxTeam2.Text = selection?.Team2.Value?.Acronym; - groupingDropdown.Bindable.Value = selection?.Grouping.Value ?? groupingOptions.First(); - losersCheckbox.Current.Value = selection?.Losers.Value ?? false; - dateTimeBox.Bindable.Value = selection?.Date.Value ?? DateTimeOffset.UtcNow; + textboxTeam1.Text = selection.NewValue?.Team1.Value?.Acronym; + textboxTeam2.Text = selection.NewValue?.Team2.Value?.Acronym; + groupingDropdown.Bindable.Value = selection.NewValue?.Grouping.Value ?? groupingOptions.First(); + losersCheckbox.Current.Value = selection.NewValue?.Losers.Value ?? false; + dateTimeBox.Bindable.Value = selection.NewValue?.Date.Value ?? DateTimeOffset.UtcNow; }; textboxTeam1.OnCommit = (val, newText) => @@ -116,10 +116,10 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { if (editorInfo.Selected.Value != null) { - editorInfo.Selected.Value.Grouping.Value = grouping; - if (editorInfo.Selected.Value.Date.Value < grouping.StartDate.Value) + editorInfo.Selected.Value.Grouping.Value = grouping.NewValue; + if (editorInfo.Selected.Value.Date.Value < grouping.NewValue.StartDate.Value) { - editorInfo.Selected.Value.Date.Value = grouping.StartDate.Value; + editorInfo.Selected.Value.Date.Value = grouping.NewValue.StartDate.Value; editorInfo.Selected.TriggerChange(); } } @@ -128,13 +128,13 @@ namespace osu.Game.Tournament.Screens.Ladder.Components losersCheckbox.Current.ValueChanged += losers => { if (editorInfo.Selected.Value != null) - editorInfo.Selected.Value.Losers.Value = losers; + editorInfo.Selected.Value.Losers.Value = losers.NewValue; }; dateTimeBox.Bindable.ValueChanged += date => { if (editorInfo.Selected.Value != null) - editorInfo.Selected.Value.Date.Value = date; + editorInfo.Selected.Value.Date.Value = date.NewValue; }; } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index 7d6acc5bd2..760f6d42f4 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using Newtonsoft.Json; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Game.Tournament.Components; using SixLabors.Primitives; @@ -71,8 +71,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public MatchPairing() { - Team1.BindValueChanged(t => Team1Acronym = t?.Acronym, true); - Team2.BindValueChanged(t => Team2Acronym = t?.Acronym, true); + Team1.BindValueChanged(t => Team1Acronym = t.NewValue?.Acronym, true); + Team2.BindValueChanged(t => Team2Acronym = t.NewValue?.Acronym, true); } public MatchPairing(TournamentTeam team1 = null, TournamentTeam team2 = null) @@ -88,7 +88,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [JsonIgnore] public TournamentTeam Loser => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team2.Value : Team1.Value; - public int PointsToWin => Grouping.Value == null ? 0 : Grouping.Value.BestOf / 2 + 1; + public int PointsToWin => Grouping.Value?.BestOf.Value / 2 + 1 ?? 0; /// /// Remove scores from the match, in case of a false click or false start. diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs index 370f0ea643..3555e4db11 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using Newtonsoft.Json; -using osu.Framework.Configuration; +using osu.Framework.Bindables; namespace osu.Game.Tournament.Screens.Ladder.Components { diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs index 36baaf0b39..7c6aabbe53 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -98,13 +98,13 @@ namespace osu.Game.Tournament.Screens.Ladder // clean up outdated progressions. pairing.Pairing.Progression.Value = null; else - paths.Add(new ProgressionPath(pairing, dest) { Colour = pairing.Pairing.Losers ? losersPathColour : normalPathColour }); + paths.Add(new ProgressionPath(pairing, dest) { Colour = pairing.Pairing.Losers.Value ? losersPathColour : normalPathColour }); } } foreach (var group in LadderInfo.Groupings) { - var topPairing = PairingsContainer.Where(p => !p.Pairing.Losers && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); + var topPairing = PairingsContainer.Where(p => !p.Pairing.Losers.Value && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); if (topPairing == null) continue; @@ -118,7 +118,7 @@ namespace osu.Game.Tournament.Screens.Ladder foreach (var group in LadderInfo.Groupings) { - var topPairing = PairingsContainer.Where(p => p.Pairing.Losers && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); + var topPairing = PairingsContainer.Where(p => p.Pairing.Losers.Value && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); if (topPairing == null) continue; diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index bed2c35b76..41368eb96e 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -3,7 +3,7 @@ using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -102,13 +102,13 @@ namespace osu.Game.Tournament.Screens.MapPool ipc.Beatmap.BindValueChanged(beatmapChanged); } - private void beatmapChanged(BeatmapInfo beatmap) + private void beatmapChanged(ValueChangedEvent beatmap) { if (currentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) < 2) return; - if (beatmap.OnlineBeatmapID != null) - addForBeatmap(beatmap.OnlineBeatmapID.Value); + if (beatmap.NewValue.OnlineBeatmapID != null) + addForBeatmap(beatmap.NewValue.OnlineBeatmapID.Value); } private void setMode(TeamColour colour, ChoiceType choiceType) @@ -201,16 +201,16 @@ namespace osu.Game.Tournament.Screens.MapPool } } - private void matchChanged(MatchPairing match) + private void matchChanged(ValueChangedEvent match) { mapFlows.Clear(); - if (match.Grouping.Value != null) + if (match.NewValue.Grouping.Value != null) { FillFlowContainer currentFlow = null; string currentMod = null; - foreach (var b in match.Grouping.Value.Beatmaps) + foreach (var b in match.NewValue.Grouping.Value.Beatmaps) { if (currentFlow == null || currentMod != b.Mods) { diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index f3f3667db1..def2161ea0 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -4,7 +4,7 @@ using System; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -48,9 +48,9 @@ namespace osu.Game.Tournament.Screens.Schedule currentMatch.BindTo(ladder.CurrentMatch); } - private void matchChanged(MatchPairing pairing) + private void matchChanged(ValueChangedEvent pairing) { - if (pairing == null) + if (pairing.NewValue == null) { mainContainer.Clear(); return; @@ -113,10 +113,10 @@ namespace osu.Game.Tournament.Screens.Schedule Colour = Color4.Black, TextSize = 20 }, - new SchedulePairing(currentMatch, false), + new SchedulePairing(currentMatch.Value, false), new OsuSpriteText { - Text = "Start Time " + pairing.Date.Value.ToUniversalTime().ToString("HH:mm UTC"), + Text = "Start Time " + pairing.NewValue.Date.Value.ToUniversalTime().ToString("HH:mm UTC"), Colour = Color4.Black, TextSize = 20 }, diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index dc79d4aab0..a42c7775e6 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Video; @@ -46,9 +46,9 @@ namespace osu.Game.Tournament.Screens.TeamIntro currentMatch.BindTo(ladder.CurrentMatch); } - private void matchChanged(MatchPairing pairing) + private void matchChanged(ValueChangedEvent pairing) { - if (pairing == null) + if (pairing.NewValue == null) { mainContainer.Clear(); return; @@ -56,7 +56,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro mainContainer.Children = new Drawable[] { - new TeamWithPlayers(pairing.Team1, true) + new TeamWithPlayers(pairing.NewValue.Team1.Value, true) { RelativeSizeAxes = Axes.Both, Width = 0.5f, @@ -64,7 +64,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = Anchor.Centre, Origin = Anchor.CentreRight }, - new TeamWithPlayers(pairing.Team2) + new TeamWithPlayers(pairing.NewValue.Team2.Value) { RelativeSizeAxes = Axes.Both, Width = 0.5f, @@ -72,7 +72,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = Anchor.Centre, Origin = Anchor.CentreLeft }, - new RoundDisplay(pairing) + new RoundDisplay(pairing.NewValue) { RelativeSizeAxes = Axes.Both, Height = 0.25f, diff --git a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs index 2e2c8e5020..f0f46f2dc8 100644 --- a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs +++ b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Video; @@ -62,10 +62,10 @@ namespace osu.Game.Tournament.Screens.TeamWin currentCompleted.BindValueChanged(_ => update()); } - private void matchChanged(MatchPairing pairing) + private void matchChanged(ValueChangedEvent pairing) { currentCompleted.UnbindBindings(); - currentCompleted.BindTo(pairing.Completed); + currentCompleted.BindTo(pairing.NewValue.Completed); update(); } diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 5c9bee560e..bf6c02d064 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using Newtonsoft.Json; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; From f1832103622d5e7816c570b73f6ebf371aa9ffa2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Mar 2019 13:50:43 +0900 Subject: [PATCH 0351/2854] Fix test bindables --- osu.Game.Tournament.Tests/TestCaseTeamIntro.cs | 4 ++-- osu.Game.Tournament.Tests/TestCaseTeamWin.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs b/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs index 004009f269..d8d5007238 100644 --- a/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs +++ b/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs @@ -3,7 +3,7 @@ using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Tournament.Screens.Ladder.Components; using osu.Game.Tournament.Screens.TeamIntro; @@ -21,7 +21,7 @@ namespace osu.Game.Tournament.Tests var pairing = new MatchPairing(); pairing.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == "USA"); pairing.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == "JPN"); - pairing.Grouping.Value = Ladder.Groupings.FirstOrDefault(g => g.Name == "Finals"); + pairing.Grouping.Value = Ladder.Groupings.FirstOrDefault(g => g.Name.Value == "Finals"); currentMatch.Value = pairing; Add(new TeamIntroScreen diff --git a/osu.Game.Tournament.Tests/TestCaseTeamWin.cs b/osu.Game.Tournament.Tests/TestCaseTeamWin.cs index 48ae9acb91..fb798d3587 100644 --- a/osu.Game.Tournament.Tests/TestCaseTeamWin.cs +++ b/osu.Game.Tournament.Tests/TestCaseTeamWin.cs @@ -3,7 +3,7 @@ using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Tournament.Screens.Ladder.Components; using osu.Game.Tournament.Screens.TeamWin; @@ -21,7 +21,7 @@ namespace osu.Game.Tournament.Tests var pairing = new MatchPairing(); pairing.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == "USA"); pairing.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == "JPN"); - pairing.Grouping.Value = Ladder.Groupings.FirstOrDefault(g => g.Name == "Finals"); + pairing.Grouping.Value = Ladder.Groupings.FirstOrDefault(g => g.Name.Value == "Finals"); currentMatch.Value = pairing; Add(new TeamWinScreen From 4c66ebb501187edc9b6a5cc2ebe8927f4a3e600e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Mar 2019 13:52:56 +0900 Subject: [PATCH 0352/2854] Fix formatting issues --- osu.Game.Tournament/Components/TournamentTeam.cs | 8 ++++---- osu.Game.Tournament/IPC/FileBasedIPC.cs | 1 - osu.Game.Tournament/Screens/Drawings/Components/Group.cs | 3 ++- .../Screens/Ladder/Components/DrawableMatchPairing.cs | 1 + osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs | 9 +++++---- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentTeam.cs b/osu.Game.Tournament/Components/TournamentTeam.cs index 62dc703fee..73c24282c4 100644 --- a/osu.Game.Tournament/Components/TournamentTeam.cs +++ b/osu.Game.Tournament/Components/TournamentTeam.cs @@ -23,8 +23,8 @@ namespace osu.Game.Tournament.Components /// public string FlagName { - get { return flagName ?? Acronym?.Substring(0, 2); } - set { flagName = value; } + get => flagName ?? Acronym?.Substring(0, 2); + set => flagName = value; } private string acronym; @@ -34,8 +34,8 @@ namespace osu.Game.Tournament.Components /// public string Acronym { - get { return acronym ?? FullName?.Substring(0, 3); } - set { acronym = value; } + get => acronym ?? FullName?.Substring(0, 3); + set => acronym = value; } [JsonProperty] diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 7f8ed0cb3d..da66460acb 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -129,7 +129,6 @@ namespace osu.Game.Tournament.IPC { protected override string LocateBasePath() { - bool checkExists(string p) { return File.Exists(Path.Combine(p, "ipc.txt")); diff --git a/osu.Game.Tournament/Screens/Drawings/Components/Group.cs b/osu.Game.Tournament/Screens/Drawings/Components/Group.cs index a701731d1e..ca092cdf02 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/Group.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/Group.cs @@ -121,7 +121,8 @@ namespace osu.Game.Tournament.Screens.Drawings.Components { private readonly FillFlowContainer innerContainer; - public GroupTeam(TournamentTeam team) : base(team) + public GroupTeam(TournamentTeam team) + : base(team) { Width = 36; AutoSizeAxes = Axes.Y; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index d993fbda20..9d3af60427 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -111,6 +111,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components set { if (value == selected) return; + selected = value; if (selected) diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index def2161ea0..05343117bf 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -57,8 +57,9 @@ namespace osu.Game.Tournament.Screens.Schedule } var upcoming = ladder.Pairings.Where(p => !p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4); - var conditionals = ladder.Pairings.Where(p => !p.Completed.Value && (p.Team1.Value == null || p.Team2.Value == null) && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4) - .SelectMany(m => m.ConditionalPairings.Where(cp => m.Acronyms.TrueForAll(a => cp.Acronyms.Contains(a)))); + var conditionals = ladder + .Pairings.Where(p => !p.Completed.Value && (p.Team1.Value == null || p.Team2.Value == null) && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4) + .SelectMany(m => m.ConditionalPairings.Where(cp => m.Acronyms.TrueForAll(a => cp.Acronyms.Contains(a)))); upcoming = upcoming.Concat(conditionals); upcoming = upcoming.OrderBy(p => p.Date.Value).Take(12); @@ -145,7 +146,7 @@ namespace osu.Game.Tournament.Screens.Schedule Anchor = Anchor.TopRight, Origin = Anchor.TopLeft, Colour = Color4.Black, - Alpha = conditional ? 0.6f : 1, + Alpha = conditional ? 0.6f : 1, Margin = new MarginPadding { Horizontal = 10, Vertical = 5 }, }); AddInternal(new OsuSpriteText @@ -153,7 +154,7 @@ namespace osu.Game.Tournament.Screens.Schedule Anchor = Anchor.BottomRight, Origin = Anchor.BottomLeft, Colour = Color4.Black, - Alpha = conditional ? 0.6f : 1, + Alpha = conditional ? 0.6f : 1, Margin = new MarginPadding { Horizontal = 10, Vertical = 5 }, Text = pairing.Date.Value.ToUniversalTime().ToString("HH:mm UTC") + (conditional ? " (conditional)" : "") }); From 73d266fe1041f7c56571f241a558de29ade43b53 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Mar 2019 16:32:11 +0900 Subject: [PATCH 0353/2854] Fix win screen being incorrectly displayed after switching matches --- .../Screens/Gameplay/GameplayScreen.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index eb33c2fbdb..2b6d16505a 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -120,7 +120,12 @@ namespace osu.Game.Tournament.Screens.Gameplay State.BindTo(ipc.State); State.BindValueChanged(stateChanged, true); - currentMatch.BindValueChanged(m => warmup.Value = m.NewValue.Team1Score.Value + m.NewValue.Team2Score.Value == 0); + currentMatch.BindValueChanged(m => + { + warmup.Value = m.NewValue.Team1Score.Value + m.NewValue.Team2Score.Value == 0; + scheduledOperation?.Cancel(); + }); + currentMatch.BindTo(ladder.CurrentMatch); warmup.BindValueChanged(w => warmupButton.Alpha = !w.NewValue ? 0.5f : 1, true); @@ -172,12 +177,16 @@ namespace osu.Game.Tournament.Screens.Gameplay case TourneyState.Idle: contract(); + const float delay_before_progression = 4000; + + // if we've returned to idle and the last screen was ranking + // we should automatically proceed after a short delay if (lastState == TourneyState.Ranking && !warmup.Value) { if (currentMatch.Value?.Completed.Value == true) - scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(TeamWinScreen)); }, 4000); + scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(TeamWinScreen)); }, delay_before_progression); else if (currentMatch.Value?.Completed.Value == false) - scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(MapPoolScreen)); }, 4000); + scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(MapPoolScreen)); }, delay_before_progression); } break; From 132ce541f3eca72d9a119aca7b4dbd64f0e895f5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Mar 2019 12:06:41 +0900 Subject: [PATCH 0354/2854] Fix obsolete font usages --- .../Components/ControlPanel.cs | 4 ++-- osu.Game.Tournament/Components/SongBar.cs | 5 ++--- .../Components/TournamentBeatmapPanel.cs | 12 ++++------- .../Screens/Drawings/Components/Group.cs | 7 +++---- .../Screens/Drawings/DrawingsScreen.cs | 3 +-- .../Gameplay/Components/MatchHeader.cs | 7 +++---- .../Gameplay/Components/MatchScoreDisplay.cs | 20 +++++-------------- .../Ladder/Components/DrawableMatchTeam.cs | 4 ++-- .../Screens/Schedule/ScheduleScreen.cs | 6 +++--- .../Screens/TeamIntro/TeamIntroScreen.cs | 16 ++++++--------- .../Screens/TeamWin/TeamWinScreen.cs | 14 +++++-------- osu.Game/Graphics/OsuFont.cs | 3 +++ 12 files changed, 39 insertions(+), 62 deletions(-) diff --git a/osu.Game.Tournament/Components/ControlPanel.cs b/osu.Game.Tournament/Components/ControlPanel.cs index 5f777fbf43..dee6cfbe0e 100644 --- a/osu.Game.Tournament/Components/ControlPanel.cs +++ b/osu.Game.Tournament/Components/ControlPanel.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; @@ -40,8 +41,7 @@ namespace osu.Game.Tournament.Components Origin = Anchor.TopCentre, Text = "Control Panel", - TextSize = 22f, - Font = "Exo2.0-Bold" + Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 22) }, buttons = new FillFlowContainer { diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index fc75bd59a4..a138dfaa1e 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -163,7 +163,7 @@ namespace osu.Game.Tournament.Components string hardRockExtra = ""; string srExtra = ""; - var ar = beatmap.BaseDifficulty.ApproachRate; + //var ar = beatmap.BaseDifficulty.ApproachRate; if ((mods & LegacyMods.HardRock) > 0) { hardRockExtra = "*"; @@ -225,8 +225,7 @@ namespace osu.Game.Tournament.Components void cp(SpriteText s, Color4 colour) { s.Colour = colour; - s.TextSize = 15; - s.Font = @"Exo2.0-Bold"; + s.Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 15); } bool first = true; diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 94aa86d5cb..6b80094990 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -100,29 +100,25 @@ namespace osu.Game.Tournament.Components new OsuSpriteText { Text = "mapper", - Font = @"Exo2.0-RegularItalic", Padding = new MarginPadding { Right = 5 }, - TextSize = 14 + Font = OsuFont.GetFont(italics: true, weight: FontWeight.Regular, size: 14) }, new OsuSpriteText { Text = Beatmap.Metadata.AuthorString, - Font = @"Exo2.0-BoldItalic", Padding = new MarginPadding { Right = 20 }, - TextSize = 14 + Font = OsuFont.GetFont(italics: true, weight: FontWeight.Bold, size: 14) }, new OsuSpriteText { Text = "difficulty", - Font = @"Exo2.0-RegularItalic", Padding = new MarginPadding { Right = 5 }, - TextSize = 14 + Font = OsuFont.GetFont(italics: true, weight: FontWeight.Regular, size: 14) }, new OsuSpriteText { Text = Beatmap.Version, - Font = @"Exo2.0-BoldItalic", - TextSize = 14 + Font = OsuFont.GetFont(italics: true, weight: FontWeight.Bold, size: 14) }, } } diff --git a/osu.Game.Tournament/Screens/Drawings/Components/Group.cs b/osu.Game.Tournament/Screens/Drawings/Components/Group.cs index ca092cdf02..36ab7482a0 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/Group.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/Group.cs @@ -7,6 +7,7 @@ using System.Text; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Tournament.Components; using osuTK; @@ -49,8 +50,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components Position = new Vector2(0, 7f), Text = $"GROUP {name.ToUpperInvariant()}", - TextSize = 8f, - Font = @"Exo2.0-Bold", + Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 8), Colour = new Color4(255, 204, 34, 255), }, teams = new FillFlowContainer @@ -134,8 +134,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components AcronymText.Anchor = Anchor.TopCentre; AcronymText.Origin = Anchor.TopCentre; AcronymText.Text = team.Acronym.ToUpperInvariant(); - AcronymText.TextSize = 10f; - AcronymText.Font = @"Exo2.0-Bold"; + AcronymText.Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 10); InternalChildren = new Drawable[] { diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index f68adabb4c..c30caa5a00 100644 --- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -120,8 +120,7 @@ namespace osu.Game.Tournament.Screens.Drawings Alpha = 0, - Font = "Exo2.0-Light", - TextSize = 42f + Font = OsuFont.GetFont(weight: FontWeight.Light, size: 42), } } }, diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index 364d437619..5bbdbe104f 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; @@ -174,9 +175,8 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components Text = team?.FullName.ToUpper() ?? "???", X = (flip ? -1 : 1) * 90, Y = -10, - TextSize = 20, Colour = colour, - Font = "Aquatico-Regular", + Font = OsuFont.GetFont(typeface: Typeface.Aquatico, weight: FontWeight.Regular, size: 20), Origin = anchor, Anchor = anchor, }, @@ -219,8 +219,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components Origin = Anchor.Centre, Colour = Color4.White, Text = match.NewValue.Grouping.Value?.Name.Value ?? "Unknown Grouping", - Font = "Aquatico-Regular", - TextSize = 18, + Font = OsuFont.GetFont(typeface: Typeface.Aquatico, weight: FontWeight.Regular, size: 18), }, }; } diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs index 30cd2b2e85..a648072d04 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Screens.Ladder.Components; @@ -119,26 +120,15 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components public MatchScoreCounter() { Margin = new MarginPadding { Top = bar_height + 5, Horizontal = 10 }; - Winning = false; - DisplayedCountSpriteText.FixedWidth = false; + Winning = false; } public bool Winning { - set - { - if (value) - { - DisplayedCountSpriteText.Font = "Aquatico-Regular"; - DisplayedCountSpriteText.TextSize = 60; - } - else - { - DisplayedCountSpriteText.Font = "Aquatico-Light"; - DisplayedCountSpriteText.TextSize = 40; - } - } + set => DisplayedCountSpriteText.Font = value + ? OsuFont.GetFont(typeface: Typeface.Aquatico, weight: FontWeight.Regular, size: 60) + : OsuFont.GetFont(typeface: Typeface.Aquatico, weight: FontWeight.Light, size: 40); } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index 63d15781ca..c4097848d7 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -67,7 +67,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components AcronymText.Anchor = AcronymText.Origin = Anchor.CentreLeft; AcronymText.Padding = new MarginPadding { Left = 50 }; - AcronymText.TextSize = 24; + AcronymText.Font = OsuFont.GetFont(size: 24); if (pairing != null) { @@ -121,7 +121,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { Anchor = Anchor.Centre, Origin = Anchor.Centre, - TextSize = 20, + Font = OsuFont.GetFont(size: 20), } } } diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index 05343117bf..384f2f7ab2 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -112,14 +112,14 @@ namespace osu.Game.Tournament.Screens.Schedule Spacing = new Vector2(10, 0), Text = currentMatch.Value.Grouping.Value.Name.Value, Colour = Color4.Black, - TextSize = 20 + Font = OsuFont.GetFont(size: 20) }, new SchedulePairing(currentMatch.Value, false), new OsuSpriteText { Text = "Start Time " + pairing.NewValue.Date.Value.ToUniversalTime().ToString("HH:mm UTC"), Colour = Color4.Black, - TextSize = 20 + Font = OsuFont.GetFont(size: 20) }, } } @@ -179,7 +179,7 @@ namespace osu.Game.Tournament.Screens.Schedule Text = title, Colour = Color4.Black, Spacing = new Vector2(10, 0), - TextSize = 30 + Font = OsuFont.GetFont(size: 30) }, content = new FillFlowContainer { diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index a42c7775e6..94130cb383 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -104,10 +104,9 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Colour = col, - Font = "Exo2.0-Black", Text = "COMING UP NEXT", Spacing = new Vector2(2, 0), - TextSize = 15, + Font = OsuFont.GetFont(size: 15, weight: FontWeight.Black) }, new OsuSpriteText { @@ -115,9 +114,8 @@ namespace osu.Game.Tournament.Screens.TeamIntro Origin = Anchor.TopCentre, Colour = col, Text = pairing.Grouping.Value?.Name.Value ?? "Unknown Grouping", - Font = "Exo2.0-Light", Spacing = new Vector2(10, 0), - TextSize = 50, + Font = OsuFont.GetFont(size: 50, weight: FontWeight.Light) }, new OsuSpriteText { @@ -125,7 +123,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Origin = Anchor.TopCentre, Colour = col, Text = pairing.Date.Value.ToUniversalTime().ToString("dd MMMM HH:mm UTC"), - TextSize = 20, + Font = OsuFont.GetFont(size: 20) }, } } @@ -170,7 +168,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro players.Add(new OsuSpriteText { Text = p.Username, - TextSize = 24, + Font = OsuFont.GetFont(size: 24), Colour = colour, Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft, Origin = left ? Anchor.CentreRight : Anchor.CentreLeft, @@ -202,17 +200,15 @@ namespace osu.Game.Tournament.Screens.TeamIntro new OsuSpriteText { Text = team?.FullName.ToUpper() ?? "???", - TextSize = 40, + Font = OsuFont.GetFont(Typeface.Aquatico, 40, FontWeight.Light), Colour = Color4.Black, - Font = "Aquatico-Light", Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, }, new OsuSpriteText { Text = teamName.ToUpper(), - TextSize = 20, - Font = "Aquatico-Regular", + Font = OsuFont.GetFont(Typeface.Aquatico, 20, FontWeight.Regular), Colour = colour, Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, diff --git a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs index f0f46f2dc8..1de9b6ae5c 100644 --- a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs +++ b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs @@ -127,8 +127,7 @@ namespace osu.Game.Tournament.Screens.TeamWin Origin = Anchor.TopCentre, Colour = col, Text = "WINNER", - Font = "Aquatico-Regular", - TextSize = 15, + Font = OsuFont.GetFont(Typeface.Aquatico, 15, FontWeight.Regular), }, new OsuSpriteText { @@ -136,18 +135,16 @@ namespace osu.Game.Tournament.Screens.TeamWin Origin = Anchor.TopCentre, Colour = col, Text = pairing.Grouping.Value?.Name.Value ?? "Unknown Grouping", - Font = "Aquatico-Light", + Font = OsuFont.GetFont(Typeface.Aquatico, 50, FontWeight.Light), Spacing = new Vector2(10, 0), - TextSize = 50, }, new OsuSpriteText { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Font = "Aquatico-Light", Colour = col, Text = pairing.Date.Value.ToUniversalTime().ToString("dd MMMM HH:mm UTC"), - TextSize = 20, + Font = OsuFont.GetFont(Typeface.Aquatico, 20, FontWeight.Light), }, } } @@ -207,16 +204,15 @@ namespace osu.Game.Tournament.Screens.TeamWin new OsuSpriteText { Text = team?.FullName.ToUpper() ?? "???", - TextSize = 40, + Font = OsuFont.GetFont(Typeface.Aquatico, 40, FontWeight.Light), Colour = Color4.Black, - Font = "Aquatico-Light", Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, }, new OsuSpriteText { Text = teamName.ToUpper(), - TextSize = 20, + Font = OsuFont.GetFont(size: 20), Colour = colour, Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, diff --git a/osu.Game/Graphics/OsuFont.cs b/osu.Game/Graphics/OsuFont.cs index dc660fd159..22937333d7 100644 --- a/osu.Game/Graphics/OsuFont.cs +++ b/osu.Game/Graphics/OsuFont.cs @@ -46,6 +46,8 @@ namespace osu.Game.Graphics return "FontAwesome"; case Typeface.Venera: return "Venera"; + case Typeface.Aquatico: + return "Aquatico"; } return null; @@ -103,6 +105,7 @@ namespace osu.Game.Graphics Exo, FontAwesome, Venera, + Aquatico // tournament use only } public enum FontWeight From 5c8cbd43c8fab1e0d14e0cc85fb939bcd58dbace Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Mar 2019 13:02:43 +0900 Subject: [PATCH 0355/2854] Enforce max consecutive blank lines --- osu.sln.DotSettings | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 5363d6dddf..3c6a6dd2a9 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -230,6 +230,8 @@ True NEXT_LINE NEXT_LINE + 1 + 1 True NEVER NEVER From 5b81de7663cb0ebcbce195df7dd2177f147a9f7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Mar 2019 13:04:07 +0900 Subject: [PATCH 0356/2854] Apply codefactor fixes --- osu.Game.Tournament/Components/SongBar.cs | 2 -- osu.Game.Tournament/Screens/Drawings/Components/Group.cs | 1 - osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs | 1 - .../Screens/Ladder/Components/DrawableMatchPairing.cs | 1 - 4 files changed, 5 deletions(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index a138dfaa1e..452565d7d8 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -80,7 +80,6 @@ namespace osu.Game.Tournament.Components } } - [BackgroundDependencyLoader] private void load() { @@ -247,4 +246,3 @@ namespace osu.Game.Tournament.Components } } } - diff --git a/osu.Game.Tournament/Screens/Drawings/Components/Group.cs b/osu.Game.Tournament/Screens/Drawings/Components/Group.cs index 36ab7482a0..50e413afea 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/Group.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/Group.cs @@ -127,7 +127,6 @@ namespace osu.Game.Tournament.Screens.Drawings.Components Width = 36; AutoSizeAxes = Axes.Y; - Flag.Anchor = Anchor.TopCentre; Flag.Origin = Anchor.TopCentre; diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index 5bbdbe104f..89168aa8bb 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -87,7 +87,6 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components teamChanged(currentTeam.Value); } - protected override bool OnMouseDown(MouseDownEvent e) { switch (e.Button) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 9d3af60427..0244403bff 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -30,7 +30,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [Resolved(CanBeNull = true)] private LadderInfo ladderInfo { get; set; } - public DrawableMatchPairing(MatchPairing pairing, bool editor = false) { Pairing = pairing; From 8bf49830d5e994eb1a6100779947da9da8b97802 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Mar 2019 13:13:31 +0900 Subject: [PATCH 0357/2854] Simplify and extract complex method --- osu.Game.Tournament/TournamentGameBase.cs | 144 ++++++++++++++-------- 1 file changed, 93 insertions(+), 51 deletions(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index bf6c02d064..e3996bc572 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -27,7 +27,8 @@ namespace osu.Game.Tournament { private const string bracket_filename = "bracket.json"; - protected LadderInfo Ladder; + private LadderInfo ladder; + private Storage storage; private DependencyContainer dependencies; @@ -57,42 +58,61 @@ namespace osu.Game.Tournament windowSize = frameworkConfig.GetBindable(FrameworkSetting.WindowedSize); - string content = null; - if (storage.Exists(bracket_filename)) - using (Stream stream = storage.GetStream(bracket_filename, FileAccess.Read, FileMode.Open)) - using (var sr = new StreamReader(stream)) - { - content = sr.ReadToEnd(); - } + readBracket(); - Ladder = content != null ? JsonConvert.DeserializeObject(content) : new LadderInfo(); - - dependencies.Cache(Ladder); + ladder.CurrentMatch.Value = ladder.Pairings.FirstOrDefault(p => p.Current.Value); dependencies.CacheAs(ipc = new FileBasedIPC()); Add(ipc); + Add(new OsuButton + { + Text = "Save Changes", + Width = 140, + Height = 50, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Padding = new MarginPadding(10), + Action = SaveChanges, + }); + } + + private void readBracket() + { + if (storage.Exists(bracket_filename)) + { + using (Stream stream = storage.GetStream(bracket_filename, FileAccess.Read, FileMode.Open)) + using (var sr = new StreamReader(stream)) + ladder = JsonConvert.DeserializeObject(sr.ReadToEnd()); + } + else + { + ladder = new LadderInfo(); + } + + dependencies.Cache(ladder); + bool addedInfo = false; // assign teams - foreach (var pairing in Ladder.Pairings) + foreach (var pairing in ladder.Pairings) { - pairing.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == pairing.Team1Acronym); - pairing.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == pairing.Team2Acronym); + pairing.Team1.Value = ladder.Teams.FirstOrDefault(t => t.Acronym == pairing.Team1Acronym); + pairing.Team2.Value = ladder.Teams.FirstOrDefault(t => t.Acronym == pairing.Team2Acronym); foreach (var conditional in pairing.ConditionalPairings) { - conditional.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == conditional.Team1Acronym); - conditional.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym == conditional.Team2Acronym); + conditional.Team1.Value = ladder.Teams.FirstOrDefault(t => t.Acronym == conditional.Team1Acronym); + conditional.Team2.Value = ladder.Teams.FirstOrDefault(t => t.Acronym == conditional.Team2Acronym); conditional.Grouping.Value = pairing.Grouping.Value; } } // assign progressions - foreach (var pair in Ladder.Progressions) + foreach (var pair in ladder.Progressions) { - var src = Ladder.Pairings.FirstOrDefault(p => p.ID == pair.Item1); - var dest = Ladder.Pairings.FirstOrDefault(p => p.ID == pair.Item2); + var src = ladder.Pairings.FirstOrDefault(p => p.ID == pair.Item1); + var dest = ladder.Pairings.FirstOrDefault(p => p.ID == pair.Item2); if (src == null) throw new InvalidOperationException(); @@ -106,10 +126,10 @@ namespace osu.Game.Tournament } // link pairings to groupings - foreach (var group in Ladder.Groupings) + foreach (var group in ladder.Groupings) foreach (var id in group.Pairings) { - var found = Ladder.Pairings.FirstOrDefault(p => p.ID == id); + var found = ladder.Pairings.FirstOrDefault(p => p.ID == id); if (found != null) { found.Grouping.Value = group; @@ -118,10 +138,23 @@ namespace osu.Game.Tournament } } - Ladder.CurrentMatch.Value = Ladder.Pairings.FirstOrDefault(p => p.Current.Value); + addedInfo |= addPlayers(); + addedInfo |= addBeatmaps(); + addedInfo |= addCountries(); - // add full player info based on user IDs - foreach (var t in Ladder.Teams) + if (addedInfo) + SaveChanges(); + } + + /// + /// Add missing player info based on user IDs. + /// + /// + private bool addPlayers() + { + bool addedInfo = false; + + foreach (var t in ladder.Teams) foreach (var p in t.Players) if (string.IsNullOrEmpty(p.Username)) { @@ -132,8 +165,16 @@ namespace osu.Game.Tournament addedInfo = true; } - // add full beatmap info based on beatmap IDs - foreach (var g in Ladder.Groupings) + return addedInfo; + } + + /// + /// Add missing beatmap info based on beatmap IDs + /// + private bool addBeatmaps() + { + bool addedInfo = false; + foreach (var g in ladder.Groupings) foreach (var b in g.Beatmaps) if (b.BeatmapInfo == null) { @@ -144,36 +185,37 @@ namespace osu.Game.Tournament addedInfo = true; } + return addedInfo; + } + + /// + /// Add missing country info based on acronyms. + /// + private bool addCountries() + { + bool addedInfo = false; + List countries; using (Stream stream = Resources.GetStream("Resources/countries.json")) using (var sr = new StreamReader(stream)) countries = JsonConvert.DeserializeObject>(sr.ReadToEnd()); - foreach (var t in Ladder.Teams) - if (string.IsNullOrEmpty(t.FullName)) - { - var result = countries.FirstOrDefault(c => c.Acronym == t.Acronym); - if (result != null) - { - t.Acronym = result.Acronym; - t.FlagName = result.FlagName; - t.FullName = result.FullName; - } - } - - if (addedInfo) - SaveChanges(); - - Add(new OsuButton + foreach (var t in ladder.Teams) { - Text = "Save Changes", - Width = 140, - Height = 50, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Padding = new MarginPadding(10), - Action = SaveChanges, - }); + if (!string.IsNullOrEmpty(t.FullName)) + continue; + + var result = countries.FirstOrDefault(c => c.Acronym == t.Acronym); + + if (result == null) continue; + + t.Acronym = result.Acronym; + t.FlagName = result.FlagName; + t.FullName = result.FullName; + addedInfo = true; + } + + return addedInfo; } protected override void LoadComplete() @@ -198,7 +240,7 @@ namespace osu.Game.Tournament using (var stream = storage.GetStream(bracket_filename, FileAccess.Write, FileMode.Create)) using (var sw = new StreamWriter(stream)) { - sw.Write(JsonConvert.SerializeObject(Ladder, + sw.Write(JsonConvert.SerializeObject(ladder, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, From cf63ee4948a812edf153274112cbf83100fa18ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Mar 2019 13:24:19 +0900 Subject: [PATCH 0358/2854] Update licence headers --- osu.Game.Tournament.Tests/LadderTestCase.cs | 4 ++-- osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs | 4 ++-- osu.Game.Tournament.Tests/TestCaseGameplay.cs | 4 ++-- osu.Game.Tournament.Tests/TestCaseGroupingsEditorScreen.cs | 4 ++-- osu.Game.Tournament.Tests/TestCaseLadderManager.cs | 4 ++-- osu.Game.Tournament.Tests/TestCaseMapPool.cs | 4 ++-- osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs | 4 ++-- osu.Game.Tournament.Tests/TestCaseMatchPairings.cs | 4 ++-- osu.Game.Tournament.Tests/TestCaseMatchScoreDisplay.cs | 4 ++-- osu.Game.Tournament.Tests/TestCaseSceneManager.cs | 4 ++-- osu.Game.Tournament.Tests/TestCaseSchedule.cs | 4 ++-- osu.Game.Tournament.Tests/TestCaseShowcase.cs | 4 ++-- osu.Game.Tournament.Tests/TestCaseTeamIntro.cs | 4 ++-- osu.Game.Tournament.Tests/TestCaseTeamWin.cs | 4 ++-- osu.Game.Tournament.Tests/TournamentTestBrowser.cs | 4 ++-- osu.Game.Tournament.Tests/TournamentTestRunner.cs | 4 ++-- osu.Game.Tournament/Components/ControlPanel.cs | 4 ++-- osu.Game.Tournament/Components/DateTextBox.cs | 4 ++-- osu.Game.Tournament/Components/DrawableTournamentTeam.cs | 4 ++-- osu.Game.Tournament/Components/MatchChatDisplay.cs | 4 ++-- osu.Game.Tournament/Components/SongBar.cs | 4 ++-- osu.Game.Tournament/Components/TournamentBeatmapPanel.cs | 4 ++-- osu.Game.Tournament/Components/TournamentTeam.cs | 4 ++-- osu.Game.Tournament/IPC/FileBasedIPC.cs | 4 ++-- osu.Game.Tournament/IPC/MatchIPCInfo.cs | 4 ++-- osu.Game.Tournament/IPC/TourneyState.cs | 4 ++-- osu.Game.Tournament/LadderInfo.cs | 4 ++-- osu.Game.Tournament/Properties/AssemblyInfo.cs | 4 ++-- osu.Game.Tournament/Screens/BeatmapInfoScreen.cs | 4 ++-- osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs | 4 ++-- .../Screens/Gameplay/Components/MatchHeader.cs | 4 ++-- .../Screens/Gameplay/Components/MatchScoreDisplay.cs | 4 ++-- osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs | 4 ++-- .../Screens/Groupings/GroupingsEditorScreen.cs | 4 ++-- osu.Game.Tournament/Screens/IProvideVideo.cs | 4 ++-- .../Screens/Ladder/Components/BeatmapChoice.cs | 4 ++-- .../Screens/Ladder/Components/ConditionalMatchPairing.cs | 4 ++-- .../Screens/Ladder/Components/DrawableMatchPairing.cs | 4 ++-- .../Screens/Ladder/Components/DrawableMatchTeam.cs | 4 ++-- .../Screens/Ladder/Components/DrawableTournamentGrouping.cs | 4 ++-- .../Screens/Ladder/Components/GroupingBeatmap.cs | 4 ++-- .../Screens/Ladder/Components/LadderEditorInfo.cs | 4 ++-- .../Screens/Ladder/Components/LadderEditorSettings.cs | 4 ++-- osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs | 4 ++-- .../Screens/Ladder/Components/ProgressionPath.cs | 4 ++-- .../Screens/Ladder/Components/TournamentGrouping.cs | 4 ++-- .../Screens/Ladder/Components/TournamentProgression.cs | 4 ++-- osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs | 4 ++-- osu.Game.Tournament/Screens/Ladder/LadderScreen.cs | 4 ++-- osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs | 4 ++-- osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs | 4 ++-- osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs | 4 ++-- osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs | 4 ++-- osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs | 4 ++-- osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs | 4 ++-- osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs | 4 ++-- osu.Game.Tournament/Screens/TournamentSceneManager.cs | 4 ++-- osu.Game.Tournament/Screens/TournamentScreen.cs | 4 ++-- osu.Game.Tournament/TournamentGame.cs | 4 ++-- osu.Game.Tournament/TournamentGameBase.cs | 4 ++-- 60 files changed, 120 insertions(+), 120 deletions(-) diff --git a/osu.Game.Tournament.Tests/LadderTestCase.cs b/osu.Game.Tournament.Tests/LadderTestCase.cs index acc96930ee..fc827150bd 100644 --- a/osu.Game.Tournament.Tests/LadderTestCase.cs +++ b/osu.Game.Tournament.Tests/LadderTestCase.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Game.Tests.Visual; diff --git a/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs b/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs index de80d36067..8bcc34bd7a 100644 --- a/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs +++ b/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; diff --git a/osu.Game.Tournament.Tests/TestCaseGameplay.cs b/osu.Game.Tournament.Tests/TestCaseGameplay.cs index eefcd79661..8e435de5e6 100644 --- a/osu.Game.Tournament.Tests/TestCaseGameplay.cs +++ b/osu.Game.Tournament.Tests/TestCaseGameplay.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; diff --git a/osu.Game.Tournament.Tests/TestCaseGroupingsEditorScreen.cs b/osu.Game.Tournament.Tests/TestCaseGroupingsEditorScreen.cs index 4e82e27fd9..d3ef011535 100644 --- a/osu.Game.Tournament.Tests/TestCaseGroupingsEditorScreen.cs +++ b/osu.Game.Tournament.Tests/TestCaseGroupingsEditorScreen.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Game.Tournament.Screens.Groupings; diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index 3001f46ed2..8704c3aec5 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Graphics; diff --git a/osu.Game.Tournament.Tests/TestCaseMapPool.cs b/osu.Game.Tournament.Tests/TestCaseMapPool.cs index d953dbc5d3..6d2f64e7a7 100644 --- a/osu.Game.Tournament.Tests/TestCaseMapPool.cs +++ b/osu.Game.Tournament.Tests/TestCaseMapPool.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; diff --git a/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs b/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs index d71e4b8006..0c3c189cf5 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; using osu.Framework.Allocation; diff --git a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs index b4a754e439..2dce0c6017 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; diff --git a/osu.Game.Tournament.Tests/TestCaseMatchScoreDisplay.cs b/osu.Game.Tournament.Tests/TestCaseMatchScoreDisplay.cs index 62c5ca786b..c7de5cf8ce 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchScoreDisplay.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchScoreDisplay.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Graphics; diff --git a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs index a19c933d8b..97d2018e3d 100644 --- a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Platform; diff --git a/osu.Game.Tournament.Tests/TestCaseSchedule.cs b/osu.Game.Tournament.Tests/TestCaseSchedule.cs index a12586cb27..f9dc447077 100644 --- a/osu.Game.Tournament.Tests/TestCaseSchedule.cs +++ b/osu.Game.Tournament.Tests/TestCaseSchedule.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; diff --git a/osu.Game.Tournament.Tests/TestCaseShowcase.cs b/osu.Game.Tournament.Tests/TestCaseShowcase.cs index dcd4b6aec7..51877cdfb6 100644 --- a/osu.Game.Tournament.Tests/TestCaseShowcase.cs +++ b/osu.Game.Tournament.Tests/TestCaseShowcase.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; diff --git a/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs b/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs index d8d5007238..78614518ce 100644 --- a/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs +++ b/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System.Linq; using osu.Framework.Allocation; diff --git a/osu.Game.Tournament.Tests/TestCaseTeamWin.cs b/osu.Game.Tournament.Tests/TestCaseTeamWin.cs index fb798d3587..df24877f09 100644 --- a/osu.Game.Tournament.Tests/TestCaseTeamWin.cs +++ b/osu.Game.Tournament.Tests/TestCaseTeamWin.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System.Linq; using osu.Framework.Allocation; diff --git a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs index 429adb2c0d..6d4063ec4f 100644 --- a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs +++ b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Testing; using osu.Game.Graphics; diff --git a/osu.Game.Tournament.Tests/TournamentTestRunner.cs b/osu.Game.Tournament.Tests/TournamentTestRunner.cs index 51c2c65cb4..1f63f7c545 100644 --- a/osu.Game.Tournament.Tests/TournamentTestRunner.cs +++ b/osu.Game.Tournament.Tests/TournamentTestRunner.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using osu.Framework; diff --git a/osu.Game.Tournament/Components/ControlPanel.cs b/osu.Game.Tournament/Components/ControlPanel.cs index dee6cfbe0e..0d228fb3b4 100644 --- a/osu.Game.Tournament/Components/ControlPanel.cs +++ b/osu.Game.Tournament/Components/ControlPanel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game.Tournament/Components/DateTextBox.cs b/osu.Game.Tournament/Components/DateTextBox.cs index 0a2485e4f3..f25c4b6e78 100644 --- a/osu.Game.Tournament/Components/DateTextBox.cs +++ b/osu.Game.Tournament/Components/DateTextBox.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using osu.Framework.Bindables; diff --git a/osu.Game.Tournament/Components/DrawableTournamentTeam.cs b/osu.Game.Tournament/Components/DrawableTournamentTeam.cs index 016db57773..a0c0856e7f 100644 --- a/osu.Game.Tournament/Components/DrawableTournamentTeam.cs +++ b/osu.Game.Tournament/Components/DrawableTournamentTeam.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Graphics; diff --git a/osu.Game.Tournament/Components/MatchChatDisplay.cs b/osu.Game.Tournament/Components/MatchChatDisplay.cs index 33c544f83d..dd567ed290 100644 --- a/osu.Game.Tournament/Components/MatchChatDisplay.cs +++ b/osu.Game.Tournament/Components/MatchChatDisplay.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System.Linq; using osu.Framework.Allocation; diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 452565d7d8..4844074372 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using osu.Framework.Allocation; diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 6b80094990..dd3ba1af47 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Collections.ObjectModel; diff --git a/osu.Game.Tournament/Components/TournamentTeam.cs b/osu.Game.Tournament/Components/TournamentTeam.cs index 73c24282c4..167f77c229 100644 --- a/osu.Game.Tournament/Components/TournamentTeam.cs +++ b/osu.Game.Tournament/Components/TournamentTeam.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index da66460acb..8cdd06003f 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.IO; diff --git a/osu.Game.Tournament/IPC/MatchIPCInfo.cs b/osu.Game.Tournament/IPC/MatchIPCInfo.cs index cd57756f60..701258c6c7 100644 --- a/osu.Game.Tournament/IPC/MatchIPCInfo.cs +++ b/osu.Game.Tournament/IPC/MatchIPCInfo.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; using osu.Framework.Graphics; diff --git a/osu.Game.Tournament/IPC/TourneyState.cs b/osu.Game.Tournament/IPC/TourneyState.cs index afa5b400ba..ef1c612a53 100644 --- a/osu.Game.Tournament/IPC/TourneyState.cs +++ b/osu.Game.Tournament/IPC/TourneyState.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. namespace osu.Game.Tournament.IPC { diff --git a/osu.Game.Tournament/LadderInfo.cs b/osu.Game.Tournament/LadderInfo.cs index 83d0f76020..9ddca460e2 100644 --- a/osu.Game.Tournament/LadderInfo.cs +++ b/osu.Game.Tournament/LadderInfo.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; using Newtonsoft.Json; diff --git a/osu.Game.Tournament/Properties/AssemblyInfo.cs b/osu.Game.Tournament/Properties/AssemblyInfo.cs index 4955391097..70e42bcafb 100644 --- a/osu.Game.Tournament/Properties/AssemblyInfo.cs +++ b/osu.Game.Tournament/Properties/AssemblyInfo.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System.Runtime.CompilerServices; diff --git a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs index b0aadc7e9e..fccd35ca9e 100644 --- a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs +++ b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Bindables; diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index c30caa5a00..d902defe94 100644 --- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index 89168aa8bb..dd12581046 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Bindables; diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs index a648072d04..f8b887b952 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using osu.Framework.Allocation; diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 2b6d16505a..444f080b6c 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Bindables; diff --git a/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs b/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs index 238de0d5bc..9c35ff21d4 100644 --- a/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Linq; diff --git a/osu.Game.Tournament/Screens/IProvideVideo.cs b/osu.Game.Tournament/Screens/IProvideVideo.cs index b5a4e1ad8e..c11c921412 100644 --- a/osu.Game.Tournament/Screens/IProvideVideo.cs +++ b/osu.Game.Tournament/Screens/IProvideVideo.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. namespace osu.Game.Tournament.Screens { diff --git a/osu.Game.Tournament/Screens/Ladder/Components/BeatmapChoice.cs b/osu.Game.Tournament/Screens/Ladder/Components/BeatmapChoice.cs index 5b38e539c6..bb9ed39b82 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/BeatmapChoice.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/BeatmapChoice.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using Newtonsoft.Json; using Newtonsoft.Json.Converters; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ConditionalMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/ConditionalMatchPairing.cs index bff661bcf4..7831cac84d 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/ConditionalMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/ConditionalMatchPairing.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. namespace osu.Game.Tournament.Screens.Ladder.Components { diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 0244403bff..cf9fe3f2be 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Bindables; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index c4097848d7..3078b58c45 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using osu.Framework.Allocation; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs index 3abeca3d81..175910d9d6 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/GroupingBeatmap.cs b/osu.Game.Tournament/Screens/Ladder/Components/GroupingBeatmap.cs index 416f960404..b16ba13f65 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/GroupingBeatmap.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/GroupingBeatmap.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Game.Beatmaps; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs index 6979aae295..d6b5d172de 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index f0559e0266..d43a410a8f 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Linq; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index 760f6d42f4..f788737add 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs index 578ec79321..4ed46223c8 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System.Linq; using osu.Framework.Graphics; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs index 3555e4db11..e38b684c28 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs index 9f2d2c4045..241e1d1d0b 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. namespace osu.Game.Tournament.Screens.Ladder.Components { diff --git a/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs index e3cb1473be..e605de9a7c 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Linq; diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs index 7c6aabbe53..351eee0433 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System.Linq; using osu.Framework.Allocation; diff --git a/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs b/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs index 125d358638..832e218b74 100644 --- a/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs +++ b/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 41368eb96e..3116a1361c 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System.Linq; using osu.Framework.Allocation; diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index 384f2f7ab2..093a1ba2d9 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Linq; diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index 65369fa6da..d809dfc994 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; diff --git a/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs b/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs index cd4f646fe7..0db3348e5d 100644 --- a/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs +++ b/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Graphics; diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index 94130cb383..d2ff632da0 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Bindables; diff --git a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs index 1de9b6ae5c..69c088efbc 100644 --- a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs +++ b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Bindables; diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index ec103f2444..cb9737d13f 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Linq; diff --git a/osu.Game.Tournament/Screens/TournamentScreen.cs b/osu.Game.Tournament/Screens/TournamentScreen.cs index e830509db9..7f6c5f8e18 100644 --- a/osu.Game.Tournament/Screens/TournamentScreen.cs +++ b/osu.Game.Tournament/Screens/TournamentScreen.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Graphics; diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 711ecc5ae9..bb5682bb42 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; using osu.Framework.Screens; diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index e3996bc572..a90f17ac68 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; From b0971ef0fda240df52b3ff4d729f7705646b6662 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Mar 2019 14:18:04 +0900 Subject: [PATCH 0359/2854] Fix remaining inspections --- osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs | 2 ++ osu.Game/Graphics/UserInterface/RollingCounter.cs | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index f788737add..043ade4285 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -14,6 +14,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components /// /// A collection of two teams competing in a head-to-head match. /// + [Serializable] public class MatchPairing { public int ID; @@ -65,6 +66,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly Bindable Date = new Bindable(); + [JsonProperty] public readonly BindableList ConditionalPairings = new BindableList(); public readonly Bindable Position = new Bindable(); diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index 47e12f5f15..cd244ed7e6 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -59,7 +59,6 @@ namespace osu.Game.Graphics.UserInterface public abstract void Increment(T amount); - public float TextSize { get => DisplayedCountSpriteText.Font.Size; From 999b947153206c10b5f1295a08d70391758256b1 Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Tue, 5 Mar 2019 17:55:18 -0600 Subject: [PATCH 0360/2854] Update Android csproj files --- osu.Android.props | 85 +- osu.Android.sln | 10 +- osu.Android.sln.DotSettings | 815 ++++++++++++++++++ osu.Android/Properties/AndroidManifest.xml | 4 +- osu.Android/monogc.txt | 1 + osu.Android/osu.Android.csproj | 40 +- ...u.Game.Rulesets.Catch.Tests.Android.csproj | 21 +- ...u.Game.Rulesets.Mania.Tests.Android.csproj | 21 +- ...osu.Game.Rulesets.Osu.Tests.Android.csproj | 21 +- ...u.Game.Rulesets.Taiko.Tests.Android.csproj | 24 +- .../osu.Game.Tests.Android.csproj | 21 +- osu.Game/osu.Game.csproj | 2 +- 12 files changed, 880 insertions(+), 185 deletions(-) create mode 100644 osu.Android.sln.DotSettings create mode 100644 osu.Android/monogc.txt diff --git a/osu.Android.props b/osu.Android.props index a662855fc1..bf18d9f022 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -1,60 +1,69 @@ + + bin\$(Configuration) + 4 + 2.0 + false + false + default + Library + 512 + Off + True + Xamarin.Android.Net.AndroidClientHandler + v8.1 + false + True portable False - bin\Debug\ DEBUG;TRACE prompt - 4 - True - None - False - false false - false - CJK;Mideast;Rare;West;Other - - false + false + SdkOnly + true + false + cjk,mideast,other,rare,west + true + armeabi-v7a;x86;arm64-v8a + true - True - pdbonly + false + None True - bin\Release\ - TRACE prompt - 4 - true - False + true + false SdkOnly - True - CJK;Mideast;Rare;West;Other + False + true + cjk,mideast,other,rare,west + true + armeabi-v7a;x86;arm64-v8a + true + + + osu.licenseheader + + + Always + + + + + - - - 0.0.7939 - - - 0.0.7939 - - - 0.22.0 - - - 1.1.0 - - - 1.0.0-dev000096 - - - 1.0.0-dev002315 - + + + \ No newline at end of file diff --git a/osu.Android.sln b/osu.Android.sln index d9c6ffc939..ebf2c55cb4 100644 --- a/osu.Android.sln +++ b/osu.Android.sln @@ -1,12 +1,10 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27004.2006 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28516.95 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game", "osu.Game\osu.Game.csproj", "{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Resources", "osu-resources\osu.Game.Resources\osu.Game.Resources.csproj", "{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Osu", "osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj", "{C92A607B-1FDD-4954-9F92-03FF547D9080}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Catch", "osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj", "{58F6C80C-1253-4A0E-A465-B8C85EBEADF3}" @@ -37,10 +35,6 @@ Global {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Debug|Any CPU.Build.0 = Debug|Any CPU {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.ActiveCfg = Release|Any CPU {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.Build.0 = Release|Any CPU - {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Release|Any CPU.Build.0 = Release|Any CPU {C92A607B-1FDD-4954-9F92-03FF547D9080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C92A607B-1FDD-4954-9F92-03FF547D9080}.Debug|Any CPU.Build.0 = Debug|Any CPU {C92A607B-1FDD-4954-9F92-03FF547D9080}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/osu.Android.sln.DotSettings b/osu.Android.sln.DotSettings new file mode 100644 index 0000000000..3f5bd9d34d --- /dev/null +++ b/osu.Android.sln.DotSettings @@ -0,0 +1,815 @@ + + True + True + True + True + ExplicitlyExcluded + ExplicitlyExcluded + SOLUTION + HINT + WARNING + + True + WARNING + WARNING + HINT + HINT + HINT + HINT + WARNING + WARNING + WARNING + HINT + WARNING + HINT + SUGGESTION + HINT + HINT + HINT + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + HINT + WARNING + WARNING + HINT + WARNING + WARNING + DO_NOT_SHOW + HINT + WARNING + DO_NOT_SHOW + WARNING + HINT + HINT + HINT + ERROR + HINT + HINT + HINT + WARNING + WARNING + HINT + DO_NOT_SHOW + HINT + HINT + HINT + HINT + WARNING + WARNING + WARNING + WARNING + WARNING + HINT + HINT + HINT + HINT + HINT + WARNING + WARNING + WARNING + WARNING + WARNING + HINT + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + WARNING + + WARNING + WARNING + WARNING + ERROR + WARNING + WARNING + HINT + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + HINT + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + HINT + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + WARNING + WARNING + HINT + WARNING + HINT + HINT + HINT + HINT + HINT + HINT + HINT + + HINT + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + HINT + WARNING + WARNING + HINT + HINT + WARNING + <?xml version="1.0" encoding="utf-16"?><Profile name="Code Cleanup (peppy)"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSUseVar><BehavourStyle>CAN_CHANGE_TO_EXPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_EXPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSUpdateFileHeader>True</CSUpdateFileHeader><CSCodeStyleAttributes ArrangeTypeAccessModifier="False" ArrangeTypeMemberAccessModifier="False" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="False" ArrangeBraces="False" ArrangeAttributes="False" ArrangeArgumentsStyle="False" /><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CSArrangeQualifiers>True</CSArrangeQualifiers></Profile> + Code Cleanup (peppy) + True + True + True + True + True + True + True + True + NEXT_LINE + NEXT_LINE + True + NEVER + NEVER + False + NEVER + False + True + False + False + True + True + False + CHOP_IF_LONG + True + 200 + CHOP_IF_LONG + False + False + AABB + API + BPM + GC + GL + GLSL + HID + HUD + ID + IP + IPC + LTRB + MD5 + NS + OS + RGB + RNG + SHA + SRGB + TK + SS + PP + GMT + QAT + BNG + UI + HINT + <?xml version="1.0" encoding="utf-16"?> +<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"> + <TypePattern DisplayName="COM interfaces or structs"> + <TypePattern.Match> + <Or> + <And> + <Kind Is="Interface" /> + <Or> + <HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" /> + <HasAttribute Name="System.Runtime.InteropServices.ComImport" /> + </Or> + </And> + <Kind Is="Struct" /> + </Or> + </TypePattern.Match> + </TypePattern> + <TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All"> + <TypePattern.Match> + <And> + <Kind Is="Class" /> + <HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" /> + </And> + </TypePattern.Match> + <Entry DisplayName="Setup/Teardown Methods"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <Or> + <HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.FixtureSetUpAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.FixtureTearDownAttribute" Inherited="True" /> + </Or> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="All other members" /> + <Entry Priority="100" DisplayName="Test Methods"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <HasAttribute Name="NUnit.Framework.TestAttribute" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Name /> + </Entry.SortBy> + </Entry> + </TypePattern> + <TypePattern DisplayName="Default Pattern"> + <Group DisplayName="Fields/Properties"> + <Group DisplayName="Public Fields"> + <Entry DisplayName="Constant Fields"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Or> + <Kind Is="Constant" /> + <Readonly /> + <And> + <Static /> + <Readonly /> + </And> + </Or> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Static Fields"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Static /> + <Not> + <Readonly /> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Normal Fields"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Not> + <Or> + <Static /> + <Readonly /> + </Or> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Entry DisplayName="Public Properties"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Kind Is="Property" /> + </And> + </Entry.Match> + </Entry> + <Group DisplayName="Internal Fields"> + <Entry DisplayName="Constant Fields"> + <Entry.Match> + <And> + <Access Is="Internal" /> + <Or> + <Kind Is="Constant" /> + <Readonly /> + <And> + <Static /> + <Readonly /> + </And> + </Or> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Static Fields"> + <Entry.Match> + <And> + <Access Is="Internal" /> + <Static /> + <Not> + <Readonly /> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Normal Fields"> + <Entry.Match> + <And> + <Access Is="Internal" /> + <Not> + <Or> + <Static /> + <Readonly /> + </Or> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Entry DisplayName="Internal Properties"> + <Entry.Match> + <And> + <Access Is="Internal" /> + <Kind Is="Property" /> + </And> + </Entry.Match> + </Entry> + <Group DisplayName="Protected Fields"> + <Entry DisplayName="Constant Fields"> + <Entry.Match> + <And> + <Access Is="Protected" /> + <Or> + <Kind Is="Constant" /> + <Readonly /> + <And> + <Static /> + <Readonly /> + </And> + </Or> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Static Fields"> + <Entry.Match> + <And> + <Access Is="Protected" /> + <Static /> + <Not> + <Readonly /> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Normal Fields"> + <Entry.Match> + <And> + <Access Is="Protected" /> + <Not> + <Or> + <Static /> + <Readonly /> + </Or> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Entry DisplayName="Protected Properties"> + <Entry.Match> + <And> + <Access Is="Protected" /> + <Kind Is="Property" /> + </And> + </Entry.Match> + </Entry> + <Group DisplayName="Private Fields"> + <Entry DisplayName="Constant Fields"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Or> + <Kind Is="Constant" /> + <Readonly /> + <And> + <Static /> + <Readonly /> + </And> + </Or> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Static Fields"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Static /> + <Not> + <Readonly /> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Normal Fields"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Not> + <Or> + <Static /> + <Readonly /> + </Or> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Entry DisplayName="Private Properties"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Kind Is="Property" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Group DisplayName="Constructor/Destructor"> + <Entry DisplayName="Ctor"> + <Entry.Match> + <Kind Is="Constructor" /> + </Entry.Match> + </Entry> + <Region Name="Disposal"> + <Entry DisplayName="Dtor"> + <Entry.Match> + <Kind Is="Destructor" /> + </Entry.Match> + </Entry> + <Entry DisplayName="Dispose()"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Kind Is="Method" /> + <Name Is="Dispose" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Dispose(true)"> + <Entry.Match> + <And> + <Access Is="Protected" /> + <Or> + <Virtual /> + <Override /> + </Or> + <Kind Is="Method" /> + <Name Is="Dispose" /> + </And> + </Entry.Match> + </Entry> + </Region> + </Group> + <Group DisplayName="Methods"> + <Group DisplayName="Public"> + <Entry DisplayName="Static Methods"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Static /> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Methods"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Not> + <Static /> + </Not> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Group DisplayName="Internal"> + <Entry DisplayName="Static Methods"> + <Entry.Match> + <And> + <Access Is="Internal" /> + <Static /> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Methods"> + <Entry.Match> + <And> + <Access Is="Internal" /> + <Not> + <Static /> + </Not> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Group DisplayName="Protected"> + <Entry DisplayName="Static Methods"> + <Entry.Match> + <And> + <Access Is="Protected" /> + <Static /> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Methods"> + <Entry.Match> + <And> + <Access Is="Protected" /> + <Not> + <Static /> + </Not> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Group DisplayName="Private"> + <Entry DisplayName="Static Methods"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Static /> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Methods"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Not> + <Static /> + </Not> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + </Group> + </Group> + </TypePattern> +</Patterns> + Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. +See the LICENCE file in the repository root for full licence text. + + <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> + <Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="private methods"><ElementKinds><Kind Name="ASYNC_METHOD" /><Kind Name="METHOD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public" Description="internal/protected/public methods"><ElementKinds><Kind Name="ASYNC_METHOD" /><Kind Name="METHOD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy> + <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="private properties"><ElementKinds><Kind Name="PROPERTY" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public" Description="internal/protected/public properties"><ElementKinds><Kind Name="PROPERTY" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True + True + True + True + True + True + True + True + True + True + True + True + o!f – Object Initializer: Anchor&Origin + True + constant("Centre") + 0 + True + True + 2.0 + InCSharpFile + ofao + True + Anchor = Anchor.$anchor$, +Origin = Anchor.$anchor$, + True + True + o!f – InternalChildren = [] + True + True + 2.0 + InCSharpFile + ofic + True + InternalChildren = new Drawable[] +{ + $END$ +}; + True + True + o!f – new GridContainer { .. } + True + True + 2.0 + InCSharpFile + ofgc + True + new GridContainer +{ + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] { $END$ }, + new Drawable[] { } + } +}; + True + True + o!f – new FillFlowContainer { .. } + True + True + 2.0 + InCSharpFile + offf + True + new FillFlowContainer +{ + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + $END$ + } +}, + True + True + o!f – new Container { .. } + True + True + 2.0 + InCSharpFile + ofcont + True + new Container +{ + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + $END$ + } +}, + True + True + o!f – BackgroundDependencyLoader load() + True + True + 2.0 + InCSharpFile + ofbdl + True + [BackgroundDependencyLoader] +private void load() +{ + $END$ +} + True + True + o!f – new Box { .. } + True + True + 2.0 + InCSharpFile + ofbox + True + new Box +{ + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, +}, + True + True + o!f – Children = [] + True + True + 2.0 + InCSharpFile + ofc + True + Children = new Drawable[] +{ + $END$ +}; + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml index 9b337eec6c..c053ced9e2 100644 --- a/osu.Android/Properties/AndroidManifest.xml +++ b/osu.Android/Properties/AndroidManifest.xml @@ -1,9 +1,9 @@  - + - + \ No newline at end of file diff --git a/osu.Android/monogc.txt b/osu.Android/monogc.txt new file mode 100644 index 0000000000..d15a2bd2ae --- /dev/null +++ b/osu.Android/monogc.txt @@ -0,0 +1 @@ +MONO_GC_PARAMS=nursery-size=8m \ No newline at end of file diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index bfde981207..45ff8ba31a 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -1,5 +1,6 @@  + Debug AnyCPU @@ -8,49 +9,20 @@ {D1D5F9A8-B40B-40E6-B02F-482D03346D3D} {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} {122416d6-6b49-4ee2-a1e8-b825f31c79fe} - Library - Properties osu.Android osu.Android - 512 - True - Resources\Resource.designer.cs - Resource - Off - false - v8.1 Properties\AndroidManifest.xml - Resources - Assets - Xamarin.Android.Net.AndroidClientHandler - + + true + - - osu.licenseheader - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} - osu.Game.Resources - {58f6c80c-1253-4a0e-a465-b8c85ebeadf3} osu.Game.Rulesets.Catch @@ -73,8 +45,10 @@ - + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj index 591f4cbc31..ed746921be 100644 --- a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj +++ b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj @@ -1,5 +1,6 @@  + Debug AnyCPU @@ -8,30 +9,14 @@ {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D} {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} {122416d6-6b49-4ee2-a1e8-b825f31c79fe} - Library - Properties osu.Game.Rulesets.Catch.Tests osu.Game.Rulesets.Catch.Tests.Android - 512 - True - Resources\Resource.designer.cs - Resource - Off - false - v8.1 Properties\AndroidManifest.xml - Resources - Assets - Xamarin.Android.Net.AndroidClientHandler - - - osu.licenseheader - @@ -40,10 +25,6 @@ - - {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} - osu.Game.Resources - {58f6c80c-1253-4a0e-a465-b8c85ebeadf3} osu.Game.Rulesets.Catch diff --git a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj index 5faf9f747d..b366958342 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj +++ b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj @@ -1,5 +1,6 @@  + Debug AnyCPU @@ -8,30 +9,14 @@ {531F1092-DB27-445D-AA33-2A77C7187C99} {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} {122416d6-6b49-4ee2-a1e8-b825f31c79fe} - Library - Properties osu.Game.Rulesets.Mania.Tests osu.Game.Rulesets.Mania.Tests.Android - 512 - True - Resources\Resource.designer.cs - Resource - Off - false - v8.1 Properties\AndroidManifest.xml - Resources - Assets - Xamarin.Android.Net.AndroidClientHandler - - - osu.licenseheader - @@ -40,10 +25,6 @@ - - {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} - osu.Game.Resources - {48f4582b-7687-4621-9cbe-5c24197cb536} osu.Game.Rulesets.Mania diff --git a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj index f99aa61f01..a40f7ca588 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj +++ b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj @@ -1,5 +1,6 @@  + Debug AnyCPU @@ -8,30 +9,14 @@ {90CAB706-39CB-4B93-9629-3218A6FF8E9B} {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} {122416d6-6b49-4ee2-a1e8-b825f31c79fe} - Library - Properties osu.Game.Rulesets.Osu.Tests osu.Game.Rulesets.Osu.Tests.Android - 512 - True - Resources\Resource.designer.cs - Resource - Off - false - v8.1 Properties\AndroidManifest.xml - Resources - Assets - Xamarin.Android.Net.AndroidClientHandler - - - osu.licenseheader - @@ -40,10 +25,6 @@ - - {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} - osu.Game.Resources - {c92a607b-1fdd-4954-9f92-03ff547d9080} osu.Game.Rulesets.Osu diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj index defeab5a64..bc7b00ffc8 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj +++ b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj @@ -1,5 +1,6 @@  + Debug AnyCPU @@ -8,30 +9,14 @@ {3701A0A1-8476-42C6-B5C4-D24129B4A484} {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} {122416d6-6b49-4ee2-a1e8-b825f31c79fe} - Library - Properties osu.Game.Rulesets.Taiko.Tests osu.Game.Rulesets.Taiko.Tests.Android - 512 - True - Resources\Resource.designer.cs - Resource - Off - false - v8.1 Properties\AndroidManifest.xml - Resources - Assets - Xamarin.Android.Net.AndroidClientHandler - - - osu.licenseheader - @@ -40,13 +25,6 @@ - - - - - {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} - osu.Game.Resources - {f167e17a-7de6-4af5-b920-a5112296c695} osu.Game.Rulesets.Taiko diff --git a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj index 8a8a2b6733..4e83234e7d 100644 --- a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj +++ b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj @@ -1,5 +1,6 @@  + Debug AnyCPU @@ -8,30 +9,14 @@ {5CC222DC-5716-4499-B897-DCBDDA4A5CF9} {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} {122416d6-6b49-4ee2-a1e8-b825f31c79fe} - Library - Properties osu.Game.Tests osu.Game.Tests.Android - 512 - True - Resources\Resource.designer.cs - Resource - Off - false - v8.1 Properties\AndroidManifest.xml - Resources - Assets - Xamarin.Android.Net.AndroidClientHandler - - - osu.licenseheader - @@ -61,10 +46,6 @@ - - {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} - osu.Game.Resources - {58f6c80c-1253-4a0e-a465-b8c85ebeadf3} osu.Game.Rulesets.Catch diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e87b43ac93..8f9a7cd14b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + From 3c999d64d451db3ab3163cc6100790f967a9a11c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Mar 2019 16:09:21 +0900 Subject: [PATCH 0361/2854] Fix post-merge errors --- .../Profile/Header/CenterHeaderContainer.cs | 20 +++++++++---------- .../Profile/Header/ProfileHeaderTabControl.cs | 16 +++++++-------- osu.Game/Overlays/Profile/ProfileHeader.cs | 10 ++++------ 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs index 30671487d3..fc330a03a9 100644 --- a/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Framework.Configuration; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -108,8 +108,7 @@ namespace osu.Game.Overlays.Profile.Header { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - TextSize = 16, - Font = "Exo2.0-Bold" + Font = OsuFont.GetFont(weight: FontWeight.Bold) } } } @@ -182,10 +181,9 @@ namespace osu.Game.Overlays.Profile.Header }, levelBadgeText = new OsuSpriteText { - TextSize = 20, - Font = "Exo2.0-Medium", Anchor = Anchor.Centre, Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium) } } }, @@ -215,8 +213,7 @@ namespace osu.Game.Overlays.Profile.Header { Anchor = Anchor.BottomRight, Origin = Anchor.TopRight, - Font = "Exo2.0-Bold", - TextSize = 12, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold) } } }, @@ -248,9 +245,12 @@ namespace osu.Game.Overlays.Profile.Header } }; - DetailsVisible.ValueChanged += newValue => expandButtonIcon.Icon = newValue ? FontAwesome.fa_chevron_down : FontAwesome.fa_chevron_up; - DetailsVisible.ValueChanged += newValue => hiddenDetailContainer.Alpha = newValue ? 1 : 0; - DetailsVisible.ValueChanged += newValue => expandedDetailContainer.Alpha = newValue ? 0 : 1; + DetailsVisible.ValueChanged += visible => + { + expandButtonIcon.Icon = visible.NewValue ? FontAwesome.fa_chevron_down : FontAwesome.fa_chevron_up; + hiddenDetailContainer.Alpha = visible.NewValue ? 1 : 0; + expandedDetailContainer.Alpha = visible.NewValue ? 0 : 1; + }; } private void updateDisplay() diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs index fd7124f20f..a8e50e00fe 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs +++ b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; +using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osuTK; @@ -36,7 +37,7 @@ namespace osu.Game.Overlays.Profile.Header } } - public MarginPadding Padding + public new MarginPadding Padding { get => TabContainer.Padding; set => TabContainer.Padding = value; @@ -78,7 +79,7 @@ namespace osu.Game.Overlays.Profile.Header accentColour = value; bar.Colour = value; - if (!Active) text.Colour = value; + if (!Active.Value) text.Colour = value; } } @@ -96,8 +97,7 @@ namespace osu.Game.Overlays.Profile.Header Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, Text = value, - TextSize = 14, - Font = "Exo2.0-Bold", + Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold) }, bar = new Circle { @@ -112,7 +112,7 @@ namespace osu.Game.Overlays.Profile.Header protected override bool OnHover(HoverEvent e) { - if (!Active) + if (!Active.Value) onActivated(true); return base.OnHover(e); } @@ -121,7 +121,7 @@ namespace osu.Game.Overlays.Profile.Header { base.OnHoverLost(e); - if (!Active) + if (!Active.Value) OnDeactivated(); } @@ -134,7 +134,7 @@ namespace osu.Game.Overlays.Profile.Header { text.FadeColour(AccentColour, 120, Easing.InQuad); bar.ResizeHeightTo(0, 120, Easing.InQuad); - text.Font = "Exo2.0-Medium"; + text.Font = text.Font.With(Typeface.Exo, weight: FontWeight.Medium); } private void onActivated(bool fake = false) @@ -142,7 +142,7 @@ namespace osu.Game.Overlays.Profile.Header text.FadeColour(Color4.White, 120, Easing.InQuad); bar.ResizeHeightTo(7.5f, 120, Easing.InQuad); if (!fake) - text.Font = "Exo2.0-Bold"; + text.Font = text.Font.With(Typeface.Exo, weight: FontWeight.Bold); } } } diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 21f1989606..fdb515270b 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Configuration; using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -15,6 +14,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Overlays.Profile.Header; using osu.Game.Users; +using osu.Framework.Bindables; namespace osu.Game.Overlays.Profile { @@ -145,7 +145,7 @@ namespace osu.Game.Overlays.Profile infoTabControl.AddItem("Modding"); centerHeaderContainer.DetailsVisible.BindTo(DetailsVisible); - DetailsVisible.ValueChanged += newValue => detailHeaderContainer.Alpha = newValue ? 0 : 1; + DetailsVisible.ValueChanged += visible => detailHeaderContainer.Alpha = visible.NewValue ? 0 : 1; } [BackgroundDependencyLoader] @@ -224,13 +224,11 @@ namespace osu.Game.Overlays.Profile }, title = new OsuSpriteText { - Font = "Exo2.0-Bold", - TextSize = big ? 14 : 12, + Font = OsuFont.GetFont(size: big ? 14 : 12, weight: FontWeight.Bold) }, content = new OsuSpriteText { - Font = "Exo2.0-Light", - TextSize = big ? 40 : 18, + Font = OsuFont.GetFont(size: big ? 40 : 18, weight: FontWeight.Light) }, new Container //Add a minimum size to the FillFlowContainer { From e3d463a141269e4909b49ed6bf4c277de84dab44 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Mar 2019 16:30:56 +0900 Subject: [PATCH 0362/2854] Formatting fixes --- .../Graphics/Containers/OsuHoverContainer.cs | 2 +- .../Profile/Header/BottomHeaderContainer.cs | 9 ++++--- .../Profile/Header/CenterHeaderContainer.cs | 8 ++++-- .../Profile/Header/DetailHeaderContainer.cs | 8 ++++-- .../Profile/Header/MedalHeaderContainer.cs | 6 ++++- .../Profile/Header/TopHeaderContainer.cs | 6 ++++- osu.Game/Users/User.cs | 3 +++ osu.Game/Users/UserStatistics.cs | 25 +++++++++++++------ 8 files changed, 49 insertions(+), 18 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs index f94fb2540c..1e0b56dae3 100644 --- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs +++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs @@ -33,7 +33,7 @@ namespace osu.Game.Graphics.Containers [BackgroundDependencyLoader] private void load(OsuColour colours) { - if(HoverColour == default) + if (HoverColour == default) HoverColour = colours.Yellow; } diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index df0409272f..7bb1b8acc2 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -24,13 +24,17 @@ namespace osu.Game.Overlays.Profile.Header private Color4 linkBlue, communityUserGrayGreenLighter; private User user; + public User User { get => user; set { - if (user == value) return; + if (user == value) + return; + user = value; + updateDisplay(); } } @@ -131,9 +135,8 @@ namespace osu.Game.Overlays.Profile.Header }); } else - { bottomLinkContainer.AddText(" " + content, bold); - } + addSpacer(bottomLinkContainer); } diff --git a/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs index fc330a03a9..ca615ccf4b 100644 --- a/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs @@ -48,13 +48,17 @@ namespace osu.Game.Overlays.Profile.Header private APIAccess apiAccess { get; set; } private User user; + public User User { get => user; set { - if (user == value) return; + if (user == value) + return; + user = value; + updateDisplay(); } } @@ -183,7 +187,7 @@ namespace osu.Game.Overlays.Profile.Header { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium) + Font = OsuFont.GetFont(size: 20) } } }, diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 124299b0ba..a7d21bcdf3 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -26,13 +26,17 @@ namespace osu.Game.Overlays.Profile.Header private RankGraph rankGraph; private User user; + public User User { get => user; set { - if (user == value) return; + if (user == value) + return; + user = value; + updateDisplay(); } } @@ -181,7 +185,7 @@ namespace osu.Game.Overlays.Profile.Header totalPlayTimeTooltip.TooltipText = (user?.Statistics?.PlayTime ?? 0) / 3600 + " hours"; foreach (var scoreRankInfo in scoreRankInfos) - scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount.GetForScoreRank(scoreRankInfo.Key) ?? 0; + scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0; detailGlobalRank.Content = user?.Statistics?.Ranks.Global?.ToString("#,##0") ?? "-"; detailCountryRank.Content = user?.Statistics?.Ranks.Country?.ToString("#,##0") ?? "-"; diff --git a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs index 5a54270b80..45c1e1e208 100644 --- a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs @@ -22,13 +22,17 @@ namespace osu.Game.Overlays.Profile.Header private FillFlowContainer badgeFlowContainer; private User user; + public User User { get => user; set { - if (user == value) return; + if (user == value) + return; + user = value; + updateDisplay(); } } diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 4186d08729..13a7951170 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -28,13 +28,17 @@ namespace osu.Game.Overlays.Profile.Header private const float avatar_size = 110; private User user; + public User User { get => user; set { - if (user == value) return; + if (user == value) + return; + user = value; + updateDisplay(); } } diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index d8e1ec81b5..314684069a 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -186,10 +186,13 @@ namespace osu.Game.Users { [Description("Keyboard")] Keyboard, + [Description("Mouse")] Mouse, + [Description("Tablet")] Tablet, + [Description("Touch Screen")] Touch, } diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs index a69cd794b3..752534a80d 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -77,16 +77,25 @@ namespace osu.Game.Users [JsonProperty(@"a")] public int A; - public int GetForScoreRank(ScoreRank rank) + public int this[ScoreRank rank] { - switch (rank) + get { - case ScoreRank.XH: return SSPlus; - case ScoreRank.X: return SS; - case ScoreRank.SH: return SPlus; - case ScoreRank.S: return S; - case ScoreRank.A: return A; - default: throw new ArgumentException($"API does not return {rank.ToString()}"); + switch (rank) + { + case ScoreRank.XH: + return SSPlus; + case ScoreRank.X: + return SS; + case ScoreRank.SH: + return SPlus; + case ScoreRank.S: + return S; + case ScoreRank.A: + return A; + default: + throw new ArgumentException($"API does not return {rank.ToString()}"); + } } } } From 22423f60d42efe96c985289daa01fbc6900bd566 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Mar 2019 19:27:42 +0900 Subject: [PATCH 0363/2854] Fix DI not working --- osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs b/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs index 6f164890f9..49ffa2d63f 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual typeof(ProfileHeader), typeof(RankGraph), typeof(LineGraph), - typeof(SupporterIcon) + typeof(ProfileHeaderTabControl), }; [Resolved] From c93adb220eb5a02afe3c5811af4cbb826b5f83a4 Mon Sep 17 00:00:00 2001 From: Albert Tang Date: Thu, 7 Mar 2019 10:14:19 -0600 Subject: [PATCH 0364/2854] Use official NuGet osu-framework build for Android --- osu.Android.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index bf18d9f022..da3abeb446 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -64,6 +64,6 @@ - + - \ No newline at end of file + From 2525f5bcb787955ab3a315c62143e76bd26825b7 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sat, 9 Mar 2019 23:58:14 +0100 Subject: [PATCH 0365/2854] Apply most suggestions --- osu.Game.Tests/Visual/TestCaseUserProfile.cs | 13 -- .../Visual/TestCaseUserProfileHeader.cs | 8 +- .../Profile/Header/BottomHeaderContainer.cs | 26 +-- .../Profile/Header/CenterHeaderContainer.cs | 138 +++------------- .../Profile/Header/DetailHeaderContainer.cs | 40 ++--- .../Overlays/Profile/Header/DrawableBadge.cs | 46 ++++++ .../Profile/Header/MedalHeaderContainer.cs | 63 ++------ .../Profile/Header/OverlinedInfoContainer.cs | 63 ++++++++ .../Profile/Header/ProfileHeaderButton.cs | 50 ++++++ .../Profile/Header/ProfileHeaderTabControl.cs | 8 +- .../Profile/Header/ProfileMessageButton.cs | 57 +++++++ .../Overlays/Profile/Header/SupporterIcon.cs | 48 +++--- .../Profile/Header/TopHeaderContainer.cs | 34 ++-- osu.Game/Overlays/Profile/ProfileHeader.cs | 153 +++--------------- osu.Game/Overlays/UserProfileOverlay.cs | 4 +- osu.Game/Users/UserCoverBackground.cs | 47 ++++-- osu.Game/Users/UserPanel.cs | 4 +- 17 files changed, 375 insertions(+), 427 deletions(-) create mode 100644 osu.Game/Overlays/Profile/Header/DrawableBadge.cs create mode 100644 osu.Game/Overlays/Profile/Header/OverlinedInfoContainer.cs create mode 100644 osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs create mode 100644 osu.Game/Overlays/Profile/Header/ProfileMessageButton.cs diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs index d0c4e495b8..3abb2584ce 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs @@ -87,8 +87,6 @@ namespace osu.Game.Tests.Visual AddStep("Show offline dummy", () => profile.ShowUser(TEST_USER, false)); - checkSupporterTag(false); - AddStep("Show null dummy", () => profile.ShowUser(new User { Username = @"Null", @@ -104,8 +102,6 @@ namespace osu.Game.Tests.Visual CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg" }, api.IsLoggedIn)); - checkSupporterTag(true); - AddStep("Show flyte", () => profile.ShowUser(new User { Username = @"flyte", @@ -118,15 +114,6 @@ namespace osu.Game.Tests.Visual AddStep("Show without reload", profile.Show); } - private void checkSupporterTag(bool isSupporter) - { - AddUntilStep(() => profile.Header.User != null, "wait for load"); - if (isSupporter) - AddAssert("is supporter", () => profile.Header.SupporterTag.Alpha == 1); - else - AddAssert("no supporter", () => profile.Header.SupporterTag.Alpha == 0); - } - private class TestUserProfileOverlay : UserProfileOverlay { public new ProfileHeader Header => base.Header; diff --git a/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs b/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs index 49ffa2d63f..8f36b4dda8 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs @@ -33,9 +33,9 @@ namespace osu.Game.Tests.Visual header = new ProfileHeader(); Add(header); - AddStep("Show offline dummy", () => header.User = TestCaseUserProfile.TEST_USER); + AddStep("Show offline dummy", () => header.User.Value = TestCaseUserProfile.TEST_USER); - AddStep("Show null dummy", () => header.User = new User + AddStep("Show null dummy", () => header.User.Value = new User { Username = "Null" }); @@ -65,11 +65,11 @@ namespace osu.Game.Tests.Visual if (api.IsLoggedIn) { var request = new GetUserRequest(fallback.Id); - request.Success += user => header.User = user; + request.Success += user => header.User.Value = user; api.Queue(request); } else - header.User = fallback; + header.User.Value = fallback; }); } } diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index 7bb1b8acc2..1f9d741b8d 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -17,32 +18,21 @@ using osuTK.Graphics; namespace osu.Game.Overlays.Profile.Header { - public class BottomHeaderContainer : Container + public class BottomHeaderContainer : CompositeDrawable { private LinkFlowContainer bottomTopLinkContainer; private LinkFlowContainer bottomLinkContainer; private Color4 linkBlue, communityUserGrayGreenLighter; - private User user; - - public User User - { - get => user; - set - { - if (user == value) - return; - - user = value; - - updateDisplay(); - } - } + public readonly Bindable User = new Bindable(); [BackgroundDependencyLoader] private void load(OsuColour colours) { - Children = new Drawable[] + AutoSizeAxes = Axes.Y; + User.ValueChanged += e => updateDisplay(e.NewValue); + + InternalChildren = new Drawable[] { new Box { @@ -76,7 +66,7 @@ namespace osu.Game.Overlays.Profile.Header communityUserGrayGreenLighter = colours.CommunityUserGrayGreenLighter; } - private void updateDisplay() + private void updateDisplay(User user) { void bold(SpriteText t) => t.Font = @"Exo2.0-Bold"; void addSpacer(OsuTextFlowContainer textFlow) => textFlow.AddArbitraryDrawable(new Container { Width = 15 }); diff --git a/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs index ca615ccf4b..b5e04bee9e 100644 --- a/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs @@ -1,75 +1,48 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; -using osu.Game.Online.Chat; using osu.Game.Users; using osuTK; using osuTK.Graphics; namespace osu.Game.Overlays.Profile.Header { - public class CenterHeaderContainer : Container + public class CenterHeaderContainer : CompositeDrawable { - public readonly BindableBool DetailsVisible = new BindableBool(); + public Action DetailsVisibilityAction; + private bool detailsVisible; private OsuSpriteText followerText; - private ProfileHeaderButton messageButton; private OsuSpriteText levelBadgeText; private Bar levelProgressBar; private OsuSpriteText levelProgressText; - private ProfileHeader.OverlinedInfoContainer hiddenDetailGlobal, hiddenDetailCountry; + private OverlinedInfoContainer hiddenDetailGlobal, hiddenDetailCountry; - [Resolved(CanBeNull = true)] - private ChannelManager channelManager { get; set; } - - [Resolved(CanBeNull = true)] - private UserProfileOverlay userOverlay { get; set; } - - [Resolved(CanBeNull = true)] - private ChatOverlay chatOverlay { get; set; } - - [Resolved] - private APIAccess apiAccess { get; set; } - - private User user; - - public User User - { - get => user; - set - { - if (user == value) - return; - - user = value; - - updateDisplay(); - } - } + public readonly Bindable User = new Bindable(); [BackgroundDependencyLoader] private void load(OsuColour colours, TextureStore textures) { Container hiddenDetailContainer, expandedDetailContainer; SpriteIcon expandButtonIcon; + ProfileHeaderButton detailsToggleButton; + Height = 60; + User.ValueChanged += e => updateDisplay(e.NewValue); - Children = new Drawable[] + InternalChildren = new Drawable[] { new Box { @@ -118,21 +91,9 @@ namespace osu.Game.Overlays.Profile.Header } } }, - messageButton = new ProfileHeaderButton + new ProfileMessageButton { - Alpha = 0, - RelativeSizeAxes = Axes.Y, - Children = new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.fa_envelope, - FillMode = FillMode.Fit, - Size = new Vector2(50, 14) - }, - } + User = { BindTarget = User } }, } }, @@ -143,12 +104,11 @@ namespace osu.Game.Overlays.Profile.Header RelativeSizeAxes = Axes.Y, Padding = new MarginPadding { Vertical = 10 }, Width = UserProfileOverlay.CONTENT_X_MARGIN, - Child = new ProfileHeaderButton + Child = detailsToggleButton = new ProfileHeaderButton { RelativeSizeAxes = Axes.Y, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Action = DetailsVisible.Toggle, Children = new Drawable[] { expandButtonIcon = new SpriteIcon @@ -233,12 +193,12 @@ namespace osu.Game.Overlays.Profile.Header Margin = new MarginPadding { Right = 50 }, Children = new[] { - hiddenDetailGlobal = new ProfileHeader.OverlinedInfoContainer + hiddenDetailGlobal = new OverlinedInfoContainer { Title = "Global Ranking", LineColour = colours.Yellow }, - hiddenDetailCountry = new ProfileHeader.OverlinedInfoContainer + hiddenDetailCountry = new OverlinedInfoContainer { Title = "Country Ranking", LineColour = colours.Yellow @@ -249,76 +209,26 @@ namespace osu.Game.Overlays.Profile.Header } }; - DetailsVisible.ValueChanged += visible => + detailsToggleButton.Action = () => { - expandButtonIcon.Icon = visible.NewValue ? FontAwesome.fa_chevron_down : FontAwesome.fa_chevron_up; - hiddenDetailContainer.Alpha = visible.NewValue ? 1 : 0; - expandedDetailContainer.Alpha = visible.NewValue ? 0 : 1; + detailsVisible = !detailsVisible; + expandButtonIcon.Icon = detailsVisible ? FontAwesome.fa_chevron_down : FontAwesome.fa_chevron_up; + hiddenDetailContainer.Alpha = detailsVisible ? 1 : 0; + expandedDetailContainer.Alpha = detailsVisible ? 0 : 1; + DetailsVisibilityAction(detailsVisible); }; } - private void updateDisplay() + private void updateDisplay(User user) { followerText.Text = user.FollowerCount?.Length > 0 ? user.FollowerCount[0].ToString("#,##0") : "0"; - if (!user.PMFriendsOnly && apiAccess.LocalUser.Value.Id != user.Id) - { - messageButton.Show(); - messageButton.Action = () => - { - channelManager?.OpenPrivateChannel(user); - userOverlay?.Hide(); - chatOverlay?.Show(); - }; - } - else - { - messageButton.Hide(); - } - levelBadgeText.Text = user.Statistics?.Level.Current.ToString() ?? "0"; levelProgressBar.Length = user.Statistics?.Level.Progress / 100f ?? 0; levelProgressText.Text = user.Statistics?.Level.Progress.ToString("0'%'"); - hiddenDetailGlobal.Content = user?.Statistics?.Ranks.Global?.ToString("#,##0") ?? "-"; - hiddenDetailCountry.Content = user?.Statistics?.Ranks.Country?.ToString("#,##0") ?? "-"; - } - - private class ProfileHeaderButton : OsuHoverContainer - { - private readonly Box background; - private readonly Container content; - - protected override Container Content => content; - - protected override IEnumerable EffectTargets => new[] { background }; - - public ProfileHeaderButton() - { - HoverColour = Color4.Black.Opacity(0.75f); - IdleColour = Color4.Black.Opacity(0.7f); - AutoSizeAxes = Axes.X; - - base.Content.Add(new CircularContainer - { - Masking = true, - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - }, - content = new Container - { - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = 10 }, - } - } - }); - } + hiddenDetailGlobal.Content = user.Statistics?.Ranks.Global?.ToString("#,##0") ?? "-"; + hiddenDetailCountry.Content = user.Statistics?.Ranks.Country?.ToString("#,##0") ?? "-"; } } } diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index a7d21bcdf3..19894f0301 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -17,34 +18,23 @@ using osuTK; namespace osu.Game.Overlays.Profile.Header { - public class DetailHeaderContainer : Container + public class DetailHeaderContainer : CompositeDrawable { private ProfileHeader.HasTooltipContainer totalPlayTimeTooltip; - private ProfileHeader.OverlinedInfoContainer totalPlayTimeInfo, medalInfo, ppInfo; + private OverlinedInfoContainer totalPlayTimeInfo, medalInfo, ppInfo; private readonly Dictionary scoreRankInfos = new Dictionary(); - private ProfileHeader.OverlinedInfoContainer detailGlobalRank, detailCountryRank; + private OverlinedInfoContainer detailGlobalRank, detailCountryRank; private RankGraph rankGraph; - private User user; - - public User User - { - get => user; - set - { - if (user == value) - return; - - user = value; - - updateDisplay(); - } - } + public readonly Bindable User = new Bindable(); [BackgroundDependencyLoader] private void load(OsuColour colours) { - Children = new Drawable[] + AutoSizeAxes = Axes.Y; + User.ValueChanged += e => updateDisplay(e.NewValue); + + InternalChildren = new Drawable[] { new Box { @@ -79,18 +69,18 @@ namespace osu.Game.Overlays.Profile.Header { AutoSizeAxes = Axes.Both, TooltipText = "0 hours", - Child = totalPlayTimeInfo = new ProfileHeader.OverlinedInfoContainer + Child = totalPlayTimeInfo = new OverlinedInfoContainer { Title = "Total Play Time", LineColour = colours.Yellow, }, }, - medalInfo = new ProfileHeader.OverlinedInfoContainer + medalInfo = new OverlinedInfoContainer { Title = "Medals", LineColour = colours.GreenLight, }, - ppInfo = new ProfileHeader.OverlinedInfoContainer + ppInfo = new OverlinedInfoContainer { Title = "pp", LineColour = colours.Red, @@ -135,12 +125,12 @@ namespace osu.Game.Overlays.Profile.Header Spacing = new Vector2(0, 20), Children = new Drawable[] { - detailGlobalRank = new ProfileHeader.OverlinedInfoContainer(true, 110) + detailGlobalRank = new OverlinedInfoContainer(true, 110) { Title = "Global Ranking", LineColour = colours.Yellow, }, - detailCountryRank = new ProfileHeader.OverlinedInfoContainer(false, 110) + detailCountryRank = new OverlinedInfoContainer(false, 110) { Title = "Country Ranking", LineColour = colours.Yellow, @@ -154,7 +144,7 @@ namespace osu.Game.Overlays.Profile.Header }; } - private void updateDisplay() + private void updateDisplay(User user) { medalInfo.Content = user?.Achievements?.Length.ToString() ?? "0"; ppInfo.Content = user?.Statistics?.PP?.ToString("#,##0") ?? "0"; diff --git a/osu.Game/Overlays/Profile/Header/DrawableBadge.cs b/osu.Game/Overlays/Profile/Header/DrawableBadge.cs new file mode 100644 index 0000000000..75a8f4e415 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/DrawableBadge.cs @@ -0,0 +1,46 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Overlays.Profile.Header +{ + public class DrawableBadge : CompositeDrawable, IHasTooltip + { + public static readonly Vector2 DRAWABLE_BADGE_SIZE = new Vector2(86, 40); + + private readonly Badge badge; + + public DrawableBadge(Badge badge) + { + this.badge = badge; + Size = DRAWABLE_BADGE_SIZE; + } + + [BackgroundDependencyLoader] + private void load(LargeTextureStore textures) + { + InternalChild = new Sprite + { + FillMode = FillMode.Fit, + RelativeSizeAxes = Axes.Both, + Texture = textures.Get(badge.ImageUrl), + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + InternalChild.FadeInFromZero(200); + } + + public string TooltipText => badge.Description; + } +} diff --git a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs index 45c1e1e208..69b1203b9f 100644 --- a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs @@ -2,14 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using osu.Game.Graphics; using osu.Game.Users; using osuTK; @@ -17,31 +15,20 @@ using osuTK.Graphics; namespace osu.Game.Overlays.Profile.Header { - public class MedalHeaderContainer : Container + public class MedalHeaderContainer : CompositeDrawable { private FillFlowContainer badgeFlowContainer; - private User user; - - public User User - { - get => user; - set - { - if (user == value) - return; - - user = value; - - updateDisplay(); - } - } + public readonly Bindable User = new Bindable(); [BackgroundDependencyLoader] private void load(OsuColour colours) { Alpha = 0; - Children = new Drawable[] + AutoSizeAxes = Axes.Y; + User.ValueChanged += e => updateDisplay(e.NewValue); + + InternalChildren = new Drawable[] { new Box { @@ -76,9 +63,9 @@ namespace osu.Game.Overlays.Profile.Header }; } - private void updateDisplay() + private void updateDisplay(User user) { - var badges = User.Badges; + var badges = user.Badges; badgeFlowContainer.Clear(); if (badges?.Length > 0) { @@ -100,37 +87,5 @@ namespace osu.Game.Overlays.Profile.Header Hide(); } } - - private class DrawableBadge : CompositeDrawable, IHasTooltip - { - public static readonly Vector2 DRAWABLE_BADGE_SIZE = new Vector2(86, 40); - - private readonly Badge badge; - - public DrawableBadge(Badge badge) - { - this.badge = badge; - Size = DRAWABLE_BADGE_SIZE; - } - - [BackgroundDependencyLoader] - private void load(LargeTextureStore textures) - { - InternalChild = new Sprite - { - FillMode = FillMode.Fit, - RelativeSizeAxes = Axes.Both, - Texture = textures.Get(badge.ImageUrl), - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - InternalChild.FadeInFromZero(200); - } - - public string TooltipText => badge.Description; - } } } diff --git a/osu.Game/Overlays/Profile/Header/OverlinedInfoContainer.cs b/osu.Game/Overlays/Profile/Header/OverlinedInfoContainer.cs new file mode 100644 index 0000000000..6d15f96eea --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/OverlinedInfoContainer.cs @@ -0,0 +1,63 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Profile.Header +{ + public class OverlinedInfoContainer : CompositeDrawable + { + private readonly Circle line; + private readonly OsuSpriteText title, content; + + public string Title + { + set => title.Text = value; + } + + public string Content + { + set => content.Text = value; + } + + public Color4 LineColour + { + set => line.Colour = value; + } + + public OverlinedInfoContainer(bool big = false, int minimumWidth = 60) + { + AutoSizeAxes = Axes.Both; + InternalChild = new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + line = new Circle + { + RelativeSizeAxes = Axes.X, + Height = 4, + }, + title = new OsuSpriteText + { + Font = OsuFont.GetFont(size: big ? 14 : 12, weight: FontWeight.Bold) + }, + content = new OsuSpriteText + { + Font = OsuFont.GetFont(size: big ? 40 : 18, weight: FontWeight.Light) + }, + new Container //Add a minimum size to the FillFlowContainer + { + Width = minimumWidth, + } + } + }; + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs b/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs new file mode 100644 index 0000000000..6d9ab7a4d8 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs @@ -0,0 +1,50 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Profile.Header +{ + public class ProfileHeaderButton : OsuHoverContainer + { + private readonly Box background; + private readonly Container content; + + protected override Container Content => content; + + protected override IEnumerable EffectTargets => new[] { background }; + + public ProfileHeaderButton() + { + HoverColour = Color4.Black.Opacity(0.75f); + IdleColour = Color4.Black.Opacity(0.7f); + AutoSizeAxes = Axes.X; + + base.Content.Add(new CircularContainer + { + Masking = true, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + content = new Container + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 10 }, + } + } + }); + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs index a8e50e00fe..e7c9676550 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs +++ b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs @@ -93,11 +93,11 @@ namespace osu.Game.Overlays.Profile.Header { text = new OsuSpriteText { - Margin = new MarginPadding { Bottom = 15 }, + Margin = new MarginPadding { Bottom = 10 }, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, Text = value, - Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold) + Font = OsuFont.GetFont() }, bar = new Circle { @@ -134,7 +134,7 @@ namespace osu.Game.Overlays.Profile.Header { text.FadeColour(AccentColour, 120, Easing.InQuad); bar.ResizeHeightTo(0, 120, Easing.InQuad); - text.Font = text.Font.With(Typeface.Exo, weight: FontWeight.Medium); + text.Font = text.Font.With(weight: FontWeight.Medium); } private void onActivated(bool fake = false) @@ -142,7 +142,7 @@ namespace osu.Game.Overlays.Profile.Header text.FadeColour(Color4.White, 120, Easing.InQuad); bar.ResizeHeightTo(7.5f, 120, Easing.InQuad); if (!fake) - text.Font = text.Font.With(Typeface.Exo, weight: FontWeight.Bold); + text.Font = text.Font.With(weight: FontWeight.Bold); } } } diff --git a/osu.Game/Overlays/Profile/Header/ProfileMessageButton.cs b/osu.Game/Overlays/Profile/Header/ProfileMessageButton.cs new file mode 100644 index 0000000000..a14c0324d7 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/ProfileMessageButton.cs @@ -0,0 +1,57 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Online.API; +using osu.Game.Online.Chat; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Overlays.Profile.Header +{ + public class ProfileMessageButton : ProfileHeaderButton + { + public readonly Bindable User = new Bindable(); + + [Resolved(CanBeNull = true)] + private ChannelManager channelManager { get; set; } + + [Resolved(CanBeNull = true)] + private UserProfileOverlay userOverlay { get; set; } + + [Resolved(CanBeNull = true)] + private ChatOverlay chatOverlay { get; set; } + + [Resolved] + private APIAccess apiAccess { get; set; } + + public ProfileMessageButton() + { + Content.Alpha = 0; + RelativeSizeAxes = Axes.Y; + + Child = new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.fa_envelope, + FillMode = FillMode.Fit, + Size = new Vector2(50, 14) + }; + + Action = () => + { + if (!Content.IsPresent) return; + + channelManager?.OpenPrivateChannel(User.Value); + userOverlay?.Hide(); + chatOverlay?.Show(); + }; + + User.ValueChanged += e => Content.Alpha = !e.NewValue.PMFriendsOnly && apiAccess.LocalUser.Value.Id != e.NewValue.Id ? 1 : 0; + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/SupporterIcon.cs index d7e25e32c2..04d6db8b3c 100644 --- a/osu.Game/Overlays/Profile/Header/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/SupporterIcon.cs @@ -10,10 +10,11 @@ using osu.Game.Graphics; namespace osu.Game.Overlays.Profile.Header { - public class SupporterIcon : CircularContainer, IHasTooltip + public class SupporterIcon : CompositeDrawable, IHasTooltip { private readonly Box background; private readonly FillFlowContainer iconContainer; + private readonly CircularContainer content; public string TooltipText => "osu!supporter"; @@ -23,11 +24,11 @@ namespace osu.Game.Overlays.Profile.Header { if (value == 0) { - Hide(); + content.Hide(); } else { - Show(); + content.Show(); iconContainer.Clear(); for (int i = 0; i < value; i++) { @@ -38,43 +39,38 @@ namespace osu.Game.Overlays.Profile.Header Icon = FontAwesome.fa_heart, }); } + + iconContainer.Padding = new MarginPadding { Horizontal = DrawHeight / 2 }; } } } public SupporterIcon() { - Masking = true; AutoSizeAxes = Axes.X; - Hide(); - Children = new Drawable[] + InternalChild = content = new CircularContainer { - background = new Box { RelativeSizeAxes = Axes.Both }, - iconContainer = new FillFlowContainer + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Masking = true, + Alpha = 0, + Children = new Drawable[] { - Direction = FillDirection.Horizontal, - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Height = 0.6f, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + background = new Box { RelativeSizeAxes = Axes.Both }, + iconContainer = new FillFlowContainer + { + Direction = FillDirection.Horizontal, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Height = 0.6f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } } }; } - public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) - { - bool invalid = base.Invalidate(invalidation, source, shallPropagate); - - if ((invalidation & Invalidation.DrawSize) != 0) - { - iconContainer.Padding = new MarginPadding { Horizontal = DrawHeight / 2 }; - } - - return invalid; - } - [BackgroundDependencyLoader] private void load(OsuColour colours) { diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 13a7951170..8e4d72c702 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -14,9 +15,9 @@ using osuTK; namespace osu.Game.Overlays.Profile.Header { - public class TopHeaderContainer : Container + public class TopHeaderContainer : CompositeDrawable { - public SupporterIcon SupporterTag; + private SupporterIcon supporterTag; private UpdateableAvatar avatar; private OsuSpriteText usernameText; private ExternalLinkButton openUserExternally; @@ -27,26 +28,15 @@ namespace osu.Game.Overlays.Profile.Header private const float avatar_size = 110; - private User user; - - public User User - { - get => user; - set - { - if (user == value) - return; - - user = value; - - updateDisplay(); - } - } + public readonly Bindable User = new Bindable(); [BackgroundDependencyLoader] private void load(OsuColour colours) { - Children = new Drawable[] + Height = 150; + User.ValueChanged += e => updateDisplay(e.NewValue); + + InternalChildren = new Drawable[] { new Box { @@ -109,7 +99,7 @@ namespace osu.Game.Overlays.Profile.Header TextSize = 18, Font = "Exo2.0-Regular" }, - SupporterTag = new SupporterIcon + supporterTag = new SupporterIcon { Height = 20, Margin = new MarginPadding { Top = 5 } @@ -161,14 +151,14 @@ namespace osu.Game.Overlays.Profile.Header }; } - private void updateDisplay() + private void updateDisplay(User user) { - avatar.User = User; + avatar.User = user; usernameText.Text = user.Username; openUserExternally.Link = $@"https://osu.ppy.sh/users/{user.Id}"; userFlag.Country = user.Country; userCountryText.Text = user.Country?.FullName ?? "Alien"; - SupporterTag.SupporterLevel = user.SupportLevel; + supporterTag.SupporterLevel = user.SupportLevel; titleText.Text = user.Title; titleText.Colour = OsuColour.FromHex(user.Colour ?? "fff"); diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index fdb515270b..988fa3afd9 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -2,43 +2,31 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osuTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Game.Overlays.Profile.Header; using osu.Game.Users; using osu.Framework.Bindables; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Profile { public class ProfileHeader : Container { - private readonly Container coverContainer; - private readonly OsuSpriteText coverInfoText; + private readonly UserCoverBackground coverContainer; + private readonly ScreenTitle coverTitle; private readonly ProfileHeaderTabControl infoTabControl; - private readonly TopHeaderContainer topHeaderContainer; - public SupporterIcon SupporterTag => topHeaderContainer.SupporterTag; - - private readonly CenterHeaderContainer centerHeaderContainer; - public readonly BindableBool DetailsVisible = new BindableBool(); - - private readonly DetailHeaderContainer detailHeaderContainer; - private readonly MedalHeaderContainer medalHeaderContainer; - private readonly BottomHeaderContainer bottomHeaderContainer; - private const float cover_height = 150; private const float cover_info_height = 75; public ProfileHeader() { + CenterHeaderContainer centerHeaderContainer; + DetailHeaderContainer detailHeaderContainer; Container expandedDetailContainer; FillFlowContainer hiddenDetailContainer, headerDetailContainer; SpriteIcon expandButtonIcon; @@ -48,19 +36,10 @@ namespace osu.Game.Overlays.Profile Children = new Drawable[] { - coverContainer = new Container + coverContainer = new UserCoverBackground { RelativeSizeAxes = Axes.X, Height = cover_height, - Masking = true, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.75f)) - }, - } }, new Container { @@ -73,25 +52,10 @@ namespace osu.Game.Overlays.Profile Depth = -float.MaxValue, Children = new Drawable[] { - new FillFlowContainer + coverTitle = new ScreenTitle { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new[] - { - new OsuSpriteText - { - Text = "Player ", - Font = "Exo2.0-Regular", - TextSize = 30 - }, - coverInfoText = new OsuSpriteText - { - Text = "Info", - Font = "Exo2.0-Regular", - TextSize = 30 - } - } + Title = "Player ", + Page = "Info" }, infoTabControl = new ProfileHeaderTabControl { @@ -112,30 +76,30 @@ namespace osu.Game.Overlays.Profile Direction = FillDirection.Vertical, Children = new Drawable[] { - topHeaderContainer = new TopHeaderContainer + new TopHeaderContainer { RelativeSizeAxes = Axes.X, - Height = 150, + User = { BindTarget = User }, }, centerHeaderContainer = new CenterHeaderContainer { RelativeSizeAxes = Axes.X, - Height = 60, + User = { BindTarget = User }, }, detailHeaderContainer = new DetailHeaderContainer { RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, + User = { BindTarget = User }, }, - medalHeaderContainer = new MedalHeaderContainer + new MedalHeaderContainer { RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, + User = { BindTarget = User }, }, - bottomHeaderContainer = new BottomHeaderContainer + new BottomHeaderContainer { RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, + User = { BindTarget = User }, }, } } @@ -144,99 +108,28 @@ namespace osu.Game.Overlays.Profile infoTabControl.AddItem("Info"); infoTabControl.AddItem("Modding"); - centerHeaderContainer.DetailsVisible.BindTo(DetailsVisible); - DetailsVisible.ValueChanged += visible => detailHeaderContainer.Alpha = visible.NewValue ? 0 : 1; + centerHeaderContainer.DetailsVisibilityAction = visible => detailHeaderContainer.Alpha = visible ? 0 : 1; + User.ValueChanged += e => updateDisplay(e.NewValue); } [BackgroundDependencyLoader] private void load(OsuColour colours, TextureStore textures) { - coverInfoText.Colour = colours.CommunityUserGreen; + coverTitle.AccentColour = colours.CommunityUserGreen; infoTabControl.AccentColour = colours.CommunityUserGreen; } - private User user; + public Bindable User = new Bindable(); - public User User + private void updateDisplay(User user) { - get => user; - set - { - medalHeaderContainer.User = detailHeaderContainer.User = bottomHeaderContainer.User = - centerHeaderContainer.User = topHeaderContainer.User = user = value; - updateDisplay(); - } - } - - private void updateDisplay() - { - coverContainer.RemoveAll(d => d is UserCoverBackground); - LoadComponentAsync(new UserCoverBackground(user) - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - FillMode = FillMode.Fill, - OnLoadComplete = d => d.FadeInFromZero(200), - Depth = float.MaxValue, - }, coverContainer.Add); + coverContainer.User = user; } public class HasTooltipContainer : Container, IHasTooltip { public string TooltipText { get; set; } } - - public class OverlinedInfoContainer : CompositeDrawable - { - private readonly Circle line; - private readonly OsuSpriteText title, content; - - public string Title - { - set => title.Text = value; - } - - public string Content - { - set => content.Text = value; - } - - public Color4 LineColour - { - set => line.Colour = value; - } - - public OverlinedInfoContainer(bool big = false, int minimumWidth = 60) - { - AutoSizeAxes = Axes.Both; - InternalChild = new FillFlowContainer - { - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - line = new Circle - { - RelativeSizeAxes = Axes.X, - Height = 4, - }, - title = new OsuSpriteText - { - Font = OsuFont.GetFont(size: big ? 14 : 12, weight: FontWeight.Bold) - }, - content = new OsuSpriteText - { - Font = OsuFont.GetFont(size: big ? 40 : 18, weight: FontWeight.Light) - }, - new Container //Add a minimum size to the FillFlowContainer - { - Width = minimumWidth, - } - } - }; - } - } } } diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 60e2921127..9b6e34d399 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -81,7 +81,7 @@ namespace osu.Game.Overlays Show(); - if (user.Id == Header?.User?.Id) + if (user.Id == Header?.User.Value?.Id) return; userReq?.Cancel(); @@ -167,7 +167,7 @@ namespace osu.Game.Overlays private void userLoadComplete(User user) { - Header.User = user; + Header.User.Value = user; if (user.ProfileOrder != null) { diff --git a/osu.Game/Users/UserCoverBackground.cs b/osu.Game/Users/UserCoverBackground.cs index 4c72762498..d037f809b4 100644 --- a/osu.Game/Users/UserCoverBackground.cs +++ b/osu.Game/Users/UserCoverBackground.cs @@ -1,30 +1,51 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +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.Framework.Graphics.Textures; +using osuTK.Graphics; namespace osu.Game.Users { - public class UserCoverBackground : Sprite + public class UserCoverBackground : ModelBackedDrawable { - private readonly User user; - - public UserCoverBackground(User user) + public User User { - this.user = user; + get => Model; + set => Model = value; } - [BackgroundDependencyLoader] - private void load(LargeTextureStore textures) - { - if (textures == null) - throw new ArgumentNullException(nameof(textures)); + [Resolved] + private LargeTextureStore textures { get; set; } - if (!string.IsNullOrEmpty(user.CoverUrl)) - Texture = textures.Get(user.CoverUrl); + protected override Drawable CreateDrawable(User user) + { + if (user == null) + { + return new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.75f)) + }; + } + else + { + return new Sprite + { + RelativeSizeAxes = Axes.Both, + Texture = textures.Get(user.CoverUrl), + FillMode = FillMode.Fill, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + OnLoadComplete = d => d.FadeInFromZero(400), + }; + } } } } diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index cebbc74d50..def967e69b 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -73,12 +73,12 @@ namespace osu.Game.Users Children = new Drawable[] { - new DelayedLoadWrapper(new UserCoverBackground(user) + new DelayedLoadWrapper(new UserCoverBackground { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - FillMode = FillMode.Fill, + User = user, OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out) }, 300) { RelativeSizeAxes = Axes.Both }, new Box From 92595e43f60aeb8f2c2d4308d2c9235d07bbcaa2 Mon Sep 17 00:00:00 2001 From: Santeri Nogelainen Date: Thu, 14 Mar 2019 21:57:39 +0200 Subject: [PATCH 0366/2854] slider border thickness --- .../Objects/Drawables/DrawableSlider.cs | 1 + .../Objects/Drawables/Pieces/SliderBody.cs | 35 +++++++++++++++++-- osu.Game/Skinning/LegacySkinDecoder.cs | 4 +++ osu.Game/Skinning/SkinConfiguration.cs | 2 ++ 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 57ea0abdd8..1e7155ced9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -160,6 +160,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.SkinChanged(skin, allowFallback); + Body.BorderSize = skin.GetValue(s => s.SliderBorderSize) ?? Body.BorderSize; Body.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? Body.AccentColour; Body.BorderColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Body.BorderColour; Ball.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Ball.AccentColour; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 2f5c326bda..a4b27b2ee9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -64,6 +64,21 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } + /// + /// Used to size the path border. + /// + public int BorderSize { + get => path.BorderSize; + set { + if (path.BorderSize == value) + return; + + path.BorderSize = value; + + container.ForceRedraw(); + } + } + public Quad PathDrawQuad => container.ScreenSpaceDrawQuad; protected SliderBody() @@ -130,12 +145,28 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } + private int borderSize = 100; + + public int BorderSize { + get => borderSize; + set { + if (borderSize == value) + return; + + borderSize = value; + + InvalidateTexture(); + } + } + + private float calucatedBorderPortion => BorderSize / 100f * border_portion; + protected override Color4 ColourAt(float position) { - if (position <= border_portion) + if (position <= calucatedBorderPortion) return BorderColour; - position -= border_portion; + position -= calucatedBorderPortion; return new Color4(AccentColour.R, AccentColour.G, AccentColour.B, (opacity_at_edge - (opacity_at_edge - opacity_at_centre) * position / gradient_portion) * AccentColour.A); } } diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index 96a9116c51..50d7191f08 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -33,6 +33,10 @@ namespace osu.Game.Skinning case @"CursorExpand": skin.CursorExpand = pair.Value != "0"; break; + case @"SliderBorderSize": + if (int.TryParse(pair.Value, out int size)) + skin.SliderBorderSize = size; + break; } break; diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs index 82faec4e9d..bd6393db36 100644 --- a/osu.Game/Skinning/SkinConfiguration.cs +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -25,6 +25,8 @@ namespace osu.Game.Skinning public int HitCircleOverlap { get; set; } + public int? SliderBorderSize { get; set; } + public bool? CursorExpand { get; set; } = true; } } From cbb7498a42d6311b94ca07f939fb72dd0c7ac532 Mon Sep 17 00:00:00 2001 From: Santeri Nogelainen Date: Sat, 16 Mar 2019 12:41:03 +0200 Subject: [PATCH 0367/2854] Border size to float, add min and max size, other small changes --- .../Objects/Drawables/DrawableSlider.cs | 2 +- .../Objects/Drawables/Pieces/SliderBody.cs | 20 +++++++++++++------ osu.Game/Skinning/LegacySkinDecoder.cs | 4 +++- osu.Game/Skinning/SkinConfiguration.cs | 2 +- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 1e7155ced9..d76612e26d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -160,7 +160,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.SkinChanged(skin, allowFallback); - Body.BorderSize = skin.GetValue(s => s.SliderBorderSize) ?? Body.BorderSize; + Body.BorderSize = skin.GetValue(s => s.SliderBorderSize) ?? Body.BorderSize; Body.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? Body.AccentColour; Body.BorderColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Body.BorderColour; Ball.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Ball.AccentColour; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index a4b27b2ee9..9340d32bd9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -67,7 +67,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces /// /// Used to size the path border. /// - public int BorderSize { + public float BorderSize + { get => path.BorderSize; set { if (path.BorderSize == value) @@ -107,6 +108,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private class SliderPath : SmoothPath { + private const float border_max_size = 10f; + private const float border_min_size = 0f; // = no border + private const float border_portion = 0.128f; private const float gradient_portion = 1 - border_portion; @@ -145,28 +149,32 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } - private int borderSize = 100; + private float borderSize = 1f; - public int BorderSize { + public float BorderSize + { get => borderSize; set { if (borderSize == value) return; + if (value < border_min_size || value > border_max_size) + return; + borderSize = value; InvalidateTexture(); } } - private float calucatedBorderPortion => BorderSize / 100f * border_portion; + private float calculatedBorderPortion => BorderSize * border_portion; protected override Color4 ColourAt(float position) { - if (position <= calucatedBorderPortion) + if (calculatedBorderPortion != 0f && position <= calculatedBorderPortion) return BorderColour; - position -= calucatedBorderPortion; + position -= calculatedBorderPortion; return new Color4(AccentColour.R, AccentColour.G, AccentColour.B, (opacity_at_edge - (opacity_at_edge - opacity_at_centre) * position / gradient_portion) * AccentColour.A); } } diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index 50d7191f08..1f530de4fb 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Beatmaps.Formats; +using System; +using System.Globalization; namespace osu.Game.Skinning { @@ -34,7 +36,7 @@ namespace osu.Game.Skinning skin.CursorExpand = pair.Value != "0"; break; case @"SliderBorderSize": - if (int.TryParse(pair.Value, out int size)) + if (Single.TryParse(pair.Value, NumberStyles.Number, CultureInfo.CreateSpecificCulture("en-US"), out float size)) skin.SliderBorderSize = size; break; } diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs index bd6393db36..043622f8ce 100644 --- a/osu.Game/Skinning/SkinConfiguration.cs +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -25,7 +25,7 @@ namespace osu.Game.Skinning public int HitCircleOverlap { get; set; } - public int? SliderBorderSize { get; set; } + public float? SliderBorderSize { get; set; } public bool? CursorExpand { get; set; } = true; } From b624ecabde67901b254a67a7037a6f1bb54c1cfe Mon Sep 17 00:00:00 2001 From: Santeri Nogelainen Date: Sat, 16 Mar 2019 12:47:37 +0200 Subject: [PATCH 0368/2854] Max = 8 --- osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 9340d32bd9..b99f79afcb 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -108,8 +108,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private class SliderPath : SmoothPath { - private const float border_max_size = 10f; - private const float border_min_size = 0f; // = no border + private const float border_max_size = 8f; + private const float border_min_size = 0f; private const float border_portion = 0.128f; private const float gradient_portion = 1 - border_portion; From c0471019ffa0605752d601eb519ad68189307bc8 Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Sun, 17 Mar 2019 09:39:07 -0500 Subject: [PATCH 0369/2854] Very minor changes --- osu.Android/OsuGameActivity.cs | 4 +--- osu.Android/OsuGameAndroid.cs | 14 ++++++++++++++ osu.Android/osu.Android.csproj | 4 +--- 3 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 osu.Android/OsuGameAndroid.cs diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 130144d417..4623402b1a 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -6,15 +6,13 @@ using Android.Content.PM; using Android.OS; using Android.Views; using osu.Framework.Android; -using osu.Game; namespace osu.Android { [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = true)] public class OsuGameActivity : AndroidGameActivity { - protected override Framework.Game CreateGame() - => new OsuGame(); + protected override Framework.Game CreateGame() => new OsuGameAndroid(); protected override void OnCreate(Bundle savedInstanceState) { diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs new file mode 100644 index 0000000000..d9bdd9c0c2 --- /dev/null +++ b/osu.Android/OsuGameAndroid.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using Android.App; +using osu.Game; + +namespace osu.Android +{ + public class OsuGameAndroid : OsuGame + { + public override Version AssemblyVersion => new Version(Application.Context.ApplicationContext.PackageManager.GetPackageInfo(Application.Context.ApplicationContext.PackageName, 0).VersionName); + } +} diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 45ff8ba31a..6a859742ee 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -13,11 +13,9 @@ osu.Android Properties\AndroidManifest.xml - - true - + From c6c98257f2022a0d62b46114b6aa281633729652 Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Sun, 17 Mar 2019 09:45:01 -0500 Subject: [PATCH 0370/2854] Add internet permission, update Android framework --- osu.Android.props | 2 +- osu.Android/Properties/AndroidManifest.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Android.props b/osu.Android.props index da3abeb446..e320c45ea9 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -64,6 +64,6 @@ - + diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml index c053ced9e2..549ba9f46a 100644 --- a/osu.Android/Properties/AndroidManifest.xml +++ b/osu.Android/Properties/AndroidManifest.xml @@ -5,5 +5,6 @@ + \ No newline at end of file From edfc0368ac3fdb155847b8e8a1c713a7f6d3cba5 Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Wed, 20 Mar 2019 11:34:12 -0500 Subject: [PATCH 0371/2854] Update Android framework --- osu.Android.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android.props b/osu.Android.props index e320c45ea9..028b4bda83 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -64,6 +64,6 @@ - + From e93311fdc9e2372e262e5fb9f4cd496ec53f5798 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 22 Mar 2019 19:01:32 +0900 Subject: [PATCH 0372/2854] DI facade --- .../Graphics/Containers/FacadeContainer.cs | 30 +++++++++++++++++++ osu.Game/Screens/Play/PlayerLoader.cs | 14 ++++++--- 2 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Graphics/Containers/FacadeContainer.cs diff --git a/osu.Game/Graphics/Containers/FacadeContainer.cs b/osu.Game/Graphics/Containers/FacadeContainer.cs new file mode 100644 index 0000000000..7d7a4b0680 --- /dev/null +++ b/osu.Game/Graphics/Containers/FacadeContainer.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Game.Screens.Menu; +using osuTK; + +namespace osu.Game.Graphics.Containers +{ + public class FacadeContainer : Container + { + [Cached] + private Facade facade; + + public FacadeContainer() + { + facade = new Facade(); + } + + public void SetLogo(OsuLogo logo) + { + facade.Size = new Vector2(logo.SizeForFlow); + } + } + + public class Facade : Container + { + } +} diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index e9ee5d3fa8..878c2541e9 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -14,6 +14,7 @@ using osu.Framework.Screens; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Menu; @@ -32,7 +33,7 @@ namespace osu.Game.Screens.Play private Player player; - private Container content; + private FacadeContainer content; private BeatmapMetadataDisplay info; @@ -59,7 +60,7 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load() { - InternalChild = content = new Container + InternalChild = content = new FacadeContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -153,6 +154,8 @@ namespace osu.Game.Screens.Play logo.FadeIn(350); logo.Delay(resuming ? 0 : 500).MoveToOffset(new Vector2(0, -0.24f), 500, Easing.InOutExpo); + + content.SetLogo(logo); } protected override void LoadComplete() @@ -302,6 +305,8 @@ namespace osu.Game.Screens.Play private LoadingAnimation loading; private Sprite backgroundSprite; private ModDisplay modDisplay; + private FillFlowContainer fillFlowContainer; + private FacadeContainer facadeContainer; public bool Loading { @@ -326,14 +331,14 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load() + private void load(Facade facade) { var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); AutoSizeAxes = Axes.Both; Children = new Drawable[] { - new FillFlowContainer + fillFlowContainer = new FillFlowContainer { AutoSizeAxes = Axes.Both, Origin = Anchor.TopCentre, @@ -341,6 +346,7 @@ namespace osu.Game.Screens.Play Direction = FillDirection.Vertical, Children = new Drawable[] { + facade, new OsuSpriteText { Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)), From 6e98a8dd7c15ff372a39ebf9b7785b0c6e16a755 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 22 Mar 2019 20:01:58 +0900 Subject: [PATCH 0373/2854] Initial implementation --- osu.Game.Tests/Visual/TestCasePlayerLoader.cs | 10 +++- .../Graphics/Containers/FacadeContainer.cs | 53 ++++++++++++++++++- osu.Game/Screens/Play/PlayerLoader.cs | 13 +++-- 3 files changed, 67 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs index 2bc416f7f4..ad0965b4d6 100644 --- a/osu.Game.Tests/Visual/TestCasePlayerLoader.cs +++ b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs @@ -7,7 +7,9 @@ using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Screens; +using osu.Game.Screens.Menu; using osu.Game.Screens.Play; +using osuTK; namespace osu.Game.Tests.Visual { @@ -16,6 +18,9 @@ namespace osu.Game.Tests.Visual private PlayerLoader loader; private readonly ScreenStack stack; + [Cached] + private OsuLogo logo; + [Cached] private BackgroundScreenStack backgroundStack; @@ -23,6 +28,7 @@ namespace osu.Game.Tests.Visual { InputManager.Add(backgroundStack = new BackgroundScreenStack { RelativeSizeAxes = Axes.Both }); InputManager.Add(stack = new ScreenStack { RelativeSizeAxes = Axes.Both }); + InputManager.Add(logo = new OsuLogo()); } [BackgroundDependencyLoader] @@ -30,6 +36,8 @@ namespace osu.Game.Tests.Visual { Beatmap.Value = new DummyWorkingBeatmap(game); + AddStep("Reset logo position", () => logo = new OsuLogo { Position = new Vector2(0, 0) }); + AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => new Player { AllowPause = false, @@ -57,8 +65,6 @@ namespace osu.Game.Tests.Visual AllowLeadIn = false, AllowResults = false, })); - - Scheduler.AddDelayed(() => slow.Ready = true, 5000); }); AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen()); diff --git a/osu.Game/Graphics/Containers/FacadeContainer.cs b/osu.Game/Graphics/Containers/FacadeContainer.cs index 7d7a4b0680..e39fba64ce 100644 --- a/osu.Game/Graphics/Containers/FacadeContainer.cs +++ b/osu.Game/Graphics/Containers/FacadeContainer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Screens.Menu; using osuTK; @@ -13,14 +14,62 @@ namespace osu.Game.Graphics.Containers [Cached] private Facade facade; + private OsuLogo logo; + + private bool tracking; + private bool smoothTransform; + public FacadeContainer() { facade = new Facade(); } - public void SetLogo(OsuLogo logo) + private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(facade.ScreenSpaceDrawQuad.Centre); + + public void SetLogo(OsuLogo logo, bool resuming, double transformDelay) { - facade.Size = new Vector2(logo.SizeForFlow); + if (logo != null) + { + facade.Size = new Vector2(logo.SizeForFlow * 0.3f); + this.logo = logo; + Scheduler.AddDelayed(() => + { + tracking = true; + smoothTransform = !resuming; + }, transformDelay); + } + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + facade.Size = new Vector2(logo.SizeForFlow * 0.3f); + + if (!tracking) + return; + + logo.RelativePositionAxes = Axes.None; + + bool childrenLoaded = true; + + foreach (var d in Children) + { + if (!d.IsAlive) + childrenLoaded = false; + } + + if (smoothTransform && childrenLoaded) + { + // Our initial movement to the tracking location should be smooth. + Schedule(() => logo.MoveTo(logoTrackingPosition, 500, Easing.InOutExpo)); + smoothTransform = false; + } + else if (logo.Transforms.Count == 0) + { + // If all transforms have finished playing, the logo constantly track the position of the facade. + logo.Position = logoTrackingPosition; + } } } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 878c2541e9..97249ee6cd 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -149,13 +149,13 @@ namespace osu.Game.Screens.Play { base.LogoArriving(logo, resuming); - logo.ScaleTo(new Vector2(0.15f), 300, Easing.In); - logo.MoveTo(new Vector2(0.5f), 300, Easing.In); + const double duration = 300; + + logo.ScaleTo(new Vector2(0.15f), duration, Easing.In); + logo.MoveTo(new Vector2(0.5f), duration, Easing.In); logo.FadeIn(350); - logo.Delay(resuming ? 0 : 500).MoveToOffset(new Vector2(0, -0.24f), 500, Easing.InOutExpo); - - content.SetLogo(logo); + content.SetLogo(logo, resuming, duration); } protected override void LoadComplete() @@ -335,6 +335,9 @@ namespace osu.Game.Screens.Play { var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); + facade.Anchor = Anchor.TopCentre; + facade.Origin = Anchor.TopCentre; + AutoSizeAxes = Axes.Both; Children = new Drawable[] { From d37968d88df2ad6f6f0a6f64596212df28bfabde Mon Sep 17 00:00:00 2001 From: David Zhao Date: Sun, 24 Mar 2019 15:18:38 +0900 Subject: [PATCH 0374/2854] Add better test for facade containers --- .../Visual/TestCaseFacadeContainer.cs | 113 ++++++++++++++++++ .../Graphics/Containers/FacadeContainer.cs | 33 +++-- osu.Game/Screens/Play/PlayerLoader.cs | 46 +++---- 3 files changed, 152 insertions(+), 40 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestCaseFacadeContainer.cs diff --git a/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs new file mode 100644 index 0000000000..3063a4ca3f --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs @@ -0,0 +1,113 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; +using osu.Game.Configuration; +using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Screens; +using osu.Game.Screens.Menu; +using osu.Game.Screens.Play; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseFacadeContainer : ScreenTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(PlayerLoader), + typeof(Player), + typeof(Facade), + }; + + [Cached] + private OsuLogo logo; + + private readonly Bindable uiScale = new Bindable(); + + private OsuScreen baseScreen; + + public TestCaseFacadeContainer() + { + Add(logo = new OsuLogo()); + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + baseScreen = null; + config.BindWith(OsuSetting.UIScale, uiScale); + AddSliderStep("Adjust scale", 1f, 1.5f, 1f, v => uiScale.Value = v); + AddToggleStep("Toggle mods", b => { Beatmap.Value.Mods.Value = b ? Beatmap.Value.Mods.Value.Concat(new[] { new OsuModNoFail() }) : Enumerable.Empty(); }); + } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Null screens", () => baseScreen = null); + } + + [Test] + public void PlayerLoaderTest() + { + AddStep("Add new playerloader", () => LoadScreen(baseScreen = new TestPlayerLoader(() => new TestPlayer + { + AllowPause = false, + AllowLeadIn = false, + AllowResults = false, + }))); + } + + [Test] + public void MainMenuTest() + { + AddStep("Add new Main Menu", () => LoadScreen(baseScreen = new MainMenu())); + } + + private class TestFacadeContainer : FacadeContainer + { + protected override Facade CreateFacade() => new Facade + { + Colour = Color4.Tomato, + Alpha = 0.35f, + Child = new Box + { + Colour = Color4.Tomato, + RelativeSizeAxes = Axes.Both, + }, + }; + } + + private class TestPlayerLoader : PlayerLoader + { + public TestPlayerLoader(Func player) + : base(player) + { + } + + protected override FacadeContainer CreateFacadeContainer() => new TestFacadeContainer(); + } + + private class TestPlayer : Player + { + [BackgroundDependencyLoader] + private void load() + { + // Never finish loading + while (true) + Thread.Sleep(1); + } + } + } +} diff --git a/osu.Game/Graphics/Containers/FacadeContainer.cs b/osu.Game/Graphics/Containers/FacadeContainer.cs index e39fba64ce..d7fae8887f 100644 --- a/osu.Game/Graphics/Containers/FacadeContainer.cs +++ b/osu.Game/Graphics/Containers/FacadeContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -19,9 +20,11 @@ namespace osu.Game.Graphics.Containers private bool tracking; private bool smoothTransform; + protected virtual Facade CreateFacade() => new Facade(); + public FacadeContainer() { - facade = new Facade(); + facade = CreateFacade(); } private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(facade.ScreenSpaceDrawQuad.Centre); @@ -44,30 +47,26 @@ namespace osu.Game.Graphics.Containers { base.UpdateAfterChildren(); - facade.Size = new Vector2(logo.SizeForFlow * 0.3f); - - if (!tracking) + if (logo == null) return; - logo.RelativePositionAxes = Axes.None; + facade.Size = new Vector2(logo.SizeForFlow * 0.3f); - bool childrenLoaded = true; - - foreach (var d in Children) - { - if (!d.IsAlive) - childrenLoaded = false; - } - - if (smoothTransform && childrenLoaded) + if (smoothTransform && facade.IsLoaded && logo.Transforms.Count == 0) { // Our initial movement to the tracking location should be smooth. - Schedule(() => logo.MoveTo(logoTrackingPosition, 500, Easing.InOutExpo)); - smoothTransform = false; + Schedule(() => + { + facade.Size = new Vector2(logo.SizeForFlow * 0.3f); + logo.RelativePositionAxes = Axes.None; + logo.MoveTo(logoTrackingPosition, 500, Easing.InOutExpo); + smoothTransform = false; + }); } - else if (logo.Transforms.Count == 0) + else if (facade.IsLoaded && logo.Transforms.Count == 0) { // If all transforms have finished playing, the logo constantly track the position of the facade. + logo.RelativePositionAxes = Axes.None; logo.Position = logoTrackingPosition; } } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 97249ee6cd..5817e11277 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -35,6 +35,8 @@ namespace osu.Game.Screens.Play private FacadeContainer content; + protected virtual FacadeContainer CreateFacadeContainer() => new FacadeContainer(); + private BeatmapMetadataDisplay info; private bool hideOverlays; @@ -60,32 +62,30 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load() { - InternalChild = content = new FacadeContainer + InternalChild = content = CreateFacadeContainer(); + content.Anchor = Anchor.Centre; + content.Origin = Anchor.Centre; + content.RelativeSizeAxes = Axes.Both; + content.Children = new Drawable[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + info = new BeatmapMetadataDisplay(Beatmap.Value) { - info = new BeatmapMetadataDisplay(Beatmap.Value) + Alpha = 0, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Margin = new MarginPadding(25), + Children = new PlayerSettingsGroup[] { - Alpha = 0, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 20), - Margin = new MarginPadding(25), - Children = new PlayerSettingsGroup[] - { - VisualSettings = new VisualSettings(), - new InputSettings() - } + VisualSettings = new VisualSettings(), + new InputSettings() } } }; From a0f6718145f4cadb74e86161fe635c221d30e2d9 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 26 Mar 2019 10:48:29 +0900 Subject: [PATCH 0375/2854] Better tests and implementation --- .../Visual/TestCaseFacadeContainer.cs | 75 ++++++++++++++++++- .../Graphics/Containers/FacadeContainer.cs | 55 ++++++++------ osu.Game/Screens/Play/PlayerLoader.cs | 5 +- 3 files changed, 109 insertions(+), 26 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs index 3063a4ca3f..d3f854998c 100644 --- a/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs @@ -9,7 +9,9 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; using osu.Game.Configuration; using osu.Game.Graphics.Containers; @@ -18,6 +20,7 @@ using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; +using osuTK; using osuTK.Graphics; namespace osu.Game.Tests.Visual @@ -36,6 +39,7 @@ namespace osu.Game.Tests.Visual private readonly Bindable uiScale = new Bindable(); + private TestScreen screen1; private OsuScreen baseScreen; public TestCaseFacadeContainer() @@ -49,7 +53,6 @@ namespace osu.Game.Tests.Visual baseScreen = null; config.BindWith(OsuSetting.UIScale, uiScale); AddSliderStep("Adjust scale", 1f, 1.5f, 1f, v => uiScale.Value = v); - AddToggleStep("Toggle mods", b => { Beatmap.Value.Mods.Value = b ? Beatmap.Value.Mods.Value.Concat(new[] { new OsuModNoFail() }) : Enumerable.Empty(); }); } [SetUpSteps] @@ -58,9 +61,18 @@ namespace osu.Game.Tests.Visual AddStep("Null screens", () => baseScreen = null); } + [Test] + public void IsolatedTest() + { + bool randomPositions = false; + AddToggleStep("Toggle move continuously", b => randomPositions = b); + AddStep("Move facade to random position", () => LoadScreen(screen1 = new TestScreen(randomPositions))); + } + [Test] public void PlayerLoaderTest() { + AddToggleStep("Toggle mods", b => { Beatmap.Value.Mods.Value = b ? Beatmap.Value.Mods.Value.Concat(new[] { new OsuModNoFail() }) : Enumerable.Empty(); }); AddStep("Add new playerloader", () => LoadScreen(baseScreen = new TestPlayerLoader(() => new TestPlayer { AllowPause = false, @@ -89,6 +101,67 @@ namespace osu.Game.Tests.Visual }; } + private class TestScreen : OsuScreen + { + private TestFacadeContainer facadeContainer; + private FacadeFlowComponent facadeFlowComponent; + private OsuLogo logo; + + private readonly bool randomPositions; + + public TestScreen(bool randomPositions = false) + { + this.randomPositions = randomPositions; + } + + private SpriteText positionText; + private SpriteText sizeAxesText; + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = facadeContainer = new TestFacadeContainer + { + Child = facadeFlowComponent = new FacadeFlowComponent + { + AutoSizeAxes = Axes.Both + } + }; + } + + protected override void LogoArriving(OsuLogo logo, bool resuming) + { + base.LogoArriving(logo, resuming); + logo.FadeIn(350); + logo.ScaleTo(new Vector2(0.15f), 350, Easing.In); + facadeContainer.SetLogo(logo); + moveLogoFacade(); + } + + private void moveLogoFacade() + { + Random random = new Random(); + if (facadeFlowComponent.Transforms.Count == 0) + { + facadeFlowComponent.Delay(500).MoveTo(new Vector2(random.Next(0, 800), random.Next(0, 600)), 300); + } + + if (randomPositions) + Schedule(moveLogoFacade); + } + } + + private class FacadeFlowComponent : FillFlowContainer + { + [BackgroundDependencyLoader] + private void load(Facade facade) + { + facade.Anchor = Anchor.TopCentre; + facade.Origin = Anchor.TopCentre; + Child = facade; + } + } + private class TestPlayerLoader : PlayerLoader { public TestPlayerLoader(Func player) diff --git a/osu.Game/Graphics/Containers/FacadeContainer.cs b/osu.Game/Graphics/Containers/FacadeContainer.cs index d7fae8887f..0511c87166 100644 --- a/osu.Game/Graphics/Containers/FacadeContainer.cs +++ b/osu.Game/Graphics/Containers/FacadeContainer.cs @@ -1,10 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.MathUtils; using osu.Game.Screens.Menu; using osuTK; @@ -18,7 +18,6 @@ namespace osu.Game.Graphics.Containers private OsuLogo logo; private bool tracking; - private bool smoothTransform; protected virtual Facade CreateFacade() => new Facade(); @@ -29,7 +28,7 @@ namespace osu.Game.Graphics.Containers private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(facade.ScreenSpaceDrawQuad.Centre); - public void SetLogo(OsuLogo logo, bool resuming, double transformDelay) + public void SetLogo(OsuLogo logo, double transformDelay = 0) { if (logo != null) { @@ -38,41 +37,53 @@ namespace osu.Game.Graphics.Containers Scheduler.AddDelayed(() => { tracking = true; - smoothTransform = !resuming; }, transformDelay); } } + private double startTime; + private double duration = 1000; + + private Vector2 startPosition; + private Easing easing = Easing.InOutExpo; + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); - if (logo == null) + if (logo == null || !tracking) return; facade.Size = new Vector2(logo.SizeForFlow * 0.3f); - if (smoothTransform && facade.IsLoaded && logo.Transforms.Count == 0) + if (facade.IsLoaded && logo.Position != logoTrackingPosition) { - // Our initial movement to the tracking location should be smooth. - Schedule(() => + if (logo.RelativePositionAxes != Axes.None) { - facade.Size = new Vector2(logo.SizeForFlow * 0.3f); + logo.Position = logo.Parent.ToLocalSpace(logo.Position); logo.RelativePositionAxes = Axes.None; - logo.MoveTo(logoTrackingPosition, 500, Easing.InOutExpo); - smoothTransform = false; - }); - } - else if (facade.IsLoaded && logo.Transforms.Count == 0) - { - // If all transforms have finished playing, the logo constantly track the position of the facade. - logo.RelativePositionAxes = Axes.None; - logo.Position = logoTrackingPosition; + } + + if (startTime == 0) + { + startTime = Time.Current; + } + + var endTime = startTime + duration; + var remainingDuration = endTime - Time.Current; + + if (remainingDuration <= 0) + { + remainingDuration = 0; + } + + float currentTime = (float)Interpolation.ApplyEasing(easing, remainingDuration / duration); + logo.Position = Vector2.Lerp(logoTrackingPosition, startPosition, currentTime); } } } - - public class Facade : Container - { - } } + +public class Facade : Container +{ +} \ No newline at end of file diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 5817e11277..b7155f771f 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -155,7 +155,7 @@ namespace osu.Game.Screens.Play logo.MoveTo(new Vector2(0.5f), duration, Easing.In); logo.FadeIn(350); - content.SetLogo(logo, resuming, duration); + content.SetLogo(logo, duration); } protected override void LoadComplete() @@ -167,7 +167,7 @@ namespace osu.Game.Screens.Play private ScheduledDelegate pushDebounce; protected VisualSettings VisualSettings; - // Hhere because IsHovered will not update unless we do so. + // Here because IsHovered will not update unless we do so. public override bool HandlePositionalInput => true; private bool readyForPush => player.LoadState == LoadState.Ready && IsHovered && GetContainingInputManager()?.DraggedDrawable == null; @@ -306,7 +306,6 @@ namespace osu.Game.Screens.Play private Sprite backgroundSprite; private ModDisplay modDisplay; private FillFlowContainer fillFlowContainer; - private FacadeContainer facadeContainer; public bool Loading { From be9ac39f542eb6bea400086c9cf04cb7b4a15e69 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 26 Mar 2019 11:11:27 +0900 Subject: [PATCH 0376/2854] Cleanup --- .../Visual/TestCaseFacadeContainer.cs | 23 ++++--------------- .../Graphics/Containers/FacadeContainer.cs | 2 +- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs index d3f854998c..5591bec0b8 100644 --- a/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs @@ -11,7 +11,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; using osu.Game.Configuration; using osu.Game.Graphics.Containers; @@ -32,6 +31,7 @@ namespace osu.Game.Tests.Visual typeof(PlayerLoader), typeof(Player), typeof(Facade), + typeof(FacadeContainer) }; [Cached] @@ -39,9 +39,6 @@ namespace osu.Game.Tests.Visual private readonly Bindable uiScale = new Bindable(); - private TestScreen screen1; - private OsuScreen baseScreen; - public TestCaseFacadeContainer() { Add(logo = new OsuLogo()); @@ -50,30 +47,23 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - baseScreen = null; config.BindWith(OsuSetting.UIScale, uiScale); AddSliderStep("Adjust scale", 1f, 1.5f, 1f, v => uiScale.Value = v); } - [SetUpSteps] - public void SetUpSteps() - { - AddStep("Null screens", () => baseScreen = null); - } - [Test] public void IsolatedTest() { bool randomPositions = false; AddToggleStep("Toggle move continuously", b => randomPositions = b); - AddStep("Move facade to random position", () => LoadScreen(screen1 = new TestScreen(randomPositions))); + AddStep("Move facade to random position", () => LoadScreen(new TestScreen(randomPositions))); } [Test] public void PlayerLoaderTest() { AddToggleStep("Toggle mods", b => { Beatmap.Value.Mods.Value = b ? Beatmap.Value.Mods.Value.Concat(new[] { new OsuModNoFail() }) : Enumerable.Empty(); }); - AddStep("Add new playerloader", () => LoadScreen(baseScreen = new TestPlayerLoader(() => new TestPlayer + AddStep("Add new playerloader", () => LoadScreen(new TestPlayerLoader(() => new TestPlayer { AllowPause = false, AllowLeadIn = false, @@ -84,7 +74,7 @@ namespace osu.Game.Tests.Visual [Test] public void MainMenuTest() { - AddStep("Add new Main Menu", () => LoadScreen(baseScreen = new MainMenu())); + AddStep("Add new Main Menu", () => LoadScreen(new MainMenu())); } private class TestFacadeContainer : FacadeContainer @@ -105,8 +95,6 @@ namespace osu.Game.Tests.Visual { private TestFacadeContainer facadeContainer; private FacadeFlowComponent facadeFlowComponent; - private OsuLogo logo; - private readonly bool randomPositions; public TestScreen(bool randomPositions = false) @@ -114,9 +102,6 @@ namespace osu.Game.Tests.Visual this.randomPositions = randomPositions; } - private SpriteText positionText; - private SpriteText sizeAxesText; - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game/Graphics/Containers/FacadeContainer.cs b/osu.Game/Graphics/Containers/FacadeContainer.cs index 0511c87166..611cc94958 100644 --- a/osu.Game/Graphics/Containers/FacadeContainer.cs +++ b/osu.Game/Graphics/Containers/FacadeContainer.cs @@ -60,8 +60,8 @@ namespace osu.Game.Graphics.Containers { if (logo.RelativePositionAxes != Axes.None) { - logo.Position = logo.Parent.ToLocalSpace(logo.Position); logo.RelativePositionAxes = Axes.None; + logo.Position = logo.Parent.ToLocalSpace(logo.Position); } if (startTime == 0) From 3fe52be77febe7ce3e41cc1762717cdd71b02db6 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 26 Mar 2019 17:18:35 +0900 Subject: [PATCH 0377/2854] Better tests, add documentation --- .../Visual/Gameplay/TestCasePlayerLoader.cs | 7 +- .../Visual/TestCaseFacadeContainer.cs | 30 ++++--- .../Graphics/Containers/FacadeContainer.cs | 78 ++++++++++++------- osu.Game/Screens/Menu/ButtonSystem.cs | 63 +++++++-------- osu.Game/Screens/Play/PlayerLoader.cs | 37 +++++---- 5 files changed, 121 insertions(+), 94 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs index 67dba71d45..be2a21d23d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs @@ -7,16 +7,13 @@ using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Screens; -using osu.Game.Screens.Menu; using osu.Game.Screens.Play; -using osuTK; namespace osu.Game.Tests.Visual.Gameplay { public class TestCasePlayerLoader : ManualInputManagerTestCase { private PlayerLoader loader; - private readonly OsuScreenStack stack; public TestCasePlayerLoader() @@ -29,8 +26,6 @@ namespace osu.Game.Tests.Visual.Gameplay { Beatmap.Value = new DummyWorkingBeatmap(game); - AddStep("Reset logo position", () => logo = new OsuLogo { Position = new Vector2(0, 0) }); - AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => new Player { AllowPause = false, @@ -58,6 +53,8 @@ namespace osu.Game.Tests.Visual.Gameplay AllowLeadIn = false, AllowResults = false, })); + + Scheduler.AddDelayed(() => slow.Ready = true, 5000); }); AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen()); diff --git a/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs index 5591bec0b8..0d4caff97e 100644 --- a/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs @@ -11,7 +11,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Testing; +using osu.Framework.Graphics.UserInterface; using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Mods; @@ -31,7 +31,11 @@ namespace osu.Game.Tests.Visual typeof(PlayerLoader), typeof(Player), typeof(Facade), - typeof(FacadeContainer) + typeof(FacadeContainer), + typeof(ButtonSystem), + typeof(ButtonSystemState), + typeof(Menu), + typeof(MainMenu) }; [Cached] @@ -68,15 +72,10 @@ namespace osu.Game.Tests.Visual AllowPause = false, AllowLeadIn = false, AllowResults = false, + Ready = false }))); } - [Test] - public void MainMenuTest() - { - AddStep("Add new Main Menu", () => LoadScreen(new MainMenu())); - } - private class TestFacadeContainer : FacadeContainer { protected override Facade CreateFacade() => new Facade @@ -119,16 +118,23 @@ namespace osu.Game.Tests.Visual base.LogoArriving(logo, resuming); logo.FadeIn(350); logo.ScaleTo(new Vector2(0.15f), 350, Easing.In); - facadeContainer.SetLogo(logo); + facadeContainer.SetLogo(logo, 0.3f, 1000, Easing.InOutQuint); + facadeContainer.Tracking = true; moveLogoFacade(); } + protected override void LogoExiting(OsuLogo logo) + { + base.LogoExiting(logo); + facadeContainer.Tracking = false; + } + private void moveLogoFacade() { Random random = new Random(); if (facadeFlowComponent.Transforms.Count == 0) { - facadeFlowComponent.Delay(500).MoveTo(new Vector2(random.Next(0, 800), random.Next(0, 600)), 300); + facadeFlowComponent.Delay(500).MoveTo(new Vector2(random.Next(0, (int)DrawWidth), random.Next(0, (int)DrawHeight)), 300); } if (randomPositions) @@ -159,11 +165,13 @@ namespace osu.Game.Tests.Visual private class TestPlayer : Player { + public bool Ready; + [BackgroundDependencyLoader] private void load() { // Never finish loading - while (true) + while (!Ready) Thread.Sleep(1); } } diff --git a/osu.Game/Graphics/Containers/FacadeContainer.cs b/osu.Game/Graphics/Containers/FacadeContainer.cs index 611cc94958..47ba738f1c 100644 --- a/osu.Game/Graphics/Containers/FacadeContainer.cs +++ b/osu.Game/Graphics/Containers/FacadeContainer.cs @@ -10,80 +10,98 @@ using osuTK; namespace osu.Game.Graphics.Containers { + /// + /// A container that creates a to be used by its children. + /// This container also updates the position and size of the Facade, and contains logic for tracking an on the Facade's position. + /// public class FacadeContainer : Container { + protected virtual Facade CreateFacade() => new Facade(); + + public Facade Facade => facade; + + /// + /// Whether or not the logo assigned to this FacadeContainer should be tracking the position its facade. + /// + public bool Tracking; + [Cached] private Facade facade; private OsuLogo logo; + private float facadeScale; - private bool tracking; - - protected virtual Facade CreateFacade() => new Facade(); + private Vector2 startPosition; + private Easing easing; + private double startTime; + private double duration; public FacadeContainer() { facade = CreateFacade(); } - private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(facade.ScreenSpaceDrawQuad.Centre); - - public void SetLogo(OsuLogo logo, double transformDelay = 0) + /// + /// Set the logo that should track the Facade's position, as well as how it should transform to its initial position. + /// + /// The instance of the logo to be used for tracking. + /// The scale of the facade. + /// The duration of the initial transform. Default is instant. + /// The easing type of the initial transform. + public void SetLogo(OsuLogo logo, float facadeScale, double duration = 0, Easing easing = Easing.None) { if (logo != null) { - facade.Size = new Vector2(logo.SizeForFlow * 0.3f); this.logo = logo; - Scheduler.AddDelayed(() => - { - tracking = true; - }, transformDelay); } + + this.facadeScale = facadeScale; + this.duration = duration; + this.easing = easing; } - private double startTime; - private double duration = 1000; - - private Vector2 startPosition; - private Easing easing = Easing.InOutExpo; + private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(facade.ScreenSpaceDrawQuad.Centre); protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); - if (logo == null || !tracking) + if (logo == null || !Tracking) return; - facade.Size = new Vector2(logo.SizeForFlow * 0.3f); + facade.Size = new Vector2(logo.SizeForFlow * facadeScale); if (facade.IsLoaded && logo.Position != logoTrackingPosition) { - if (logo.RelativePositionAxes != Axes.None) - { - logo.RelativePositionAxes = Axes.None; - logo.Position = logo.Parent.ToLocalSpace(logo.Position); - } + // Required for the correct position of the logo to be set with respect to logoTrackingPosition + logo.RelativePositionAxes = Axes.None; + // If this is our first update since tracking has started, initialize our starting values for interpolation if (startTime == 0) { startTime = Time.Current; + startPosition = logo.Position; } var endTime = startTime + duration; var remainingDuration = endTime - Time.Current; - if (remainingDuration <= 0) - { - remainingDuration = 0; - } + // If our transform should be instant, our position should already be at logoTrackingPosition, thus set the blend to 0. + // If we are already past when the transform should be finished playing, set the blend to 0 so that the logo is always at the position of the facade. + var blend = duration > 0 && remainingDuration > 0 + ? (float)Interpolation.ApplyEasing(easing, remainingDuration / duration) + : 0; - float currentTime = (float)Interpolation.ApplyEasing(easing, remainingDuration / duration); - logo.Position = Vector2.Lerp(logoTrackingPosition, startPosition, currentTime); + // Interpolate the position of the logo, where blend 0 is the position of the Facade, and blend 1 is where the logo was when it first began interpolating. + logo.Position = Vector2.Lerp(logoTrackingPosition, startPosition, blend); } } } } +/// +/// A placeholder container that serves as a dummy object to denote another object's location and size. +/// public class Facade : Container { -} \ No newline at end of file +} diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 3df4ef9059..4c0bcd399a 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; @@ -16,6 +17,7 @@ using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.Online.API; @@ -53,15 +55,19 @@ namespace osu.Game.Screens.Menu if (this.logo != null) { this.logo.Action = onOsuLogo; + facadeContainer.SetLogo(logo, 0.5f); // osuLogo.SizeForFlow relies on loading to be complete. buttonArea.Flow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0); updateLogoState(); } + else + { + facadeContainer.Tracking = false; + } } - private readonly Drawable iconFacade; private readonly ButtonArea buttonArea; private readonly Button backButton; @@ -71,26 +77,29 @@ namespace osu.Game.Screens.Menu private SampleChannel sampleBack; + private readonly FacadeContainer facadeContainer; + public ButtonSystem() { RelativeSizeAxes = Axes.Both; - Child = buttonArea = new ButtonArea(); + Child = facadeContainer = new FacadeContainer + { + RelativeSizeAxes = Axes.Both, + Child = buttonArea = new ButtonArea() + }; - buttonArea.AddRange(new[] + buttonArea.AddRange(new Container[] { new Button(@"settings", string.Empty, FontAwesome.fa_gear, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O), backButton = new Button(@"back", @"button-back-select", FontAwesome.fa_osu_left_o, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, -WEDGE_WIDTH) { VisibleState = ButtonSystemState.Play, }, - iconFacade = new Container //need a container to make the osu! icon flow properly. - { - Size = new Vector2(0, ButtonArea.BUTTON_AREA_HEIGHT) - } + facadeContainer.Facade }); - buttonArea.Flow.CentreTarget = iconFacade; + buttonArea.Flow.CentreTarget = facadeContainer.Facade; } [Resolved(CanBeNull = true)] @@ -120,6 +129,15 @@ namespace osu.Game.Screens.Menu buttonArea.AddRange(buttonsPlay); buttonArea.AddRange(buttonsTopLevel); + buttonArea.ForEach(b => + { + if (b is Button) + { + b.Origin = Anchor.CentreLeft; + b.Anchor = Anchor.CentreLeft; + } + }); + isIdle.ValueChanged += idle => updateIdleState(idle.NewValue); if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle); @@ -247,7 +265,7 @@ namespace osu.Game.Screens.Menu logoDelayedAction?.Cancel(); logoDelayedAction = Scheduler.AddDelayed(() => { - logoTracking = false; + facadeContainer.Tracking = false; game?.Toolbar.Hide(); @@ -266,19 +284,16 @@ namespace osu.Game.Screens.Menu break; case ButtonSystemState.Initial: logo.ClearTransforms(targetMember: nameof(Position)); - logo.RelativePositionAxes = Axes.None; bool impact = logo.Scale.X > 0.6f; if (lastState == ButtonSystemState.Initial) logo.ScaleTo(0.5f, 200, Easing.In); - logo.MoveTo(logoTrackingPosition, lastState == ButtonSystemState.EnteringMode ? 0 : 200, Easing.In); - logoDelayedAction?.Cancel(); logoDelayedAction = Scheduler.AddDelayed(() => { - logoTracking = true; + facadeContainer.Tracking = true; if (impact) logo.Impact(); @@ -288,35 +303,17 @@ namespace osu.Game.Screens.Menu break; default: logo.ClearTransforms(targetMember: nameof(Position)); - logo.RelativePositionAxes = Axes.None; - logoTracking = true; + facadeContainer.Tracking = true; logo.ScaleTo(0.5f, 200, Easing.OutQuint); break; } break; case ButtonSystemState.EnteringMode: - logoTracking = true; + facadeContainer.Tracking = true; break; } } - - private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(iconFacade.ScreenSpaceDrawQuad.Centre); - - private bool logoTracking; - - protected override void Update() - { - base.Update(); - - if (logo != null) - { - if (logoTracking && logo.RelativePositionAxes == Axes.None && iconFacade.IsLoaded) - logo.Position = logoTrackingPosition; - - iconFacade.Width = logo.SizeForFlow * 0.5f; - } - } } public enum ButtonSystemState diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index b7155f771f..f35eb6979d 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Play private Player player; - private FacadeContainer content; + private FacadeContainer facadeContainer; protected virtual FacadeContainer CreateFacadeContainer() => new FacadeContainer(); @@ -62,11 +62,11 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load() { - InternalChild = content = CreateFacadeContainer(); - content.Anchor = Anchor.Centre; - content.Origin = Anchor.Centre; - content.RelativeSizeAxes = Axes.Both; - content.Children = new Drawable[] + InternalChild = facadeContainer = CreateFacadeContainer(); + facadeContainer.Anchor = Anchor.Centre; + facadeContainer.Origin = Anchor.Centre; + facadeContainer.RelativeSizeAxes = Axes.Both; + facadeContainer.Children = new Drawable[] { info = new BeatmapMetadataDisplay(Beatmap.Value) { @@ -122,21 +122,21 @@ namespace osu.Game.Screens.Play private void contentIn() { - content.ScaleTo(1, 650, Easing.OutQuint); - content.FadeInFromZero(400); + facadeContainer.ScaleTo(1, 650, Easing.OutQuint); + facadeContainer.FadeInFromZero(400); } private void contentOut() { - content.ScaleTo(0.7f, 300, Easing.InQuint); - content.FadeOut(250); + facadeContainer.ScaleTo(0.7f, 300, Easing.InQuint); + facadeContainer.FadeOut(250); } public override void OnEntering(IScreen last) { base.OnEntering(last); - content.ScaleTo(0.7f); + facadeContainer.ScaleTo(0.7f); Background?.FadeColour(Color4.White, 800, Easing.OutQuint); contentIn(); @@ -155,7 +155,15 @@ namespace osu.Game.Screens.Play logo.MoveTo(new Vector2(0.5f), duration, Easing.In); logo.FadeIn(350); - content.SetLogo(logo, duration); + facadeContainer.SetLogo(logo, 0.3f, 500, Easing.InOutQuint); + + Scheduler.AddDelayed(() => facadeContainer.Tracking = true, duration); + } + + protected override void LogoExiting(OsuLogo logo) + { + base.LogoExiting(logo); + facadeContainer.Tracking = false; } protected override void LoadComplete() @@ -230,7 +238,7 @@ namespace osu.Game.Screens.Play public override bool OnExiting(IScreen next) { - content.ScaleTo(0.7f, 150, Easing.InQuint); + facadeContainer.ScaleTo(0.7f, 150, Easing.InQuint); this.FadeOut(150); cancelLoad(); @@ -305,7 +313,6 @@ namespace osu.Game.Screens.Play private LoadingAnimation loading; private Sprite backgroundSprite; private ModDisplay modDisplay; - private FillFlowContainer fillFlowContainer; public bool Loading { @@ -340,7 +347,7 @@ namespace osu.Game.Screens.Play AutoSizeAxes = Axes.Both; Children = new Drawable[] { - fillFlowContainer = new FillFlowContainer + new FillFlowContainer { AutoSizeAxes = Axes.Both, Origin = Anchor.TopCentre, From 384eee33957a30c88ec2b8ff2baffa30ede3bb98 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 27 Mar 2019 11:32:26 +0900 Subject: [PATCH 0378/2854] Remove DI requirement for the Facade in PlayerLoader --- .../Visual/TestCaseFacadeContainer.cs | 11 +++-------- osu.Game/Graphics/Containers/FacadeContainer.cs | 17 ++++++----------- osu.Game/Screens/Play/PlayerLoader.cs | 8 +++++--- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs index 0d4caff97e..6291b026f3 100644 --- a/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs @@ -93,7 +93,7 @@ namespace osu.Game.Tests.Visual private class TestScreen : OsuScreen { private TestFacadeContainer facadeContainer; - private FacadeFlowComponent facadeFlowComponent; + private Facade facadeFlowComponent; private readonly bool randomPositions; public TestScreen(bool randomPositions = false) @@ -104,13 +104,8 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load() { - InternalChild = facadeContainer = new TestFacadeContainer - { - Child = facadeFlowComponent = new FacadeFlowComponent - { - AutoSizeAxes = Axes.Both - } - }; + InternalChild = facadeContainer = new TestFacadeContainer(); + facadeContainer.Child = facadeFlowComponent = facadeContainer.Facade; } protected override void LogoArriving(OsuLogo logo, bool resuming) diff --git a/osu.Game/Graphics/Containers/FacadeContainer.cs b/osu.Game/Graphics/Containers/FacadeContainer.cs index 47ba738f1c..11c256725d 100644 --- a/osu.Game/Graphics/Containers/FacadeContainer.cs +++ b/osu.Game/Graphics/Containers/FacadeContainer.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; @@ -18,19 +17,15 @@ namespace osu.Game.Graphics.Containers { protected virtual Facade CreateFacade() => new Facade(); - public Facade Facade => facade; + public readonly Facade Facade; /// /// Whether or not the logo assigned to this FacadeContainer should be tracking the position its facade. /// public bool Tracking; - [Cached] - private Facade facade; - private OsuLogo logo; private float facadeScale; - private Vector2 startPosition; private Easing easing; private double startTime; @@ -38,11 +33,11 @@ namespace osu.Game.Graphics.Containers public FacadeContainer() { - facade = CreateFacade(); + Facade = CreateFacade(); } /// - /// Set the logo that should track the Facade's position, as well as how it should transform to its initial position. + /// Assign the logo that should track the Facade's position, as well as how it should transform to its initial position. /// /// The instance of the logo to be used for tracking. /// The scale of the facade. @@ -60,7 +55,7 @@ namespace osu.Game.Graphics.Containers this.easing = easing; } - private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(facade.ScreenSpaceDrawQuad.Centre); + private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(Facade.ScreenSpaceDrawQuad.Centre); protected override void UpdateAfterChildren() { @@ -69,9 +64,9 @@ namespace osu.Game.Graphics.Containers if (logo == null || !Tracking) return; - facade.Size = new Vector2(logo.SizeForFlow * facadeScale); + Facade.Size = new Vector2(logo.SizeForFlow * facadeScale); - if (facade.IsLoaded && logo.Position != logoTrackingPosition) + if (Facade.IsLoaded && logo.Position != logoTrackingPosition) { // Required for the correct position of the logo to be set with respect to logoTrackingPosition logo.RelativePositionAxes = Axes.None; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index f35eb6979d..4601cf71e0 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -68,7 +68,7 @@ namespace osu.Game.Screens.Play facadeContainer.RelativeSizeAxes = Axes.Both; facadeContainer.Children = new Drawable[] { - info = new BeatmapMetadataDisplay(Beatmap.Value) + info = new BeatmapMetadataDisplay(Beatmap.Value, facadeContainer.Facade) { Alpha = 0, Anchor = Anchor.Centre, @@ -310,6 +310,7 @@ namespace osu.Game.Screens.Play } private readonly WorkingBeatmap beatmap; + private readonly Facade facade; private LoadingAnimation loading; private Sprite backgroundSprite; private ModDisplay modDisplay; @@ -331,13 +332,14 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Facade facade) { this.beatmap = beatmap; + this.facade = facade; } [BackgroundDependencyLoader] - private void load(Facade facade) + private void load() { var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); From efeed715170c5e9e45501cabda7da80d59f947fa Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 27 Mar 2019 11:37:16 +0900 Subject: [PATCH 0379/2854] Add comment --- osu.Game/Screens/Menu/ButtonSystem.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 4c0bcd399a..7b458809b1 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -64,6 +64,8 @@ namespace osu.Game.Screens.Menu } else { + // If logo is null, we are suspending from the screen that uses this ButtonSystem. + // We should stop tracking as the facade is now out of scope. facadeContainer.Tracking = false; } } From 43c6a8d2e59a3fbdba14ce3014abaa289d32bef8 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 27 Mar 2019 11:44:50 +0900 Subject: [PATCH 0380/2854] use a property instead --- osu.Game/Graphics/Containers/FacadeContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/FacadeContainer.cs b/osu.Game/Graphics/Containers/FacadeContainer.cs index 11c256725d..0ec5e77c72 100644 --- a/osu.Game/Graphics/Containers/FacadeContainer.cs +++ b/osu.Game/Graphics/Containers/FacadeContainer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Graphics.Containers { protected virtual Facade CreateFacade() => new Facade(); - public readonly Facade Facade; + public Facade Facade { get; } /// /// Whether or not the logo assigned to this FacadeContainer should be tracking the position its facade. From 4b1e564df2fe8d7882725ad71b744dd6ffc70f42 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 27 Mar 2019 14:31:07 +0900 Subject: [PATCH 0381/2854] Fix test cases potentially getting stuck after 4th run --- osu.Game.Tests/Visual/TestCaseFacadeContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs index 6291b026f3..55842af867 100644 --- a/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs @@ -163,10 +163,10 @@ namespace osu.Game.Tests.Visual public bool Ready; [BackgroundDependencyLoader] - private void load() + private void load(CancellationToken token) { // Never finish loading - while (!Ready) + while (!Ready && !token.IsCancellationRequested) Thread.Sleep(1); } } From ca7a20585dbb58b09b7426949e0ad4b28543efd5 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 27 Mar 2019 17:28:53 +0900 Subject: [PATCH 0382/2854] Apply reviews, delete playerloader test --- ...iner.cs => TestCaseLogoFacadeContainer.cs} | 72 +++---------- .../Graphics/Containers/FacadeContainer.cs | 102 ------------------ .../Containers/LogoFacadeContainer.cs | 100 +++++++++++++++++ osu.Game/Screens/Menu/ButtonSystem.cs | 25 +++-- osu.Game/Screens/Play/PlayerLoader.cs | 70 ++++++------ 5 files changed, 161 insertions(+), 208 deletions(-) rename osu.Game.Tests/Visual/{TestCaseFacadeContainer.cs => TestCaseLogoFacadeContainer.cs} (60%) delete mode 100644 osu.Game/Graphics/Containers/FacadeContainer.cs create mode 100644 osu.Game/Graphics/Containers/LogoFacadeContainer.cs diff --git a/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs similarity index 60% rename from osu.Game.Tests/Visual/TestCaseFacadeContainer.cs rename to osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs index 55842af867..0e7f4d4bc0 100644 --- a/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs @@ -9,7 +9,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Game.Configuration; @@ -24,14 +23,14 @@ using osuTK.Graphics; namespace osu.Game.Tests.Visual { - public class TestCaseFacadeContainer : ScreenTestCase + public class TestCaseLogoFacadeContainer : ScreenTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(PlayerLoader), typeof(Player), - typeof(Facade), - typeof(FacadeContainer), + typeof(LogoFacadeContainer.Facade), + typeof(LogoFacadeContainer), typeof(ButtonSystem), typeof(ButtonSystemState), typeof(Menu), @@ -43,7 +42,7 @@ namespace osu.Game.Tests.Visual private readonly Bindable uiScale = new Bindable(); - public TestCaseFacadeContainer() + public TestCaseLogoFacadeContainer() { Add(logo = new OsuLogo()); } @@ -63,20 +62,7 @@ namespace osu.Game.Tests.Visual AddStep("Move facade to random position", () => LoadScreen(new TestScreen(randomPositions))); } - [Test] - public void PlayerLoaderTest() - { - AddToggleStep("Toggle mods", b => { Beatmap.Value.Mods.Value = b ? Beatmap.Value.Mods.Value.Concat(new[] { new OsuModNoFail() }) : Enumerable.Empty(); }); - AddStep("Add new playerloader", () => LoadScreen(new TestPlayerLoader(() => new TestPlayer - { - AllowPause = false, - AllowLeadIn = false, - AllowResults = false, - Ready = false - }))); - } - - private class TestFacadeContainer : FacadeContainer + private class TestLogoFacadeContainer : LogoFacadeContainer { protected override Facade CreateFacade() => new Facade { @@ -92,8 +78,8 @@ namespace osu.Game.Tests.Visual private class TestScreen : OsuScreen { - private TestFacadeContainer facadeContainer; - private Facade facadeFlowComponent; + private TestLogoFacadeContainer logoFacadeContainer; + private LogoFacadeContainer.Facade facadeFlowComponent; private readonly bool randomPositions; public TestScreen(bool randomPositions = false) @@ -104,8 +90,8 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load() { - InternalChild = facadeContainer = new TestFacadeContainer(); - facadeContainer.Child = facadeFlowComponent = facadeContainer.Facade; + InternalChild = logoFacadeContainer = new TestLogoFacadeContainer(); + logoFacadeContainer.Child = facadeFlowComponent = logoFacadeContainer.LogoFacade; } protected override void LogoArriving(OsuLogo logo, bool resuming) @@ -113,15 +99,15 @@ namespace osu.Game.Tests.Visual base.LogoArriving(logo, resuming); logo.FadeIn(350); logo.ScaleTo(new Vector2(0.15f), 350, Easing.In); - facadeContainer.SetLogo(logo, 0.3f, 1000, Easing.InOutQuint); - facadeContainer.Tracking = true; + logoFacadeContainer.SetLogo(logo, 0.3f, 1000, Easing.InOutQuint); + logoFacadeContainer.Tracking = true; moveLogoFacade(); } protected override void LogoExiting(OsuLogo logo) { base.LogoExiting(logo); - facadeContainer.Tracking = false; + logoFacadeContainer.Tracking = false; } private void moveLogoFacade() @@ -136,39 +122,5 @@ namespace osu.Game.Tests.Visual Schedule(moveLogoFacade); } } - - private class FacadeFlowComponent : FillFlowContainer - { - [BackgroundDependencyLoader] - private void load(Facade facade) - { - facade.Anchor = Anchor.TopCentre; - facade.Origin = Anchor.TopCentre; - Child = facade; - } - } - - private class TestPlayerLoader : PlayerLoader - { - public TestPlayerLoader(Func player) - : base(player) - { - } - - protected override FacadeContainer CreateFacadeContainer() => new TestFacadeContainer(); - } - - private class TestPlayer : Player - { - public bool Ready; - - [BackgroundDependencyLoader] - private void load(CancellationToken token) - { - // Never finish loading - while (!Ready && !token.IsCancellationRequested) - Thread.Sleep(1); - } - } } } diff --git a/osu.Game/Graphics/Containers/FacadeContainer.cs b/osu.Game/Graphics/Containers/FacadeContainer.cs deleted file mode 100644 index 0ec5e77c72..0000000000 --- a/osu.Game/Graphics/Containers/FacadeContainer.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.MathUtils; -using osu.Game.Screens.Menu; -using osuTK; - -namespace osu.Game.Graphics.Containers -{ - /// - /// A container that creates a to be used by its children. - /// This container also updates the position and size of the Facade, and contains logic for tracking an on the Facade's position. - /// - public class FacadeContainer : Container - { - protected virtual Facade CreateFacade() => new Facade(); - - public Facade Facade { get; } - - /// - /// Whether or not the logo assigned to this FacadeContainer should be tracking the position its facade. - /// - public bool Tracking; - - private OsuLogo logo; - private float facadeScale; - private Vector2 startPosition; - private Easing easing; - private double startTime; - private double duration; - - public FacadeContainer() - { - Facade = CreateFacade(); - } - - /// - /// Assign the logo that should track the Facade's position, as well as how it should transform to its initial position. - /// - /// The instance of the logo to be used for tracking. - /// The scale of the facade. - /// The duration of the initial transform. Default is instant. - /// The easing type of the initial transform. - public void SetLogo(OsuLogo logo, float facadeScale, double duration = 0, Easing easing = Easing.None) - { - if (logo != null) - { - this.logo = logo; - } - - this.facadeScale = facadeScale; - this.duration = duration; - this.easing = easing; - } - - private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(Facade.ScreenSpaceDrawQuad.Centre); - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - if (logo == null || !Tracking) - return; - - Facade.Size = new Vector2(logo.SizeForFlow * facadeScale); - - if (Facade.IsLoaded && logo.Position != logoTrackingPosition) - { - // Required for the correct position of the logo to be set with respect to logoTrackingPosition - logo.RelativePositionAxes = Axes.None; - - // If this is our first update since tracking has started, initialize our starting values for interpolation - if (startTime == 0) - { - startTime = Time.Current; - startPosition = logo.Position; - } - - var endTime = startTime + duration; - var remainingDuration = endTime - Time.Current; - - // If our transform should be instant, our position should already be at logoTrackingPosition, thus set the blend to 0. - // If we are already past when the transform should be finished playing, set the blend to 0 so that the logo is always at the position of the facade. - var blend = duration > 0 && remainingDuration > 0 - ? (float)Interpolation.ApplyEasing(easing, remainingDuration / duration) - : 0; - - // Interpolate the position of the logo, where blend 0 is the position of the Facade, and blend 1 is where the logo was when it first began interpolating. - logo.Position = Vector2.Lerp(logoTrackingPosition, startPosition, blend); - } - } - } -} - -/// -/// A placeholder container that serves as a dummy object to denote another object's location and size. -/// -public class Facade : Container -{ -} diff --git a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs new file mode 100644 index 0000000000..a556fa697c --- /dev/null +++ b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs @@ -0,0 +1,100 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.MathUtils; +using osu.Game.Screens.Menu; +using osuTK; + +namespace osu.Game.Graphics.Containers +{ + /// + /// A container that creates a to be used to update and track the position of an . + /// + public class LogoFacadeContainer : Container + { + protected virtual Facade CreateFacade() => new Facade(); + + public Facade LogoFacade { get; } + + /// + /// Whether or not the logo assigned to this FacadeContainer should be tracking the position its facade. + /// + public bool Tracking = false; + + private OsuLogo logo; + private float facadeScale; + private Vector2 startPosition; + private Easing easing; + private double? startTime; + private double duration; + + public LogoFacadeContainer() + { + LogoFacade = CreateFacade(); + } + + /// + /// Assign the logo that should track the Facade's position, as well as how it should transform to its initial position. + /// + /// The instance of the logo to be used for tracking. + /// The scale of the facade. Does not actually affect the logo itself. + /// The duration of the initial transform. Default is instant. + /// The easing type of the initial transform. + public void SetLogo(OsuLogo logo, float facadeScale, double duration = 0, Easing easing = Easing.None) + { + this.logo = logo ?? throw new ArgumentNullException(nameof(logo)); + this.facadeScale = facadeScale; + this.duration = duration; + this.easing = easing; + } + + private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre); + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + if (logo == null || !Tracking) + return; + + LogoFacade.Size = new Vector2(logo.SizeForFlow * facadeScale); + + if (LogoFacade.Parent != null && logo.Position != logoTrackingPosition) + { + // Required for the correct position of the logo to be set with respect to logoTrackingPosition + logo.RelativePositionAxes = Axes.None; + + // If this is our first update since tracking has started, initialize our starting values for interpolation + if (startTime == null) + { + startTime = Time.Current; + startPosition = logo.Position; + } + + if (duration != 0) + { + double elapsedDuration = Time.Current - startTime ?? 0; + + var mount = (float)Interpolation.ApplyEasing(easing, Math.Min(elapsedDuration / duration, 1)); + + // Interpolate the position of the logo, where mount 0 is where the logo was when it first began interpolating, and mount 1 is the target location. + logo.Position = Vector2.Lerp(startPosition, logoTrackingPosition, mount); + } + else + { + logo.Position = logoTrackingPosition; + } + } + } + + /// + /// A placeholder container that serves as a dummy object to denote another object's location and size. + /// + public class Facade : Container + { + } + } +} diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 7b458809b1..3fad36cddb 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -48,6 +48,10 @@ namespace osu.Game.Screens.Menu private OsuLogo logo; + /// + /// Assign the that this ButtonSystem should manage the position of. + /// + /// The instance of the logo to be assigned. If null, we are suspending from the screen that uses this ButtonSystem. public void SetOsuLogo(OsuLogo logo) { this.logo = logo; @@ -55,7 +59,7 @@ namespace osu.Game.Screens.Menu if (this.logo != null) { this.logo.Action = onOsuLogo; - facadeContainer.SetLogo(logo, 0.5f); + logoFacadeContainer.SetLogo(logo, 0.5f); // osuLogo.SizeForFlow relies on loading to be complete. buttonArea.Flow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0); @@ -64,9 +68,8 @@ namespace osu.Game.Screens.Menu } else { - // If logo is null, we are suspending from the screen that uses this ButtonSystem. // We should stop tracking as the facade is now out of scope. - facadeContainer.Tracking = false; + logoFacadeContainer.Tracking = false; } } @@ -79,13 +82,13 @@ namespace osu.Game.Screens.Menu private SampleChannel sampleBack; - private readonly FacadeContainer facadeContainer; + private readonly LogoFacadeContainer logoFacadeContainer; public ButtonSystem() { RelativeSizeAxes = Axes.Both; - Child = facadeContainer = new FacadeContainer + Child = logoFacadeContainer = new LogoFacadeContainer { RelativeSizeAxes = Axes.Both, Child = buttonArea = new ButtonArea() @@ -98,10 +101,10 @@ namespace osu.Game.Screens.Menu { VisibleState = ButtonSystemState.Play, }, - facadeContainer.Facade + logoFacadeContainer.LogoFacade }); - buttonArea.Flow.CentreTarget = facadeContainer.Facade; + buttonArea.Flow.CentreTarget = logoFacadeContainer.LogoFacade; } [Resolved(CanBeNull = true)] @@ -267,7 +270,7 @@ namespace osu.Game.Screens.Menu logoDelayedAction?.Cancel(); logoDelayedAction = Scheduler.AddDelayed(() => { - facadeContainer.Tracking = false; + logoFacadeContainer.Tracking = false; game?.Toolbar.Hide(); @@ -295,7 +298,7 @@ namespace osu.Game.Screens.Menu logoDelayedAction?.Cancel(); logoDelayedAction = Scheduler.AddDelayed(() => { - facadeContainer.Tracking = true; + logoFacadeContainer.Tracking = true; if (impact) logo.Impact(); @@ -305,14 +308,14 @@ namespace osu.Game.Screens.Menu break; default: logo.ClearTransforms(targetMember: nameof(Position)); - facadeContainer.Tracking = true; + logoFacadeContainer.Tracking = true; logo.ScaleTo(0.5f, 200, Easing.OutQuint); break; } break; case ButtonSystemState.EnteringMode: - facadeContainer.Tracking = true; + logoFacadeContainer.Tracking = true; break; } } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 4601cf71e0..6ac2c8220f 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -33,9 +33,7 @@ namespace osu.Game.Screens.Play private Player player; - private FacadeContainer facadeContainer; - - protected virtual FacadeContainer CreateFacadeContainer() => new FacadeContainer(); + private LogoFacadeContainer content; private BeatmapMetadataDisplay info; @@ -62,30 +60,32 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load() { - InternalChild = facadeContainer = CreateFacadeContainer(); - facadeContainer.Anchor = Anchor.Centre; - facadeContainer.Origin = Anchor.Centre; - facadeContainer.RelativeSizeAxes = Axes.Both; - facadeContainer.Children = new Drawable[] + InternalChild = content = new LogoFacadeContainer { - info = new BeatmapMetadataDisplay(Beatmap.Value, facadeContainer.Facade) + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - Alpha = 0, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 20), - Margin = new MarginPadding(25), - Children = new PlayerSettingsGroup[] + info = new BeatmapMetadataDisplay(Beatmap.Value, content.LogoFacade) { - VisualSettings = new VisualSettings(), - new InputSettings() + Alpha = 0, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Margin = new MarginPadding(25), + Children = new PlayerSettingsGroup[] + { + VisualSettings = new VisualSettings(), + new InputSettings() + } } } }; @@ -122,21 +122,21 @@ namespace osu.Game.Screens.Play private void contentIn() { - facadeContainer.ScaleTo(1, 650, Easing.OutQuint); - facadeContainer.FadeInFromZero(400); + content.ScaleTo(1, 650, Easing.OutQuint); + content.FadeInFromZero(400); } private void contentOut() { - facadeContainer.ScaleTo(0.7f, 300, Easing.InQuint); - facadeContainer.FadeOut(250); + content.ScaleTo(0.7f, 300, Easing.InQuint); + content.FadeOut(250); } public override void OnEntering(IScreen last) { base.OnEntering(last); - facadeContainer.ScaleTo(0.7f); + content.ScaleTo(0.7f); Background?.FadeColour(Color4.White, 800, Easing.OutQuint); contentIn(); @@ -155,15 +155,15 @@ namespace osu.Game.Screens.Play logo.MoveTo(new Vector2(0.5f), duration, Easing.In); logo.FadeIn(350); - facadeContainer.SetLogo(logo, 0.3f, 500, Easing.InOutQuint); + content.SetLogo(logo, 0.3f, 500, Easing.InOutExpo); - Scheduler.AddDelayed(() => facadeContainer.Tracking = true, duration); + Scheduler.AddDelayed(() => content.Tracking = true, resuming ? 0 : 500); } protected override void LogoExiting(OsuLogo logo) { base.LogoExiting(logo); - facadeContainer.Tracking = false; + content.Tracking = false; } protected override void LoadComplete() @@ -238,7 +238,7 @@ namespace osu.Game.Screens.Play public override bool OnExiting(IScreen next) { - facadeContainer.ScaleTo(0.7f, 150, Easing.InQuint); + content.ScaleTo(0.7f, 150, Easing.InQuint); this.FadeOut(150); cancelLoad(); @@ -310,7 +310,7 @@ namespace osu.Game.Screens.Play } private readonly WorkingBeatmap beatmap; - private readonly Facade facade; + private readonly LogoFacadeContainer.Facade facade; private LoadingAnimation loading; private Sprite backgroundSprite; private ModDisplay modDisplay; @@ -332,7 +332,7 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Facade facade) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, LogoFacadeContainer.Facade facade) { this.beatmap = beatmap; this.facade = facade; From 34a33b335d1e5046cd1cc5f9712f5994753d8fc9 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 27 Mar 2019 17:29:38 +0900 Subject: [PATCH 0383/2854] oops --- osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs index 0e7f4d4bc0..152b9b1706 100644 --- a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs @@ -3,8 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Threading; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -13,8 +11,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Game.Configuration; using osu.Game.Graphics.Containers; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; From 2e3791be1ca9e67676e4b5b42be184cd7c001060 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 27 Mar 2019 18:11:12 +0900 Subject: [PATCH 0384/2854] Fix incorrect usage of LogoFacade --- osu.Game/Screens/Play/PlayerLoader.cs | 39 ++++++++++++++------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 6ac2c8220f..3f547755c1 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -65,27 +65,28 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + }; + + content.Children = new Drawable[] + { + info = new BeatmapMetadataDisplay(Beatmap.Value, content.LogoFacade) { - info = new BeatmapMetadataDisplay(Beatmap.Value, content.LogoFacade) + Alpha = 0, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Margin = new MarginPadding(25), + Children = new PlayerSettingsGroup[] { - Alpha = 0, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 20), - Margin = new MarginPadding(25), - Children = new PlayerSettingsGroup[] - { - VisualSettings = new VisualSettings(), - new InputSettings() - } + VisualSettings = new VisualSettings(), + new InputSettings() } } }; From 061527a260241e78b5ce479006f6c91e2b91e271 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 27 Mar 2019 20:04:01 +0900 Subject: [PATCH 0385/2854] Add new automated tests for logofacade, reset interpolation --- .../Visual/TestCaseLogoFacadeContainer.cs | 115 ++++++++++++++---- .../Containers/LogoFacadeContainer.cs | 11 +- osu.Game/Screens/Play/PlayerLoader.cs | 4 +- 3 files changed, 103 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs index 152b9b1706..8be0d25a86 100644 --- a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs @@ -7,8 +7,10 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Screens; using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Screens; @@ -25,7 +27,6 @@ namespace osu.Game.Tests.Visual { typeof(PlayerLoader), typeof(Player), - typeof(LogoFacadeContainer.Facade), typeof(LogoFacadeContainer), typeof(ButtonSystem), typeof(ButtonSystemState), @@ -47,36 +48,65 @@ namespace osu.Game.Tests.Visual private void load(OsuConfigManager config) { config.BindWith(OsuSetting.UIScale, uiScale); - AddSliderStep("Adjust scale", 1f, 1.5f, 1f, v => uiScale.Value = v); + AddSliderStep("Adjust scale", 0.8f, 1.5f, 1f, v => uiScale.Value = v); } + /// + /// Move the facade to 0,0, then move it to a random new location while the logo is still transforming to it. + /// Check if the logo is still tracking the facade. + /// [Test] - public void IsolatedTest() + public void MoveFacadeTest() { + TestScreen screen = null; bool randomPositions = false; AddToggleStep("Toggle move continuously", b => randomPositions = b); - AddStep("Move facade to random position", () => LoadScreen(new TestScreen(randomPositions))); + AddStep("Move facade to random position", () => LoadScreen(screen = new TestScreen(randomPositions))); + AddUntilStep("Screen is current", () => screen.IsCurrentScreen()); + waitForMove(); + AddAssert("Logo is tracking", () => screen.IsLogoTracking); } - private class TestLogoFacadeContainer : LogoFacadeContainer + /// + /// Check if the facade is removed from the container, the logo stops tracking. + /// + [Test] + public void RemoveFacadeTest() { - protected override Facade CreateFacade() => new Facade - { - Colour = Color4.Tomato, - Alpha = 0.35f, - Child = new Box - { - Colour = Color4.Tomato, - RelativeSizeAxes = Axes.Both, - }, - }; + TestScreen screen = null; + AddStep("Move facade to random position", () => LoadScreen(screen = new TestScreen())); + AddUntilStep("Screen is current", () => screen.IsCurrentScreen()); + AddStep("Remove facade from FacadeContainer", () => screen.RemoveFacade()); + waitForMove(); + AddAssert("Logo is not tracking", () => !screen.IsLogoTracking); } + /// + /// Check if the facade gets added to a new container, tracking starts on the new facade. + /// + [Test] + public void TransferFacadeTest() + { + TestScreen screen = null; + AddStep("Move facade to random position", () => LoadScreen(screen = new TestScreen())); + AddUntilStep("Screen is current", () => screen.IsCurrentScreen()); + AddStep("Remove facade from FacadeContainer", () => screen.RemoveFacade()); + AddStep("Transfer facade to a new container", () => screen.TransferFacade()); + waitForMove(); + AddAssert("Logo is tracking", () => screen.IsLogoTracking); + } + + private void waitForMove() => AddWaitStep("Wait for transforms to finish", 5); + private class TestScreen : OsuScreen { - private TestLogoFacadeContainer logoFacadeContainer; - private LogoFacadeContainer.Facade facadeFlowComponent; + private LogoFacadeContainer logoFacadeContainer; + private Container transferContainer; + private Container logoFacade; private readonly bool randomPositions; + private OsuLogo logo; + private Box visualBox; + private Box transferContainerBox; public TestScreen(bool randomPositions = false) { @@ -86,13 +116,55 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load() { - InternalChild = logoFacadeContainer = new TestLogoFacadeContainer(); - logoFacadeContainer.Child = facadeFlowComponent = logoFacadeContainer.LogoFacade; + InternalChildren = new Drawable[] + { + logoFacadeContainer = new LogoFacadeContainer + { + Alpha = 0.35f, + RelativeSizeAxes = Axes.None, + Size = new Vector2(107), + Child = visualBox = new Box + { + Colour = Color4.Tomato, + RelativeSizeAxes = Axes.Both, + } + }, + transferContainer = new Container + { + Alpha = 0.35f, + RelativeSizeAxes = Axes.None, + Size = new Vector2(107), + Child = transferContainerBox = new Box + { + Colour = Color4.White, + RelativeSizeAxes = Axes.Both, + } + }, + }; + + logoFacadeContainer.Add(logoFacade = logoFacadeContainer.LogoFacade); + } + + public bool IsLogoTracking => logo.Position == logo.Parent.ToLocalSpace(logoFacadeContainer.LogoFacade.ScreenSpaceDrawQuad.Centre); + + public void RemoveFacade() + { + logoFacadeContainer.Remove(logoFacade); + visualBox.Colour = Color4.White; + moveLogoFacade(); + } + + public void TransferFacade() + { + transferContainer.Add(logoFacade); + transferContainerBox.Colour = Color4.Tomato; + moveLogoFacade(); } protected override void LogoArriving(OsuLogo logo, bool resuming) { base.LogoArriving(logo, resuming); + this.logo = logo; logo.FadeIn(350); logo.ScaleTo(new Vector2(0.15f), 350, Easing.In); logoFacadeContainer.SetLogo(logo, 0.3f, 1000, Easing.InOutQuint); @@ -109,9 +181,10 @@ namespace osu.Game.Tests.Visual private void moveLogoFacade() { Random random = new Random(); - if (facadeFlowComponent.Transforms.Count == 0) + if (logoFacade.Transforms.Count == 0 && transferContainer.Transforms.Count == 0) { - facadeFlowComponent.Delay(500).MoveTo(new Vector2(random.Next(0, (int)DrawWidth), random.Next(0, (int)DrawHeight)), 300); + logoFacadeContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)DrawWidth), random.Next(0, (int)DrawHeight)), 300); + transferContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)DrawWidth), random.Next(0, (int)DrawHeight)), 300); } if (randomPositions) diff --git a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs index a556fa697c..f4599e0039 100644 --- a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs +++ b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs @@ -26,8 +26,8 @@ namespace osu.Game.Graphics.Containers private OsuLogo logo; private float facadeScale; - private Vector2 startPosition; private Easing easing; + private Vector2? startPosition; private double? startTime; private double duration; @@ -49,6 +49,9 @@ namespace osu.Game.Graphics.Containers this.facadeScale = facadeScale; this.duration = duration; this.easing = easing; + + startTime = null; + startPosition = null; } private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre); @@ -68,7 +71,7 @@ namespace osu.Game.Graphics.Containers logo.RelativePositionAxes = Axes.None; // If this is our first update since tracking has started, initialize our starting values for interpolation - if (startTime == null) + if (startTime == null || startPosition == null) { startTime = Time.Current; startPosition = logo.Position; @@ -76,12 +79,12 @@ namespace osu.Game.Graphics.Containers if (duration != 0) { - double elapsedDuration = Time.Current - startTime ?? 0; + double elapsedDuration = Time.Current - startTime ?? throw new ArgumentNullException(nameof(startTime)); var mount = (float)Interpolation.ApplyEasing(easing, Math.Min(elapsedDuration / duration, 1)); // Interpolate the position of the logo, where mount 0 is where the logo was when it first began interpolating, and mount 1 is the target location. - logo.Position = Vector2.Lerp(startPosition, logoTrackingPosition, mount); + logo.Position = Vector2.Lerp(startPosition ?? throw new ArgumentNullException(nameof(startPosition)), logoTrackingPosition, mount); } else { diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 3f547755c1..72fbb378a4 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -311,7 +311,7 @@ namespace osu.Game.Screens.Play } private readonly WorkingBeatmap beatmap; - private readonly LogoFacadeContainer.Facade facade; + private readonly Container facade; private LoadingAnimation loading; private Sprite backgroundSprite; private ModDisplay modDisplay; @@ -333,7 +333,7 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap, LogoFacadeContainer.Facade facade) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Container facade) { this.beatmap = beatmap; this.facade = facade; From 9b047d9b90c112013369959d23fb1703775ac52d Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 28 Mar 2019 12:00:50 +0900 Subject: [PATCH 0386/2854] Add back menu logo transform --- osu.Game/Screens/Menu/ButtonSystem.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 3fad36cddb..21305c6489 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -295,11 +295,12 @@ namespace osu.Game.Screens.Menu if (lastState == ButtonSystemState.Initial) logo.ScaleTo(0.5f, 200, Easing.In); + logoFacadeContainer.SetLogo(logo, 0.5f, lastState == ButtonSystemState.EnteringMode ? 0 : 200, Easing.In); + logoFacadeContainer.Tracking = true; + logoDelayedAction?.Cancel(); logoDelayedAction = Scheduler.AddDelayed(() => { - logoFacadeContainer.Tracking = true; - if (impact) logo.Impact(); From bfe44eb33d7df500b5031171b928c9320b347473 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 28 Mar 2019 15:40:58 +0900 Subject: [PATCH 0387/2854] Remove SizeForFlow magic number --- osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs | 7 ++++++- osu.Game/Graphics/Containers/LogoFacadeContainer.cs | 3 ++- osu.Game/Screens/Menu/OsuLogo.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 3 ++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs index 8be0d25a86..f81e70e7b0 100644 --- a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs @@ -145,7 +145,12 @@ namespace osu.Game.Tests.Visual logoFacadeContainer.Add(logoFacade = logoFacadeContainer.LogoFacade); } - public bool IsLogoTracking => logo.Position == logo.Parent.ToLocalSpace(logoFacadeContainer.LogoFacade.ScreenSpaceDrawQuad.Centre); + private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(logoFacade.ScreenSpaceDrawQuad.Centre); + + /// + /// Check that the logo is tracking the position of the facade, with an acceptable precision lenience. + /// + public bool IsLogoTracking => Math.Abs(logo.Position.X - logoTrackingPosition.X) < 0.001f && Math.Abs(logo.Position.Y - logoTrackingPosition.Y) < 0.001f; public void RemoveFacade() { diff --git a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs index f4599e0039..547af87522 100644 --- a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs +++ b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs @@ -63,7 +63,8 @@ namespace osu.Game.Graphics.Containers if (logo == null || !Tracking) return; - LogoFacade.Size = new Vector2(logo.SizeForFlow * facadeScale); + // Account for the scale of the actual logo container, as SizeForFlow only accounts for the sprite scale. + LogoFacade.Size = new Vector2(logo.SizeForFlow * logo.Scale.X * facadeScale); if (LogoFacade.Parent != null && logo.Position != logoTrackingPosition) { diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index af697d37bd..c54ccd21b5 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -54,7 +54,7 @@ namespace osu.Game.Screens.Menu /// public Func Action; - public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * logoHoverContainer.Scale.X * 0.74f; + public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * logoHoverContainer.Scale.X; private readonly Sprite ripple; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 72fbb378a4..5a8c3846fa 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -156,7 +156,7 @@ namespace osu.Game.Screens.Play logo.MoveTo(new Vector2(0.5f), duration, Easing.In); logo.FadeIn(350); - content.SetLogo(logo, 0.3f, 500, Easing.InOutExpo); + content.SetLogo(logo, 1.0f, 500, Easing.InOutExpo); Scheduler.AddDelayed(() => content.Tracking = true, resuming ? 0 : 500); } @@ -365,6 +365,7 @@ namespace osu.Game.Screens.Play Font = OsuFont.GetFont(size: 36, italics: true), Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, + Margin = new MarginPadding { Top = 15 }, }, new OsuSpriteText { From 039e451ab1b4eee334c937a9bcaf3320118e1f48 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 28 Mar 2019 16:09:42 +0900 Subject: [PATCH 0388/2854] ensure logo is where it already needs to be on resume --- osu.Game/Screens/Play/PlayerLoader.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 5a8c3846fa..da5abbae95 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -152,8 +152,12 @@ namespace osu.Game.Screens.Play const double duration = 300; + if (!resuming) + { + logo.MoveTo(new Vector2(0.5f), duration, Easing.In); + } + logo.ScaleTo(new Vector2(0.15f), duration, Easing.In); - logo.MoveTo(new Vector2(0.5f), duration, Easing.In); logo.FadeIn(350); content.SetLogo(logo, 1.0f, 500, Easing.InOutExpo); From 9d66a5e4b20841ed8d6222a71e5767a4fe7c6eaa Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 28 Mar 2019 16:29:35 +0900 Subject: [PATCH 0389/2854] Ensure logo stops tracking before suspend animation --- osu.Game/Screens/Play/PlayerLoader.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index da5abbae95..53a349f595 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -129,6 +129,9 @@ namespace osu.Game.Screens.Play private void contentOut() { + // Ensure the logo is no longer tracking before we scale the content. + content.Tracking = false; + content.ScaleTo(0.7f, 300, Easing.InQuint); content.FadeOut(250); } From f066bd11384b56ea55afb7a0439b9a6fc8de12fd Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 28 Mar 2019 16:35:15 +0900 Subject: [PATCH 0390/2854] Adjust facade scale now that the size is different --- osu.Game/Screens/Menu/ButtonSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 21305c6489..19b460250f 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -59,7 +59,7 @@ namespace osu.Game.Screens.Menu if (this.logo != null) { this.logo.Action = onOsuLogo; - logoFacadeContainer.SetLogo(logo, 0.5f); + logoFacadeContainer.SetLogo(logo, 0.74f); // osuLogo.SizeForFlow relies on loading to be complete. buttonArea.Flow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0); @@ -295,7 +295,7 @@ namespace osu.Game.Screens.Menu if (lastState == ButtonSystemState.Initial) logo.ScaleTo(0.5f, 200, Easing.In); - logoFacadeContainer.SetLogo(logo, 0.5f, lastState == ButtonSystemState.EnteringMode ? 0 : 200, Easing.In); + logoFacadeContainer.SetLogo(logo, 0.74f, lastState == ButtonSystemState.EnteringMode ? 0 : 200, Easing.In); logoFacadeContainer.Tracking = true; logoDelayedAction?.Cancel(); From 352b4b20d917d34d2d8f9ec4b34e9b9323f41c46 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 29 Mar 2019 16:28:25 +0900 Subject: [PATCH 0391/2854] Correct the sizes of TestCaseLogoFacadeContainer --- osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs index f81e70e7b0..adbf22302b 100644 --- a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs @@ -122,7 +122,7 @@ namespace osu.Game.Tests.Visual { Alpha = 0.35f, RelativeSizeAxes = Axes.None, - Size = new Vector2(107), + Size = new Vector2(72), Child = visualBox = new Box { Colour = Color4.Tomato, @@ -133,7 +133,7 @@ namespace osu.Game.Tests.Visual { Alpha = 0.35f, RelativeSizeAxes = Axes.None, - Size = new Vector2(107), + Size = new Vector2(72), Child = transferContainerBox = new Box { Colour = Color4.White, @@ -172,7 +172,7 @@ namespace osu.Game.Tests.Visual this.logo = logo; logo.FadeIn(350); logo.ScaleTo(new Vector2(0.15f), 350, Easing.In); - logoFacadeContainer.SetLogo(logo, 0.3f, 1000, Easing.InOutQuint); + logoFacadeContainer.SetLogo(logo, 1.0f, 1000, Easing.InOutQuint); logoFacadeContainer.Tracking = true; moveLogoFacade(); } From 952a12bb19b118fa129b8aa73551906e8baa8388 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 29 Mar 2019 16:54:34 +0900 Subject: [PATCH 0392/2854] Return logo relativepositionaxes on content out --- osu.Game/Screens/Play/PlayerLoader.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 53a349f595..9e6f7cf24e 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -37,6 +37,8 @@ namespace osu.Game.Screens.Play private BeatmapMetadataDisplay info; + private OsuLogo logo; + private bool hideOverlays; public override bool HideOverlaysOnEnter => hideOverlays; @@ -129,9 +131,12 @@ namespace osu.Game.Screens.Play private void contentOut() { - // Ensure the logo is no longer tracking before we scale the content. + // Ensure the logo is no longer tracking before we scale the content, and that its RelativePositionAxes have been returned. content.Tracking = false; + if (logo != null) + logo.RelativePositionAxes = Axes.Both; + content.ScaleTo(0.7f, 300, Easing.InQuint); content.FadeOut(250); } @@ -153,6 +158,8 @@ namespace osu.Game.Screens.Play { base.LogoArriving(logo, resuming); + this.logo = logo; + const double duration = 300; if (!resuming) From 7d6a08d6dac94a942174ae52aa4a5f8ab9357809 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Apr 2019 11:39:02 +0900 Subject: [PATCH 0393/2854] Fix a few new inspections in latest Rider EAP --- .../Replays/OsuAutoGenerator.cs | 6 ++---- osu.Game.Tests/Visual/TestCaseCharLookup.cs | 16 ++++++++++++++++ osu.Game/Scoring/Legacy/LegacyScoreParser.cs | 4 ++++ osu.Game/Skinning/LegacySkinDecoder.cs | 11 +++-------- osu.sln.DotSettings | 1 + 5 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestCaseCharLookup.cs diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index c1aaa7767e..690263c6a0 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -199,6 +199,7 @@ namespace osu.Game.Rulesets.Osu.Replays // Wait until Auto could "see and react" to the next note. double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - reactionTime); + if (waitTime > lastFrame.Time) { lastFrame = new OsuReplayFrame(waitTime, lastFrame.Position) { Actions = lastFrame.Actions }; @@ -292,7 +293,6 @@ namespace osu.Game.Rulesets.Osu.Replays { // We add intermediate frames for spinning / following a slider here. case Spinner spinner: - { Vector2 difference = startPosition - SPINNER_CENTRE; float radius = difference.Length; @@ -315,9 +315,8 @@ namespace osu.Game.Rulesets.Osu.Replays endFrame.Position = endPosition; break; - } + case Slider slider: - { for (double j = FrameDelay; j < slider.Duration; j += FrameDelay) { Vector2 pos = slider.StackedPositionAt(j / slider.Duration); @@ -326,7 +325,6 @@ namespace osu.Game.Rulesets.Osu.Replays AddFrameToReplay(new OsuReplayFrame(slider.EndTime, new Vector2(slider.StackedEndPosition.X, slider.StackedEndPosition.Y), action)); break; - } } // We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed! diff --git a/osu.Game.Tests/Visual/TestCaseCharLookup.cs b/osu.Game.Tests/Visual/TestCaseCharLookup.cs new file mode 100644 index 0000000000..0b9413f332 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseCharLookup.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseCharLookup : OsuTestCase + { + public TestCaseCharLookup() + { + AddStep("null", () => { }); + AddStep("display acharacter", () => Add(new OsuSpriteText { Text = "振込申請" })); + } + } +} diff --git a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs index 51810945db..bc54882f28 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs @@ -88,6 +88,7 @@ namespace osu.Game.Scoring.Legacy throw new IOException("input .lzma is too short"); long outSize = 0; + for (int i = 0; i < 8; i++) { int v = replayInStream.ReadByte(); @@ -144,6 +145,7 @@ namespace osu.Game.Scoring.Legacy score.Rank = ScoreRank.D; break; } + case 1: { int totalHits = count50 + count100 + count300 + countMiss; @@ -166,6 +168,7 @@ namespace osu.Game.Scoring.Legacy score.Rank = ScoreRank.D; break; } + case 2: { int totalHits = count50 + count100 + count300 + countMiss + countKatu; @@ -185,6 +188,7 @@ namespace osu.Game.Scoring.Legacy score.Rank = ScoreRank.D; break; } + case 3: { int totalHits = count50 + count100 + count300 + countMiss + countGeki + countKatu; diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index 96a9116c51..461dfed589 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -16,12 +16,11 @@ namespace osu.Game.Skinning { line = StripComments(line); + var pair = SplitKeyVal(line); + switch (section) { case Section.General: - { - var pair = SplitKeyVal(line); - switch (pair.Key) { case @"Name": @@ -36,11 +35,8 @@ namespace osu.Game.Skinning } break; - } - case Section.Fonts: - { - var pair = SplitKeyVal(line); + case Section.Fonts: switch (pair.Key) { case "HitCirclePrefix": @@ -52,7 +48,6 @@ namespace osu.Game.Skinning } break; - } } base.ParseLine(skin, section, line); diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 71cbd83e3e..cfffed663c 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -230,6 +230,7 @@ True True NEXT_LINE + 1 NEXT_LINE 1 1 From 612db31c38737e3be893fb65f7a598730c7a1960 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Apr 2019 12:16:05 +0900 Subject: [PATCH 0394/2854] Apply newline additions --- osu.Desktop/OsuGameDesktop.cs | 2 + osu.Desktop/Overlays/VersionManager.cs | 1 + osu.Desktop/Program.cs | 1 + .../Beatmaps/CatchBeatmapProcessor.cs | 3 ++ .../Difficulty/CatchDifficultyCalculator.cs | 1 + .../Objects/Drawable/DrawableFruit.cs | 11 +++++ osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 5 +++ .../TestCaseNotes.cs | 2 + .../Beatmaps/ManiaBeatmapConverter.cs | 1 + .../Legacy/DistanceObjectPatternGenerator.cs | 7 ++++ .../Legacy/HitObjectPatternGenerator.cs | 9 ++++ .../Patterns/Legacy/PatternGenerator.cs | 1 + osu.Game.Rulesets.Mania/ManiaRuleset.cs | 7 ++++ .../Objects/Drawables/Pieces/BodyPiece.cs | 1 + .../Replays/ManiaAutoGenerator.cs | 3 ++ .../Replays/ManiaReplayFrame.cs | 1 + .../UI/DrawableManiaRuleset.cs | 3 ++ osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 2 + .../Beatmaps/OsuBeatmapProcessor.cs | 4 ++ .../Difficulty/OsuPerformanceCalculator.cs | 1 + .../Preprocessing/OsuDifficultyHitObject.cs | 1 + .../Difficulty/Skills/Speed.cs | 2 + .../Sliders/SliderPlacementBlueprint.cs | 2 + osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs | 2 + .../Connections/FollowPointRenderer.cs | 1 + .../Objects/Drawables/DrawableHitCircle.cs | 3 ++ .../UI/Cursor/CursorTrail.cs | 1 + .../Beatmaps/TaikoBeatmapConverter.cs | 1 + .../Objects/Drawables/DrawableHit.cs | 4 ++ .../Drawables/DrawableTaikoHitObject.cs | 1 + osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 1 + .../Replays/TaikoAutoGenerator.cs | 4 ++ .../SongSelect/TestCaseBeatmapCarousel.cs | 1 + .../TestCaseBeatmapScoresContainer.cs | 1 + osu.Game/Beatmaps/BeatmapManager.cs | 2 + .../Beatmaps/BeatmapManager_WorkingBeatmap.cs | 2 + osu.Game/Beatmaps/Formats/Decoder.cs | 1 + .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 42 +++++++++++++++++++ osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 1 + .../Formats/LegacyStoryboardDecoder.cs | 35 ++++++++++++++-- .../Configuration/DatabasedConfigManager.cs | 1 + osu.Game/Database/ArchiveModelManager.cs | 2 + osu.Game/Database/OsuDbContext.cs | 3 ++ .../Containers/ConstrainedIconContainer.cs | 1 + .../Graphics/Containers/LinkFlowContainer.cs | 7 ++++ .../Graphics/Containers/SectionsContainer.cs | 2 + osu.Game/Graphics/Cursor/MenuCursor.cs | 2 + .../Graphics/Cursor/OsuTooltipContainer.cs | 1 + osu.Game/Graphics/DrawableDate.cs | 2 + osu.Game/Graphics/UserInterface/BarGraph.cs | 2 + osu.Game/Graphics/UserInterface/LineGraph.cs | 1 + .../Graphics/UserInterface/OsuSliderBar.cs | 1 + .../Graphics/UserInterface/StarCounter.cs | 2 + osu.Game/IO/Legacy/SerializationReader.cs | 21 ++++++++++ osu.Game/IO/Legacy/SerializationWriter.cs | 1 + .../Converters/TypedListConverter.cs | 2 + osu.Game/Online/API/APIAccess.cs | 2 + .../Requests/Responses/APILegacyScoreInfo.cs | 7 ++++ osu.Game/Online/Chat/MessageFormatter.cs | 13 ++++++ osu.Game/Online/Leaderboards/Leaderboard.cs | 6 +++ osu.Game/OsuGame.cs | 9 ++++ .../Overlays/AccountCreation/ScreenEntry.cs | 1 + osu.Game/Overlays/Chat/DrawableChannel.cs | 1 + osu.Game/Overlays/ChatOverlay.cs | 2 + osu.Game/Overlays/Dialog/PopupDialog.cs | 2 + osu.Game/Overlays/DirectOverlay.cs | 2 + osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 1 + osu.Game/Overlays/Mods/ModButton.cs | 4 ++ osu.Game/Overlays/Mods/ModSection.cs | 1 + osu.Game/Overlays/Music/PlaylistList.cs | 1 + osu.Game/Overlays/Music/PlaylistOverlay.cs | 1 + osu.Game/Overlays/MusicController.cs | 3 ++ .../Overlays/Profile/Header/BadgeContainer.cs | 1 + osu.Game/Overlays/Profile/ProfileHeader.cs | 6 +-- osu.Game/Overlays/Settings/SettingsItem.cs | 1 + osu.Game/Overlays/Settings/SidebarButton.cs | 1 + osu.Game/Overlays/SocialOverlay.cs | 4 ++ .../Toolbar/ToolbarOverlayToggleButton.cs | 2 + .../Toolbar/ToolbarRulesetSelector.cs | 1 + osu.Game/Overlays/UserProfileOverlay.cs | 1 + osu.Game/Rulesets/Edit/SelectionBlueprint.cs | 2 + .../Objects/Drawables/DrawableHitObject.cs | 3 ++ .../Objects/Legacy/ConvertHitObjectParser.cs | 6 +++ osu.Game/Rulesets/Objects/SliderPath.cs | 3 ++ osu.Game/Rulesets/UI/DrawableRuleset.cs | 1 + .../Backgrounds/BackgroundScreenBeatmap.cs | 1 + .../Screens/Edit/Compose/ComposeScreen.cs | 2 + osu.Game/Screens/Edit/Editor.cs | 5 +++ osu.Game/Screens/Edit/EditorClock.cs | 1 + osu.Game/Screens/Menu/ButtonSystem.cs | 11 +++++ osu.Game/Screens/Menu/LogoVisualisation.cs | 1 + .../Multi/Lounge/Components/RoomInspector.cs | 1 + .../Screens/Multi/Match/MatchSubScreen.cs | 1 + osu.Game/Screens/Multi/Multiplayer.cs | 1 + osu.Game/Screens/OsuScreenDependencies.cs | 2 + osu.Game/Screens/Play/HUD/ModDisplay.cs | 1 + osu.Game/Screens/Play/KeyCounter.cs | 1 + osu.Game/Screens/Play/SquareGraph.cs | 1 + .../Screens/Ranking/Pages/ScoreResultsPage.cs | 2 + osu.Game/Screens/Select/BeatmapCarousel.cs | 8 ++++ osu.Game/Screens/Select/PlaySongSelect.cs | 1 + osu.Game/Screens/Tournament/Drawings.cs | 1 + osu.Game/Skinning/LegacySkin.cs | 6 +++ osu.Game/Skinning/SkinManager.cs | 1 + .../Drawables/DrawableStoryboardAnimation.cs | 1 + osu.Game/Storyboards/StoryboardSprite.cs | 1 + .../Tests/Beatmaps/BeatmapConversionTest.cs | 2 + osu.sln.DotSettings | 1 + 108 files changed, 359 insertions(+), 7 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index e7e0af7eea..00cabbadf7 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -77,6 +77,7 @@ namespace osu.Desktop if (versionManager != null) versionManager.State = Visibility.Visible; break; + default: if (versionManager != null) versionManager.State = Visibility.Hidden; @@ -87,6 +88,7 @@ namespace osu.Desktop public override void SetHost(GameHost host) { base.SetHost(host); + if (host.Window is DesktopGameWindow desktopWindow) { desktopWindow.CursorState |= CursorState.Hidden; diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 2998e08715..2fbbe6f685 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -95,6 +95,7 @@ namespace osu.Desktop.Overlays var version = game.Version; var lastVersion = config.Get(OsuSetting.Version); + if (game.IsDeployedBuild && version != lastVersion) { config.Set(OsuSetting.Version, version); diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index ff9972ac48..29554df64c 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -31,6 +31,7 @@ namespace osu.Desktop var importer = new ArchiveImportIPCChannel(host); // Restore the cwd so relative paths given at the command line work correctly Directory.SetCurrentDirectory(cwd); + foreach (var file in args) { Console.WriteLine(@"Importing {0}", file); diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 78b5a510b2..645cb5701a 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -31,6 +31,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps initialiseHyperDash((List)Beatmap.HitObjects); int index = 0; + foreach (var obj in Beatmap.HitObjects.OfType()) { obj.IndexInBeatmap = index++; @@ -58,6 +59,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps } break; + case JuiceStream juiceStream: foreach (var nested in juiceStream.NestedHitObjects) { @@ -103,6 +105,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext); + if (distanceToHyper < 0) { currentObject.HyperDashTarget = nextObject; diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index b4998347f4..bd647fd667 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -73,6 +73,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty lastObject = hitObject; break; + case JuiceStream _: foreach (var nested in hitObject.NestedHitObjects.OfType().Where(o => !(o is TinyDroplet))) { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs index 0dc3f73404..8368e2f276 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs @@ -105,6 +105,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable { default: return new Container(); + case FruitVisualRepresentation.Raspberry: return new Container { @@ -143,6 +144,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable }, } }; + case FruitVisualRepresentation.Pineapple: return new Container { @@ -181,6 +183,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable }, } }; + case FruitVisualRepresentation.Pear: return new Container { @@ -213,6 +216,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable }, } }; + case FruitVisualRepresentation.Grape: return new Container { @@ -245,6 +249,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable }, } }; + case FruitVisualRepresentation.Banana: return new Container { @@ -282,19 +287,25 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable default: case FruitVisualRepresentation.Pear: return new Color4(17, 136, 170, 255); + case FruitVisualRepresentation.Grape: return new Color4(204, 102, 0, 255); + case FruitVisualRepresentation.Raspberry: return new Color4(121, 9, 13, 255); + case FruitVisualRepresentation.Pineapple: return new Color4(102, 136, 0, 255); + case FruitVisualRepresentation.Banana: switch (RNG.Next(0, 3)) { default: return new Color4(255, 240, 0, 255); + case 1: return new Color4(255, 192, 0, 255); + case 2: return new Color4(214, 221, 28, 255); } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 83f791690a..e7c7fd77df 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -292,6 +292,7 @@ namespace osu.Game.Rulesets.Catch.UI const float hyper_dash_transition_length = 180; bool previouslyHyperDashing = HyperDashing; + if (modifier <= 1 || X == targetPosition) { hyperDashModifier = 1; @@ -325,9 +326,11 @@ namespace osu.Game.Rulesets.Catch.UI case CatchAction.MoveLeft: currentDirection--; return true; + case CatchAction.MoveRight: currentDirection++; return true; + case CatchAction.Dash: Dashing = true; return true; @@ -343,9 +346,11 @@ namespace osu.Game.Rulesets.Catch.UI case CatchAction.MoveLeft: currentDirection++; return true; + case CatchAction.MoveRight: currentDirection--; return true; + case CatchAction.Dash: Dashing = false; return true; diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs index 0bc2c3ea28..2220873d89 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs @@ -168,11 +168,13 @@ namespace osu.Game.Rulesets.Mania.Tests foreach (var nested in obj.NestedHitObjects) { double finalPosition = (nested.HitObject.StartTime - obj.HitObject.StartTime) / endTime.Duration; + switch (direction) { case ScrollingDirection.Up: nested.Y = (float)(finalPosition * content.DrawHeight); break; + case ScrollingDirection.Down: nested.Y = (float)(-finalPosition * content.DrawHeight); break; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 71df68c087..704deba78b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -48,6 +48,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps if (IsForCurrentRuleset) { TargetColumns = (int)Math.Max(1, roundedCircleSize); + if (TargetColumns >= 10) { TargetColumns = TargetColumns / 2; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index ed52bdd23f..1b6ff16388 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -179,6 +179,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy int usableColumns = TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects; int nextColumn = GetRandomColumn(); + for (int i = 0; i < Math.Min(usableColumns, noteCount); i++) { // Find available column @@ -217,6 +218,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy nextColumn = FindAvailableColumn(nextColumn, PreviousPattern); int lastColumn = nextColumn; + for (int i = 0; i < noteCount; i++) { addToPattern(pattern, nextColumn, startTime, startTime); @@ -299,6 +301,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy int interval = Random.Next(1, TotalColumns - (legacy ? 1 : 0)); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); + for (int i = 0; i <= spanCount; i++) { addToPattern(pattern, nextColumn, startTime, startTime); @@ -341,16 +344,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy p3 = 0; p4 = 0; break; + case 3: p2 = Math.Min(p2, 0.1); p3 = 0; p4 = 0; break; + case 4: p2 = Math.Min(p2, 0.3); p3 = Math.Min(p3, 0.04); p4 = 0; break; + case 5: p2 = Math.Min(p2, 0.34); p3 = Math.Min(p3, 0.1); @@ -440,6 +446,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP); var rowPattern = new Pattern(); + for (int i = 0; i <= spanCount; i++) { if (!(ignoreHead && startTime == HitObject.StartTime)) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 34f5f5c415..d13b21183b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -233,6 +233,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); + for (int i = 0; i < noteCount; i++) { nextColumn = allowStacking @@ -303,6 +304,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2; int nextColumn = GetRandomColumn(upperBound: columnLimit); + for (int i = 0; i < noteCount; i++) { nextColumn = FindAvailableColumn(nextColumn, upperBound: columnLimit, patterns: pattern); @@ -340,18 +342,21 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy p4 = 0; p5 = 0; break; + case 3: p2 = Math.Min(p2, 0.1); p3 = 0; p4 = 0; p5 = 0; break; + case 4: p2 = Math.Min(p2, 0.23); p3 = Math.Min(p3, 0.04); p4 = 0; p5 = 0; break; + case 5: p3 = Math.Min(p3, 0.15); p4 = Math.Min(p4, 0.03); @@ -384,20 +389,24 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy p2 = 0; p3 = 0; break; + case 3: centreProbability = Math.Min(centreProbability, 0.03); p2 = 0; p3 = 0; break; + case 4: centreProbability = 0; p2 = Math.Min(p2 * 2, 0.2); p3 = 0; break; + case 5: centreProbability = Math.Min(centreProbability, 0.03); p3 = 0; break; + case 6: centreProbability = 0; p2 = Math.Min(p2 * 2, 0.5); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index b702291c5d..fba52dfc32 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -158,6 +158,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy // Ensure that we have at least one free column, so that an endless loop is avoided bool hasValidColumns = false; + for (int i = lowerBound.Value; i < upperBound.Value; i++) { hasValidColumns = isValid(i); diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 0ff79d2836..0ef53f0f37 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -117,6 +117,7 @@ namespace osu.Game.Rulesets.Mania new ManiaModNoFail(), new MultiMod(new ManiaModHalfTime(), new ManiaModDaycore()), }; + case ModType.DifficultyIncrease: return new Mod[] { @@ -126,6 +127,7 @@ namespace osu.Game.Rulesets.Mania new MultiMod(new ManiaModFadeIn(), new ManiaModHidden()), new ManiaModFlashlight(), }; + case ModType.Conversion: return new Mod[] { @@ -142,16 +144,19 @@ namespace osu.Game.Rulesets.Mania new ManiaModDualStages(), new ManiaModMirror(), }; + case ModType.Automation: return new Mod[] { new MultiMod(new ManiaModAutoplay(), new ModCinema()), }; + case ModType.Fun: return new Mod[] { new MultiMod(new ModWindUp(), new ModWindDown()) }; + default: return new Mod[] { }; } @@ -214,6 +219,7 @@ namespace osu.Game.Rulesets.Mania SpecialAction = ManiaAction.Special1, NormalActionStart = ManiaAction.Key1, }.GenerateKeyBindingsFor(variant, out _); + case PlayfieldType.Dual: int keys = getDualStageKeyCount(variant); @@ -271,6 +277,7 @@ namespace osu.Game.Rulesets.Mania { default: return $"{variant}K"; + case PlayfieldType.Dual: { var keys = getDualStageKeyCount(variant); diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs index 2baf1ad520..2bed1d42db 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs @@ -144,6 +144,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces const float animation_length = 50; Foreground.ClearTransforms(false, nameof(Foreground.Colour)); + if (hitting) { // wait for the next sync point diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 65b7d54cd2..e5669816fa 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -28,6 +28,7 @@ namespace osu.Game.Rulesets.Mania.Replays var normalAction = ManiaAction.Key1; var specialAction = ManiaAction.Special1; int totalCounter = 0; + foreach (var stage in Beatmap.Stages) { for (int i = 0; i < stage.Columns; i++) @@ -51,6 +52,7 @@ namespace osu.Game.Rulesets.Mania.Replays var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time); var actions = new List(); + foreach (var group in pointGroups) { foreach (var point in group) @@ -60,6 +62,7 @@ namespace osu.Game.Rulesets.Mania.Replays case HitPoint _: actions.Add(columnActions[point.Column]); break; + case ReleasePoint _: actions.Remove(columnActions[point.Column]); break; diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index 81a76c93e6..f7277d3669 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -39,6 +39,7 @@ namespace osu.Game.Rulesets.Mania.Replays int activeColumns = (int)(legacyFrame.MouseX ?? 0); int counter = 0; + while (activeColumns > 0) { var isSpecial = stage.IsSpecialColumn(counter); diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 1c1ec604f6..989bbdbfde 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -56,6 +56,7 @@ namespace osu.Game.Rulesets.Mania.UI double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature; int index = 0; + for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++) { barLines.Add(new BarLine @@ -104,8 +105,10 @@ namespace osu.Game.Rulesets.Mania.UI { case HoldNote holdNote: return new DrawableHoldNote(holdNote); + case Note note: return new DrawableNote(note); + default: return null; } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index cbabfcc8b4..5ab07416a6 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Mania.UI var normalColumnAction = ManiaAction.Key1; var specialColumnAction = ManiaAction.Special1; int firstColumnIndex = 0; + for (int i = 0; i < stageDefinitions.Count; i++) { var newStage = new ManiaStage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction); @@ -92,6 +93,7 @@ namespace osu.Game.Rulesets.Mania.UI private ManiaStage getStageByColumn(int column) { int sum = 0; + foreach (var stage in stages) { sum = sum + stage.Columns.Count; diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index 59a5f90fd0..b2beda18f4 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -44,12 +44,14 @@ namespace osu.Game.Rulesets.Osu.Beatmaps if (endIndex < 0) throw new ArgumentOutOfRangeException(nameof(endIndex), $"{nameof(endIndex)} cannot be less than 0."); int extendedEndIndex = endIndex; + if (endIndex < beatmap.HitObjects.Count - 1) { // Extend the end index to include objects they are stacked on for (int i = endIndex; i >= startIndex; i--) { int stackBaseIndex = i; + for (int n = stackBaseIndex + 1; n < beatmap.HitObjects.Count; n++) { OsuHitObject stackBaseObject = beatmap.HitObjects[stackBaseIndex]; @@ -87,6 +89,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps //Reverse pass for stack calculation. int extendedStartIndex = startIndex; + for (int i = extendedEndIndex; i > startIndex; i--) { int n = i; @@ -138,6 +141,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps if (objectN is Slider && Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance) { int offset = objectI.StackHeight - objectN.StackHeight + 1; + for (int j = n + 1; j <= i; j++) { //For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above). diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 0dce5208dd..093081b6a1 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -109,6 +109,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f); double approachRateFactor = 1.0f; + if (Attributes.ApproachRate > 10.33f) approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f); else if (Attributes.ApproachRate < 8.0f) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 37276a3432..eacac7ae6a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -56,6 +56,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing { // We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps. float scalingFactor = normalized_radius / (float)BaseObject.Radius; + if (BaseObject.Radius < 30) { float smallCircleBonus = Math.Min(30 - (float)BaseObject.Radius, 5) / 50; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 46a81a9480..01f2fb8dc8 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -42,9 +42,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2); double angleBonus = 1.0; + if (osuCurrent.Angle != null && osuCurrent.Angle.Value < angle_bonus_begin) { angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - osuCurrent.Angle.Value)), 2) / 3.57; + if (osuCurrent.Angle.Value < pi_over_2) { angleBonus = 1.28; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 989a53db1f..55de626d7d 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -62,6 +62,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders case PlacementState.Initial: HitObject.Position = e.MousePosition; return true; + case PlacementState.Body: cursor = e.MousePosition - HitObject.Position; return true; @@ -77,6 +78,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders case PlacementState.Initial: beginCurve(); break; + case PlacementState.Body: switch (e.Button) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs index 2e93815ef0..3e53cd7087 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs @@ -33,6 +33,7 @@ namespace osu.Game.Rulesets.Osu.Mods { case DrawableSpinner _: continue; + default: drawable.ApplyCustomUpdateState += ApplyCustomState; break; @@ -51,6 +52,7 @@ namespace osu.Game.Rulesets.Osu.Mods case DrawableSliderTail _: // special cases we should *not* be scaling. break; + case DrawableSlider _: case DrawableHitCircle _: { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs index 8f9d487d49..7569626230 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs @@ -67,6 +67,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections return; OsuHitObject prevHitObject = null; + foreach (var currHitObject in hitObjects) { if (prevHitObject != null && !currHitObject.NewCombo && !(prevHitObject is Spinner) && !(currHitObject is Spinner)) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index decd0ce073..fef0bfdc2c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -124,6 +124,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } var result = HitObject.HitWindows.ResultFor(timeOffset); + if (result == HitResult.None) { Shake(Math.Abs(timeOffset) - HitObject.HitWindows.HalfWindowFor(HitResult.Miss)); @@ -158,11 +159,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early. LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.HalfWindowFor(HitResult.Miss); break; + case ArmedState.Miss: ApproachCircle.FadeOut(50); this.FadeOut(100); Expire(); break; + case ArmedState.Hit: ApproachCircle.FadeOut(50); diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 03dbf7ac63..1a3e244fa6 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -191,6 +191,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Shader.GetUniform("g_FadeClock").UpdateValue(ref Time); int updateStart = -1, updateEnd = 0; + for (int i = 0; i < Parts.Length; ++i) { if (Parts[i].WasUpdated) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 3e0e2624bf..f8672037cd 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -120,6 +120,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps List> allSamples = curveData != null ? curveData.NodeSamples : new List>(new[] { samples }); int i = 0; + for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing) { List currentSamples = allSamples[i]; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index d3837946c9..4c8d5d5204 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -98,6 +98,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables circlePiece?.FlashBox.FinishTransforms(); var offset = !AllJudged ? 0 : Time.Current - HitObject.StartTime; + using (BeginDelayedSequence(HitObject.StartTime - Time.Current + offset, true)) { switch (State.Value) @@ -108,15 +109,18 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables UnproxyContent(); this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire(); break; + case ArmedState.Miss: this.FadeOut(100) .Expire(); break; + case ArmedState.Hit: // If we're far enough away from the left stage, we should bring outselves in front of it ProxyContent(); var flash = circlePiece?.FlashBox; + if (flash != null) { flash.FadeTo(0.9f); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 8dfe89eea7..119940536e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -111,6 +111,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables MainPiece.KiaiMode = HitObject.Kiai; var strongObject = HitObject.NestedHitObjects.OfType().FirstOrDefault(); + if (strongObject != null) { var strongHit = CreateStrongHit(strongObject); diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 6f3bdca6fb..1d25735fe3 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -70,6 +70,7 @@ namespace osu.Game.Rulesets.Taiko.Objects return; bool first = true; + for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing) { AddNested(new DrumRollTick diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index 01ba53e07b..422ba748e3 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -52,6 +52,7 @@ namespace osu.Game.Rulesets.Taiko.Replays int count = 0; int req = swell.RequiredHits; double hitRate = Math.Min(swell_hit_speed, swell.Duration / req); + for (double j = h.StartTime; j < endTime; j += hitRate) { TaikoAction action; @@ -62,12 +63,15 @@ namespace osu.Game.Rulesets.Taiko.Replays case 0: action = TaikoAction.LeftCentre; break; + case 1: action = TaikoAction.LeftRim; break; + case 2: action = TaikoAction.RightCentre; break; + case 3: action = TaikoAction.RightRim; break; diff --git a/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapCarousel.cs index 1500605896..1ffc164026 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapCarousel.cs @@ -508,6 +508,7 @@ namespace osu.Game.Tests.Visual.SongSelect }, Beatmaps = new List(), }; + for (int b = 1; b < 101; b++) { toReturn.Beatmaps.Add(new BeatmapInfo diff --git a/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapScoresContainer.cs b/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapScoresContainer.cs index 8de6cc2a88..5bddf4222a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapScoresContainer.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapScoresContainer.cs @@ -261,6 +261,7 @@ namespace osu.Game.Tests.Visual.SongSelect Accuracy = 0.8765, }, }; + foreach (var s in anotherScores) { s.Statistics.Add(HitResult.Great, RNG.Next(2000)); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 9caa64ec96..95ec7d55c8 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -117,6 +117,7 @@ namespace osu.Game.Beatmaps if (beatmapSet.OnlineBeatmapSetID != null) { var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == beatmapSet.OnlineBeatmapSetID); + if (existingOnlineId != null) { Delete(existingOnlineId); @@ -325,6 +326,7 @@ namespace osu.Game.Beatmaps { // let's make sure there are actually .osu files to import. string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu")); + if (string.IsNullOrEmpty(mapName)) { Logger.Log($"No beatmap files found in the beatmap archive ({reader.Name}).", LoggingTarget.Database); diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index a2e43e5a97..0bdab22dd2 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -101,6 +101,7 @@ namespace osu.Game.Beatmaps protected override Storyboard GetStoryboard() { Storyboard storyboard; + try { using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) @@ -131,6 +132,7 @@ namespace osu.Game.Beatmaps protected override Skin GetSkin() { Skin skin; + try { skin = new LegacyBeatmapSkin(BeatmapInfo, store, audioManager); diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs index a895ba3d63..953e50eadc 100644 --- a/osu.Game/Beatmaps/Formats/Decoder.cs +++ b/osu.Game/Beatmaps/Formats/Decoder.cs @@ -49,6 +49,7 @@ namespace osu.Game.Beatmaps.Formats throw new IOException(@"Unknown decoder type"); string line; + do { line = stream.ReadLine()?.Trim(); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index a27126ad9c..b489b5e6d9 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -70,21 +70,27 @@ namespace osu.Game.Beatmaps.Formats case Section.General: handleGeneral(strippedLine); return; + case Section.Editor: handleEditor(strippedLine); return; + case Section.Metadata: handleMetadata(line); return; + case Section.Difficulty: handleDifficulty(strippedLine); return; + case Section.Events: handleEvent(strippedLine); return; + case Section.TimingPoints: handleTimingPoint(strippedLine); return; + case Section.HitObjects: handleHitObject(strippedLine); return; @@ -98,29 +104,37 @@ namespace osu.Game.Beatmaps.Formats var pair = SplitKeyVal(line); var metadata = beatmap.BeatmapInfo.Metadata; + switch (pair.Key) { case @"AudioFilename": metadata.AudioFile = FileSafety.PathStandardise(pair.Value); break; + case @"AudioLeadIn": beatmap.BeatmapInfo.AudioLeadIn = Parsing.ParseInt(pair.Value); break; + case @"PreviewTime": metadata.PreviewTime = getOffsetTime(Parsing.ParseInt(pair.Value)); break; + case @"Countdown": beatmap.BeatmapInfo.Countdown = Parsing.ParseInt(pair.Value) == 1; break; + case @"SampleSet": defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value); break; + case @"SampleVolume": defaultSampleVolume = Parsing.ParseInt(pair.Value); break; + case @"StackLeniency": beatmap.BeatmapInfo.StackLeniency = Parsing.ParseFloat(pair.Value); break; + case @"Mode": beatmap.BeatmapInfo.RulesetID = Parsing.ParseInt(pair.Value); @@ -129,24 +143,30 @@ namespace osu.Game.Beatmaps.Formats case 0: parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(getOffsetTime(), FormatVersion); break; + case 1: parser = new Rulesets.Objects.Legacy.Taiko.ConvertHitObjectParser(getOffsetTime(), FormatVersion); break; + case 2: parser = new Rulesets.Objects.Legacy.Catch.ConvertHitObjectParser(getOffsetTime(), FormatVersion); break; + case 3: parser = new Rulesets.Objects.Legacy.Mania.ConvertHitObjectParser(getOffsetTime(), FormatVersion); break; } break; + case @"LetterboxInBreaks": beatmap.BeatmapInfo.LetterboxInBreaks = Parsing.ParseInt(pair.Value) == 1; break; + case @"SpecialStyle": beatmap.BeatmapInfo.SpecialStyle = Parsing.ParseInt(pair.Value) == 1; break; + case @"WidescreenStoryboard": beatmap.BeatmapInfo.WidescreenStoryboard = Parsing.ParseInt(pair.Value) == 1; break; @@ -162,15 +182,19 @@ namespace osu.Game.Beatmaps.Formats case @"Bookmarks": beatmap.BeatmapInfo.StoredBookmarks = pair.Value; break; + case @"DistanceSpacing": beatmap.BeatmapInfo.DistanceSpacing = Math.Max(0, Parsing.ParseDouble(pair.Value)); break; + case @"BeatDivisor": beatmap.BeatmapInfo.BeatDivisor = Parsing.ParseInt(pair.Value); break; + case @"GridSize": beatmap.BeatmapInfo.GridSize = Parsing.ParseInt(pair.Value); break; + case @"TimelineZoom": beatmap.BeatmapInfo.TimelineZoom = Math.Max(0, Parsing.ParseDouble(pair.Value)); break; @@ -182,35 +206,45 @@ namespace osu.Game.Beatmaps.Formats var pair = SplitKeyVal(line); var metadata = beatmap.BeatmapInfo.Metadata; + switch (pair.Key) { case @"Title": metadata.Title = pair.Value; break; + case @"TitleUnicode": metadata.TitleUnicode = pair.Value; break; + case @"Artist": metadata.Artist = pair.Value; break; + case @"ArtistUnicode": metadata.ArtistUnicode = pair.Value; break; + case @"Creator": metadata.AuthorString = pair.Value; break; + case @"Version": beatmap.BeatmapInfo.Version = pair.Value; break; + case @"Source": beatmap.BeatmapInfo.Metadata.Source = pair.Value; break; + case @"Tags": beatmap.BeatmapInfo.Metadata.Tags = pair.Value; break; + case @"BeatmapID": beatmap.BeatmapInfo.OnlineBeatmapID = Parsing.ParseInt(pair.Value); break; + case @"BeatmapSetID": beatmap.BeatmapInfo.BeatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = Parsing.ParseInt(pair.Value) }; break; @@ -222,23 +256,29 @@ namespace osu.Game.Beatmaps.Formats var pair = SplitKeyVal(line); var difficulty = beatmap.BeatmapInfo.BaseDifficulty; + switch (pair.Key) { case @"HPDrainRate": difficulty.DrainRate = Parsing.ParseFloat(pair.Value); break; + case @"CircleSize": difficulty.CircleSize = Parsing.ParseFloat(pair.Value); break; + case @"OverallDifficulty": difficulty.OverallDifficulty = Parsing.ParseFloat(pair.Value); break; + case @"ApproachRate": difficulty.ApproachRate = Parsing.ParseFloat(pair.Value); break; + case @"SliderMultiplier": difficulty.SliderMultiplier = Parsing.ParseDouble(pair.Value); break; + case @"SliderTickRate": difficulty.SliderTickRate = Parsing.ParseDouble(pair.Value); break; @@ -259,6 +299,7 @@ namespace osu.Game.Beatmaps.Formats string filename = split[2].Trim('"'); beatmap.BeatmapInfo.Metadata.BackgroundFile = FileSafety.PathStandardise(filename); break; + case EventType.Break: double start = getOffsetTime(Parsing.ParseDouble(split[1])); @@ -308,6 +349,7 @@ namespace osu.Game.Beatmaps.Formats bool kiaiMode = false; bool omitFirstBarSignature = false; + if (split.Length >= 8) { EffectFlags effectFlags = (EffectFlags)Parsing.ParseInt(split[7]); diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 040f582e3b..cd1d8a6218 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -26,6 +26,7 @@ namespace osu.Game.Beatmaps.Formats Section section = Section.None; string line; + while ((line = stream.ReadLine()) != null) { if (ShouldSkipLine(line)) diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 0f83edf034..f6e2bf6966 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -54,6 +54,7 @@ namespace osu.Game.Beatmaps.Formats case Section.Events: handleEvents(line); return; + case Section.Variables: handleVariables(line); return; @@ -65,6 +66,7 @@ namespace osu.Game.Beatmaps.Formats private void handleEvents(string line) { var depth = 0; + while (line.StartsWith(" ", StringComparison.Ordinal) || line.StartsWith("_", StringComparison.Ordinal)) { ++depth; @@ -94,8 +96,9 @@ namespace osu.Game.Beatmaps.Formats var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); storyboard.GetLayer(layer).Add(storyboardSprite); - } break; + } + case EventType.Animation: { var layer = parseLayer(split[1]); @@ -108,8 +111,9 @@ namespace osu.Game.Beatmaps.Formats var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); storyboard.GetLayer(layer).Add(storyboardSprite); - } break; + } + case EventType.Sample: { var time = double.Parse(split[1], CultureInfo.InvariantCulture); @@ -117,8 +121,8 @@ namespace osu.Game.Beatmaps.Formats var path = cleanFilename(split[3]); var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); - } break; + } } } else @@ -127,6 +131,7 @@ namespace osu.Game.Beatmaps.Formats timelineGroup = storyboardSprite?.TimelineGroup; var commandType = split[0]; + switch (commandType) { case "T": @@ -138,6 +143,7 @@ namespace osu.Game.Beatmaps.Formats timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber); } break; + case "L": { var startTime = double.Parse(split[1], CultureInfo.InvariantCulture); @@ -145,6 +151,7 @@ namespace osu.Game.Beatmaps.Formats timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount); } break; + default: { if (string.IsNullOrEmpty(split[3])) @@ -163,6 +170,7 @@ namespace osu.Game.Beatmaps.Formats timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue); } break; + case "S": { var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); @@ -170,6 +178,7 @@ namespace osu.Game.Beatmaps.Formats timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue)); } break; + case "V": { var startX = float.Parse(split[4], CultureInfo.InvariantCulture); @@ -179,6 +188,7 @@ namespace osu.Game.Beatmaps.Formats timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY)); } break; + case "R": { var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); @@ -186,6 +196,7 @@ namespace osu.Game.Beatmaps.Formats timelineGroup?.Rotation.Add(easing, startTime, endTime, MathHelper.RadiansToDegrees(startValue), MathHelper.RadiansToDegrees(endValue)); } break; + case "M": { var startX = float.Parse(split[4], CultureInfo.InvariantCulture); @@ -196,6 +207,7 @@ namespace osu.Game.Beatmaps.Formats timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY); } break; + case "MX": { var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); @@ -203,6 +215,7 @@ namespace osu.Game.Beatmaps.Formats timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue); } break; + case "MY": { var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); @@ -210,6 +223,7 @@ namespace osu.Game.Beatmaps.Formats timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue); } break; + case "C": { var startRed = float.Parse(split[4], CultureInfo.InvariantCulture); @@ -223,23 +237,28 @@ namespace osu.Game.Beatmaps.Formats new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1)); } break; + case "P": { var type = split[4]; + switch (type) { case "A": timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); break; + case "H": timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); break; + case "V": timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); break; } } break; + default: throw new InvalidDataException($@"Unknown command type: {commandType}"); } @@ -254,26 +273,36 @@ namespace osu.Game.Beatmaps.Formats private Anchor parseOrigin(string value) { var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value); + switch (origin) { case LegacyOrigins.TopLeft: return Anchor.TopLeft; + case LegacyOrigins.TopCentre: return Anchor.TopCentre; + case LegacyOrigins.TopRight: return Anchor.TopRight; + case LegacyOrigins.CentreLeft: return Anchor.CentreLeft; + case LegacyOrigins.Centre: return Anchor.Centre; + case LegacyOrigins.CentreRight: return Anchor.CentreRight; + case LegacyOrigins.BottomLeft: return Anchor.BottomLeft; + case LegacyOrigins.BottomCentre: return Anchor.BottomCentre; + case LegacyOrigins.BottomRight: return Anchor.BottomRight; + default: return Anchor.TopLeft; } diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs index f547a7d3e1..8f1780cab5 100644 --- a/osu.Game/Configuration/DatabasedConfigManager.cs +++ b/osu.Game/Configuration/DatabasedConfigManager.cs @@ -44,6 +44,7 @@ namespace osu.Game.Configuration base.AddBindable(lookup, bindable); var setting = databasedSettings.Find(s => (int)s.Key == (int)(object)lookup); + if (setting != null) { bindable.Parse(setting.Value); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 3805921ac2..1eb199327e 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -84,6 +84,7 @@ namespace osu.Game.Database private void flushEvents(bool perform) { Action[] events; + lock (queuedEvents) { events = queuedEvents.ToArray(); @@ -147,6 +148,7 @@ namespace osu.Game.Database List imported = new List(); int current = 0; + foreach (string path in paths) { if (notification.State == ProgressNotificationState.Cancelled) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 17efe2c839..38f2a53586 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -60,6 +60,7 @@ namespace osu.Game.Database this.connectionString = connectionString; var connection = Database.GetDbConnection(); + try { connection.Open(); @@ -170,9 +171,11 @@ namespace osu.Game.Database default: frameworkLogLevel = Framework.Logging.LogLevel.Debug; break; + case LogLevel.Warning: frameworkLogLevel = Framework.Logging.LogLevel.Important; break; + case LogLevel.Error: case LogLevel.Critical: frameworkLogLevel = Framework.Logging.LogLevel.Error; diff --git a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs index c1811f37d5..3bb9e1f091 100644 --- a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs +++ b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs @@ -34,6 +34,7 @@ namespace osu.Game.Graphics.Containers protected override void Update() { base.Update(); + if (InternalChildren.Count > 0 && InternalChild.DrawSize.X > 0) { // We're modifying scale here for a few reasons diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 204c83aac9..23e2a66107 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -51,6 +51,7 @@ namespace osu.Game.Graphics.Containers } int previousLinkEnd = 0; + foreach (var link in links) { AddText(text.Substring(previousLinkEnd, link.Index - previousLinkEnd)); @@ -90,10 +91,12 @@ namespace osu.Game.Graphics.Containers if (linkArgument != null && int.TryParse(linkArgument.Contains('?') ? linkArgument.Split('?')[0] : linkArgument, out int beatmapId)) game?.ShowBeatmap(beatmapId); break; + case LinkAction.OpenBeatmapSet: if (int.TryParse(linkArgument, out int setId)) game?.ShowBeatmapSet(setId); break; + case LinkAction.OpenChannel: try { @@ -105,18 +108,22 @@ namespace osu.Game.Graphics.Containers } break; + case LinkAction.OpenEditorTimestamp: case LinkAction.JoinMultiplayerMatch: case LinkAction.Spectate: showNotImplementedError?.Invoke(); break; + case LinkAction.External: game?.OpenUrlExternally(url); break; + case LinkAction.OpenUserProfile: if (long.TryParse(linkArgument, out long userId)) game?.ShowUser(userId); break; + default: throw new NotImplementedException($"This {nameof(LinkAction)} ({linkType.ToString()}) is missing an associated action."); } diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 6bbab4766d..1f2ee53fe9 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -150,6 +150,7 @@ namespace osu.Game.Graphics.Containers float headerH = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0); float footerH = Footer?.LayoutSize.Y ?? 0; + if (headerH != headerHeight || footerH != footerHeight) { headerHeight = headerH; @@ -181,6 +182,7 @@ namespace osu.Game.Graphics.Containers foreach (var section in Children) { float diff = Math.Abs(scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset); + if (diff < minDiff) { minDiff = diff; diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index 059beeca4d..092a23e787 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -45,10 +45,12 @@ namespace osu.Game.Graphics.Cursor { var position = e.MousePosition; var distance = Vector2Extensions.Distance(position, positionMouseDown); + // don't start rotating until we're moved a minimum distance away from the mouse down location, // else it can have an annoying effect. if (dragRotationState == DragRotationState.DragStarted && distance > 30) dragRotationState = DragRotationState.Rotating; + // don't rotate when distance is zero to avoid NaN if (dragRotationState == DragRotationState.Rotating && distance > 0) { diff --git a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs index 4e0ce4a3e1..fa79331274 100644 --- a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs @@ -37,6 +37,7 @@ namespace osu.Game.Graphics.Cursor if (value == text.Text) return; text.Text = value; + if (IsPresent) { AutoSizeDuration = 250; diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs index 3ae1033f5d..125c994c92 100644 --- a/osu.Game/Graphics/DrawableDate.cs +++ b/osu.Game/Graphics/DrawableDate.cs @@ -54,9 +54,11 @@ namespace osu.Game.Graphics var diffToNow = DateTimeOffset.Now.Subtract(Date); double timeUntilNextUpdate = 1000; + if (Math.Abs(diffToNow.TotalSeconds) > 120) { timeUntilNextUpdate *= 60; + if (Math.Abs(diffToNow.TotalMinutes) > 120) { timeUntilNextUpdate *= 60; diff --git a/osu.Game/Graphics/UserInterface/BarGraph.cs b/osu.Game/Graphics/UserInterface/BarGraph.cs index 58058c9d4c..953f3985f9 100644 --- a/osu.Game/Graphics/UserInterface/BarGraph.cs +++ b/osu.Game/Graphics/UserInterface/BarGraph.cs @@ -25,6 +25,7 @@ namespace osu.Game.Graphics.UserInterface { direction = value; base.Direction = direction.HasFlag(BarDirection.Horizontal) ? FillDirection.Vertical : FillDirection.Horizontal; + foreach (var bar in Children) { bar.Size = direction.HasFlag(BarDirection.Horizontal) ? new Vector2(1, 1.0f / Children.Count) : new Vector2(1.0f / Children.Count, 1); @@ -41,6 +42,7 @@ namespace osu.Game.Graphics.UserInterface set { List bars = Children.ToList(); + foreach (var bar in value.Select((length, index) => new { Value = length, Bar = bars.Count > index ? bars[index] : null })) { float length = MaxValue ?? value.Max(); diff --git a/osu.Game/Graphics/UserInterface/LineGraph.cs b/osu.Game/Graphics/UserInterface/LineGraph.cs index 74025b71ff..3882e7c1e5 100644 --- a/osu.Game/Graphics/UserInterface/LineGraph.cs +++ b/osu.Game/Graphics/UserInterface/LineGraph.cs @@ -86,6 +86,7 @@ namespace osu.Game.Graphics.UserInterface protected override void Update() { base.Update(); + if (!pathCached.IsValid) { applyPath(); diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index c558fd7c7b..c3c447ef83 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -203,6 +203,7 @@ namespace osu.Game.Graphics.UserInterface private int findPrecision(decimal d) { int precision = 0; + while (d != Math.Round(d)) { d *= 10; diff --git a/osu.Game/Graphics/UserInterface/StarCounter.cs b/osu.Game/Graphics/UserInterface/StarCounter.cs index ac6e393435..f7f282c1aa 100644 --- a/osu.Game/Graphics/UserInterface/StarCounter.cs +++ b/osu.Game/Graphics/UserInterface/StarCounter.cs @@ -100,6 +100,7 @@ namespace osu.Game.Graphics.UserInterface public void StopAnimation() { int i = 0; + foreach (var star in stars.Children) { star.ClearTransforms(true); @@ -120,6 +121,7 @@ namespace osu.Game.Graphics.UserInterface private void transformCount(float newValue) { int i = 0; + foreach (var star in stars.Children) { star.ClearTransforms(true); diff --git a/osu.Game/IO/Legacy/SerializationReader.cs b/osu.Game/IO/Legacy/SerializationReader.cs index 95ee5aea6b..7a84c11930 100644 --- a/osu.Game/IO/Legacy/SerializationReader.cs +++ b/osu.Game/IO/Legacy/SerializationReader.cs @@ -85,6 +85,7 @@ namespace osu.Game.IO.Legacy for (int i = 0; i < count; i++) { T obj = new T(); + try { obj.ReadFromStream(sr); @@ -129,44 +130,63 @@ namespace osu.Game.IO.Legacy public object ReadObject() { ObjType t = (ObjType)ReadByte(); + switch (t) { case ObjType.boolType: return ReadBoolean(); + case ObjType.byteType: return ReadByte(); + case ObjType.uint16Type: return ReadUInt16(); + case ObjType.uint32Type: return ReadUInt32(); + case ObjType.uint64Type: return ReadUInt64(); + case ObjType.sbyteType: return ReadSByte(); + case ObjType.int16Type: return ReadInt16(); + case ObjType.int32Type: return ReadInt32(); + case ObjType.int64Type: return ReadInt64(); + case ObjType.charType: return ReadChar(); + case ObjType.stringType: return base.ReadString(); + case ObjType.singleType: return ReadSingle(); + case ObjType.doubleType: return ReadDouble(); + case ObjType.decimalType: return ReadDecimal(); + case ObjType.dateTimeType: return ReadDateTime(); + case ObjType.byteArrayType: return ReadByteArray(); + case ObjType.charArrayType: return ReadCharArray(); + case ObjType.otherType: return DynamicDeserializer.Deserialize(BaseStream); + default: return null; } @@ -241,6 +261,7 @@ namespace osu.Game.IO.Legacy string toAssemblyName = assemblyName.Split(',')[0]; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (Assembly a in assemblies) { if (a.FullName.Split(',')[0] == toAssemblyName) diff --git a/osu.Game/IO/Legacy/SerializationWriter.cs b/osu.Game/IO/Legacy/SerializationWriter.cs index 695767c822..f30e4492af 100644 --- a/osu.Game/IO/Legacy/SerializationWriter.cs +++ b/osu.Game/IO/Legacy/SerializationWriter.cs @@ -111,6 +111,7 @@ namespace osu.Game.IO.Legacy else { Write(d.Count); + foreach (KeyValuePair kvp in d) { WriteObject(kvp.Key); diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index 13be4be0c6..6d244bff60 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -65,6 +65,7 @@ namespace osu.Game.IO.Serialization.Converters var lookupTable = new List(); var objects = new List(); + foreach (var item in list) { var type = item.GetType(); @@ -75,6 +76,7 @@ namespace osu.Game.IO.Serialization.Converters typeString += $", {assemblyName.Version}"; int typeId = lookupTable.IndexOf(typeString); + if (typeId == -1) { lookupTable.Add(typeString); diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index c5f6ef41c2..6d855765b1 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -113,6 +113,7 @@ namespace osu.Game.Online.API } break; + case APIState.Offline: case APIState.Connecting: //work to restore a connection... @@ -300,6 +301,7 @@ namespace osu.Game.Online.API case HttpStatusCode.Unauthorized: Logout(); return true; + case HttpStatusCode.RequestTimeout: failureCount++; log.Add($@"API failure count is now {failureCount}"); diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs index 8ee71ce9ac..b2bb48b3de 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs @@ -72,26 +72,33 @@ namespace osu.Game.Online.API.Requests.Responses foreach (var kvp in value) { HitResult newKey; + switch (kvp.Key) { case @"count_geki": CountGeki = kvp.Value; break; + case @"count_300": Count300 = kvp.Value; break; + case @"count_katu": CountKatu = kvp.Value; break; + case @"count_100": Count100 = kvp.Value; break; + case @"count_50": Count50 = kvp.Value; break; + case @"count_miss": CountMiss = kvp.Value; break; + default: continue; } diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index d35dc07368..e1fc65da6c 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -51,6 +51,7 @@ namespace osu.Game.Online.Chat private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null) { int captureOffset = 0; + foreach (Match m in regex.Matches(result.Text, startIndex)) { var index = m.Index - captureOffset; @@ -114,51 +115,63 @@ namespace osu.Game.Online.Chat case "b": case "beatmaps": return new LinkDetails(LinkAction.OpenBeatmap, args[3]); + case "s": case "beatmapsets": case "d": return new LinkDetails(LinkAction.OpenBeatmapSet, args[3]); + case "u": return new LinkDetails(LinkAction.OpenUserProfile, args[3]); } } return new LinkDetails(LinkAction.External, null); + case "osu": // every internal link also needs some kind of argument if (args.Length < 3) return new LinkDetails(LinkAction.External, null); LinkAction linkType; + switch (args[1]) { case "chan": linkType = LinkAction.OpenChannel; break; + case "edit": linkType = LinkAction.OpenEditorTimestamp; break; + case "b": linkType = LinkAction.OpenBeatmap; break; + case "s": case "dl": linkType = LinkAction.OpenBeatmapSet; break; + case "spectate": linkType = LinkAction.Spectate; break; + case "u": linkType = LinkAction.OpenUserProfile; break; + default: linkType = LinkAction.External; break; } return new LinkDetails(linkType, args[2]); + case "osump": return new LinkDetails(LinkAction.JoinMultiplayerMatch, args[1]); + default: return new LinkDetails(LinkAction.External, null); } diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index ac1666f8ed..3ce71cccba 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -74,6 +74,7 @@ namespace osu.Game.Online.Leaderboards scrollContainer.Add(scrollFlow); int i = 0; + foreach (var s in scrollFlow.Children) { using (s.BeginDelayedSequence(i++ * 50, true)) @@ -138,18 +139,23 @@ namespace osu.Game.Online.Leaderboards OnRetry = UpdateScores, }); break; + case PlaceholderState.Unavailable: replacePlaceholder(new MessagePlaceholder(@"Leaderboards are not available for this beatmap!")); break; + case PlaceholderState.NoScores: replacePlaceholder(new MessagePlaceholder(@"No records yet!")); break; + case PlaceholderState.NotLoggedIn: replacePlaceholder(new MessagePlaceholder(@"Please sign in to view online leaderboards!")); break; + case PlaceholderState.NotSupporter: replacePlaceholder(new MessagePlaceholder(@"Please invest in an osu!supporter tag to view this leaderboard!")); break; + default: replacePlaceholder(null); break; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e470d554c9..5ac5842b22 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -271,6 +271,7 @@ namespace osu.Game { var databasedScore = ScoreManager.GetScore(score); var databasedScoreInfo = databasedScore.ScoreInfo; + if (databasedScore.Replay == null) { Logger.Log("The loaded score has no replay data.", LoggingTarget.Information); @@ -278,6 +279,7 @@ namespace osu.Game } var databasedBeatmap = BeatmapManager.QueryBeatmap(b => b.ID == databasedScoreInfo.Beatmap.ID); + if (databasedBeatmap == null) { Logger.Log("Tried to load a score for a beatmap we don't have!", LoggingTarget.Information); @@ -661,9 +663,11 @@ namespace osu.Game case GlobalAction.ToggleChat: chatOverlay.ToggleVisibility(); return true; + case GlobalAction.ToggleSocial: social.ToggleVisibility(); return true; + case GlobalAction.ResetInputSettings: var sensitivity = frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity); @@ -674,15 +678,19 @@ namespace osu.Game frameworkConfig.Set(FrameworkSetting.IgnoredInputHandlers, string.Empty); frameworkConfig.GetBindable(FrameworkSetting.ConfineMouseMode).SetDefault(); return true; + case GlobalAction.ToggleToolbar: Toolbar.ToggleVisibility(); return true; + case GlobalAction.ToggleSettings: settings.ToggleVisibility(); return true; + case GlobalAction.ToggleDirect: direct.ToggleVisibility(); return true; + case GlobalAction.ToggleGameplayMouseButtons: LocalConfig.Set(OsuSetting.MouseDisableButtons, !LocalConfig.Get(OsuSetting.MouseDisableButtons)); return true; @@ -758,6 +766,7 @@ namespace osu.Game case Intro intro: introScreen = intro; break; + case MainMenu menu: menuScreen = menu; break; diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index 13d8df098f..e136fc1403 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -209,6 +209,7 @@ namespace osu.Game.Overlays.AccountCreation private bool focusNextTextbox() { var nextTextbox = nextUnfilledTextbox(); + if (nextTextbox != null) { Schedule(() => GetContainingInputManager().ChangeFocus(nextTextbox)); diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index e3df81e455..aec78b962f 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -99,6 +99,7 @@ namespace osu.Game.Overlays.Chat private void pendingMessageResolved(Message existing, Message updated) { var found = ChatLineFlow.Children.LastOrDefault(c => c.Message == existing); + if (found != null) { Trace.Assert(updated.Id.HasValue, "An updated message was returned with no ID."); diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 77f88ab4e7..221fd35576 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -205,6 +205,7 @@ namespace osu.Game.Overlays Scheduler.Add(() => channelTabControl.Current.Value = e.NewValue); var loaded = loadedChannels.Find(d => d.Channel == e.NewValue); + if (loaded == null) { currentChannelContainer.FadeOut(500, Easing.OutQuint); @@ -288,6 +289,7 @@ namespace osu.Game.Overlays case Key.Number9: selectTab((int)e.Key - (int)Key.Number1); return true; + case Key.Number0: selectTab(9); return true; diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index ede2f34574..1ab5d76555 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -72,6 +72,7 @@ namespace osu.Game.Overlays.Dialog set { buttonsContainer.ChildrenEnumerable = value; + foreach (PopupDialogButton b in value) { var action = b.Action; @@ -222,6 +223,7 @@ namespace osu.Game.Overlays.Dialog // press button at number if 1-9 on number row or keypad are pressed var k = e.Key; + if (k >= Key.Number1 && k <= Key.Number9) { pressButtonAtIndex(k - Key.Number1); diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 34edbbcc8b..40c4c90fca 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -58,6 +58,7 @@ namespace osu.Game.Overlays var artists = new List(); var songs = new List(); var tags = new List(); + foreach (var s in beatmapSets) { artists.Add(s.Metadata.Artist); @@ -210,6 +211,7 @@ namespace osu.Game.Overlays Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }; + default: return new DirectListPanel(b); } diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 8313dac50a..ee7a65042b 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -117,6 +117,7 @@ namespace osu.Game.Overlays.KeyBinding public void RestoreDefaults() { int i = 0; + foreach (var d in Defaults) { var button = buttons[i++]; diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 23b75caedc..fa1ee500a8 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -81,6 +81,7 @@ namespace osu.Game.Overlays.Mods backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing); backgroundIcon.Icon = modAfter.Icon; + using (BeginDelayedSequence(mod_switch_duration, true)) { foregroundIcon @@ -139,6 +140,7 @@ namespace osu.Game.Overlays.Mods } createIcons(); + if (Mods.Length > 0) { displayMod(Mods[0]); @@ -168,6 +170,7 @@ namespace osu.Game.Overlays.Mods case MouseButton.Left: SelectNext(1); break; + case MouseButton.Right: SelectNext(-1); break; @@ -219,6 +222,7 @@ namespace osu.Game.Overlays.Mods private void createIcons() { iconsContainer.Clear(); + if (Mods.Length > 1) { iconsContainer.AddRange(new[] diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index a118357f21..50400e254f 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -77,6 +77,7 @@ namespace osu.Game.Overlays.Mods public void DeselectTypes(IEnumerable modTypes, bool immediate = false) { int delay = 0; + foreach (var button in buttons) { Mod selected = button.SelectedMod; diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index 310c6c919f..89d166b788 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -190,6 +190,7 @@ namespace osu.Game.Overlays.Music // the item positions as they are being transformed float heightAccumulator = 0; int dstIndex = 0; + for (; dstIndex < items.Count; dstIndex++) { // Using BoundingBox here takes care of scale, paddings, etc... diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 8cbea63fe3..c66d0694ae 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -81,6 +81,7 @@ namespace osu.Game.Overlays.Music filter.Search.OnCommit = (sender, newText) => { BeatmapInfo toSelect = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault(); + if (toSelect != null) { beatmap.Value = beatmaps.GetWorkingBeatmap(toSelect); diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index ce2137346f..75073a14f1 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -296,6 +296,7 @@ namespace osu.Game.Overlays queuedDirection = TransformDirection.Prev; var playable = beatmapSets.TakeWhile(i => i.ID != current.BeatmapSetInfo.ID).LastOrDefault() ?? beatmapSets.LastOrDefault(); + if (playable != null) { beatmap.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value); @@ -309,6 +310,7 @@ namespace osu.Game.Overlays queuedDirection = TransformDirection.Next; var playable = beatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).Skip(1).FirstOrDefault() ?? beatmapSets.FirstOrDefault(); + if (playable != null) { beatmap.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value); @@ -399,6 +401,7 @@ namespace osu.Game.Overlays newBackground.MoveToX(0, 500, Easing.OutCubic); background.MoveToX(-400, 500, Easing.OutCubic); break; + case TransformDirection.Prev: newBackground.Position = new Vector2(-400, 0); newBackground.MoveToX(0, 500, Easing.OutCubic); diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs index ff4d7a10dc..769eff53c2 100644 --- a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs @@ -106,6 +106,7 @@ namespace osu.Game.Overlays.Profile.Header visibleBadge = 0; badgeFlowContainer.Clear(); + for (var index = 0; index < badges.Length; index++) { int displayIndex = index; diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 28877c21f0..b8a07dfad2 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -408,6 +408,7 @@ namespace osu.Game.Overlays.Profile infoTextLeft.AddLink("forum post".ToQuantity(user.PostCount), url: $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: boldItalic); string websiteWithoutProtcol = user.Website; + if (!string.IsNullOrEmpty(websiteWithoutProtcol)) { int protocolIndex = websiteWithoutProtcol.IndexOf("//", StringComparison.Ordinal); @@ -468,14 +469,11 @@ namespace osu.Game.Overlays.Profile if (string.IsNullOrEmpty(str)) return; infoTextRight.AddIcon(icon); + if (url != null) - { infoTextRight.AddLink(" " + str, url); - } else - { infoTextRight.AddText(" " + str); - } infoTextRight.NewLine(); } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 02e9d48f40..ec35040a42 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -64,6 +64,7 @@ namespace osu.Game.Overlays.Settings { bindable = value; controlWithCurrent?.Current.BindTo(bindable); + if (ShowsDefaultIndicator) { restoreDefaultButton.Bindable = bindable.GetBoundCopy(); diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs index c7736d6047..a94f76e7af 100644 --- a/osu.Game/Overlays/Settings/SidebarButton.cs +++ b/osu.Game/Overlays/Settings/SidebarButton.cs @@ -46,6 +46,7 @@ namespace osu.Game.Overlays.Settings set { selected = value; + if (selected) { selectionIndicator.FadeIn(50); diff --git a/osu.Game/Overlays/SocialOverlay.cs b/osu.Game/Overlays/SocialOverlay.cs index daf3d1c576..e6d0c2fe40 100644 --- a/osu.Game/Overlays/SocialOverlay.cs +++ b/osu.Game/Overlays/SocialOverlay.cs @@ -111,6 +111,7 @@ namespace osu.Game.Overlays ChildrenEnumerable = Users.Select(u => { SocialPanel panel; + switch (displayStyle) { case PanelDisplayStyle.Grid: @@ -120,6 +121,7 @@ namespace osu.Game.Overlays Origin = Anchor.TopCentre }; break; + default: panel = new SocialListPanel(u); break; @@ -167,6 +169,7 @@ namespace osu.Game.Overlays friendRequest.Success += updateUsers; api.Queue(getUsersRequest = friendRequest); break; + default: var userRequest = new GetUsersRequest(); // TODO filter arguments! userRequest.Success += response => updateUsers(response.Select(r => r.User)); @@ -200,6 +203,7 @@ namespace osu.Game.Overlays case APIState.Online: Scheduler.AddOnce(updateSearch); break; + default: Users = null; clearPanels(); diff --git a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs index ca86ce7aa7..b2ae273e31 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs @@ -21,6 +21,7 @@ namespace osu.Game.Overlays.Toolbar set { stateContainer = value; + if (stateContainer != null) { Action = stateContainer.ToggleVisibility; @@ -55,6 +56,7 @@ namespace osu.Game.Overlays.Toolbar case Visibility.Hidden: stateBackground.FadeOut(200); break; + case Visibility.Visible: stateBackground.FadeIn(200); break; diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index d01eab4dab..bbe1b34a48 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -71,6 +71,7 @@ namespace osu.Game.Overlays.Toolbar private void load(RulesetStore rulesets, Bindable parentRuleset) { this.rulesets = rulesets; + foreach (var r in rulesets.AvailableRulesets) { modeButtons.Add(new ToolbarRulesetButton diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 48ce055975..f856592f2e 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -176,6 +176,7 @@ namespace osu.Game.Overlays foreach (string id in user.ProfileOrder) { var sec = sections.FirstOrDefault(s => s.Identifier == id); + if (sec != null) { sec.User.Value = user; diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs index 85b6c91a07..8f892f2be1 100644 --- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs @@ -71,12 +71,14 @@ namespace osu.Game.Rulesets.Edit if (state == value) return; state = value; + switch (state) { case SelectionState.Selected: Show(); Selected?.Invoke(this); break; + case SelectionState.NotSelected: Hide(); Deselected?.Invoke(this); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index a7cfbd3300..edbf9079af 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -97,6 +97,7 @@ namespace osu.Game.Rulesets.Objects.Drawables private void load() { var judgement = HitObject.CreateJudgement(); + if (judgement != null) { Result = CreateResult(judgement); @@ -211,9 +212,11 @@ namespace osu.Game.Rulesets.Objects.Drawables { case HitResult.None: break; + case HitResult.Miss: State.Value = ArmedState.Miss; break; + default: State.Value = ArmedState.Hit; break; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 8d6bb8bd3f..c14f3b6a42 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -84,6 +84,7 @@ namespace osu.Game.Rulesets.Objects.Legacy var points = new Vector2[pointCount]; int pointIndex = 1; + foreach (string t in pointSplit) { if (t.Length == 1) @@ -93,12 +94,15 @@ namespace osu.Game.Rulesets.Objects.Legacy case @"C": pathType = PathType.Catmull; break; + case @"B": pathType = PathType.Bezier; break; + case @"L": pathType = PathType.Linear; break; + case @"P": pathType = PathType.PerfectCurve; break; @@ -143,6 +147,7 @@ namespace osu.Game.Rulesets.Objects.Legacy if (split.Length > 9 && split[9].Length > 0) { string[] sets = split[9].Split('|'); + for (int i = 0; i < nodes; i++) { if (i >= sets.Length) @@ -162,6 +167,7 @@ namespace osu.Game.Rulesets.Objects.Legacy if (split.Length > 8 && split[8].Length > 0) { string[] adds = split[8].Split('|'); + for (int i = 0; i < nodes; i++) { if (i >= adds.Length) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 1e9767a54f..5515d4a41a 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -95,6 +95,7 @@ namespace osu.Game.Rulesets.Objects path.Clear(); int i = 0; + for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) { } @@ -142,6 +143,7 @@ namespace osu.Game.Rulesets.Objects { case PathType.Linear: return PathApproximator.ApproximateLinear(subControlPoints); + case PathType.PerfectCurve: //we can only use CircularArc iff we have exactly three control points and no dissection. if (ControlPoints.Length != 3 || subControlPoints.Length != 3) @@ -155,6 +157,7 @@ namespace osu.Game.Rulesets.Objects break; return subpath; + case PathType.Catmull: return PathApproximator.ApproximateCatmull(subControlPoints); } diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index bbb587cb3f..0e74caf8ba 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -124,6 +124,7 @@ namespace osu.Game.Rulesets.UI onScreenDisplay = dependencies.Get(); Config = dependencies.Get().GetConfigFor(Ruleset); + if (Config != null) { dependencies.Cache(Config); diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 6df418753c..b6c2d016d2 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -76,6 +76,7 @@ namespace osu.Game.Screens.Backgrounds private void switchBackground(BeatmapBackground b) { float newDepth = 0; + if (Background != null) { newDepth = Background.Depth + 1; diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index 9ccf974244..5699ef0a84 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -100,6 +100,7 @@ namespace osu.Game.Screens.Edit.Compose }; var ruleset = Beatmap.Value.BeatmapInfo.Ruleset?.CreateInstance(); + if (ruleset == null) { Logger.Log("Beatmap doesn't have a ruleset assigned."); @@ -108,6 +109,7 @@ namespace osu.Game.Screens.Edit.Compose } composer = ruleset.CreateHitObjectComposer(); + if (composer == null) { Logger.Log($"Ruleset {ruleset.Description} doesn't support hitobject composition."); diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 0ba1e74aca..3ae26c4ea4 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -70,6 +70,7 @@ namespace osu.Game.Screens.Edit PlaybackControl playback; var fileMenuItems = new List(); + if (RuntimeInfo.IsDesktop) { fileMenuItems.Add(new EditorMenuItem("Export", MenuItemType.Standard, exportBeatmap)); @@ -173,6 +174,7 @@ namespace osu.Game.Screens.Edit case Key.Left: seek(e, -1); return true; + case Key.Right: seek(e, 1); return true; @@ -218,6 +220,7 @@ namespace osu.Game.Screens.Edit public override bool OnExiting(IScreen next) { Background.FadeColour(Color4.White, 500); + if (Beatmap.Value.Track != null) { Beatmap.Value.Track.Tempo.Value = 1; @@ -238,9 +241,11 @@ namespace osu.Game.Screens.Edit case EditorScreenMode.Compose: currentScreen = new ComposeScreen(); break; + case EditorScreenMode.Design: currentScreen = new DesignScreen(); break; + default: currentScreen = new EditorScreen(); break; diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index 8f65366650..24fb561f04 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -83,6 +83,7 @@ namespace osu.Game.Screens.Edit if (amount <= 0) throw new ArgumentException("Value should be greater than zero", nameof(amount)); var timingPoint = ControlPointInfo.TimingPointAt(CurrentTime); + if (direction < 0 && timingPoint.Time == CurrentTime) { // When going backwards and we're at the boundary of two timing points, we compute the seek distance with the timing point which we are seeking into diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index bcd24fd83e..61b93c9486 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -156,9 +156,11 @@ namespace osu.Game.Screens.Menu { case GlobalAction.Back: return goBack(); + case GlobalAction.Select: logo?.Click(); return true; + default: return false; } @@ -174,9 +176,11 @@ namespace osu.Game.Screens.Menu State = ButtonSystemState.Initial; sampleBack?.Play(); return true; + case ButtonSystemState.Play: backButton.Click(); return true; + default: return false; } @@ -188,12 +192,15 @@ namespace osu.Game.Screens.Menu { default: return true; + case ButtonSystemState.Initial: State = ButtonSystemState.TopLevel; return true; + case ButtonSystemState.TopLevel: buttonsTopLevel.First().Click(); return false; + case ButtonSystemState.Play: buttonsPlay.First().Click(); return false; @@ -259,12 +266,14 @@ namespace osu.Game.Screens.Menu logo.ScaleTo(1, 800, Easing.OutExpo); }, buttonArea.Alpha * 150); break; + case ButtonSystemState.TopLevel: case ButtonSystemState.Play: switch (lastState) { case ButtonSystemState.TopLevel: // coming from toplevel to play break; + case ButtonSystemState.Initial: logo.ClearTransforms(targetMember: nameof(Position)); logo.RelativePositionAxes = Axes.None; @@ -287,6 +296,7 @@ namespace osu.Game.Screens.Menu game?.Toolbar.Show(); }, 200); break; + default: logo.ClearTransforms(targetMember: nameof(Position)); logo.RelativePositionAxes = Axes.None; @@ -296,6 +306,7 @@ namespace osu.Game.Screens.Menu } break; + case ButtonSystemState.EnteringMode: logoTracking = true; break; diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index a41a12927b..a6ca483c12 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -118,6 +118,7 @@ namespace osu.Game.Screens.Menu base.Update(); float decayFactor = (float)Time.Elapsed * decay_per_milisecond; + for (int i = 0; i < bars_per_visualiser; i++) { //3% of extra bar length to make it a little faster when bar is almost at it's minimum diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs index 5798fce457..485de87d31 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs @@ -258,6 +258,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components // nice little progressive fade int time = 500; + foreach (var c in fill.Children) { c.Delay(500 - time).FadeOut(time, Easing.Out); diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index a71106872e..eac9871a57 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -149,6 +149,7 @@ namespace osu.Game.Screens.Multi.Match header.Tabs.Current.BindValueChanged(tab => { const float fade_duration = 500; + if (tab.NewValue is SettingsMatchPage) { settings.Show(); diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 5e019a7b3a..a726523ee5 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -248,6 +248,7 @@ namespace osu.Game.Screens.Multi if (screenStack.CurrentScreen is MatchSubScreen) { var track = Beatmap.Value.Track; + if (track != null) { track.Looping = true; diff --git a/osu.Game/Screens/OsuScreenDependencies.cs b/osu.Game/Screens/OsuScreenDependencies.cs index 84e5de76de..8c759ec6f8 100644 --- a/osu.Game/Screens/OsuScreenDependencies.cs +++ b/osu.Game/Screens/OsuScreenDependencies.cs @@ -20,12 +20,14 @@ namespace osu.Game.Screens if (requireLease) { Beatmap = parent.Get>()?.GetBoundCopy(); + if (Beatmap == null) { Cache(Beatmap = parent.Get>().BeginLease(false)); } Ruleset = parent.Get>()?.GetBoundCopy(); + if (Ruleset == null) { Cache(Ruleset = parent.Get>().BeginLease(true)); diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 2c1293833f..2df5ce101c 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -68,6 +68,7 @@ namespace osu.Game.Screens.Play.HUD Current.ValueChanged += mods => { iconsContainer.Clear(); + foreach (Mod mod in mods.NewValue) { iconsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.6f) }); diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index 0626c40334..88a62ac8d4 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -52,6 +52,7 @@ namespace osu.Game.Screens.Play { isLit = value; updateGlowSprite(value); + if (value && IsCounting) { CountPresses++; diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index d10034d552..5b7a9574b6 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -169,6 +169,7 @@ namespace osu.Game.Screens.Play var max = values.Max(); float step = values.Length / (float)ColumnCount; + for (float i = 0; i < values.Length; i += step) { newValues.Add((float)values[(int)i] / max); diff --git a/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs b/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs index 043bf55d2b..fab227c7f4 100644 --- a/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs +++ b/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs @@ -180,6 +180,7 @@ namespace osu.Game.Screens.Ranking.Pages scoreCounter.Increment(Score.TotalScore); int delay = 0; + foreach (var s in statisticsContainer.Children) { s.FadeOut() @@ -336,6 +337,7 @@ namespace osu.Game.Screens.Ranking.Pages versionMapper.Colour = colours.Gray8; var creator = beatmap.Metadata.Author?.Username; + if (!string.IsNullOrEmpty(creator)) { versionMapper.Text = $"mapped by {creator}"; diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index d7240a40ad..63ad3b6ab2 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -257,6 +257,7 @@ namespace osu.Game.Screens.Select select(beatmap); return; + case CarouselBeatmapSet set: if (skipDifficulties) select(set); @@ -292,6 +293,7 @@ namespace osu.Game.Screens.Select if (RandomAlgorithm.Value == RandomSelectAlgorithm.RandomPermutation) { var notYetVisitedSets = visibleSets.Except(previouslyVisitedRandomSets).ToList(); + if (!notYetVisitedSets.Any()) { previouslyVisitedRandomSets.RemoveAll(s => visibleSets.Contains(s)); @@ -394,13 +396,16 @@ namespace osu.Game.Screens.Select case Key.Up: direction = -1; break; + case Key.Down: direction = 1; break; + case Key.Left: direction = -1; skipDifficulties = true; break; + case Key.Right: direction = 1; skipDifficulties = true; @@ -465,8 +470,10 @@ namespace osu.Game.Screens.Select case LoadState.NotLoaded: LoadComponentAsync(item); break; + case LoadState.Loading: break; + default: scrollableContent.Add(item); break; @@ -557,6 +564,7 @@ namespace osu.Game.Screens.Select set.MoveToX(set.Item.State.Value == CarouselItemState.Selected ? -100 : 0, 500, Easing.OutExpo); set.MoveToY(currentY, 750, Easing.OutExpo); break; + case DrawableCarouselBeatmap beatmap: if (beatmap.Item.State.Value == CarouselItemState.Selected) scrollTarget = currentY + beatmap.DrawHeight / 2 - DrawHeight / 2; diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 6a10e86198..7c7d9e3928 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -53,6 +53,7 @@ namespace osu.Game.Screens.Select var autoType = auto.GetType(); var mods = SelectedMods.Value; + if (mods.All(m => m.GetType() != autoType)) { SelectedMods.Value = mods.Append(auto); diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs index f8445a4a7d..8499b56847 100644 --- a/osu.Game/Screens/Tournament/Drawings.cs +++ b/osu.Game/Screens/Tournament/Drawings.cs @@ -318,6 +318,7 @@ namespace osu.Game.Screens.Tournament using (StreamReader sr = new StreamReader(stream)) { string line; + while ((line = sr.ReadLine()?.Trim()) != null) { if (string.IsNullOrEmpty(line)) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 358b2b222b..ea4a777b47 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -49,15 +49,19 @@ namespace osu.Game.Skinning case "Play/Miss": componentName = "hit0"; break; + case "Play/Meh": componentName = "hit50"; break; + case "Play/Good": componentName = "hit100"; break; + case "Play/Great": componentName = "hit300"; break; + case "Play/osu/number-text": return !hasFont(Configuration.HitCircleFont) ? null @@ -82,6 +86,7 @@ namespace osu.Game.Skinning float ratio = 2; var texture = Textures.Get($"{componentName}@2x"); + if (texture == null) { ratio = 1; @@ -184,6 +189,7 @@ namespace osu.Game.Skinning float ratio = 36; var texture = textures.Get($"{textureName}@2x"); + if (texture == null) { ratio = 18; diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index f6bbbc8355..3a4d44f608 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -76,6 +76,7 @@ namespace osu.Game.Skinning base.Populate(model, archive); Skin reference = getSkin(model); + if (!string.IsNullOrEmpty(reference.Configuration.SkinInfo.Name)) { model.Name = reference.Configuration.SkinInfo.Name; diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index 0b9ebaf3a7..d01fba7d39 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -67,6 +67,7 @@ namespace osu.Game.Storyboards.Drawables private void load(IBindable beatmap, TextureStore textureStore) { var basePath = Animation.Path.ToLowerInvariant(); + for (var frame = 0; frame < Animation.FrameCount; frame++) { var framePath = basePath.Replace(".", frame + "."); diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index b91b05bd04..8f8ec22aae 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -82,6 +82,7 @@ namespace osu.Game.Storyboards where T : struct { var initialized = false; + foreach (var command in commands.OrderBy(l => l)) { if (!initialized) diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 44ac38044d..6a5e17eb38 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -35,6 +35,7 @@ namespace osu.Game.Tests.Beatmaps Assert.Multiple(() => { int mappingCounter = 0; + while (true) { if (mappingCounter >= ourResult.Mappings.Count && mappingCounter >= expectedResult.Mappings.Count) @@ -61,6 +62,7 @@ namespace osu.Game.Tests.Beatmaps Assert.Multiple(() => { int objectCounter = 0; + while (true) { if (objectCounter >= ourMapping.Objects.Count && objectCounter >= expectedMapping.Objects.Count) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index cfffed663c..e6f8044b60 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -231,6 +231,7 @@ True NEXT_LINE 1 + 1 NEXT_LINE 1 1 From c39c37a18dce16f25fbf9a23be5f27c93c485320 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Apr 2019 12:44:46 +0900 Subject: [PATCH 0395/2854] Apply more missed cases --- osu.Desktop/Updater/SimpleUpdateManager.cs | 1 + .../CatchBeatmapConversionTest.cs | 2 ++ osu.Game.Rulesets.Catch/CatchRuleset.cs | 4 ++++ .../Judgements/CatchBananaJudgement.cs | 2 ++ .../Judgements/CatchDropletJudgement.cs | 2 ++ .../Judgements/CatchJudgement.cs | 2 ++ .../Judgements/CatchTinyDropletJudgement.cs | 2 ++ .../Objects/Drawable/DrawableCatchHitObject.cs | 1 + osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 1 + .../UI/DrawableCatchRuleset.cs | 5 +++++ .../Legacy/EndTimeObjectPatternGenerator.cs | 2 ++ .../Edit/ManiaHitObjectComposer.cs | 1 + .../Judgements/ManiaJudgement.cs | 4 ++++ .../Objects/Drawables/DrawableManiaHitObject.cs | 1 + .../Scoring/ManiaScoreProcessor.cs | 5 +++++ .../OsuBeatmapConversionTest.cs | 1 + .../Sliders/Components/SliderCirclePiece.cs | 1 + .../Edit/OsuHitObjectComposer.cs | 2 ++ osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs | 3 +++ osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 3 +++ .../Objects/Drawables/DrawableRepeatPoint.cs | 2 ++ .../Objects/Drawables/DrawableSliderTick.cs | 2 ++ .../Objects/Drawables/DrawableSpinner.cs | 2 ++ osu.Game.Rulesets.Osu/Objects/Slider.cs | 3 +++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 5 +++++ osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs | 2 ++ .../TestCaseTaikoPlayfield.cs | 5 +++++ .../Judgements/TaikoDrumRollJudgement.cs | 1 + .../Judgements/TaikoDrumRollTickJudgement.cs | 2 ++ .../Judgements/TaikoJudgement.cs | 5 +++++ .../Judgements/TaikoSwellJudgement.cs | 1 + .../Objects/Drawables/DrawableSwell.cs | 1 + .../Objects/TaikoHitWindows.cs | 1 + osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 4 ++++ .../UI/DrawableTaikoJudgement.cs | 1 + .../UI/DrawableTaikoRuleset.cs | 5 +++++ osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 ++ .../Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 15 +++++++++++++++ .../Formats/LegacyStoryboardDecoderTest.cs | 2 ++ .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 1 + .../Beatmaps/IO/OszArchiveReaderTest.cs | 1 + osu.Game.Tests/Skins/LegacySkinDecoderTest.cs | 1 + .../Gameplay/TestCaseScrollingHitObjects.cs | 4 ++++ .../Visual/SongSelect/TestCaseBeatmapInfoWedge.cs | 4 ++++ .../UserInterface/TestCaseNotificationOverlay.cs | 3 +++ osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs | 2 ++ .../Drawables/DifficultyColouredContainer.cs | 5 +++++ .../WorkingBeatmap_VirtualBeatmapTrack.cs | 2 ++ .../Containers/OsuFocusedOverlayContainer.cs | 2 ++ osu.Game/Graphics/OsuColour.cs | 2 ++ osu.Game/Graphics/OsuFont.cs | 2 ++ osu.Game/Graphics/ScreenshotManager.cs | 2 ++ osu.Game/Graphics/UserInterface/OsuMenu.cs | 2 ++ osu.Game/Input/IdleTracker.cs | 1 + osu.Game/Online/API/Requests/GetRoomsRequest.cs | 3 +++ osu.Game/Overlays/AccountCreationOverlay.cs | 2 ++ .../Overlays/BeatmapSet/Buttons/DownloadButton.cs | 3 +++ osu.Game/Overlays/BeatmapSet/Header.cs | 2 ++ osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs | 1 + osu.Game/Overlays/Direct/DownloadButton.cs | 4 ++++ osu.Game/Overlays/Direct/DownloadProgressBar.cs | 3 +++ osu.Game/Overlays/MainSettings.cs | 1 + osu.Game/Overlays/MedalSplash/DrawableMedal.cs | 3 +++ .../Notifications/ProgressNotification.cs | 3 +++ osu.Game/Overlays/OnScreenDisplay.cs | 1 + .../Sections/Ranks/PaginatedScoreContainer.cs | 1 + .../Settings/Sections/General/LoginSettings.cs | 5 +++++ osu.Game/Overlays/Settings/Sidebar.cs | 1 + osu.Game/Overlays/Toolbar/ToolbarUserButton.cs | 1 + osu.Game/Overlays/VolumeOverlay.cs | 2 ++ .../Rulesets/Difficulty/DifficultyCalculator.cs | 2 ++ osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 2 ++ osu.Game/Rulesets/Judgements/DrawableJudgement.cs | 6 ++++++ osu.Game/Rulesets/Mods/ModTimeRamp.cs | 2 ++ osu.Game/Rulesets/Objects/HitWindows.cs | 7 +++++++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 3 +++ osu.Game/Rulesets/UI/ModIcon.cs | 4 ++++ osu.Game/Rulesets/UI/RulesetInputManager.cs | 1 + .../UI/Scrolling/DrawableScrollingRuleset.cs | 3 +++ .../UI/Scrolling/ScrollingHitObjectContainer.cs | 5 +++++ osu.Game/Scoring/Legacy/LegacyScoreInfo.cs | 2 ++ .../Edit/Compose/Components/BeatDivisorControl.cs | 9 +++++++++ osu.Game/Screens/Menu/Button.cs | 5 +++++ osu.Game/Screens/Menu/ButtonArea.cs | 3 +++ osu.Game/Screens/Menu/MainMenu.cs | 1 + osu.Game/Screens/Play/GameplayMenuOverlay.cs | 2 ++ osu.Game/Screens/Play/KeyCounterMouse.cs | 2 ++ osu.Game/Screens/Play/SkipOverlay.cs | 1 + .../Screens/Select/Carousel/CarouselBeatmapSet.cs | 3 +++ osu.Game/Screens/Select/Carousel/CarouselGroup.cs | 1 + .../Select/Carousel/CarouselGroupEagerSelect.cs | 1 + .../Select/Carousel/DrawableCarouselItem.cs | 1 + .../Screens/Tournament/ScrollingTeamContainer.cs | 3 +++ osu.Game/Skinning/LegacySkinDecoder.cs | 3 +++ osu.Game/Tests/Visual/ScrollingTestContainer.cs | 2 ++ 95 files changed, 250 insertions(+) diff --git a/osu.Desktop/Updater/SimpleUpdateManager.cs b/osu.Desktop/Updater/SimpleUpdateManager.cs index 1cb47d6b58..e07ecc9433 100644 --- a/osu.Desktop/Updater/SimpleUpdateManager.cs +++ b/osu.Desktop/Updater/SimpleUpdateManager.cs @@ -78,6 +78,7 @@ namespace osu.Desktop.Updater case RuntimeInfo.Platform.Windows: bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".exe")); break; + case RuntimeInfo.Platform.MacOsx: bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".app.zip")); break; diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index 7f85d4ccce..e45ed8c6f4 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -36,11 +36,13 @@ namespace osu.Game.Rulesets.Catch.Tests yield return new ConvertValue((CatchHitObject)nested); break; + case BananaShower shower: foreach (var nested in shower.NestedHitObjects) yield return new ConvertValue((CatchHitObject)nested); break; + default: yield return new ConvertValue((CatchHitObject)hitObject); diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index aa00e182a9..0c46c1f9e5 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -87,6 +87,7 @@ namespace osu.Game.Rulesets.Catch new CatchModNoFail(), new MultiMod(new CatchModHalfTime(), new CatchModDaycore()) }; + case ModType.DifficultyIncrease: return new Mod[] { @@ -96,17 +97,20 @@ namespace osu.Game.Rulesets.Catch new CatchModHidden(), new CatchModFlashlight(), }; + case ModType.Automation: return new Mod[] { new MultiMod(new CatchModAutoplay(), new ModCinema()), new CatchModRelax(), }; + case ModType.Fun: return new Mod[] { new MultiMod(new ModWindUp(), new ModWindDown()) }; + default: return new Mod[] { }; } diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs index 4e64753a65..1da4cf4a5e 100644 --- a/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs +++ b/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs @@ -16,6 +16,7 @@ namespace osu.Game.Rulesets.Catch.Judgements { default: return 0; + case HitResult.Perfect: return 1100; } @@ -27,6 +28,7 @@ namespace osu.Game.Rulesets.Catch.Judgements { default: return 0; + case HitResult.Perfect: return 8; } diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs index 2598dee156..4272d8471e 100644 --- a/osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs +++ b/osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs @@ -13,6 +13,7 @@ namespace osu.Game.Rulesets.Catch.Judgements { default: return 0; + case HitResult.Perfect: return 30; } @@ -24,6 +25,7 @@ namespace osu.Game.Rulesets.Catch.Judgements { default: return 0; + case HitResult.Perfect: return 7; } diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs index 5d7ef04dd2..6acef7190c 100644 --- a/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs +++ b/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs @@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Catch.Judgements { default: return 0; + case HitResult.Perfect: return 300; } @@ -28,6 +29,7 @@ namespace osu.Game.Rulesets.Catch.Judgements { default: return 0; + case HitResult.Perfect: return 10.2; } diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchTinyDropletJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchTinyDropletJudgement.cs index b8c51b7b60..d71ff3f640 100644 --- a/osu.Game.Rulesets.Catch/Judgements/CatchTinyDropletJudgement.cs +++ b/osu.Game.Rulesets.Catch/Judgements/CatchTinyDropletJudgement.cs @@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Catch.Judgements { default: return 0; + case HitResult.Perfect: return 10; } @@ -26,6 +27,7 @@ namespace osu.Game.Rulesets.Catch.Judgements { default: return 0; + case HitResult.Perfect: return 4; } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs index 294fd97d59..2f8ccec48b 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -84,6 +84,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable case ArmedState.Miss: this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out).Expire(); break; + case ArmedState.Hit: this.FadeOut().Expire(); break; diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 2adc156efd..a9fd34455a 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -95,6 +95,7 @@ namespace osu.Game.Rulesets.Catch.Objects X = X + Path.PositionAt(e.PathProgress).X / CatchPlayfield.BASE_WIDTH, }); break; + case SliderEventType.Head: case SliderEventType.Tail: case SliderEventType.Repeat: diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index ba0f5b90ba..555c1adc4d 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -46,14 +46,19 @@ namespace osu.Game.Rulesets.Catch.UI { case Banana banana: return new DrawableBanana(banana); + case Fruit fruit: return new DrawableFruit(fruit); + case JuiceStream stream: return new DrawableJuiceStream(stream, CreateDrawableRepresentation); + case BananaShower shower: return new DrawableBananaShower(shower, CreateDrawableRepresentation); + case TinyDroplet tiny: return new DrawableTinyDroplet(tiny); + case Droplet droplet: return new DrawableDroplet(droplet); } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index 0bf6c055ac..9e95be35fa 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -38,9 +38,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy case 8 when HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000: addToPattern(pattern, 0, generateHold); break; + case 8: addToPattern(pattern, FindAvailableColumn(GetRandomColumn(), PreviousPattern), generateHold); break; + default: if (TotalColumns > 0) addToPattern(pattern, GetRandomColumn(), generateHold); diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 56c9471462..eec3e1b33d 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -65,6 +65,7 @@ namespace osu.Game.Rulesets.Mania.Edit { case DrawableNote note: return new NoteSelectionBlueprint(note); + case DrawableHoldNote holdNote: return new HoldNoteSelectionBlueprint(holdNote); } diff --git a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs index b6fb37f054..c2f8fb8678 100644 --- a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs +++ b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs @@ -14,12 +14,16 @@ namespace osu.Game.Rulesets.Mania.Judgements { default: return 0; + case HitResult.Meh: return 50; + case HitResult.Ok: return 100; + case HitResult.Good: return 200; + case HitResult.Great: case HitResult.Perfect: return 300; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index a78524011f..0873f753be 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -65,6 +65,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables case ArmedState.Miss: this.FadeOut(150, Easing.In).Expire(); break; + case ArmedState.Hit: this.FadeOut(150, Easing.OutQuint).Expire(); break; diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index 5c914d8eac..ba38d11225 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -140,18 +140,23 @@ namespace osu.Game.Rulesets.Mania.Scoring case HitResult.Miss: Health.Value += hpMissMultiplier * hp_increase_miss; break; + case HitResult.Meh: Health.Value += hpMultiplier * hp_increase_bad; break; + case HitResult.Ok: Health.Value += hpMultiplier * hp_increase_ok; break; + case HitResult.Good: Health.Value += hpMultiplier * hp_increase_good; break; + case HitResult.Great: Health.Value += hpMultiplier * hp_increase_great; break; + case HitResult.Perfect: Health.Value += hpMultiplier * hp_increase_perfect; break; diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs index f7d1ff4db1..f98d63e6c7 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs @@ -35,6 +35,7 @@ namespace osu.Game.Rulesets.Osu.Tests yield return createConvertValue(nested); break; + default: yield return createConvertValue(hitObject); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs index 9d164ebe0b..2ecfea2e3e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs @@ -37,6 +37,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components case SliderPosition.Start: Position = slider.StackedPosition + slider.Path.PositionAt(0); break; + case SliderPosition.End: Position = slider.StackedPosition + slider.Path.PositionAt(1); break; diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 039ec5585e..8d007ad88e 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -41,8 +41,10 @@ namespace osu.Game.Rulesets.Osu.Edit { case DrawableHitCircle circle: return new HitCircleSelectionBlueprint(circle); + case DrawableSlider slider: return new SliderSelectionBlueprint(slider); + case DrawableSpinner spinner: return new SpinnerSelectionBlueprint(spinner); } diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs index 81fedf9f4a..bf30fbc351 100644 --- a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs @@ -16,10 +16,13 @@ namespace osu.Game.Rulesets.Osu.Judgements { default: return 0; + case HitResult.Meh: return 50; + case HitResult.Good: return 100; + case HitResult.Great: return 300; } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 1a30b2c944..ddf708d0f1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -59,11 +59,13 @@ namespace osu.Game.Rulesets.Osu.Mods circle.FadeOut(fadeOutDuration); break; + case DrawableSlider slider: using (slider.BeginAbsoluteSequence(fadeOutStartTime, true)) slider.Body.FadeOut(longFadeDuration, Easing.Out); break; + case DrawableSliderTick sliderTick: // slider ticks fade out over up to one second var tickFadeOutDuration = Math.Min(sliderTick.HitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000); @@ -72,6 +74,7 @@ namespace osu.Game.Rulesets.Osu.Mods sliderTick.FadeOut(tickFadeOutDuration); break; + case DrawableSpinner spinner: // hide elements we don't care about. spinner.Disc.Hide(); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index a6714690b1..c278c0c7ec 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -64,9 +64,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables case ArmedState.Idle: this.Delay(HitObject.TimePreempt).FadeOut(); break; + case ArmedState.Miss: this.FadeOut(animDuration); break; + case ArmedState.Hit: this.FadeOut(animDuration, Easing.OutQuint) .ScaleTo(Scale * 1.5f, animDuration, Easing.Out); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index b5ce36f889..72b648bfd0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -67,10 +67,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables case ArmedState.Idle: this.Delay(HitObject.TimePreempt).FadeOut(); break; + case ArmedState.Miss: this.FadeOut(ANIM_DURATION); this.FadeColour(Color4.Red, ANIM_DURATION / 2); break; + case ArmedState.Hit: this.FadeOut(ANIM_DURATION, Easing.OutQuint); this.ScaleTo(Scale * 1.5f, ANIM_DURATION, Easing.Out); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 3a6ff3fcf8..ca219b0094 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -222,9 +222,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables case ArmedState.Idle: Expire(true); break; + case ArmedState.Hit: sequence.ScaleTo(Scale * 1.2f, 320, Easing.Out); break; + case ArmedState.Miss: sequence.ScaleTo(Scale * 0.8f, 320, Easing.In); break; diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 1afbacc01e..a8aec005d1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -183,6 +183,7 @@ namespace osu.Game.Rulesets.Osu.Objects Samples = sampleList }); break; + case SliderEventType.Head: AddNested(HeadCircle = new SliderCircle { @@ -194,6 +195,7 @@ namespace osu.Game.Rulesets.Osu.Objects ComboIndex = ComboIndex, }); break; + case SliderEventType.LegacyLastTick: // we need to use the LegacyLastTick here for compatibility reasons (difficulty). // it is *okay* to use this because the TailCircle is not used for any meaningful purpose in gameplay. @@ -206,6 +208,7 @@ namespace osu.Game.Rulesets.Osu.Objects ComboIndex = ComboIndex, }); break; + case SliderEventType.Repeat: AddNested(new RepeatPoint { diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 44bce5bed8..3481b7751b 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -104,6 +104,7 @@ namespace osu.Game.Rulesets.Osu new MultiMod(new OsuModHalfTime(), new OsuModDaycore()), new OsuModSpunOut(), }; + case ModType.DifficultyIncrease: return new Mod[] { @@ -113,11 +114,13 @@ namespace osu.Game.Rulesets.Osu new OsuModHidden(), new MultiMod(new OsuModFlashlight(), new OsuModBlinds()), }; + case ModType.Conversion: return new Mod[] { new OsuModTarget(), }; + case ModType.Automation: return new Mod[] { @@ -125,6 +128,7 @@ namespace osu.Game.Rulesets.Osu new OsuModRelax(), new OsuModAutopilot(), }; + case ModType.Fun: return new Mod[] { @@ -133,6 +137,7 @@ namespace osu.Game.Rulesets.Osu new OsuModGrow(), new MultiMod(new ModWindUp(), new ModWindDown()), }; + default: return new Mod[] { }; } diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index 828b3720d3..3adf788665 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -40,8 +40,10 @@ namespace osu.Game.Rulesets.Osu.UI { case HitCircle circle: return new DrawableHitCircle(circle); + case Slider slider: return new DrawableSlider(slider); + case Spinner spinner: return new DrawableSpinner(spinner); } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs index 369cdd49d2..69eb48b7ce 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs @@ -100,15 +100,19 @@ namespace osu.Game.Rulesets.Taiko.Tests case 1: addCentreHit(false); break; + case 2: addCentreHit(true); break; + case 3: addDrumRoll(false); break; + case 4: addDrumRoll(true); break; + case 5: addSwell(); delay = scroll_time - 100; @@ -121,6 +125,7 @@ namespace osu.Game.Rulesets.Taiko.Tests default: playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, rng.Next(25, 400)), 500); break; + case 6: playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, TaikoPlayfield.DEFAULT_HEIGHT), 500); break; diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollJudgement.cs index e5ebd5c647..604daa929f 100644 --- a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollJudgement.cs +++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollJudgement.cs @@ -16,6 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Judgements { case HitResult.Miss: return 0; + default: return base.HealthIncreaseFor(result); } diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs index 32d4b77ca4..a617028f1c 100644 --- a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs +++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs @@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Judgements { case HitResult.Great: return 200; + default: return 0; } @@ -26,6 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Judgements { case HitResult.Great: return 0.15; + default: return 0; } diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs index 427d38aaa7..eb5f443365 100644 --- a/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs +++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs @@ -16,8 +16,10 @@ namespace osu.Game.Rulesets.Taiko.Judgements { case HitResult.Good: return 100; + case HitResult.Great: return 300; + default: return 0; } @@ -29,10 +31,13 @@ namespace osu.Game.Rulesets.Taiko.Judgements { case HitResult.Miss: return -1.0; + case HitResult.Good: return 1.1; + case HitResult.Great: return 3.0; + default: return 0; } diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoSwellJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoSwellJudgement.cs index f0f621d12b..29be5e0eac 100644 --- a/osu.Game.Rulesets.Taiko/Judgements/TaikoSwellJudgement.cs +++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoSwellJudgement.cs @@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Judgements { case HitResult.Miss: return -0.65; + default: return 0; } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 9211eccc40..5ec9dc61e2 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -192,6 +192,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables using (BeginAbsoluteSequence(HitObject.StartTime - preempt, true)) targetRing.ScaleTo(target_ring_scale, preempt * 4, Easing.OutQuint); break; + case ArmedState.Miss: case ArmedState.Hit: this.FadeOut(out_transition_time, Easing.Out); diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitWindows.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitWindows.cs index ce841fff80..f232919cbf 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitWindows.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitWindows.cs @@ -25,6 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Objects case HitResult.Good: case HitResult.Miss: return true; + default: return false; } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 448b1b42bb..1fec4ae173 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -86,6 +86,7 @@ namespace osu.Game.Rulesets.Taiko new TaikoModNoFail(), new MultiMod(new TaikoModHalfTime(), new TaikoModDaycore()), }; + case ModType.DifficultyIncrease: return new Mod[] { @@ -95,17 +96,20 @@ namespace osu.Game.Rulesets.Taiko new TaikoModHidden(), new TaikoModFlashlight(), }; + case ModType.Automation: return new Mod[] { new MultiMod(new TaikoModAutoplay(), new ModCinema()), new TaikoModRelax(), }; + case ModType.Fun: return new Mod[] { new MultiMod(new ModWindUp(), new ModWindDown()) }; + default: return new Mod[] { }; } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs index 943adaed4b..f91bbb14e8 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs @@ -33,6 +33,7 @@ namespace osu.Game.Rulesets.Taiko.UI case HitResult.Good: JudgementBody.Colour = colours.GreenLight; break; + case HitResult.Great: JudgementBody.Colour = colours.BlueLight; break; diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index f4b9c46dfc..d3b2f4e987 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -52,9 +52,11 @@ namespace osu.Game.Rulesets.Taiko.UI int currentIndex = 0; int currentBeat = 0; double time = timingPoints[currentIndex].Time; + while (time <= lastHitTime) { int nextIndex = currentIndex + 1; + if (nextIndex < timingPoints.Count && time > timingPoints[nextIndex].Time) { currentIndex = nextIndex; @@ -93,10 +95,13 @@ namespace osu.Game.Rulesets.Taiko.UI { case CentreHit centreHit: return new DrawableCentreHit(centreHit); + case RimHit rimHit: return new DrawableRimHit(rimHit); + case DrumRoll drumRoll: return new DrawableDrumRoll(drumRoll); + case Swell swell: return new DrawableSwell(swell); } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index dbff5270d2..260c5b0727 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -211,6 +211,7 @@ namespace osu.Game.Rulesets.Taiko.UI case DrawableBarLine barline: barlineContainer.Add(barline.CreateProxy()); break; + case DrawableTaikoHitObject taikoObject: topLevelHitContainer.Add(taikoObject.CreateProxiedContent()); break; @@ -231,6 +232,7 @@ namespace osu.Game.Rulesets.Taiko.UI if (result.IsHit) hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).MainObject)?.VisualiseSecondHit(); break; + default: judgementContainer.Add(new DrawableTaikoJudgement(result, judgedObject) { diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 02dff6993d..6df4e7d6c8 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -47,6 +47,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapGeneral() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { @@ -70,6 +71,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapEditor() { var decoder = new LegacyBeatmapDecoder(); + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { @@ -95,6 +97,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapMetadata() { var decoder = new LegacyBeatmapDecoder(); + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { @@ -119,6 +122,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapDifficulty() { var decoder = new LegacyBeatmapDecoder(); + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { @@ -137,6 +141,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapEvents() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { @@ -155,6 +160,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapTimingPoints() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { @@ -190,6 +196,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapColours() { var decoder = new LegacySkinDecoder(); + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { @@ -215,6 +222,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapComboOffsetsOsu() { var decoder = new LegacyBeatmapDecoder(); + using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu")) using (var stream = new StreamReader(resStream)) { @@ -237,6 +245,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapComboOffsetsCatch() { var decoder = new LegacyBeatmapDecoder(); + using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu")) using (var stream = new StreamReader(resStream)) { @@ -259,6 +268,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapHitObjects() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { @@ -286,6 +296,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeControlPointCustomSampleBank() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + using (var resStream = TestResources.OpenResource("controlpoint-custom-samplebank.osu")) using (var stream = new StreamReader(resStream)) { @@ -307,6 +318,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeHitObjectCustomSampleBank() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + using (var resStream = TestResources.OpenResource("hitobject-custom-samplebank.osu")) using (var stream = new StreamReader(resStream)) { @@ -324,6 +336,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeHitObjectFileSamples() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + using (var resStream = TestResources.OpenResource("hitobject-file-samples.osu")) using (var stream = new StreamReader(resStream)) { @@ -343,6 +356,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeSliderSamples() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + using (var resStream = TestResources.OpenResource("slider-samples.osu")) using (var stream = new StreamReader(resStream)) { @@ -386,6 +400,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeHitObjectNullAdditionBank() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + using (var resStream = TestResources.OpenResource("hitobject-no-addition-bank.osu")) using (var stream = new StreamReader(resStream)) { diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 2288d04493..971518909d 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -19,6 +19,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeStoryboardEvents() { var decoder = new LegacyStoryboardDecoder(); + using (var resStream = TestResources.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu")) using (var stream = new StreamReader(resStream)) { @@ -91,6 +92,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeVariableWithSuffix() { var decoder = new LegacyStoryboardDecoder(); + using (var resStream = TestResources.OpenResource("variable-with-suffix.osb")) using (var stream = new StreamReader(resStream)) { diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index a867ddebae..39b7735a55 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -151,6 +151,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var sr = new StreamReader(stream)) { var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr); + using (var ms = new MemoryStream()) using (var sw = new StreamWriter(ms)) using (var sr2 = new StreamReader(ms)) diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index 17197aff27..37e0565df0 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -75,6 +75,7 @@ namespace osu.Game.Tests.Beatmaps.IO using (var osz = TestResources.GetTestBeatmapStream()) { var reader = new ZipArchiveReader(osz); + using (var stream = new StreamReader( reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) { diff --git a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs index 2a97519e21..24ef9e4535 100644 --- a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs +++ b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs @@ -18,6 +18,7 @@ namespace osu.Game.Tests.Skins public void TestDecodeSkinColours(bool hasColours) { var decoder = new LegacySkinDecoder(); + using (var resStream = TestResources.OpenResource(hasColours ? "skin.ini" : "skin-empty.ini")) using (var stream = new StreamReader(resStream)) { diff --git a/osu.Game.Tests/Visual/Gameplay/TestCaseScrollingHitObjects.cs b/osu.Game.Tests/Visual/Gameplay/TestCaseScrollingHitObjects.cs index c99a4bb89b..b5a7240839 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCaseScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCaseScrollingHitObjects.cs @@ -124,12 +124,15 @@ namespace osu.Game.Tests.Visual.Gameplay case ScrollingDirection.Up: obj.Anchor = Anchor.TopCentre; break; + case ScrollingDirection.Down: obj.Anchor = Anchor.BottomCentre; break; + case ScrollingDirection.Left: obj.Anchor = Anchor.CentreLeft; break; + case ScrollingDirection.Right: obj.Anchor = Anchor.CentreRight; break; @@ -184,6 +187,7 @@ namespace osu.Game.Tests.Visual.Gameplay RelativeSizeAxes = Axes.X; Height = 2; break; + case ScrollingDirection.Left: case ScrollingDirection.Right: RelativeSizeAxes = Axes.Y; diff --git a/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapInfoWedge.cs index f3e44bd808..36a7eba37f 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapInfoWedge.cs @@ -83,15 +83,19 @@ namespace osu.Game.Tests.Visual.SongSelect case OsuRuleset _: testInfoLabels(5); break; + case TaikoRuleset _: testInfoLabels(5); break; + case CatchRuleset _: testInfoLabels(5); break; + case ManiaRuleset _: testInfoLabels(4); break; + default: testInfoLabels(2); break; diff --git a/osu.Game.Tests/Visual/UserInterface/TestCaseNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseNotificationOverlay.cs index 4819597d22..faf80d22ff 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestCaseNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestCaseNotificationOverlay.cs @@ -86,12 +86,15 @@ namespace osu.Game.Tests.Visual.UserInterface case 0: sendHelloNotification(); break; + case 1: sendAmazingNotification(); break; + case 2: sendUploadProgress(); break; + case 3: sendDownloadProgress(); break; diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs index 3adfcb85ea..d0db7765c2 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs @@ -32,9 +32,11 @@ namespace osu.Game.Beatmaps.Drawables case BeatmapSetCoverType.Cover: resource = set.OnlineInfo.Covers.Cover; break; + case BeatmapSetCoverType.Card: resource = set.OnlineInfo.Covers.Card; break; + case BeatmapSetCoverType.List: resource = set.OnlineInfo.Covers.List; break; diff --git a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs index f1607ad749..26ffcca1ec 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs @@ -63,15 +63,20 @@ namespace osu.Game.Beatmaps.Drawables { case DifficultyRating.Easy: return palette.Green; + default: case DifficultyRating.Normal: return palette.Blue; + case DifficultyRating.Hard: return palette.Yellow; + case DifficultyRating.Insane: return palette.Pink; + case DifficultyRating.Expert: return palette.Purple; + case DifficultyRating.ExpertPlus: return palette.Gray0; } diff --git a/osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs b/osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs index cb1be82c69..1e237a2b53 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs @@ -26,9 +26,11 @@ namespace osu.Game.Beatmaps case null: Length = excess_length; break; + case IHasEndTime endTime: Length = endTime.EndTime + excess_length; break; + default: Length = lastObject.StartTime + excess_length; break; diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index c6ee91f961..3f84f77081 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -84,6 +84,7 @@ namespace osu.Game.Graphics.Containers case GlobalAction.Back: State = Visibility.Hidden; return true; + case GlobalAction.Select: return true; } @@ -107,6 +108,7 @@ namespace osu.Game.Graphics.Containers State = Visibility.Hidden; break; + case Visibility.Hidden: if (PlaySamplesOnStateChange) samplePopOut?.Play(); if (BlockScreenWideMouse) osuGame?.RemoveBlockingOverlay(this); diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 712dc4c444..192cb917d5 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -20,12 +20,14 @@ namespace osu.Game.Graphics { default: throw new ArgumentException(@"Invalid hex string length!"); + case 3: return new Color4( (byte)(Convert.ToByte(hex.Substring(0, 1), 16) * 17), (byte)(Convert.ToByte(hex.Substring(1, 1), 16) * 17), (byte)(Convert.ToByte(hex.Substring(2, 1), 16) * 17), 255); + case 6: return new Color4( Convert.ToByte(hex.Substring(0, 2), 16), diff --git a/osu.Game/Graphics/OsuFont.cs b/osu.Game/Graphics/OsuFont.cs index 26112430f6..841e53ffbb 100644 --- a/osu.Game/Graphics/OsuFont.cs +++ b/osu.Game/Graphics/OsuFont.cs @@ -42,8 +42,10 @@ namespace osu.Game.Graphics { case Typeface.Exo: return "Exo2.0"; + case Typeface.FontAwesome: return "FontAwesome"; + case Typeface.Venera: return "Venera"; } diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index a2ac71de93..24a98e6dc9 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -104,9 +104,11 @@ namespace osu.Game.Graphics case ScreenshotFormat.Png: image.SaveAsPng(stream); break; + case ScreenshotFormat.Jpg: image.SaveAsJpeg(stream); break; + default: throw new ArgumentOutOfRangeException(nameof(screenshotFormat)); } diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs index 9b5755ea8b..32994be78a 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs @@ -88,9 +88,11 @@ namespace osu.Game.Graphics.UserInterface case MenuItemType.Standard: text.Colour = Color4.White; break; + case MenuItemType.Destructive: text.Colour = Color4.Red; break; + case MenuItemType.Highlighted: text.Colour = OsuColour.FromHex(@"ffcc22"); break; diff --git a/osu.Game/Input/IdleTracker.cs b/osu.Game/Input/IdleTracker.cs index 91e2456ef7..cbc446a126 100644 --- a/osu.Game/Input/IdleTracker.cs +++ b/osu.Game/Input/IdleTracker.cs @@ -62,6 +62,7 @@ namespace osu.Game.Input case MouseUpEvent _: case MouseMoveEvent _: return updateLastInteractionTime(); + default: return base.Handle(e); } diff --git a/osu.Game/Online/API/Requests/GetRoomsRequest.cs b/osu.Game/Online/API/Requests/GetRoomsRequest.cs index d7c66707e4..8f1497ef33 100644 --- a/osu.Game/Online/API/Requests/GetRoomsRequest.cs +++ b/osu.Game/Online/API/Requests/GetRoomsRequest.cs @@ -26,12 +26,15 @@ namespace osu.Game.Online.API.Requests { case PrimaryFilter.Open: break; + case PrimaryFilter.Owned: target += "/owned"; break; + case PrimaryFilter.Participated: target += "/participated"; break; + case PrimaryFilter.RecentlyEnded: target += "/ended"; break; diff --git a/osu.Game/Overlays/AccountCreationOverlay.cs b/osu.Game/Overlays/AccountCreationOverlay.cs index e8e44c206e..0169401d2d 100644 --- a/osu.Game/Overlays/AccountCreationOverlay.cs +++ b/osu.Game/Overlays/AccountCreationOverlay.cs @@ -103,8 +103,10 @@ namespace osu.Game.Overlays case APIState.Offline: case APIState.Failing: break; + case APIState.Connecting: break; + case APIState.Online: State = Visibility.Hidden; break; diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs index 667869e310..e6fca84731 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs @@ -123,6 +123,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons }, }; break; + case DownloadState.Downloaded: textSprites.Children = new Drawable[] { @@ -133,9 +134,11 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons }, }; break; + case DownloadState.LocallyAvailable: this.FadeOut(200); break; + case DownloadState.NotDownloaded: textSprites.Children = new Drawable[] { diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 95cf9e9d04..7ea0748585 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -225,11 +225,13 @@ namespace osu.Game.Overlays.BeatmapSet RelativeSizeAxes = Axes.Y }; break; + case DownloadState.Downloading: case DownloadState.Downloaded: // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design. downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); break; + default: downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); if (BeatmapSet.Value.OnlineInfo.HasVideo) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs index f8a8038878..a47a494a42 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -58,6 +58,7 @@ namespace osu.Game.Overlays.Chat.Tabs { default: return new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; + case ChannelType.PM: return new PrivateChannelTabItem(value) { OnRequestClose = tabCloseRequested }; } diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs index 7fc145d635..26e2bb1ae4 100644 --- a/osu.Game/Overlays/Direct/DownloadButton.cs +++ b/osu.Game/Overlays/Direct/DownloadButton.cs @@ -85,9 +85,11 @@ namespace osu.Game.Overlays.Direct case DownloadState.Downloaded: shakeContainer.Shake(); break; + case DownloadState.LocallyAvailable: game.PresentBeatmap(BeatmapSet.Value); break; + default: beatmaps.Download(BeatmapSet.Value, noVideo); break; @@ -110,9 +112,11 @@ namespace osu.Game.Overlays.Direct icon.MoveToX(0, 500, Easing.InOutExpo); checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo); break; + case DownloadState.Downloaded: background.FadeColour(colours.Yellow, 500, Easing.InOutExpo); break; + case DownloadState.LocallyAvailable: background.FadeColour(colours.Green, 500, Easing.InOutExpo); icon.MoveToX(-8, 500, Easing.InOutExpo); diff --git a/osu.Game/Overlays/Direct/DownloadProgressBar.cs b/osu.Game/Overlays/Direct/DownloadProgressBar.cs index 9c2b1e5b63..57500b3531 100644 --- a/osu.Game/Overlays/Direct/DownloadProgressBar.cs +++ b/osu.Game/Overlays/Direct/DownloadProgressBar.cs @@ -43,10 +43,12 @@ namespace osu.Game.Overlays.Direct progressBar.Current.Value = 0; progressBar.FadeOut(500); break; + case DownloadState.Downloading: progressBar.FadeIn(400, Easing.OutQuint); progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); break; + case DownloadState.Downloaded: progressBar.FadeIn(400, Easing.OutQuint); progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); @@ -54,6 +56,7 @@ namespace osu.Game.Overlays.Direct progressBar.Current.Value = 1; progressBar.FillColour = colours.Yellow; break; + case DownloadState.LocallyAvailable: progressBar.FadeOut(500); break; diff --git a/osu.Game/Overlays/MainSettings.cs b/osu.Game/Overlays/MainSettings.cs index 61dd51d16f..39bfdfd4d6 100644 --- a/osu.Game/Overlays/MainSettings.cs +++ b/osu.Game/Overlays/MainSettings.cs @@ -55,6 +55,7 @@ namespace osu.Game.Overlays SectionsContainer.FadeOut(300, Easing.OutQuint); ContentContainer.MoveToX(-WIDTH, 500, Easing.OutQuint); break; + case Visibility.Hidden: Background.FadeTo(0.6f, 500, Easing.OutQuint); Sidebar?.FadeColour(Color4.White, 300, Easing.OutQuint); diff --git a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs index 431ae98c2c..f1ae5d64f5 100644 --- a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs +++ b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs @@ -156,11 +156,13 @@ namespace osu.Game.Overlays.MedalSplash case DisplayState.None: medalContainer.ScaleTo(0); break; + case DisplayState.Icon: medalContainer .FadeIn(duration) .ScaleTo(1, duration, Easing.OutElastic); break; + case DisplayState.MedalUnlocked: medalContainer .FadeTo(1) @@ -170,6 +172,7 @@ namespace osu.Game.Overlays.MedalSplash this.MoveToY(MedalOverlay.DISC_SIZE / 2 - 30, duration, Easing.OutExpo); unlocked.FadeInFromZero(duration); break; + case DisplayState.Full: medalContainer .FadeTo(1) diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 75e70b18ea..857a0bda9e 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -54,11 +54,13 @@ namespace osu.Game.Overlays.Notifications Light.Pulsate = false; progressBar.Active = false; break; + case ProgressNotificationState.Active: Light.Colour = colourActive; Light.Pulsate = true; progressBar.Active = true; break; + case ProgressNotificationState.Cancelled: Light.Colour = colourCancelled; Light.Pulsate = false; @@ -145,6 +147,7 @@ namespace osu.Game.Overlays.Notifications case ProgressNotificationState.Cancelled: base.Close(); break; + case ProgressNotificationState.Active: case ProgressNotificationState.Queued: if (CancelRequested?.Invoke() != false) diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs index 5e45fbf081..5af7d5aea5 100644 --- a/osu.Game/Overlays/OnScreenDisplay.cs +++ b/osu.Game/Overlays/OnScreenDisplay.cs @@ -188,6 +188,7 @@ namespace osu.Game.Overlays optionCount = 1; if (val) selectedOption = 0; break; + case Enum _: var values = Enum.GetValues(description.RawValue.GetType()); optionCount = values.Length; diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 95a18ccfa9..470bed2854 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -54,6 +54,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks default: drawableScores = scores.Select(score => new DrawablePerformanceScore(score, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null)); break; + case ScoreType.Recent: drawableScores = scores.Select(score => new DrawableTotalScore(score)); break; diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index 078c01ce92..8ac13060b0 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -87,6 +87,7 @@ namespace osu.Game.Overlays.Settings.Sections.General } }; break; + case APIState.Failing: case APIState.Connecting: LinkFlowContainer linkFlow; @@ -112,6 +113,7 @@ namespace osu.Game.Overlays.Settings.Sections.General linkFlow.AddLink("cancel", api.Logout, string.Empty); break; + case APIState.Online: Children = new Drawable[] { @@ -160,14 +162,17 @@ namespace osu.Game.Overlays.Settings.Sections.General api.LocalUser.Value.Status.Value = new UserStatusOnline(); dropdown.StatusColour = colours.Green; break; + case UserAction.DoNotDisturb: api.LocalUser.Value.Status.Value = new UserStatusDoNotDisturb(); dropdown.StatusColour = colours.Red; break; + case UserAction.AppearOffline: api.LocalUser.Value.Status.Value = new UserStatusOffline(); dropdown.StatusColour = colours.Gray7; break; + case UserAction.SignOut: api.Logout(); break; diff --git a/osu.Game/Overlays/Settings/Sidebar.cs b/osu.Game/Overlays/Settings/Sidebar.cs index 969686e36d..3c18627f23 100644 --- a/osu.Game/Overlays/Settings/Sidebar.cs +++ b/osu.Game/Overlays/Settings/Sidebar.cs @@ -102,6 +102,7 @@ namespace osu.Game.Overlays.Settings default: this.ResizeTo(new Vector2(DEFAULT_WIDTH, Height), 500, Easing.OutQuint); break; + case ExpandedState.Expanded: this.ResizeTo(new Vector2(EXPANDED_WIDTH, Height), 500, Easing.OutQuint); break; diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index 356ffa5180..77def1adcf 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -58,6 +58,7 @@ namespace osu.Game.Overlays.Toolbar Text = @"Guest"; avatar.User = new User(); break; + case APIState.Online: Text = api.LocalUser.Value.Username; avatar.User = api.LocalUser.Value; diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index e2e480ef53..34b15d958d 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -105,12 +105,14 @@ namespace osu.Game.Overlays else volumeMeterMaster.Decrease(amount, isPrecise); return true; + case GlobalAction.IncreaseVolume: if (State == Visibility.Hidden) Show(); else volumeMeterMaster.Increase(amount, isPrecise); return true; + case GlobalAction.ToggleMute: Show(); muteButton.Current.Value = !muteButton.Current.Value; diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index aad55f8a38..4e71615195 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -117,10 +117,12 @@ namespace osu.Game.Rulesets.Difficulty yield return new ModNoMod(); break; + case 1: yield return currentSet.Single(); break; + default: yield return new MultiMod(currentSet.ToArray()); diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index 74aa9ace2d..2dd45660c7 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -122,8 +122,10 @@ namespace osu.Game.Rulesets.Edit { case ScrollEvent _: return false; + case MouseEvent _: return true; + default: return false; } diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 89db954c36..2150726a42 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -89,6 +89,7 @@ namespace osu.Game.Rulesets.Judgements { case HitResult.None: break; + case HitResult.Miss: JudgementBody.ScaleTo(1.6f); JudgementBody.ScaleTo(1, 100, Easing.In); @@ -98,6 +99,7 @@ namespace osu.Game.Rulesets.Judgements this.Delay(600).FadeOut(200); break; + default: ApplyHitAnimations(); break; @@ -113,13 +115,17 @@ namespace osu.Game.Rulesets.Judgements case HitResult.Perfect: case HitResult.Great: return colours.Blue; + case HitResult.Ok: case HitResult.Good: return colours.Green; + case HitResult.Meh: return colours.Yellow; + case HitResult.Miss: return colours.Red; + default: return Color4.White; } diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 62407907c1..a5f96087c0 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -73,10 +73,12 @@ namespace osu.Game.Rulesets.Mods pitch.PitchAdjust /= lastAdjust; pitch.PitchAdjust *= adjust; break; + case IHasTempoAdjust tempo: tempo.TempoAdjust /= lastAdjust; tempo.TempoAdjust *= adjust; break; + default: clock.Rate /= lastAdjust; clock.Rate *= adjust; diff --git a/osu.Game/Rulesets/Objects/HitWindows.cs b/osu.Game/Rulesets/Objects/HitWindows.cs index c5b7686da6..6fb8425a33 100644 --- a/osu.Game/Rulesets/Objects/HitWindows.cs +++ b/osu.Game/Rulesets/Objects/HitWindows.cs @@ -77,6 +77,7 @@ namespace osu.Game.Rulesets.Objects case HitResult.Perfect: case HitResult.Ok: return false; + default: return true; } @@ -126,16 +127,22 @@ namespace osu.Game.Rulesets.Objects { case HitResult.Perfect: return Perfect / 2; + case HitResult.Great: return Great / 2; + case HitResult.Good: return Good / 2; + case HitResult.Ok: return Ok / 2; + case HitResult.Meh: return Meh / 2; + case HitResult.Miss: return Miss / 2; + default: throw new ArgumentException(nameof(result)); } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 0fddb19a6c..034ebbeb3e 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -313,9 +313,11 @@ namespace osu.Game.Rulesets.Scoring { case HitResult.None: break; + case HitResult.Miss: Combo.Value = 0; break; + default: Combo.Value++; break; @@ -373,6 +375,7 @@ namespace osu.Game.Rulesets.Scoring default: case ScoringMode.Standardised: return max_score * (base_portion * baseScore / maxBaseScore + combo_portion * HighestCombo.Value / maxHighestCombo) + bonusScore; + case ScoringMode.Classic: // should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1) return bonusScore + baseScore * (1 + Math.Max(0, HighestCombo.Value - 1) / 25); diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index f9f6b5cc2f..86feea09a8 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -76,18 +76,22 @@ namespace osu.Game.Rulesets.UI backgroundColour = colours.Yellow; highlightedColour = colours.YellowLight; break; + case ModType.DifficultyReduction: backgroundColour = colours.Green; highlightedColour = colours.GreenLight; break; + case ModType.Automation: backgroundColour = colours.Blue; highlightedColour = colours.BlueLight; break; + case ModType.Conversion: backgroundColour = colours.Purple; highlightedColour = colours.PurpleLight; break; + case ModType.Fun: backgroundColour = colours.Pink; highlightedColour = colours.PinkLight; diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index b4271085f5..e25c3bd0e7 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -105,6 +105,7 @@ namespace osu.Game.Rulesets.UI return false; break; + case MouseUpEvent mouseUp: if (!CurrentState.Mouse.IsPressed(mouseUp.Button)) return false; diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index 3b368652f2..a0bfb356bc 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -92,9 +92,11 @@ namespace osu.Game.Rulesets.UI.Scrolling case ScrollVisualisationMethod.Sequential: scrollingInfo.Algorithm = new SequentialScrollAlgorithm(controlPoints); break; + case ScrollVisualisationMethod.Overlapping: scrollingInfo.Algorithm = new OverlappingScrollAlgorithm(controlPoints); break; + case ScrollVisualisationMethod.Constant: scrollingInfo.Algorithm = new ConstantScrollAlgorithm(); break; @@ -159,6 +161,7 @@ namespace osu.Game.Rulesets.UI.Scrolling case GlobalAction.IncreaseScrollSpeed: this.TransformBindableTo(TimeRange, TimeRange.Value - time_span_step, 200, Easing.OutQuint); return true; + case GlobalAction.DecreaseScrollSpeed: this.TransformBindableTo(TimeRange, TimeRange.Value + time_span_step, 200, Easing.OutQuint); return true; diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index ed3534fb36..069e2d1a0b 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -72,6 +72,7 @@ namespace osu.Game.Rulesets.UI.Scrolling case ScrollingDirection.Down: scrollLength = DrawSize.Y; break; + default: scrollLength = DrawSize.X; break; @@ -97,6 +98,7 @@ namespace osu.Game.Rulesets.UI.Scrolling case ScrollingDirection.Down: hitObject.Height = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, endTime.EndTime, timeRange.Value, scrollLength); break; + case ScrollingDirection.Left: case ScrollingDirection.Right: hitObject.Width = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, endTime.EndTime, timeRange.Value, scrollLength); @@ -129,12 +131,15 @@ namespace osu.Game.Rulesets.UI.Scrolling case ScrollingDirection.Up: hitObject.Y = scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength); break; + case ScrollingDirection.Down: hitObject.Y = -scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength); break; + case ScrollingDirection.Left: hitObject.X = scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength); break; + case ScrollingDirection.Right: hitObject.X = -scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength); break; diff --git a/osu.Game/Scoring/Legacy/LegacyScoreInfo.cs b/osu.Game/Scoring/Legacy/LegacyScoreInfo.cs index df80f848e3..e66f93ec8d 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreInfo.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreInfo.cs @@ -41,6 +41,7 @@ namespace osu.Game.Scoring.Legacy case 3: Statistics[HitResult.Great] = value; break; + case 2: Statistics[HitResult.Perfect] = value; break; @@ -81,6 +82,7 @@ namespace osu.Game.Scoring.Legacy case 1: Statistics[HitResult.Good] = value; break; + case 3: Statistics[HitResult.Ok] = value; break; diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 1f13797497..74b1e3c6cb 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -236,10 +236,12 @@ namespace osu.Game.Screens.Edit.Compose.Components beatDivisor.Next(); OnUserChange(Current.Value); return true; + case Key.Left: beatDivisor.Previous(); OnUserChange(Current.Value); return true; + default: return false; } @@ -307,18 +309,25 @@ namespace osu.Game.Screens.Edit.Compose.Components { case 2: return colours.BlueLight; + case 4: return colours.Blue; + case 8: return colours.BlueDarker; + case 16: return colours.PurpleDark; + case 3: return colours.YellowLight; + case 6: return colours.Yellow; + case 12: return colours.YellowDarker; + default: return Color4.White; } diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index 794fc093d3..cc2a0c6c46 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -260,6 +260,7 @@ namespace osu.Game.Screens.Menu box.ScaleTo(new Vector2(0, 1), 500, Easing.OutExpo); this.FadeOut(500); break; + case 1: box.ScaleTo(new Vector2(0, 1), 400, Easing.InSine); this.FadeOut(800); @@ -267,11 +268,13 @@ namespace osu.Game.Screens.Menu } break; + case ButtonState.Expanded: const int expand_duration = 500; box.ScaleTo(new Vector2(1, 1), expand_duration, Easing.OutExpo); this.FadeIn(expand_duration / 6f); break; + case ButtonState.Exploded: const int explode_duration = 200; box.ScaleTo(new Vector2(2, 1), explode_duration, Easing.OutExpo); @@ -294,10 +297,12 @@ namespace osu.Game.Screens.Menu case ButtonSystemState.Initial: State = ButtonState.Contracted; break; + case ButtonSystemState.EnteringMode: ContractStyle = 1; State = ButtonState.Contracted; break; + default: if (value == VisibleState) State = ButtonState.Expanded; diff --git a/osu.Game/Screens/Menu/ButtonArea.cs b/osu.Game/Screens/Menu/ButtonArea.cs index d6e1aef63c..b25efe53e1 100644 --- a/osu.Game/Screens/Menu/ButtonArea.cs +++ b/osu.Game/Screens/Menu/ButtonArea.cs @@ -57,6 +57,7 @@ namespace osu.Game.Screens.Menu case ButtonSystemState.EnteringMode: State = Visibility.Hidden; break; + case ButtonSystemState.TopLevel: case ButtonSystemState.Play: State = Visibility.Visible; @@ -109,6 +110,7 @@ namespace osu.Game.Screens.Menu case ButtonAreaBackgroundState.Flat: this.ScaleTo(new Vector2(2, 0), 300, Easing.InSine); break; + case ButtonAreaBackgroundState.Normal: this.ScaleTo(Vector2.One, 400, Easing.OutQuint); break; @@ -127,6 +129,7 @@ namespace osu.Game.Screens.Menu default: State = ButtonAreaBackgroundState.Normal; break; + case ButtonSystemState.Initial: case ButtonSystemState.Exit: case ButtonSystemState.EnteringMode: diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 5403f7c702..21fc53be6e 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -79,6 +79,7 @@ namespace osu.Game.Screens.Menu case ButtonSystemState.Exit: Background.FadeColour(Color4.White, 500, Easing.OutSine); break; + default: Background.FadeColour(OsuColour.Gray(0.8f), 500, Easing.OutSine); break; diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 2fac8de799..c4425fd5fe 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -221,6 +221,7 @@ namespace osu.Game.Screens.Play else selectionIndex--; return true; + case Key.Down: if (selectionIndex == -1 || selectionIndex == InternalButtons.Count - 1) selectionIndex = 0; @@ -240,6 +241,7 @@ namespace osu.Game.Screens.Play case GlobalAction.Back: BackAction.Invoke(); return true; + case GlobalAction.Select: SelectAction.Invoke(); return true; diff --git a/osu.Game/Screens/Play/KeyCounterMouse.cs b/osu.Game/Screens/Play/KeyCounterMouse.cs index 13dbe40a8b..95fa58e5c0 100644 --- a/osu.Game/Screens/Play/KeyCounterMouse.cs +++ b/osu.Game/Screens/Play/KeyCounterMouse.cs @@ -25,8 +25,10 @@ namespace osu.Game.Screens.Play { default: return button.ToString(); + case MouseButton.Left: return @"M1"; + case MouseButton.Right: return @"M2"; } diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index 78ed742bfa..d2d56f8c40 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -174,6 +174,7 @@ namespace osu.Game.Screens.Play using (BeginDelayedSequence(1000)) scheduledHide = Schedule(() => State = Visibility.Hidden); break; + case Visibility.Hidden: this.FadeOut(1000, Easing.OutExpo); break; diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 8e7ea8f964..5c334b126c 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -38,10 +38,13 @@ namespace osu.Game.Screens.Select.Carousel default: case SortMode.Artist: return string.Compare(BeatmapSet.Metadata.Artist, otherSet.BeatmapSet.Metadata.Artist, StringComparison.InvariantCultureIgnoreCase); + case SortMode.Title: return string.Compare(BeatmapSet.Metadata.Title, otherSet.BeatmapSet.Metadata.Title, StringComparison.InvariantCultureIgnoreCase); + case SortMode.Author: return string.Compare(BeatmapSet.Metadata.Author.Username, otherSet.BeatmapSet.Metadata.Author.Username, StringComparison.InvariantCultureIgnoreCase); + case SortMode.Difficulty: return BeatmapSet.MaxStarDifficulty.CompareTo(otherSet.BeatmapSet.MaxStarDifficulty); } diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs index 5d8f4f0ec6..6ebd2d41cc 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs @@ -66,6 +66,7 @@ namespace osu.Game.Screens.Select.Carousel case CarouselItemState.NotSelected: InternalChildren.ForEach(c => c.State.Value = CarouselItemState.Collapsed); break; + case CarouselItemState.Selected: InternalChildren.ForEach(c => { diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index 67e8282b76..045c682dc3 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -69,6 +69,7 @@ namespace osu.Game.Screens.Select.Carousel case CarouselItemState.Selected: updateSelected(item); break; + case CarouselItemState.NotSelected: case CarouselItemState.Collapsed: attemptSelection(); diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs index 3c1b7cc831..9a07852e02 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs @@ -106,6 +106,7 @@ namespace osu.Game.Screens.Select.Carousel case CarouselItemState.NotSelected: Deselected(); break; + case CarouselItemState.Selected: Selected(); break; diff --git a/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs b/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs index 0bcf1b1816..02f7f73399 100644 --- a/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs +++ b/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs @@ -108,12 +108,14 @@ namespace osu.Game.Screens.Tournament speedTo(1000f, 200); tracker.FadeOut(100); break; + case ScrollState.Stopping: speedTo(0f, 2000); tracker.FadeIn(200); delayedStateChangeDelegate = Scheduler.AddDelayed(() => scrollState = ScrollState.Stopped, 2300); break; + case ScrollState.Stopped: // Find closest to center if (!Children.Any()) @@ -155,6 +157,7 @@ namespace osu.Game.Screens.Tournament delayedStateChangeDelegate = Scheduler.AddDelayed(() => scrollState = ScrollState.Idle, 10000); break; + case ScrollState.Idle: resetSelected(); diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index 461dfed589..09f7e09961 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -26,9 +26,11 @@ namespace osu.Game.Skinning case @"Name": skin.SkinInfo.Name = pair.Value; break; + case @"Author": skin.SkinInfo.Creator = pair.Value; break; + case @"CursorExpand": skin.CursorExpand = pair.Value != "0"; break; @@ -42,6 +44,7 @@ namespace osu.Game.Skinning case "HitCirclePrefix": skin.HitCircleFont = pair.Value; break; + case "HitCircleOverlap": skin.HitCircleOverlap = int.Parse(pair.Value); break; diff --git a/osu.Game/Tests/Visual/ScrollingTestContainer.cs b/osu.Game/Tests/Visual/ScrollingTestContainer.cs index f2e03208fd..bdad3d278c 100644 --- a/osu.Game/Tests/Visual/ScrollingTestContainer.cs +++ b/osu.Game/Tests/Visual/ScrollingTestContainer.cs @@ -74,9 +74,11 @@ namespace osu.Game.Tests.Visual case ScrollVisualisationMethod.Constant: implementation = new ConstantScrollAlgorithm(); break; + case ScrollVisualisationMethod.Overlapping: implementation = new OverlappingScrollAlgorithm(ControlPoints); break; + case ScrollVisualisationMethod.Sequential: implementation = new SequentialScrollAlgorithm(ControlPoints); break; From d8af5e1c5a01df26271c5d9fa50f080ac1660514 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Apr 2019 11:56:22 +0900 Subject: [PATCH 0396/2854] Update in-line with drawnode changes --- .../UI/Cursor/CursorTrail.cs | 76 ++++++++++--------- osu.Game/Graphics/Backgrounds/Triangles.cs | 62 ++++++++------- osu.Game/Rulesets/Mods/ModFlashlight.cs | 61 ++++++++------- osu.Game/Screens/Menu/LogoVisualisation.cs | 61 ++++++++------- 4 files changed, 139 insertions(+), 121 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 03dbf7ac63..4aec7c634e 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -43,22 +43,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private readonly InputResampler resampler = new InputResampler(); - protected override DrawNode CreateDrawNode() => new TrailDrawNode(); - - protected override void ApplyDrawNode(DrawNode node) - { - base.ApplyDrawNode(node); - - TrailDrawNode tNode = (TrailDrawNode)node; - tNode.Shader = shader; - tNode.Texture = texture; - tNode.Size = size; - tNode.Time = time; - - for (int i = 0; i < parts.Length; ++i) - if (parts[i].InvalidationID > tNode.Parts[i].InvalidationID) - tNode.Parts[i] = parts[i]; - } + protected override DrawNode CreateDrawNode() => new TrailDrawNode(this); public CursorTrail() { @@ -167,33 +152,52 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private class TrailDrawNode : DrawNode { - public IShader Shader; - public Texture Texture; + protected new CursorTrail Source => (CursorTrail)base.Source; - public float Time; + private IShader shader; + private Texture texture; - public readonly TrailPart[] Parts = new TrailPart[max_sprites]; - public Vector2 Size; + private float time; + + private readonly TrailPart[] parts = new TrailPart[max_sprites]; + private Vector2 size; private readonly VertexBuffer vertexBuffer = new QuadVertexBuffer(max_sprites, BufferUsageHint.DynamicDraw); - public TrailDrawNode() + public TrailDrawNode(CursorTrail source) + : base(source) { for (int i = 0; i < max_sprites; i++) { - Parts[i].InvalidationID = 0; - Parts[i].WasUpdated = false; + parts[i].InvalidationID = 0; + parts[i].WasUpdated = false; + } + } + + public override void ApplyState() + { + base.ApplyState(); + + shader = Source.shader; + texture = Source.texture; + size = Source.size; + time = Source.time; + + for (int i = 0; i < Source.parts.Length; ++i) + { + if (Source.parts[i].InvalidationID > parts[i].InvalidationID) + parts[i] = Source.parts[i]; } } public override void Draw(Action vertexAction) { - Shader.GetUniform("g_FadeClock").UpdateValue(ref Time); + shader.GetUniform("g_FadeClock").UpdateValue(ref time); int updateStart = -1, updateEnd = 0; - for (int i = 0; i < Parts.Length; ++i) + for (int i = 0; i < parts.Length; ++i) { - if (Parts[i].WasUpdated) + if (parts[i].WasUpdated) { if (updateStart == -1) updateStart = i; @@ -202,22 +206,22 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor int start = i * 4; int end = start; - Vector2 pos = Parts[i].Position; - float time = Parts[i].Time; + Vector2 pos = parts[i].Position; + float localTime = parts[i].Time; - Texture.DrawQuad( - new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y), + texture.DrawQuad( + new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y), DrawColourInfo.Colour, null, v => vertexBuffer.Vertices[end++] = new TexturedTrailVertex { Position = v.Position, TexturePosition = v.TexturePosition, - Time = time + 1, + Time = localTime + 1, Colour = v.Colour, }); - Parts[i].WasUpdated = false; + parts[i].WasUpdated = false; } else if (updateStart != -1) { @@ -232,12 +236,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor base.Draw(vertexAction); - Shader.Bind(); + shader.Bind(); - Texture.TextureGL.Bind(); + texture.TextureGL.Bind(); vertexBuffer.Draw(); - Shader.Unbind(); + shader.Unbind(); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index c67d779c37..e2c7693700 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -178,64 +178,68 @@ namespace osu.Game.Graphics.Backgrounds /// The colour. protected virtual Color4 CreateTriangleShade() => Interpolation.ValueAt(RNG.NextSingle(), ColourDark, ColourLight, 0, 1); - protected override DrawNode CreateDrawNode() => new TrianglesDrawNode(); - - protected override void ApplyDrawNode(DrawNode node) - { - base.ApplyDrawNode(node); - - var trianglesNode = (TrianglesDrawNode)node; - - trianglesNode.Shader = shader; - trianglesNode.Texture = texture; - trianglesNode.Size = DrawSize; - - trianglesNode.Parts.Clear(); - trianglesNode.Parts.AddRange(parts); - } + protected override DrawNode CreateDrawNode() => new TrianglesDrawNode(this); private class TrianglesDrawNode : DrawNode { - public IShader Shader; - public Texture Texture; + protected new Triangles Source => (Triangles)base.Source; - public readonly List Parts = new List(); - public Vector2 Size; + private IShader shader; + private Texture texture; + + private readonly List parts = new List(); + private Vector2 size; private readonly LinearBatch vertexBatch = new LinearBatch(100 * 3, 10, PrimitiveType.Triangles); + public TrianglesDrawNode(Triangles source) + : base(source) + { + } + + public override void ApplyState() + { + base.ApplyState(); + + shader = Source.shader; + texture = Source.texture; + size = Source.DrawSize; + + parts.Clear(); + parts.AddRange(Source.parts); + } + public override void Draw(Action vertexAction) { base.Draw(vertexAction); - Shader.Bind(); - Texture.TextureGL.Bind(); + shader.Bind(); + texture.TextureGL.Bind(); Vector2 localInflationAmount = edge_smoothness * DrawInfo.MatrixInverse.ExtractScale().Xy; - foreach (TriangleParticle particle in Parts) + foreach (TriangleParticle particle in parts) { var offset = triangle_size * new Vector2(particle.Scale * 0.5f, particle.Scale * 0.866f); - var size = new Vector2(2 * offset.X, offset.Y); var triangle = new Triangle( - Vector2Extensions.Transform(particle.Position * Size, DrawInfo.Matrix), - Vector2Extensions.Transform(particle.Position * Size + offset, DrawInfo.Matrix), - Vector2Extensions.Transform(particle.Position * Size + new Vector2(-offset.X, offset.Y), DrawInfo.Matrix) + Vector2Extensions.Transform(particle.Position * size, DrawInfo.Matrix), + Vector2Extensions.Transform(particle.Position * size + offset, DrawInfo.Matrix), + Vector2Extensions.Transform(particle.Position * size + new Vector2(-offset.X, offset.Y), DrawInfo.Matrix) ); ColourInfo colourInfo = DrawColourInfo.Colour; colourInfo.ApplyChild(particle.Colour); - Texture.DrawTriangle( + texture.DrawTriangle( triangle, colourInfo, null, vertexBatch.AddAction, - Vector2.Divide(localInflationAmount, size)); + Vector2.Divide(localInflationAmount, new Vector2(2 * offset.X, offset.Y))); } - Shader.Unbind(); + shader.Unbind(); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 0ad99d13ff..31d3720cee 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -64,24 +64,12 @@ namespace osu.Game.Rulesets.Mods internal BindableInt Combo; private IShader shader; - protected override DrawNode CreateDrawNode() => new FlashlightDrawNode(); + protected override DrawNode CreateDrawNode() => new FlashlightDrawNode(this); public override bool RemoveCompletedTransforms => false; public List Breaks; - protected override void ApplyDrawNode(DrawNode node) - { - base.ApplyDrawNode(node); - - var flashNode = (FlashlightDrawNode)node; - - flashNode.Shader = shader; - flashNode.ScreenSpaceDrawQuad = ScreenSpaceDrawQuad; - flashNode.FlashlightPosition = Vector2Extensions.Transform(FlashlightPosition, DrawInfo.Matrix); - flashNode.FlashlightSize = Vector2Extensions.Transform(FlashlightSize, DrawInfo.Matrix); - } - [BackgroundDependencyLoader] private void load(ShaderManager shaderManager) { @@ -136,27 +124,44 @@ namespace osu.Game.Rulesets.Mods Invalidate(Invalidation.DrawNode); } } - } - private class FlashlightDrawNode : DrawNode - { - public IShader Shader; - public Quad ScreenSpaceDrawQuad; - public Vector2 FlashlightPosition; - public Vector2 FlashlightSize; - - public override void Draw(Action vertexAction) + private class FlashlightDrawNode : DrawNode { - base.Draw(vertexAction); + protected new Flashlight Source => (Flashlight)base.Source; - Shader.Bind(); + private IShader shader; + private Quad screenSpaceDrawQuad; + private Vector2 flashlightPosition; + private Vector2 flashlightSize; - Shader.GetUniform("flashlightPos").UpdateValue(ref FlashlightPosition); - Shader.GetUniform("flashlightSize").UpdateValue(ref FlashlightSize); + public FlashlightDrawNode(Flashlight source) + : base(source) + { + } - Texture.WhitePixel.DrawQuad(ScreenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction); + public override void ApplyState() + { + base.ApplyState(); - Shader.Unbind(); + shader = Source.shader; + screenSpaceDrawQuad = Source.ScreenSpaceDrawQuad; + flashlightPosition = Vector2Extensions.Transform(Source.FlashlightPosition, DrawInfo.Matrix); + flashlightSize = Vector2Extensions.Transform(Source.FlashlightSize, DrawInfo.Matrix); + } + + public override void Draw(Action vertexAction) + { + base.Draw(vertexAction); + + shader.Bind(); + + shader.GetUniform("flashlightPos").UpdateValue(ref flashlightPosition); + shader.GetUniform("flashlightSize").UpdateValue(ref flashlightSize); + + Texture.WhitePixel.DrawQuad(screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction); + + shader.Unbind(); + } } } } diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 8283bf7ea2..9eab588a57 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -150,62 +150,67 @@ namespace osu.Game.Screens.Menu Invalidate(Invalidation.DrawNode, shallPropagate: false); } - protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(); - - protected override void ApplyDrawNode(DrawNode node) - { - base.ApplyDrawNode(node); - - var visNode = (VisualisationDrawNode)node; - - visNode.Shader = shader; - visNode.Texture = texture; - visNode.Size = DrawSize.X; - visNode.Colour = AccentColour; - visNode.AudioData = frequencyAmplitudes; - } + protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(this); private class VisualisationDrawNode : DrawNode { - public IShader Shader; - public Texture Texture; + protected new LogoVisualisation Source => (LogoVisualisation)base.Source; + + private IShader shader; + private Texture texture; //Asuming the logo is a circle, we don't need a second dimension. - public float Size; + private float size; - public Color4 Colour; - public float[] AudioData; + private Color4 colour; + private float[] audioData; private readonly QuadBatch vertexBatch = new QuadBatch(100, 10); + public VisualisationDrawNode(LogoVisualisation source) + : base(source) + { + } + + public override void ApplyState() + { + base.ApplyState(); + + shader = Source.shader; + texture = Source.texture; + size = Source.DrawSize.X; + colour = Source.AccentColour; + audioData = Source.frequencyAmplitudes; + } + public override void Draw(Action vertexAction) { base.Draw(vertexAction); - Shader.Bind(); - Texture.TextureGL.Bind(); + shader.Bind(); + texture.TextureGL.Bind(); Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy; ColourInfo colourInfo = DrawColourInfo.Colour; - colourInfo.ApplyChild(Colour); + colourInfo.ApplyChild(colour); - if (AudioData != null) + if (audioData != null) { for (int j = 0; j < visualiser_rounds; j++) { for (int i = 0; i < bars_per_visualiser; i++) { - if (AudioData[i] < amplitude_dead_zone) + if (audioData[i] < amplitude_dead_zone) continue; float rotation = MathHelper.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds); float rotationCos = (float)Math.Cos(rotation); float rotationSin = (float)Math.Sin(rotation); //taking the cos and sin to the 0..1 range - var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * Size; + var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * size; - var barSize = new Vector2(Size * (float)Math.Sqrt(2 * (1 - Math.Cos(MathHelper.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * AudioData[i]); + var barSize = new Vector2(size * (float)Math.Sqrt(2 * (1 - Math.Cos(MathHelper.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * audioData[i]); //The distance between the position and the sides of the bar. var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2); //The distance between the bottom side of the bar and the top side. @@ -218,7 +223,7 @@ namespace osu.Game.Screens.Menu Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix) ); - Texture.DrawQuad( + texture.DrawQuad( rectangle, colourInfo, null, @@ -229,7 +234,7 @@ namespace osu.Game.Screens.Menu } } - Shader.Unbind(); + shader.Unbind(); } protected override void Dispose(bool isDisposing) From a56e29347fdcdad1618c139b6fe47f1bed276f32 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Apr 2019 14:51:28 +0900 Subject: [PATCH 0397/2854] Adjust namespaces --- osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs | 1 + osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs | 2 +- .../Objects/Drawables/DrawableHoldNoteTick.cs | 1 + osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs | 2 +- osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs | 1 + osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs | 1 + osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs | 1 + osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs | 1 + osu.Game.Rulesets.Mania/UI/HitExplosion.cs | 1 + .../Objects/Drawables/Connections/FollowPoint.cs | 1 + osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs | 1 + .../Objects/Drawables/Pieces/SpinnerBackground.cs | 1 + osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs | 1 + osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs | 1 + osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs | 1 + osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs | 1 + osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 1 + osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 1 + osu.Game/Graphics/Containers/ConstrainedIconContainer.cs | 1 + osu.Game/Graphics/Containers/WaveContainer.cs | 1 + osu.Game/Graphics/Cursor/OsuTooltipContainer.cs | 2 +- osu.Game/Graphics/UserInterface/DialogButton.cs | 1 + osu.Game/Graphics/UserInterface/Nub.cs | 1 + osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs | 1 + osu.Game/Graphics/UserInterface/OsuContextMenu.cs | 2 +- osu.Game/Graphics/UserInterface/TwoLayerButton.cs | 1 + osu.Game/Online/Leaderboards/LeaderboardScore.cs | 1 + osu.Game/Overlays/AccountCreationOverlay.cs | 1 + osu.Game/Overlays/BeatmapSet/AuthorInfo.cs | 1 + osu.Game/Overlays/BeatmapSet/Header.cs | 1 + osu.Game/Overlays/BeatmapSet/Info.cs | 1 + osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs | 1 + osu.Game/Overlays/BeatmapSetOverlay.cs | 1 + osu.Game/Overlays/Chat/ChatLine.cs | 1 + osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 1 + osu.Game/Overlays/Dialog/PopupDialog.cs | 1 + osu.Game/Overlays/Direct/DirectPanel.cs | 1 + osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 1 + osu.Game/Overlays/MedalOverlay.cs | 1 + osu.Game/Overlays/Music/CollectionsDropdown.cs | 2 +- osu.Game/Overlays/Music/PlaylistOverlay.cs | 1 + osu.Game/Overlays/MusicController.cs | 1 + osu.Game/Overlays/Notifications/Notification.cs | 1 + osu.Game/Overlays/OnScreenDisplay.cs | 1 + osu.Game/Overlays/Profile/ProfileHeader.cs | 1 + osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs | 1 + osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 1 + osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs | 1 + osu.Game/Overlays/Settings/SettingsItem.cs | 1 + osu.Game/Overlays/Social/SocialPanel.cs | 2 +- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 1 + osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs | 2 +- osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs | 1 + osu.Game/Overlays/Toolbar/ToolbarUserButton.cs | 2 +- osu.Game/Overlays/UserProfileOverlay.cs | 2 +- .../Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs | 1 + osu.Game/Screens/Menu/Button.cs | 1 + osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs | 1 + osu.Game/Screens/Play/GameplayMenuOverlay.cs | 1 + osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs | 1 + osu.Game/Screens/Ranking/ResultModeButton.cs | 1 + osu.Game/Screens/Ranking/Results.cs | 1 + osu.Game/Screens/Ranking/ResultsPage.cs | 1 + osu.Game/Screens/Select/BeatmapInfoWedge.cs | 1 + osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs | 1 + osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs | 1 + osu.Game/Users/UserPanel.cs | 1 + 67 files changed, 67 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs index 0dc3f73404..625e857156 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.MathUtils; using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs index 2e18c5f2ad..b9b6d5b924 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs @@ -3,7 +3,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osuTK.Graphics; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs index f2be8d614c..9a29273282 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs @@ -7,6 +7,7 @@ using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Scoring; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 82a34224f4..afd7777861 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -5,7 +5,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osuTK.Graphics; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Scoring; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs index 2baf1ad520..b515abcc86 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs @@ -7,6 +7,7 @@ using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs index b146a33fd3..1d25a0c966 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs @@ -4,6 +4,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osuTK.Graphics; diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs index 89e8cd9b5a..a0d713067d 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Rulesets.UI; diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs index 03b55cbead..85880222d7 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs @@ -7,6 +7,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; using osu.Game.Graphics; diff --git a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs index f5a9978f77..0ec1fc38d2 100644 --- a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs @@ -4,6 +4,7 @@ using osuTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.MathUtils; using osu.Game.Rulesets.Mania.Objects.Drawables; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs index 3c64fe57d4..aacf3ee08d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs @@ -6,6 +6,7 @@ using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Skinning; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs index 93ac8748dd..84034d3ee9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs @@ -4,6 +4,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Game.Graphics.Sprites; using osuTK.Graphics; using osu.Framework.Graphics.Shapes; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs index c982f53c2b..77228e28af 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs @@ -4,6 +4,7 @@ using osuTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs index f47617bcf6..9219fab830 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osuTK; using osuTK.Graphics; using osu.Framework.Graphics.Shapes; diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index ba6b27f743..0cc7858f5a 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs index 53dbe5d08e..b7db819717 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs @@ -9,6 +9,7 @@ using osu.Game.Graphics.Backgrounds; using osuTK.Graphics; using osu.Game.Beatmaps.ControlPoints; using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Effects; namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces { diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs index bed2c554ec..e80b463481 100644 --- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs @@ -5,6 +5,7 @@ using osuTK; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index dbff5270d2..88d7f9a751 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index f0f58b9b5d..5bb2767438 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Containers; diff --git a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs index c1811f37d5..f5ef291c8f 100644 --- a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs +++ b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osuTK; namespace osu.Game.Graphics.Containers diff --git a/osu.Game/Graphics/Containers/WaveContainer.cs b/osu.Game/Graphics/Containers/WaveContainer.cs index 48131d7e86..464682a0ad 100644 --- a/osu.Game/Graphics/Containers/WaveContainer.cs +++ b/osu.Game/Graphics/Containers/WaveContainer.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osuTK.Graphics; diff --git a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs index 4e0ce4a3e1..7bb6396041 100644 --- a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs @@ -6,8 +6,8 @@ using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Sprites; diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index dbbe5b4258..b50bf14bab 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics.Effects; using osu.Game.Graphics.Containers; using osu.Framework.Input.Events; diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index 1f5195eaf1..82b09e0821 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; diff --git a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs index d64068f74c..a8041c79fc 100644 --- a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs index c72d11b57e..cea8427296 100644 --- a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs @@ -5,7 +5,7 @@ using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; namespace osu.Game.Graphics.UserInterface { diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs index 9911a7c368..36a9aca412 100644 --- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs +++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Beatmaps.ControlPoints; using osu.Framework.Audio.Track; using System; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index da5cc76060..e4458181f6 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -8,6 +8,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; diff --git a/osu.Game/Overlays/AccountCreationOverlay.cs b/osu.Game/Overlays/AccountCreationOverlay.cs index e8e44c206e..0d376257e0 100644 --- a/osu.Game/Overlays/AccountCreationOverlay.cs +++ b/osu.Game/Overlays/AccountCreationOverlay.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Screens; using osu.Game.Graphics; diff --git a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs index 18de87e7b4..abe954aa80 100644 --- a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs +++ b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs @@ -11,6 +11,7 @@ using osuTK; using osuTK.Graphics; using osu.Game.Graphics.Containers; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 95cf9e9d04..3659769752 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -6,6 +6,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index 4d974a0b63..44827f0a0c 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics; diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs index ac4485a410..44b0d9e4f6 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics; diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index c49268bc16..82bac71f5e 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Beatmaps; diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 908ec5f026..66a6672ab1 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -8,6 +8,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.Containers; diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index e1f29a40e4..5e506f1e4b 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index ede2f34574..a5f5ea37eb 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 2b509f370e..f413dc3771 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -8,6 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 8313dac50a..58d6cd10d2 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -8,6 +8,7 @@ using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; diff --git a/osu.Game/Overlays/MedalOverlay.cs b/osu.Game/Overlays/MedalOverlay.cs index a5703eba92..6d82db5603 100644 --- a/osu.Game/Overlays/MedalOverlay.cs +++ b/osu.Game/Overlays/MedalOverlay.cs @@ -19,6 +19,7 @@ using osu.Framework.Graphics.Textures; using osuTK.Input; using osu.Framework.Graphics.Shapes; using System; +using osu.Framework.Graphics.Effects; using osu.Framework.Input.Events; using osu.Framework.MathUtils; diff --git a/osu.Game/Overlays/Music/CollectionsDropdown.cs b/osu.Game/Overlays/Music/CollectionsDropdown.cs index aa93e349e8..4f59b053b6 100644 --- a/osu.Game/Overlays/Music/CollectionsDropdown.cs +++ b/osu.Game/Overlays/Music/CollectionsDropdown.cs @@ -6,7 +6,7 @@ using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 8cbea63fe3..949090e8b8 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -8,6 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics; diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index ce2137346f..307fac11df 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -10,6 +10,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 7abff9252f..711b3a1eef 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -7,6 +7,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Game.Graphics; using osuTK; using osuTK.Graphics; diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs index 5e45fbf081..9198455bf7 100644 --- a/osu.Game/Overlays/OnScreenDisplay.cs +++ b/osu.Game/Overlays/OnScreenDisplay.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics; using osuTK; using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Transforms; using osu.Framework.Threading; using osu.Game.Configuration; diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 28877c21f0..46b0726123 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -20,6 +20,7 @@ using osu.Game.Overlays.Profile.Components; using osu.Game.Overlays.Profile.Header; using osu.Game.Users; using Humanizer; +using osu.Framework.Graphics.Effects; namespace osu.Game.Overlays.Profile { diff --git a/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs b/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs index a93fefdd75..23fe6e9cd5 100644 --- a/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs +++ b/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics; diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index 3c69082e9d..aeea5118a7 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -7,6 +7,7 @@ using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics; diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index 078c01ce92..0dbc96998a 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -16,6 +16,7 @@ using System.ComponentModel; using osu.Game.Graphics; using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 02e9d48f40..e970ff6211 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -9,6 +9,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; diff --git a/osu.Game/Overlays/Social/SocialPanel.cs b/osu.Game/Overlays/Social/SocialPanel.cs index 738f940484..555527670a 100644 --- a/osu.Game/Overlays/Social/SocialPanel.cs +++ b/osu.Game/Overlays/Social/SocialPanel.cs @@ -5,7 +5,7 @@ using osuTK; using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Input.Events; using osu.Game.Users; diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 71374d5180..2b2b19b73a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -4,6 +4,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs index f729810fbc..87b18ba9f4 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Game.Rulesets; using osuTK.Graphics; diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index d01eab4dab..ebfa6706d4 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Caching; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osuTK; using osuTK.Input; using osuTK.Graphics; diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index 356ffa5180..c9e49a09f4 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -4,7 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Game.Graphics; using osu.Game.Online.API; using osu.Game.Users; diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 48ce055975..0d33a8b9ef 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -5,7 +5,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs index 1ad69afe91..70c0cf623e 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index 794fc093d3..611dd66ab3 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -16,6 +16,7 @@ using osuTK.Input; using osu.Framework.Extensions.Color4Extensions; using osu.Game.Graphics.Containers; using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Beatmaps.ControlPoints; diff --git a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs index dce597b276..6ec8f2bfe5 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs @@ -9,6 +9,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 2fac8de799..ae50e0898a 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -19,6 +19,7 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Input.Bindings; using Humanizer; +using osu.Framework.Graphics.Effects; namespace osu.Game.Screens.Play { diff --git a/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs b/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs index 8f09c2b2bf..315bc27a79 100644 --- a/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Game.Graphics; using osu.Game.Rulesets.Judgements; using osuTK; diff --git a/osu.Game/Screens/Ranking/ResultModeButton.cs b/osu.Game/Screens/Ranking/ResultModeButton.cs index 109d0195db..1383511241 100644 --- a/osu.Game/Screens/Ranking/ResultModeButton.cs +++ b/osu.Game/Screens/Ranking/ResultModeButton.cs @@ -6,6 +6,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osuTK; diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index dafb4c0aad..bebeaee00a 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -8,6 +8,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; using osu.Game.Graphics.Containers; diff --git a/osu.Game/Screens/Ranking/ResultsPage.cs b/osu.Game/Screens/Ranking/ResultsPage.cs index 1b17dda563..8776c599dd 100644 --- a/osu.Game/Screens/Ranking/ResultsPage.cs +++ b/osu.Game/Screens/Ranking/ResultsPage.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics; diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index b2e08aeefd..670b5ca62c 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -22,6 +22,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Rulesets; diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs index 3c1b7cc831..4402b25c9e 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs @@ -7,6 +7,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.MathUtils; diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs index 0f1f49bd85..410102b607 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs @@ -4,6 +4,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 1f62111a4e..fc7a544433 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -16,6 +16,7 @@ using osu.Game.Overlays; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Containers; using osu.Game.Overlays.Profile.Header; From 2ed945605ec06da3c2ff57493a83c4bc953904b3 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 3 Apr 2019 19:57:22 +0900 Subject: [PATCH 0398/2854] Fix Axes.None requirement for FacadeContainer --- .../Visual/TestCaseLogoFacadeContainer.cs | 292 +++++++++++------- osu.Game.Tests/Visual/TestCasePositionAxes.cs | 33 ++ .../Containers/LogoFacadeContainer.cs | 21 +- osu.Game/Screens/Menu/ButtonSystem.cs | 2 + osu.Game/Screens/Play/PlayerLoader.cs | 6 +- 5 files changed, 229 insertions(+), 125 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestCasePositionAxes.cs diff --git a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs index adbf22302b..b4d7976e4a 100644 --- a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Drawing; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -10,10 +11,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Screens; +using osu.Framework.Testing; using osu.Game.Configuration; using osu.Game.Graphics.Containers; -using osu.Game.Screens; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; using osuTK; @@ -21,7 +21,7 @@ using osuTK.Graphics; namespace osu.Game.Tests.Visual { - public class TestCaseLogoFacadeContainer : ScreenTestCase + public class TestCaseLogoFacadeContainer : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { @@ -34,23 +34,38 @@ namespace osu.Game.Tests.Visual typeof(MainMenu) }; - [Cached] private OsuLogo logo; private readonly Bindable uiScale = new Bindable(); + private LogoFacadeContainer logoFacadeContainer; + private Container transferContainer; + private Box visualBox; + private Box transferContainerBox; + private Container logoFacade; - public TestCaseLogoFacadeContainer() - { - Add(logo = new OsuLogo()); - } + private bool randomPositions = false; [BackgroundDependencyLoader] private void load(OsuConfigManager config) { + Add(logo = new OsuLogo { Scale = new Vector2(0.15f), RelativePositionAxes = Axes.None }); + config.BindWith(OsuSetting.UIScale, uiScale); AddSliderStep("Adjust scale", 0.8f, 1.5f, 1f, v => uiScale.Value = v); } + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Clear facades", () => + { + Clear(); + Add(logo = new OsuLogo { Scale = new Vector2(0.15f), RelativePositionAxes = Axes.None }); + logoFacadeContainer = null; + transferContainer = null; + }); + } + /// /// Move the facade to 0,0, then move it to a random new location while the logo is still transforming to it. /// Check if the logo is still tracking the facade. @@ -58,13 +73,11 @@ namespace osu.Game.Tests.Visual [Test] public void MoveFacadeTest() { - TestScreen screen = null; - bool randomPositions = false; AddToggleStep("Toggle move continuously", b => randomPositions = b); - AddStep("Move facade to random position", () => LoadScreen(screen = new TestScreen(randomPositions))); - AddUntilStep("Screen is current", () => screen.IsCurrentScreen()); + AddStep("Add facade containers", addFacadeContainers); + AddStep("Move facade to random position", StartTrackingRandom); waitForMove(); - AddAssert("Logo is tracking", () => screen.IsLogoTracking); + AddAssert("Logo is tracking", () => IsLogoTracking); } /// @@ -73,12 +86,11 @@ namespace osu.Game.Tests.Visual [Test] public void RemoveFacadeTest() { - TestScreen screen = null; - AddStep("Move facade to random position", () => LoadScreen(screen = new TestScreen())); - AddUntilStep("Screen is current", () => screen.IsCurrentScreen()); - AddStep("Remove facade from FacadeContainer", () => screen.RemoveFacade()); + AddStep("Add facade containers", addFacadeContainers); + AddStep("Move facade to random position", StartTrackingRandom); + AddStep("Remove facade from FacadeContainer", RemoveFacade); waitForMove(); - AddAssert("Logo is not tracking", () => !screen.IsLogoTracking); + AddAssert("Logo is not tracking", () => !IsLogoTracking); } /// @@ -87,114 +99,162 @@ namespace osu.Game.Tests.Visual [Test] public void TransferFacadeTest() { - TestScreen screen = null; - AddStep("Move facade to random position", () => LoadScreen(screen = new TestScreen())); - AddUntilStep("Screen is current", () => screen.IsCurrentScreen()); - AddStep("Remove facade from FacadeContainer", () => screen.RemoveFacade()); - AddStep("Transfer facade to a new container", () => screen.TransferFacade()); + AddStep("Add facade containers", addFacadeContainers); + AddStep("Move facade to random position", StartTrackingRandom); + AddStep("Remove facade from FacadeContainer", RemoveFacade); + AddStep("Transfer facade to a new container", TransferFacade); waitForMove(); - AddAssert("Logo is tracking", () => screen.IsLogoTracking); + AddAssert("Logo is tracking", () => IsLogoTracking); + } + + /// + /// Add a facade to a flow container then move logo to facade. + /// + [Test] + public void FlowContainerTest() + { + FillFlowContainer flowContainer; + + AddStep("Create new Logo Facade Container", () => + { + Add(logoFacadeContainer = new LogoFacadeContainer + { + AutoSizeAxes = Axes.Both, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Child = flowContainer = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Direction = FillDirection.Vertical, + } + }); + flowContainer.Children = new Drawable[] + { + new Box + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Colour = Color4.Azure, + Size = new Vector2(70) + }, + new Container + { + Alpha = 0.35f, + RelativeSizeAxes = Axes.None, + Size = new Vector2(72), + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Children = new Drawable[] + { + visualBox = new Box + { + Colour = Color4.White, + RelativeSizeAxes = Axes.Both, + }, + logoFacadeContainer.LogoFacade, + } + }, + new Box + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Colour = Color4.Azure, + Size = new Vector2(70) + }, + }; + }); + + AddStep("Perform logo movements", () => + { + logoFacadeContainer.Tracking = false; + logo.RelativePositionAxes = Axes.Both; + logo.MoveTo(new Vector2(0.5f), 500, Easing.InOutExpo); + logoFacadeContainer.SetLogo(logo, 1.0f, 1000, Easing.InOutExpo); + visualBox.Colour = Color4.White; + + Scheduler.AddDelayed(() => + { + logoFacadeContainer.Tracking = true; + //logo.RelativePositionAxes = Axes.None; + visualBox.Colour = Color4.Tomato; + }, 700); + }); + } + + private void addFacadeContainers() + { + AddRange(new Drawable[] + { + logoFacadeContainer = new LogoFacadeContainer + { + Alpha = 0.35f, + RelativeSizeAxes = Axes.None, + Size = new Vector2(72), + Child = visualBox = new Box + { + Colour = Color4.Tomato, + RelativeSizeAxes = Axes.Both, + } + }, + transferContainer = new Container + { + Alpha = 0.35f, + RelativeSizeAxes = Axes.None, + Size = new Vector2(72), + Child = transferContainerBox = new Box + { + Colour = Color4.White, + RelativeSizeAxes = Axes.Both, + } + }, + }); + + logoFacadeContainer.Add(logoFacade = logoFacadeContainer.LogoFacade); + logoFacadeContainer.SetLogo(logo, 1.0f, 1000); } private void waitForMove() => AddWaitStep("Wait for transforms to finish", 5); - private class TestScreen : OsuScreen + private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(logoFacade.ScreenSpaceDrawQuad.Centre); + + /// + /// Check that the logo is tracking the position of the facade, with an acceptable precision lenience. + /// + public bool IsLogoTracking => Math.Abs(logo.Position.X - logoTrackingPosition.X) < 0.001f && Math.Abs(logo.Position.Y - logoTrackingPosition.Y) < 0.001f; + + public void RemoveFacade() { - private LogoFacadeContainer logoFacadeContainer; - private Container transferContainer; - private Container logoFacade; - private readonly bool randomPositions; - private OsuLogo logo; - private Box visualBox; - private Box transferContainerBox; + logoFacadeContainer.Remove(logoFacade); + visualBox.Colour = Color4.White; + moveLogoFacade(); + } - public TestScreen(bool randomPositions = false) + public void TransferFacade() + { + transferContainer.Add(logoFacade); + transferContainerBox.Colour = Color4.Tomato; + moveLogoFacade(); + } + + public void StartTrackingRandom() + { + logoFacadeContainer.Tracking = true; + moveLogoFacade(); + } + + private void moveLogoFacade() + { + Random random = new Random(); + if (logoFacade.Transforms.Count == 0 && transferContainer.Transforms.Count == 0) { - this.randomPositions = randomPositions; + logoFacadeContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300); + transferContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300); } - [BackgroundDependencyLoader] - private void load() - { - InternalChildren = new Drawable[] - { - logoFacadeContainer = new LogoFacadeContainer - { - Alpha = 0.35f, - RelativeSizeAxes = Axes.None, - Size = new Vector2(72), - Child = visualBox = new Box - { - Colour = Color4.Tomato, - RelativeSizeAxes = Axes.Both, - } - }, - transferContainer = new Container - { - Alpha = 0.35f, - RelativeSizeAxes = Axes.None, - Size = new Vector2(72), - Child = transferContainerBox = new Box - { - Colour = Color4.White, - RelativeSizeAxes = Axes.Both, - } - }, - }; - - logoFacadeContainer.Add(logoFacade = logoFacadeContainer.LogoFacade); - } - - private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(logoFacade.ScreenSpaceDrawQuad.Centre); - - /// - /// Check that the logo is tracking the position of the facade, with an acceptable precision lenience. - /// - public bool IsLogoTracking => Math.Abs(logo.Position.X - logoTrackingPosition.X) < 0.001f && Math.Abs(logo.Position.Y - logoTrackingPosition.Y) < 0.001f; - - public void RemoveFacade() - { - logoFacadeContainer.Remove(logoFacade); - visualBox.Colour = Color4.White; - moveLogoFacade(); - } - - public void TransferFacade() - { - transferContainer.Add(logoFacade); - transferContainerBox.Colour = Color4.Tomato; - moveLogoFacade(); - } - - protected override void LogoArriving(OsuLogo logo, bool resuming) - { - base.LogoArriving(logo, resuming); - this.logo = logo; - logo.FadeIn(350); - logo.ScaleTo(new Vector2(0.15f), 350, Easing.In); - logoFacadeContainer.SetLogo(logo, 1.0f, 1000, Easing.InOutQuint); - logoFacadeContainer.Tracking = true; - moveLogoFacade(); - } - - protected override void LogoExiting(OsuLogo logo) - { - base.LogoExiting(logo); - logoFacadeContainer.Tracking = false; - } - - private void moveLogoFacade() - { - Random random = new Random(); - if (logoFacade.Transforms.Count == 0 && transferContainer.Transforms.Count == 0) - { - logoFacadeContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)DrawWidth), random.Next(0, (int)DrawHeight)), 300); - transferContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)DrawWidth), random.Next(0, (int)DrawHeight)), 300); - } - - if (randomPositions) - Schedule(moveLogoFacade); - } + if (randomPositions) + Schedule(moveLogoFacade); } } } diff --git a/osu.Game.Tests/Visual/TestCasePositionAxes.cs b/osu.Game.Tests/Visual/TestCasePositionAxes.cs new file mode 100644 index 0000000000..04ff0dcf8c --- /dev/null +++ b/osu.Game.Tests/Visual/TestCasePositionAxes.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; +using osuTK; + +namespace osu.Game.Tests.Visual +{ + public class TestCasePositionAxes : OsuTestCase + { + private Box box; + + public TestCasePositionAxes() + { + Add(new Container() + { + Size = new Vector2(1), + Child = box = new Box + { + Size = new Vector2(25), + RelativePositionAxes = Axes.None, + Position = new Vector2(250) + } + }); + + AddStep("blank", () => { }); + AddStep("change axes", () => box.RelativePositionAxes = Axes.Both); + } + } +} diff --git a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs index 547af87522..c9f41e75e7 100644 --- a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs +++ b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs @@ -43,7 +43,7 @@ namespace osu.Game.Graphics.Containers /// The scale of the facade. Does not actually affect the logo itself. /// The duration of the initial transform. Default is instant. /// The easing type of the initial transform. - public void SetLogo(OsuLogo logo, float facadeScale, double duration = 0, Easing easing = Easing.None) + public void SetLogo(OsuLogo logo, float facadeScale = 1.0f, double duration = 0, Easing easing = Easing.None) { this.logo = logo ?? throw new ArgumentNullException(nameof(logo)); this.facadeScale = facadeScale; @@ -54,11 +54,19 @@ namespace osu.Game.Graphics.Containers startPosition = null; } - private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre); - - protected override void UpdateAfterChildren() + private Vector2 localSpaceConversion() { - base.UpdateAfterChildren(); + Vector2 local; + local.X = logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre).X / logo.Parent.RelativeToAbsoluteFactor.X; + local.Y = logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre).Y / logo.Parent.RelativeToAbsoluteFactor.Y; + return local; + } + + private Vector2 logoTrackingPosition => logo.RelativePositionAxes == Axes.None ? logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre) : localSpaceConversion(); + + protected override void Update() + { + base.Update(); if (logo == null || !Tracking) return; @@ -68,9 +76,6 @@ namespace osu.Game.Graphics.Containers if (LogoFacade.Parent != null && logo.Position != logoTrackingPosition) { - // Required for the correct position of the logo to be set with respect to logoTrackingPosition - logo.RelativePositionAxes = Axes.None; - // If this is our first update since tracking has started, initialize our starting values for interpolation if (startTime == null || startPosition == null) { diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index f13dadf3dd..607700649d 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -290,6 +290,7 @@ namespace osu.Game.Screens.Menu break; case ButtonSystemState.Initial: logo.ClearTransforms(targetMember: nameof(Position)); + logo.RelativePositionAxes = Axes.None; bool impact = logo.Scale.X > 0.6f; @@ -310,6 +311,7 @@ namespace osu.Game.Screens.Menu break; default: logo.ClearTransforms(targetMember: nameof(Position)); + logo.RelativePositionAxes = Axes.None; logoFacadeContainer.Tracking = true; logo.ScaleTo(0.5f, 200, Easing.OutQuint); break; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 9e6f7cf24e..19607129a0 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -172,7 +172,11 @@ namespace osu.Game.Screens.Play content.SetLogo(logo, 1.0f, 500, Easing.InOutExpo); - Scheduler.AddDelayed(() => content.Tracking = true, resuming ? 0 : 500); + Scheduler.AddDelayed(() => + { + content.Tracking = true; + //logo.RelativePositionAxes = Axes.None; + }, resuming ? 0 : 500); } protected override void LogoExiting(OsuLogo logo) From 8a40b27e8fc3cf9b17069439e8af130a72d7564e Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 3 Apr 2019 20:32:53 +0900 Subject: [PATCH 0399/2854] Remove need for logo relativePositionAxes none --- osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs | 1 - osu.Game/Graphics/Containers/LogoFacadeContainer.cs | 10 ++++------ osu.Game/Screens/Menu/ButtonSystem.cs | 2 -- osu.Game/Screens/Play/PlayerLoader.cs | 1 - 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs index b4d7976e4a..bd1fb932bb 100644 --- a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs @@ -177,7 +177,6 @@ namespace osu.Game.Tests.Visual Scheduler.AddDelayed(() => { logoFacadeContainer.Tracking = true; - //logo.RelativePositionAxes = Axes.None; visualBox.Colour = Color4.Tomato; }, 700); }); diff --git a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs index c9f41e75e7..76903a20b3 100644 --- a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs +++ b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs @@ -54,7 +54,7 @@ namespace osu.Game.Graphics.Containers startPosition = null; } - private Vector2 localSpaceConversion() + private Vector2 logoTrackingPosition() { Vector2 local; local.X = logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre).X / logo.Parent.RelativeToAbsoluteFactor.X; @@ -62,8 +62,6 @@ namespace osu.Game.Graphics.Containers return local; } - private Vector2 logoTrackingPosition => logo.RelativePositionAxes == Axes.None ? logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre) : localSpaceConversion(); - protected override void Update() { base.Update(); @@ -74,7 +72,7 @@ namespace osu.Game.Graphics.Containers // Account for the scale of the actual logo container, as SizeForFlow only accounts for the sprite scale. LogoFacade.Size = new Vector2(logo.SizeForFlow * logo.Scale.X * facadeScale); - if (LogoFacade.Parent != null && logo.Position != logoTrackingPosition) + if (LogoFacade.Parent != null && logo.Position != logoTrackingPosition()) { // If this is our first update since tracking has started, initialize our starting values for interpolation if (startTime == null || startPosition == null) @@ -90,11 +88,11 @@ namespace osu.Game.Graphics.Containers var mount = (float)Interpolation.ApplyEasing(easing, Math.Min(elapsedDuration / duration, 1)); // Interpolate the position of the logo, where mount 0 is where the logo was when it first began interpolating, and mount 1 is the target location. - logo.Position = Vector2.Lerp(startPosition ?? throw new ArgumentNullException(nameof(startPosition)), logoTrackingPosition, mount); + logo.Position = Vector2.Lerp(startPosition ?? throw new ArgumentNullException(nameof(startPosition)), logoTrackingPosition(), mount); } else { - logo.Position = logoTrackingPosition; + logo.Position = logoTrackingPosition(); } } } diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 607700649d..f13dadf3dd 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -290,7 +290,6 @@ namespace osu.Game.Screens.Menu break; case ButtonSystemState.Initial: logo.ClearTransforms(targetMember: nameof(Position)); - logo.RelativePositionAxes = Axes.None; bool impact = logo.Scale.X > 0.6f; @@ -311,7 +310,6 @@ namespace osu.Game.Screens.Menu break; default: logo.ClearTransforms(targetMember: nameof(Position)); - logo.RelativePositionAxes = Axes.None; logoFacadeContainer.Tracking = true; logo.ScaleTo(0.5f, 200, Easing.OutQuint); break; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 19607129a0..2ba5bd81d1 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -175,7 +175,6 @@ namespace osu.Game.Screens.Play Scheduler.AddDelayed(() => { content.Tracking = true; - //logo.RelativePositionAxes = Axes.None; }, resuming ? 0 : 500); } From ccc804a9b2ac61e4f90448fd265dac4ec1db568f Mon Sep 17 00:00:00 2001 From: jorolf Date: Thu, 4 Apr 2019 00:24:42 +0200 Subject: [PATCH 0400/2854] get everything working again --- .../{ => Online}/TestCaseUserProfileHeader.cs | 4 +-- .../Graphics/UserInterface/ScreenTitle.cs | 6 +++-- .../Profile/Header/BottomHeaderContainer.cs | 18 ++++++------- .../Profile/Header/CenterHeaderContainer.cs | 6 ++--- .../Profile/Header/ProfileMessageButton.cs | 8 +++--- .../Overlays/Profile/Header/SupporterIcon.cs | 5 ++-- osu.Game/Overlays/Profile/ProfileHeader.cs | 27 +++++++++++++------ osu.Game/Screens/Multi/Header.cs | 2 +- osu.Game/Users/UserCoverBackground.cs | 7 ++--- osu.Game/Users/UserPanel.cs | 2 +- 10 files changed, 49 insertions(+), 36 deletions(-) rename osu.Game.Tests/Visual/{ => Online}/TestCaseUserProfileHeader.cs (96%) diff --git a/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs similarity index 96% rename from osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs rename to osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs index 8f36b4dda8..98bad9831f 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs @@ -11,7 +11,7 @@ using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile.Header; using osu.Game.Users; -namespace osu.Game.Tests.Visual +namespace osu.Game.Tests.Visual.Online { public class TestCaseUserProfileHeader : OsuTestCase { @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual }; [Resolved] - private APIAccess api { get; set; } + private IAPIProvider api { get; set; } private readonly ProfileHeader header; diff --git a/osu.Game/Graphics/UserInterface/ScreenTitle.cs b/osu.Game/Graphics/UserInterface/ScreenTitle.cs index 1574023068..b9d9b5427d 100644 --- a/osu.Game/Graphics/UserInterface/ScreenTitle.cs +++ b/osu.Game/Graphics/UserInterface/ScreenTitle.cs @@ -14,6 +14,8 @@ namespace osu.Game.Graphics.UserInterface { private readonly SpriteIcon iconSprite; private readonly OsuSpriteText titleText, pageText; + public const float ICON_WIDTH = icon_size + icon_spacing; + private const float icon_size = 25, icon_spacing = 10; protected IconUsage Icon { @@ -48,12 +50,12 @@ namespace osu.Game.Graphics.UserInterface new FillFlowContainer { AutoSizeAxes = Axes.Both, - Spacing = new Vector2(10, 0), + Spacing = new Vector2(icon_spacing, 0), Children = new Drawable[] { iconSprite = new SpriteIcon { - Size = new Vector2(25), + Size = new Vector2(icon_size), }, new FillFlowContainer { diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index 1f9d741b8d..04b70ea10f 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -107,7 +107,7 @@ namespace osu.Game.Overlays.Profile.Header bottomTopLinkContainer.AddText("Contributed "); bottomTopLinkContainer.AddLink($@"{user.PostCount:#,##0} forum posts", $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: bold); - void tryAddInfo(FontAwesome icon, string content, string link = null) + void tryAddInfo(IconUsage icon, string content, string link = null) { if (string.IsNullOrEmpty(content)) return; @@ -138,16 +138,16 @@ namespace osu.Game.Overlays.Profile.Header websiteWithoutProtcol = websiteWithoutProtcol.Substring(protocolIndex + 2); } - tryAddInfo(FontAwesome.fa_map_marker, user.Location); - tryAddInfo(FontAwesome.fa_heart_o, user.Interests); - tryAddInfo(FontAwesome.fa_suitcase, user.Occupation); + tryAddInfo(FontAwesome.Solid.MapMarker, user.Location); + tryAddInfo(OsuIcon.Heart, user.Interests); + tryAddInfo(FontAwesome.Solid.Suitcase, user.Occupation); bottomLinkContainer.NewLine(); if (!string.IsNullOrEmpty(user.Twitter)) - tryAddInfo(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}"); - tryAddInfo(FontAwesome.fa_gamepad, user.Discord); //todo: update fontawesome to include discord logo - tryAddInfo(FontAwesome.fa_skype, user.Skype, @"skype:" + user.Skype + @"?chat"); - tryAddInfo(FontAwesome.fa_lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}"); - tryAddInfo(FontAwesome.fa_link, websiteWithoutProtcol, user.Website); + tryAddInfo(FontAwesome.Brands.Twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}"); + tryAddInfo(FontAwesome.Brands.Discord, user.Discord); + tryAddInfo(FontAwesome.Brands.Skype, user.Skype, @"skype:" + user.Skype + @"?chat"); + tryAddInfo(FontAwesome.Brands.Lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}"); + tryAddInfo(FontAwesome.Solid.Link, websiteWithoutProtcol, user.Website); } } } diff --git a/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs index b5e04bee9e..3d0b9b9db4 100644 --- a/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs @@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Profile.Header { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Icon = FontAwesome.fa_user, + Icon = FontAwesome.Solid.User, FillMode = FillMode.Fit, Size = new Vector2(50, 14) }, @@ -116,7 +116,7 @@ namespace osu.Game.Overlays.Profile.Header Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(20), - Icon = FontAwesome.fa_chevron_up, + Icon = FontAwesome.Solid.ChevronUp, }, } }, @@ -212,7 +212,7 @@ namespace osu.Game.Overlays.Profile.Header detailsToggleButton.Action = () => { detailsVisible = !detailsVisible; - expandButtonIcon.Icon = detailsVisible ? FontAwesome.fa_chevron_down : FontAwesome.fa_chevron_up; + expandButtonIcon.Icon = detailsVisible ? FontAwesome.Solid.ChevronDown : FontAwesome.Solid.ChevronUp; hiddenDetailContainer.Alpha = detailsVisible ? 1 : 0; expandedDetailContainer.Alpha = detailsVisible ? 0 : 1; DetailsVisibilityAction(detailsVisible); diff --git a/osu.Game/Overlays/Profile/Header/ProfileMessageButton.cs b/osu.Game/Overlays/Profile/Header/ProfileMessageButton.cs index a14c0324d7..3121eae727 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileMessageButton.cs +++ b/osu.Game/Overlays/Profile/Header/ProfileMessageButton.cs @@ -4,7 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Graphics; +using osu.Framework.Graphics.Sprites; using osu.Game.Online.API; using osu.Game.Online.Chat; using osu.Game.Users; @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.Profile.Header private ChatOverlay chatOverlay { get; set; } [Resolved] - private APIAccess apiAccess { get; set; } + private IAPIProvider apiProvider { get; set; } public ProfileMessageButton() { @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.Profile.Header { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Icon = FontAwesome.fa_envelope, + Icon = FontAwesome.Solid.Envelope, FillMode = FillMode.Fit, Size = new Vector2(50, 14) }; @@ -51,7 +51,7 @@ namespace osu.Game.Overlays.Profile.Header chatOverlay?.Show(); }; - User.ValueChanged += e => Content.Alpha = !e.NewValue.PMFriendsOnly && apiAccess.LocalUser.Value.Id != e.NewValue.Id ? 1 : 0; + User.ValueChanged += e => Content.Alpha = !e.NewValue.PMFriendsOnly && apiProvider.LocalUser.Value.Id != e.NewValue.Id ? 1 : 0; } } } diff --git a/osu.Game/Overlays/Profile/Header/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/SupporterIcon.cs index 3cb30ab044..569ed252b7 100644 --- a/osu.Game/Overlays/Profile/Header/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/SupporterIcon.cs @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.Profile.Header { Width = 12, RelativeSizeAxes = Axes.Y, - Icon = FontAwesome.fa_heart, + Icon = FontAwesome.Solid.Heart, }); } @@ -66,8 +66,7 @@ namespace osu.Game.Overlays.Profile.Header AutoSizeAxes = Axes.X, Height = 0.6f, Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = FontAwesome.Solid.Heart, + Origin = Anchor.Centre } } }; diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 988fa3afd9..b75de39c4b 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -5,11 +5,11 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Textures; using osu.Game.Graphics; using osu.Game.Overlays.Profile.Header; using osu.Game.Users; using osu.Framework.Bindables; +using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Profile @@ -17,7 +17,6 @@ namespace osu.Game.Overlays.Profile public class ProfileHeader : Container { private readonly UserCoverBackground coverContainer; - private readonly ScreenTitle coverTitle; private readonly ProfileHeaderTabControl infoTabControl; private const float cover_height = 150; @@ -52,10 +51,9 @@ namespace osu.Game.Overlays.Profile Depth = -float.MaxValue, Children = new Drawable[] { - coverTitle = new ScreenTitle + new ProfileHeaderTitle { - Title = "Player ", - Page = "Info" + X = -ScreenTitle.ICON_WIDTH, }, infoTabControl = new ProfileHeaderTabControl { @@ -113,10 +111,8 @@ namespace osu.Game.Overlays.Profile } [BackgroundDependencyLoader] - private void load(OsuColour colours, TextureStore textures) + private void load(OsuColour colours) { - coverTitle.AccentColour = colours.CommunityUserGreen; - infoTabControl.AccentColour = colours.CommunityUserGreen; } @@ -131,5 +127,20 @@ namespace osu.Game.Overlays.Profile { public string TooltipText { get; set; } } + + private class ProfileHeaderTitle : ScreenTitle + { + public ProfileHeaderTitle() + { + Title = "Player "; + Section = "Info"; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.CommunityUserGreen; + } + } } } diff --git a/osu.Game/Screens/Multi/Header.cs b/osu.Game/Screens/Multi/Header.cs index 7924086389..35edc5bac8 100644 --- a/osu.Game/Screens/Multi/Header.cs +++ b/osu.Game/Screens/Multi/Header.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.Multi { Anchor = Anchor.CentreLeft, Origin = Anchor.BottomLeft, - X = -35, + X = -ScreenTitle.ICON_WIDTH, }, breadcrumbs = new HeaderBreadcrumbControl(stack) { diff --git a/osu.Game/Users/UserCoverBackground.cs b/osu.Game/Users/UserCoverBackground.cs index d037f809b4..dbc132995a 100644 --- a/osu.Game/Users/UserCoverBackground.cs +++ b/osu.Game/Users/UserCoverBackground.cs @@ -36,15 +36,16 @@ namespace osu.Game.Users } else { - return new Sprite + var sprite = new Sprite { RelativeSizeAxes = Axes.Both, Texture = textures.Get(user.CoverUrl), FillMode = FillMode.Fill, Anchor = Anchor.Centre, - Origin = Anchor.Centre, - OnLoadComplete = d => d.FadeInFromZero(400), + Origin = Anchor.Centre }; + sprite.OnLoadComplete += d => d.FadeInFromZero(400); + return sprite; } } } diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 0e928e0aac..eb7f0941fb 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -76,7 +76,7 @@ namespace osu.Game.Users Children = new Drawable[] { - new DelayedLoadWrapper(coverBackground = new UserCoverBackground(user) + new DelayedLoadWrapper(coverBackground = new UserCoverBackground { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, From ba871364514ae468bc5e0a47a12c4adda5206dc0 Mon Sep 17 00:00:00 2001 From: jorolf Date: Thu, 4 Apr 2019 00:57:15 +0200 Subject: [PATCH 0401/2854] add gradient and fix remaining error --- .../Visual/Online/TestCaseUserProfile.cs | 10 +++------- osu.Game/Overlays/Profile/ProfileHeader.cs | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/Online/TestCaseUserProfile.cs index abb96b8e84..c455c092ed 100644 --- a/osu.Game.Tests/Visual/Online/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/Online/TestCaseUserProfile.cs @@ -20,7 +20,9 @@ namespace osu.Game.Tests.Visual.Online public class TestCaseUserProfile : OsuTestCase { private readonly TestUserProfileOverlay profile; - private IAPIProvider api; + + [Resolved] + private IAPIProvider api { get; set; } public override IReadOnlyList RequiredTypes => new[] { @@ -75,12 +77,6 @@ namespace osu.Game.Tests.Visual.Online Add(profile = new TestUserProfileOverlay()); } - [BackgroundDependencyLoader] - private void load(APIAccess api) - { - this.api = api; - } - protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index b75de39c4b..776fcbb8b7 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -9,6 +9,9 @@ using osu.Game.Graphics; using osu.Game.Overlays.Profile.Header; using osu.Game.Users; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -35,10 +38,23 @@ namespace osu.Game.Overlays.Profile Children = new Drawable[] { - coverContainer = new UserCoverBackground + new Container { RelativeSizeAxes = Axes.X, Height = cover_height, + Masking = true, + Children = new Drawable[] + { + coverContainer = new UserCoverBackground + { + RelativeSizeAxes = Axes.Both, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(OsuColour.FromHex("222").Opacity(0.8f), OsuColour.FromHex("222").Opacity(0.2f)) + }, + } }, new Container { From 6b5458a625840759eb178278a88f423532359081 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 4 Apr 2019 11:22:05 +0900 Subject: [PATCH 0402/2854] Clean up test cases --- .../Visual/TestCaseLogoFacadeContainer.cs | 92 +++++++++---------- osu.Game.Tests/Visual/TestCasePositionAxes.cs | 33 ------- .../Containers/LogoFacadeContainer.cs | 23 ++--- 3 files changed, 57 insertions(+), 91 deletions(-) delete mode 100644 osu.Game.Tests/Visual/TestCasePositionAxes.cs diff --git a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs index bd1fb932bb..d66ac19aed 100644 --- a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Drawing; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -35,21 +34,18 @@ namespace osu.Game.Tests.Visual }; private OsuLogo logo; - private readonly Bindable uiScale = new Bindable(); - private LogoFacadeContainer logoFacadeContainer; + private TestLogoFacadeContainer facadeContainer; private Container transferContainer; private Box visualBox; private Box transferContainerBox; private Container logoFacade; - private bool randomPositions = false; + private bool randomPositions; [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - Add(logo = new OsuLogo { Scale = new Vector2(0.15f), RelativePositionAxes = Axes.None }); - config.BindWith(OsuSetting.UIScale, uiScale); AddSliderStep("Adjust scale", 0.8f, 1.5f, 1f, v => uiScale.Value = v); } @@ -60,8 +56,8 @@ namespace osu.Game.Tests.Visual AddStep("Clear facades", () => { Clear(); - Add(logo = new OsuLogo { Scale = new Vector2(0.15f), RelativePositionAxes = Axes.None }); - logoFacadeContainer = null; + Add(logo = new OsuLogo { Scale = new Vector2(0.15f), RelativePositionAxes = Axes.Both }); + facadeContainer = null; transferContainer = null; }); } @@ -75,9 +71,9 @@ namespace osu.Game.Tests.Visual { AddToggleStep("Toggle move continuously", b => randomPositions = b); AddStep("Add facade containers", addFacadeContainers); - AddStep("Move facade to random position", StartTrackingRandom); + AddStep("Move facade to random position", startTrackingRandom); waitForMove(); - AddAssert("Logo is tracking", () => IsLogoTracking); + AddAssert("Logo is tracking", () => facadeContainer.IsLogoTracking); } /// @@ -87,10 +83,10 @@ namespace osu.Game.Tests.Visual public void RemoveFacadeTest() { AddStep("Add facade containers", addFacadeContainers); - AddStep("Move facade to random position", StartTrackingRandom); - AddStep("Remove facade from FacadeContainer", RemoveFacade); + AddStep("Move facade to random position", startTrackingRandom); + AddStep("Remove facade from FacadeContainer", removeFacade); waitForMove(); - AddAssert("Logo is not tracking", () => !IsLogoTracking); + AddAssert("Logo is not tracking", () => !facadeContainer.IsLogoTracking); } /// @@ -100,11 +96,16 @@ namespace osu.Game.Tests.Visual public void TransferFacadeTest() { AddStep("Add facade containers", addFacadeContainers); - AddStep("Move facade to random position", StartTrackingRandom); - AddStep("Remove facade from FacadeContainer", RemoveFacade); - AddStep("Transfer facade to a new container", TransferFacade); + AddStep("Move facade to random position", startTrackingRandom); + AddStep("Remove facade from FacadeContainer", removeFacade); + AddStep("Transfer facade to a new container", () => + { + transferContainer.Add(logoFacade); + transferContainerBox.Colour = Color4.Tomato; + moveLogoFacade(); + }); waitForMove(); - AddAssert("Logo is tracking", () => IsLogoTracking); + AddAssert("Logo is tracking", () => facadeContainer.IsLogoTracking); } /// @@ -115,9 +116,9 @@ namespace osu.Game.Tests.Visual { FillFlowContainer flowContainer; - AddStep("Create new Logo Facade Container", () => + AddStep("Create new flow container with facade", () => { - Add(logoFacadeContainer = new LogoFacadeContainer + Add(facadeContainer = new TestLogoFacadeContainer { AutoSizeAxes = Axes.Both, Origin = Anchor.TopCentre, @@ -153,7 +154,7 @@ namespace osu.Game.Tests.Visual Colour = Color4.White, RelativeSizeAxes = Axes.Both, }, - logoFacadeContainer.LogoFacade, + facadeContainer.LogoFacade, } }, new Box @@ -168,25 +169,28 @@ namespace osu.Game.Tests.Visual AddStep("Perform logo movements", () => { - logoFacadeContainer.Tracking = false; + facadeContainer.Tracking = false; logo.RelativePositionAxes = Axes.Both; logo.MoveTo(new Vector2(0.5f), 500, Easing.InOutExpo); - logoFacadeContainer.SetLogo(logo, 1.0f, 1000, Easing.InOutExpo); + facadeContainer.SetLogo(logo, 1.0f, 1000, Easing.InOutExpo); visualBox.Colour = Color4.White; Scheduler.AddDelayed(() => { - logoFacadeContainer.Tracking = true; + facadeContainer.Tracking = true; visualBox.Colour = Color4.Tomato; }, 700); }); + + waitForMove(); + AddAssert("Logo is tracking", () => facadeContainer.IsLogoTracking); } private void addFacadeContainers() { AddRange(new Drawable[] { - logoFacadeContainer = new LogoFacadeContainer + facadeContainer = new TestLogoFacadeContainer { Alpha = 0.35f, RelativeSizeAxes = Axes.None, @@ -210,50 +214,44 @@ namespace osu.Game.Tests.Visual }, }); - logoFacadeContainer.Add(logoFacade = logoFacadeContainer.LogoFacade); - logoFacadeContainer.SetLogo(logo, 1.0f, 1000); + facadeContainer.Add(logoFacade = facadeContainer.LogoFacade); + facadeContainer.SetLogo(logo, 1.0f, 1000); } private void waitForMove() => AddWaitStep("Wait for transforms to finish", 5); - private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(logoFacade.ScreenSpaceDrawQuad.Centre); - - /// - /// Check that the logo is tracking the position of the facade, with an acceptable precision lenience. - /// - public bool IsLogoTracking => Math.Abs(logo.Position.X - logoTrackingPosition.X) < 0.001f && Math.Abs(logo.Position.Y - logoTrackingPosition.Y) < 0.001f; - - public void RemoveFacade() + private void removeFacade() { - logoFacadeContainer.Remove(logoFacade); + facadeContainer.Remove(logoFacade); visualBox.Colour = Color4.White; moveLogoFacade(); } - public void TransferFacade() + private void startTrackingRandom() { - transferContainer.Add(logoFacade); - transferContainerBox.Colour = Color4.Tomato; - moveLogoFacade(); - } - - public void StartTrackingRandom() - { - logoFacadeContainer.Tracking = true; + facadeContainer.Tracking = true; moveLogoFacade(); } private void moveLogoFacade() { - Random random = new Random(); - if (logoFacade.Transforms.Count == 0 && transferContainer.Transforms.Count == 0) + if (logoFacade?.Transforms.Count == 0 && transferContainer?.Transforms.Count == 0) { - logoFacadeContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300); + Random random = new Random(); + facadeContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300); transferContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300); } if (randomPositions) Schedule(moveLogoFacade); } + + private class TestLogoFacadeContainer : LogoFacadeContainer + { + /// + /// Check that the logo is tracking the position of the facade, with an acceptable precision lenience. + /// + public bool IsLogoTracking => Math.Abs(Logo.Position.X - LogoTrackingPosition().X) < 0.001f && Math.Abs(Logo.Position.Y - LogoTrackingPosition().Y) < 0.001f; + } } } diff --git a/osu.Game.Tests/Visual/TestCasePositionAxes.cs b/osu.Game.Tests/Visual/TestCasePositionAxes.cs deleted file mode 100644 index 04ff0dcf8c..0000000000 --- a/osu.Game.Tests/Visual/TestCasePositionAxes.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Testing; -using osuTK; - -namespace osu.Game.Tests.Visual -{ - public class TestCasePositionAxes : OsuTestCase - { - private Box box; - - public TestCasePositionAxes() - { - Add(new Container() - { - Size = new Vector2(1), - Child = box = new Box - { - Size = new Vector2(25), - RelativePositionAxes = Axes.None, - Position = new Vector2(250) - } - }); - - AddStep("blank", () => { }); - AddStep("change axes", () => box.RelativePositionAxes = Axes.Both); - } - } -} diff --git a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs index 76903a20b3..a80b687057 100644 --- a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs +++ b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs @@ -24,7 +24,8 @@ namespace osu.Game.Graphics.Containers /// public bool Tracking = false; - private OsuLogo logo; + protected OsuLogo Logo; + private float facadeScale; private Easing easing; private Vector2? startPosition; @@ -45,7 +46,7 @@ namespace osu.Game.Graphics.Containers /// The easing type of the initial transform. public void SetLogo(OsuLogo logo, float facadeScale = 1.0f, double duration = 0, Easing easing = Easing.None) { - this.logo = logo ?? throw new ArgumentNullException(nameof(logo)); + Logo = logo ?? throw new ArgumentNullException(nameof(logo)); this.facadeScale = facadeScale; this.duration = duration; this.easing = easing; @@ -54,11 +55,11 @@ namespace osu.Game.Graphics.Containers startPosition = null; } - private Vector2 logoTrackingPosition() + protected Vector2 LogoTrackingPosition() { Vector2 local; - local.X = logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre).X / logo.Parent.RelativeToAbsoluteFactor.X; - local.Y = logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre).Y / logo.Parent.RelativeToAbsoluteFactor.Y; + local.X = Logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre).X / Logo.Parent.RelativeToAbsoluteFactor.X; + local.Y = Logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre).Y / Logo.Parent.RelativeToAbsoluteFactor.Y; return local; } @@ -66,19 +67,19 @@ namespace osu.Game.Graphics.Containers { base.Update(); - if (logo == null || !Tracking) + if (Logo == null || !Tracking) return; // Account for the scale of the actual logo container, as SizeForFlow only accounts for the sprite scale. - LogoFacade.Size = new Vector2(logo.SizeForFlow * logo.Scale.X * facadeScale); + LogoFacade.Size = new Vector2(Logo.SizeForFlow * Logo.Scale.X * facadeScale); - if (LogoFacade.Parent != null && logo.Position != logoTrackingPosition()) + if (LogoFacade.Parent != null && Logo.Position != LogoTrackingPosition()) { // If this is our first update since tracking has started, initialize our starting values for interpolation if (startTime == null || startPosition == null) { startTime = Time.Current; - startPosition = logo.Position; + startPosition = Logo.Position; } if (duration != 0) @@ -88,11 +89,11 @@ namespace osu.Game.Graphics.Containers var mount = (float)Interpolation.ApplyEasing(easing, Math.Min(elapsedDuration / duration, 1)); // Interpolate the position of the logo, where mount 0 is where the logo was when it first began interpolating, and mount 1 is the target location. - logo.Position = Vector2.Lerp(startPosition ?? throw new ArgumentNullException(nameof(startPosition)), logoTrackingPosition(), mount); + Logo.Position = Vector2.Lerp(startPosition ?? throw new ArgumentNullException(nameof(startPosition)), LogoTrackingPosition(), mount); } else { - logo.Position = logoTrackingPosition(); + Logo.Position = LogoTrackingPosition(); } } } From 15b2b6af7d8c8da6c371b14ff4c571b1c284d423 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 4 Apr 2019 11:28:36 +0900 Subject: [PATCH 0403/2854] Clean up remaining assignments of logo relativePositionAxes --- osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs | 1 - osu.Game/Screens/Menu/ButtonSystem.cs | 2 -- osu.Game/Screens/Play/PlayerLoader.cs | 5 +---- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs index d66ac19aed..3e9d52483d 100644 --- a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs @@ -170,7 +170,6 @@ namespace osu.Game.Tests.Visual AddStep("Perform logo movements", () => { facadeContainer.Tracking = false; - logo.RelativePositionAxes = Axes.Both; logo.MoveTo(new Vector2(0.5f), 500, Easing.InOutExpo); facadeContainer.SetLogo(logo, 1.0f, 1000, Easing.InOutExpo); visualBox.Colour = Color4.White; diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index f13dadf3dd..cb95ec1765 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -276,8 +276,6 @@ namespace osu.Game.Screens.Menu game?.Toolbar.Hide(); logo.ClearTransforms(targetMember: nameof(Position)); - logo.RelativePositionAxes = Axes.Both; - logo.MoveTo(new Vector2(0.5f), 800, Easing.OutExpo); logo.ScaleTo(1, 800, Easing.OutExpo); }, buttonArea.Alpha * 150); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 2ba5bd81d1..ba27f87ee6 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -131,12 +131,9 @@ namespace osu.Game.Screens.Play private void contentOut() { - // Ensure the logo is no longer tracking before we scale the content, and that its RelativePositionAxes have been returned. + // Ensure the logo is no longer tracking before we scale the content content.Tracking = false; - if (logo != null) - logo.RelativePositionAxes = Axes.Both; - content.ScaleTo(0.7f, 300, Easing.InQuint); content.FadeOut(250); } From e89143d76b94610bf88c3a733e64e340f97713e5 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 4 Apr 2019 12:07:11 +0900 Subject: [PATCH 0404/2854] Add xmldoc for LogoTrackingPosition --- osu.Game/Graphics/Containers/LogoFacadeContainer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs index a80b687057..1664fdde4d 100644 --- a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs +++ b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs @@ -55,6 +55,12 @@ namespace osu.Game.Graphics.Containers startPosition = null; } + /// + /// Gets the position that the logo should move to with respect to the . + /// Manually performs a conversion of the Facade's position to the relative position of the Logo's Parent. + /// + /// Will only be correct if the logo's are set to Axes.Both + /// The position that the logo should move to in its parent's relative space. protected Vector2 LogoTrackingPosition() { Vector2 local; From b2857384b8f3c83be474b17cca9c7cc90342cf4e Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 4 Apr 2019 12:08:05 +0900 Subject: [PATCH 0405/2854] Remove unnecessary logo assignment --- osu.Game/Screens/Play/PlayerLoader.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index ba27f87ee6..ccfd965f53 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -37,8 +37,6 @@ namespace osu.Game.Screens.Play private BeatmapMetadataDisplay info; - private OsuLogo logo; - private bool hideOverlays; public override bool HideOverlaysOnEnter => hideOverlays; @@ -155,8 +153,6 @@ namespace osu.Game.Screens.Play { base.LogoArriving(logo, resuming); - this.logo = logo; - const double duration = 300; if (!resuming) From f2bbde83bf3788957ffb3ad7b7e35b1f2e4c9e6d Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 4 Apr 2019 13:05:34 +0900 Subject: [PATCH 0406/2854] Use precision almost equals --- .../{ => UserInterface}/TestCaseLogoFacadeContainer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename osu.Game.Tests/Visual/{ => UserInterface}/TestCaseLogoFacadeContainer.cs (97%) diff --git a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoFacadeContainer.cs similarity index 97% rename from osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs rename to osu.Game.Tests/Visual/UserInterface/TestCaseLogoFacadeContainer.cs index 3e9d52483d..4bc4ae724a 100644 --- a/osu.Game.Tests/Visual/TestCaseLogoFacadeContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoFacadeContainer.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; +using osu.Framework.MathUtils; using osu.Framework.Testing; using osu.Game.Configuration; using osu.Game.Graphics.Containers; @@ -18,7 +19,7 @@ using osu.Game.Screens.Play; using osuTK; using osuTK.Graphics; -namespace osu.Game.Tests.Visual +namespace osu.Game.Tests.Visual.UserInterface { public class TestCaseLogoFacadeContainer : OsuTestCase { @@ -250,7 +251,7 @@ namespace osu.Game.Tests.Visual /// /// Check that the logo is tracking the position of the facade, with an acceptable precision lenience. /// - public bool IsLogoTracking => Math.Abs(Logo.Position.X - LogoTrackingPosition().X) < 0.001f && Math.Abs(Logo.Position.Y - LogoTrackingPosition().Y) < 0.001f; + public bool IsLogoTracking => Precision.AlmostEquals(Logo.Position, LogoTrackingPosition()); } } } From b2e932dc74c5eae1184172758b3a62789d23e719 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 4 Apr 2019 13:13:03 +0900 Subject: [PATCH 0407/2854] Clean up tests, xmldoc --- .../UserInterface/TestCaseLogoFacadeContainer.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoFacadeContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoFacadeContainer.cs index 4bc4ae724a..5f864eeabb 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoFacadeContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoFacadeContainer.cs @@ -41,9 +41,10 @@ namespace osu.Game.Tests.Visual.UserInterface private Box visualBox; private Box transferContainerBox; private Container logoFacade; - private bool randomPositions; + private const float visual_box_size = 72; + [BackgroundDependencyLoader] private void load(OsuConfigManager config) { @@ -110,7 +111,7 @@ namespace osu.Game.Tests.Visual.UserInterface } /// - /// Add a facade to a flow container then move logo to facade. + /// Add a facade to a flow container, move the logo to the center of the screen, then start tracking on the facade. /// [Test] public void FlowContainerTest() @@ -139,13 +140,13 @@ namespace osu.Game.Tests.Visual.UserInterface Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, Colour = Color4.Azure, - Size = new Vector2(70) + Size = new Vector2(visual_box_size) }, new Container { Alpha = 0.35f, RelativeSizeAxes = Axes.None, - Size = new Vector2(72), + Size = new Vector2(visual_box_size), Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, Children = new Drawable[] @@ -163,7 +164,7 @@ namespace osu.Game.Tests.Visual.UserInterface Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, Colour = Color4.Azure, - Size = new Vector2(70) + Size = new Vector2(visual_box_size) }, }; }); @@ -194,7 +195,7 @@ namespace osu.Game.Tests.Visual.UserInterface { Alpha = 0.35f, RelativeSizeAxes = Axes.None, - Size = new Vector2(72), + Size = new Vector2(visual_box_size), Child = visualBox = new Box { Colour = Color4.Tomato, @@ -205,7 +206,7 @@ namespace osu.Game.Tests.Visual.UserInterface { Alpha = 0.35f, RelativeSizeAxes = Axes.None, - Size = new Vector2(72), + Size = new Vector2(visual_box_size), Child = transferContainerBox = new Box { Colour = Color4.White, From 456459cafa91f49981dd085e43312d10e9dc1255 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 4 Apr 2019 13:25:24 +0900 Subject: [PATCH 0408/2854] Give flow container test long enough to finish --- .../Visual/UserInterface/TestCaseLogoFacadeContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoFacadeContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoFacadeContainer.cs index 5f864eeabb..0b664a310a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoFacadeContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoFacadeContainer.cs @@ -183,7 +183,7 @@ namespace osu.Game.Tests.Visual.UserInterface }, 700); }); - waitForMove(); + waitForMove(8); AddAssert("Logo is tracking", () => facadeContainer.IsLogoTracking); } @@ -219,7 +219,7 @@ namespace osu.Game.Tests.Visual.UserInterface facadeContainer.SetLogo(logo, 1.0f, 1000); } - private void waitForMove() => AddWaitStep("Wait for transforms to finish", 5); + private void waitForMove(int count = 5) => AddWaitStep("Wait for transforms to finish", count); private void removeFacade() { From 6f5e9fe50d3976de9c1d686bce6e14c75ada64f8 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 4 Apr 2019 13:33:13 +0900 Subject: [PATCH 0409/2854] Correct xmldoc --- osu.Game/Graphics/Containers/LogoFacadeContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs index 1664fdde4d..c37588cb55 100644 --- a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs +++ b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs @@ -57,7 +57,7 @@ namespace osu.Game.Graphics.Containers /// /// Gets the position that the logo should move to with respect to the . - /// Manually performs a conversion of the Facade's position to the relative position of the Logo's Parent. + /// Manually performs a conversion of the Facade's position to the Logo's parent's relative space. /// /// Will only be correct if the logo's are set to Axes.Both /// The position that the logo should move to in its parent's relative space. From 7047f305a1a7af92532d8902e4490f96119d23a9 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 5 Apr 2019 12:02:47 +0900 Subject: [PATCH 0410/2854] Apply reviews, add safety for multiple facades --- ...er.cs => TestCaseLogoTrackingContainer.cs} | 71 ++++---- .../Containers/LogoFacadeContainer.cs | 114 ------------- .../Containers/LogoTrackingContainer.cs | 156 ++++++++++++++++++ osu.Game/Screens/Menu/ButtonSystem.cs | 24 +-- osu.Game/Screens/Menu/OsuLogo.cs | 6 + osu.Game/Screens/Play/PlayerLoader.cs | 23 +-- 6 files changed, 223 insertions(+), 171 deletions(-) rename osu.Game.Tests/Visual/UserInterface/{TestCaseLogoFacadeContainer.cs => TestCaseLogoTrackingContainer.cs} (77%) delete mode 100644 osu.Game/Graphics/Containers/LogoFacadeContainer.cs create mode 100644 osu.Game/Graphics/Containers/LogoTrackingContainer.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoFacadeContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs similarity index 77% rename from osu.Game.Tests/Visual/UserInterface/TestCaseLogoFacadeContainer.cs rename to osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs index 0b664a310a..4d255eb25a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoFacadeContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs @@ -21,13 +21,13 @@ using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface { - public class TestCaseLogoFacadeContainer : OsuTestCase + public class TestCaseLogoTrackingContainer : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(PlayerLoader), typeof(Player), - typeof(LogoFacadeContainer), + typeof(LogoTrackingContainer), typeof(ButtonSystem), typeof(ButtonSystemState), typeof(Menu), @@ -36,11 +36,11 @@ namespace osu.Game.Tests.Visual.UserInterface private OsuLogo logo; private readonly Bindable uiScale = new Bindable(); - private TestLogoFacadeContainer facadeContainer; + private TestLogoTrackingContainer trackingContainer; private Container transferContainer; private Box visualBox; private Box transferContainerBox; - private Container logoFacade; + private Drawable logoFacade; private bool randomPositions; private const float visual_box_size = 72; @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.UserInterface { Clear(); Add(logo = new OsuLogo { Scale = new Vector2(0.15f), RelativePositionAxes = Axes.Both }); - facadeContainer = null; + trackingContainer = null; transferContainer = null; }); } @@ -72,10 +72,10 @@ namespace osu.Game.Tests.Visual.UserInterface public void MoveFacadeTest() { AddToggleStep("Toggle move continuously", b => randomPositions = b); - AddStep("Add facade containers", addFacadeContainers); + AddStep("Add tracking containers", addFacadeContainers); AddStep("Move facade to random position", startTrackingRandom); waitForMove(); - AddAssert("Logo is tracking", () => facadeContainer.IsLogoTracking); + AddAssert("Logo is tracking", () => trackingContainer.IsLogoTracking); } /// @@ -84,11 +84,11 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void RemoveFacadeTest() { - AddStep("Add facade containers", addFacadeContainers); + AddStep("Add tracking containers", addFacadeContainers); AddStep("Move facade to random position", startTrackingRandom); AddStep("Remove facade from FacadeContainer", removeFacade); waitForMove(); - AddAssert("Logo is not tracking", () => !facadeContainer.IsLogoTracking); + AddAssert("Logo is not tracking", () => !trackingContainer.IsLogoTracking); } /// @@ -97,17 +97,12 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TransferFacadeTest() { - AddStep("Add facade containers", addFacadeContainers); + AddStep("Add tracking containers", addFacadeContainers); AddStep("Move facade to random position", startTrackingRandom); AddStep("Remove facade from FacadeContainer", removeFacade); - AddStep("Transfer facade to a new container", () => - { - transferContainer.Add(logoFacade); - transferContainerBox.Colour = Color4.Tomato; - moveLogoFacade(); - }); + AddStep("Transfer facade to a new container", addFacadeToNewContainer); waitForMove(); - AddAssert("Logo is tracking", () => facadeContainer.IsLogoTracking); + AddAssert("Logo is tracking", () => trackingContainer.IsLogoTracking); } /// @@ -120,7 +115,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Create new flow container with facade", () => { - Add(facadeContainer = new TestLogoFacadeContainer + Add(trackingContainer = new TestLogoTrackingContainer { AutoSizeAxes = Axes.Both, Origin = Anchor.TopCentre, @@ -156,7 +151,7 @@ namespace osu.Game.Tests.Visual.UserInterface Colour = Color4.White, RelativeSizeAxes = Axes.Both, }, - facadeContainer.LogoFacade, + trackingContainer.LogoFacade, } }, new Box @@ -171,27 +166,34 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Perform logo movements", () => { - facadeContainer.Tracking = false; + trackingContainer.Tracking = false; logo.MoveTo(new Vector2(0.5f), 500, Easing.InOutExpo); - facadeContainer.SetLogo(logo, 1.0f, 1000, Easing.InOutExpo); + trackingContainer.SetLogo(logo, 1.0f, 1000, Easing.InOutExpo); visualBox.Colour = Color4.White; Scheduler.AddDelayed(() => { - facadeContainer.Tracking = true; + trackingContainer.Tracking = true; visualBox.Colour = Color4.Tomato; }, 700); }); waitForMove(8); - AddAssert("Logo is tracking", () => facadeContainer.IsLogoTracking); + AddAssert("Logo is tracking", () => trackingContainer.IsLogoTracking); + } + + [Test] + public void SetFacadeSizeTest() + { + AddStep("Add tracking containers", addFacadeContainers); + AddStep("Break shit", () => { logoFacade.Size = new Vector2(0, 0); }); } private void addFacadeContainers() { AddRange(new Drawable[] { - facadeContainer = new TestLogoFacadeContainer + trackingContainer = new TestLogoTrackingContainer { Alpha = 0.35f, RelativeSizeAxes = Axes.None, @@ -215,22 +217,29 @@ namespace osu.Game.Tests.Visual.UserInterface }, }); - facadeContainer.Add(logoFacade = facadeContainer.LogoFacade); - facadeContainer.SetLogo(logo, 1.0f, 1000); + trackingContainer.Add(logoFacade = trackingContainer.LogoFacade); + trackingContainer.SetLogo(logo, 1.0f, 1000); } private void waitForMove(int count = 5) => AddWaitStep("Wait for transforms to finish", count); private void removeFacade() { - facadeContainer.Remove(logoFacade); + trackingContainer.Remove(logoFacade); visualBox.Colour = Color4.White; moveLogoFacade(); } private void startTrackingRandom() { - facadeContainer.Tracking = true; + trackingContainer.Tracking = true; + moveLogoFacade(); + } + + private void addFacadeToNewContainer() + { + transferContainer.Add(logoFacade); + transferContainerBox.Colour = Color4.Tomato; moveLogoFacade(); } @@ -239,7 +248,7 @@ namespace osu.Game.Tests.Visual.UserInterface if (logoFacade?.Transforms.Count == 0 && transferContainer?.Transforms.Count == 0) { Random random = new Random(); - facadeContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300); + trackingContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300); transferContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300); } @@ -247,12 +256,12 @@ namespace osu.Game.Tests.Visual.UserInterface Schedule(moveLogoFacade); } - private class TestLogoFacadeContainer : LogoFacadeContainer + private class TestLogoTrackingContainer : LogoTrackingContainer { /// /// Check that the logo is tracking the position of the facade, with an acceptable precision lenience. /// - public bool IsLogoTracking => Precision.AlmostEquals(Logo.Position, LogoTrackingPosition()); + public bool IsLogoTracking => Precision.AlmostEquals(Logo.Position, LogoTrackingPosition); } } } diff --git a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs b/osu.Game/Graphics/Containers/LogoFacadeContainer.cs deleted file mode 100644 index c37588cb55..0000000000 --- a/osu.Game/Graphics/Containers/LogoFacadeContainer.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.MathUtils; -using osu.Game.Screens.Menu; -using osuTK; - -namespace osu.Game.Graphics.Containers -{ - /// - /// A container that creates a to be used to update and track the position of an . - /// - public class LogoFacadeContainer : Container - { - protected virtual Facade CreateFacade() => new Facade(); - - public Facade LogoFacade { get; } - - /// - /// Whether or not the logo assigned to this FacadeContainer should be tracking the position its facade. - /// - public bool Tracking = false; - - protected OsuLogo Logo; - - private float facadeScale; - private Easing easing; - private Vector2? startPosition; - private double? startTime; - private double duration; - - public LogoFacadeContainer() - { - LogoFacade = CreateFacade(); - } - - /// - /// Assign the logo that should track the Facade's position, as well as how it should transform to its initial position. - /// - /// The instance of the logo to be used for tracking. - /// The scale of the facade. Does not actually affect the logo itself. - /// The duration of the initial transform. Default is instant. - /// The easing type of the initial transform. - public void SetLogo(OsuLogo logo, float facadeScale = 1.0f, double duration = 0, Easing easing = Easing.None) - { - Logo = logo ?? throw new ArgumentNullException(nameof(logo)); - this.facadeScale = facadeScale; - this.duration = duration; - this.easing = easing; - - startTime = null; - startPosition = null; - } - - /// - /// Gets the position that the logo should move to with respect to the . - /// Manually performs a conversion of the Facade's position to the Logo's parent's relative space. - /// - /// Will only be correct if the logo's are set to Axes.Both - /// The position that the logo should move to in its parent's relative space. - protected Vector2 LogoTrackingPosition() - { - Vector2 local; - local.X = Logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre).X / Logo.Parent.RelativeToAbsoluteFactor.X; - local.Y = Logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre).Y / Logo.Parent.RelativeToAbsoluteFactor.Y; - return local; - } - - protected override void Update() - { - base.Update(); - - if (Logo == null || !Tracking) - return; - - // Account for the scale of the actual logo container, as SizeForFlow only accounts for the sprite scale. - LogoFacade.Size = new Vector2(Logo.SizeForFlow * Logo.Scale.X * facadeScale); - - if (LogoFacade.Parent != null && Logo.Position != LogoTrackingPosition()) - { - // If this is our first update since tracking has started, initialize our starting values for interpolation - if (startTime == null || startPosition == null) - { - startTime = Time.Current; - startPosition = Logo.Position; - } - - if (duration != 0) - { - double elapsedDuration = Time.Current - startTime ?? throw new ArgumentNullException(nameof(startTime)); - - var mount = (float)Interpolation.ApplyEasing(easing, Math.Min(elapsedDuration / duration, 1)); - - // Interpolate the position of the logo, where mount 0 is where the logo was when it first began interpolating, and mount 1 is the target location. - Logo.Position = Vector2.Lerp(startPosition ?? throw new ArgumentNullException(nameof(startPosition)), LogoTrackingPosition(), mount); - } - else - { - Logo.Position = LogoTrackingPosition(); - } - } - } - - /// - /// A placeholder container that serves as a dummy object to denote another object's location and size. - /// - public class Facade : Container - { - } - } -} diff --git a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs new file mode 100644 index 0000000000..afb30f981b --- /dev/null +++ b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs @@ -0,0 +1,156 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.MathUtils; +using osu.Game.Screens.Menu; +using osuTK; + +namespace osu.Game.Graphics.Containers +{ + /// + /// A container that handles tracking of an through different layout scenarios + /// + public class LogoTrackingContainer : Container + { + public Facade LogoFacade { get; } + + /// + /// Whether or not the logo assigned to this FacadeContainer should be tracking the position of its facade. + /// + public bool Tracking { get; set; } + + protected OsuLogo Logo; + + private float facadeScale; + private Easing easing; + private Vector2? startPosition; + private double? startTime; + private double duration; + + public LogoTrackingContainer() + { + LogoFacade = new ExposedFacade(); + } + + /// + /// Assign the logo that should track the Facade's position, as well as how it should transform to its initial position. + /// + /// The instance of the logo to be used for tracking. + /// The scale of the facade. Does not actually affect the logo itself. + /// The duration of the initial transform. Default is instant. + /// The easing type of the initial transform. + public void SetLogo(OsuLogo logo, float facadeScale = 1.0f, double duration = 0, Easing easing = Easing.None) + { + if (Logo != logo) + { + if (logo?.HasTrackingContainer ?? throw new ArgumentNullException(nameof(logo))) + { + // Prevent the same logo from being added to multiple LogoTrackingContainers. + throw new InvalidOperationException($"Cannot add an instance of {typeof(OsuLogo)} to multiple {typeof(LogoTrackingContainer)}s"); + } + + if (Logo != null) + { + // If we're replacing the logo to be tracked, the old one no longer has a tracking container + Logo.HasTrackingContainer = false; + } + } + + Logo = logo; + Logo.HasTrackingContainer = true; + this.facadeScale = facadeScale; + this.duration = duration; + this.easing = easing; + + startTime = null; + startPosition = null; + } + + /// + /// Gets the position that the logo should move to with respect to the . + /// Manually performs a conversion of the Facade's position to the Logo's parent's relative space. + /// + /// Will only be correct if the logo's are set to Axes.Both + protected Vector2 LogoTrackingPosition => new Vector2(Logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre).X / Logo.Parent.RelativeToAbsoluteFactor.X, + Logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre).Y / Logo.Parent.RelativeToAbsoluteFactor.Y); + + protected override void Update() + { + base.Update(); + + if (Logo == null || !Tracking) + return; + + // Account for the scale of the actual OsuLogo, as SizeForFlow only accounts for the sprite scale. + ((ExposedFacade)LogoFacade).SetSize(new Vector2(Logo.SizeForFlow * Logo.Scale.X * facadeScale)); + + if (LogoFacade.Parent != null && Logo.Position != LogoTrackingPosition && Logo.RelativePositionAxes == Axes.Both) + { + // If this is our first update since tracking has started, initialize our starting values for interpolation + if (startTime == null || startPosition == null) + { + startTime = Time.Current; + startPosition = Logo.Position; + } + + if (duration != 0) + { + double elapsedDuration = (double)(Time.Current - startTime); + + var amount = (float)Interpolation.ApplyEasing(easing, Math.Min(elapsedDuration / duration, 1)); + + // Interpolate the position of the logo, where amount 0 is where the logo was when it first began interpolating, and amount 1 is the target location. + Logo.Position = Vector2.Lerp((Vector2)startPosition, LogoTrackingPosition, amount); + } + else + { + Logo.Position = LogoTrackingPosition; + } + } + } + + protected override void Dispose(bool isDisposing) + { + if (Logo != null) + Logo.HasTrackingContainer = false; + + base.Dispose(isDisposing); + } + + private Vector2 size; + + private class ExposedFacade : Facade + { + public override Vector2 Size + { + get => base.Size; + set => throw new InvalidOperationException($"Cannot set the Size of a {typeof(Facade)} outside of a {typeof(LogoTrackingContainer)}"); + } + + public void SetSize(Vector2 size) + { + base.SetSize(size); + } + } + + /// + /// A dummy object used to denote another object's location. + /// + public abstract class Facade : Drawable + { + public override Vector2 Size + { + get => base.Size; + set => throw new InvalidOperationException($"Cannot set the Size of a {typeof(Facade)} outside of a {typeof(LogoTrackingContainer)}"); + } + + protected void SetSize(Vector2 size) + { + base.Size = size; + } + } + } +} diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index cb95ec1765..a359893418 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Menu if (this.logo != null) { this.logo.Action = onOsuLogo; - logoFacadeContainer.SetLogo(logo, 0.74f); + logoTrackingContainer.SetLogo(logo, 0.74f); // osuLogo.SizeForFlow relies on loading to be complete. buttonArea.Flow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0); @@ -70,7 +70,7 @@ namespace osu.Game.Screens.Menu else { // We should stop tracking as the facade is now out of scope. - logoFacadeContainer.Tracking = false; + logoTrackingContainer.Tracking = false; } } @@ -83,29 +83,29 @@ namespace osu.Game.Screens.Menu private SampleChannel sampleBack; - private readonly LogoFacadeContainer logoFacadeContainer; + private readonly LogoTrackingContainer logoTrackingContainer; public ButtonSystem() { RelativeSizeAxes = Axes.Both; - Child = logoFacadeContainer = new LogoFacadeContainer + Child = logoTrackingContainer = new LogoTrackingContainer { RelativeSizeAxes = Axes.Both, Child = buttonArea = new ButtonArea() }; - buttonArea.AddRange(new Container[] + buttonArea.AddRange(new Drawable[] { new Button(@"settings", string.Empty, FontAwesome.Gear, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O), backButton = new Button(@"back", @"button-back-select", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, -WEDGE_WIDTH) { VisibleState = ButtonSystemState.Play, }, - logoFacadeContainer.LogoFacade + logoTrackingContainer.LogoFacade }); - buttonArea.Flow.CentreTarget = logoFacadeContainer.LogoFacade; + buttonArea.Flow.CentreTarget = logoTrackingContainer.LogoFacade; } [Resolved(CanBeNull = true)] @@ -271,7 +271,7 @@ namespace osu.Game.Screens.Menu logoDelayedAction?.Cancel(); logoDelayedAction = Scheduler.AddDelayed(() => { - logoFacadeContainer.Tracking = false; + logoTrackingContainer.Tracking = false; game?.Toolbar.Hide(); @@ -294,8 +294,8 @@ namespace osu.Game.Screens.Menu if (lastState == ButtonSystemState.Initial) logo.ScaleTo(0.5f, 200, Easing.In); - logoFacadeContainer.SetLogo(logo, 0.74f, lastState == ButtonSystemState.EnteringMode ? 0 : 200, Easing.In); - logoFacadeContainer.Tracking = true; + logoTrackingContainer.SetLogo(logo, 0.74f, lastState == ButtonSystemState.EnteringMode ? 0 : 200, Easing.In); + logoTrackingContainer.Tracking = true; logoDelayedAction?.Cancel(); logoDelayedAction = Scheduler.AddDelayed(() => @@ -308,14 +308,14 @@ namespace osu.Game.Screens.Menu break; default: logo.ClearTransforms(targetMember: nameof(Position)); - logoFacadeContainer.Tracking = true; + logoTrackingContainer.Tracking = true; logo.ScaleTo(0.5f, 200, Easing.OutQuint); break; } break; case ButtonSystemState.EnteringMode: - logoFacadeContainer.Tracking = true; + logoTrackingContainer.Tracking = true; break; } } diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index c54ccd21b5..ad70d34637 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -54,8 +54,14 @@ namespace osu.Game.Screens.Menu /// public Func Action; + /// + /// The size of the logo Sprite with respect to the scale of its hover and bounce containers. + /// + /// Does not account for the scale of this public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * logoHoverContainer.Scale.X; + public bool HasTrackingContainer { get; set; } + private readonly Sprite ripple; private readonly Container rippleContainer; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index ccfd965f53..f7feb3535d 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Play private Player player; - private LogoFacadeContainer content; + private LogoTrackingContainer content; private BeatmapMetadataDisplay info; @@ -60,14 +60,12 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load() { - InternalChild = content = new LogoFacadeContainer + InternalChild = (content = new LogoTrackingContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - }; - - content.Children = new Drawable[] + }).WithChildren(new Drawable[] { info = new BeatmapMetadataDisplay(Beatmap.Value, content.LogoFacade) { @@ -89,7 +87,7 @@ namespace osu.Game.Screens.Play new InputSettings() } } - }; + }); loadNewPlayer(); } @@ -163,12 +161,9 @@ namespace osu.Game.Screens.Play logo.ScaleTo(new Vector2(0.15f), duration, Easing.In); logo.FadeIn(350); - content.SetLogo(logo, 1.0f, 500, Easing.InOutExpo); + content.SetLogo(logo, 1.0f, resuming ? 0 : 500, Easing.InOutExpo); - Scheduler.AddDelayed(() => - { - content.Tracking = true; - }, resuming ? 0 : 500); + Scheduler.AddDelayed(() => { content.Tracking = true; }, resuming ? 0 : 500); } protected override void LogoExiting(OsuLogo logo) @@ -321,7 +316,7 @@ namespace osu.Game.Screens.Play } private readonly WorkingBeatmap beatmap; - private readonly Container facade; + private readonly Drawable facade; private LoadingAnimation loading; private Sprite backgroundSprite; private ModDisplay modDisplay; @@ -343,7 +338,7 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Container facade) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Drawable facade) { this.beatmap = beatmap; this.facade = facade; @@ -366,7 +361,7 @@ namespace osu.Game.Screens.Play Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, Direction = FillDirection.Vertical, - Children = new Drawable[] + Children = new[] { facade, new OsuSpriteText From b1d74e57e58fc175fed9061dabed5f8e6d2e9642 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 5 Apr 2019 13:56:08 +0900 Subject: [PATCH 0411/2854] Add checks guarding against setting tracking on multiple trackingcongtainers and setting facade size --- .../Containers/LogoTrackingContainer.cs | 48 ++++++++++--------- osu.Game/Screens/Menu/OsuLogo.cs | 2 +- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs index afb30f981b..ee6d5dec20 100644 --- a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs +++ b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Reflection.Metadata.Ecma335; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; @@ -20,7 +21,24 @@ namespace osu.Game.Graphics.Containers /// /// Whether or not the logo assigned to this FacadeContainer should be tracking the position of its facade. /// - public bool Tracking { get; set; } + public bool Tracking + { + get => tracking; + set + { + if (Logo != null) + { + if (value && !tracking && Logo.IsTracking) + throw new InvalidOperationException($"Cannot track an instance of {typeof(OsuLogo)} to multiple {typeof(LogoTrackingContainer)}s"); + + Logo.IsTracking = value; + } + + tracking = value; + } + } + + private bool tracking; protected OsuLogo Logo; @@ -44,23 +62,15 @@ namespace osu.Game.Graphics.Containers /// The easing type of the initial transform. public void SetLogo(OsuLogo logo, float facadeScale = 1.0f, double duration = 0, Easing easing = Easing.None) { - if (Logo != logo) + if (Logo != logo && Logo != null) { - if (logo?.HasTrackingContainer ?? throw new ArgumentNullException(nameof(logo))) - { - // Prevent the same logo from being added to multiple LogoTrackingContainers. - throw new InvalidOperationException($"Cannot add an instance of {typeof(OsuLogo)} to multiple {typeof(LogoTrackingContainer)}s"); - } - - if (Logo != null) - { - // If we're replacing the logo to be tracked, the old one no longer has a tracking container - Logo.HasTrackingContainer = false; - } + // If we're replacing the logo to be tracked, the old one no longer has a tracking container + Logo.IsTracking = false; } - Logo = logo; - Logo.HasTrackingContainer = true; + Logo = logo ?? throw new ArgumentNullException(nameof(logo)); + Logo.IsTracking = Tracking; + this.facadeScale = facadeScale; this.duration = duration; this.easing = easing; @@ -115,7 +125,7 @@ namespace osu.Game.Graphics.Containers protected override void Dispose(bool isDisposing) { if (Logo != null) - Logo.HasTrackingContainer = false; + Tracking = false; base.Dispose(isDisposing); } @@ -124,12 +134,6 @@ namespace osu.Game.Graphics.Containers private class ExposedFacade : Facade { - public override Vector2 Size - { - get => base.Size; - set => throw new InvalidOperationException($"Cannot set the Size of a {typeof(Facade)} outside of a {typeof(LogoTrackingContainer)}"); - } - public void SetSize(Vector2 size) { base.SetSize(size); diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index ad70d34637..4631f4e222 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Menu /// Does not account for the scale of this public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * logoHoverContainer.Scale.X; - public bool HasTrackingContainer { get; set; } + public bool IsTracking { get; set; } private readonly Sprite ripple; From 5fa93f4a050263be0a2a546bcbfbd1bbc97e2771 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 5 Apr 2019 15:05:11 +0900 Subject: [PATCH 0412/2854] Add test for checking exception --- .../UserInterface/TestCaseLogoTrackingContainer.cs | 12 +++++++++++- .../Graphics/Containers/LogoTrackingContainer.cs | 11 +++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs index 4d255eb25a..3b57d892ad 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs @@ -186,7 +186,17 @@ namespace osu.Game.Tests.Visual.UserInterface public void SetFacadeSizeTest() { AddStep("Add tracking containers", addFacadeContainers); - AddStep("Break shit", () => { logoFacade.Size = new Vector2(0, 0); }); + AddStep("Break stuff", () => { logoFacade.Size = new Vector2(0, 0); }); + } + + [Test] + public void SetMultipleContainers() + { + LogoTrackingContainer newContainer = new LogoTrackingContainer(); + AddStep("Add tracking containers", addFacadeContainers); + AddStep("Move facade to random position", startTrackingRandom); + AddStep("Add logo to new container", () => newContainer.SetLogo(logo)); + AddStep("Break stuff", () => newContainer.Tracking = true); } private void addFacadeContainers() diff --git a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs index ee6d5dec20..ab6f7ebc2a 100644 --- a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs +++ b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs @@ -12,7 +12,7 @@ using osuTK; namespace osu.Game.Graphics.Containers { /// - /// A container that handles tracking of an through different layout scenarios + /// A container that handles tracking of an through different layout scenarios. /// public class LogoTrackingContainer : Container { @@ -38,8 +38,6 @@ namespace osu.Game.Graphics.Containers } } - private bool tracking; - protected OsuLogo Logo; private float facadeScale; @@ -47,6 +45,7 @@ namespace osu.Game.Graphics.Containers private Vector2? startPosition; private double? startTime; private double duration; + private bool tracking; public LogoTrackingContainer() { @@ -69,7 +68,11 @@ namespace osu.Game.Graphics.Containers } Logo = logo ?? throw new ArgumentNullException(nameof(logo)); - Logo.IsTracking = Tracking; + + if (Tracking) + { + Logo.IsTracking = true; + } this.facadeScale = facadeScale; this.duration = duration; From e06fe7950b04bec9df82a6244281b06faaf4892d Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 5 Apr 2019 15:06:37 +0900 Subject: [PATCH 0413/2854] Cleanup --- .../TestCaseLogoTrackingContainer.cs | 17 ----------------- .../Containers/LogoTrackingContainer.cs | 3 --- 2 files changed, 20 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs index 3b57d892ad..19e430da3e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs @@ -182,23 +182,6 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("Logo is tracking", () => trackingContainer.IsLogoTracking); } - [Test] - public void SetFacadeSizeTest() - { - AddStep("Add tracking containers", addFacadeContainers); - AddStep("Break stuff", () => { logoFacade.Size = new Vector2(0, 0); }); - } - - [Test] - public void SetMultipleContainers() - { - LogoTrackingContainer newContainer = new LogoTrackingContainer(); - AddStep("Add tracking containers", addFacadeContainers); - AddStep("Move facade to random position", startTrackingRandom); - AddStep("Add logo to new container", () => newContainer.SetLogo(logo)); - AddStep("Break stuff", () => newContainer.Tracking = true); - } - private void addFacadeContainers() { AddRange(new Drawable[] diff --git a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs index ab6f7ebc2a..746710bcda 100644 --- a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs +++ b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Reflection.Metadata.Ecma335; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; @@ -133,8 +132,6 @@ namespace osu.Game.Graphics.Containers base.Dispose(isDisposing); } - private Vector2 size; - private class ExposedFacade : Facade { public void SetSize(Vector2 size) From 37ffe47e4b763140befcea82f29cfe662869b2c4 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 5 Apr 2019 15:30:09 +0900 Subject: [PATCH 0414/2854] Add back exception tests with better descriptions --- .../TestCaseLogoTrackingContainer.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs index 19e430da3e..cf84a34344 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs @@ -182,6 +182,49 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("Logo is tracking", () => trackingContainer.IsLogoTracking); } + [Test] + public void SetFacadeSizeTest() + { + bool failed = false; + AddStep("Add tracking containers", addFacadeContainers); + AddStep("Try setting facade size", () => + { + try + { + logoFacade.Size = new Vector2(0, 0); + } + catch (Exception e) + { + if (e is InvalidOperationException) + failed = true; + } + }); + AddAssert("Exception thrown", () => failed); + } + + [Test] + public void SetMultipleContainers() + { + bool failed = false; + LogoTrackingContainer newContainer = new LogoTrackingContainer(); + AddStep("Add tracking containers", addFacadeContainers); + AddStep("Move facade to random position", startTrackingRandom); + AddStep("Add logo to new container", () => newContainer.SetLogo(logo)); + AddStep("Try tracking new container", () => + { + try + { + newContainer.Tracking = true; + } + catch (Exception e) + { + if (e is InvalidOperationException) + failed = true; + } + }); + AddAssert("Exception thrown", () => failed); + } + private void addFacadeContainers() { AddRange(new Drawable[] From c693d1fad89ea3b3d4ab655808668a6350bb9f6c Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 5 Apr 2019 15:48:48 +0900 Subject: [PATCH 0415/2854] Further condense steps --- .../TestCaseLogoTrackingContainer.cs | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs index cf84a34344..01a3113fe1 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs @@ -100,7 +100,12 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Add tracking containers", addFacadeContainers); AddStep("Move facade to random position", startTrackingRandom); AddStep("Remove facade from FacadeContainer", removeFacade); - AddStep("Transfer facade to a new container", addFacadeToNewContainer); + AddStep("Transfer facade to a new container", () => + { + transferContainer.Add(logoFacade); + transferContainerBox.Colour = Color4.Tomato; + moveLogoFacade(); + }); waitForMove(); AddAssert("Logo is tracking", () => trackingContainer.IsLogoTracking); } @@ -186,7 +191,11 @@ namespace osu.Game.Tests.Visual.UserInterface public void SetFacadeSizeTest() { bool failed = false; - AddStep("Add tracking containers", addFacadeContainers); + AddStep("Set up scenario", () => + { + failed = false; + addFacadeContainers(); + }); AddStep("Try setting facade size", () => { try @@ -203,13 +212,18 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - public void SetMultipleContainers() + public void SetMultipleContainersTest() { bool failed = false; LogoTrackingContainer newContainer = new LogoTrackingContainer(); - AddStep("Add tracking containers", addFacadeContainers); - AddStep("Move facade to random position", startTrackingRandom); - AddStep("Add logo to new container", () => newContainer.SetLogo(logo)); + AddStep("Set up scenario", () => + { + failed = false; + newContainer = new LogoTrackingContainer(); + addFacadeContainers(); + startTrackingRandom(); + newContainer.SetLogo(logo); + }); AddStep("Try tracking new container", () => { try @@ -272,13 +286,6 @@ namespace osu.Game.Tests.Visual.UserInterface moveLogoFacade(); } - private void addFacadeToNewContainer() - { - transferContainer.Add(logoFacade); - transferContainerBox.Colour = Color4.Tomato; - moveLogoFacade(); - } - private void moveLogoFacade() { if (logoFacade?.Transforms.Count == 0 && transferContainer?.Transforms.Count == 0) From e76897d6a76091885e3318d7bfccae9fe9268277 Mon Sep 17 00:00:00 2001 From: DrabWeb Date: Sun, 7 Apr 2019 16:32:55 -0300 Subject: [PATCH 0416/2854] Fix ResumeContainers appearing when resuming during breaks. --- osu.Game/Screens/Play/BreakOverlay.cs | 4 ++++ osu.Game/Screens/Play/Player.cs | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index d390787090..ab81e613cc 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -23,6 +23,8 @@ namespace osu.Game.Screens.Play private readonly Container fadeContainer; + public bool IsActive { get; private set; } + public List Breaks { get => breaks; @@ -125,6 +127,7 @@ namespace osu.Game.Screens.Play using (BeginAbsoluteSequence(b.StartTime, true)) { + Schedule(() => IsActive = true); fadeContainer.FadeIn(fade_duration); breakArrows.Show(fade_duration); @@ -142,6 +145,7 @@ namespace osu.Game.Screens.Play using (BeginDelayedSequence(b.Duration - fade_duration, true)) { + Schedule(() => IsActive = false); fadeContainer.FadeOut(fade_duration); breakArrows.Hide(fade_duration); } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0eebefec86..60287f7a2e 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -60,6 +60,8 @@ namespace osu.Game.Screens.Play private SampleChannel sampleRestart; + private BreakOverlay breakOverlay; + protected ScoreProcessor ScoreProcessor { get; private set; } protected DrawableRuleset DrawableRuleset { get; private set; } @@ -115,7 +117,7 @@ namespace osu.Game.Screens.Play Child = DrawableRuleset } }, - new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) + breakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -405,8 +407,8 @@ namespace osu.Game.Screens.Play IsResuming = true; PauseOverlay.Hide(); - // time-based conditions may allow instant resume. - if (GameplayClockContainer.GameplayClock.CurrentTime < Beatmap.Value.Beatmap.HitObjects.First().StartTime) + // breaks and time-based conditions may allow instant resume. + if (breakOverlay.IsActive || GameplayClockContainer.GameplayClock.CurrentTime < Beatmap.Value.Beatmap.HitObjects.First().StartTime) completeResume(); else DrawableRuleset.RequestResume(completeResume); From d7655bc579df0a5193caa5cad65b02fd7084c3d8 Mon Sep 17 00:00:00 2001 From: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com> Date: Mon, 8 Apr 2019 11:22:01 +0900 Subject: [PATCH 0417/2854] Use .Value instead of cast Co-Authored-By: nyquillerium --- osu.Game/Graphics/Containers/LogoTrackingContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs index 746710bcda..de70231989 100644 --- a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs +++ b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs @@ -115,7 +115,7 @@ namespace osu.Game.Graphics.Containers var amount = (float)Interpolation.ApplyEasing(easing, Math.Min(elapsedDuration / duration, 1)); // Interpolate the position of the logo, where amount 0 is where the logo was when it first began interpolating, and amount 1 is the target location. - Logo.Position = Vector2.Lerp((Vector2)startPosition, LogoTrackingPosition, amount); + Logo.Position = Vector2.Lerp(startPosition.Value, LogoTrackingPosition, amount); } else { From a690302d00cefdae17fa150e1d567cb9669fbc9d Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 8 Apr 2019 15:24:09 +0900 Subject: [PATCH 0418/2854] Apply reviews --- .../UserInterface/TestCaseButtonSystem.cs | 2 +- .../TestCaseLogoTrackingContainer.cs | 27 +++---- .../Containers/LogoTrackingContainer.cs | 78 +++++++++---------- osu.Game/Screens/Menu/ButtonSystem.cs | 13 ++-- .../Screens/Menu/FlowContainerWithOrigin.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 8 +- 6 files changed, 59 insertions(+), 71 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestCaseButtonSystem.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseButtonSystem.cs index 261e87ff07..04aa8bce7e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestCaseButtonSystem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestCaseButtonSystem.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.UserInterface RelativeSizeAxes = Axes.Both, }, buttons = new ButtonSystem(), - logo = new OsuLogo() + logo = new OsuLogo { RelativePositionAxes = Axes.Both } }; buttons.SetOsuLogo(logo); diff --git a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs index 01a3113fe1..8b70a34c85 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs @@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.UserInterface { AddToggleStep("Toggle move continuously", b => randomPositions = b); AddStep("Add tracking containers", addFacadeContainers); - AddStep("Move facade to random position", startTrackingRandom); + AddStep("Move facade to random position", moveLogoFacade); waitForMove(); AddAssert("Logo is tracking", () => trackingContainer.IsLogoTracking); } @@ -85,7 +85,7 @@ namespace osu.Game.Tests.Visual.UserInterface public void RemoveFacadeTest() { AddStep("Add tracking containers", addFacadeContainers); - AddStep("Move facade to random position", startTrackingRandom); + AddStep("Move facade to random position", moveLogoFacade); AddStep("Remove facade from FacadeContainer", removeFacade); waitForMove(); AddAssert("Logo is not tracking", () => !trackingContainer.IsLogoTracking); @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Visual.UserInterface public void TransferFacadeTest() { AddStep("Add tracking containers", addFacadeContainers); - AddStep("Move facade to random position", startTrackingRandom); + AddStep("Move facade to random position", moveLogoFacade); AddStep("Remove facade from FacadeContainer", removeFacade); AddStep("Transfer facade to a new container", () => { @@ -171,14 +171,14 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Perform logo movements", () => { - trackingContainer.Tracking = false; + trackingContainer.StopTracking(); logo.MoveTo(new Vector2(0.5f), 500, Easing.InOutExpo); - trackingContainer.SetLogo(logo, 1.0f, 1000, Easing.InOutExpo); + visualBox.Colour = Color4.White; Scheduler.AddDelayed(() => { - trackingContainer.Tracking = true; + trackingContainer.StartTracking(logo, 1000, Easing.InOutExpo); visualBox.Colour = Color4.Tomato; }, 700); }); @@ -221,14 +221,13 @@ namespace osu.Game.Tests.Visual.UserInterface failed = false; newContainer = new LogoTrackingContainer(); addFacadeContainers(); - startTrackingRandom(); - newContainer.SetLogo(logo); + moveLogoFacade(); }); AddStep("Try tracking new container", () => { try { - newContainer.Tracking = true; + newContainer.StartTracking(logo); } catch (Exception e) { @@ -268,7 +267,7 @@ namespace osu.Game.Tests.Visual.UserInterface }); trackingContainer.Add(logoFacade = trackingContainer.LogoFacade); - trackingContainer.SetLogo(logo, 1.0f, 1000); + trackingContainer.StartTracking(logo, 1000); } private void waitForMove(int count = 5) => AddWaitStep("Wait for transforms to finish", count); @@ -280,12 +279,6 @@ namespace osu.Game.Tests.Visual.UserInterface moveLogoFacade(); } - private void startTrackingRandom() - { - trackingContainer.Tracking = true; - moveLogoFacade(); - } - private void moveLogoFacade() { if (logoFacade?.Transforms.Count == 0 && transferContainer?.Transforms.Count == 0) @@ -304,7 +297,7 @@ namespace osu.Game.Tests.Visual.UserInterface /// /// Check that the logo is tracking the position of the facade, with an acceptable precision lenience. /// - public bool IsLogoTracking => Precision.AlmostEquals(Logo.Position, LogoTrackingPosition); + public bool IsLogoTracking => Precision.AlmostEquals(Logo.Position, ComputeLogoTrackingPosition()); } } } diff --git a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs index 746710bcda..ae585a2ba7 100644 --- a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs +++ b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs @@ -17,29 +17,9 @@ namespace osu.Game.Graphics.Containers { public Facade LogoFacade { get; } - /// - /// Whether or not the logo assigned to this FacadeContainer should be tracking the position of its facade. - /// - public bool Tracking - { - get => tracking; - set - { - if (Logo != null) - { - if (value && !tracking && Logo.IsTracking) - throw new InvalidOperationException($"Cannot track an instance of {typeof(OsuLogo)} to multiple {typeof(LogoTrackingContainer)}s"); + protected OsuLogo Logo => logo; - Logo.IsTracking = value; - } - - tracking = value; - } - } - - protected OsuLogo Logo; - - private float facadeScale; + private OsuLogo logo; private Easing easing; private Vector2? startPosition; private double? startTime; @@ -58,27 +38,38 @@ namespace osu.Game.Graphics.Containers /// The scale of the facade. Does not actually affect the logo itself. /// The duration of the initial transform. Default is instant. /// The easing type of the initial transform. - public void SetLogo(OsuLogo logo, float facadeScale = 1.0f, double duration = 0, Easing easing = Easing.None) + public void StartTracking(OsuLogo logo, double duration = 0, Easing easing = Easing.None) { - if (Logo != logo && Logo != null) + if (logo == null) + throw new ArgumentNullException(nameof(logo)); + + if (logo.IsTracking && tracking == false) + throw new InvalidOperationException($"Cannot track an instance of {typeof(OsuLogo)} to multiple {typeof(LogoTrackingContainer)}s"); + + if (this.logo != logo && this.logo != null) { // If we're replacing the logo to be tracked, the old one no longer has a tracking container - Logo.IsTracking = false; + this.logo.IsTracking = false; } - Logo = logo ?? throw new ArgumentNullException(nameof(logo)); + this.logo = logo; + this.logo.IsTracking = true; - if (Tracking) - { - Logo.IsTracking = true; - } - - this.facadeScale = facadeScale; this.duration = duration; this.easing = easing; startTime = null; startPosition = null; + + tracking = true; + } + + public void StopTracking() + { + if (logo != null) + logo.IsTracking = false; + + tracking = false; } /// @@ -86,20 +77,27 @@ namespace osu.Game.Graphics.Containers /// Manually performs a conversion of the Facade's position to the Logo's parent's relative space. /// /// Will only be correct if the logo's are set to Axes.Both - protected Vector2 LogoTrackingPosition => new Vector2(Logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre).X / Logo.Parent.RelativeToAbsoluteFactor.X, - Logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre).Y / Logo.Parent.RelativeToAbsoluteFactor.Y); + protected Vector2 ComputeLogoTrackingPosition() + { + var absolutePos = Logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre); + + return new Vector2(absolutePos.X / Logo.Parent.RelativeToAbsoluteFactor.X, + absolutePos.Y / Logo.Parent.RelativeToAbsoluteFactor.Y); + } protected override void Update() { base.Update(); - if (Logo == null || !Tracking) + if (Logo == null || !tracking) return; // Account for the scale of the actual OsuLogo, as SizeForFlow only accounts for the sprite scale. - ((ExposedFacade)LogoFacade).SetSize(new Vector2(Logo.SizeForFlow * Logo.Scale.X * facadeScale)); + ((ExposedFacade)LogoFacade).SetSize(new Vector2(Logo.SizeForFlow * Logo.Scale.X)); - if (LogoFacade.Parent != null && Logo.Position != LogoTrackingPosition && Logo.RelativePositionAxes == Axes.Both) + var localPos = ComputeLogoTrackingPosition(); + + if (LogoFacade.Parent != null && Logo.Position != localPos && Logo.RelativePositionAxes == Axes.Both) { // If this is our first update since tracking has started, initialize our starting values for interpolation if (startTime == null || startPosition == null) @@ -115,11 +113,11 @@ namespace osu.Game.Graphics.Containers var amount = (float)Interpolation.ApplyEasing(easing, Math.Min(elapsedDuration / duration, 1)); // Interpolate the position of the logo, where amount 0 is where the logo was when it first began interpolating, and amount 1 is the target location. - Logo.Position = Vector2.Lerp((Vector2)startPosition, LogoTrackingPosition, amount); + Logo.Position = Vector2.Lerp((Vector2)startPosition, localPos, amount); } else { - Logo.Position = LogoTrackingPosition; + Logo.Position = localPos; } } } @@ -127,7 +125,7 @@ namespace osu.Game.Graphics.Containers protected override void Dispose(bool isDisposing) { if (Logo != null) - Tracking = false; + Logo.IsTracking = false; base.Dispose(isDisposing); } diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index d042e110e9..1d2ee3c284 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -60,17 +60,17 @@ namespace osu.Game.Screens.Menu if (this.logo != null) { this.logo.Action = onOsuLogo; - logoTrackingContainer.SetLogo(logo, 0.74f); // osuLogo.SizeForFlow relies on loading to be complete. buttonArea.Flow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0); + logoTrackingContainer.LogoFacade.Scale = new Vector2(0.74f); updateLogoState(); } else { // We should stop tracking as the facade is now out of scope. - logoTrackingContainer.Tracking = false; + logoTrackingContainer.StopTracking(); } } @@ -271,7 +271,7 @@ namespace osu.Game.Screens.Menu logoDelayedAction?.Cancel(); logoDelayedAction = Scheduler.AddDelayed(() => { - logoTrackingContainer.Tracking = false; + logoTrackingContainer.StopTracking(); game?.Toolbar.Hide(); @@ -294,8 +294,7 @@ namespace osu.Game.Screens.Menu if (lastState == ButtonSystemState.Initial) logo.ScaleTo(0.5f, 200, Easing.In); - logoTrackingContainer.SetLogo(logo, 0.74f, lastState == ButtonSystemState.EnteringMode ? 0 : 200, Easing.In); - logoTrackingContainer.Tracking = true; + logoTrackingContainer.StartTracking(logo, lastState == ButtonSystemState.EnteringMode ? 0 : 200, Easing.In); logoDelayedAction?.Cancel(); logoDelayedAction = Scheduler.AddDelayed(() => @@ -308,14 +307,14 @@ namespace osu.Game.Screens.Menu break; default: logo.ClearTransforms(targetMember: nameof(Position)); - logoTrackingContainer.Tracking = true; + logoTrackingContainer.StartTracking(logo, 0, Easing.In); logo.ScaleTo(0.5f, 200, Easing.OutQuint); break; } break; case ButtonSystemState.EnteringMode: - logoTrackingContainer.Tracking = true; + logoTrackingContainer.StartTracking(logo, 0, Easing.In); break; } } diff --git a/osu.Game/Screens/Menu/FlowContainerWithOrigin.cs b/osu.Game/Screens/Menu/FlowContainerWithOrigin.cs index ec7333ec02..8310ab06eb 100644 --- a/osu.Game/Screens/Menu/FlowContainerWithOrigin.cs +++ b/osu.Game/Screens/Menu/FlowContainerWithOrigin.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Menu if (CentreTarget == null) return base.OriginPosition; - return CentreTarget.DrawPosition + CentreTarget.DrawSize / 2; + return CentreTarget.DrawPosition + CentreTarget.DrawSize / 2 * CentreTarget.Scale; } } } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index f7feb3535d..4c8f9a3539 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -128,7 +128,7 @@ namespace osu.Game.Screens.Play private void contentOut() { // Ensure the logo is no longer tracking before we scale the content - content.Tracking = false; + content.StopTracking(); content.ScaleTo(0.7f, 300, Easing.InQuint); content.FadeOut(250); @@ -161,15 +161,13 @@ namespace osu.Game.Screens.Play logo.ScaleTo(new Vector2(0.15f), duration, Easing.In); logo.FadeIn(350); - content.SetLogo(logo, 1.0f, resuming ? 0 : 500, Easing.InOutExpo); - - Scheduler.AddDelayed(() => { content.Tracking = true; }, resuming ? 0 : 500); + Scheduler.AddDelayed(() => { content.StartTracking(logo, resuming ? 0 : 500, Easing.InOutExpo); }, resuming ? 0 : 500); } protected override void LogoExiting(OsuLogo logo) { base.LogoExiting(logo); - content.Tracking = false; + content.StopTracking(); } protected override void LoadComplete() From 8a01995668734e46e0630ce8b143fb446d868a1b Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 8 Apr 2019 16:14:41 +0900 Subject: [PATCH 0419/2854] Remove need for tracking bool and backing logo --- .../Containers/LogoTrackingContainer.cs | 27 +++++++++---------- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs index 0721011345..5ebdb7d45d 100644 --- a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs +++ b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs @@ -17,14 +17,12 @@ namespace osu.Game.Graphics.Containers { public Facade LogoFacade { get; } - protected OsuLogo Logo => logo; + protected OsuLogo Logo { get; private set; } - private OsuLogo logo; private Easing easing; private Vector2? startPosition; private double? startTime; private double duration; - private bool tracking; public LogoTrackingContainer() { @@ -43,33 +41,32 @@ namespace osu.Game.Graphics.Containers if (logo == null) throw new ArgumentNullException(nameof(logo)); - if (logo.IsTracking && tracking == false) + if (logo.IsTracking && Logo == null) throw new InvalidOperationException($"Cannot track an instance of {typeof(OsuLogo)} to multiple {typeof(LogoTrackingContainer)}s"); - if (this.logo != logo && this.logo != null) + if (Logo != logo && Logo != null) { // If we're replacing the logo to be tracked, the old one no longer has a tracking container - this.logo.IsTracking = false; + Logo.IsTracking = false; } - this.logo = logo; - this.logo.IsTracking = true; + Logo = logo; + Logo.IsTracking = true; this.duration = duration; this.easing = easing; startTime = null; startPosition = null; - - tracking = true; } public void StopTracking() { - if (logo != null) - logo.IsTracking = false; - - tracking = false; + if (Logo != null) + { + Logo.IsTracking = false; + Logo = null; + } } /// @@ -89,7 +86,7 @@ namespace osu.Game.Graphics.Containers { base.Update(); - if (Logo == null || !tracking) + if (Logo == null) return; // Account for the scale of the actual OsuLogo, as SizeForFlow only accounts for the sprite scale. diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 1d2ee3c284..a6157cdb64 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -63,7 +63,6 @@ namespace osu.Game.Screens.Menu // osuLogo.SizeForFlow relies on loading to be complete. buttonArea.Flow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0); - logoTrackingContainer.LogoFacade.Scale = new Vector2(0.74f); updateLogoState(); } @@ -106,6 +105,7 @@ namespace osu.Game.Screens.Menu }); buttonArea.Flow.CentreTarget = logoTrackingContainer.LogoFacade; + logoTrackingContainer.LogoFacade.Scale = new Vector2(0.74f); } [Resolved(CanBeNull = true)] From c584967eb12adb64c26111e43d4f2fa790e7ec8b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 8 Apr 2019 18:32:05 +0900 Subject: [PATCH 0420/2854] Remove mods from workingbeatmap --- .../TestCaseAutoJuiceStream.cs | 2 +- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 +- .../UI/DrawableCatchRuleset.cs | 6 ++-- .../Edit/DrawableManiaEditRuleset.cs | 6 ++-- .../Edit/ManiaHitObjectComposer.cs | 5 +-- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- .../UI/DrawableManiaRuleset.cs | 5 +-- osu.Game.Rulesets.Osu.Tests/StackingTest.cs | 3 +- .../Edit/DrawableOsuEditRuleset.cs | 6 ++-- .../Edit/OsuHitObjectComposer.cs | 5 +-- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- .../UI/DrawableOsuRuleset.cs | 6 ++-- .../TestCaseTaikoPlayfield.cs | 4 ++- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- .../UI/DrawableTaikoRuleset.cs | 6 ++-- .../Formats/LegacyBeatmapDecoderTest.cs | 3 +- .../TestCaseBackgroundScreenBeatmap.cs | 2 +- .../Visual/Gameplay/TestCaseAutoplay.cs | 2 +- .../Visual/Gameplay/TestCaseReplay.cs | 3 +- .../SongSelect/TestCasePlaySongSelect.cs | 4 +-- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 35 ++++++++----------- osu.Game/OsuGame.cs | 3 +- .../Difficulty/DifficultyCalculator.cs | 3 +- .../Difficulty/PerformanceCalculator.cs | 3 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 5 +-- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Rulesets/UI/DrawableRuleset.cs | 19 +++++----- osu.Game/Rulesets/UI/Playfield.cs | 12 ++++--- .../UI/Scrolling/DrawableScrollingRuleset.cs | 5 +-- .../Screens/Multi/Match/MatchSubScreen.cs | 2 -- osu.Game/Screens/OsuScreen.cs | 5 +++ osu.Game/Screens/OsuScreenDependencies.cs | 13 ++++--- .../Screens/Play/GameplayClockContainer.cs | 9 ++--- osu.Game/Screens/Play/HUDOverlay.cs | 7 ++-- osu.Game/Screens/Play/Player.cs | 14 ++++---- osu.Game/Screens/Play/PlayerLoader.cs | 10 ++++-- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 5 +-- osu.Game/Screens/Select/MatchSongSelect.cs | 6 +--- osu.Game/Screens/Select/SongSelect.cs | 6 ++-- osu.Game/Tests/Visual/AllPlayersTestCase.cs | 2 +- osu.Game/Tests/Visual/OsuTestCase.cs | 17 +++++---- osu.Game/Tests/Visual/PlayerTestCase.cs | 2 +- 43 files changed, 147 insertions(+), 116 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs index fbb2db33b0..a696ba4e7e 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Tests protected override Player CreatePlayer(Ruleset ruleset) { - Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); + SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); return base.CreatePlayer(ruleset); } } diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index aa00e182a9..544694fc8f 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch { public class CatchRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap) => new DrawableCatchRuleset(this, beatmap); + public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IEnumerable mods) => new DrawableCatchRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap); diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index ba0f5b90ba..4b75b54d41 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -10,6 +11,7 @@ using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Catch.Scoring; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; @@ -23,8 +25,8 @@ namespace osu.Game.Rulesets.Catch.UI protected override bool UserScrollSpeedAdjustment => false; - public DrawableCatchRuleset(Ruleset ruleset, WorkingBeatmap beatmap) - : base(ruleset, beatmap) + public DrawableCatchRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + : base(ruleset, beatmap, mods) { Direction.Value = ScrollingDirection.Down; TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450); diff --git a/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs b/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs index acafaffee6..0bfbf38832 100644 --- a/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs +++ b/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs @@ -1,10 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Graphics; using osuTK; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; @@ -14,8 +16,8 @@ namespace osu.Game.Rulesets.Mania.Edit { public new IScrollingInfo ScrollingInfo => base.ScrollingInfo; - public DrawableManiaEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap) - : base(ruleset, beatmap) + public DrawableManiaEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + : base(ruleset, beatmap, mods) { } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 56c9471462..ebc94c86e2 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Compose.Components; using osuTK; @@ -41,9 +42,9 @@ namespace osu.Game.Rulesets.Mania.Edit public int TotalColumns => ((ManiaPlayfield)DrawableRuleset.Playfield).TotalColumns; - protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap) + protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) { - DrawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap); + DrawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods); // This is the earliest we can cache the scrolling info to ourselves, before masks are added to the hierarchy and inject it dependencies.CacheAs(DrawableRuleset.ScrollingInfo); diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 0ff79d2836..02a9b5ed30 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Mania { public class ManiaRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap) => new DrawableManiaRuleset(this, beatmap); + public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IEnumerable mods) => new DrawableManiaRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap); public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score); diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 1c1ec604f6..9d10657680 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -18,6 +18,7 @@ using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Scoring; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; @@ -39,8 +40,8 @@ namespace osu.Game.Rulesets.Mania.UI private readonly Bindable configDirection = new Bindable(); - public DrawableManiaRuleset(Ruleset ruleset, WorkingBeatmap beatmap) - : base(ruleset, beatmap) + public DrawableManiaRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + : base(ruleset, beatmap, mods) { // Generate the bar lines double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; diff --git a/osu.Game.Rulesets.Osu.Tests/StackingTest.cs b/osu.Game.Rulesets.Osu.Tests/StackingTest.cs index e2f6b2164c..13c9985f47 100644 --- a/osu.Game.Rulesets.Osu.Tests/StackingTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/StackingTest.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text; using NUnit.Framework; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Tests.Beatmaps; using Decoder = osu.Game.Beatmaps.Formats.Decoder; @@ -22,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Tests using (var reader = new StreamReader(stream)) { var beatmap = Decoder.GetDecoder(reader).Decode(reader); - var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo); + var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Enumerable.Empty()); var objects = converted.HitObjects.ToList(); diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs index d9cb203bdf..0c3050bfb4 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs @@ -1,7 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; using osuTK; @@ -10,8 +12,8 @@ namespace osu.Game.Rulesets.Osu.Edit { public class DrawableOsuEditRuleset : DrawableOsuRuleset { - public DrawableOsuEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap) - : base(ruleset, beatmap) + public DrawableOsuEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + : base(ruleset, beatmap, mods) { } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 039ec5585e..9c4b6ee7aa 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders; @@ -23,8 +24,8 @@ namespace osu.Game.Rulesets.Osu.Edit { } - protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap) - => new DrawableOsuEditRuleset(ruleset, beatmap); + protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + => new DrawableOsuEditRuleset(ruleset, beatmap, mods); protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] { diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 44bce5bed8..115271da85 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu { public class OsuRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap) => new DrawableOsuRuleset(this, beatmap); + public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IEnumerable mods) => new DrawableOsuRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap); diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index f6a3be40b0..c2954e1b3b 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -1,11 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Input.Handlers; using osu.Game.Replays; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Objects; @@ -22,8 +24,8 @@ namespace osu.Game.Rulesets.Osu.UI { protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config; - public DrawableOsuRuleset(Ruleset ruleset, WorkingBeatmap beatmap) - : base(ruleset, beatmap) + public DrawableOsuRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + : base(ruleset, beatmap, mods) { } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs index 369cdd49d2..c42faea9f9 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -11,6 +12,7 @@ using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Judgements; @@ -86,7 +88,7 @@ namespace osu.Game.Rulesets.Taiko.Tests Origin = Anchor.Centre, RelativeSizeAxes = Axes.X, Height = 768, - Children = new[] { drawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap) } + Children = new[] { drawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap, Enumerable.Empty()) } }); } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 448b1b42bb..cb53ec890b 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko { public class TaikoRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap) => new DrawableTaikoRuleset(this, beatmap); + public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IEnumerable mods) => new DrawableTaikoRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap); public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index f4b9c46dfc..76bdd37ed3 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Drawables; @@ -16,6 +17,7 @@ using osu.Framework.Input; using osu.Game.Configuration; using osu.Game.Input.Handlers; using osu.Game.Replays; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI.Scrolling; namespace osu.Game.Rulesets.Taiko.UI @@ -26,8 +28,8 @@ namespace osu.Game.Rulesets.Taiko.UI protected override bool UserScrollSpeedAdjustment => false; - public DrawableTaikoRuleset(Ruleset ruleset, WorkingBeatmap beatmap) - : base(ruleset, beatmap) + public DrawableTaikoRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + : base(ruleset, beatmap, mods) { Direction.Value = ScrollingDirection.Left; TimeRange.Value = 7000; diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 02dff6993d..e181130774 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Catch.Beatmaps; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Osu; @@ -39,7 +40,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(6, working.BeatmapInfo.BeatmapVersion); Assert.AreEqual(6, working.Beatmap.BeatmapInfo.BeatmapVersion); - Assert.AreEqual(6, working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapVersion); + Assert.AreEqual(6, working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Enumerable.Empty()).BeatmapInfo.BeatmapVersion); } } diff --git a/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs index 891b89e72d..e3875421b1 100644 --- a/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs +++ b/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs @@ -267,7 +267,7 @@ namespace osu.Game.Tests.Visual.Background AddUntilStep("Song select has selection", () => songSelect.Carousel.SelectedBeatmap != null); AddStep("Set default user settings", () => { - Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { new OsuModNoFail() }); + SelectedMods.Value = SelectedMods.Value.Concat(new[] { new OsuModNoFail() }); songSelect.DimLevel.Value = 0.7f; songSelect.BlurLevel.Value = 0.4f; }); diff --git a/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs index a2d92b7861..c930e77e9e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Gameplay { protected override Player CreatePlayer(Ruleset ruleset) { - Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); + SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); return new ScoreAccessiblePlayer(); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestCaseReplay.cs b/osu.Game.Tests/Visual/Gameplay/TestCaseReplay.cs index b98ce96fbb..2a72dc8242 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCaseReplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCaseReplay.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Linq; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play; @@ -15,7 +16,7 @@ namespace osu.Game.Tests.Visual.Gameplay { protected override Player CreatePlayer(Ruleset ruleset) { - var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo); + var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Enumerable.Empty()); return new ScoreAccessibleReplayPlayer(ruleset.GetAutoplayMod().CreateReplayScore(beatmap)); } diff --git a/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs index d5bc452d75..bc644da9d5 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs @@ -175,12 +175,12 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("change ruleset", () => { - songSelect.CurrentBeatmap.Mods.ValueChanged += onModChange; + SelectedMods.ValueChanged += onModChange; songSelect.Ruleset.ValueChanged += onRulesetChange; Ruleset.Value = new TaikoRuleset().RulesetInfo; - songSelect.CurrentBeatmap.Mods.ValueChanged -= onModChange; + SelectedMods.ValueChanged -= onModChange; songSelect.Ruleset.ValueChanged -= onRulesetChange; }); diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 73aa12a3db..3e6033da9c 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -52,7 +52,7 @@ namespace osu.Game.Beatmaps { public override IEnumerable GetModsFor(ModType type) => new Mod[] { }; - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap) + public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IEnumerable mods) { throw new NotImplementedException(); } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 2e36d87024..bb13a8c0e7 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -11,7 +11,6 @@ using osu.Framework.IO.File; using System.IO; using System.Linq; using System.Threading; -using osu.Framework.Bindables; using osu.Game.IO.Serialization; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; @@ -28,16 +27,12 @@ namespace osu.Game.Beatmaps public readonly BeatmapMetadata Metadata; - public readonly Bindable> Mods = new Bindable>(new Mod[] { }); - protected WorkingBeatmap(BeatmapInfo beatmapInfo) { BeatmapInfo = beatmapInfo; BeatmapSetInfo = beatmapInfo.BeatmapSet; Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); - Mods.ValueChanged += _ => applyRateAdjustments(); - beatmap = new RecyclableLazy(() => { var b = GetBeatmap() ?? new Beatmap(); @@ -55,7 +50,7 @@ namespace osu.Game.Beatmaps { // we want to ensure that we always have a track, even if it's a fake one. var t = GetTrack() ?? new VirtualBeatmapTrack(Beatmap); - applyRateAdjustments(t); + // applyRateAdjustments(t); return t; }); @@ -87,7 +82,7 @@ namespace osu.Game.Beatmaps /// The to create a playable for. /// The converted . /// If could not be converted to . - public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset) + public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IEnumerable mods) { var rulesetInstance = ruleset.CreateInstance(); @@ -98,19 +93,19 @@ namespace osu.Game.Beatmaps throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter})."); // Apply conversion mods - foreach (var mod in Mods.Value.OfType()) + foreach (var mod in mods.OfType()) mod.ApplyToBeatmapConverter(converter); // Convert IBeatmap converted = converter.Convert(); // Apply difficulty mods - if (Mods.Value.Any(m => m is IApplicableToDifficulty)) + if (mods.Any(m => m is IApplicableToDifficulty)) { converted.BeatmapInfo = converted.BeatmapInfo.Clone(); converted.BeatmapInfo.BaseDifficulty = converted.BeatmapInfo.BaseDifficulty.Clone(); - foreach (var mod in Mods.Value.OfType()) + foreach (var mod in mods.OfType()) mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty); } @@ -122,7 +117,7 @@ namespace osu.Game.Beatmaps foreach (var obj in converted.HitObjects) obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty); - foreach (var mod in Mods.Value.OfType()) + foreach (var mod in mods.OfType()) foreach (var obj in converted.HitObjects) mod.ApplyToHitObject(obj); @@ -188,15 +183,15 @@ namespace osu.Game.Beatmaps /// public void RecycleTrack() => track.Recycle(); - private void applyRateAdjustments(Track t = null) - { - if (t == null && track.IsResultAvailable) t = Track; - if (t == null) return; - - t.ResetSpeedAdjustments(); - foreach (var mod in Mods.Value.OfType()) - mod.ApplyToClock(t); - } + // private void applyRateAdjustments(Track t = null) + // { + // if (t == null && track.IsResultAvailable) t = Track; + // if (t == null) return; + // + // t.ResetSpeedAdjustments(); + // foreach (var mod in Mods.Value.OfType()) + // mod.ApplyToClock(t); + // } public class RecyclableLazy { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 4f92ff831c..2cef4f66a9 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -293,9 +293,8 @@ namespace osu.Game performFromMainMenu(() => { ruleset.Value = databasedScoreInfo.Ruleset; - Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap); - Beatmap.Value.Mods.Value = databasedScoreInfo.Mods; + selectedMods.Value = databasedScoreInfo.Mods; menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore))); }, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true); diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index aad55f8a38..e181051737 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -39,8 +39,7 @@ namespace osu.Game.Rulesets.Difficulty { mods = mods.Select(m => m.CreateCopy()).ToArray(); - beatmap.Mods.Value = mods; - IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo); + IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods); var clock = new StopwatchClock(); mods.OfType().ForEach(m => m.ApplyToClock(clock)); diff --git a/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs b/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs index 2b627ee8e6..9ab81b9580 100644 --- a/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs @@ -26,8 +26,7 @@ namespace osu.Game.Rulesets.Difficulty Ruleset = ruleset; Score = score; - beatmap.Mods.Value = score.Mods; - Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo); + Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, score.Mods); Attributes = ruleset.CreateDifficultyCalculator(beatmap).Calculate(score.Mods); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 41de0c36fc..5219cb9581 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -14,6 +14,7 @@ using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; @@ -185,8 +186,8 @@ namespace osu.Game.Rulesets.Edit } internal override DrawableEditRuleset CreateDrawableRuleset() - => new DrawableEditRuleset(CreateDrawableRuleset(Ruleset, Beatmap.Value)); + => new DrawableEditRuleset(CreateDrawableRuleset(Ruleset, Beatmap.Value, Enumerable.Empty())); - protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap); + protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods); } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index cdfe02b60b..90fe25accf 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets /// The beatmap to create the hit renderer for. /// Unable to successfully load the beatmap to be usable with this ruleset. /// - public abstract DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap); + public abstract DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IEnumerable mods); /// /// Creates a to convert a to one that is applicable for this . diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 27137d9f98..0ab07de1ac 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -11,7 +11,6 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics.Cursor; @@ -82,7 +81,9 @@ namespace osu.Game.Rulesets.UI /// /// The mods which are to be applied. /// - private readonly IEnumerable mods; + [Cached] + [Cached(typeof(IBindable>))] + private readonly Bindable> mods = new Bindable>(); private FrameStabilityContainer frameStabilityContainer; @@ -93,16 +94,18 @@ namespace osu.Game.Rulesets.UI /// /// The ruleset being represented. /// The beatmap to create the hit renderer for. - protected DrawableRuleset(Ruleset ruleset, WorkingBeatmap workingBeatmap) + protected DrawableRuleset(Ruleset ruleset, WorkingBeatmap workingBeatmap, IEnumerable mods) : base(ruleset) { - Debug.Assert(workingBeatmap != null, "DrawableRuleset initialized with a null beatmap."); + if (workingBeatmap == null) + throw new ArgumentException("Beatmap cannot be null.", nameof(workingBeatmap)); + + this.mods.Value = mods; RelativeSizeAxes = Axes.Both; - Beatmap = (Beatmap)workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo); + Beatmap = (Beatmap)workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods); - mods = workingBeatmap.Mods.Value; applyBeatmapMods(mods); KeyBindingInputManager = CreateInputManager(); @@ -157,7 +160,7 @@ namespace osu.Game.Rulesets.UI .WithChild(ResumeOverlay))); } - applyRulesetMods(mods, config); + applyRulesetMods(mods.Value, config); loadObjects(); } @@ -172,7 +175,7 @@ namespace osu.Game.Rulesets.UI Playfield.PostProcess(); - foreach (var mod in mods.OfType()) + foreach (var mod in mods.Value.OfType()) mod.ApplyToDrawableHitObjects(Playfield.HitObjectContainer.Objects); } diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 48b950c070..6ea565af44 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -57,13 +57,15 @@ namespace osu.Game.Rulesets.UI hitObjectContainerLazy = new Lazy(CreateHitObjectContainer); } - private WorkingBeatmap beatmap; + [Resolved] + private IBindable beatmap { get; set; } + + [Resolved] + private IBindable> selectedMods { get; set; } [BackgroundDependencyLoader] - private void load(IBindable beatmap) + private void load() { - this.beatmap = beatmap.Value; - Cursor = CreateCursor(); if (Cursor != null) AddInternal(Cursor); @@ -123,7 +125,7 @@ namespace osu.Game.Rulesets.UI base.Update(); if (beatmap != null) - foreach (var mod in beatmap.Mods.Value) + foreach (var mod in selectedMods.Value) if (mod is IUpdatableByPlayfield updatable) updatable.Update(this); } diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index 3b368652f2..2bb98dd679 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -13,6 +13,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; using osu.Game.Input.Bindings; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Timing; @@ -80,8 +81,8 @@ namespace osu.Game.Rulesets.UI.Scrolling [Cached(Type = typeof(IScrollingInfo))] private readonly LocalScrollingInfo scrollingInfo; - protected DrawableScrollingRuleset(Ruleset ruleset, WorkingBeatmap beatmap) - : base(ruleset, beatmap) + protected DrawableScrollingRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + : base(ruleset, beatmap, mods) { scrollingInfo = new LocalScrollingInfo(); scrollingInfo.Direction.BindTo(Direction); diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index a71106872e..b3f9a4ae21 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -222,8 +222,6 @@ namespace osu.Game.Screens.Multi.Match private void onStart() { - Beatmap.Value.Mods.Value = SelectedMods.Value.ToArray(); - switch (type.Value) { default: diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index e0a25deecf..33e725eb41 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Internal; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -14,6 +15,7 @@ using osu.Game.Input.Bindings; using osu.Game.Rulesets; using osu.Game.Screens.Menu; using osu.Game.Overlays; +using osu.Game.Rulesets.Mods; namespace osu.Game.Screens { @@ -63,12 +65,15 @@ namespace osu.Game.Screens public Bindable Ruleset { get; set; } + public Bindable> SelectedMods { get; set; } + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { var screenDependencies = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, parent); Beatmap = screenDependencies.Beatmap; Ruleset = screenDependencies.Ruleset; + SelectedMods = screenDependencies.SelectedMods; return base.CreateChildDependencies(screenDependencies); } diff --git a/osu.Game/Screens/OsuScreenDependencies.cs b/osu.Game/Screens/OsuScreenDependencies.cs index 84e5de76de..9cc415f581 100644 --- a/osu.Game/Screens/OsuScreenDependencies.cs +++ b/osu.Game/Screens/OsuScreenDependencies.cs @@ -1,10 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; namespace osu.Game.Screens { @@ -14,6 +16,8 @@ namespace osu.Game.Screens public Bindable Ruleset { get; } + public Bindable> SelectedMods { get; } + public OsuScreenDependencies(bool requireLease, IReadOnlyDependencyContainer parent) : base(parent) { @@ -21,20 +25,21 @@ namespace osu.Game.Screens { Beatmap = parent.Get>()?.GetBoundCopy(); if (Beatmap == null) - { Cache(Beatmap = parent.Get>().BeginLease(false)); - } Ruleset = parent.Get>()?.GetBoundCopy(); if (Ruleset == null) - { Cache(Ruleset = parent.Get>().BeginLease(true)); - } + + SelectedMods = parent.Get>>()?.GetBoundCopy(); + if (SelectedMods == null) + Cache(SelectedMods = parent.Get>>().BeginLease(true)); } else { Beatmap = (parent.Get>() ?? parent.Get>()).GetBoundCopy(); Ruleset = (parent.Get>() ?? parent.Get>()).GetBoundCopy(); + SelectedMods = (parent.Get>>() ?? parent.Get>>()).GetBoundCopy(); } } } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index c13222c6de..b17286f69e 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using osu.Framework; @@ -22,7 +23,7 @@ namespace osu.Game.Screens.Play /// public class GameplayClockContainer : Container { - private readonly WorkingBeatmap beatmap; + private readonly IEnumerable mods; /// /// The original source (usually a 's track). @@ -54,9 +55,9 @@ namespace osu.Game.Screens.Play private readonly FramedOffsetClock offsetClock; - public GameplayClockContainer(WorkingBeatmap beatmap, double gameplayStartTime) + public GameplayClockContainer(WorkingBeatmap beatmap, IEnumerable mods, double gameplayStartTime) { - this.beatmap = beatmap; + this.mods = mods; RelativeSizeAxes = Axes.Both; @@ -154,7 +155,7 @@ namespace osu.Game.Screens.Play else sourceClock.Rate = UserPlaybackRate.Value; - foreach (var mod in beatmap.Mods.Value.OfType()) + foreach (var mod in mods.OfType()) mod.ApplyToClock(sourceClock); } } diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index a7b7f96e7a..aa4375f652 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -2,16 +2,17 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; -using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play.HUD; @@ -42,7 +43,7 @@ namespace osu.Game.Screens.Play public Action RequestSeek; - public HUDOverlay(ScoreProcessor scoreProcessor, DrawableRuleset drawableRuleset, WorkingBeatmap working) + public HUDOverlay(ScoreProcessor scoreProcessor, DrawableRuleset drawableRuleset, IEnumerable mods) { RelativeSizeAxes = Axes.Both; @@ -96,7 +97,7 @@ namespace osu.Game.Screens.Play Progress.AllowSeeking = drawableRuleset.HasReplayLoaded.Value; Progress.RequestSeek = time => RequestSeek(time); - ModDisplay.Current.BindTo(working.Mods); + ModDisplay.Current.Value = mods; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0eebefec86..bf2bbc58b8 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -102,7 +102,7 @@ namespace osu.Game.Screens.Play if (!ScoreProcessor.Mode.Disabled) config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode); - InternalChild = GameplayClockContainer = new GameplayClockContainer(working, DrawableRuleset.GameplayStartTime); + InternalChild = GameplayClockContainer = new GameplayClockContainer(working, SelectedMods.Value, DrawableRuleset.GameplayStartTime); GameplayClockContainer.Children = new[] { @@ -123,7 +123,7 @@ namespace osu.Game.Screens.Play }, // display the cursor above some HUD elements. DrawableRuleset.Cursor?.CreateProxy() ?? new Container(), - HUDOverlay = new HUDOverlay(ScoreProcessor, DrawableRuleset, working) + HUDOverlay = new HUDOverlay(ScoreProcessor, DrawableRuleset, SelectedMods.Value) { HoldToQuit = { Action = performUserRequestedExit }, PlayerSettingsOverlay = { PlaybackSettings = { UserPlaybackRate = { BindTarget = GameplayClockContainer.UserPlaybackRate } } }, @@ -170,7 +170,7 @@ namespace osu.Game.Screens.Play ScoreProcessor.AllJudged += onCompletion; ScoreProcessor.Failed += onFail; - foreach (var mod in Beatmap.Value.Mods.Value.OfType()) + foreach (var mod in SelectedMods.Value.OfType()) mod.ApplyToScoreProcessor(ScoreProcessor); } @@ -192,7 +192,7 @@ namespace osu.Game.Screens.Play try { - DrawableRuleset = rulesetInstance.CreateDrawableRulesetWith(working); + DrawableRuleset = rulesetInstance.CreateDrawableRulesetWith(working, SelectedMods.Value); } catch (BeatmapInvalidForRulesetException) { @@ -200,7 +200,7 @@ namespace osu.Game.Screens.Play // let's try again forcing the beatmap's ruleset. ruleset = beatmap.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); - DrawableRuleset = rulesetInstance.CreateDrawableRulesetWith(Beatmap.Value); + DrawableRuleset = rulesetInstance.CreateDrawableRulesetWith(Beatmap.Value, SelectedMods.Value); } if (!DrawableRuleset.Objects.Any()) @@ -271,7 +271,7 @@ namespace osu.Game.Screens.Play { Beatmap = Beatmap.Value.BeatmapInfo, Ruleset = ruleset, - Mods = Beatmap.Value.Mods.Value.ToArray(), + Mods = SelectedMods.Value.ToArray(), User = api.LocalUser.Value, }; @@ -325,7 +325,7 @@ namespace osu.Game.Screens.Play private bool onFail() { - if (Beatmap.Value.Mods.Value.OfType().Any(m => !m.AllowFail)) + if (SelectedMods.Value.OfType().Any(m => !m.AllowFail)) return false; GameplayClockContainer.Stop(); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index e9ee5d3fa8..70f5736786 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; @@ -16,6 +17,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Mods; using osu.Game.Screens.Menu; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.PlayerSettings; @@ -66,7 +68,7 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - info = new BeatmapMetadataDisplay(Beatmap.Value) + info = new BeatmapMetadataDisplay(Beatmap.Value, SelectedMods.Value) { Alpha = 0, Anchor = Anchor.Centre, @@ -299,6 +301,7 @@ namespace osu.Game.Screens.Play } private readonly WorkingBeatmap beatmap; + private readonly IEnumerable mods; private LoadingAnimation loading; private Sprite backgroundSprite; private ModDisplay modDisplay; @@ -320,9 +323,10 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, IEnumerable mods) { this.beatmap = beatmap; + this.mods = mods; } [BackgroundDependencyLoader] @@ -403,7 +407,7 @@ namespace osu.Game.Screens.Play Origin = Anchor.TopCentre, AutoSizeAxes = Axes.Both, Margin = new MarginPadding { Top = 20 }, - Current = beatmap.Mods + Current = { Value = mods } } }, } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index d32387c1d3..03cf767062 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -25,6 +25,7 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; namespace osu.Game.Screens.Select @@ -309,12 +310,12 @@ namespace osu.Game.Screens.Select try { // Try to get the beatmap with the user's ruleset - playableBeatmap = beatmap.GetPlayableBeatmap(ruleset); + playableBeatmap = beatmap.GetPlayableBeatmap(ruleset, Enumerable.Empty()); } catch (BeatmapInvalidForRulesetException) { // Can't be converted to the user's ruleset, so use the beatmap's own ruleset - playableBeatmap = beatmap.GetPlayableBeatmap(beatmap.BeatmapInfo.Ruleset); + playableBeatmap = beatmap.GetPlayableBeatmap(beatmap.BeatmapInfo.Ruleset, Enumerable.Empty()); } labels.AddRange(playableBeatmap.GetStatistics().Select(s => new InfoLabel(s))); diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index fa5dc4c1d1..bc33750d6f 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using Humanizer; using osu.Framework.Allocation; @@ -26,9 +25,6 @@ namespace osu.Game.Screens.Select [Resolved(typeof(Room))] protected Bindable CurrentItem { get; private set; } - [Resolved] - private Bindable> selectedMods { get; set; } - [Resolved] private BeatmapManager beatmaps { get; set; } @@ -65,7 +61,7 @@ namespace osu.Game.Screens.Select { Ruleset.Value = CurrentItem.Value.Ruleset; Beatmap.Value = beatmaps.GetWorkingBeatmap(CurrentItem.Value.Beatmap); - Beatmap.Value.Mods.Value = selectedMods.Value = CurrentItem.Value.RequiredMods ?? Enumerable.Empty(); + SelectedMods.Value = CurrentItem.Value.RequiredMods ?? Enumerable.Empty(); } Beatmap.Disabled = true; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index b60e693cbf..64e64edce2 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -394,7 +394,7 @@ namespace osu.Game.Screens.Select { Logger.Log($"ruleset changed from \"{decoupledRuleset.Value}\" to \"{ruleset}\""); - Beatmap.Value.Mods.Value = Enumerable.Empty(); + SelectedMods.Value = Enumerable.Empty(); decoupledRuleset.Value = ruleset; // force a filter before attempting to change the beatmap. @@ -530,7 +530,7 @@ namespace osu.Game.Screens.Select Beatmap.Value.Track.Looping = false; SelectedMods.UnbindAll(); - Beatmap.Value.Mods.Value = new Mod[] { }; + base.SelectedMods.Value = Enumerable.Empty(); return false; } @@ -557,8 +557,6 @@ namespace osu.Game.Screens.Select /// The working beatmap. protected virtual void UpdateBeatmap(WorkingBeatmap beatmap) { - beatmap.Mods.BindTo(SelectedMods); - Logger.Log($"working beatmap updated to {beatmap}"); if (Background is BackgroundScreenBeatmap backgroundModeBeatmap) diff --git a/osu.Game/Tests/Visual/AllPlayersTestCase.cs b/osu.Game/Tests/Visual/AllPlayersTestCase.cs index 4ef9b346b0..21955da6e9 100644 --- a/osu.Game/Tests/Visual/AllPlayersTestCase.cs +++ b/osu.Game/Tests/Visual/AllPlayersTestCase.cs @@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual var working = CreateWorkingBeatmap(beatmap, Clock); Beatmap.Value = working; - Beatmap.Value.Mods.Value = new[] { r.GetAllMods().First(m => m is ModNoFail) }; + SelectedMods.Value = new[] { r.GetAllMods().First(m => m is ModNoFail) }; Player?.Exit(); Player = null; diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index 495c5dfbad..9dab981ed5 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -10,16 +11,26 @@ using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; namespace osu.Game.Tests.Visual { public abstract class OsuTestCase : TestCase { + [Cached(typeof(Bindable))] + [Cached(typeof(IBindable))] private readonly OsuTestBeatmap beatmap = new OsuTestBeatmap(new DummyWorkingBeatmap()); + protected BindableBeatmap Beatmap => beatmap; + [Cached] + [Cached(typeof(IBindable))] protected readonly Bindable Ruleset = new Bindable(); + [Cached] + [Cached(Type = typeof(IBindable>))] + protected readonly Bindable> SelectedMods = new Bindable>(); + protected DependencyContainer Dependencies { get; private set; } private readonly Lazy localStorage; @@ -32,12 +43,6 @@ namespace osu.Game.Tests.Visual // This is the earliest we can get OsuGameBase, which is used by the dummy working beatmap to find textures beatmap.Default = new DummyWorkingBeatmap(Dependencies.Get()); - Dependencies.CacheAs>(beatmap); - Dependencies.CacheAs>(beatmap); - - Dependencies.CacheAs(Ruleset); - Dependencies.CacheAs>(Ruleset); - return Dependencies; } diff --git a/osu.Game/Tests/Visual/PlayerTestCase.cs b/osu.Game/Tests/Visual/PlayerTestCase.cs index 3bf707fade..409e79b4a5 100644 --- a/osu.Game/Tests/Visual/PlayerTestCase.cs +++ b/osu.Game/Tests/Visual/PlayerTestCase.cs @@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual Beatmap.Value = new TestWorkingBeatmap(beatmap, Clock); if (!AllowFail) - Beatmap.Value.Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) }; + SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) }; Player = CreatePlayer(ruleset); LoadScreen(Player); From ad124bfeec6745939b172f310618d2b6c5a99656 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 8 Apr 2019 19:16:34 +0900 Subject: [PATCH 0421/2854] Reimplement select mod track adjustments --- osu.Game/Beatmaps/WorkingBeatmap.cs | 19 +------------------ osu.Game/OsuGame.cs | 2 +- osu.Game/Overlays/MusicController.cs | 22 ++++++++++++++++++++-- osu.Game/Screens/OsuScreen.cs | 6 +++--- osu.Game/Screens/Select/SongSelect.cs | 15 ++++++++------- 5 files changed, 33 insertions(+), 31 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index bb13a8c0e7..ec0a76b52b 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -46,14 +46,7 @@ namespace osu.Game.Beatmaps return b; }); - track = new RecyclableLazy(() => - { - // we want to ensure that we always have a track, even if it's a fake one. - var t = GetTrack() ?? new VirtualBeatmapTrack(Beatmap); - // applyRateAdjustments(t); - return t; - }); - + track = new RecyclableLazy(() => GetTrack() ?? new VirtualBeatmapTrack(Beatmap)); background = new RecyclableLazy(GetBackground, BackgroundStillValid); waveform = new RecyclableLazy(GetWaveform); storyboard = new RecyclableLazy(GetStoryboard); @@ -183,16 +176,6 @@ namespace osu.Game.Beatmaps /// public void RecycleTrack() => track.Recycle(); - // private void applyRateAdjustments(Track t = null) - // { - // if (t == null && track.IsResultAvailable) t = Track; - // if (t == null) return; - // - // t.ResetSpeedAdjustments(); - // foreach (var mod in Mods.Value.OfType()) - // mod.ApplyToClock(t); - // } - public class RecyclableLazy { private Lazy lazy; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2cef4f66a9..4d889677a5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -113,7 +113,7 @@ namespace osu.Game // todo: move this to SongSelect once Screen has the ability to unsuspend. [Cached] [Cached(Type = typeof(IBindable>))] - private readonly Bindable> selectedMods = new Bindable>(new Mod[] { }); + private readonly Bindable> selectedMods = new Bindable>(Enumerable.Empty()); public OsuGame(string[] args = null) { diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index b24c6c3508..0ad4da2ce9 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -22,6 +22,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Music; +using osu.Game.Rulesets.Mods; using osuTK; using osuTK.Graphics; @@ -54,7 +55,11 @@ namespace osu.Game.Overlays private Container dragContainer; private Container playerContainer; - private readonly Bindable beatmap = new Bindable(); + [Resolved] + private Bindable beatmap { get; set; } + + [Resolved] + private IBindable> selectedMods { get; set; } /// /// Provide a source for the toolbar height. @@ -73,7 +78,6 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load(Bindable beatmap, BeatmapManager beatmaps, OsuColour colours) { - this.beatmap.BindTo(beatmap); this.beatmaps = beatmaps; Children = new Drawable[] @@ -231,6 +235,7 @@ namespace osu.Game.Overlays { beatmap.BindValueChanged(beatmapChanged, true); beatmap.BindDisabledChanged(beatmapDisabledChanged, true); + selectedMods.BindValueChanged(_ => updateAudioAdjustments(), true); base.LoadComplete(); } @@ -354,10 +359,23 @@ namespace osu.Game.Overlays progressBar.CurrentTime = 0; updateDisplay(current, direction); + updateAudioAdjustments(); queuedDirection = null; } + private void updateAudioAdjustments() + { + var track = current?.Track; + if (track == null) + return; + + track.ResetSpeedAdjustments(); + + foreach (var mod in selectedMods.Value.OfType()) + mod.ApplyToClock(track); + } + private void currentTrackCompleted() => Schedule(() => { if (!current.Track.Looping && !beatmap.Disabled && beatmapSets.Any()) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 33e725eb41..bd7f326b50 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -61,11 +61,11 @@ namespace osu.Game.Screens public virtual float BackgroundParallaxAmount => 1; - public Bindable Beatmap { get; set; } + public Bindable Beatmap { get; private set; } - public Bindable Ruleset { get; set; } + public Bindable Ruleset { get; private set; } - public Bindable> SelectedMods { get; set; } + public Bindable> SelectedMods { get; private set; } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 64e64edce2..0b6bb6f570 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -83,9 +83,7 @@ namespace osu.Game.Screens.Select private readonly Bindable decoupledRuleset = new Bindable(); - [Cached] - [Cached(Type = typeof(IBindable>))] - protected readonly Bindable> SelectedMods = new Bindable>(new Mod[] { }); + protected readonly Bindable> SelectedMods = new Bindable>(Enumerable.Empty()); protected SongSelect() { @@ -217,11 +215,8 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader(true)] - private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, Bindable> selectedMods) + private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins) { - if (selectedMods != null) - SelectedMods.BindTo(selectedMods); - if (Footer != null) { Footer.AddButton(@"mods", colours.Yellow, ModSelect, Key.F1); @@ -269,6 +264,12 @@ namespace osu.Game.Screens.Select protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + + SelectedMods.BindTo(base.SelectedMods); + + dependencies.CacheAs(SelectedMods); + dependencies.CacheAs>>(SelectedMods); + dependencies.CacheAs(this); dependencies.CacheAs(decoupledRuleset); dependencies.CacheAs>(decoupledRuleset); From cbb3fdaca8f0fe2bc05f7839c5e9d57c07dac573 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 9 Apr 2019 12:44:03 +0900 Subject: [PATCH 0422/2854] Fix various crashes due to bindable being disabled --- .../Visual/SongSelect/TestCasePlaySongSelect.cs | 8 ++------ osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 4 ---- osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs | 7 +------ osu.Game/Screens/Select/MatchSongSelect.cs | 2 ++ osu.Game/Screens/Select/SongSelect.cs | 17 ++++++++--------- 5 files changed, 13 insertions(+), 25 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs index bc644da9d5..a70529b384 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs @@ -35,10 +35,6 @@ namespace osu.Game.Tests.Visual.SongSelect private WorkingBeatmap defaultBeatmap; private DatabaseContextFactory factory; - [Cached] - [Cached(Type = typeof(IBindable>))] - private readonly Bindable> selectedMods = new Bindable>(new Mod[] { }); - public override IReadOnlyList RequiredTypes => new[] { typeof(Screens.Select.SongSelect), @@ -185,7 +181,7 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddAssert("mods changed before ruleset", () => modChangeIndex < rulesetChangeIndex); - AddAssert("empty mods", () => !selectedMods.Value.Any()); + AddAssert("empty mods", () => !SelectedMods.Value.Any()); void onModChange(ValueChangedEvent> e) => modChangeIndex = actionIndex++; void onRulesetChange(ValueChangedEvent e) => rulesetChangeIndex = actionIndex--; @@ -218,7 +214,7 @@ namespace osu.Game.Tests.Visual.SongSelect private static int importId; private int getImportId() => ++importId; - private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => selectedMods.Value = mods); + private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => SelectedMods.Value = mods); private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id)); diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index b3f9a4ae21..e5ab46ebe2 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -42,9 +41,6 @@ namespace osu.Game.Screens.Multi.Match [Resolved(typeof(Room))] protected Bindable CurrentItem { get; private set; } - [Resolved] - protected Bindable> SelectedMods { get; private set; } - [Resolved] private BeatmapManager beatmapManager { get; set; } diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index d5b8f1f0c8..8ce68ac593 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; @@ -14,7 +13,6 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Multi.Ranking; using osu.Game.Screens.Play; @@ -37,9 +35,6 @@ namespace osu.Game.Screens.Multi.Play [Resolved] private IBindable ruleset { get; set; } - [Resolved] - private Bindable> selectedMods { get; set; } - public TimeshiftPlayer(PlaylistItem playlistItem) { this.playlistItem = playlistItem; @@ -61,7 +56,7 @@ namespace osu.Game.Screens.Multi.Play if (ruleset.Value.ID != playlistItem.Ruleset.ID) throw new InvalidOperationException("Current Ruleset does not match PlaylistItem's Ruleset"); - if (!playlistItem.RequiredMods.All(m => selectedMods.Value.Contains(m))) + if (!playlistItem.RequiredMods.All(m => SelectedMods.Value.Contains(m))) throw new InvalidOperationException("Current Mods do not match PlaylistItem's RequiredMods"); var req = new CreateRoomScoreRequest(roomId.Value ?? 0, playlistItem.ID); diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index bc33750d6f..1011f6f32d 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -66,6 +66,7 @@ namespace osu.Game.Screens.Select Beatmap.Disabled = true; Ruleset.Disabled = true; + SelectedMods.Disabled = true; return false; } @@ -76,6 +77,7 @@ namespace osu.Game.Screens.Select Beatmap.Disabled = false; Ruleset.Disabled = false; + SelectedMods.Disabled = false; } } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 0b6bb6f570..2bb646750c 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -83,7 +83,9 @@ namespace osu.Game.Screens.Select private readonly Bindable decoupledRuleset = new Bindable(); - protected readonly Bindable> SelectedMods = new Bindable>(Enumerable.Empty()); + [Cached] + [Cached(Type = typeof(IBindable>))] + private readonly Bindable> selectedMods = new Bindable>(Enumerable.Empty()); // Bound to the game's mods, but is not reset on exiting protected SongSelect() { @@ -217,6 +219,8 @@ namespace osu.Game.Screens.Select [BackgroundDependencyLoader(true)] private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins) { + selectedMods.BindTo(SelectedMods); + if (Footer != null) { Footer.AddButton(@"mods", colours.Yellow, ModSelect, Key.F1); @@ -265,11 +269,6 @@ namespace osu.Game.Screens.Select { dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - SelectedMods.BindTo(base.SelectedMods); - - dependencies.CacheAs(SelectedMods); - dependencies.CacheAs>>(SelectedMods); - dependencies.CacheAs(this); dependencies.CacheAs(decoupledRuleset); dependencies.CacheAs>(decoupledRuleset); @@ -395,7 +394,7 @@ namespace osu.Game.Screens.Select { Logger.Log($"ruleset changed from \"{decoupledRuleset.Value}\" to \"{ruleset}\""); - SelectedMods.Value = Enumerable.Empty(); + selectedMods.Value = Enumerable.Empty(); decoupledRuleset.Value = ruleset; // force a filter before attempting to change the beatmap. @@ -530,8 +529,8 @@ namespace osu.Game.Screens.Select if (Beatmap.Value.Track != null) Beatmap.Value.Track.Looping = false; - SelectedMods.UnbindAll(); - base.SelectedMods.Value = Enumerable.Empty(); + selectedMods.UnbindAll(); + SelectedMods.Value = Enumerable.Empty(); return false; } From 56496d28ba9575ea75b37dd0e02243433a292bea Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 9 Apr 2019 12:50:54 +0900 Subject: [PATCH 0423/2854] Reset mods when exiting match --- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index e5ab46ebe2..5f29044c76 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -178,6 +178,9 @@ namespace osu.Game.Screens.Multi.Match public override bool OnExiting(IScreen next) { RoomManager?.PartRoom(); + + SelectedMods.Value = Enumerable.Empty(); + return base.OnExiting(next); } From 1c952e58cc45710bf63d0226972d71818b33e778 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 9 Apr 2019 13:15:48 +0900 Subject: [PATCH 0424/2854] Fix testcase failures --- osu.Game/Tests/Visual/OsuTestCase.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index 9dab981ed5..1a8403db40 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual [Cached] [Cached(Type = typeof(IBindable>))] - protected readonly Bindable> SelectedMods = new Bindable>(); + protected readonly Bindable> SelectedMods = new Bindable>(Enumerable.Empty()); protected DependencyContainer Dependencies { get; private set; } @@ -38,12 +38,10 @@ namespace osu.Game.Tests.Visual protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - Dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - // This is the earliest we can get OsuGameBase, which is used by the dummy working beatmap to find textures - beatmap.Default = new DummyWorkingBeatmap(Dependencies.Get()); + beatmap.Default = new DummyWorkingBeatmap(parent.Get()); - return Dependencies; + return Dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); } protected OsuTestCase() From 4c571acd671a293dfbe3a6aee6963475bcbb67cc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 9 Apr 2019 13:33:16 +0900 Subject: [PATCH 0425/2854] Reinstantiate mods for every player --- osu.Game/Screens/Play/Player.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index bf2bbc58b8..d8e31b7ad6 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -69,6 +70,10 @@ namespace osu.Game.Screens.Play protected GameplayClockContainer GameplayClockContainer { get; private set; } + [Cached] + [Cached(Type = typeof(IBindable>))] + protected readonly Bindable> SelectedMods = new Bindable>(Enumerable.Empty()); + private readonly bool allowPause; private readonly bool showResults; @@ -88,6 +93,8 @@ namespace osu.Game.Screens.Play { this.api = api; + SelectedMods.Value = base.SelectedMods.Value.Select(m => m.CreateCopy()).ToArray(); + WorkingBeatmap working = loadBeatmap(); if (working == null) From d8ec1e73a3025c162e10cebbf8606af91c0ac88c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 9 Apr 2019 13:50:54 +0900 Subject: [PATCH 0426/2854] Cleanup TestCasePlayerLoader --- .../Visual/Gameplay/TestCasePlayerLoader.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs index 41d484e21f..aba689b241 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs @@ -2,10 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System.Threading; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Screens; -using osu.Game.Beatmaps; using osu.Game.Screens; using osu.Game.Screens.Play; @@ -21,23 +21,15 @@ namespace osu.Game.Tests.Visual.Gameplay InputManager.Add(stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }); } - [BackgroundDependencyLoader] - private void load(OsuGameBase game) + [Test] + public void TestLoadContinuation() { - Beatmap.Value = new DummyWorkingBeatmap(game); - AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => new Player(false, false)))); - AddUntilStep("wait for current", () => loader.IsCurrentScreen()); - AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre)); - AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen()); - AddStep("exit loader", () => loader.Exit()); - AddUntilStep("wait for no longer alive", () => !loader.IsAlive); - AddStep("load slow dummy beatmap", () => { SlowLoadPlayer slow = null; From aa2c97b859eec9cc6778520a45b13bb5d1c21341 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 9 Apr 2019 15:05:03 +0900 Subject: [PATCH 0427/2854] Add mod reinstantiation testcase --- .../Visual/Gameplay/TestCasePlayerLoader.cs | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs index aba689b241..fba4aca343 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs @@ -1,11 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; using System.Threading; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Screens; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; using osu.Game.Screens; using osu.Game.Screens.Play; @@ -42,6 +47,81 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen()); } + [Test] + public void TestModReinstantiation() + { + TestPlayer player = null; + TestMod gameMod = null; + TestMod playerMod1 = null; + TestMod playerMod2 = null; + + AddStep("load player", () => + { + SelectedMods.Value = new[] { gameMod = new TestMod() }; + InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre); + stack.Push(new PlayerLoader(() => player = new TestPlayer())); + }); + + AddUntilStep("wait for player to become current", () => + { + if (player.IsCurrentScreen()) + { + playerMod1 = (TestMod)player.SelectedMods.Value.Single(); + return true; + } + + return false; + }); + + AddAssert("game mods not applied", () => gameMod.Applied == false); + AddAssert("player mods applied", () => playerMod1.Applied); + + AddStep("restart player", () => + { + player = null; + player.Restart(); + }); + + AddUntilStep("wait for player to become current", () => + { + if (player.IsCurrentScreen()) + { + playerMod2 = (TestMod)player.SelectedMods.Value.Single(); + return true; + } + + return false; + }); + + AddAssert("game mods not applied", () => gameMod.Applied == false); + AddAssert("player has different mods", () => playerMod1 != playerMod2); + AddAssert("player mods applied", () => playerMod2.Applied); + } + + private class TestMod : Mod, IApplicableToScoreProcessor + { + public override string Name => string.Empty; + public override string Acronym => string.Empty; + public override double ScoreMultiplier => 1; + + public bool Applied { get; private set; } + + public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) + { + Applied = true; + } + } + + private class TestPlayer : Player + { + public new Bindable> SelectedMods => base.SelectedMods; + + public TestPlayer() + : base(false, false) + { + } + } + protected class SlowLoadPlayer : Player { public bool Ready; From 0603ed9ab755dc45c0d25822e4de2c5ec84c9721 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 9 Apr 2019 16:27:10 +0900 Subject: [PATCH 0428/2854] Fix post-merge errors --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 1 + osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index ecdafb0fa2..27546fa424 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Configuration; diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index c0d9ecad3a..e70bf4c572 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Containers; From 4310f07a5cbba63eba921db02ed3e4ff9ce9498d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 10 Apr 2019 12:03:57 +0900 Subject: [PATCH 0429/2854] Rename SelectedMods -> Mods --- .../TestCaseAutoJuiceStream.cs | 2 +- osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs | 3 +-- .../TestCaseHitCircleHidden.cs | 2 +- osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs | 3 +-- .../TestCaseSliderHidden.cs | 2 +- osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs | 3 +-- .../TestCaseSpinnerHidden.cs | 2 +- .../Background/TestCaseBackgroundScreenBeatmap.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs | 2 +- .../Visual/SongSelect/TestCasePlaySongSelect.cs | 8 ++++---- osu.Game/OsuGame.cs | 4 ++-- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 6 +++--- osu.Game/Overlays/MusicController.cs | 6 +++--- osu.Game/Rulesets/UI/Playfield.cs | 4 ++-- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 4 ++-- osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs | 2 +- osu.Game/Screens/OsuScreen.cs | 4 ++-- osu.Game/Screens/OsuScreenDependencies.cs | 10 +++++----- osu.Game/Screens/Play/Player.cs | 14 +++++++------- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- osu.Game/Screens/Select/MatchSongSelect.cs | 8 ++++---- osu.Game/Screens/Select/PlaySongSelect.cs | 4 ++-- osu.Game/Screens/Select/SongSelect.cs | 10 +++++----- osu.Game/Tests/Visual/AllPlayersTestCase.cs | 2 +- osu.Game/Tests/Visual/OsuTestCase.cs | 2 +- osu.Game/Tests/Visual/PlayerTestCase.cs | 2 +- 26 files changed, 55 insertions(+), 58 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs index a696ba4e7e..0b20f34eb1 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Tests protected override Player CreatePlayer(Ruleset ruleset) { - SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); + Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); return base.CreatePlayer(ruleset); } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs index e1e854e8dc..31f3146046 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs @@ -30,7 +30,6 @@ namespace osu.Game.Rulesets.Osu.Tests protected override Container Content => content; private int depthIndex; - protected readonly List Mods = new List(); public TestCaseHitCircle() { @@ -68,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Tests Depth = depthIndex++ }; - foreach (var mod in Mods.OfType()) + foreach (var mod in Mods.Value.OfType()) mod.ApplyToDrawableHitObjects(new[] { drawable }); Add(drawable); diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleHidden.cs index 26d9b5ae91..7391c0f11a 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleHidden.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Tests public TestCaseHitCircleHidden() { - Mods.Add(new OsuModHidden()); + Mods.Value = new[] { new OsuModHidden() }; } } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs index 35e8f3e17e..0f02050605 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs @@ -44,7 +44,6 @@ namespace osu.Game.Rulesets.Osu.Tests protected override Container Content => content; private int depthIndex; - protected readonly List Mods = new List(); public TestCaseSlider() { @@ -292,7 +291,7 @@ namespace osu.Game.Rulesets.Osu.Tests Depth = depthIndex++ }; - foreach (var mod in Mods.OfType()) + foreach (var mod in Mods.Value.OfType()) mod.ApplyToDrawableHitObjects(new[] { drawable }); drawable.OnNewResult += onNewResult; diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderHidden.cs index ba5bd48c51..65a8005407 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderHidden.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Tests public TestCaseSliderHidden() { - Mods.Add(new OsuModHidden()); + Mods.Value = new[] { new OsuModHidden() }; } } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs index e8b534bba9..ab33d1e518 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs @@ -31,7 +31,6 @@ namespace osu.Game.Rulesets.Osu.Tests protected override Container Content => content; private int depthIndex; - protected readonly List Mods = new List(); public TestCaseSpinner() { @@ -57,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Tests Depth = depthIndex++ }; - foreach (var mod in Mods.OfType()) + foreach (var mod in Mods.Value.OfType()) mod.ApplyToDrawableHitObjects(new[] { drawable }); Add(drawable); diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerHidden.cs index 6136ce1639..24e3bcb47b 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerHidden.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Tests public TestCaseSpinnerHidden() { - Mods.Add(new OsuModHidden()); + Mods.Value = new[] { new OsuModHidden() }; } } } diff --git a/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs index e3875421b1..283fd4c8b9 100644 --- a/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs +++ b/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs @@ -267,7 +267,7 @@ namespace osu.Game.Tests.Visual.Background AddUntilStep("Song select has selection", () => songSelect.Carousel.SelectedBeatmap != null); AddStep("Set default user settings", () => { - SelectedMods.Value = SelectedMods.Value.Concat(new[] { new OsuModNoFail() }); + Mods.Value = Mods.Value.Concat(new[] { new OsuModNoFail() }); songSelect.DimLevel.Value = 0.7f; songSelect.BlurLevel.Value = 0.4f; }); diff --git a/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs index c930e77e9e..df48834e1f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Gameplay { protected override Player CreatePlayer(Ruleset ruleset) { - SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); + Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); return new ScoreAccessiblePlayer(); } diff --git a/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs index a70529b384..e89b361104 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs @@ -171,17 +171,17 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("change ruleset", () => { - SelectedMods.ValueChanged += onModChange; + Mods.ValueChanged += onModChange; songSelect.Ruleset.ValueChanged += onRulesetChange; Ruleset.Value = new TaikoRuleset().RulesetInfo; - SelectedMods.ValueChanged -= onModChange; + Mods.ValueChanged -= onModChange; songSelect.Ruleset.ValueChanged -= onRulesetChange; }); AddAssert("mods changed before ruleset", () => modChangeIndex < rulesetChangeIndex); - AddAssert("empty mods", () => !SelectedMods.Value.Any()); + AddAssert("empty mods", () => !Mods.Value.Any()); void onModChange(ValueChangedEvent> e) => modChangeIndex = actionIndex++; void onRulesetChange(ValueChangedEvent e) => rulesetChangeIndex = actionIndex--; @@ -214,7 +214,7 @@ namespace osu.Game.Tests.Visual.SongSelect private static int importId; private int getImportId() => ++importId; - private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => SelectedMods.Value = mods); + private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => Mods.Value = mods); private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id)); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 4d889677a5..0785c588e5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -113,7 +113,7 @@ namespace osu.Game // todo: move this to SongSelect once Screen has the ability to unsuspend. [Cached] [Cached(Type = typeof(IBindable>))] - private readonly Bindable> selectedMods = new Bindable>(Enumerable.Empty()); + private readonly Bindable> mods = new Bindable>(Enumerable.Empty()); public OsuGame(string[] args = null) { @@ -294,7 +294,7 @@ namespace osu.Game { ruleset.Value = databasedScoreInfo.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap); - selectedMods.Value = databasedScoreInfo.Mods; + mods.Value = databasedScoreInfo.Mods; menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore))); }, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true); diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index aa41723ca6..9e97f4551a 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -42,19 +42,19 @@ namespace osu.Game.Overlays.Mods protected readonly FillFlowContainer ModSectionsContainer; - protected readonly Bindable> SelectedMods = new Bindable>(new Mod[] { }); + protected readonly Bindable> SelectedMods = new Bindable>(Enumerable.Empty()); protected readonly IBindable Ruleset = new Bindable(); [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, IBindable ruleset, AudioManager audio, Bindable> selectedMods) + private void load(OsuColour colours, IBindable ruleset, AudioManager audio, Bindable> mods) { LowMultiplierColour = colours.Red; HighMultiplierColour = colours.Green; UnrankedLabel.Colour = colours.Blue; Ruleset.BindTo(ruleset); - if (selectedMods != null) SelectedMods.BindTo(selectedMods); + if (mods != null) SelectedMods.BindTo(mods); sampleOn = audio.Sample.Get(@"UI/check-on"); sampleOff = audio.Sample.Get(@"UI/check-off"); diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 0ad4da2ce9..d9b669f753 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays private Bindable beatmap { get; set; } [Resolved] - private IBindable> selectedMods { get; set; } + private IBindable> mods { get; set; } /// /// Provide a source for the toolbar height. @@ -235,7 +235,7 @@ namespace osu.Game.Overlays { beatmap.BindValueChanged(beatmapChanged, true); beatmap.BindDisabledChanged(beatmapDisabledChanged, true); - selectedMods.BindValueChanged(_ => updateAudioAdjustments(), true); + mods.BindValueChanged(_ => updateAudioAdjustments(), true); base.LoadComplete(); } @@ -372,7 +372,7 @@ namespace osu.Game.Overlays track.ResetSpeedAdjustments(); - foreach (var mod in selectedMods.Value.OfType()) + foreach (var mod in mods.Value.OfType()) mod.ApplyToClock(track); } diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 6ea565af44..c9a05dae2c 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.UI private IBindable beatmap { get; set; } [Resolved] - private IBindable> selectedMods { get; set; } + private IBindable> mods { get; set; } [BackgroundDependencyLoader] private void load() @@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.UI base.Update(); if (beatmap != null) - foreach (var mod in selectedMods.Value) + foreach (var mod in mods.Value) if (mod is IUpdatableByPlayfield updatable) updatable.Update(this); } diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 5f29044c76..aad571cf87 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -179,7 +179,7 @@ namespace osu.Game.Screens.Multi.Match { RoomManager?.PartRoom(); - SelectedMods.Value = Enumerable.Empty(); + Mods.Value = Enumerable.Empty(); return base.OnExiting(next); } @@ -193,7 +193,7 @@ namespace osu.Game.Screens.Multi.Match var localBeatmap = e.NewValue?.Beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == e.NewValue.Beatmap.OnlineBeatmapID); Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); - SelectedMods.Value = e.NewValue?.RequiredMods ?? Enumerable.Empty(); + Mods.Value = e.NewValue?.RequiredMods ?? Enumerable.Empty(); if (e.NewValue?.Ruleset != null) Ruleset.Value = e.NewValue.Ruleset; } diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 8ce68ac593..9692dc513d 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Multi.Play if (ruleset.Value.ID != playlistItem.Ruleset.ID) throw new InvalidOperationException("Current Ruleset does not match PlaylistItem's Ruleset"); - if (!playlistItem.RequiredMods.All(m => SelectedMods.Value.Contains(m))) + if (!playlistItem.RequiredMods.All(m => Mods.Value.Contains(m))) throw new InvalidOperationException("Current Mods do not match PlaylistItem's RequiredMods"); var req = new CreateRoomScoreRequest(roomId.Value ?? 0, playlistItem.ID); diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index bd7f326b50..d1e700cf92 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -65,7 +65,7 @@ namespace osu.Game.Screens public Bindable Ruleset { get; private set; } - public Bindable> SelectedMods { get; private set; } + public Bindable> Mods { get; private set; } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { @@ -73,7 +73,7 @@ namespace osu.Game.Screens Beatmap = screenDependencies.Beatmap; Ruleset = screenDependencies.Ruleset; - SelectedMods = screenDependencies.SelectedMods; + Mods = screenDependencies.Mods; return base.CreateChildDependencies(screenDependencies); } diff --git a/osu.Game/Screens/OsuScreenDependencies.cs b/osu.Game/Screens/OsuScreenDependencies.cs index 9cc415f581..d7e1862c05 100644 --- a/osu.Game/Screens/OsuScreenDependencies.cs +++ b/osu.Game/Screens/OsuScreenDependencies.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens public Bindable Ruleset { get; } - public Bindable> SelectedMods { get; } + public Bindable> Mods { get; } public OsuScreenDependencies(bool requireLease, IReadOnlyDependencyContainer parent) : base(parent) @@ -31,15 +31,15 @@ namespace osu.Game.Screens if (Ruleset == null) Cache(Ruleset = parent.Get>().BeginLease(true)); - SelectedMods = parent.Get>>()?.GetBoundCopy(); - if (SelectedMods == null) - Cache(SelectedMods = parent.Get>>().BeginLease(true)); + Mods = parent.Get>>()?.GetBoundCopy(); + if (Mods == null) + Cache(Mods = parent.Get>>().BeginLease(true)); } else { Beatmap = (parent.Get>() ?? parent.Get>()).GetBoundCopy(); Ruleset = (parent.Get>() ?? parent.Get>()).GetBoundCopy(); - SelectedMods = (parent.Get>>() ?? parent.Get>>()).GetBoundCopy(); + Mods = (parent.Get>>() ?? parent.Get>>()).GetBoundCopy(); } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index bf2bbc58b8..5add641404 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -102,7 +102,7 @@ namespace osu.Game.Screens.Play if (!ScoreProcessor.Mode.Disabled) config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode); - InternalChild = GameplayClockContainer = new GameplayClockContainer(working, SelectedMods.Value, DrawableRuleset.GameplayStartTime); + InternalChild = GameplayClockContainer = new GameplayClockContainer(working, Mods.Value, DrawableRuleset.GameplayStartTime); GameplayClockContainer.Children = new[] { @@ -123,7 +123,7 @@ namespace osu.Game.Screens.Play }, // display the cursor above some HUD elements. DrawableRuleset.Cursor?.CreateProxy() ?? new Container(), - HUDOverlay = new HUDOverlay(ScoreProcessor, DrawableRuleset, SelectedMods.Value) + HUDOverlay = new HUDOverlay(ScoreProcessor, DrawableRuleset, Mods.Value) { HoldToQuit = { Action = performUserRequestedExit }, PlayerSettingsOverlay = { PlaybackSettings = { UserPlaybackRate = { BindTarget = GameplayClockContainer.UserPlaybackRate } } }, @@ -170,7 +170,7 @@ namespace osu.Game.Screens.Play ScoreProcessor.AllJudged += onCompletion; ScoreProcessor.Failed += onFail; - foreach (var mod in SelectedMods.Value.OfType()) + foreach (var mod in Mods.Value.OfType()) mod.ApplyToScoreProcessor(ScoreProcessor); } @@ -192,7 +192,7 @@ namespace osu.Game.Screens.Play try { - DrawableRuleset = rulesetInstance.CreateDrawableRulesetWith(working, SelectedMods.Value); + DrawableRuleset = rulesetInstance.CreateDrawableRulesetWith(working, Mods.Value); } catch (BeatmapInvalidForRulesetException) { @@ -200,7 +200,7 @@ namespace osu.Game.Screens.Play // let's try again forcing the beatmap's ruleset. ruleset = beatmap.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); - DrawableRuleset = rulesetInstance.CreateDrawableRulesetWith(Beatmap.Value, SelectedMods.Value); + DrawableRuleset = rulesetInstance.CreateDrawableRulesetWith(Beatmap.Value, Mods.Value); } if (!DrawableRuleset.Objects.Any()) @@ -271,7 +271,7 @@ namespace osu.Game.Screens.Play { Beatmap = Beatmap.Value.BeatmapInfo, Ruleset = ruleset, - Mods = SelectedMods.Value.ToArray(), + Mods = Mods.Value.ToArray(), User = api.LocalUser.Value, }; @@ -325,7 +325,7 @@ namespace osu.Game.Screens.Play private bool onFail() { - if (SelectedMods.Value.OfType().Any(m => !m.AllowFail)) + if (Mods.Value.OfType().Any(m => !m.AllowFail)) return false; GameplayClockContainer.Stop(); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 70f5736786..2096f3f0f8 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -68,7 +68,7 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - info = new BeatmapMetadataDisplay(Beatmap.Value, SelectedMods.Value) + info = new BeatmapMetadataDisplay(Beatmap.Value, Mods.Value) { Alpha = 0, Anchor = Anchor.Centre, diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index 1011f6f32d..4468f5704f 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.Select RulesetID = Ruleset.Value.ID ?? 0 }; - item.RequiredMods.AddRange(SelectedMods.Value); + item.RequiredMods.AddRange(Mods.Value); Selected?.Invoke(item); @@ -61,12 +61,12 @@ namespace osu.Game.Screens.Select { Ruleset.Value = CurrentItem.Value.Ruleset; Beatmap.Value = beatmaps.GetWorkingBeatmap(CurrentItem.Value.Beatmap); - SelectedMods.Value = CurrentItem.Value.RequiredMods ?? Enumerable.Empty(); + Mods.Value = CurrentItem.Value.RequiredMods ?? Enumerable.Empty(); } Beatmap.Disabled = true; Ruleset.Disabled = true; - SelectedMods.Disabled = true; + Mods.Disabled = true; return false; } @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Select Beatmap.Disabled = false; Ruleset.Disabled = false; - SelectedMods.Disabled = false; + Mods.Disabled = false; } } } diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 340ceb6864..44e38edda8 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -52,10 +52,10 @@ namespace osu.Game.Screens.Select var auto = Ruleset.Value.CreateInstance().GetAutoplayMod(); var autoType = auto.GetType(); - var mods = SelectedMods.Value; + var mods = Mods.Value; if (mods.All(m => m.GetType() != autoType)) { - SelectedMods.Value = mods.Append(auto); + Mods.Value = mods.Append(auto); removeAutoModOnResume = true; } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 2bb646750c..6ad5b54e65 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -85,7 +85,7 @@ namespace osu.Game.Screens.Select [Cached] [Cached(Type = typeof(IBindable>))] - private readonly Bindable> selectedMods = new Bindable>(Enumerable.Empty()); // Bound to the game's mods, but is not reset on exiting + private readonly Bindable> mods = new Bindable>(Enumerable.Empty()); // Bound to the game's mods, but is not reset on exiting protected SongSelect() { @@ -219,7 +219,7 @@ namespace osu.Game.Screens.Select [BackgroundDependencyLoader(true)] private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins) { - selectedMods.BindTo(SelectedMods); + mods.BindTo(Mods); if (Footer != null) { @@ -394,7 +394,7 @@ namespace osu.Game.Screens.Select { Logger.Log($"ruleset changed from \"{decoupledRuleset.Value}\" to \"{ruleset}\""); - selectedMods.Value = Enumerable.Empty(); + mods.Value = Enumerable.Empty(); decoupledRuleset.Value = ruleset; // force a filter before attempting to change the beatmap. @@ -529,8 +529,8 @@ namespace osu.Game.Screens.Select if (Beatmap.Value.Track != null) Beatmap.Value.Track.Looping = false; - selectedMods.UnbindAll(); - SelectedMods.Value = Enumerable.Empty(); + mods.UnbindAll(); + Mods.Value = Enumerable.Empty(); return false; } diff --git a/osu.Game/Tests/Visual/AllPlayersTestCase.cs b/osu.Game/Tests/Visual/AllPlayersTestCase.cs index 21955da6e9..af190ec7d5 100644 --- a/osu.Game/Tests/Visual/AllPlayersTestCase.cs +++ b/osu.Game/Tests/Visual/AllPlayersTestCase.cs @@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual var working = CreateWorkingBeatmap(beatmap, Clock); Beatmap.Value = working; - SelectedMods.Value = new[] { r.GetAllMods().First(m => m is ModNoFail) }; + Mods.Value = new[] { r.GetAllMods().First(m => m is ModNoFail) }; Player?.Exit(); Player = null; diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index 1a8403db40..8ec440db8b 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual [Cached] [Cached(Type = typeof(IBindable>))] - protected readonly Bindable> SelectedMods = new Bindable>(Enumerable.Empty()); + protected readonly Bindable> Mods = new Bindable>(Enumerable.Empty()); protected DependencyContainer Dependencies { get; private set; } diff --git a/osu.Game/Tests/Visual/PlayerTestCase.cs b/osu.Game/Tests/Visual/PlayerTestCase.cs index 409e79b4a5..719b1d6892 100644 --- a/osu.Game/Tests/Visual/PlayerTestCase.cs +++ b/osu.Game/Tests/Visual/PlayerTestCase.cs @@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual Beatmap.Value = new TestWorkingBeatmap(beatmap, Clock); if (!AllowFail) - SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) }; + Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) }; Player = CreatePlayer(ruleset); LoadScreen(Player); From 7845d542e3b63d86c2c9364c14021bbd9c96a239 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 10 Apr 2019 17:11:17 +0900 Subject: [PATCH 0430/2854] Cache mods as array in DrawableRuleset --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 11 +++++------ osu.Game/Rulesets/UI/Playfield.cs | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 0ab07de1ac..654f330a08 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -81,9 +81,8 @@ namespace osu.Game.Rulesets.UI /// /// The mods which are to be applied. /// - [Cached] - [Cached(typeof(IBindable>))] - private readonly Bindable> mods = new Bindable>(); + [Cached(typeof(IReadOnlyList))] + private readonly IReadOnlyList mods; private FrameStabilityContainer frameStabilityContainer; @@ -100,7 +99,7 @@ namespace osu.Game.Rulesets.UI if (workingBeatmap == null) throw new ArgumentException("Beatmap cannot be null.", nameof(workingBeatmap)); - this.mods.Value = mods; + this.mods = mods.ToArray(); RelativeSizeAxes = Axes.Both; @@ -160,7 +159,7 @@ namespace osu.Game.Rulesets.UI .WithChild(ResumeOverlay))); } - applyRulesetMods(mods.Value, config); + applyRulesetMods(mods, config); loadObjects(); } @@ -175,7 +174,7 @@ namespace osu.Game.Rulesets.UI Playfield.PostProcess(); - foreach (var mod in mods.Value.OfType()) + foreach (var mod in mods.OfType()) mod.ApplyToDrawableHitObjects(Playfield.HitObjectContainer.Objects); } diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index c9a05dae2c..13689153f0 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.UI private IBindable beatmap { get; set; } [Resolved] - private IBindable> mods { get; set; } + private IReadOnlyList mods { get; set; } [BackgroundDependencyLoader] private void load() @@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.UI base.Update(); if (beatmap != null) - foreach (var mod in mods.Value) + foreach (var mod in mods) if (mod is IUpdatableByPlayfield updatable) updatable.Update(this); } From 0222424aef8cf51e5f201633938880d8d756f25f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 10 Apr 2019 17:13:12 +0900 Subject: [PATCH 0431/2854] Make mods IReadOnlyList gamewide Prevents potential multiple evaluations of enumerable. --- osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs | 2 +- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 +- osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs | 2 +- osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs | 2 +- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 2 +- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 2 +- osu.Game.Rulesets.Osu.Tests/StackingTest.cs | 3 ++- osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs | 2 +- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs | 2 +- osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs | 3 +-- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs | 2 +- .../Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 3 ++- .../Visual/Background/TestCaseBackgroundScreenBeatmap.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestCaseReplay.cs | 3 ++- .../Visual/SongSelect/TestCasePlaySongSelect.cs | 2 +- osu.Game.Tests/Visual/UserInterface/TestCaseMods.cs | 2 +- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- osu.Game/OsuGame.cs | 4 ++-- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 8 ++++---- osu.Game/Overlays/MusicController.cs | 2 +- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 2 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 4 ++-- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Rulesets/UI/DrawableRuleset.cs | 6 +++--- .../Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs | 2 +- osu.Game/Screens/Multi/Match/Components/Header.cs | 3 +-- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 6 +++--- osu.Game/Screens/OsuScreen.cs | 2 +- osu.Game/Screens/OsuScreenDependencies.cs | 8 ++++---- osu.Game/Screens/Play/GameplayClockContainer.cs | 4 ++-- osu.Game/Screens/Play/HUD/ModDisplay.cs | 6 +++--- osu.Game/Screens/Play/HUDOverlay.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 4 ++-- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 4 ++-- osu.Game/Screens/Select/MatchSongSelect.cs | 3 +-- osu.Game/Screens/Select/PlaySongSelect.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 8 ++++---- osu.Game/Tests/Visual/OsuTestCase.cs | 4 ++-- 44 files changed, 68 insertions(+), 68 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs index 0b20f34eb1..102afa9ca6 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Tests protected override Player CreatePlayer(Ruleset ruleset) { - Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); + Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); return base.CreatePlayer(ruleset); } } diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 544694fc8f..404766051f 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch { public class CatchRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IEnumerable mods) => new DrawableCatchRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableCatchRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap); diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index 4b75b54d41..0324ba1d0b 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.UI protected override bool UserScrollSpeedAdjustment => false; - public DrawableCatchRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + public DrawableCatchRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { Direction.Value = ScrollingDirection.Down; diff --git a/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs b/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs index 0bfbf38832..e5f379f608 100644 --- a/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs +++ b/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Edit { public new IScrollingInfo ScrollingInfo => base.ScrollingInfo; - public DrawableManiaEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + public DrawableManiaEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index ebc94c86e2..5a8af60aab 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Edit public int TotalColumns => ((ManiaPlayfield)DrawableRuleset.Playfield).TotalColumns; - protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) { DrawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods); diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 02a9b5ed30..36381294f5 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Mania { public class ManiaRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IEnumerable mods) => new DrawableManiaRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableManiaRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap); public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score); diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 9d10657680..f592023300 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.UI private readonly Bindable configDirection = new Bindable(); - public DrawableManiaRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + public DrawableManiaRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { // Generate the bar lines diff --git a/osu.Game.Rulesets.Osu.Tests/StackingTest.cs b/osu.Game.Rulesets.Osu.Tests/StackingTest.cs index 13c9985f47..e8b99e86f9 100644 --- a/osu.Game.Rulesets.Osu.Tests/StackingTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/StackingTest.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.IO; using System.Linq; using System.Text; @@ -23,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Tests using (var reader = new StreamReader(stream)) { var beatmap = Decoder.GetDecoder(reader).Decode(reader); - var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Enumerable.Empty()); + var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Array.Empty()); var objects = converted.HitObjects.ToList(); diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs index 0c3050bfb4..bcb6099cfb 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Edit { public class DrawableOsuEditRuleset : DrawableOsuRuleset { - public DrawableOsuEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + public DrawableOsuEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 9c4b6ee7aa..12e15be9b9 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit { } - protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableOsuEditRuleset(ruleset, beatmap, mods); protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 115271da85..40155cf8a9 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu { public class OsuRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IEnumerable mods) => new DrawableOsuRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableOsuRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap); diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index c2954e1b3b..ba7241c165 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.UI { protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config; - public DrawableOsuRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + public DrawableOsuRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs index c42faea9f9..9ceb1a9846 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -88,7 +87,7 @@ namespace osu.Game.Rulesets.Taiko.Tests Origin = Anchor.Centre, RelativeSizeAxes = Axes.X, Height = 768, - Children = new[] { drawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap, Enumerable.Empty()) } + Children = new[] { drawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap, Array.Empty()) } }); } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index cb53ec890b..f90894ff5d 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko { public class TaikoRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IEnumerable mods) => new DrawableTaikoRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableTaikoRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap); public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index 76bdd37ed3..adff869d26 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.UI protected override bool UserScrollSpeedAdjustment => false; - public DrawableTaikoRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + public DrawableTaikoRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { Direction.Value = ScrollingDirection.Left; diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index e181130774..6738e0e7c2 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.IO; using NUnit.Framework; using osuTK; @@ -40,7 +41,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(6, working.BeatmapInfo.BeatmapVersion); Assert.AreEqual(6, working.Beatmap.BeatmapInfo.BeatmapVersion); - Assert.AreEqual(6, working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Enumerable.Empty()).BeatmapInfo.BeatmapVersion); + Assert.AreEqual(6, working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Array.Empty()).BeatmapInfo.BeatmapVersion); } } diff --git a/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs index 283fd4c8b9..81fab5b4b3 100644 --- a/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs +++ b/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs @@ -267,7 +267,7 @@ namespace osu.Game.Tests.Visual.Background AddUntilStep("Song select has selection", () => songSelect.Carousel.SelectedBeatmap != null); AddStep("Set default user settings", () => { - Mods.Value = Mods.Value.Concat(new[] { new OsuModNoFail() }); + Mods.Value = Mods.Value.Concat(new[] { new OsuModNoFail() }).ToArray(); songSelect.DimLevel.Value = 0.7f; songSelect.BlurLevel.Value = 0.4f; }); diff --git a/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs index df48834e1f..624e5f08bd 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Gameplay { protected override Player CreatePlayer(Ruleset ruleset) { - Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); + Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); return new ScoreAccessiblePlayer(); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestCaseReplay.cs b/osu.Game.Tests/Visual/Gameplay/TestCaseReplay.cs index 2a72dc8242..263070ab21 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCaseReplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCaseReplay.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.ComponentModel; using System.Linq; using osu.Game.Rulesets; @@ -16,7 +17,7 @@ namespace osu.Game.Tests.Visual.Gameplay { protected override Player CreatePlayer(Ruleset ruleset) { - var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Enumerable.Empty()); + var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Array.Empty()); return new ScoreAccessibleReplayPlayer(ruleset.GetAutoplayMod().CreateReplayScore(beatmap)); } diff --git a/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs index e89b361104..7e33f6ce02 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestCasePlaySongSelect.cs @@ -183,7 +183,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("mods changed before ruleset", () => modChangeIndex < rulesetChangeIndex); AddAssert("empty mods", () => !Mods.Value.Any()); - void onModChange(ValueChangedEvent> e) => modChangeIndex = actionIndex++; + void onModChange(ValueChangedEvent> e) => modChangeIndex = actionIndex++; void onRulesetChange(ValueChangedEvent e) => rulesetChangeIndex = actionIndex--; } diff --git a/osu.Game.Tests/Visual/UserInterface/TestCaseMods.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseMods.cs index aab44f7d92..fd003c7ea2 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestCaseMods.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestCaseMods.cs @@ -253,7 +253,7 @@ namespace osu.Game.Tests.Visual.UserInterface private class TestModSelectOverlay : ModSelectOverlay { - public new Bindable> SelectedMods => base.SelectedMods; + public new Bindable> SelectedMods => base.SelectedMods; public ModButton GetModButton(Mod mod) { diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 3e6033da9c..58463d2219 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -52,7 +52,7 @@ namespace osu.Game.Beatmaps { public override IEnumerable GetModsFor(ModType type) => new Mod[] { }; - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IEnumerable mods) + public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods) { throw new NotImplementedException(); } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index ec0a76b52b..8989785dcd 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -75,7 +75,7 @@ namespace osu.Game.Beatmaps /// The to create a playable for. /// The converted . /// If could not be converted to . - public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IEnumerable mods) + public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods) { var rulesetInstance = ruleset.CreateInstance(); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 0785c588e5..30f98aa1ce 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -112,8 +112,8 @@ namespace osu.Game // todo: move this to SongSelect once Screen has the ability to unsuspend. [Cached] - [Cached(Type = typeof(IBindable>))] - private readonly Bindable> mods = new Bindable>(Enumerable.Empty()); + [Cached(typeof(IBindable>))] + private readonly Bindable> mods = new Bindable>(Array.Empty()); public OsuGame(string[] args = null) { diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 9e97f4551a..97769fe5aa 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -42,12 +42,12 @@ namespace osu.Game.Overlays.Mods protected readonly FillFlowContainer ModSectionsContainer; - protected readonly Bindable> SelectedMods = new Bindable>(Enumerable.Empty()); + protected readonly Bindable> SelectedMods = new Bindable>(Array.Empty()); protected readonly IBindable Ruleset = new Bindable(); [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, IBindable ruleset, AudioManager audio, Bindable> mods) + private void load(OsuColour colours, IBindable ruleset, AudioManager audio, Bindable> mods) { LowMultiplierColour = colours.Red; HighMultiplierColour = colours.Green; @@ -87,14 +87,14 @@ namespace osu.Game.Overlays.Mods // attempt to re-select any already selected mods. // this may be the first time we are receiving the ruleset, in which case they will still match. - selectedModsChanged(new ValueChangedEvent>(SelectedMods.Value, SelectedMods.Value)); + selectedModsChanged(new ValueChangedEvent>(SelectedMods.Value, SelectedMods.Value)); // write the mods back to the SelectedMods bindable in the case a change was not applicable. // this generally isn't required as the previous line will perform deselection; just here for safety. refreshSelectedMods(); } - private void selectedModsChanged(ValueChangedEvent> e) + private void selectedModsChanged(ValueChangedEvent> e) { foreach (ModSection section in ModSectionsContainer.Children) section.SelectTypes(e.NewValue.Select(m => m.GetType()).ToList()); diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index d9b669f753..c250d3b62a 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays private Bindable beatmap { get; set; } [Resolved] - private IBindable> mods { get; set; } + private IBindable> mods { get; set; } /// /// Provide a source for the toolbar height. diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index e181051737..14f7665e05 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Difficulty /// public Mod[] CreateDifficultyAdjustmentModCombinations() { - return createDifficultyAdjustmentModCombinations(Enumerable.Empty(), DifficultyAdjustmentMods).ToArray(); + return createDifficultyAdjustmentModCombinations(Array.Empty(), DifficultyAdjustmentMods).ToArray(); IEnumerable createDifficultyAdjustmentModCombinations(IEnumerable currentSet, Mod[] adjustmentSet, int currentSetCount = 0, int adjustmentSetStart = 0) { diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 5219cb9581..38ec09535d 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -186,8 +186,8 @@ namespace osu.Game.Rulesets.Edit } internal override DrawableEditRuleset CreateDrawableRuleset() - => new DrawableEditRuleset(CreateDrawableRuleset(Ruleset, Beatmap.Value, Enumerable.Empty())); + => new DrawableEditRuleset(CreateDrawableRuleset(Ruleset, Beatmap.Value, Array.Empty())); - protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods); + protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods); } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 90fe25accf..3521c17b23 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets /// The beatmap to create the hit renderer for. /// Unable to successfully load the beatmap to be usable with this ruleset. /// - public abstract DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IEnumerable mods); + public abstract DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods); /// /// Creates a to convert a to one that is applicable for this . diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 654f330a08..2866e81682 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.UI /// /// The ruleset being represented. /// The beatmap to create the hit renderer for. - protected DrawableRuleset(Ruleset ruleset, WorkingBeatmap workingBeatmap, IEnumerable mods) + protected DrawableRuleset(Ruleset ruleset, WorkingBeatmap workingBeatmap, IReadOnlyList mods) : base(ruleset) { if (workingBeatmap == null) @@ -257,7 +257,7 @@ namespace osu.Game.Rulesets.UI /// Applies the active mods to the Beatmap. /// /// - private void applyBeatmapMods(IEnumerable mods) + private void applyBeatmapMods(IReadOnlyList mods) { if (mods == null) return; @@ -270,7 +270,7 @@ namespace osu.Game.Rulesets.UI /// Applies the active mods to this DrawableRuleset. /// /// - private void applyRulesetMods(IEnumerable mods, OsuConfigManager config) + private void applyRulesetMods(IReadOnlyList mods, OsuConfigManager config) { if (mods == null) return; diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index 2bb98dd679..dbe8d8c299 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.UI.Scrolling [Cached(Type = typeof(IScrollingInfo))] private readonly LocalScrollingInfo scrollingInfo; - protected DrawableScrollingRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IEnumerable mods) + protected DrawableScrollingRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { scrollingInfo = new LocalScrollingInfo(); diff --git a/osu.Game/Screens/Multi/Match/Components/Header.cs b/osu.Game/Screens/Multi/Match/Components/Header.cs index e1592532a3..2a6074882d 100644 --- a/osu.Game/Screens/Multi/Match/Components/Header.cs +++ b/osu.Game/Screens/Multi/Match/Components/Header.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -110,7 +109,7 @@ namespace osu.Game.Screens.Multi.Match.Components }, }; - CurrentItem.BindValueChanged(item => modDisplay.Current.Value = item.NewValue?.RequiredMods ?? Enumerable.Empty(), true); + CurrentItem.BindValueChanged(item => modDisplay.Current.Value = item.NewValue?.RequiredMods?.ToArray() ?? Array.Empty(), true); beatmapButton.Action = () => RequestBeatmapSelection?.Invoke(); } diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index aad571cf87..6271693a6a 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -179,7 +179,7 @@ namespace osu.Game.Screens.Multi.Match { RoomManager?.PartRoom(); - Mods.Value = Enumerable.Empty(); + Mods.Value = Array.Empty(); return base.OnExiting(next); } @@ -193,7 +193,7 @@ namespace osu.Game.Screens.Multi.Match var localBeatmap = e.NewValue?.Beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == e.NewValue.Beatmap.OnlineBeatmapID); Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); - Mods.Value = e.NewValue?.RequiredMods ?? Enumerable.Empty(); + Mods.Value = e.NewValue?.RequiredMods?.ToArray() ?? Array.Empty(); if (e.NewValue?.Ruleset != null) Ruleset.Value = e.NewValue.Ruleset; } diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index d1e700cf92..c1a822c75c 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -65,7 +65,7 @@ namespace osu.Game.Screens public Bindable Ruleset { get; private set; } - public Bindable> Mods { get; private set; } + public Bindable> Mods { get; private set; } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { diff --git a/osu.Game/Screens/OsuScreenDependencies.cs b/osu.Game/Screens/OsuScreenDependencies.cs index d7e1862c05..4167faba83 100644 --- a/osu.Game/Screens/OsuScreenDependencies.cs +++ b/osu.Game/Screens/OsuScreenDependencies.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens public Bindable Ruleset { get; } - public Bindable> Mods { get; } + public Bindable> Mods { get; } public OsuScreenDependencies(bool requireLease, IReadOnlyDependencyContainer parent) : base(parent) @@ -31,15 +31,15 @@ namespace osu.Game.Screens if (Ruleset == null) Cache(Ruleset = parent.Get>().BeginLease(true)); - Mods = parent.Get>>()?.GetBoundCopy(); + Mods = parent.Get>>()?.GetBoundCopy(); if (Mods == null) - Cache(Mods = parent.Get>>().BeginLease(true)); + Cache(Mods = parent.Get>>().BeginLease(true)); } else { Beatmap = (parent.Get>() ?? parent.Get>()).GetBoundCopy(); Ruleset = (parent.Get>() ?? parent.Get>()).GetBoundCopy(); - Mods = (parent.Get>>() ?? parent.Get>>()).GetBoundCopy(); + Mods = (parent.Get>>() ?? parent.Get>>()).GetBoundCopy(); } } } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 2b18ef5ecd..29974b728e 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Play public class GameplayClockContainer : Container { private readonly WorkingBeatmap beatmap; - private readonly IEnumerable mods; + private readonly IReadOnlyList mods; /// /// The original source (usually a 's track). @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Play private readonly FramedOffsetClock platformOffsetClock; - public GameplayClockContainer(WorkingBeatmap beatmap, IEnumerable mods, double gameplayStartTime) + public GameplayClockContainer(WorkingBeatmap beatmap, IReadOnlyList mods, double gameplayStartTime) { this.beatmap = beatmap; this.mods = mods; diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 2c1293833f..878d2b7c38 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -18,15 +18,15 @@ using osu.Game.Graphics; namespace osu.Game.Screens.Play.HUD { - public class ModDisplay : Container, IHasCurrentValue> + public class ModDisplay : Container, IHasCurrentValue> { private const int fade_duration = 1000; public bool DisplayUnrankedText = true; - private readonly Bindable> current = new Bindable>(); + private readonly Bindable> current = new Bindable>(); - public Bindable> Current + public Bindable> Current { get => current; set diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index aa4375f652..3c1b33297a 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Play public Action RequestSeek; - public HUDOverlay(ScoreProcessor scoreProcessor, DrawableRuleset drawableRuleset, IEnumerable mods) + public HUDOverlay(ScoreProcessor scoreProcessor, DrawableRuleset drawableRuleset, IReadOnlyList mods) { RelativeSizeAxes = Axes.Both; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 2096f3f0f8..1c558eae2e 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -301,7 +301,7 @@ namespace osu.Game.Screens.Play } private readonly WorkingBeatmap beatmap; - private readonly IEnumerable mods; + private readonly IReadOnlyList mods; private LoadingAnimation loading; private Sprite backgroundSprite; private ModDisplay modDisplay; @@ -323,7 +323,7 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap, IEnumerable mods) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, IReadOnlyList mods) { this.beatmap = beatmap; this.mods = mods; diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 03cf767062..51f87dcc6d 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -310,12 +310,12 @@ namespace osu.Game.Screens.Select try { // Try to get the beatmap with the user's ruleset - playableBeatmap = beatmap.GetPlayableBeatmap(ruleset, Enumerable.Empty()); + playableBeatmap = beatmap.GetPlayableBeatmap(ruleset, Array.Empty()); } catch (BeatmapInvalidForRulesetException) { // Can't be converted to the user's ruleset, so use the beatmap's own ruleset - playableBeatmap = beatmap.GetPlayableBeatmap(beatmap.BeatmapInfo.Ruleset, Enumerable.Empty()); + playableBeatmap = beatmap.GetPlayableBeatmap(beatmap.BeatmapInfo.Ruleset, Array.Empty()); } labels.AddRange(playableBeatmap.GetStatistics().Select(s => new InfoLabel(s))); diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index 4468f5704f..c5fa9e2396 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -61,7 +60,7 @@ namespace osu.Game.Screens.Select { Ruleset.Value = CurrentItem.Value.Ruleset; Beatmap.Value = beatmaps.GetWorkingBeatmap(CurrentItem.Value.Beatmap); - Mods.Value = CurrentItem.Value.RequiredMods ?? Enumerable.Empty(); + Mods.Value = CurrentItem.Value.RequiredMods?.ToArray() ?? Array.Empty(); } Beatmap.Disabled = true; diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 44e38edda8..bf4f898323 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Select var mods = Mods.Value; if (mods.All(m => m.GetType() != autoType)) { - Mods.Value = mods.Append(auto); + Mods.Value = mods.Append(auto).ToArray(); removeAutoModOnResume = true; } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 6ad5b54e65..a78238c584 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -84,8 +84,8 @@ namespace osu.Game.Screens.Select private readonly Bindable decoupledRuleset = new Bindable(); [Cached] - [Cached(Type = typeof(IBindable>))] - private readonly Bindable> mods = new Bindable>(Enumerable.Empty()); // Bound to the game's mods, but is not reset on exiting + [Cached(Type = typeof(IBindable>))] + private readonly Bindable> mods = new Bindable>(Array.Empty()); // Bound to the game's mods, but is not reset on exiting protected SongSelect() { @@ -394,7 +394,7 @@ namespace osu.Game.Screens.Select { Logger.Log($"ruleset changed from \"{decoupledRuleset.Value}\" to \"{ruleset}\""); - mods.Value = Enumerable.Empty(); + mods.Value = Array.Empty(); decoupledRuleset.Value = ruleset; // force a filter before attempting to change the beatmap. @@ -530,7 +530,7 @@ namespace osu.Game.Screens.Select Beatmap.Value.Track.Looping = false; mods.UnbindAll(); - Mods.Value = Enumerable.Empty(); + Mods.Value = Array.Empty(); return false; } diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index 8ec440db8b..c08a6a4bcb 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -28,8 +28,8 @@ namespace osu.Game.Tests.Visual protected readonly Bindable Ruleset = new Bindable(); [Cached] - [Cached(Type = typeof(IBindable>))] - protected readonly Bindable> Mods = new Bindable>(Enumerable.Empty()); + [Cached(Type = typeof(IBindable>))] + protected readonly Bindable> Mods = new Bindable>(Array.Empty()); protected DependencyContainer Dependencies { get; private set; } From 1db2d49696a8cc33b54968e6ec6ab3dbfe5ece70 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 10 Apr 2019 17:54:57 +0900 Subject: [PATCH 0432/2854] Fix testcases --- .../ManiaPlacementBlueprintTestCase.cs | 6 ++++++ osu.Game.Rulesets.Mania.Tests/TestCaseColumn.cs | 4 ++++ osu.Game.Rulesets.Mania.Tests/TestCaseStage.cs | 5 +++++ .../Visual/Gameplay/TestCaseScrollingHitObjects.cs | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestCase.cs b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestCase.cs index 13bbe87513..9ad22498a9 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestCase.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestCase.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -8,6 +10,7 @@ using osu.Framework.Timing; using osu.Game.Rulesets.Mania.Edit; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; @@ -21,6 +24,9 @@ namespace osu.Game.Rulesets.Mania.Tests { private readonly Column column; + [Cached(typeof(IReadOnlyList))] + private IReadOnlyList mods { get; set; } = Array.Empty(); + protected ManiaPlacementBlueprintTestCase() { Add(column = new Column(0) diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseColumn.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseColumn.cs index b14f999f61..d46b661eea 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestCaseColumn.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestCaseColumn.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI.Components; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; using osuTK; @@ -31,6 +32,9 @@ namespace osu.Game.Rulesets.Mania.Tests typeof(ColumnHitObjectArea) }; + [Cached(typeof(IReadOnlyList))] + private IReadOnlyList mods { get; set; } = Array.Empty(); + private readonly List columns = new List(); public TestCaseColumn() diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseStage.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseStage.cs index ac430037e4..9a7a3d1c5c 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestCaseStage.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestCaseStage.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -13,6 +14,7 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; using osuTK; @@ -24,6 +26,9 @@ namespace osu.Game.Rulesets.Mania.Tests { private const int columns = 4; + [Cached(typeof(IReadOnlyList))] + private IReadOnlyList mods { get; set; } = Array.Empty(); + private readonly List stages = new List(); private FillFlowContainer fill; diff --git a/osu.Game.Tests/Visual/Gameplay/TestCaseScrollingHitObjects.cs b/osu.Game.Tests/Visual/Gameplay/TestCaseScrollingHitObjects.cs index c99a4bb89b..3cfc5ac7c8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCaseScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCaseScrollingHitObjects.cs @@ -4,11 +4,13 @@ using System; using System.Collections.Generic; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Configuration; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Timing; @@ -23,6 +25,9 @@ namespace osu.Game.Tests.Visual.Gameplay { public override IReadOnlyList RequiredTypes => new[] { typeof(Playfield) }; + [Cached(typeof(IReadOnlyList))] + private IReadOnlyList mods { get; set; } = Array.Empty(); + private readonly ScrollingTestContainer[] scrollContainers = new ScrollingTestContainer[4]; private readonly TestPlayfield[] playfields = new TestPlayfield[4]; From 22f9339b01ff091b1e538050912c85377fbe38bf Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Wed, 10 Apr 2019 21:53:13 +0800 Subject: [PATCH 0433/2854] let mods button have selected mod icons --- osu.Game/Screens/Select/Footer.cs | 31 ++++++------- osu.Game/Screens/Select/FooterButton.cs | 16 ++++--- osu.Game/Screens/Select/FooterButtonMods.cs | 48 +++++++++++++++++++++ osu.Game/Screens/Select/SongSelect.cs | 6 +-- 4 files changed, 74 insertions(+), 27 deletions(-) create mode 100644 osu.Game/Screens/Select/FooterButtonMods.cs diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index 03b9826e9b..c991e5c350 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -20,9 +20,6 @@ namespace osu.Game.Screens.Select { private readonly Box modeLight; - private const float play_song_select_button_width = 100; - private const float play_song_select_button_height = 50; - public const float HEIGHT = 50; public const int TRANSITION_LENGTH = 300; @@ -33,6 +30,7 @@ namespace osu.Game.Screens.Select private readonly FillFlowContainer buttons; + /// Button to be added. /// Text on the button. /// Colour of the button. /// Hotkey of the button. @@ -41,21 +39,16 @@ namespace osu.Game.Screens.Select /// Higher depth to be put on the left, and lower to be put on the right. /// Notice this is different to ! /// - public void AddButton(string text, Color4 colour, Action action, Key? hotkey = null, float depth = 0) + public void AddButton(FooterButton button, string text, Color4 colour, Action action, Key? hotkey = null, float depth = 0) { - var button = new FooterButton - { - Text = text, - Height = play_song_select_button_height, - Width = play_song_select_button_width, - Depth = depth, - SelectedColour = colour, - DeselectedColour = colour.Opacity(0.5f), - Hotkey = hotkey, - Hovered = updateModeLight, - HoverLost = updateModeLight, - Action = action, - }; + button.Text = text; + button.Depth = depth; + button.SelectedColour = colour; + button.DeselectedColour = colour.Opacity(0.5f); + button.Hotkey = hotkey; + button.Hovered = updateModeLight; + button.HoverLost = updateModeLight; + button.Action = action; buttons.Add(button); buttons.SetLayoutPosition(button, -depth); @@ -71,10 +64,10 @@ namespace osu.Game.Screens.Select /// Higher depth to be put on the left, and lower to be put on the right. /// Notice this is different to ! /// - public void AddButton(string text, Color4 colour, OverlayContainer overlay, Key? hotkey = null, float depth = 0) + public void AddButton(FooterButton button, string text, Color4 colour, OverlayContainer overlay, Key? hotkey = null, float depth = 0) { overlays.Add(overlay); - AddButton(text, colour, () => + AddButton(button, text, colour, () => { foreach (var o in overlays) { diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs index 9b98e344ce..0000bb95dd 100644 --- a/osu.Game/Screens/Select/FooterButton.cs +++ b/osu.Game/Screens/Select/FooterButton.cs @@ -6,6 +6,7 @@ using osuTK; using osuTK.Graphics; using osuTK.Input; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; @@ -61,8 +62,18 @@ namespace osu.Game.Screens.Select public FooterButton() { + AutoSizeAxes = Axes.Both; Children = new Drawable[] { + new Container + { + Size = new Vector2(100, 50), + Child = spriteText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }, box = new Box { RelativeSizeAxes = Axes.Both, @@ -78,11 +89,6 @@ namespace osu.Game.Screens.Select EdgeSmoothness = new Vector2(2, 0), RelativeSizeAxes = Axes.X, }, - spriteText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } }; } diff --git a/osu.Game/Screens/Select/FooterButtonMods.cs b/osu.Game/Screens/Select/FooterButtonMods.cs new file mode 100644 index 0000000000..a08870ba03 --- /dev/null +++ b/osu.Game/Screens/Select/FooterButtonMods.cs @@ -0,0 +1,48 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; +using System; +using System.Collections.Generic; +using osuTK; + +namespace osu.Game.Screens.Select +{ + public class FooterButtonMods : FooterButton + { + private readonly Bindable> selectedMods = new Bindable>(); + + private readonly FillFlowContainer modIcons; + + public FooterButtonMods(Bindable> mods) : base() + { + Add(modIcons = new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding {Left = 80, Right = 20} + }); + + if (mods != null) + { + selectedMods.BindTo(mods); + selectedMods.ValueChanged += updateModIcons; + } + } + + private void updateModIcons(ValueChangedEvent> mods) + { + modIcons.Clear(); + foreach (Mod mod in mods.NewValue) + { + modIcons.Add(new ModIcon(mod) { Scale = new Vector2(0.4f) }); + } + } + } +} diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index b60e693cbf..6fc95ea2e1 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -224,9 +224,9 @@ namespace osu.Game.Screens.Select if (Footer != null) { - Footer.AddButton(@"mods", colours.Yellow, ModSelect, Key.F1); - Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2); - Footer.AddButton(@"options", colours.Blue, BeatmapOptions, Key.F3); + Footer.AddButton(new FooterButtonMods(selectedMods), @"mods", colours.Yellow, ModSelect, Key.F1); + Footer.AddButton(new FooterButton(), @"random", colours.Green, triggerRandom, Key.F2); + Footer.AddButton(new FooterButton(), @"options", colours.Blue, BeatmapOptions, Key.F3); BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue); BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.Regular.TimesCircle, colours.Purple, null, Key.Number1); From b4d07558186a9d8dc3627ac1af564e375cab2f95 Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Wed, 10 Apr 2019 22:10:09 +0800 Subject: [PATCH 0434/2854] please appveyor --- osu.Game/Screens/Select/FooterButtonMods.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/FooterButtonMods.cs b/osu.Game/Screens/Select/FooterButtonMods.cs index a08870ba03..51973b1e3d 100644 --- a/osu.Game/Screens/Select/FooterButtonMods.cs +++ b/osu.Game/Screens/Select/FooterButtonMods.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; -using System; using System.Collections.Generic; using osuTK; @@ -18,7 +17,7 @@ namespace osu.Game.Screens.Select private readonly FillFlowContainer modIcons; - public FooterButtonMods(Bindable> mods) : base() + public FooterButtonMods(Bindable> mods) { Add(modIcons = new FillFlowContainer { @@ -26,7 +25,7 @@ namespace osu.Game.Screens.Select Origin = Anchor.CentreLeft, Direction = FillDirection.Horizontal, AutoSizeAxes = Axes.Both, - Margin = new MarginPadding {Left = 80, Right = 20} + Margin = new MarginPadding { Left = 80, Right = 20 } }); if (mods != null) From 01cc78108c1bd0d2fa3a21d6327e85354ccc6e8b Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Thu, 11 Apr 2019 05:47:32 +0800 Subject: [PATCH 0435/2854] add random button --- osu.Game/Screens/Select/FooterButton.cs | 5 +- osu.Game/Screens/Select/FooterButtonRandom.cs | 56 +++++++++++++++++++ osu.Game/Screens/Select/SongSelect.cs | 2 +- 3 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Screens/Select/FooterButtonRandom.cs diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs index 0000bb95dd..aa547c658f 100644 --- a/osu.Game/Screens/Select/FooterButton.cs +++ b/osu.Game/Screens/Select/FooterButton.cs @@ -54,7 +54,8 @@ namespace osu.Game.Screens.Select } } - private readonly SpriteText spriteText; + protected readonly Container textContainer; + protected readonly SpriteText spriteText; private readonly Box box; private readonly Box light; @@ -65,7 +66,7 @@ namespace osu.Game.Screens.Select AutoSizeAxes = Axes.Both; Children = new Drawable[] { - new Container + textContainer = new Container { Size = new Vector2(100, 50), Child = spriteText = new OsuSpriteText diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs new file mode 100644 index 0000000000..7466e1f243 --- /dev/null +++ b/osu.Game/Screens/Select/FooterButtonRandom.cs @@ -0,0 +1,56 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Sprites; +using System; + +namespace osu.Game.Screens.Select +{ + public class FooterButtonRandom : FooterButton + { + private readonly SpriteText secondaryText; + private bool secondaryActive; + + public FooterButtonRandom() + { + textContainer.Add(secondaryText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = @"rewind", + Alpha = 0 + }); + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + secondaryActive = e.ShiftPressed; + updateText(); + return base.OnKeyDown(e); + } + + protected override bool OnKeyUp(KeyUpEvent e) + { + secondaryActive = e.ShiftPressed; + updateText(); + return base.OnKeyUp(e); + } + + private void updateText() + { + if (secondaryActive) + { + spriteText.FadeOut(120, Easing.InQuad); + secondaryText.FadeIn(120, Easing.InQuad); + } + else + { + spriteText.FadeIn(120, Easing.InQuad); + secondaryText.FadeOut(120, Easing.InQuad); + } + } + } +} diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 6fc95ea2e1..7fb49e6dc6 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -225,7 +225,7 @@ namespace osu.Game.Screens.Select if (Footer != null) { Footer.AddButton(new FooterButtonMods(selectedMods), @"mods", colours.Yellow, ModSelect, Key.F1); - Footer.AddButton(new FooterButton(), @"random", colours.Green, triggerRandom, Key.F2); + Footer.AddButton(new FooterButtonRandom(), @"random", colours.Green, triggerRandom, Key.F2); Footer.AddButton(new FooterButton(), @"options", colours.Blue, BeatmapOptions, Key.F3); BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue); From 664a4ba540677fb6851d7a6c7277bc1eb788928c Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 12 Apr 2019 10:47:22 +0900 Subject: [PATCH 0436/2854] Implement flashlight dimming on slider slide --- .../Mods/OsuModFlashlight.cs | 30 +++++++++++++++++-- .../Objects/Drawables/DrawableRepeatPoint.cs | 2 +- .../Objects/Drawables/DrawableSlider.cs | 4 +-- osu.Game/Rulesets/Mods/ModFlashlight.cs | 17 +++++++++++ 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index 2c40d18f1b..26c0c26f0f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -1,23 +1,49 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; using osuTK; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModFlashlight : ModFlashlight + public class OsuModFlashlight : ModFlashlight, IApplicableToDrawableHitObjects { public override double ScoreMultiplier => 1.12; private const float default_flashlight_size = 180; - public override Flashlight CreateFlashlight() => new OsuFlashlight(); + private int trackingSliders; + + private OsuFlashlight flashlight; + + public override Flashlight CreateFlashlight() => flashlight = new OsuFlashlight(); + + public void ApplyToDrawableHitObjects(IEnumerable drawables) + { + foreach (DrawableSlider drawable in drawables.OfType()) + { + drawable.Tracking.ValueChanged += updateTrackingSliders; + } + } + + private void updateTrackingSliders(ValueChangedEvent value) + { + if (value.NewValue) + trackingSliders++; + else + trackingSliders--; + + flashlight.FlashlightLightness = trackingSliders > 0 ? 0.2f : 1.0f; + } private class OsuFlashlight : Flashlight, IRequireHighFrequencyMousePosition { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index edf2d90c08..bece2a49cd 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void CheckForResult(bool userTriggered, double timeOffset) { if (repeatPoint.StartTime <= Time.Current) - ApplyResult(r => r.Type = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss); + ApplyResult(r => r.Type = drawableSlider.Tracking.Value ? HitResult.Great : HitResult.Miss); } protected override void UpdatePreemptState() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 57ea0abdd8..c1a4c1981f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -130,13 +130,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - public bool Tracking; + public readonly Bindable Tracking = new Bindable(); protected override void Update() { base.Update(); - Tracking = Ball.Tracking; + Tracking.Value = Ball.Tracking; double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 0ad99d13ff..fa070dc9e8 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -80,6 +80,7 @@ namespace osu.Game.Rulesets.Mods flashNode.ScreenSpaceDrawQuad = ScreenSpaceDrawQuad; flashNode.FlashlightPosition = Vector2Extensions.Transform(FlashlightPosition, DrawInfo.Matrix); flashNode.FlashlightSize = Vector2Extensions.Transform(FlashlightSize, DrawInfo.Matrix); + flashNode.FlashlightLightness = FlashlightLightness; } [BackgroundDependencyLoader] @@ -136,6 +137,20 @@ namespace osu.Game.Rulesets.Mods Invalidate(Invalidation.DrawNode); } } + + private float flashlightLightness = 1.0f; + + public float FlashlightLightness + { + get => flashlightLightness; + set + { + if (flashlightLightness == value) return; + + flashlightLightness = value; + Invalidate(Invalidation.DrawNode); + } + } } private class FlashlightDrawNode : DrawNode @@ -144,6 +159,7 @@ namespace osu.Game.Rulesets.Mods public Quad ScreenSpaceDrawQuad; public Vector2 FlashlightPosition; public Vector2 FlashlightSize; + public float FlashlightLightness; public override void Draw(Action vertexAction) { @@ -153,6 +169,7 @@ namespace osu.Game.Rulesets.Mods Shader.GetUniform("flashlightPos").UpdateValue(ref FlashlightPosition); Shader.GetUniform("flashlightSize").UpdateValue(ref FlashlightSize); + Shader.GetUniform("flashlightLightness").UpdateValue(ref FlashlightLightness); Texture.WhitePixel.DrawQuad(ScreenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction); From 846a4835ca3a5915a31318d8b67472eb0ab636d2 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 12 Apr 2019 11:23:40 +0900 Subject: [PATCH 0437/2854] Invert flashlight dim --- osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs | 2 +- osu.Game/Rulesets/Mods/ModFlashlight.cs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index 26c0c26f0f..6a57e39616 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Mods else trackingSliders--; - flashlight.FlashlightLightness = trackingSliders > 0 ? 0.2f : 1.0f; + flashlight.FlashlightDim = trackingSliders > 0 ? 0.8f : 0.0f; } private class OsuFlashlight : Flashlight, IRequireHighFrequencyMousePosition diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index fa070dc9e8..e454c59fab 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Mods flashNode.ScreenSpaceDrawQuad = ScreenSpaceDrawQuad; flashNode.FlashlightPosition = Vector2Extensions.Transform(FlashlightPosition, DrawInfo.Matrix); flashNode.FlashlightSize = Vector2Extensions.Transform(FlashlightSize, DrawInfo.Matrix); - flashNode.FlashlightLightness = FlashlightLightness; + flashNode.FlashlightDim = FlashlightDim; } [BackgroundDependencyLoader] @@ -138,16 +138,16 @@ namespace osu.Game.Rulesets.Mods } } - private float flashlightLightness = 1.0f; + private float flashlightDim; - public float FlashlightLightness + public float FlashlightDim { - get => flashlightLightness; + get => flashlightDim; set { - if (flashlightLightness == value) return; + if (flashlightDim == value) return; - flashlightLightness = value; + flashlightDim = value; Invalidate(Invalidation.DrawNode); } } @@ -159,7 +159,7 @@ namespace osu.Game.Rulesets.Mods public Quad ScreenSpaceDrawQuad; public Vector2 FlashlightPosition; public Vector2 FlashlightSize; - public float FlashlightLightness; + public float FlashlightDim; public override void Draw(Action vertexAction) { @@ -169,7 +169,7 @@ namespace osu.Game.Rulesets.Mods Shader.GetUniform("flashlightPos").UpdateValue(ref FlashlightPosition); Shader.GetUniform("flashlightSize").UpdateValue(ref FlashlightSize); - Shader.GetUniform("flashlightLightness").UpdateValue(ref FlashlightLightness); + Shader.GetUniform("flashlightDim").UpdateValue(ref FlashlightDim); Texture.WhitePixel.DrawQuad(ScreenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction); From dba4ccdf74b401f6f07584df67acd4ead0c49c89 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 12 Apr 2019 14:53:23 +0900 Subject: [PATCH 0438/2854] Add back flashlight testcase --- .../TestCaseFlashlight.cs | 18 ++++++++++++++++++ osu.Game/Tests/Visual/AllPlayersTestCase.cs | 12 +++++------- osu.Game/Tests/Visual/PlayerTestCase.cs | 13 ++++--------- 3 files changed, 27 insertions(+), 16 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/TestCaseFlashlight.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseFlashlight.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseFlashlight.cs new file mode 100644 index 0000000000..28a732cc49 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseFlashlight.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Screens.Play; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public class TestCaseFlashlight : TestCaseOsuPlayer + { + protected override Player CreatePlayer(Ruleset ruleset) + { + Beatmap.Value.Mods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), }; + return base.CreatePlayer(ruleset); + } + } +} diff --git a/osu.Game/Tests/Visual/AllPlayersTestCase.cs b/osu.Game/Tests/Visual/AllPlayersTestCase.cs index 4ef9b346b0..6747493509 100644 --- a/osu.Game/Tests/Visual/AllPlayersTestCase.cs +++ b/osu.Game/Tests/Visual/AllPlayersTestCase.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Screens; using osu.Framework.Timing; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play; @@ -26,13 +27,6 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { - Add(new Box - { - RelativeSizeAxes = Framework.Graphics.Axes.Both, - Colour = Color4.Black, - Depth = int.MaxValue - }); - foreach (var r in rulesets.AvailableRulesets) { Player p = null; @@ -50,6 +44,10 @@ namespace osu.Game.Tests.Visual AddCheckSteps(); } + + OsuConfigManager manager; + Dependencies.Cache(manager = new OsuConfigManager(LocalStorage)); + manager.GetBindable(OsuSetting.DimLevel).Value = 1.0; } protected abstract void AddCheckSteps(); diff --git a/osu.Game/Tests/Visual/PlayerTestCase.cs b/osu.Game/Tests/Visual/PlayerTestCase.cs index 3bf707fade..8ec822957f 100644 --- a/osu.Game/Tests/Visual/PlayerTestCase.cs +++ b/osu.Game/Tests/Visual/PlayerTestCase.cs @@ -3,15 +3,13 @@ using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps; -using osuTK.Graphics; namespace osu.Game.Tests.Visual { @@ -29,12 +27,9 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load() { - Add(new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Depth = int.MaxValue - }); + OsuConfigManager manager; + Dependencies.Cache(manager = new OsuConfigManager(LocalStorage)); + manager.GetBindable(OsuSetting.DimLevel).Value = 1.0; } [SetUpSteps] From 69748abedcd1e8e018afd8c55da4e6a36297018d Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 12 Apr 2019 15:09:43 +0900 Subject: [PATCH 0439/2854] Rename to TestCaseOsuFlashlight --- .../{TestCaseFlashlight.cs => TestCaseOsuFlashlight.cs} | 2 +- osu.Game/Tests/Visual/AllPlayersTestCase.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) rename osu.Game.Rulesets.Osu.Tests/{TestCaseFlashlight.cs => TestCaseOsuFlashlight.cs} (89%) diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseFlashlight.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseOsuFlashlight.cs similarity index 89% rename from osu.Game.Rulesets.Osu.Tests/TestCaseFlashlight.cs rename to osu.Game.Rulesets.Osu.Tests/TestCaseOsuFlashlight.cs index 28a732cc49..6f198084f5 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseFlashlight.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseOsuFlashlight.cs @@ -7,7 +7,7 @@ using osu.Game.Screens.Play; namespace osu.Game.Rulesets.Osu.Tests { - public class TestCaseFlashlight : TestCaseOsuPlayer + public class TestCaseOsuFlashlight : TestCaseOsuPlayer { protected override Player CreatePlayer(Ruleset ruleset) { diff --git a/osu.Game/Tests/Visual/AllPlayersTestCase.cs b/osu.Game/Tests/Visual/AllPlayersTestCase.cs index 6747493509..882f510b64 100644 --- a/osu.Game/Tests/Visual/AllPlayersTestCase.cs +++ b/osu.Game/Tests/Visual/AllPlayersTestCase.cs @@ -3,7 +3,6 @@ using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Graphics.Shapes; using osu.Framework.Screens; using osu.Framework.Timing; using osu.Game.Beatmaps; @@ -12,7 +11,6 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps; -using osuTK.Graphics; namespace osu.Game.Tests.Visual { From d9ed68b18968ce1c1eafd6c3dc466b7119c5e134 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 12 Apr 2019 15:33:31 +0900 Subject: [PATCH 0440/2854] Add short fade to flashlight dimming --- .../Mods/OsuModFlashlight.cs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index 6a57e39616..5d136e4cbe 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -21,8 +21,6 @@ namespace osu.Game.Rulesets.Osu.Mods private const float default_flashlight_size = 180; - private int trackingSliders; - private OsuFlashlight flashlight; public override Flashlight CreateFlashlight() => flashlight = new OsuFlashlight(); @@ -31,27 +29,30 @@ namespace osu.Game.Rulesets.Osu.Mods { foreach (DrawableSlider drawable in drawables.OfType()) { - drawable.Tracking.ValueChanged += updateTrackingSliders; + drawable.Tracking.ValueChanged += flashlight.OnSliderTrackingChange; } } - private void updateTrackingSliders(ValueChangedEvent value) - { - if (value.NewValue) - trackingSliders++; - else - trackingSliders--; - - flashlight.FlashlightDim = trackingSliders > 0 ? 0.8f : 0.0f; - } - private class OsuFlashlight : Flashlight, IRequireHighFrequencyMousePosition { + private int trackingSliders; + public OsuFlashlight() { FlashlightSize = new Vector2(0, getSizeFor(0)); } + public void OnSliderTrackingChange(ValueChangedEvent e) + { + if (e.NewValue) + trackingSliders++; + else + trackingSliders--; + + // If there are any sliders in a tracking state, apply a dim to the entire playfield over a brief duration. + this.TransformTo(nameof(FlashlightDim), trackingSliders > 0 ? 0.8f : 0.0f, 50); + } + protected override bool OnMouseMove(MouseMoveEvent e) { FlashlightPosition = e.MousePosition; From e25c7fdb9b5c8de23ef51c39065fbde9c7af2bf1 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 12 Apr 2019 17:39:25 +0900 Subject: [PATCH 0441/2854] Use absolute sequence for flashlight breaks --- osu.Game/Rulesets/Mods/ModFlashlight.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index e454c59fab..b5a907c0ef 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -95,14 +95,17 @@ namespace osu.Game.Rulesets.Mods Combo.ValueChanged += OnComboChange; - this.FadeInFromZero(FLASHLIGHT_FADE_DURATION); - - foreach (var breakPeriod in Breaks) + using (BeginAbsoluteSequence(0)) { - if (breakPeriod.Duration < FLASHLIGHT_FADE_DURATION * 2) continue; + this.FadeInFromZero(FLASHLIGHT_FADE_DURATION); - this.Delay(breakPeriod.StartTime + FLASHLIGHT_FADE_DURATION).FadeOutFromOne(FLASHLIGHT_FADE_DURATION); - this.Delay(breakPeriod.EndTime - FLASHLIGHT_FADE_DURATION).FadeInFromZero(FLASHLIGHT_FADE_DURATION); + foreach (var breakPeriod in Breaks) + { + if (breakPeriod.Duration < FLASHLIGHT_FADE_DURATION * 2) continue; + + this.Delay(breakPeriod.StartTime + FLASHLIGHT_FADE_DURATION).FadeOutFromOne(FLASHLIGHT_FADE_DURATION); + this.Delay(breakPeriod.EndTime - FLASHLIGHT_FADE_DURATION).FadeInFromZero(FLASHLIGHT_FADE_DURATION); + } } } From 69dda0ffd43caa6b94efd871e887cd3667433a9c Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 12 Apr 2019 22:36:01 +0200 Subject: [PATCH 0442/2854] OsuScreens can now set a per screen user status which defaults to UserStatusOnline --- osu.Game/Screens/OsuScreen.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index e0a25deecf..d54936ffda 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -14,6 +14,8 @@ using osu.Game.Input.Bindings; using osu.Game.Rulesets; using osu.Game.Screens.Menu; using osu.Game.Overlays; +using osu.Game.Users; +using osu.Game.Online.API; namespace osu.Game.Screens { @@ -50,6 +52,14 @@ namespace osu.Game.Screens protected new OsuGameBase Game => base.Game as OsuGameBase; + /// + /// The to set the user's status automatically to when this screen is entered / resumed. + /// Note that the user status won't be automatically set if : + /// - is overriden and returns null + /// - The current is or + /// + protected virtual UserStatus ScreenStatus => new UserStatusOnline(); + /// /// Whether to disallow changes to game-wise Beatmap/Ruleset bindables for this screen (and all children). /// @@ -83,6 +93,9 @@ namespace osu.Game.Screens [Resolved(canBeNull: true)] private OsuLogo logo { get; set; } + [Resolved(canBeNull: true)] + private IAPIProvider api { get; set; } + protected OsuScreen() { Anchor = Anchor.Centre; @@ -115,6 +128,8 @@ namespace osu.Game.Screens sampleExit?.Play(); applyArrivingDefaults(true); + setUserStatus(ScreenStatus); + base.OnResuming(last); } @@ -130,6 +145,8 @@ namespace osu.Game.Screens backgroundStack?.Push(localBackground = CreateBackground()); + setUserStatus(ScreenStatus); + base.OnEntering(last); } @@ -147,6 +164,12 @@ namespace osu.Game.Screens return false; } + private void setUserStatus(UserStatus status) + { + if (api != null && status != null && !(api.LocalUser.Value.Status.Value is UserStatusDoNotDisturb) && !(api.LocalUser.Value.Status.Value is UserStatusOffline)) //only sets the user's status to the given one if + api.LocalUser.Value.Status.Value = status; //status is not null and the current status isn't either UserStatusDoNotDisturb or UserStatusOffline + } + /// /// Fired when this screen was entered or resumed and the logo state is required to be adjusted. /// From da5d6cb1d48c7622e8f4e365dbea1a840fd48e22 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 12 Apr 2019 22:54:35 +0200 Subject: [PATCH 0443/2854] Add Beatmap fields to UserStatusSoloGame & UserStatusEditing so they can carry metadata about the played / edited beatmap --- osu.Game/Screens/Edit/Editor.cs | 3 +++ osu.Game/Screens/Play/Player.cs | 3 +++ osu.Game/Screens/Select/PlaySongSelect.cs | 3 +++ osu.Game/Users/UserStatus.cs | 27 +++++++++++++++++++++++ 4 files changed, 36 insertions(+) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 0ba1e74aca..bf00d23903 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -24,6 +24,7 @@ using osu.Game.Screens.Edit.Design; using osuTK.Input; using System.Collections.Generic; using osu.Framework; +using osu.Game.Users; namespace osu.Game.Screens.Edit { @@ -47,6 +48,8 @@ namespace osu.Game.Screens.Edit private DependencyContainer dependencies; private GameHost host; + protected override UserStatus ScreenStatus => new UserStatusEditing(Beatmap.Value.BeatmapInfo); + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0eebefec86..edb6f9f865 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -26,6 +26,7 @@ using osu.Game.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Skinning; using osu.Game.Storyboards.Drawables; +using osu.Game.Users; namespace osu.Game.Screens.Play { @@ -33,6 +34,8 @@ namespace osu.Game.Screens.Play { protected override bool AllowBackButton => false; // handled by HoldForMenuButton + protected override UserStatus ScreenStatus => new UserStatusSoloGame(Beatmap.Value.BeatmapInfo); + public override float BackgroundParallaxAmount => 0.1f; public override bool HideOverlaysOnEnter => true; diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 340ceb6864..384064d2a8 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Screens.Play; +using osu.Game.Users; using osuTK.Input; namespace osu.Game.Screens.Select @@ -18,6 +19,8 @@ namespace osu.Game.Screens.Select public override bool AllowExternalScreenChange => true; + protected override UserStatus ScreenStatus => new UserStatusChoosingBeatmap(); + [BackgroundDependencyLoader] private void load(OsuColour colours) { diff --git a/osu.Game/Users/UserStatus.cs b/osu.Game/Users/UserStatus.cs index 14b4538a00..60e637d7e4 100644 --- a/osu.Game/Users/UserStatus.cs +++ b/osu.Game/Users/UserStatus.cs @@ -3,6 +3,7 @@ using osuTK.Graphics; using osu.Game.Graphics; +using osu.Game.Beatmaps; namespace osu.Game.Users { @@ -41,7 +42,33 @@ namespace osu.Game.Users public class UserStatusSoloGame : UserStatusBusy { + public UserStatusSoloGame(BeatmapInfo info) + { + Beatmap = info; + } + public override string Message => @"Solo Game"; + + public BeatmapInfo Beatmap { get; } + } + + public class UserStatusEditing : UserStatusBusy + { + public UserStatusEditing(BeatmapInfo info) + { + Beatmap = info; + } + + public override string Message => @"Editing a beatmap"; + + public override Color4 GetAppropriateColour(OsuColour colours) => colours.GreenDarker; + + public BeatmapInfo Beatmap { get; } + } + + public class UserStatusChoosingBeatmap : UserStatusOnline + { + public override string Message => @"Choosing a beatmap"; } public class UserStatusMultiplayerGame : UserStatusBusy From 5ab278f9eac4a3d72b8da0e37b07bfc037117717 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 12 Apr 2019 23:01:11 +0200 Subject: [PATCH 0444/2854] Add missing user statuses to tests --- osu.Game.Tests/Visual/Online/TestCaseUserPanel.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestCaseUserPanel.cs b/osu.Game.Tests/Visual/Online/TestCaseUserPanel.cs index b2877f7bd7..182ea6da01 100644 --- a/osu.Game.Tests/Visual/Online/TestCaseUserPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestCaseUserPanel.cs @@ -43,11 +43,13 @@ namespace osu.Game.Tests.Visual.Online }); flyte.Status.Value = new UserStatusOnline(); - peppy.Status.Value = new UserStatusSoloGame(); + peppy.Status.Value = new UserStatusSoloGame(new Game.Beatmaps.BeatmapInfo()); AddStep(@"spectating", () => { flyte.Status.Value = new UserStatusSpectating(); }); AddStep(@"multiplaying", () => { flyte.Status.Value = new UserStatusMultiplayerGame(); }); AddStep(@"modding", () => { flyte.Status.Value = new UserStatusModding(); }); + AddStep(@"editing", () => { flyte.Status.Value = new UserStatusEditing(null); }); + AddStep(@"choosing", () => { flyte.Status.Value = new UserStatusChoosingBeatmap(); }); AddStep(@"offline", () => { flyte.Status.Value = new UserStatusOffline(); }); AddStep(@"null status", () => { flyte.Status.Value = null; }); } From 361c0ec9f26d9c066cb4830e16343bc3d5b6e02b Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 13 Apr 2019 13:18:44 +0200 Subject: [PATCH 0445/2854] Allow UserStatusSoloGame to provide metadata such as the ruleset the current beatmap is played in --- osu.Game.Tests/Visual/Online/TestCaseUserPanel.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 3 +++ osu.Game/Users/UserStatus.cs | 5 ++++- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestCaseUserPanel.cs b/osu.Game.Tests/Visual/Online/TestCaseUserPanel.cs index 182ea6da01..90cdd48a1e 100644 --- a/osu.Game.Tests/Visual/Online/TestCaseUserPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestCaseUserPanel.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Online }); flyte.Status.Value = new UserStatusOnline(); - peppy.Status.Value = new UserStatusSoloGame(new Game.Beatmaps.BeatmapInfo()); + peppy.Status.Value = new UserStatusSoloGame(null, null); AddStep(@"spectating", () => { flyte.Status.Value = new UserStatusSpectating(); }); AddStep(@"multiplaying", () => { flyte.Status.Value = new UserStatusMultiplayerGame(); }); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index edb6f9f865..97133773fa 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Play { protected override bool AllowBackButton => false; // handled by HoldForMenuButton - protected override UserStatus ScreenStatus => new UserStatusSoloGame(Beatmap.Value.BeatmapInfo); + protected override UserStatus ScreenStatus => new UserStatusSoloGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); public override float BackgroundParallaxAmount => 0.1f; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index e9ee5d3fa8..4d2e3a8cca 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -19,6 +19,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Menu; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.PlayerSettings; +using osu.Game.Users; using osuTK; using osuTK.Graphics; @@ -39,6 +40,8 @@ namespace osu.Game.Screens.Play private bool hideOverlays; public override bool HideOverlaysOnEnter => hideOverlays; + protected override UserStatus ScreenStatus => null; //shows the previous screen status + public override bool DisallowExternalBeatmapRulesetChanges => true; private Task loadTask; diff --git a/osu.Game/Users/UserStatus.cs b/osu.Game/Users/UserStatus.cs index 60e637d7e4..39f295f445 100644 --- a/osu.Game/Users/UserStatus.cs +++ b/osu.Game/Users/UserStatus.cs @@ -42,14 +42,17 @@ namespace osu.Game.Users public class UserStatusSoloGame : UserStatusBusy { - public UserStatusSoloGame(BeatmapInfo info) + public UserStatusSoloGame(BeatmapInfo info, Rulesets.RulesetInfo ruleset) { Beatmap = info; + Ruleset = ruleset; } public override string Message => @"Solo Game"; public BeatmapInfo Beatmap { get; } + + public Rulesets.RulesetInfo Ruleset { get; } } public class UserStatusEditing : UserStatusBusy From c766631ad6a163e04c531d20d4ed24db47d0e4d3 Mon Sep 17 00:00:00 2001 From: DrabWeb Date: Sat, 13 Apr 2019 11:39:22 -0300 Subject: [PATCH 0446/2854] Check for breaks using GameplayClock time. --- osu.Game/Screens/Play/BreakOverlay.cs | 4 ---- osu.Game/Screens/Play/Player.cs | 8 ++++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index ab81e613cc..d390787090 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -23,8 +23,6 @@ namespace osu.Game.Screens.Play private readonly Container fadeContainer; - public bool IsActive { get; private set; } - public List Breaks { get => breaks; @@ -127,7 +125,6 @@ namespace osu.Game.Screens.Play using (BeginAbsoluteSequence(b.StartTime, true)) { - Schedule(() => IsActive = true); fadeContainer.FadeIn(fade_duration); breakArrows.Show(fade_duration); @@ -145,7 +142,6 @@ namespace osu.Game.Screens.Play using (BeginDelayedSequence(b.Duration - fade_duration, true)) { - Schedule(() => IsActive = false); fadeContainer.FadeOut(fade_duration); breakArrows.Hide(fade_duration); } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 60287f7a2e..1878b19a1c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -60,8 +60,6 @@ namespace osu.Game.Screens.Play private SampleChannel sampleRestart; - private BreakOverlay breakOverlay; - protected ScoreProcessor ScoreProcessor { get; private set; } protected DrawableRuleset DrawableRuleset { get; private set; } @@ -117,7 +115,7 @@ namespace osu.Game.Screens.Play Child = DrawableRuleset } }, - breakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) + new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -408,7 +406,9 @@ namespace osu.Game.Screens.Play PauseOverlay.Hide(); // breaks and time-based conditions may allow instant resume. - if (breakOverlay.IsActive || GameplayClockContainer.GameplayClock.CurrentTime < Beatmap.Value.Beatmap.HitObjects.First().StartTime) + double time = GameplayClockContainer.GameplayClock.CurrentTime; + if (Beatmap.Value.Beatmap.Breaks.Any(b => time >= b.StartTime && time <= b.EndTime) || + time < Beatmap.Value.Beatmap.HitObjects.First().StartTime) completeResume(); else DrawableRuleset.RequestResume(completeResume); From 91c327a90f14b151ff3a62710559ab3f96153aa5 Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Sun, 14 Apr 2019 07:22:31 +0800 Subject: [PATCH 0447/2854] use ModDisplay --- osu.Game/Screens/Select/FooterButtonMods.cs | 28 +++++++++------------ 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/osu.Game/Screens/Select/FooterButtonMods.cs b/osu.Game/Screens/Select/FooterButtonMods.cs index 51973b1e3d..d4bb7d3f21 100644 --- a/osu.Game/Screens/Select/FooterButtonMods.cs +++ b/osu.Game/Screens/Select/FooterButtonMods.cs @@ -4,44 +4,40 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Screens.Play.HUD; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using System.Collections.Generic; using osuTK; +using osu.Framework.Input.Events; namespace osu.Game.Screens.Select { public class FooterButtonMods : FooterButton { - private readonly Bindable> selectedMods = new Bindable>(); - - private readonly FillFlowContainer modIcons; + private readonly FooterModDisplay modDisplay; public FooterButtonMods(Bindable> mods) { - Add(modIcons = new FillFlowContainer + Add(new Container { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Direction = FillDirection.Horizontal, + Child = modDisplay = new FooterModDisplay { + DisplayUnrankedText = false, + Scale = new Vector2(0.8f) + }, AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Left = 80, Right = 20 } + Margin = new MarginPadding { Left = 70 } }); if (mods != null) - { - selectedMods.BindTo(mods); - selectedMods.ValueChanged += updateModIcons; - } + modDisplay.Current = mods; } - private void updateModIcons(ValueChangedEvent> mods) + private class FooterModDisplay : ModDisplay { - modIcons.Clear(); - foreach (Mod mod in mods.NewValue) - { - modIcons.Add(new ModIcon(mod) { Scale = new Vector2(0.4f) }); - } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent?.Parent?.ReceivePositionalInputAt(screenSpacePos) ?? false; } } } From 2d227d25ccf45132e813e5293cd165fa30a6606f Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Sun, 14 Apr 2019 07:55:15 +0800 Subject: [PATCH 0448/2854] fix appveyor warnings --- osu.Game/Screens/Select/FooterButton.cs | 14 +++++++------- osu.Game/Screens/Select/FooterButtonMods.cs | 6 ++---- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs index aa547c658f..e18a086a10 100644 --- a/osu.Game/Screens/Select/FooterButton.cs +++ b/osu.Game/Screens/Select/FooterButton.cs @@ -21,11 +21,11 @@ namespace osu.Game.Screens.Select public string Text { - get => spriteText?.Text; + get => SpriteText?.Text; set { - if (spriteText != null) - spriteText.Text = value; + if (SpriteText != null) + SpriteText.Text = value; } } @@ -54,8 +54,8 @@ namespace osu.Game.Screens.Select } } - protected readonly Container textContainer; - protected readonly SpriteText spriteText; + protected readonly Container TextContainer; + protected readonly SpriteText SpriteText; private readonly Box box; private readonly Box light; @@ -66,10 +66,10 @@ namespace osu.Game.Screens.Select AutoSizeAxes = Axes.Both; Children = new Drawable[] { - textContainer = new Container + TextContainer = new Container { Size = new Vector2(100, 50), - Child = spriteText = new OsuSpriteText + Child = SpriteText = new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Select/FooterButtonMods.cs b/osu.Game/Screens/Select/FooterButtonMods.cs index d4bb7d3f21..0a5b8c0929 100644 --- a/osu.Game/Screens/Select/FooterButtonMods.cs +++ b/osu.Game/Screens/Select/FooterButtonMods.cs @@ -6,19 +6,17 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Screens.Play.HUD; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; using System.Collections.Generic; using osuTK; -using osu.Framework.Input.Events; namespace osu.Game.Screens.Select { public class FooterButtonMods : FooterButton { - private readonly FooterModDisplay modDisplay; - public FooterButtonMods(Bindable> mods) { + FooterModDisplay modDisplay; + Add(new Container { Anchor = Anchor.CentreLeft, From ac2eabc9bfb8bf7c5d52b5b8a1f97ca37fc1c585 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 16 Apr 2019 17:47:00 +0900 Subject: [PATCH 0449/2854] Fix replay rewinding not respecting 60fps playback --- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index deec2b8eac..c307520aca 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.UI { if (Math.Abs(manualClock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f) { - newProposedTime = manualClock.Rate > 0 + newProposedTime = newProposedTime > manualClock.CurrentTime ? Math.Min(newProposedTime, manualClock.CurrentTime + sixty_frame_time) : Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time); } From b684cd49e6fdd0233f91be155c1bdd182a76ddf7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Apr 2019 22:51:57 +0800 Subject: [PATCH 0450/2854] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 74ed9f91dd..1fcbe7c4c1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 9fff64c61c..831f33f0b8 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 5a3d6a02585b2d3b97e5846359e28a7b317cf6e3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Apr 2019 16:11:59 +0900 Subject: [PATCH 0451/2854] Fix post-merge errors --- osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs | 8 ++++---- osu.Game/Screens/Play/Player.cs | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs index fba4aca343..f58c0d35b3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs @@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("load player", () => { - SelectedMods.Value = new[] { gameMod = new TestMod() }; + Mods.Value = new[] { gameMod = new TestMod() }; InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre); stack.Push(new PlayerLoader(() => player = new TestPlayer())); }); @@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual.Gameplay { if (player.IsCurrentScreen()) { - playerMod1 = (TestMod)player.SelectedMods.Value.Single(); + playerMod1 = (TestMod)player.Mods.Value.Single(); return true; } @@ -86,7 +86,7 @@ namespace osu.Game.Tests.Visual.Gameplay { if (player.IsCurrentScreen()) { - playerMod2 = (TestMod)player.SelectedMods.Value.Single(); + playerMod2 = (TestMod)player.Mods.Value.Single(); return true; } @@ -114,7 +114,7 @@ namespace osu.Game.Tests.Visual.Gameplay private class TestPlayer : Player { - public new Bindable> SelectedMods => base.SelectedMods; + public new Bindable> Mods => base.Mods; public TestPlayer() : base(false, false) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index f8b3efb781..f833aa2bb7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -71,8 +71,8 @@ namespace osu.Game.Screens.Play protected GameplayClockContainer GameplayClockContainer { get; private set; } [Cached] - [Cached(Type = typeof(IBindable>))] - protected readonly Bindable> SelectedMods = new Bindable>(Enumerable.Empty()); + [Cached(Type = typeof(IBindable>))] + protected readonly Bindable> Mods = new Bindable>(Array.Empty()); private readonly bool allowPause; private readonly bool showResults; @@ -93,7 +93,7 @@ namespace osu.Game.Screens.Play { this.api = api; - SelectedMods.Value = base.SelectedMods.Value.Select(m => m.CreateCopy()).ToArray(); + Mods.Value = base.Mods.Value.Select(m => m.CreateCopy()).ToArray(); WorkingBeatmap working = loadBeatmap(); From 9f92b3a8ba503940bbd559388149812086a4504f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Apr 2019 16:34:53 +0900 Subject: [PATCH 0452/2854] Add xmldoc --- osu.Game/Graphics/Containers/LogoTrackingContainer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs index 5ebdb7d45d..f139040abd 100644 --- a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs +++ b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs @@ -60,6 +60,9 @@ namespace osu.Game.Graphics.Containers startPosition = null; } + /// + /// Stops the logo assigned in from tracking the facade's position. + /// public void StopTracking() { if (Logo != null) From 3a1587fa53b53ce5490032469d8e19fce34d3f47 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Apr 2019 16:40:47 +0900 Subject: [PATCH 0453/2854] Throw exception when not relatively positioned --- osu.Game/Graphics/Containers/LogoTrackingContainer.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs index f139040abd..ddf257776f 100644 --- a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs +++ b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs @@ -92,12 +92,15 @@ namespace osu.Game.Graphics.Containers if (Logo == null) return; + if (Logo.RelativePositionAxes != Axes.Both) + throw new InvalidOperationException($"Tracking logo must have {nameof(RelativePositionAxes)} = Axes.Both"); + // Account for the scale of the actual OsuLogo, as SizeForFlow only accounts for the sprite scale. ((ExposedFacade)LogoFacade).SetSize(new Vector2(Logo.SizeForFlow * Logo.Scale.X)); var localPos = ComputeLogoTrackingPosition(); - if (LogoFacade.Parent != null && Logo.Position != localPos && Logo.RelativePositionAxes == Axes.Both) + if (LogoFacade.Parent != null && Logo.Position != localPos) { // If this is our first update since tracking has started, initialize our starting values for interpolation if (startTime == null || startPosition == null) From 4106da24303cf0289293d0d87ccc16fc4aee0b93 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Apr 2019 16:41:20 +0900 Subject: [PATCH 0454/2854] Rename facade + cleanup usage --- .../Graphics/Containers/LogoTrackingContainer.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs index ddf257776f..fb23038dde 100644 --- a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs +++ b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs @@ -15,22 +15,19 @@ namespace osu.Game.Graphics.Containers /// public class LogoTrackingContainer : Container { - public Facade LogoFacade { get; } + public Facade LogoFacade => facade; protected OsuLogo Logo { get; private set; } + private readonly InternalFacade facade = new InternalFacade(); + private Easing easing; private Vector2? startPosition; private double? startTime; private double duration; - public LogoTrackingContainer() - { - LogoFacade = new ExposedFacade(); - } - /// - /// Assign the logo that should track the Facade's position, as well as how it should transform to its initial position. + /// Assign the logo that should track the facade's position, as well as how it should transform to its initial position. /// /// The instance of the logo to be used for tracking. /// The scale of the facade. Does not actually affect the logo itself. @@ -96,7 +93,7 @@ namespace osu.Game.Graphics.Containers throw new InvalidOperationException($"Tracking logo must have {nameof(RelativePositionAxes)} = Axes.Both"); // Account for the scale of the actual OsuLogo, as SizeForFlow only accounts for the sprite scale. - ((ExposedFacade)LogoFacade).SetSize(new Vector2(Logo.SizeForFlow * Logo.Scale.X)); + facade.SetSize(new Vector2(Logo.SizeForFlow * Logo.Scale.X)); var localPos = ComputeLogoTrackingPosition(); @@ -133,7 +130,7 @@ namespace osu.Game.Graphics.Containers base.Dispose(isDisposing); } - private class ExposedFacade : Facade + private class InternalFacade : Facade { public void SetSize(Vector2 size) { From 897bfa60db361b01682be36b55640ed3bf4a4d9f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Apr 2019 17:02:47 +0900 Subject: [PATCH 0455/2854] Fix tracking position during 150ms state change delay --- osu.Game/Screens/Menu/ButtonArea.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Menu/ButtonArea.cs b/osu.Game/Screens/Menu/ButtonArea.cs index d6e1aef63c..eada1e0777 100644 --- a/osu.Game/Screens/Menu/ButtonArea.cs +++ b/osu.Game/Screens/Menu/ButtonArea.cs @@ -32,6 +32,7 @@ namespace osu.Game.Screens.Menu RelativeSizeAxes = Axes.X, Size = new Vector2(1, BUTTON_AREA_HEIGHT), Alpha = 0, + AlwaysPresent = true, // Always needs to be present for correct tracking on initial -> toplevel state change Children = new Drawable[] { buttonAreaBackground = new ButtonAreaBackground(), From 106e77c3d7794ab923380cbeb2cbe72c7b289741 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Apr 2019 17:15:23 +0900 Subject: [PATCH 0456/2854] Cleanup testcase --- .../TestCaseLogoTrackingContainer.cs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs index 8b70a34c85..088a46d5e4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs @@ -35,7 +35,6 @@ namespace osu.Game.Tests.Visual.UserInterface }; private OsuLogo logo; - private readonly Bindable uiScale = new Bindable(); private TestLogoTrackingContainer trackingContainer; private Container transferContainer; private Box visualBox; @@ -45,13 +44,6 @@ namespace osu.Game.Tests.Visual.UserInterface private const float visual_box_size = 72; - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - config.BindWith(OsuSetting.UIScale, uiScale); - AddSliderStep("Adjust scale", 0.8f, 1.5f, 1f, v => uiScale.Value = v); - } - [SetUpSteps] public void SetUpSteps() { @@ -69,7 +61,7 @@ namespace osu.Game.Tests.Visual.UserInterface /// Check if the logo is still tracking the facade. /// [Test] - public void MoveFacadeTest() + public void TestMoveFacade() { AddToggleStep("Toggle move continuously", b => randomPositions = b); AddStep("Add tracking containers", addFacadeContainers); @@ -82,7 +74,7 @@ namespace osu.Game.Tests.Visual.UserInterface /// Check if the facade is removed from the container, the logo stops tracking. /// [Test] - public void RemoveFacadeTest() + public void TestRemoveFacade() { AddStep("Add tracking containers", addFacadeContainers); AddStep("Move facade to random position", moveLogoFacade); @@ -95,7 +87,7 @@ namespace osu.Game.Tests.Visual.UserInterface /// Check if the facade gets added to a new container, tracking starts on the new facade. /// [Test] - public void TransferFacadeTest() + public void TestTransferFacade() { AddStep("Add tracking containers", addFacadeContainers); AddStep("Move facade to random position", moveLogoFacade); @@ -106,6 +98,7 @@ namespace osu.Game.Tests.Visual.UserInterface transferContainerBox.Colour = Color4.Tomato; moveLogoFacade(); }); + waitForMove(); AddAssert("Logo is tracking", () => trackingContainer.IsLogoTracking); } @@ -114,7 +107,7 @@ namespace osu.Game.Tests.Visual.UserInterface /// Add a facade to a flow container, move the logo to the center of the screen, then start tracking on the facade. /// [Test] - public void FlowContainerTest() + public void TestFlowContainer() { FillFlowContainer flowContainer; @@ -188,14 +181,16 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - public void SetFacadeSizeTest() + public void TestSetFacadeSize() { bool failed = false; + AddStep("Set up scenario", () => { failed = false; addFacadeContainers(); }); + AddStep("Try setting facade size", () => { try @@ -208,14 +203,16 @@ namespace osu.Game.Tests.Visual.UserInterface failed = true; } }); + AddAssert("Exception thrown", () => failed); } [Test] - public void SetMultipleContainersTest() + public void TestSetMultipleContainers() { bool failed = false; LogoTrackingContainer newContainer = new LogoTrackingContainer(); + AddStep("Set up scenario", () => { failed = false; @@ -223,6 +220,7 @@ namespace osu.Game.Tests.Visual.UserInterface addFacadeContainers(); moveLogoFacade(); }); + AddStep("Try tracking new container", () => { try @@ -235,6 +233,7 @@ namespace osu.Game.Tests.Visual.UserInterface failed = true; } }); + AddAssert("Exception thrown", () => failed); } From c145ce2d005ac723d1625fab276d23cc4384855d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Apr 2019 17:19:55 +0900 Subject: [PATCH 0457/2854] Remove usings --- .../Visual/UserInterface/TestCaseLogoTrackingContainer.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs index 088a46d5e4..e45e2e24da 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestCaseLogoTrackingContainer.cs @@ -4,15 +4,12 @@ using System; using System.Collections.Generic; using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.MathUtils; using osu.Framework.Testing; -using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; From 12b6bc48bd780a20b563f7c45cd9c8e5662e7c19 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Apr 2019 17:24:09 +0900 Subject: [PATCH 0458/2854] Use .With() wherever possible --- osu.Game/Screens/Menu/ButtonSystem.cs | 3 +-- osu.Game/Screens/Play/PlayerLoader.cs | 9 +++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index f6778691bf..519fadb34b 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -101,11 +101,10 @@ namespace osu.Game.Screens.Menu { VisibleState = ButtonSystemState.Play, }, - logoTrackingContainer.LogoFacade + logoTrackingContainer.LogoFacade.With(d => d.Scale = new Vector2(0.74f)) }); buttonArea.Flow.CentreTarget = logoTrackingContainer.LogoFacade; - logoTrackingContainer.LogoFacade.Scale = new Vector2(0.74f); } [Resolved(CanBeNull = true)] diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 7ff214c607..6a55fe278b 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -351,9 +351,6 @@ namespace osu.Game.Screens.Play { var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); - facade.Anchor = Anchor.TopCentre; - facade.Origin = Anchor.TopCentre; - AutoSizeAxes = Axes.Both; Children = new Drawable[] { @@ -365,7 +362,11 @@ namespace osu.Game.Screens.Play Direction = FillDirection.Vertical, Children = new[] { - facade, + facade.With(d => + { + d.Anchor = Anchor.TopCentre; + d.Origin = Anchor.TopCentre; + }), new OsuSpriteText { Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)), From d1799d197ddc5d0beee07edb14f5bac7537d039a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Apr 2019 09:27:30 +0800 Subject: [PATCH 0459/2854] Update resources and mods usage --- osu.Game.Rulesets.Osu.Tests/TestCaseOsuFlashlight.cs | 3 ++- osu.Game/osu.Game.csproj | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseOsuFlashlight.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseOsuFlashlight.cs index 6f198084f5..1e72591b87 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseOsuFlashlight.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseOsuFlashlight.cs @@ -11,7 +11,8 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override Player CreatePlayer(Ruleset ruleset) { - Beatmap.Value.Mods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), }; + Mods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), }; + return base.CreatePlayer(ruleset); } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 1fcbe7c4c1..25a98c9b74 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + From dfa5beea89ef6e2f7398723cf60ae386fd2bf738 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 18 Apr 2019 14:24:19 +0900 Subject: [PATCH 0460/2854] Use the actual scale of Flashlight for FlashlightSize --- osu.Game/Rulesets/Mods/ModFlashlight.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index b5a907c0ef..22c2f6dd51 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.MatrixExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.Primitives; @@ -79,7 +80,7 @@ namespace osu.Game.Rulesets.Mods flashNode.Shader = shader; flashNode.ScreenSpaceDrawQuad = ScreenSpaceDrawQuad; flashNode.FlashlightPosition = Vector2Extensions.Transform(FlashlightPosition, DrawInfo.Matrix); - flashNode.FlashlightSize = Vector2Extensions.Transform(FlashlightSize, DrawInfo.Matrix); + flashNode.FlashlightSize = FlashlightSize * DrawInfo.Matrix.ExtractScale().Xy; flashNode.FlashlightDim = FlashlightDim; } From 037e23247f766ce5d0bc71eeeaac4f4787f99dfe Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 18 Apr 2019 14:31:47 +0900 Subject: [PATCH 0461/2854] Remove unused using --- osu.Game/Rulesets/Mods/ModFlashlight.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 22c2f6dd51..c0816b2457 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.MatrixExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.Primitives; From 9d6912a6920848036a617f241c4c4ff154e04149 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Apr 2019 21:23:40 +0800 Subject: [PATCH 0462/2854] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 1fcbe7c4c1..2b7ded6d8c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 831f33f0b8..958191d708 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From c8d760b555b52265f0cf2dc3f3ec3832ead211e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Apr 2019 21:33:29 +0800 Subject: [PATCH 0463/2854] Attempt using previous appveyor image --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 1f485485da..4dcaa7b45e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,6 @@ clone_depth: 1 version: '{branch}-{build}' -image: Visual Studio 2017 +image: Previous Visual Studio 2017 test: off install: - cmd: git submodule update --init --recursive --depth=5 From 7a0320d39e29c66824a04ef7b6e06a4aaa35cfe9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Apr 2019 00:01:26 +0800 Subject: [PATCH 0464/2854] Increase diagnostic level --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 4dcaa7b45e..51e946219f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,8 +1,8 @@ clone_depth: 1 version: '{branch}-{build}' -image: Previous Visual Studio 2017 +image: Visual Studio 2017 test: off install: - cmd: git submodule update --init --recursive --depth=5 build_script: - - cmd: PowerShell -Version 2.0 .\build.ps1 + - cmd: PowerShell -Version 2.0 .\build.ps1 -Verbosity Diagnostic From 6502e26d09020a3ee564b40e74e0fa937aca70ea Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 19 Apr 2019 16:21:15 +0900 Subject: [PATCH 0465/2854] Add draw size invalidation to ZoomableScrollContainer --- .../Components/Timeline/ZoomableScrollContainer.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs index 1e94a20dc7..f41b3cddc0 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs @@ -92,13 +92,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } - protected override void Update() - { - base.Update(); - - zoomedContent.Width = DrawWidth * currentZoom; - } - protected override bool OnScroll(ScrollEvent e) { if (e.IsPrecise) @@ -169,6 +162,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline float targetOffset = expectedWidth * (focusPoint / contentSize) - focusOffset; d.currentZoom = newZoom; + + d.zoomedContent.Width = d.DrawWidth * d.currentZoom; + // Temporarily here to make sure ScrollTo gets the correct DrawSize for scrollable area. + // TODO: Make sure draw size gets invalidated properly on the framework side, and remove this once it is. + d.Invalidate(Invalidation.DrawSize); d.ScrollTo(targetOffset, false); } From d3920d652d82b1d7603bec9e94ba9f9500d9879e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 19 Apr 2019 12:44:57 +0900 Subject: [PATCH 0466/2854] Fix loader animation test case --- osu.Game.Tests/Visual/Menus/TestCaseLoaderAnimation.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestCaseLoaderAnimation.cs b/osu.Game.Tests/Visual/Menus/TestCaseLoaderAnimation.cs index df12e14891..eb275cbceb 100644 --- a/osu.Game.Tests/Visual/Menus/TestCaseLoaderAnimation.cs +++ b/osu.Game.Tests/Visual/Menus/TestCaseLoaderAnimation.cs @@ -23,7 +23,11 @@ namespace osu.Game.Tests.Visual.Menus public TestCaseLoaderAnimation() { - Child = logo = new OsuLogo { Depth = float.MinValue }; + Child = logo = new OsuLogo + { + Alpha = 0, + Depth = float.MinValue + }; } [Test] @@ -39,7 +43,7 @@ namespace osu.Game.Tests.Visual.Menus LoadScreen(loader); }); - AddAssert("loaded", () => + AddUntilStep("loaded", () => { logoVisible = loader.Logo?.Alpha > 0; return loader.Logo != null && loader.ScreenLoaded; From 489929d25ce7eb0907ee8a848a5aa3094c22bde1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 19 Apr 2019 19:45:17 +0900 Subject: [PATCH 0467/2854] Fix PlayerLoader testcase being completely broken --- .../Visual/Gameplay/TestCasePlayerLoader.cs | 48 +++++++------------ 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs index f58c0d35b3..1f0a97cc58 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs @@ -10,21 +10,25 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Screens; using osu.Game.Screens.Play; +using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual.Gameplay { public class TestCasePlayerLoader : ManualInputManagerTestCase { private PlayerLoader loader; - private readonly OsuScreenStack stack; + private OsuScreenStack stack; - public TestCasePlayerLoader() + [SetUp] + public void Setup() => Schedule(() => { - InputManager.Add(stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }); - } + InputManager.Child = stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }; + Beatmap.Value = new TestWorkingBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), Clock); + }); [Test] public void TestLoadContinuation() @@ -33,8 +37,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for current", () => loader.IsCurrentScreen()); AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre)); AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen()); - AddStep("exit loader", () => loader.Exit()); - AddUntilStep("wait for no longer alive", () => !loader.IsAlive); AddStep("load slow dummy beatmap", () => { SlowLoadPlayer slow = null; @@ -58,41 +60,25 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("load player", () => { Mods.Value = new[] { gameMod = new TestMod() }; - InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre); - stack.Push(new PlayerLoader(() => player = new TestPlayer())); - }); - - AddUntilStep("wait for player to become current", () => - { - if (player.IsCurrentScreen()) - { - playerMod1 = (TestMod)player.Mods.Value.Single(); - return true; - } - - return false; + stack.Push(loader = new PlayerLoader(() => player = new TestPlayer())); }); + AddUntilStep("wait for loader to become current", () => loader.IsCurrentScreen()); + AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre)); + AddUntilStep("wait for player to be current", () => player.IsCurrentScreen()); + AddStep("retrieve mods", () => playerMod1 = (TestMod)player.Mods.Value.Single()); AddAssert("game mods not applied", () => gameMod.Applied == false); AddAssert("player mods applied", () => playerMod1.Applied); AddStep("restart player", () => { + var lastPlayer = player; player = null; - player.Restart(); - }); - - AddUntilStep("wait for player to become current", () => - { - if (player.IsCurrentScreen()) - { - playerMod2 = (TestMod)player.Mods.Value.Single(); - return true; - } - - return false; + lastPlayer.Restart(); }); + AddUntilStep("wait for player to be current", () => player.IsCurrentScreen()); + AddStep("retrieve mods", () => playerMod2 = (TestMod)player.Mods.Value.Single()); AddAssert("game mods not applied", () => gameMod.Applied == false); AddAssert("player has different mods", () => playerMod1 != playerMod2); AddAssert("player mods applied", () => playerMod2.Applied); From 5989cf284904b5affa224656412a9b687f1f880d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 19 Apr 2019 20:39:25 +0900 Subject: [PATCH 0468/2854] Revert "Increase diagnostic level" This reverts commit 7a0320d39e29c66824a04ef7b6e06a4aaa35cfe9. --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 51e946219f..4dcaa7b45e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,8 +1,8 @@ clone_depth: 1 version: '{branch}-{build}' -image: Visual Studio 2017 +image: Previous Visual Studio 2017 test: off install: - cmd: git submodule update --init --recursive --depth=5 build_script: - - cmd: PowerShell -Version 2.0 .\build.ps1 -Verbosity Diagnostic + - cmd: PowerShell -Version 2.0 .\build.ps1 From 4cf234295c67e05bc598299e70212bf55f7ceb9f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 19 Apr 2019 21:27:24 +0900 Subject: [PATCH 0469/2854] Fix appveyor temporarily --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 1f485485da..4dcaa7b45e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,6 @@ clone_depth: 1 version: '{branch}-{build}' -image: Visual Studio 2017 +image: Previous Visual Studio 2017 test: off install: - cmd: git submodule update --init --recursive --depth=5 From 8e485f32835d6d0c72b9e04d52c3ab1b42879b8c Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 20 Apr 2019 06:41:09 +0300 Subject: [PATCH 0470/2854] Fix issue --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index f3c7939a94..9e3f28918c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osuTK; using osuTK.Graphics; @@ -40,6 +41,10 @@ namespace osu.Game.Rulesets.Osu.Mods { scoreProcessor.Health.ValueChanged += health => { blinds.AnimateClosedness((float)health.NewValue); }; } + + public ScoreRank AdjustRank(ScoreRank rank) + { + } /// /// Element for the Blinds mod drawing 2 black boxes covering the whole screen which resize inside a restricted area with some leniency. From 59da78b0d4fd31a3df67b77d973fc66fbc0a94f7 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 20 Apr 2019 06:44:59 +0300 Subject: [PATCH 0471/2854] nothing. --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index 9e3f28918c..f3c7939a94 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -12,7 +12,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; -using osu.Game.Scoring; using osuTK; using osuTK.Graphics; @@ -41,10 +40,6 @@ namespace osu.Game.Rulesets.Osu.Mods { scoreProcessor.Health.ValueChanged += health => { blinds.AnimateClosedness((float)health.NewValue); }; } - - public ScoreRank AdjustRank(ScoreRank rank) - { - } /// /// Element for the Blinds mod drawing 2 black boxes covering the whole screen which resize inside a restricted area with some leniency. From 6f2bc943eb57c0a0ec97de5053c1d9cbd91249d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 21 Apr 2019 12:04:15 +0800 Subject: [PATCH 0472/2854] Fix rank display on break info display --- osu.Game/Screens/Play/Break/BreakInfoLine.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Break/BreakInfoLine.cs b/osu.Game/Screens/Play/Break/BreakInfoLine.cs index 4b07405812..70e7b8f297 100644 --- a/osu.Game/Screens/Play/Break/BreakInfoLine.cs +++ b/osu.Game/Screens/Play/Break/BreakInfoLine.cs @@ -1,8 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; @@ -60,7 +62,13 @@ namespace osu.Game.Screens.Play.Break valueText.Text = newText; } - protected virtual string Format(T count) => count.ToString(); + protected virtual string Format(T count) + { + if (count is Enum countEnum) + return countEnum.GetDescription(); + + return count.ToString(); + } [BackgroundDependencyLoader] private void load(OsuColour colours) From 30d4dd93558f00bb806612040568e3827ec41d3e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 21 Apr 2019 21:38:12 +0900 Subject: [PATCH 0473/2854] Change + rank strings to be cleaner for the end-user --- osu.Game/Scoring/ScoreRank.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Scoring/ScoreRank.cs b/osu.Game/Scoring/ScoreRank.cs index 82c33748bb..a93d015f1b 100644 --- a/osu.Game/Scoring/ScoreRank.cs +++ b/osu.Game/Scoring/ScoreRank.cs @@ -25,13 +25,13 @@ namespace osu.Game.Scoring [Description(@"S")] S, - [Description(@"SPlus")] + [Description(@"S+")] SH, [Description(@"SS")] X, - [Description(@"SSPlus")] + [Description(@"SS+")] XH, } } From 73da4236391df7209c476ff769faf2a480e8f20b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 21 Apr 2019 15:58:19 +0300 Subject: [PATCH 0474/2854] Adjust rank --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 0fddb19a6c..4a1eba9c5e 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -95,6 +95,11 @@ namespace osu.Game.Rulesets.Scoring /// protected virtual bool DefaultFailCondition => Health.Value == Health.MinValue; + /// + /// Used by specific mods to adjust . + /// + public bool AdjustRank { get; set; } + protected ScoreProcessor() { Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); }; @@ -104,9 +109,9 @@ namespace osu.Game.Rulesets.Scoring private ScoreRank rankFrom(double acc) { if (acc == 1) - return ScoreRank.X; + return (AdjustRank ? ScoreRank.XH : ScoreRank.X); if (acc > 0.95) - return ScoreRank.S; + return (AdjustRank ? ScoreRank.SH : ScoreRank.S); if (acc > 0.9) return ScoreRank.A; if (acc > 0.8) From 4e07975e7ca88c49d4c4fa83206bc27d254e7456 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 21 Apr 2019 15:58:40 +0300 Subject: [PATCH 0475/2854] Ranks must be adjusted in Hidden Mod --- osu.Game/Rulesets/Mods/ModHidden.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index c7e3f0a78f..744da30f0a 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -8,10 +8,11 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mods { - public abstract class ModHidden : Mod, IReadFromConfig, IApplicableToDrawableHitObjects + public abstract class ModHidden : Mod, IReadFromConfig, IApplicableToDrawableHitObjects, IApplicableToScoreProcessor { public override string Name => "Hidden"; public override string Acronym => "HD"; @@ -31,6 +32,11 @@ namespace osu.Game.Rulesets.Mods foreach (var d in drawables.Skip(IncreaseFirstObjectVisibility.Value ? 1 : 0)) d.ApplyCustomUpdateState += ApplyHiddenState; } + + public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) + { + scoreProcessor.AdjustRank = true; + } protected virtual void ApplyHiddenState(DrawableHitObject hitObject, ArmedState state) { From 77614189c978e4bffa18b4dee16dbfa90351944c Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 21 Apr 2019 17:48:50 +0300 Subject: [PATCH 0476/2854] Make AdjustRank Bindable --- osu.Game/Rulesets/Mods/ModHidden.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index 744da30f0a..4c4177a779 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mods public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { - scoreProcessor.AdjustRank = true; + scoreProcessor.AdjustRank.Value = true; } protected virtual void ApplyHiddenState(DrawableHitObject hitObject, ArmedState state) From b200142afb174e4c16a64403cb23575ab3692fd3 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 21 Apr 2019 17:55:01 +0300 Subject: [PATCH 0477/2854] Make AdjustRank Bindable and fix issue --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 4a1eba9c5e..617e643d41 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -98,20 +98,21 @@ namespace osu.Game.Rulesets.Scoring /// /// Used by specific mods to adjust . /// - public bool AdjustRank { get; set; } + public BindableBool AdjustRank = new BindableBool(); protected ScoreProcessor() { Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); }; Accuracy.ValueChanged += delegate { Rank.Value = rankFrom(Accuracy.Value); }; + AdjustRank.ValueChanged += delegate { Rank.Value = rankFrom(Accuracy.Value); }; // Update rank immediately if AdjustRank was changed } private ScoreRank rankFrom(double acc) { if (acc == 1) - return (AdjustRank ? ScoreRank.XH : ScoreRank.X); + return (AdjustRank.Value ? ScoreRank.XH : ScoreRank.X); if (acc > 0.95) - return (AdjustRank ? ScoreRank.SH : ScoreRank.S); + return (AdjustRank.Value ? ScoreRank.SH : ScoreRank.S); if (acc > 0.9) return ScoreRank.A; if (acc > 0.8) @@ -128,6 +129,7 @@ namespace osu.Game.Rulesets.Scoring /// Whether to store the current state of the for future use. protected virtual void Reset(bool storeResults) { + AdjustRank.Value = false; TotalScore.Value = 0; Accuracy.Value = 1; Health.Value = 1; From c5bf01cc5ccaf8a53f57fce21cc1fb16cf6cfc0b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 21 Apr 2019 18:05:52 +0300 Subject: [PATCH 0478/2854] Fix issue --- osu.Game/Rulesets/Mods/ModHidden.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index 4c4177a779..b36b2362ca 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Mods foreach (var d in drawables.Skip(IncreaseFirstObjectVisibility.Value ? 1 : 0)) d.ApplyCustomUpdateState += ApplyHiddenState; } - + public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { scoreProcessor.AdjustRank.Value = true; From cff319e0d8cd22d12f7e44a4754cca2a2f8051ec Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 21 Apr 2019 18:12:50 +0300 Subject: [PATCH 0479/2854] Fix issue --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 617e643d41..e60ca70969 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Scoring protected virtual bool DefaultFailCondition => Health.Value == Health.MinValue; /// - /// Used by specific mods to adjust . + /// Used by specific mods to adjust . /// public BindableBool AdjustRank = new BindableBool(); From ae51a9e45122e453dcfc49bb237c55882854b140 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Apr 2019 09:57:33 +0900 Subject: [PATCH 0480/2854] Fix drawable rank texture lookup --- osu.Game/Online/Leaderboards/DrawableRank.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Leaderboards/DrawableRank.cs b/osu.Game/Online/Leaderboards/DrawableRank.cs index 9155df69b4..5224150181 100644 --- a/osu.Game/Online/Leaderboards/DrawableRank.cs +++ b/osu.Game/Online/Leaderboards/DrawableRank.cs @@ -43,7 +43,21 @@ namespace osu.Game.Online.Leaderboards private void updateTexture() { - rankSprite.Texture = textures.Get($@"Grades/{Rank.GetDescription()}"); + string textureName; + switch (Rank) + { + default: + textureName = Rank.GetDescription(); + break; + case ScoreRank.SH: + textureName = "SPlus"; + break; + case ScoreRank.XH: + textureName = "SSPlus"; + break; + } + + rankSprite.Texture = textures.Get($@"Grades/{textureName}"); } public void UpdateRank(ScoreRank newRank) From 732c38fa794a81ab3c2b2e8be8a9abec8f189f89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Apr 2019 10:45:56 +0900 Subject: [PATCH 0481/2854] Update framework again --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 2b7ded6d8c..a45cac2cb6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 958191d708..70edc01821 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 8ffb2f4224d5ba340fa6e42dd3518edbd938df2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Apr 2019 10:55:01 +0900 Subject: [PATCH 0482/2854] Remove black box blocking some visual tests from being visible --- osu.Game/Tests/Visual/AllPlayersTestCase.cs | 9 --------- osu.Game/Tests/Visual/PlayerTestCase.cs | 15 --------------- 2 files changed, 24 deletions(-) diff --git a/osu.Game/Tests/Visual/AllPlayersTestCase.cs b/osu.Game/Tests/Visual/AllPlayersTestCase.cs index af190ec7d5..3e1f408a16 100644 --- a/osu.Game/Tests/Visual/AllPlayersTestCase.cs +++ b/osu.Game/Tests/Visual/AllPlayersTestCase.cs @@ -3,7 +3,6 @@ using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Graphics.Shapes; using osu.Framework.Screens; using osu.Framework.Timing; using osu.Game.Beatmaps; @@ -11,7 +10,6 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps; -using osuTK.Graphics; namespace osu.Game.Tests.Visual { @@ -26,13 +24,6 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { - Add(new Box - { - RelativeSizeAxes = Framework.Graphics.Axes.Both, - Colour = Color4.Black, - Depth = int.MaxValue - }); - foreach (var r in rulesets.AvailableRulesets) { Player p = null; diff --git a/osu.Game/Tests/Visual/PlayerTestCase.cs b/osu.Game/Tests/Visual/PlayerTestCase.cs index 719b1d6892..d308b86b5a 100644 --- a/osu.Game/Tests/Visual/PlayerTestCase.cs +++ b/osu.Game/Tests/Visual/PlayerTestCase.cs @@ -2,16 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps; -using osuTK.Graphics; namespace osu.Game.Tests.Visual { @@ -26,17 +22,6 @@ namespace osu.Game.Tests.Visual this.ruleset = ruleset; } - [BackgroundDependencyLoader] - private void load() - { - Add(new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Depth = int.MaxValue - }); - } - [SetUpSteps] public void SetUpSteps() { From 26f0b2a4fe65342757a52476367b6ae432b10b40 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 22 Apr 2019 06:12:41 +0300 Subject: [PATCH 0483/2854] Remove bindable Bad usage --- osu.Game/Rulesets/Mods/ModHidden.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index b36b2362ca..389edb5a35 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mods public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { - scoreProcessor.AdjustRank.Value = true; + scoreProcessor.AdjustRank = true; } protected virtual void ApplyHiddenState(DrawableHitObject hitObject, ArmedState state) From de542ca20251951df7dea4cd449018aa03b2c29d Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 22 Apr 2019 06:14:14 +0300 Subject: [PATCH 0484/2854] Remove bindable and use property --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index e60ca70969..c37b5d189f 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -95,24 +95,29 @@ namespace osu.Game.Rulesets.Scoring /// protected virtual bool DefaultFailCondition => Health.Value == Health.MinValue; + private bool adjustRank = false; + /// /// Used by specific mods to adjust . /// - public BindableBool AdjustRank = new BindableBool(); + public bool AdjustRank + { + get { return adjustRank; } + set { adjustRank = value; Rank.Value = rankFrom(Accuracy.Value); } // Update rank immediately if AdjustRank was changed + } protected ScoreProcessor() { Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); }; Accuracy.ValueChanged += delegate { Rank.Value = rankFrom(Accuracy.Value); }; - AdjustRank.ValueChanged += delegate { Rank.Value = rankFrom(Accuracy.Value); }; // Update rank immediately if AdjustRank was changed } private ScoreRank rankFrom(double acc) { if (acc == 1) - return (AdjustRank.Value ? ScoreRank.XH : ScoreRank.X); + return (adjustRank ? ScoreRank.XH : ScoreRank.X); if (acc > 0.95) - return (AdjustRank.Value ? ScoreRank.SH : ScoreRank.S); + return (adjustRank ? ScoreRank.SH : ScoreRank.S); if (acc > 0.9) return ScoreRank.A; if (acc > 0.8) @@ -129,7 +134,6 @@ namespace osu.Game.Rulesets.Scoring /// Whether to store the current state of the for future use. protected virtual void Reset(bool storeResults) { - AdjustRank.Value = false; TotalScore.Value = 0; Accuracy.Value = 1; Health.Value = 1; @@ -137,6 +141,7 @@ namespace osu.Game.Rulesets.Scoring Rank.Value = ScoreRank.X; HighestCombo.Value = 0; + AdjustRank = false; HasFailed = false; } From c784bc4418c9908099a1ecc08fb6194140743224 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 22 Apr 2019 06:15:37 +0300 Subject: [PATCH 0485/2854] Remove whitespaces --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index c37b5d189f..e563d3a848 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Scoring protected virtual bool DefaultFailCondition => Health.Value == Health.MinValue; private bool adjustRank = false; - + /// /// Used by specific mods to adjust . /// From a47f5040af2b157f58aeabc6ac9bbae97d09b0e2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Apr 2019 13:58:05 +0900 Subject: [PATCH 0486/2854] Improve waveform graph testing --- .../Visual/Editor/TestCaseWaveform.cs | 79 +++++++++++++------ 1 file changed, 56 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Editor/TestCaseWaveform.cs b/osu.Game.Tests/Visual/Editor/TestCaseWaveform.cs index c35e8741c1..ce6ca08a61 100644 --- a/osu.Game.Tests/Visual/Editor/TestCaseWaveform.cs +++ b/osu.Game.Tests/Visual/Editor/TestCaseWaveform.cs @@ -3,12 +3,13 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio.Track; using osu.Framework.Graphics; using osu.Framework.Graphics.Audio; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; -using osuTK; using osuTK.Graphics; namespace osu.Game.Tests.Visual.Editor @@ -16,35 +17,38 @@ namespace osu.Game.Tests.Visual.Editor [TestFixture] public class TestCaseWaveform : OsuTestCase { + private WorkingBeatmap waveformBeatmap; + [BackgroundDependencyLoader] private void load() { - Beatmap.Value = new WaveformTestBeatmap(); + waveformBeatmap = new WaveformTestBeatmap(); + } - FillFlowContainer flow; - Child = flow = new FillFlowContainer + [TestCase(1f)] + [TestCase(1f / 2)] + [TestCase(1f / 4)] + [TestCase(1f / 8)] + [TestCase(1f / 16)] + [TestCase(0f)] + public void TestResolution(float resolution) + { + TestWaveformGraph graph = null; + + AddStep("add graph", () => { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10), - }; - - for (int i = 1; i <= 16; i *= 2) - { - var newDisplay = new WaveformGraph - { - RelativeSizeAxes = Axes.Both, - Resolution = 1f / i, - Waveform = Beatmap.Value.Waveform, - }; - - flow.Add(new Container + Child = new Container { RelativeSizeAxes = Axes.X, Height = 100, Children = new Drawable[] { - newDisplay, + graph = new TestWaveformGraph + { + RelativeSizeAxes = Axes.Both, + Resolution = resolution, + Waveform = waveformBeatmap.Waveform, + }, new Container { Anchor = Anchor.Centre, @@ -62,13 +66,42 @@ namespace osu.Game.Tests.Visual.Editor { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Text = $"Resolution: {1f / i:0.00}" + Text = $"Resolution: {resolution:0.00}" } } } } - }); - } + }; + }); + + AddUntilStep("wait for load", () => graph.ResampledWaveform != null); + } + + [Test] + public void TestDefaultBeatmap() + { + TestWaveformGraph graph = null; + + AddStep("add graph", () => + { + Child = new Container + { + RelativeSizeAxes = Axes.X, + Height = 100, + Child = graph = new TestWaveformGraph + { + RelativeSizeAxes = Axes.Both, + Waveform = new DummyWorkingBeatmap().Waveform, + }, + }; + }); + + AddUntilStep("wait for load", () => graph.ResampledWaveform != null); + } + + public class TestWaveformGraph : WaveformGraph + { + public new Waveform ResampledWaveform => base.ResampledWaveform; } } } From 7a385e56ecd0089689ce63d145eb5fbd44a8f5da Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 22 Apr 2019 09:58:29 +0300 Subject: [PATCH 0487/2854] Fix AppVeyor Errors --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index e563d3a848..99178409fb 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -95,15 +95,20 @@ namespace osu.Game.Rulesets.Scoring /// protected virtual bool DefaultFailCondition => Health.Value == Health.MinValue; - private bool adjustRank = false; + private bool adjustRank; /// /// Used by specific mods to adjust . /// public bool AdjustRank { - get { return adjustRank; } - set { adjustRank = value; Rank.Value = rankFrom(Accuracy.Value); } // Update rank immediately if AdjustRank was changed + get => return adjustRank; + + set + { + adjustRank = value; + Rank.Value = rankFrom(Accuracy.Value); // Update rank immediately if AdjustRank was changed + } } protected ScoreProcessor() From 3c252d79ead8fc1e7a13e8d12b3210bce989d402 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 22 Apr 2019 15:59:47 +0900 Subject: [PATCH 0488/2854] Use var, rework dim application logic --- .../Mods/OsuModFlashlight.cs | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index 5d136e4cbe..b2bc5f4320 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Bindables; @@ -27,9 +28,9 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableHitObjects(IEnumerable drawables) { - foreach (DrawableSlider drawable in drawables.OfType()) + foreach (var s in drawables.OfType()) { - drawable.Tracking.ValueChanged += flashlight.OnSliderTrackingChange; + s.Tracking.ValueChanged += flashlight.OnSliderTrackingChange; } } @@ -44,13 +45,28 @@ namespace osu.Game.Rulesets.Osu.Mods public void OnSliderTrackingChange(ValueChangedEvent e) { + // If any sliders are in a tracking state, apply a dim to the entire playfield over a brief duration. if (e.NewValue) + { trackingSliders++; + // This check being here ensures we're only applying a dim if and only if a slider begins tracking. + if (trackingSliders == 1) + { + this.TransformTo(nameof(FlashlightDim), 0.8f, 50); + } + } else + { trackingSliders--; - // If there are any sliders in a tracking state, apply a dim to the entire playfield over a brief duration. - this.TransformTo(nameof(FlashlightDim), trackingSliders > 0 ? 0.8f : 0.0f, 50); + if (trackingSliders == 0) + { + this.TransformTo(nameof(FlashlightDim), 0.0f, 50); + } + } + + if (trackingSliders < 0) + throw new InvalidOperationException($"The number of {nameof(trackingSliders)} cannot be below 0."); } protected override bool OnMouseMove(MouseMoveEvent e) From 64429aa96877c96ca67f53d6a95b0b98085fe425 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 22 Apr 2019 09:59:51 +0300 Subject: [PATCH 0489/2854] Fix AppVeyor Errors --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 99178409fb..fc7e8746b7 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Scoring public bool AdjustRank { get => return adjustRank; - + set { adjustRank = value; From 903093503041d6fd3f2061615cbc2cda56239b9d Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 22 Apr 2019 10:07:45 +0300 Subject: [PATCH 0490/2854] Fix AppVeyor Errors --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index fc7e8746b7..b3e1beed04 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -104,8 +104,8 @@ namespace osu.Game.Rulesets.Scoring { get => return adjustRank; - set - { + set + { adjustRank = value; Rank.Value = rankFrom(Accuracy.Value); // Update rank immediately if AdjustRank was changed } From cfb3c38c3a086dd1d892fe471ca1c80da1e1f710 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 22 Apr 2019 10:14:19 +0300 Subject: [PATCH 0491/2854] Fix. --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index b3e1beed04..b1cd78dde6 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Scoring /// public bool AdjustRank { - get => return adjustRank; + get => adjustRank; set { From 3fbbb7dcf9901fcd5595d5e1a88d22ce667aa94e Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 22 Apr 2019 16:39:28 +0900 Subject: [PATCH 0492/2854] Move intiial fade outside of absolute sequence --- osu.Game/Rulesets/Mods/ModFlashlight.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index c0816b2457..c3ac00f3ad 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -95,10 +95,10 @@ namespace osu.Game.Rulesets.Mods Combo.ValueChanged += OnComboChange; + this.FadeInFromZero(FLASHLIGHT_FADE_DURATION); + using (BeginAbsoluteSequence(0)) { - this.FadeInFromZero(FLASHLIGHT_FADE_DURATION); - foreach (var breakPeriod in Breaks) { if (breakPeriod.Duration < FLASHLIGHT_FADE_DURATION * 2) continue; From fbb4e9df044b2e6d3d911de3846cc6140dd7ef6f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Apr 2019 16:51:38 +0900 Subject: [PATCH 0493/2854] Implement hp at base ScoreProcessor --- osu.Game/Rulesets/Judgements/Judgement.cs | 5 +++++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index e14eedd3dc..f07f76a2b8 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -31,6 +31,11 @@ namespace osu.Game.Rulesets.Judgements ///

public int HighestComboAtJudgement { get; internal set; } + /// + /// The health prior to this occurring. + /// + public double HealthAtJudgement { get; internal set; } + /// /// Whether a miss or hit occurred. /// diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 1a682080bf..429aa620f6 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -206,9 +206,6 @@ namespace osu.Game.Rulesets.Scoring private double baseScore; private double bonusScore; - private double rollingHp; - private double rollingMaxHp; - protected ScoreProcessor() { } @@ -304,6 +301,7 @@ namespace osu.Game.Rulesets.Scoring { result.ComboAtJudgement = Combo.Value; result.HighestComboAtJudgement = HighestCombo.Value; + result.HealthAtJudgement = Health.Value; JudgedHits++; @@ -336,8 +334,7 @@ namespace osu.Game.Rulesets.Scoring rollingMaxBaseScore += result.Judgement.MaxNumericResult; } - rollingHp += HpFactorFor(result.Judgement, result.Type) * result.Judgement.HealthIncreaseFor(result); - rollingMaxHp += HpFactorFor(result.Judgement, result.Judgement.MaxResult) * result.Judgement.MaxHealthIncrease; + Health.Value += HpFactorFor(result) * result.Judgement.HealthIncreaseFor(result); } /// @@ -349,6 +346,7 @@ namespace osu.Game.Rulesets.Scoring { Combo.Value = result.ComboAtJudgement; HighestCombo.Value = result.HighestComboAtJudgement; + Health.Value = result.HealthAtJudgement; JudgedHits--; @@ -362,12 +360,9 @@ namespace osu.Game.Rulesets.Scoring baseScore -= result.Judgement.NumericResultFor(result); rollingMaxBaseScore -= result.Judgement.MaxNumericResult; } - - rollingHp -= HpFactorFor(result.Judgement, result.Type) * result.Judgement.HealthIncreaseFor(result); - rollingMaxHp -= HpFactorFor(result.Judgement, result.Judgement.MaxResult) * result.Judgement.MaxHealthIncrease; } - protected virtual double HpFactorFor(Judgement judgement, HitResult result) => 1; + protected virtual double HpFactorFor(JudgementResult result) => 1; private void updateScore() { From 910b9df2d54b93e28c6e729807337d9a14f161bf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Apr 2019 18:02:57 +0900 Subject: [PATCH 0502/2854] Fix catch awarding too much hp --- osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs | 2 +- osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs index 31f825c3ef..5835e746b9 100644 --- a/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs +++ b/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Judgements default: return 0; case HitResult.Perfect: - return 0.08; + return 0.008; } } diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs index f03897d611..679691fcd6 100644 --- a/osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs +++ b/osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.Judgements default: return base.HealthIncreaseFor(result); case HitResult.Perfect: - return 0.07; + return 0.007; } } } From aeae759fcd5b9949815b2bba02a799e9d6c277e8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Apr 2019 18:08:15 +0900 Subject: [PATCH 0503/2854] Rename method + add xmldoc --- osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs | 2 +- osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs | 2 +- osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs | 2 +- osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs | 2 +- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 9 +++++++-- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 778a7426aa..0d0ca6506c 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Scoring hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; } - protected override double HpFactorFor(JudgementResult result) + protected override double HealthAdjustmentFactorFor(JudgementResult result) { switch (result.Type) { diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index ee4618e5c2..5caf08fb1e 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Mania.Scoring } } - protected override double HpFactorFor(JudgementResult result) + protected override double HealthAdjustmentFactorFor(JudgementResult result) => result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier; public override HitWindows CreateHitWindows() => new ManiaHitWindows(); diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index 2162663539..cf0565c6da 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Scoring comboResultCounts[osuResult.ComboType] = comboResultCounts.GetOrDefault(osuResult.ComboType) + 1; } - protected override double HpFactorFor(JudgementResult result) + protected override double HealthAdjustmentFactorFor(JudgementResult result) { switch (result.Type) { diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index b2b866db4b..68ddf2db19 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120); } - protected override double HpFactorFor(JudgementResult result) + protected override double HealthAdjustmentFactorFor(JudgementResult result) => result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier; protected override void Reset(bool storeResults) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 429aa620f6..ba71e1e9b2 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -334,7 +334,7 @@ namespace osu.Game.Rulesets.Scoring rollingMaxBaseScore += result.Judgement.MaxNumericResult; } - Health.Value += HpFactorFor(result) * result.Judgement.HealthIncreaseFor(result); + Health.Value += HealthAdjustmentFactorFor(result) * result.Judgement.HealthIncreaseFor(result); } /// @@ -362,7 +362,12 @@ namespace osu.Game.Rulesets.Scoring } } - protected virtual double HpFactorFor(JudgementResult result) => 1; + /// + /// An adjustment factor which is multiplied into the health increase provided by a . + /// + /// The for which the adjustment should apply. + /// The adjustment factor. + protected virtual double HealthAdjustmentFactorFor(JudgementResult result) => 1; private void updateScore() { From 908eee9942653eabd72e68a3a60b93f7ec35cccd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Apr 2019 17:06:01 +0900 Subject: [PATCH 0504/2854] Fix pause tests --- osu.Game/Rulesets/UI/Playfield.cs | 5 +++++ osu.Game/Screens/Play/GameplayClockContainer.cs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 13689153f0..a99c16a610 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -68,7 +68,12 @@ namespace osu.Game.Rulesets.UI { Cursor = CreateCursor(); if (Cursor != null) + { + // initial showing of the cursor will be handed by MenuCursorContainer (via DrawableRuleset's IProvideCursor implementation). + Cursor.Hide(); + AddInternal(Cursor); + } } /// diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 29974b728e..3654dc679c 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -123,7 +123,7 @@ namespace osu.Game.Screens.Play { // Seeking the decoupled clock to its current time ensures that its source clock will be seeked to the same time // This accounts for the audio clock source potentially taking time to enter a completely stopped state - adjustableClock.Seek(adjustableClock.CurrentTime); + adjustableClock.Seek(GameplayClock.CurrentTime); adjustableClock.Start(); IsPaused.Value = false; } From 55c5ef898d2cba265b877bf1d4a5195a1b14c6bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Apr 2019 17:06:40 +0900 Subject: [PATCH 0505/2854] Remove delay on entering player --- osu.Game/Screens/Play/GameplayClockContainer.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 3654dc679c..2d1ba3f2ab 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -110,11 +110,8 @@ namespace osu.Game.Screens.Play adjustableClock.ChangeSource(sourceClock); updateRate(); - this.Delay(750).Schedule(() => - { - if (!IsPaused.Value) - Start(); - }); + if (!IsPaused.Value) + Start(); }); }); } From 6856571f17d06cd8080b19d967846347a54565af Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Apr 2019 18:45:58 +0900 Subject: [PATCH 0506/2854] Fix incorrect seek target --- osu.Game/Screens/Play/GameplayClockContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 2d1ba3f2ab..6b12430552 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -120,7 +120,7 @@ namespace osu.Game.Screens.Play { // Seeking the decoupled clock to its current time ensures that its source clock will be seeked to the same time // This accounts for the audio clock source potentially taking time to enter a completely stopped state - adjustableClock.Seek(GameplayClock.CurrentTime); + Seek(GameplayClock.CurrentTime); adjustableClock.Start(); IsPaused.Value = false; } From 48e82d4b1c742dff5bbe101ebf6ffefcfa044809 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Apr 2019 18:47:28 +0900 Subject: [PATCH 0507/2854] Fix hold for menu button's icon being incorrect --- osu.Game/Screens/Play/HUD/HoldForMenuButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs index 6883f246e4..c0ee5e6142 100644 --- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs +++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs @@ -129,7 +129,7 @@ namespace osu.Game.Screens.Play.HUD Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(15), - Icon = FontAwesome.Solid.TimesCircle + Icon = FontAwesome.Solid.Times }, } }; From 1f18c08cfc15a8184306ecce7915afff16c2061b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Apr 2019 19:17:58 +0900 Subject: [PATCH 0508/2854] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a45cac2cb6..303587bf88 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 70edc01821..e45a76dbac 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 414d55548439b4a1e5e16f1da7830bd5aedc3296 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Apr 2019 19:24:57 +0900 Subject: [PATCH 0509/2854] Fix possible exit from non-current screen --- osu.Game/Screens/Multi/Multiplayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 5e019a7b3a..1183396369 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -276,7 +276,7 @@ namespace osu.Game.Screens.Multi updatePollingRate(isIdle.Value); - if (screenStack.CurrentScreen == null) + if (screenStack.CurrentScreen == null && this.IsCurrentScreen()) this.Exit(); } From 52336e9062affa6742ad49c7802f31153364e04a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Apr 2019 11:52:42 +0900 Subject: [PATCH 0510/2854] Fix pause tests --- osu.Game/Tests/Visual/PlayerTestCase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/PlayerTestCase.cs b/osu.Game/Tests/Visual/PlayerTestCase.cs index d308b86b5a..fca4fccae0 100644 --- a/osu.Game/Tests/Visual/PlayerTestCase.cs +++ b/osu.Game/Tests/Visual/PlayerTestCase.cs @@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual public void SetUpSteps() { AddStep(ruleset.RulesetInfo.Name, loadPlayer); - AddUntilStep(() => Player.IsLoaded, "player loaded"); + AddUntilStep(() => Player.IsLoaded && Player.Alpha == 1, "player loaded"); } protected virtual IBeatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo); From 6add3952883f7d8b40f7e2cfd3ec0061cffa0f9b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 23 Apr 2019 13:32:44 +0900 Subject: [PATCH 0511/2854] Fix gameplay cursor being hidden in tests/replays --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 3 +++ osu.Game/Tests/Visual/ManualInputManagerTestCase.cs | 11 +++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 2866e81682..302380744a 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -225,6 +225,9 @@ namespace osu.Game.Rulesets.UI if (replayInputManager.ReplayInputHandler != null) replayInputManager.ReplayInputHandler.GamefieldToScreenSpace = Playfield.GamefieldToScreenSpace; + + if (!ProvidingUserCursor) + Playfield.Cursor?.Show(); } /// diff --git a/osu.Game/Tests/Visual/ManualInputManagerTestCase.cs b/osu.Game/Tests/Visual/ManualInputManagerTestCase.cs index f14ac833e4..9b1ccdd6a4 100644 --- a/osu.Game/Tests/Visual/ManualInputManagerTestCase.cs +++ b/osu.Game/Tests/Visual/ManualInputManagerTestCase.cs @@ -4,17 +4,24 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing.Input; +using osu.Game.Graphics.Cursor; namespace osu.Game.Tests.Visual { public abstract class ManualInputManagerTestCase : OsuTestCase { - protected override Container Content => InputManager; + protected override Container Content => content; + private readonly Container content; + protected readonly ManualInputManager InputManager; protected ManualInputManagerTestCase() { - base.Content.Add(InputManager = new ManualInputManager { UseParentInput = true }); + base.Content.Add(InputManager = new ManualInputManager + { + UseParentInput = true, + Child = content = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }, + }); } /// From 6c568d228fe7edb316492c4b4c32aae595c3b333 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 23 Apr 2019 13:45:51 +0900 Subject: [PATCH 0512/2854] Add comment --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 302380744a..01ae637158 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -227,7 +227,10 @@ namespace osu.Game.Rulesets.UI replayInputManager.ReplayInputHandler.GamefieldToScreenSpace = Playfield.GamefieldToScreenSpace; if (!ProvidingUserCursor) + { + // The cursor is hidden by default (see Playfield.load()), but should be shown when there's a replay Playfield.Cursor?.Show(); + } } /// From 9890884726181bcb8605a39c5cf3bb2c9be38c6b Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 23 Apr 2019 14:23:09 +0900 Subject: [PATCH 0513/2854] Remove fade in, update comment --- osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs | 3 ++- osu.Game/Rulesets/Mods/ModFlashlight.cs | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index b2bc5f4320..597c430e16 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -49,7 +49,8 @@ namespace osu.Game.Rulesets.Osu.Mods if (e.NewValue) { trackingSliders++; - // This check being here ensures we're only applying a dim if and only if a slider begins tracking. + // The fade should only be applied if tracking sliders is increasing from 0 to 1, and cannot be a result of a slider losing tracking. + // As a result, this logic must be exclusive to when e.NewValue is true. if (trackingSliders == 1) { this.TransformTo(nameof(FlashlightDim), 0.8f, 50); diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index c3ac00f3ad..54331508a0 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -95,8 +95,6 @@ namespace osu.Game.Rulesets.Mods Combo.ValueChanged += OnComboChange; - this.FadeInFromZero(FLASHLIGHT_FADE_DURATION); - using (BeginAbsoluteSequence(0)) { foreach (var breakPeriod in Breaks) From 0838206ddd3c54d2ec9f2d7cfc015da931a97a0c Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 22 Apr 2019 22:44:43 -0700 Subject: [PATCH 0514/2854] Shorten multiplayer header to multi --- osu.Game/Screens/Multi/Header.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Header.cs b/osu.Game/Screens/Multi/Header.cs index 7924086389..b9354cac7d 100644 --- a/osu.Game/Screens/Multi/Header.cs +++ b/osu.Game/Screens/Multi/Header.cs @@ -79,7 +79,7 @@ namespace osu.Game.Screens.Multi [BackgroundDependencyLoader] private void load(OsuColour colours) { - Title = "multiplayer"; + Title = "multi"; Icon = OsuIcon.Multi; AccentColour = colours.Yellow; } From 80e1568e974f0cfa5efd0e8a4b1c2298c7fffefc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Apr 2019 13:44:21 +0900 Subject: [PATCH 0515/2854] Fix FrameStabilityContainer performing frame-stable seeks --- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index c307520aca..2866ce08ed 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -68,6 +68,8 @@ namespace osu.Game.Rulesets.UI private const double sixty_frame_time = 1000.0 / 60; + private bool firstConsumption = true; + public override bool UpdateSubTree() { requireMoreUpdateLoops = true; @@ -103,7 +105,14 @@ namespace osu.Game.Rulesets.UI try { - if (Math.Abs(manualClock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f) + if (firstConsumption) + { + // On the first update, frame-stability seeking would result in unexpected/unwanted behaviour. + // Instead we perform an initial seek to the proposed time. + manualClock.CurrentTime = newProposedTime; + firstConsumption = false; + } + else if (Math.Abs(manualClock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f) { newProposedTime = newProposedTime > manualClock.CurrentTime ? Math.Min(newProposedTime, manualClock.CurrentTime + sixty_frame_time) From 60328cf1fb840344d3919ee80638ffe01b5010cc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Apr 2019 15:23:00 +0900 Subject: [PATCH 0516/2854] Ensure FrameStabilityContainer's ElapsedTime is zero on initial seek --- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index 2866ce08ed..ad15bcf057 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -110,6 +110,10 @@ namespace osu.Game.Rulesets.UI // On the first update, frame-stability seeking would result in unexpected/unwanted behaviour. // Instead we perform an initial seek to the proposed time. manualClock.CurrentTime = newProposedTime; + + // do a second process to clear out ElapsedTime + framedClock.ProcessFrame(); + firstConsumption = false; } else if (Math.Abs(manualClock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f) From a3e7ec0a14b85acec83e68963bb5d44e99a96300 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Apr 2019 15:23:31 +0900 Subject: [PATCH 0517/2854] Add tests for FrameStabilityContainer --- .../TestCaseFrameStabilityContainer.cs | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs b/osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs new file mode 100644 index 0000000000..ca0607cd6b --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs @@ -0,0 +1,149 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Timing; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestCaseFrameStabilityContainer : OsuTestCase + { + private ManualClock manualClock; + + private Container mainContainer; + + private ClockConsumingChild consumer; + + [SetUp] + public void SetUp() + { + Child = mainContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Clock = new FramedClock(manualClock = new ManualClock()), + }; + } + + [Test] + public void TestLargeJumps() + { + createStabilityContainer(); + seekManualTo(100000); + + confirmSeek(100000); + checkFrameCount(6000); + + seekManualTo(0); + + confirmSeek(0); + checkFrameCount(12000); + } + + [Test] + public void TestSmallJumps() + { + createStabilityContainer(); + seekManualTo(40); + + confirmSeek(40); + checkFrameCount(3); + + seekManualTo(0); + + confirmSeek(0); + checkFrameCount(6); + } + + [Test] + public void TestSingleFrameJump() + { + createStabilityContainer(); + seekManualTo(8); + confirmSeek(8); + checkFrameCount(1); + + seekManualTo(16); + confirmSeek(16); + checkFrameCount(2); + } + + [Test] + public void TestInitialSeek() + { + seekManualTo(100000); + + createStabilityContainer(); + + confirmSeek(100000); + checkFrameCount(0); + } + + private void createStabilityContainer() => AddStep("create container", () => mainContainer.Child = new FrameStabilityContainer().WithChild(consumer = new ClockConsumingChild())); + + private void seekManualTo(double time) => AddStep($"seek manual clock to {time}", () => manualClock.CurrentTime = time); + + private void confirmSeek(double time) => AddUntilStep($"wait for seek to {time}", () => consumer.Clock.CurrentTime == time); + + private void checkFrameCount(int frames) => + AddAssert($"elapsed frames is {frames}", () => consumer.ElapsedFrames == frames); + + public class ClockConsumingChild : CompositeDrawable + { + private readonly OsuSpriteText text; + private readonly OsuSpriteText text2; + private readonly OsuSpriteText text3; + + public ClockConsumingChild() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + InternalChildren = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + text2 = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + text3 = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + } + }, + }; + } + + public int ElapsedFrames; + + protected override void Update() + { + base.Update(); + + if (Clock.ElapsedFrameTime != 0) + ElapsedFrames++; + + text.Text = $"current time: {Clock.CurrentTime:F0}"; + if (Clock.ElapsedFrameTime != 0) + text2.Text = $"last elapsed frame time: {Clock.ElapsedFrameTime:F0}"; + text3.Text = $"total frames: {ElapsedFrames:F0}"; + } + } + } +} From e69963e60e2627f9682de1835ea1f3c2388b50c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Apr 2019 12:35:34 +0900 Subject: [PATCH 0518/2854] Ensure there is enough time before the first object in osu! (roughly following osu-stable specs) --- osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index ba7241c165..72adb4624d 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Input; @@ -61,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.UI get { var first = (OsuHitObject)Objects.First(); - return first.StartTime - first.TimePreempt; + return first.StartTime - Math.Max(2000, first.TimePreempt); } } } From efaedafc0870bdff673a37034b57a615a6e59d72 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Apr 2019 13:53:05 +0900 Subject: [PATCH 0519/2854] Fix (legacy) AudioLeadIn being used incorrectly. This lead-in is intended to specify a value before zero, not a value before the start time. Also removes an unnecessary ProcessFrame call (it happens within Seek itself). --- osu.Game/Screens/Play/GameplayClockContainer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 6b12430552..f1eed3f662 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -95,8 +95,7 @@ namespace osu.Game.Screens.Play UserPlaybackRate.ValueChanged += _ => updateRate(); - Seek(Math.Min(0, gameplayStartTime - beatmap.BeatmapInfo.AudioLeadIn)); - adjustableClock.ProcessFrame(); + Seek(Math.Min(-beatmap.BeatmapInfo.AudioLeadIn, gameplayStartTime)); } public void Restart() From bb69330e9f85e1e76140e225e497a2cd45a1311f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Apr 2019 14:01:46 +0900 Subject: [PATCH 0520/2854] Call ProcessFrame on the userOffsetClock after a seek Without doing this, GameplayClock can be left in an incorrect state after a seek (due to the userOffsetClock being its own FramedClock). --- osu.Game/Screens/Play/GameplayClockContainer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index f1eed3f662..c151e598f7 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -136,6 +136,9 @@ namespace osu.Game.Screens.Play // remove the offset component here because most of the time we want the seek to be aligned to gameplay, not the audio track. // we may want to consider reversing the application of offsets in the future as it may feel more correct. adjustableClock.Seek(time - totalOffset); + + // manually process frame to ensure GameplayClock is correctly updated after a seek. + userOffsetClock.ProcessFrame(); } public void Stop() From 3b36a4982deabef006ccd97909c3f2730abbe51e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Apr 2019 15:46:21 +0900 Subject: [PATCH 0521/2854] Fix tests running under nUnit and running multiple times in concession --- .../Gameplay/TestCaseFrameStabilityContainer.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs b/osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs index ca0607cd6b..bc30648566 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.UI; @@ -12,14 +13,13 @@ namespace osu.Game.Tests.Visual.Gameplay { public class TestCaseFrameStabilityContainer : OsuTestCase { - private ManualClock manualClock; + private readonly ManualClock manualClock; - private Container mainContainer; + private readonly Container mainContainer; private ClockConsumingChild consumer; - [SetUp] - public void SetUp() + public TestCaseFrameStabilityContainer() { Child = mainContainer = new Container { @@ -31,6 +31,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestLargeJumps() { + seekManualTo(0); createStabilityContainer(); seekManualTo(100000); @@ -46,6 +47,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestSmallJumps() { + seekManualTo(0); createStabilityContainer(); seekManualTo(40); @@ -61,6 +63,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestSingleFrameJump() { + seekManualTo(0); createStabilityContainer(); seekManualTo(8); confirmSeek(8); @@ -75,7 +78,6 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestInitialSeek() { seekManualTo(100000); - createStabilityContainer(); confirmSeek(100000); From f273f5daae7e6224ead6491e491b391fc261db07 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Apr 2019 15:55:51 +0900 Subject: [PATCH 0522/2854] Remove unnecessary using statement --- .../Visual/Gameplay/TestCaseFrameStabilityContainer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs b/osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs index bc30648566..5cd01fe9a8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs @@ -4,7 +4,6 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.UI; From 8222923ab8838cf754874df7edbe62fa37679291 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 24 Apr 2019 16:20:51 +0900 Subject: [PATCH 0523/2854] Only track logo if we're still the current screen --- osu.Game/Screens/Play/PlayerLoader.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 6a55fe278b..4403cae883 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -163,7 +163,11 @@ namespace osu.Game.Screens.Play logo.ScaleTo(new Vector2(0.15f), duration, Easing.In); logo.FadeIn(350); - Scheduler.AddDelayed(() => { content.StartTracking(logo, resuming ? 0 : 500, Easing.InOutExpo); }, resuming ? 0 : 500); + Scheduler.AddDelayed(() => + { + if (this.IsCurrentScreen()) + content.StartTracking(logo, resuming ? 0 : 500, Easing.InOutExpo); + }, resuming ? 0 : 500); } protected override void LogoExiting(OsuLogo logo) From 67382724f633e1f79e4e7f18204de3f49200acf5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Apr 2019 16:58:13 +0900 Subject: [PATCH 0524/2854] Reword and reoganise logic --- .../Mods/OsuModFlashlight.cs | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index 597c430e16..c2d6ec7934 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Mods private class OsuFlashlight : Flashlight, IRequireHighFrequencyMousePosition { - private int trackingSliders; + private int slidersCurrentlyTracking; public OsuFlashlight() { @@ -45,29 +45,22 @@ namespace osu.Game.Rulesets.Osu.Mods public void OnSliderTrackingChange(ValueChangedEvent e) { - // If any sliders are in a tracking state, apply a dim to the entire playfield over a brief duration. + // If any sliders are in a tracking state, a further dim should be applied to the (remaining) visible portion of the playfield over a brief duration. if (e.NewValue) { - trackingSliders++; - // The fade should only be applied if tracking sliders is increasing from 0 to 1, and cannot be a result of a slider losing tracking. - // As a result, this logic must be exclusive to when e.NewValue is true. - if (trackingSliders == 1) - { + // The transform should only be applied when the first slider begins tracking. + if (++slidersCurrentlyTracking == 1) this.TransformTo(nameof(FlashlightDim), 0.8f, 50); - } } else { - trackingSliders--; + if (slidersCurrentlyTracking == 0) + throw new InvalidOperationException($"The number of {nameof(slidersCurrentlyTracking)} cannot be below 0."); - if (trackingSliders == 0) - { + // The fade is restored after the last slider exits a tracked state. + if (--slidersCurrentlyTracking == 0) this.TransformTo(nameof(FlashlightDim), 0.0f, 50); - } } - - if (trackingSliders < 0) - throw new InvalidOperationException($"The number of {nameof(trackingSliders)} cannot be below 0."); } protected override bool OnMouseMove(MouseMoveEvent e) From 8f101e4f604bac362799fe8c171b8cdaee4aef28 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 24 Apr 2019 18:25:38 +0900 Subject: [PATCH 0525/2854] Remove unnecessary multi-slider tracking logic --- .../Mods/OsuModFlashlight.cs | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index c2d6ec7934..8e98a02392 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -36,8 +36,6 @@ namespace osu.Game.Rulesets.Osu.Mods private class OsuFlashlight : Flashlight, IRequireHighFrequencyMousePosition { - private int slidersCurrentlyTracking; - public OsuFlashlight() { FlashlightSize = new Vector2(0, getSizeFor(0)); @@ -45,22 +43,8 @@ namespace osu.Game.Rulesets.Osu.Mods public void OnSliderTrackingChange(ValueChangedEvent e) { - // If any sliders are in a tracking state, a further dim should be applied to the (remaining) visible portion of the playfield over a brief duration. - if (e.NewValue) - { - // The transform should only be applied when the first slider begins tracking. - if (++slidersCurrentlyTracking == 1) - this.TransformTo(nameof(FlashlightDim), 0.8f, 50); - } - else - { - if (slidersCurrentlyTracking == 0) - throw new InvalidOperationException($"The number of {nameof(slidersCurrentlyTracking)} cannot be below 0."); - - // The fade is restored after the last slider exits a tracked state. - if (--slidersCurrentlyTracking == 0) - this.TransformTo(nameof(FlashlightDim), 0.0f, 50); - } + // If a slider is in a tracking state, a further dim should be applied to the (remaining) visible portion of the playfield over a brief duration. + this.TransformTo(nameof(FlashlightDim), e.NewValue ? 0.8f : 0.0f, 50); } protected override bool OnMouseMove(MouseMoveEvent e) From 90d5c64cf36df3ab147383b3d8e03ae93e6c44cf Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 24 Apr 2019 18:25:55 +0900 Subject: [PATCH 0526/2854] Remove unused usings --- osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index 8e98a02392..c0332fbf60 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Bindables; From 8dc7fd8223235fa56c335840b79354c1e1d46be6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Apr 2019 12:16:12 +0900 Subject: [PATCH 0527/2854] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 303587bf88..22afce9c86 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index e45a76dbac..eaebcc3361 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 6fdcd98caadb0415e4bddd198a1e5e5804a56ea7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Apr 2019 14:15:07 +0900 Subject: [PATCH 0528/2854] Don't play screen "back" sample when retrying --- osu.Game/Screens/OsuScreen.cs | 5 ++++- osu.Game/Screens/Play/PlayerLoader.cs | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index c1a822c75c..9d53e43b80 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -59,6 +59,8 @@ namespace osu.Game.Screens private SampleChannel sampleExit; + protected virtual bool PlayResumeSound => true; + public virtual float BackgroundParallaxAmount => 1; public Bindable Beatmap { get; private set; } @@ -117,7 +119,8 @@ namespace osu.Game.Screens public override void OnResuming(IScreen last) { - sampleExit?.Play(); + if (PlayResumeSound) + sampleExit?.Play(); applyArrivingDefaults(true); base.OnResuming(last); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 6a55fe278b..d49ac0ed16 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -44,6 +44,8 @@ namespace osu.Game.Screens.Play public override bool DisallowExternalBeatmapRulesetChanges => true; + protected override bool PlayResumeSound => false; + private Task loadTask; private InputManager inputManager; From 0bd35ab7bbdb29705813c02474c16d2db35dd36f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 25 Apr 2019 17:36:17 +0900 Subject: [PATCH 0529/2854] Turn on warnings, resolve issues --- .../Visual/Gameplay/TestCaseSkinReloadable.cs | 12 ++-------- osu.Game/Audio/IPreviewTrackOwner.cs | 2 +- osu.Game/Beatmaps/BeatmapConverter.cs | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 3 +-- osu.Game/Beatmaps/BindableBeatmap.cs | 4 ++-- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 12 +++++++++- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 1 + osu.Game/Database/ArchiveModelManager.cs | 2 +- osu.Game/Database/DatabaseContextFactory.cs | 3 +-- osu.Game/Database/OsuDbContext.cs | 2 +- .../Graphics/Containers/LinkFlowContainer.cs | 2 +- .../Containers/LogoTrackingContainer.cs | 3 +-- osu.Game/Graphics/OsuFont.cs | 5 ++-- .../UserInterface/ScreenBreadcrumbControl.cs | 2 +- osu.Game/Online/API/APIAccess.cs | 2 +- .../Requests/Responses/APILegacyScoreInfo.cs | 2 -- .../Responses/APIUserMostPlayedBeatmap.cs | 8 +++---- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 2 -- osu.Game/OsuGame.cs | 1 - .../Chat/Tabs/ChannelSelectorTabItem.cs | 2 +- .../Chat/Tabs/PrivateChannelTabItem.cs | 4 ---- osu.Game/Overlays/HoldToConfirmOverlay.cs | 2 +- osu.Game/Overlays/MusicController.cs | 1 - .../Settings/RulesetSettingsSubsection.cs | 3 ++- .../Overlays/Settings/SettingsEnumDropdown.cs | 2 +- .../Difficulty/DifficultyCalculator.cs | 2 +- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 4 +++- osu.Game/Rulesets/Edit/SelectionBlueprint.cs | 6 ++++- .../Mods/IApplicableToBeatmapConverter.cs | 2 -- .../Rulesets/Mods/IApplicableToHitObject.cs | 2 +- .../Objects/Drawables/DrawableHitObject.cs | 5 ++-- osu.Game/Rulesets/Objects/HitObject.cs | 4 +--- osu.Game/Rulesets/Objects/HitWindows.cs | 2 +- osu.Game/Rulesets/Objects/SliderPath.cs | 7 ------ osu.Game/Rulesets/Ruleset.cs | 1 + osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 3 --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 4 +++- .../Rulesets/UI/GameplayCursorContainer.cs | 2 +- osu.Game/Rulesets/UI/Playfield.cs | 1 - .../Scrolling/Algorithms/IScrollAlgorithm.cs | 2 +- .../UI/Scrolling/DrawableScrollingRuleset.cs | 2 +- osu.Game/Screens/BackgroundScreenStack.cs | 2 +- .../Compose/Components/SelectionHandler.cs | 3 +-- .../Timeline/ZoomableScrollContainer.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 3 --- .../LabelledComponents/LabelledTextBox.cs | 7 ------ .../Multi/Components/DisableableTabControl.cs | 6 ++--- .../Lounge/Components/ParticipantInfo.cs | 24 ------------------- .../Multi/Lounge/Components/RoomInspector.cs | 2 +- .../Multi/Match/Components/GameTypePicker.cs | 2 +- .../Screens/Multi/Match/Components/Info.cs | 3 +-- .../Match/Components/MatchSettingsOverlay.cs | 4 ++-- .../Components/RoomAvailabilityPicker.cs | 2 +- .../Screens/Multi/MultiplayerComposite.cs | 4 ++-- .../Screens/Multi/MultiplayerSubScreen.cs | 22 +---------------- osu.Game/Screens/Multi/RoomManager.cs | 2 +- osu.Game/Screens/Play/GameplayClock.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 1 - .../Screens/Select/Carousel/CarouselItem.cs | 2 -- osu.Game/Screens/Select/SongSelect.cs | 2 -- osu.Game/Skinning/ISkin.cs | 24 +++++++++++++++++++ osu.Game/Skinning/ISkinSource.cs | 13 +--------- .../Skinning/LocalSkinOverrideContainer.cs | 14 +++++------ osu.Game/Skinning/Skin.cs | 4 +--- osu.Game/Tests/Visual/AllPlayersTestCase.cs | 4 ++-- osu.Game/Tests/Visual/EditorClockTestCase.cs | 2 +- osu.Game/Tests/Visual/OsuTestCase.cs | 2 +- osu.Game/Tests/Visual/PlayerTestCase.cs | 2 +- osu.Game/osu.Game.csproj | 1 - 71 files changed, 116 insertions(+), 182 deletions(-) create mode 100644 osu.Game/Skinning/ISkin.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestCaseSkinReloadable.cs b/osu.Game.Tests/Visual/Gameplay/TestCaseSkinReloadable.cs index a9fbf35d37..56ab70b400 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCaseSkinReloadable.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCaseSkinReloadable.cs @@ -120,12 +120,8 @@ namespace osu.Game.Tests.Visual.Gameplay } } - private class SecondarySource : ISkinSource + private class SecondarySource : ISkin { - public event Action SourceChanged; - - public void TriggerSourceChanged() => SourceChanged?.Invoke(); - public Drawable GetDrawableComponent(string componentName) => new SecondarySourceBox(); public Texture GetTexture(string componentName) => throw new NotImplementedException(); @@ -135,12 +131,8 @@ namespace osu.Game.Tests.Visual.Gameplay public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); } - private class SkinSourceContainer : Container, ISkinSource + private class SkinSourceContainer : Container, ISkin { - public event Action SourceChanged; - - public void TriggerSourceChanged() => SourceChanged?.Invoke(); - public Drawable GetDrawableComponent(string componentName) => new BaseSourceBox(); public Texture GetTexture(string componentName) => throw new NotImplementedException(); diff --git a/osu.Game/Audio/IPreviewTrackOwner.cs b/osu.Game/Audio/IPreviewTrackOwner.cs index fdcae65e3c..8ab93257a5 100644 --- a/osu.Game/Audio/IPreviewTrackOwner.cs +++ b/osu.Game/Audio/IPreviewTrackOwner.cs @@ -4,7 +4,7 @@ namespace osu.Game.Audio { /// - /// Interface for objects that can own s. + /// Interface for objects that can own s. /// /// /// s can cancel the currently playing through the diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index b6fa6674f6..7922843626 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -98,7 +98,7 @@ namespace osu.Game.Beatmaps protected abstract IEnumerable ValidConversionTypes { get; } /// - /// Creates the that will be returned by this . + /// Creates the that will be returned by this . /// protected virtual Beatmap CreateBeatmap() => new Beatmap(); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 9caa64ec96..a36a8ea7dd 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -217,7 +217,7 @@ namespace osu.Game.Beatmaps { request.Perform(api); } - catch (Exception e) + catch { // no need to handle here as exceptions will filter down to request.Failure above. } @@ -382,7 +382,6 @@ namespace osu.Game.Beatmaps /// Query the API to populate missing values like OnlineBeatmapID / OnlineBeatmapSetID or (Rank-)Status. /// /// The beatmap to populate. - /// The other beatmaps contained within this set. /// Whether to re-query if the provided beatmap already has populated values. /// True if population was successful. private bool fetchAndPopulateOnlineValues(BeatmapInfo beatmap, bool force = false) diff --git a/osu.Game/Beatmaps/BindableBeatmap.cs b/osu.Game/Beatmaps/BindableBeatmap.cs index 657dc06297..27bad65062 100644 --- a/osu.Game/Beatmaps/BindableBeatmap.cs +++ b/osu.Game/Beatmaps/BindableBeatmap.cs @@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps { /// /// A for the beatmap. - /// This should be used sparingly in-favour of . + /// This should be used sparingly in-favour of . /// public abstract class BindableBeatmap : NonNullableBindable { @@ -67,6 +67,6 @@ namespace osu.Game.Beatmaps /// If you are further binding to events of the retrieved , ensure a local reference is held. /// [NotNull] - public abstract BindableBeatmap GetBoundCopy(); + public new abstract BindableBeatmap GetBoundCopy(); } } diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 58463d2219..7d25ca3ede 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using osu.Framework.Audio.Track; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics.Textures; using osu.Game.Rulesets; using osu.Game.Rulesets.Difficulty; @@ -73,9 +74,18 @@ namespace osu.Game.Beatmaps private class DummyBeatmapConverter : IBeatmapConverter { public event Action> ObjectConverted; + public IBeatmap Beatmap { get; set; } + public bool CanConvert => true; - public IBeatmap Convert() => Beatmap; + + public IBeatmap Convert() + { + foreach (var obj in Beatmap.HitObjects) + ObjectConverted?.Invoke(obj, obj.Yield()); + + return Beatmap; + } } } } diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 040f582e3b..31cfe076cd 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -95,7 +95,7 @@ namespace osu.Game.Beatmaps.Formats { colour = new Color4(byte.Parse(split[0]), byte.Parse(split[1]), byte.Parse(split[2]), split.Length == 4 ? byte.Parse(split[3]) : (byte)255); } - catch (Exception e) + catch { throw new InvalidOperationException(@"Color must be specified with 8-bit integer components"); } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 8989785dcd..4b0720d867 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -73,6 +73,7 @@ namespace osu.Game.Beatmaps /// /// /// The to create a playable for. + /// The s to apply to the . /// The converted . /// If could not be converted to . public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 3805921ac2..41f8c64853 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -563,7 +563,7 @@ namespace osu.Game.Database /// /// Check whether an existing model already exists for a new import item. /// - /// The new model proposed for import. + /// The new model proposed for import. /// An existing model which matches the criteria to skip importing, else null. protected TModel CheckForExisting(TModel model) => model.Hash == null ? null : ModelStore.ConsumableItems.FirstOrDefault(b => b.Hash == model.Hash); diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs index f6250732d9..554337c477 100644 --- a/osu.Game/Database/DatabaseContextFactory.cs +++ b/osu.Game/Database/DatabaseContextFactory.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Linq; using System.Threading; using Microsoft.EntityFrameworkCore.Storage; @@ -67,7 +66,7 @@ namespace osu.Game.Database context = threadContexts.Value; } } - catch (Exception e) + catch { // retrieval of a context could trigger a fatal error. Monitor.Exit(writeLock); diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 17efe2c839..f4cd23f5a5 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -70,7 +70,7 @@ namespace osu.Game.Database cmd.ExecuteNonQuery(); } } - catch (Exception e) + catch { connection.Close(); throw; diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index eefbeea24c..222336d663 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -103,7 +103,7 @@ namespace osu.Game.Graphics.Containers { channelManager?.OpenChannel(linkArgument); } - catch (ChannelNotFoundException e) + catch (ChannelNotFoundException) { Logger.Log($"The requested channel \"{linkArgument}\" does not exist"); } diff --git a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs index fb23038dde..23015e8bf5 100644 --- a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs +++ b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs @@ -30,7 +30,6 @@ namespace osu.Game.Graphics.Containers /// Assign the logo that should track the facade's position, as well as how it should transform to its initial position. /// /// The instance of the logo to be used for tracking. - /// The scale of the facade. Does not actually affect the logo itself. /// The duration of the initial transform. Default is instant. /// The easing type of the initial transform. public void StartTracking(OsuLogo logo, double duration = 0, Easing easing = Easing.None) @@ -132,7 +131,7 @@ namespace osu.Game.Graphics.Containers private class InternalFacade : Facade { - public void SetSize(Vector2 size) + public new void SetSize(Vector2 size) { base.SetSize(size); } diff --git a/osu.Game/Graphics/OsuFont.cs b/osu.Game/Graphics/OsuFont.cs index c8a736f49a..5324b269ee 100644 --- a/osu.Game/Graphics/OsuFont.cs +++ b/osu.Game/Graphics/OsuFont.cs @@ -61,9 +61,9 @@ namespace osu.Game.Graphics /// /// Retrieves the string representation of a . /// - /// The . + /// The family string. /// The . - /// The string representation of in the specified . + /// The string representation of in the specified . public static string GetWeightString(string family, FontWeight weight) { string weightString = weight.ToString(); @@ -81,6 +81,7 @@ namespace osu.Game.Graphics /// /// Creates a new by applying adjustments to this . /// + /// The base . /// The font typeface. If null, the value is copied from this . /// The text size. If null, the value is copied from this . /// The font weight. If null, the value is copied from this . diff --git a/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs index f564a4b5a8..3e0a6c3265 100644 --- a/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs +++ b/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs @@ -8,7 +8,7 @@ using osu.Framework.Screens; namespace osu.Game.Graphics.UserInterface { /// - /// A which follows the active screen (and allows navigation) in a stack. + /// A which follows the active screen (and allows navigation) in a stack. /// public class ScreenBreadcrumbControl : BreadcrumbControl { diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index c5f6ef41c2..d5a496dc17 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -253,7 +253,7 @@ namespace osu.Game.Online.API handleWebException(we); return false; } - catch (Exception e) + catch { return false; } diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs index 8ee71ce9ac..ca3a77a140 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs @@ -7,7 +7,6 @@ using System.Linq; using Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Rulesets; -using osu.Game.Rulesets.Scoring; using osu.Game.Scoring.Legacy; using osu.Game.Users; @@ -71,7 +70,6 @@ namespace osu.Game.Online.API.Requests.Responses { foreach (var kvp in value) { - HitResult newKey; switch (kvp.Key) { case @"count_geki": diff --git a/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs index 8177f99abe..4614fe29b7 100644 --- a/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs @@ -10,16 +10,16 @@ namespace osu.Game.Online.API.Requests.Responses public class APIUserMostPlayedBeatmap { [JsonProperty("beatmap_id")] - public int BeatmapID; + public int BeatmapID { get; set; } [JsonProperty("count")] - public int PlayCount; + public int PlayCount { get; set; } [JsonProperty] - private BeatmapInfo beatmap; + private BeatmapInfo beatmap { get; set; } [JsonProperty] - private APIBeatmapSet beatmapSet; + private APIBeatmapSet beatmapSet { get; set; } public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets) { diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index 438bf231c4..ae4a056033 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -27,8 +27,6 @@ namespace osu.Game.Online.Chat protected ChannelManager ChannelManager; - private ScrollContainer scroll; - private DrawableChannel drawableChannel; private readonly bool postingTextbox; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 30f98aa1ce..4ce056195c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -272,7 +272,6 @@ namespace osu.Game /// Present a score's replay immediately. /// The user should have already requested this interactively. /// - /// The beatmap to select. public void PresentScore(ScoreInfo score) { var databasedScore = ScoreManager.GetScore(score); diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs index 52260506fe..c26ecfd86f 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.Chat.Tabs } [BackgroundDependencyLoader] - private new void load(OsuColour colour) + private void load(OsuColour colour) { BackgroundInactive = colour.Gray2; BackgroundActive = colour.Gray3; diff --git a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs index 8aa6d6fecd..b8165e70cb 100644 --- a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; using osu.Game.Users; using osuTK; @@ -18,9 +17,6 @@ namespace osu.Game.Overlays.Chat.Tabs { public class PrivateChannelTabItem : ChannelTabItem { - private readonly OsuSpriteText username; - private readonly Avatar avatarContainer; - protected override IconUsage DisplayIcon => FontAwesome.Solid.At; public PrivateChannelTabItem(Channel value) diff --git a/osu.Game/Overlays/HoldToConfirmOverlay.cs b/osu.Game/Overlays/HoldToConfirmOverlay.cs index 154aff605a..fb38ddcbd1 100644 --- a/osu.Game/Overlays/HoldToConfirmOverlay.cs +++ b/osu.Game/Overlays/HoldToConfirmOverlay.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays { /// /// An overlay which will display a black screen that dims over a period before confirming an exit action. - /// Action is BYO (derived class will need to call and from a user event). + /// Action is BYO (derived class will need to call and from a user event). /// public abstract class HoldToConfirmOverlay : HoldToConfirmContainer { diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index c250d3b62a..9046a196da 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -50,7 +50,6 @@ namespace osu.Game.Overlays private BeatmapManager beatmaps; private List beatmapSets; - private BeatmapSetInfo currentSet; private Container dragContainer; private Container playerContainer; diff --git a/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs b/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs index a16e852902..93b07fbac7 100644 --- a/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs +++ b/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Configuration; @@ -9,7 +10,7 @@ namespace osu.Game.Overlays.Settings { /// /// A which provides subclasses with the - /// from the 's . + /// from the 's . /// public abstract class RulesetSettingsSubsection : SettingsSubsection { diff --git a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs index 11cdbf6e0a..9f09f251c2 100644 --- a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs +++ b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs @@ -10,7 +10,7 @@ namespace osu.Game.Overlays.Settings { protected override OsuDropdown CreateDropdown() => new DropdownControl(); - protected class DropdownControl : OsuEnumDropdown + protected new class DropdownControl : OsuEnumDropdown { public DropdownControl() { diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 14f7665e05..5eabe1e936 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -165,7 +165,7 @@ namespace osu.Game.Rulesets.Difficulty /// /// Creates the s to calculate the difficulty of an . /// - /// The whose difficulty will be calculated.The whose difficulty will be calculated. /// The s. protected abstract Skill[] CreateSkills(IBeatmap beatmap); } diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index 74aa9ace2d..f12591cef4 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit.Compose; using osuTK; @@ -108,7 +109,8 @@ namespace osu.Game.Rulesets.Edit } /// - /// Invokes , refreshing and parameters for the . + /// Invokes , + /// refreshing and parameters for the . /// protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.Value.Beatmap.ControlPointInfo, beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty); diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs index 85b6c91a07..01992cbbd3 100644 --- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs @@ -68,9 +68,11 @@ namespace osu.Game.Rulesets.Edit get => state; set { - if (state == value) return; + if (state == value) + return; state = value; + switch (state) { case SelectionState.Selected: @@ -82,6 +84,8 @@ namespace osu.Game.Rulesets.Edit Deselected?.Invoke(this); break; } + + StateChanged?.Invoke(state); } } diff --git a/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs b/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs index eb80fa131a..8cefb02904 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs @@ -2,14 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mods { /// /// Interface for a that applies changes to a . /// - /// The type of converted . public interface IApplicableToBeatmapConverter : IApplicableMod { /// diff --git a/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs b/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs index c13b62812b..f7f81c92c0 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods public interface IApplicableToHitObject : IApplicableMod { /// - /// Applies this to a . + /// Applies this to a . /// /// The to apply to. void ApplyToHitObject(HitObject hitObject); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index a7cfbd3300..2e983b8fe1 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; +using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osu.Game.Audio; using osu.Game.Graphics; @@ -58,7 +59,7 @@ namespace osu.Game.Rulesets.Objects.Drawables public bool AllJudged => Judged && NestedHitObjects.All(h => h.AllJudged); /// - /// Whether this has been hit. This occurs if is . + /// Whether this has been hit. This occurs if is hit. /// Note: This does NOT include nested hitobjects. /// public bool IsHit => Result?.IsHit ?? false; @@ -223,7 +224,7 @@ namespace osu.Game.Rulesets.Objects.Drawables } /// - /// Will called at least once after the of this has been passed. + /// Will called at least once after the of this has been passed. /// internal void OnLifetimeEnd() { diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index fd542be67d..cede2e50d0 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -53,8 +53,6 @@ namespace osu.Game.Rulesets.Objects [JsonIgnore] public bool Kiai { get; private set; } - private float overallDifficulty = BeatmapDifficulty.DEFAULT_DIFFICULTY; - /// /// The hit windows for this . /// @@ -115,7 +113,7 @@ namespace osu.Game.Rulesets.Objects /// Creates the for this . /// This can be null to indicate that the has no . /// - /// This will only be invoked if hasn't been set externally (e.g. from a . + /// This will only be invoked if hasn't been set externally (e.g. from a . /// /// protected virtual HitWindows CreateHitWindows() => new HitWindows(); diff --git a/osu.Game/Rulesets/Objects/HitWindows.cs b/osu.Game/Rulesets/Objects/HitWindows.cs index c5b7686da6..589c72957b 100644 --- a/osu.Game/Rulesets/Objects/HitWindows.cs +++ b/osu.Game/Rulesets/Objects/HitWindows.cs @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Objects /// /// Given a time offset, whether the can ever be hit in the future with a non- result. - /// This happens if is less than what is required for a result. + /// This happens if is less than what is required for . /// /// The time offset. /// Whether the can be hit at any point in the future from this time offset. diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 1e9767a54f..e312b004ba 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -277,12 +277,5 @@ namespace osu.Game.Rulesets.Objects return ControlPoints.SequenceEqual(other.ControlPoints) && ExpectedDistance.Equals(other.ExpectedDistance) && Type == other.Type; } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - - return obj is SliderPath other && Equals(other); - } } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 3521c17b23..42b1322cae 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -53,6 +53,7 @@ namespace osu.Game.Rulesets /// Attempt to create a hit renderer for a beatmap /// /// The beatmap to create the hit renderer for. + /// The s to apply. /// Unable to successfully load the beatmap to be usable with this ruleset. /// public abstract DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods); diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index ba71e1e9b2..a2937ff959 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -154,7 +154,6 @@ namespace osu.Game.Rulesets.Scoring /// /// Notifies subscribers of that a new judgement has occurred. /// - /// The judgement to notify subscribers of. /// The judgement scoring result to notify subscribers of. protected void NotifyNewJudgement(JudgementResult result) { @@ -283,7 +282,6 @@ namespace osu.Game.Rulesets.Scoring /// /// Reverts the score change of a that was applied to this . /// - /// The judgement to remove. /// The judgement scoring result. private void revertResult(JudgementResult result) { @@ -340,7 +338,6 @@ namespace osu.Game.Rulesets.Scoring /// /// Reverts the score change of a that was applied to this . /// - /// The judgement to remove. /// The judgement scoring result. protected virtual void RevertResult(JudgementResult result) { diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 01ae637158..df9effb321 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -93,6 +93,7 @@ namespace osu.Game.Rulesets.UI ///
/// The ruleset being represented. /// The beatmap to create the hit renderer for. + /// The s to apply. protected DrawableRuleset(Ruleset ruleset, WorkingBeatmap workingBeatmap, IReadOnlyList mods) : base(ruleset) { @@ -275,7 +276,8 @@ namespace osu.Game.Rulesets.UI /// /// Applies the active mods to this DrawableRuleset. /// - /// + /// The s to apply. + /// The to apply. private void applyRulesetMods(IReadOnlyList mods, OsuConfigManager config) { if (mods == null) diff --git a/osu.Game/Rulesets/UI/GameplayCursorContainer.cs b/osu.Game/Rulesets/UI/GameplayCursorContainer.cs index de73c08809..41edfa0b68 100644 --- a/osu.Game/Rulesets/UI/GameplayCursorContainer.cs +++ b/osu.Game/Rulesets/UI/GameplayCursorContainer.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.UI public class GameplayCursorContainer : CursorContainer { /// - /// Because Show/Hide are executed by a parent, is updated immediately even if the cursor + /// Because Show/Hide are executed by a parent, is updated immediately even if the cursor /// is in a non-updating state (via limitations). /// /// This holds the true visibility value. diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index a99c16a610..a073ad246b 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -100,7 +100,6 @@ namespace osu.Game.Rulesets.UI /// /// Provide an optional cursor which is to be used for gameplay. - /// If providing a cursor, must also point to a valid target container. /// /// The cursor, or null if a cursor is not rqeuired. protected virtual GameplayCursorContainer CreateCursor() => null; diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs index a104b0629f..b7a5eedc22 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms /// The current time. /// The amount of visible time. /// The absolute spatial length through . - /// The time at which == . + /// The time at which == . double TimeAt(float position, double currentTime, double timeRange, float scrollLength); /// diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index dbe8d8c299..f21d0b4a66 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.UI.Scrolling protected virtual ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Sequential; /// - /// Whether the player can change . + /// Whether the player can change . /// protected virtual bool UserScrollSpeedAdjustment => true; diff --git a/osu.Game/Screens/BackgroundScreenStack.cs b/osu.Game/Screens/BackgroundScreenStack.cs index 5f82329496..9c0c5da0fb 100644 --- a/osu.Game/Screens/BackgroundScreenStack.cs +++ b/osu.Game/Screens/BackgroundScreenStack.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens //public float ParallaxAmount { set => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * value; } - public new void Push(BackgroundScreen screen) + public void Push(BackgroundScreen screen) { if (screen == null) return; diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index bcb2bee601..11e649168f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -121,6 +121,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Handle a blueprint requesting selection. /// /// The blueprint. + /// The input state at the point of selection. internal void HandleSelectionRequested(SelectionBlueprint blueprint, InputState state) { if (state.Keyboard.ControlPressed) @@ -166,8 +167,6 @@ namespace osu.Game.Screens.Edit.Compose.Components var topLeft = new Vector2(float.MaxValue, float.MaxValue); var bottomRight = new Vector2(float.MinValue, float.MinValue); - bool hasSelection = false; - foreach (var blueprint in selectedBlueprints) { topLeft = Vector2.ComponentMin(topLeft, ToLocalSpace(blueprint.SelectionQuad.TopLeft)); diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs index f41b3cddc0..9b00a3998d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs @@ -131,7 +131,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private readonly float scrollOffset; /// - /// Transforms to a new value. + /// Transforms to a new value. /// /// The focus point in absolute coordinates local to the content. /// The size of the content. diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 0ba1e74aca..09977454f0 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -65,9 +65,6 @@ namespace osu.Game.Screens.Edit dependencies.Cache(beatDivisor); EditorMenuBar menuBar; - TimeInfoContainer timeInfo; - SummaryTimeline timeline; - PlaybackControl playback; var fileMenuItems = new List(); if (RuntimeInfo.IsDesktop) diff --git a/osu.Game/Screens/Edit/Setup/Components/LabelledComponents/LabelledTextBox.cs b/osu.Game/Screens/Edit/Setup/Components/LabelledComponents/LabelledTextBox.cs index 50d524d1f5..1c53fc7088 100644 --- a/osu.Game/Screens/Edit/Setup/Components/LabelledComponents/LabelledTextBox.cs +++ b/osu.Game/Screens/Edit/Setup/Components/LabelledComponents/LabelledTextBox.cs @@ -60,14 +60,7 @@ namespace osu.Game.Screens.Edit.Setup.Components.LabelledComponents set => label.Colour = value; } - public Color4 BackgroundColour - { - get => content.Colour; - set => content.Colour = value; - } - private readonly OsuTextBox textBox; - private readonly Container content; private readonly OsuSpriteText label; public LabelledTextBox() diff --git a/osu.Game/Screens/Multi/Components/DisableableTabControl.cs b/osu.Game/Screens/Multi/Components/DisableableTabControl.cs index b6b0332cf3..27b5aec4d3 100644 --- a/osu.Game/Screens/Multi/Components/DisableableTabControl.cs +++ b/osu.Game/Screens/Multi/Components/DisableableTabControl.cs @@ -13,15 +13,13 @@ namespace osu.Game.Screens.Multi.Components protected override void AddTabItem(TabItem tab, bool addToDropdown = true) { - if (tab is DisableableTabItem disableable) + if (tab is DisableableTabItem disableable) disableable.Enabled.BindTo(Enabled); base.AddTabItem(tab, addToDropdown); } - protected abstract class DisableableTabItem : TabItem + protected abstract class DisableableTabItem : TabItem { - public readonly BindableBool Enabled = new BindableBool(); - protected DisableableTabItem(T value) : base(value) { diff --git a/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs b/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs index 51d3c93624..6570051040 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs @@ -25,8 +25,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components private void load(OsuColour colours) { OsuSpriteText summary; - OsuSpriteText levelRangeHigher; - OsuSpriteText levelRangeLower; Container flagContainer; LinkFlowContainer hostText; @@ -45,21 +43,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components Width = 22f, RelativeSizeAxes = Axes.Y, }, - /*new Container //todo: team banners - { - Width = 38f, - RelativeSizeAxes = Axes.Y, - CornerRadius = 2f, - Masking = true, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex(@"ad387e"), - }, - }, - },*/ hostText = new LinkFlowContainer { Anchor = Anchor.CentreLeft, @@ -101,13 +84,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components }, true); ParticipantCount.BindValueChanged(count => summary.Text = "participant".ToQuantity(count.NewValue), true); - - /*Participants.BindValueChanged(e => - { - var ranks = v.Select(u => u.Statistics.Ranks.Global); - levelRangeLower.Text = ranks.Min().ToString(); - levelRangeHigher.Text = ranks.Max().ToString(); - });*/ } } } diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs index 5798fce457..1297090a32 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs @@ -97,7 +97,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Font = OsuFont.GetFont(size: 30), - Current = Name + Current = RoomName }, }, }, diff --git a/osu.Game/Screens/Multi/Match/Components/GameTypePicker.cs b/osu.Game/Screens/Multi/Match/Components/GameTypePicker.cs index ccb957734f..b69cb9705d 100644 --- a/osu.Game/Screens/Multi/Match/Components/GameTypePicker.cs +++ b/osu.Game/Screens/Multi/Match/Components/GameTypePicker.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Multi.Match.Components AddItem(new GameTypeTimeshift()); } - private class GameTypePickerItem : DisableableTabItem + private class GameTypePickerItem : DisableableTabItem { private const float transition_duration = 200; diff --git a/osu.Game/Screens/Multi/Match/Components/Info.cs b/osu.Game/Screens/Multi/Match/Components/Info.cs index a944d965bd..a185c4db50 100644 --- a/osu.Game/Screens/Multi/Match/Components/Info.cs +++ b/osu.Game/Screens/Multi/Match/Components/Info.cs @@ -30,7 +30,6 @@ namespace osu.Game.Screens.Multi.Match.Components ReadyButton readyButton; ViewBeatmapButton viewBeatmapButton; HostInfo hostInfo; - RoomStatusInfo statusInfo; InternalChildren = new Drawable[] { @@ -63,7 +62,7 @@ namespace osu.Game.Screens.Multi.Match.Components new OsuSpriteText { Font = OsuFont.GetFont(size: 30), - Current = Name + Current = RoomName }, new RoomStatusInfo(), } diff --git a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs index 586a986111..359b5824c0 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs @@ -265,7 +265,7 @@ namespace osu.Game.Screens.Multi.Match.Components }; TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue?.Name ?? string.Empty, true); - Name.BindValueChanged(name => NameField.Text = name.NewValue, true); + RoomName.BindValueChanged(name => NameField.Text = name.NewValue, true); Availability.BindValueChanged(availability => AvailabilityPicker.Current.Value = availability.NewValue, true); Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true); MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); @@ -285,7 +285,7 @@ namespace osu.Game.Screens.Multi.Match.Components { hideError(); - Name.Value = NameField.Text; + RoomName.Value = NameField.Text; Availability.Value = AvailabilityPicker.Current.Value; Type.Value = TypePicker.Current.Value; diff --git a/osu.Game/Screens/Multi/Match/Components/RoomAvailabilityPicker.cs b/osu.Game/Screens/Multi/Match/Components/RoomAvailabilityPicker.cs index 8751e27552..9de4a61cde 100644 --- a/osu.Game/Screens/Multi/Match/Components/RoomAvailabilityPicker.cs +++ b/osu.Game/Screens/Multi/Match/Components/RoomAvailabilityPicker.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Multi.Match.Components AddItem(RoomAvailability.InviteOnly); } - private class RoomAvailabilityPickerItem : DisableableTabItem + private class RoomAvailabilityPickerItem : DisableableTabItem { private const float transition_duration = 200; diff --git a/osu.Game/Screens/Multi/MultiplayerComposite.cs b/osu.Game/Screens/Multi/MultiplayerComposite.cs index da6bba7865..8c09d576ff 100644 --- a/osu.Game/Screens/Multi/MultiplayerComposite.cs +++ b/osu.Game/Screens/Multi/MultiplayerComposite.cs @@ -16,8 +16,8 @@ namespace osu.Game.Screens.Multi [Resolved(typeof(Room))] protected Bindable RoomID { get; private set; } - [Resolved(typeof(Room))] - protected Bindable Name { get; private set; } + [Resolved(typeof(Room), nameof(Room.Name))] + protected Bindable RoomName { get; private set; } [Resolved(typeof(Room))] protected Bindable Host { get; private set; } diff --git a/osu.Game/Screens/Multi/MultiplayerSubScreen.cs b/osu.Game/Screens/Multi/MultiplayerSubScreen.cs index 65e501b114..ff94f63f01 100644 --- a/osu.Game/Screens/Multi/MultiplayerSubScreen.cs +++ b/osu.Game/Screens/Multi/MultiplayerSubScreen.cs @@ -3,22 +3,17 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Input.Bindings; using osu.Framework.Screens; using osu.Game.Graphics.Containers; -using osu.Game.Input.Bindings; namespace osu.Game.Screens.Multi { - public abstract class MultiplayerSubScreen : OsuScreen, IMultiplayerSubScreen, IKeyBindingHandler + public abstract class MultiplayerSubScreen : OsuScreen, IMultiplayerSubScreen { public override bool DisallowExternalBeatmapRulesetChanges => false; public virtual string ShortTitle => Title; - [Resolved(CanBeNull = true)] - protected OsuGame Game { get; private set; } - [Resolved(CanBeNull = true)] protected IRoomManager RoomManager { get; private set; } @@ -56,21 +51,6 @@ namespace osu.Game.Screens.Multi this.MoveToX(-200, WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); } - public override bool OnPressed(GlobalAction action) - { - if (!this.IsCurrentScreen()) return false; - - if (action == GlobalAction.Back) - { - this.Exit(); - return true; - } - - return false; - } - - public bool OnReleased(GlobalAction action) => action == GlobalAction.Back; - public override string ToString() => Title; } } diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index 385cbe20e5..6f473aaafa 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -171,7 +171,7 @@ namespace osu.Game.Screens.Multi /// /// Adds a to the list of available rooms. /// - /// The to add.< + /// The to add. private void addRoom(Room room) { var existing = rooms.FirstOrDefault(e => e.RoomID.Value == room.RoomID.Value); diff --git a/osu.Game/Screens/Play/GameplayClock.cs b/osu.Game/Screens/Play/GameplayClock.cs index 3efcfa0f65..b1948d02d5 100644 --- a/osu.Game/Screens/Play/GameplayClock.cs +++ b/osu.Game/Screens/Play/GameplayClock.cs @@ -8,7 +8,7 @@ namespace osu.Game.Screens.Play { /// /// A clock which is used for gameplay elements that need to follow audio time 1:1. - /// Exposed via DI by . + /// Exposed via DI by . /// /// The main purpose of this clock is to stop components using it from accidentally processing the main /// , as this should only be done once to ensure accuracy. diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index f833aa2bb7..5bf54877fc 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -72,7 +72,7 @@ namespace osu.Game.Screens.Play [Cached] [Cached(Type = typeof(IBindable>))] - protected readonly Bindable> Mods = new Bindable>(Array.Empty()); + protected new readonly Bindable> Mods = new Bindable>(Array.Empty()); private readonly bool allowPause; private readonly bool showResults; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 6a55fe278b..ebc6459abe 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -320,7 +320,6 @@ namespace osu.Game.Screens.Play private readonly Drawable facade; private LoadingAnimation loading; private Sprite backgroundSprite; - private ModDisplay modDisplay; public bool Loading { diff --git a/osu.Game/Screens/Select/Carousel/CarouselItem.cs b/osu.Game/Screens/Select/Carousel/CarouselItem.cs index a0f5969b3c..79c1a4cb6b 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselItem.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselItem.cs @@ -31,8 +31,6 @@ namespace osu.Game.Screens.Select.Carousel } } - private int creationOrder; - protected CarouselItem() { DrawableRepresentation = new Lazy(CreateDrawableRepresentation); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a78238c584..14c362b8ca 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -388,8 +388,6 @@ namespace osu.Game.Screens.Select { Logger.Log($"updating selection with beatmap:{beatmap?.ID.ToString() ?? "null"} ruleset:{ruleset?.ID.ToString() ?? "null"}"); - bool preview = false; - if (ruleset?.Equals(decoupledRuleset.Value) == false) { Logger.Log($"ruleset changed from \"{decoupledRuleset.Value}\" to \"{ruleset}\""); diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs new file mode 100644 index 0000000000..0e67a1897c --- /dev/null +++ b/osu.Game/Skinning/ISkin.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; + +namespace osu.Game.Skinning +{ + /// + /// Provides access to skinnable elements. + /// + public interface ISkin + { + Drawable GetDrawableComponent(string componentName); + + Texture GetTexture(string componentName); + + SampleChannel GetSample(string sampleName); + + TValue GetValue(Func query) where TConfiguration : SkinConfiguration; + } +} diff --git a/osu.Game/Skinning/ISkinSource.cs b/osu.Game/Skinning/ISkinSource.cs index 6d2b9e6fe2..337d2a87a4 100644 --- a/osu.Game/Skinning/ISkinSource.cs +++ b/osu.Game/Skinning/ISkinSource.cs @@ -2,25 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Framework.Audio.Sample; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Textures; namespace osu.Game.Skinning { /// /// Provides access to skinnable elements. /// - public interface ISkinSource + public interface ISkinSource : ISkin { event Action SourceChanged; - - Drawable GetDrawableComponent(string componentName); - - Texture GetTexture(string componentName); - - SampleChannel GetSample(string sampleName); - - TValue GetValue(Func query) where TConfiguration : SkinConfiguration; } } diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs index 955ef7b65b..f1ed14595e 100644 --- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs +++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs @@ -22,18 +22,18 @@ namespace osu.Game.Skinning private readonly Bindable beatmapSkins = new Bindable(); private readonly Bindable beatmapHitsounds = new Bindable(); - private readonly ISkinSource source; + private readonly ISkin skin; private ISkinSource fallbackSource; - public LocalSkinOverrideContainer(ISkinSource source) + public LocalSkinOverrideContainer(ISkin skin) { - this.source = source; + this.skin = skin; } public Drawable GetDrawableComponent(string componentName) { Drawable sourceDrawable; - if (beatmapSkins.Value && (sourceDrawable = source.GetDrawableComponent(componentName)) != null) + if (beatmapSkins.Value && (sourceDrawable = skin.GetDrawableComponent(componentName)) != null) return sourceDrawable; return fallbackSource?.GetDrawableComponent(componentName); @@ -42,7 +42,7 @@ namespace osu.Game.Skinning public Texture GetTexture(string componentName) { Texture sourceTexture; - if (beatmapSkins.Value && (sourceTexture = source.GetTexture(componentName)) != null) + if (beatmapSkins.Value && (sourceTexture = skin.GetTexture(componentName)) != null) return sourceTexture; return fallbackSource.GetTexture(componentName); @@ -51,7 +51,7 @@ namespace osu.Game.Skinning public SampleChannel GetSample(string sampleName) { SampleChannel sourceChannel; - if (beatmapHitsounds.Value && (sourceChannel = source.GetSample(sampleName)) != null) + if (beatmapHitsounds.Value && (sourceChannel = skin.GetSample(sampleName)) != null) return sourceChannel; return fallbackSource?.GetSample(sampleName); @@ -60,7 +60,7 @@ namespace osu.Game.Skinning public TValue GetValue(Func query) where TConfiguration : SkinConfiguration { TValue val; - if ((source as Skin)?.Configuration is TConfiguration conf) + if ((skin as Skin)?.Configuration is TConfiguration conf) if (beatmapSkins.Value && (val = query.Invoke(conf)) != null) return val; diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 1d14f9cd6a..09c0d3d0bc 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -8,14 +8,12 @@ using osu.Framework.Graphics.Textures; namespace osu.Game.Skinning { - public abstract class Skin : IDisposable, ISkinSource + public abstract class Skin : IDisposable, ISkin { public readonly SkinInfo SkinInfo; public virtual SkinConfiguration Configuration { get; protected set; } - public event Action SourceChanged; - public abstract Drawable GetDrawableComponent(string componentName); public abstract SampleChannel GetSample(string sampleName); diff --git a/osu.Game/Tests/Visual/AllPlayersTestCase.cs b/osu.Game/Tests/Visual/AllPlayersTestCase.cs index 3e1f408a16..6e78851e31 100644 --- a/osu.Game/Tests/Visual/AllPlayersTestCase.cs +++ b/osu.Game/Tests/Visual/AllPlayersTestCase.cs @@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual { Player p = null; AddStep(r.Name, () => p = loadPlayerFor(r)); - AddUntilStep(() => + AddUntilStep("player loaded", () => { if (p?.IsLoaded == true) { @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual } return false; - }, "player loaded"); + }); AddCheckSteps(); } diff --git a/osu.Game/Tests/Visual/EditorClockTestCase.cs b/osu.Game/Tests/Visual/EditorClockTestCase.cs index 7f36a0e142..c71c2ae857 100644 --- a/osu.Game/Tests/Visual/EditorClockTestCase.cs +++ b/osu.Game/Tests/Visual/EditorClockTestCase.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual public abstract class EditorClockTestCase : OsuTestCase { protected readonly BindableBeatDivisor BeatDivisor = new BindableBeatDivisor(); - protected readonly EditorClock Clock; + protected new readonly EditorClock Clock; protected EditorClockTestCase() { diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index c08a6a4bcb..1f475209a4 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual [Cached(Type = typeof(IBindable>))] protected readonly Bindable> Mods = new Bindable>(Array.Empty()); - protected DependencyContainer Dependencies { get; private set; } + protected new DependencyContainer Dependencies { get; private set; } private readonly Lazy localStorage; protected Storage LocalStorage => localStorage.Value; diff --git a/osu.Game/Tests/Visual/PlayerTestCase.cs b/osu.Game/Tests/Visual/PlayerTestCase.cs index fca4fccae0..b9c7933cfb 100644 --- a/osu.Game/Tests/Visual/PlayerTestCase.cs +++ b/osu.Game/Tests/Visual/PlayerTestCase.cs @@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual public void SetUpSteps() { AddStep(ruleset.RulesetInfo.Name, loadPlayer); - AddUntilStep(() => Player.IsLoaded && Player.Alpha == 1, "player loaded"); + AddUntilStep("player loaded", () => Player.IsLoaded && Player.Alpha == 1); } protected virtual IBeatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 22afce9c86..e25921c486 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -5,7 +5,6 @@ Library AnyCPU true - 0 From 1766ed8f9e2d28de824a20432c79e050d012dc34 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 25 Apr 2019 18:05:46 +0900 Subject: [PATCH 0530/2854] Fix warnings/remove obsolete usages --- .../Profile/Header/BottomHeaderContainer.cs | 8 ++++---- .../Profile/Header/DetailHeaderContainer.cs | 3 +-- osu.Game/Overlays/Profile/Header/RankGraph.cs | 12 ++++-------- .../Overlays/Profile/Header/TopHeaderContainer.cs | 15 +++++---------- osu.Game/Overlays/Profile/ProfileHeader.cs | 4 ---- 5 files changed, 14 insertions(+), 28 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index 04b70ea10f..9eb6feec8e 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -48,12 +48,12 @@ namespace osu.Game.Overlays.Profile.Header Spacing = new Vector2(0, 10), Children = new Drawable[] { - bottomTopLinkContainer = new LinkFlowContainer(text => text.TextSize = 12) + bottomTopLinkContainer = new LinkFlowContainer(text => text.Font = text.Font.With(size: 12)) { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, }, - bottomLinkContainer = new LinkFlowContainer(text => text.TextSize = 12) + bottomLinkContainer = new LinkFlowContainer(text => text.Font = text.Font.With(size: 12)) { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -68,7 +68,7 @@ namespace osu.Game.Overlays.Profile.Header private void updateDisplay(User user) { - void bold(SpriteText t) => t.Font = @"Exo2.0-Bold"; + void bold(SpriteText t) => t.Font = t.Font.With(weight: FontWeight.Bold); void addSpacer(OsuTextFlowContainer textFlow) => textFlow.AddArbitraryDrawable(new Container { Width = 15 }); bottomTopLinkContainer.Clear(); @@ -113,7 +113,7 @@ namespace osu.Game.Overlays.Profile.Header bottomLinkContainer.AddIcon(icon, text => { - text.TextSize = 10; + text.Font = text.Font.With(size: 10); text.Colour = communityUserGrayGreenLighter; }); if (link != null) diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 19894f0301..84611b3bf1 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -213,8 +213,7 @@ namespace osu.Game.Overlays.Profile.Header }, rankCount = new OsuSpriteText { - Font = "Exo2.0-Bold", - TextSize = 12, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre } diff --git a/osu.Game/Overlays/Profile/Header/RankGraph.cs b/osu.Game/Overlays/Profile/Header/RankGraph.cs index 01f16fe942..d66f2306a0 100644 --- a/osu.Game/Overlays/Profile/Header/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/RankGraph.cs @@ -43,8 +43,7 @@ namespace osu.Game.Overlays.Profile.Header Anchor = Anchor.Centre, Origin = Anchor.Centre, Text = "No recent plays", - TextSize = 12, - Font = @"Exo2.0-Regular", + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular) }, graph = new RankChartLineGraph { @@ -227,14 +226,12 @@ namespace osu.Game.Overlays.Profile.Header { new OsuSpriteText { - Font = "Exo2.0-Bold", - TextSize = 12, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), Text = "Global Ranking " }, globalRankingText = new OsuSpriteText { - Font = "Exo2.0-Regular", - TextSize = 12, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular), Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, } @@ -242,8 +239,7 @@ namespace osu.Game.Overlays.Profile.Header }, timeText = new OsuSpriteText { - TextSize = 12, - Font = "Exo2.0-Regular" + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular), } } } diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 8e4d72c702..80721af42f 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -75,8 +75,7 @@ namespace osu.Game.Overlays.Profile.Header { usernameText = new OsuSpriteText { - Font = "Exo2.0-Regular", - TextSize = 24 + Font = OsuFont.GetFont(size: 24, weight: FontWeight.Regular) }, openUserExternally = new ExternalLinkButton { @@ -96,8 +95,7 @@ namespace osu.Game.Overlays.Profile.Header { titleText = new OsuSpriteText { - TextSize = 18, - Font = "Exo2.0-Regular" + Font = OsuFont.GetFont(size: 18, weight: FontWeight.Regular) }, supporterTag = new SupporterIcon { @@ -123,8 +121,7 @@ namespace osu.Game.Overlays.Profile.Header }, userCountryText = new OsuSpriteText { - Font = "Exo2.0-Regular", - TextSize = 17.5f, + Font = OsuFont.GetFont(size: 17.5f, weight: FontWeight.Regular), Margin = new MarginPadding { Left = 40 }, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, @@ -185,17 +182,15 @@ namespace osu.Game.Overlays.Profile.Header { new OsuSpriteText { - TextSize = 15, + Font = OsuFont.GetFont(size: 15), Text = left, - Font = "Exo2.0-Medium" }, new OsuSpriteText { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - TextSize = 15, + Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold), Text = right, - Font = "Exo2.0-Bold" }, }; } diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 776fcbb8b7..6238d1bc53 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -12,7 +12,6 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Profile @@ -29,9 +28,6 @@ namespace osu.Game.Overlays.Profile { CenterHeaderContainer centerHeaderContainer; DetailHeaderContainer detailHeaderContainer; - Container expandedDetailContainer; - FillFlowContainer hiddenDetailContainer, headerDetailContainer; - SpriteIcon expandButtonIcon; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; From b33c0e9a9337763df37951623d29705d132b5f5a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 25 Apr 2019 18:42:19 +0900 Subject: [PATCH 0531/2854] Cleanup bottom header container --- .../Online/TestCaseUserProfileHeader.cs | 1 + .../Profile/Header/BottomHeaderContainer.cs | 108 +++++++++--------- 2 files changed, 55 insertions(+), 54 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs index 98bad9831f..bc2ff708c5 100644 --- a/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs @@ -21,6 +21,7 @@ namespace osu.Game.Tests.Visual.Online typeof(RankGraph), typeof(LineGraph), typeof(ProfileHeaderTabControl), + typeof(BottomHeaderContainer) }; [Resolved] diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index 9eb6feec8e..39dd1bd028 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -20,18 +20,21 @@ namespace osu.Game.Overlays.Profile.Header { public class BottomHeaderContainer : CompositeDrawable { - private LinkFlowContainer bottomTopLinkContainer; - private LinkFlowContainer bottomLinkContainer; - private Color4 linkBlue, communityUserGrayGreenLighter; - public readonly Bindable User = new Bindable(); + private LinkFlowContainer topLinkContainer; + private LinkFlowContainer bottomLinkContainer; + + private Color4 iconColour; + + public BottomHeaderContainer() + { + AutoSizeAxes = Axes.Y; + } + [BackgroundDependencyLoader] private void load(OsuColour colours) { - AutoSizeAxes = Axes.Y; - User.ValueChanged += e => updateDisplay(e.NewValue); - InternalChildren = new Drawable[] { new Box @@ -48,7 +51,7 @@ namespace osu.Game.Overlays.Profile.Header Spacing = new Vector2(0, 10), Children = new Drawable[] { - bottomTopLinkContainer = new LinkFlowContainer(text => text.Font = text.Font.With(size: 12)) + topLinkContainer = new LinkFlowContainer(text => text.Font = text.Font.With(size: 12)) { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -62,80 +65,55 @@ namespace osu.Game.Overlays.Profile.Header } }; - linkBlue = colours.BlueLight; - communityUserGrayGreenLighter = colours.CommunityUserGrayGreenLighter; + iconColour = colours.CommunityUserGrayGreenLighter; + + User.BindValueChanged(user => updateDisplay(user.NewValue)); } private void updateDisplay(User user) { - void bold(SpriteText t) => t.Font = t.Font.With(weight: FontWeight.Bold); - void addSpacer(OsuTextFlowContainer textFlow) => textFlow.AddArbitraryDrawable(new Container { Width = 15 }); - - bottomTopLinkContainer.Clear(); + topLinkContainer.Clear(); bottomLinkContainer.Clear(); if (user == null) return; if (user.JoinDate.ToUniversalTime().Year < 2008) - { - bottomTopLinkContainer.AddText("Here since the beginning"); - } + topLinkContainer.AddText("Here since the beginning"); else { - bottomTopLinkContainer.AddText("Joined "); - bottomTopLinkContainer.AddText(new DrawableDate(user.JoinDate), bold); + topLinkContainer.AddText("Joined "); + topLinkContainer.AddText(new DrawableDate(user.JoinDate), embolden); } - addSpacer(bottomTopLinkContainer); + addSpacer(topLinkContainer); if (user.PlayStyles?.Length > 0) { - bottomTopLinkContainer.AddText("Plays with "); - bottomTopLinkContainer.AddText(string.Join(", ", user.PlayStyles.Select(style => style.GetDescription())), bold); + topLinkContainer.AddText("Plays with "); + topLinkContainer.AddText(string.Join(", ", user.PlayStyles.Select(style => style.GetDescription())), embolden); - addSpacer(bottomTopLinkContainer); + addSpacer(topLinkContainer); } if (user.LastVisit.HasValue) { - bottomTopLinkContainer.AddText("Last seen "); - bottomTopLinkContainer.AddText(new DrawableDate(user.LastVisit.Value), bold); + topLinkContainer.AddText("Last seen "); + topLinkContainer.AddText(new DrawableDate(user.LastVisit.Value), embolden); - addSpacer(bottomTopLinkContainer); + addSpacer(topLinkContainer); } - bottomTopLinkContainer.AddText("Contributed "); - bottomTopLinkContainer.AddLink($@"{user.PostCount:#,##0} forum posts", $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: bold); - - void tryAddInfo(IconUsage icon, string content, string link = null) - { - if (string.IsNullOrEmpty(content)) return; - - bottomLinkContainer.AddIcon(icon, text => - { - text.Font = text.Font.With(size: 10); - text.Colour = communityUserGrayGreenLighter; - }); - if (link != null) - { - bottomLinkContainer.AddLink(" " + content, link, creationParameters: text => - { - bold(text); - text.Colour = linkBlue; - }); - } - else - bottomLinkContainer.AddText(" " + content, bold); - - addSpacer(bottomLinkContainer); - } + topLinkContainer.AddText("Contributed "); + topLinkContainer.AddLink($@"{user.PostCount:#,##0} forum posts", $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: embolden); string websiteWithoutProtcol = user.Website; if (!string.IsNullOrEmpty(websiteWithoutProtcol)) { - int protocolIndex = websiteWithoutProtcol.IndexOf("//", StringComparison.Ordinal); - if (protocolIndex >= 0) - websiteWithoutProtcol = websiteWithoutProtcol.Substring(protocolIndex + 2); + if (Uri.TryCreate(websiteWithoutProtcol, UriKind.Absolute, out var uri)) + { + websiteWithoutProtcol = uri.Host + uri.PathAndQuery + uri.Fragment; + websiteWithoutProtcol = websiteWithoutProtcol.TrimEnd('/'); + } } tryAddInfo(FontAwesome.Solid.MapMarker, user.Location); @@ -149,5 +127,27 @@ namespace osu.Game.Overlays.Profile.Header tryAddInfo(FontAwesome.Brands.Lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}"); tryAddInfo(FontAwesome.Solid.Link, websiteWithoutProtcol, user.Website); } + + private void addSpacer(OsuTextFlowContainer textFlow) => textFlow.AddArbitraryDrawable(new Container { Width = 15 }); + + private void tryAddInfo(IconUsage icon, string content, string link = null) + { + if (string.IsNullOrEmpty(content)) return; + + bottomLinkContainer.AddIcon(icon, text => + { + text.Font = text.Font.With(size: 10); + text.Colour = iconColour; + }); + + if (link != null) + bottomLinkContainer.AddLink(" " + content, link, creationParameters: embolden); + else + bottomLinkContainer.AddText(" " + content, embolden); + + addSpacer(bottomLinkContainer); + } + + private void embolden(SpriteText text) => text.Font = text.Font.With(weight: FontWeight.Bold); } } From 0eca9b9683e25c81f6d509d9bd8f749967ca56ec Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 25 Apr 2019 18:43:29 +0900 Subject: [PATCH 0532/2854] Center -> centre --- osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs | 1 + .../{CenterHeaderContainer.cs => CentreHeaderContainer.cs} | 2 +- osu.Game/Overlays/Profile/ProfileHeader.cs | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) rename osu.Game/Overlays/Profile/Header/{CenterHeaderContainer.cs => CentreHeaderContainer.cs} (99%) diff --git a/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs index bc2ff708c5..531f30de83 100644 --- a/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs @@ -21,6 +21,7 @@ namespace osu.Game.Tests.Visual.Online typeof(RankGraph), typeof(LineGraph), typeof(ProfileHeaderTabControl), + typeof(CentreHeaderContainer), typeof(BottomHeaderContainer) }; diff --git a/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs similarity index 99% rename from osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs rename to osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index 3d0b9b9db4..0ad343bb7e 100644 --- a/osu.Game/Overlays/Profile/Header/CenterHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -18,7 +18,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays.Profile.Header { - public class CenterHeaderContainer : CompositeDrawable + public class CentreHeaderContainer : CompositeDrawable { public Action DetailsVisibilityAction; private bool detailsVisible; diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 6238d1bc53..3e257e19bf 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.Profile public ProfileHeader() { - CenterHeaderContainer centerHeaderContainer; + CentreHeaderContainer centreHeaderContainer; DetailHeaderContainer detailHeaderContainer; RelativeSizeAxes = Axes.X; @@ -91,7 +91,7 @@ namespace osu.Game.Overlays.Profile RelativeSizeAxes = Axes.X, User = { BindTarget = User }, }, - centerHeaderContainer = new CenterHeaderContainer + centreHeaderContainer = new CentreHeaderContainer { RelativeSizeAxes = Axes.X, User = { BindTarget = User }, @@ -118,7 +118,7 @@ namespace osu.Game.Overlays.Profile infoTabControl.AddItem("Info"); infoTabControl.AddItem("Modding"); - centerHeaderContainer.DetailsVisibilityAction = visible => detailHeaderContainer.Alpha = visible ? 0 : 1; + centreHeaderContainer.DetailsVisibilityAction = visible => detailHeaderContainer.Alpha = visible ? 0 : 1; User.ValueChanged += e => updateDisplay(e.NewValue); } From 9d5b81165e4432b8e8299b43f8fd2c63ba8eab2a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 25 Apr 2019 19:51:05 +0900 Subject: [PATCH 0533/2854] Adjust button stylings --- .../Visual/Online/TestCaseUserProfileHeader.cs | 3 ++- .../Profile/Header/CentreHeaderContainer.cs | 15 +++++++++++++-- .../Profile/Header/ProfileHeaderButton.cs | 7 ++++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs index 531f30de83..e425c25787 100644 --- a/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs @@ -22,7 +22,8 @@ namespace osu.Game.Tests.Visual.Online typeof(LineGraph), typeof(ProfileHeaderTabControl), typeof(CentreHeaderContainer), - typeof(BottomHeaderContainer) + typeof(BottomHeaderContainer), + typeof(ProfileHeaderButton) }; [Resolved] diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index 0ad343bb7e..658dd79570 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -104,7 +105,7 @@ namespace osu.Game.Overlays.Profile.Header RelativeSizeAxes = Axes.Y, Padding = new MarginPadding { Vertical = 10 }, Width = UserProfileOverlay.CONTENT_X_MARGIN, - Child = detailsToggleButton = new ProfileHeaderButton + Child = detailsToggleButton = new ExpandButton { RelativeSizeAxes = Axes.Y, Anchor = Anchor.Centre, @@ -115,7 +116,7 @@ namespace osu.Game.Overlays.Profile.Header { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(20), + Size = new Vector2(20, 12), Icon = FontAwesome.Solid.ChevronUp, }, } @@ -230,5 +231,15 @@ namespace osu.Game.Overlays.Profile.Header hiddenDetailGlobal.Content = user.Statistics?.Ranks.Global?.ToString("#,##0") ?? "-"; hiddenDetailCountry.Content = user.Statistics?.Ranks.Country?.ToString("#,##0") ?? "-"; } + + private class ExpandButton : ProfileHeaderButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + IdleColour = colours.CommunityUserGrayGreen; + HoverColour = colours.CommunityUserGrayGreen.Darken(0.2f); + } + } } } diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs b/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs index 6d9ab7a4d8..e8c8788a10 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs +++ b/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs @@ -2,10 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osuTK.Graphics; @@ -22,8 +22,9 @@ namespace osu.Game.Overlays.Profile.Header public ProfileHeaderButton() { - HoverColour = Color4.Black.Opacity(0.75f); - IdleColour = Color4.Black.Opacity(0.7f); + IdleColour = Color4.Black; + HoverColour = OsuColour.Gray(0.1f); + AutoSizeAxes = Axes.X; base.Content.Add(new CircularContainer From c6b3197dd0081aec6db80a2b1c7f00cb13124866 Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Thu, 25 Apr 2019 13:56:57 +0300 Subject: [PATCH 0534/2854] Add AdjustRank and use it in Hidden Mod --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 3 ++ .../Visual/Gameplay/TestCasePlayerLoader.cs | 3 ++ .../Mods/IApplicableToScoreProcessor.cs | 3 ++ osu.Game/Rulesets/Mods/ModFlashlight.cs | 3 ++ osu.Game/Rulesets/Mods/ModHidden.cs | 18 +++++++++- osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 3 ++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 34 ++++++++----------- osu.Game/Screens/Play/Player.cs | 2 ++ 8 files changed, 48 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index f3c7939a94..445f81c6d4 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osuTK; using osuTK.Graphics; @@ -41,6 +42,8 @@ namespace osu.Game.Rulesets.Osu.Mods scoreProcessor.Health.ValueChanged += health => { blinds.AnimateClosedness((float)health.NewValue); }; } + public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; + /// /// Element for the Blinds mod drawing 2 black boxes covering the whole screen which resize inside a restricted area with some leniency. /// diff --git a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs index f58c0d35b3..425d8fcda1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; using osu.Game.Screens; using osu.Game.Screens.Play; @@ -110,6 +111,8 @@ namespace osu.Game.Tests.Visual.Gameplay { Applied = true; } + + public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; } private class TestPlayer : Player diff --git a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs index 1d0ed94ef4..34e8d858f6 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; namespace osu.Game.Rulesets.Mods { @@ -11,5 +12,7 @@ namespace osu.Game.Rulesets.Mods public interface IApplicableToScoreProcessor : IApplicableMod { void ApplyToScoreProcessor(ScoreProcessor scoreProcessor); + + ScoreRank AdjustRank(ScoreRank rank, double accuracy); } } diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 0ad99d13ff..f47306d16e 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -16,6 +16,7 @@ using osu.Game.Graphics; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osuTK; using osuTK.Graphics; @@ -46,6 +47,8 @@ namespace osu.Game.Rulesets.Mods Combo.BindTo(scoreProcessor.Combo); } + public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; + public virtual void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { var flashlight = CreateFlashlight(); diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index 389edb5a35..c55c45c9fa 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -9,6 +9,7 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; namespace osu.Game.Rulesets.Mods { @@ -35,7 +36,22 @@ namespace osu.Game.Rulesets.Mods public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { - scoreProcessor.AdjustRank = true; + // Default value of ScoreProcessor's Rank in Hidden Mod should bes SS+ + scoreProcessor.Rank.Value = ScoreRank.XH; + } + + // TODO: Other mods that uses AdjustRank might have some issues due to rank changes + public ScoreRank AdjustRank(ScoreRank rank, double accuracy) + { + switch (rank) + { + case ScoreRank.X: + return ScoreRank.XH; + case ScoreRank.S: + return ScoreRank.SH; + default: + return rank; + } } protected virtual void ApplyHiddenState(DrawableHitObject hitObject, ArmedState state) diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index 6a82050d26..809661db8e 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; namespace osu.Game.Rulesets.Mods { @@ -24,6 +25,8 @@ namespace osu.Game.Rulesets.Mods scoreProcessor.FailConditions += FailCondition; } + public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; + protected virtual bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Combo.Value == 0; } } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index b1cd78dde6..9f31cb8d7c 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -7,9 +7,11 @@ using System.Diagnostics; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Extensions; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.TypeExtensions; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; using osu.Game.Scoring; @@ -60,6 +62,11 @@ namespace osu.Game.Rulesets.Scoring /// public readonly BindableInt Combo = new BindableInt(); + /// + /// The current selected mods + /// + public readonly Bindable> Mods = new Bindable>(Array.Empty()); + /// /// Create a for this processor. /// @@ -95,34 +102,22 @@ namespace osu.Game.Rulesets.Scoring /// protected virtual bool DefaultFailCondition => Health.Value == Health.MinValue; - private bool adjustRank; - - /// - /// Used by specific mods to adjust . - /// - public bool AdjustRank - { - get => adjustRank; - - set - { - adjustRank = value; - Rank.Value = rankFrom(Accuracy.Value); // Update rank immediately if AdjustRank was changed - } - } - protected ScoreProcessor() { Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); }; - Accuracy.ValueChanged += delegate { Rank.Value = rankFrom(Accuracy.Value); }; + Accuracy.ValueChanged += delegate + { + foreach (var mod in Mods.Value.OfType()) + Rank.Value = mod.AdjustRank(rankFrom(Accuracy.Value), Accuracy.Value); + }; } private ScoreRank rankFrom(double acc) { if (acc == 1) - return (adjustRank ? ScoreRank.XH : ScoreRank.X); + return ScoreRank.X; if (acc > 0.95) - return (adjustRank ? ScoreRank.SH : ScoreRank.S); + return ScoreRank.S; if (acc > 0.9) return ScoreRank.A; if (acc > 0.8) @@ -146,7 +141,6 @@ namespace osu.Game.Rulesets.Scoring Rank.Value = ScoreRank.X; HighestCombo.Value = 0; - AdjustRank = false; HasFailed = false; } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index f833aa2bb7..5e2880e24b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -106,6 +106,8 @@ namespace osu.Game.Screens.Play showStoryboard = config.GetBindable(OsuSetting.ShowStoryboard); ScoreProcessor = DrawableRuleset.CreateScoreProcessor(); + ScoreProcessor.Mods.BindTo(Mods); + if (!ScoreProcessor.Mode.Disabled) config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode); From 2caea38f8c4f4e672a12197d03aee73e18e646cc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 25 Apr 2019 20:05:59 +0900 Subject: [PATCH 0535/2854] Cleanup centre header container --- .../Profile/Header/BottomHeaderContainer.cs | 4 +-- .../Profile/Header/CentreHeaderContainer.cs | 36 ++++++++++--------- .../Profile/Header/OverlinedInfoContainer.cs | 3 +- .../Profile/Header/ProfileHeaderButton.cs | 4 +-- osu.Game/Overlays/Profile/ProfileHeader.cs | 2 +- 5 files changed, 26 insertions(+), 23 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index 39dd1bd028..f97fecb913 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -35,6 +35,8 @@ namespace osu.Game.Overlays.Profile.Header [BackgroundDependencyLoader] private void load(OsuColour colours) { + iconColour = colours.CommunityUserGrayGreenLighter; + InternalChildren = new Drawable[] { new Box @@ -65,8 +67,6 @@ namespace osu.Game.Overlays.Profile.Header } }; - iconColour = colours.CommunityUserGrayGreenLighter; - User.BindValueChanged(user => updateDisplay(user.NewValue)); } diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index 658dd79570..935e25e4b8 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -21,8 +20,8 @@ namespace osu.Game.Overlays.Profile.Header { public class CentreHeaderContainer : CompositeDrawable { - public Action DetailsVisibilityAction; - private bool detailsVisible; + public readonly BindableBool DetailsVisible = new BindableBool(true); + public readonly Bindable User = new Bindable(); private OsuSpriteText followerText; private OsuSpriteText levelBadgeText; @@ -30,18 +29,20 @@ namespace osu.Game.Overlays.Profile.Header private Bar levelProgressBar; private OsuSpriteText levelProgressText; - private OverlinedInfoContainer hiddenDetailGlobal, hiddenDetailCountry; + private OverlinedInfoContainer hiddenDetailGlobal; + private OverlinedInfoContainer hiddenDetailCountry; - public readonly Bindable User = new Bindable(); + public CentreHeaderContainer() + { + Height = 60; + } [BackgroundDependencyLoader] private void load(OsuColour colours, TextureStore textures) { - Container hiddenDetailContainer, expandedDetailContainer; + Container hiddenDetailContainer; + Container expandedDetailContainer; SpriteIcon expandButtonIcon; - ProfileHeaderButton detailsToggleButton; - Height = 60; - User.ValueChanged += e => updateDisplay(e.NewValue); InternalChildren = new Drawable[] { @@ -105,11 +106,12 @@ namespace osu.Game.Overlays.Profile.Header RelativeSizeAxes = Axes.Y, Padding = new MarginPadding { Vertical = 10 }, Width = UserProfileOverlay.CONTENT_X_MARGIN, - Child = detailsToggleButton = new ExpandButton + Child = new ExpandButton { RelativeSizeAxes = Axes.Y, Anchor = Anchor.Centre, Origin = Anchor.Centre, + Action = () => DetailsVisible.Toggle(), Children = new Drawable[] { expandButtonIcon = new SpriteIcon @@ -210,14 +212,14 @@ namespace osu.Game.Overlays.Profile.Header } }; - detailsToggleButton.Action = () => + DetailsVisible.BindValueChanged(visible => { - detailsVisible = !detailsVisible; - expandButtonIcon.Icon = detailsVisible ? FontAwesome.Solid.ChevronDown : FontAwesome.Solid.ChevronUp; - hiddenDetailContainer.Alpha = detailsVisible ? 1 : 0; - expandedDetailContainer.Alpha = detailsVisible ? 0 : 1; - DetailsVisibilityAction(detailsVisible); - }; + expandButtonIcon.Icon = visible.NewValue ? FontAwesome.Solid.ChevronUp : FontAwesome.Solid.ChevronDown; + hiddenDetailContainer.Alpha = visible.NewValue ? 1 : 0; + expandedDetailContainer.Alpha = visible.NewValue ? 0 : 1; + }, true); + + User.BindValueChanged(user => updateDisplay(user.NewValue)); } private void updateDisplay(User user) diff --git a/osu.Game/Overlays/Profile/Header/OverlinedInfoContainer.cs b/osu.Game/Overlays/Profile/Header/OverlinedInfoContainer.cs index 6d15f96eea..2eb84c9d71 100644 --- a/osu.Game/Overlays/Profile/Header/OverlinedInfoContainer.cs +++ b/osu.Game/Overlays/Profile/Header/OverlinedInfoContainer.cs @@ -13,7 +13,8 @@ namespace osu.Game.Overlays.Profile.Header public class OverlinedInfoContainer : CompositeDrawable { private readonly Circle line; - private readonly OsuSpriteText title, content; + private readonly OsuSpriteText title; + private readonly OsuSpriteText content; public string Title { diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs b/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs index e8c8788a10..300767cf0d 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs +++ b/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs @@ -22,11 +22,11 @@ namespace osu.Game.Overlays.Profile.Header public ProfileHeaderButton() { + AutoSizeAxes = Axes.X; + IdleColour = Color4.Black; HoverColour = OsuColour.Gray(0.1f); - AutoSizeAxes = Axes.X; - base.Content.Add(new CircularContainer { Masking = true, diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 3e257e19bf..3ccf2af061 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -118,7 +118,7 @@ namespace osu.Game.Overlays.Profile infoTabControl.AddItem("Info"); infoTabControl.AddItem("Modding"); - centreHeaderContainer.DetailsVisibilityAction = visible => detailHeaderContainer.Alpha = visible ? 0 : 1; + centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Alpha = visible.NewValue ? 1 : 0, true); User.ValueChanged += e => updateDisplay(e.NewValue); } From 8329e53b6ceea408c7c13fb1153ba4b29f1d1a09 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 25 Apr 2019 20:08:14 +0900 Subject: [PATCH 0536/2854] Remove extra space from header title --- osu.Game/Overlays/Profile/ProfileHeader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 3ccf2af061..d9557952df 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -144,7 +144,7 @@ namespace osu.Game.Overlays.Profile { public ProfileHeaderTitle() { - Title = "Player "; + Title = "Player"; Section = "Info"; } From f8c5ee457bcdc3f46215860d74c91fa3394340dd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 25 Apr 2019 20:09:42 +0900 Subject: [PATCH 0537/2854] Reduce tabcontrol spacing --- osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs index e7c9676550..c6b66b48d0 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs +++ b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Profile.Header public ProfileHeaderTabControl() { TabContainer.Masking = false; - TabContainer.Spacing = new Vector2(20, 0); + TabContainer.Spacing = new Vector2(15, 0); AddInternal(bar = new Box { From 619071b7ee1fb4d241e264feeb22b32d43952e11 Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Thu, 25 Apr 2019 14:23:00 +0300 Subject: [PATCH 0538/2854] Unnecessary 'using' directive --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index a4ba541499..aed5631f0f 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Extensions; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.TypeExtensions; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; From 9ce7d0416bc4dfdeb865507b5926565488b92970 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 25 Apr 2019 23:16:08 +0900 Subject: [PATCH 0539/2854] Fix framework version --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 25a98c9b74..2f0266ce07 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + From 838325fed41503c00b909da2067bae8b2bb257c1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 25 Apr 2019 20:30:16 +0900 Subject: [PATCH 0540/2854] Remove HasTooltipContainer, use separate composites --- .../Online/TestCaseUserProfileHeader.cs | 1 + .../Profile/Header/CentreHeaderContainer.cs | 55 ++------------- .../Profile/Header/DetailHeaderContainer.cs | 38 +--------- .../Overlays/Profile/Header/LevelBadge.cs | 57 +++++++++++++++ .../Profile/Header/LevelProgressBar.cs | 65 +++++++++++++++++ .../Profile/Header/OverlinedTotalPlayTime.cs | 69 +++++++++++++++++++ osu.Game/Overlays/Profile/ProfileHeader.cs | 6 -- 7 files changed, 201 insertions(+), 90 deletions(-) create mode 100644 osu.Game/Overlays/Profile/Header/LevelBadge.cs create mode 100644 osu.Game/Overlays/Profile/Header/LevelProgressBar.cs create mode 100644 osu.Game/Overlays/Profile/Header/OverlinedTotalPlayTime.cs diff --git a/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs index e425c25787..0f1e954224 100644 --- a/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs @@ -23,6 +23,7 @@ namespace osu.Game.Tests.Visual.Online typeof(ProfileHeaderTabControl), typeof(CentreHeaderContainer), typeof(BottomHeaderContainer), + typeof(DetailHeaderContainer), typeof(ProfileHeaderButton) }; diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index 935e25e4b8..e10d259ca9 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -11,10 +11,8 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Users; using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays.Profile.Header { @@ -24,11 +22,6 @@ namespace osu.Game.Overlays.Profile.Header public readonly Bindable User = new Bindable(); private OsuSpriteText followerText; - private OsuSpriteText levelBadgeText; - - private Bar levelProgressBar; - private OsuSpriteText levelProgressText; - private OverlinedInfoContainer hiddenDetailGlobal; private OverlinedInfoContainer hiddenDetailCountry; @@ -132,56 +125,24 @@ namespace osu.Game.Overlays.Profile.Header Margin = new MarginPadding { Right = UserProfileOverlay.CONTENT_X_MARGIN }, Children = new Drawable[] { - new ProfileHeader.HasTooltipContainer + new LevelBadge { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Size = new Vector2(40), - TooltipText = "Level", - Children = new Drawable[] - { - new Sprite - { - RelativeSizeAxes = Axes.Both, - Texture = textures.Get("Profile/levelbadge"), - Colour = colours.Yellow, - }, - levelBadgeText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 20) - } - } + User = { BindTarget = User } }, - expandedDetailContainer = new ProfileHeader.HasTooltipContainer + expandedDetailContainer = new Container { - TooltipText = "Progress to next level", Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Width = 200, Height = 6, Margin = new MarginPadding { Right = 50 }, - Children = new Drawable[] + Child = new LevelProgressBar { - new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Child = levelProgressBar = new Bar - { - RelativeSizeAxes = Axes.Both, - BackgroundColour = Color4.Black, - Direction = BarDirection.LeftToRight, - AccentColour = colours.Yellow - } - }, - levelProgressText = new OsuSpriteText - { - Anchor = Anchor.BottomRight, - Origin = Anchor.TopRight, - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold) - } + RelativeSizeAxes = Axes.Both, + User = { BindTarget = User } } }, hiddenDetailContainer = new FillFlowContainer @@ -226,10 +187,6 @@ namespace osu.Game.Overlays.Profile.Header { followerText.Text = user.FollowerCount?.Length > 0 ? user.FollowerCount[0].ToString("#,##0") : "0"; - levelBadgeText.Text = user.Statistics?.Level.Current.ToString() ?? "0"; - levelProgressBar.Length = user.Statistics?.Level.Progress / 100f ?? 0; - levelProgressText.Text = user.Statistics?.Level.Progress.ToString("0'%'"); - hiddenDetailGlobal.Content = user.Statistics?.Ranks.Global?.ToString("#,##0") ?? "-"; hiddenDetailCountry.Content = user.Statistics?.Ranks.Country?.ToString("#,##0") ?? "-"; } diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 84611b3bf1..62e57fef79 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -20,8 +20,7 @@ namespace osu.Game.Overlays.Profile.Header { public class DetailHeaderContainer : CompositeDrawable { - private ProfileHeader.HasTooltipContainer totalPlayTimeTooltip; - private OverlinedInfoContainer totalPlayTimeInfo, medalInfo, ppInfo; + private OverlinedInfoContainer medalInfo, ppInfo; private readonly Dictionary scoreRankInfos = new Dictionary(); private OverlinedInfoContainer detailGlobalRank, detailCountryRank; private RankGraph rankGraph; @@ -65,15 +64,9 @@ namespace osu.Game.Overlays.Profile.Header Spacing = new Vector2(10, 0), Children = new Drawable[] { - totalPlayTimeTooltip = new ProfileHeader.HasTooltipContainer + new OverlinedTotalPlayTime { - AutoSizeAxes = Axes.Both, - TooltipText = "0 hours", - Child = totalPlayTimeInfo = new OverlinedInfoContainer - { - Title = "Total Play Time", - LineColour = colours.Yellow, - }, + User = { BindTarget = User } }, medalInfo = new OverlinedInfoContainer { @@ -149,31 +142,6 @@ namespace osu.Game.Overlays.Profile.Header medalInfo.Content = user?.Achievements?.Length.ToString() ?? "0"; ppInfo.Content = user?.Statistics?.PP?.ToString("#,##0") ?? "0"; - string formatTime(int? secondsNull) - { - if (secondsNull == null) return "0h 0m"; - - int seconds = secondsNull.Value; - string time = ""; - - int days = seconds / 86400; - seconds -= days * 86400; - if (days > 0) - time += days + "d "; - - int hours = seconds / 3600; - seconds -= hours * 3600; - time += hours + "h "; - - int minutes = seconds / 60; - time += minutes + "m"; - - return time; - } - - totalPlayTimeInfo.Content = formatTime(user?.Statistics?.PlayTime); - totalPlayTimeTooltip.TooltipText = (user?.Statistics?.PlayTime ?? 0) / 3600 + " hours"; - foreach (var scoreRankInfo in scoreRankInfos) scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0; diff --git a/osu.Game/Overlays/Profile/Header/LevelBadge.cs b/osu.Game/Overlays/Profile/Header/LevelBadge.cs new file mode 100644 index 0000000000..8990956811 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/LevelBadge.cs @@ -0,0 +1,57 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Users; + +namespace osu.Game.Overlays.Profile.Header +{ + public class LevelBadge : CompositeDrawable, IHasTooltip + { + public readonly Bindable User = new Bindable(); + + public string TooltipText { get; } + + private OsuSpriteText levelText; + + public LevelBadge() + { + TooltipText = "Level"; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, TextureStore textures) + { + InternalChildren = new Drawable[] + { + new Sprite + { + RelativeSizeAxes = Axes.Both, + Texture = textures.Get("Profile/levelbadge"), + Colour = colours.Yellow, + }, + levelText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 20) + } + }; + + User.BindValueChanged(updateLevel); + } + + private void updateLevel(ValueChangedEvent user) + { + levelText.Text = user.NewValue?.Statistics?.Level.Current.ToString() ?? "0"; + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/LevelProgressBar.cs new file mode 100644 index 0000000000..20d30bd993 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/LevelProgressBar.cs @@ -0,0 +1,65 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Users; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Profile.Header +{ + public class LevelProgressBar : CompositeDrawable, IHasTooltip + { + public readonly Bindable User = new Bindable(); + + public string TooltipText { get; } + + private Bar levelProgressBar; + private OsuSpriteText levelProgressText; + + public LevelProgressBar() + { + TooltipText = "Progress to next level"; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChildren = new Drawable[] + { + new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Child = levelProgressBar = new Bar + { + RelativeSizeAxes = Axes.Both, + BackgroundColour = Color4.Black, + Direction = BarDirection.LeftToRight, + AccentColour = colours.Yellow + } + }, + levelProgressText = new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.TopRight, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold) + } + }; + + User.BindValueChanged(updateProgress); + } + + private void updateProgress(ValueChangedEvent user) + { + levelProgressBar.Length = user.NewValue?.Statistics?.Level.Progress / 100f ?? 0; + levelProgressText.Text = user.NewValue?.Statistics?.Level.Progress.ToString("0'%'"); + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/OverlinedTotalPlayTime.cs b/osu.Game/Overlays/Profile/Header/OverlinedTotalPlayTime.cs new file mode 100644 index 0000000000..80c25ef4e5 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/OverlinedTotalPlayTime.cs @@ -0,0 +1,69 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Game.Graphics; +using osu.Game.Users; + +namespace osu.Game.Overlays.Profile.Header +{ + public class OverlinedTotalPlayTime : CompositeDrawable, IHasTooltip + { + public readonly Bindable User = new Bindable(); + + public string TooltipText { get; set; } + + private OverlinedInfoContainer info; + + public OverlinedTotalPlayTime() + { + AutoSizeAxes = Axes.Both; + + TooltipText = "0 hours"; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChild = info = new OverlinedInfoContainer + { + Title = "Total Play Time", + LineColour = colours.Yellow, + }; + + User.BindValueChanged(updateTime, true); + } + + private void updateTime(ValueChangedEvent user) + { + TooltipText = (user.NewValue?.Statistics?.PlayTime ?? 0) / 3600 + " hours"; + info.Content = formatTime(user.NewValue?.Statistics?.PlayTime); + } + + private string formatTime(int? secondsNull) + { + if (secondsNull == null) return "0h 0m"; + + int seconds = secondsNull.Value; + string time = ""; + + int days = seconds / 86400; + seconds -= days * 86400; + if (days > 0) + time += days + "d "; + + int hours = seconds / 3600; + seconds -= hours * 3600; + time += hours + "h "; + + int minutes = seconds / 60; + time += minutes + "m"; + + return time; + } + } +} diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index d9557952df..7969c645ec 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Game.Graphics; using osu.Game.Overlays.Profile.Header; using osu.Game.Users; @@ -135,11 +134,6 @@ namespace osu.Game.Overlays.Profile coverContainer.User = user; } - public class HasTooltipContainer : Container, IHasTooltip - { - public string TooltipText { get; set; } - } - private class ProfileHeaderTitle : ScreenTitle { public ProfileHeaderTitle() From d5b91c6455b1fe9cae8ef37f98a2c4fffa6b3070 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 26 Apr 2019 12:32:15 +0900 Subject: [PATCH 0541/2854] Cleanup top header container + user handling --- .../Profile/Header/CentreHeaderContainer.cs | 6 ++-- .../Overlays/Profile/Header/LevelBadge.cs | 6 ++-- .../Profile/Header/LevelProgressBar.cs | 8 ++--- .../Profile/Header/TopHeaderContainer.cs | 29 ++++++++++--------- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index e10d259ca9..7964d25db6 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -185,10 +185,10 @@ namespace osu.Game.Overlays.Profile.Header private void updateDisplay(User user) { - followerText.Text = user.FollowerCount?.Length > 0 ? user.FollowerCount[0].ToString("#,##0") : "0"; + followerText.Text = user?.FollowerCount?.Length > 0 ? user.FollowerCount[0].ToString("#,##0") : "0"; - hiddenDetailGlobal.Content = user.Statistics?.Ranks.Global?.ToString("#,##0") ?? "-"; - hiddenDetailCountry.Content = user.Statistics?.Ranks.Country?.ToString("#,##0") ?? "-"; + hiddenDetailGlobal.Content = user?.Statistics?.Ranks.Global?.ToString("#,##0") ?? "-"; + hiddenDetailCountry.Content = user?.Statistics?.Ranks.Country?.ToString("#,##0") ?? "-"; } private class ExpandButton : ProfileHeaderButton diff --git a/osu.Game/Overlays/Profile/Header/LevelBadge.cs b/osu.Game/Overlays/Profile/Header/LevelBadge.cs index 8990956811..cc05926be4 100644 --- a/osu.Game/Overlays/Profile/Header/LevelBadge.cs +++ b/osu.Game/Overlays/Profile/Header/LevelBadge.cs @@ -46,12 +46,12 @@ namespace osu.Game.Overlays.Profile.Header } }; - User.BindValueChanged(updateLevel); + User.BindValueChanged(user => updateLevel(user.NewValue)); } - private void updateLevel(ValueChangedEvent user) + private void updateLevel(User user) { - levelText.Text = user.NewValue?.Statistics?.Level.Current.ToString() ?? "0"; + levelText.Text = user?.Statistics?.Level.Current.ToString() ?? "0"; } } } diff --git a/osu.Game/Overlays/Profile/Header/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/LevelProgressBar.cs index 20d30bd993..c043efb423 100644 --- a/osu.Game/Overlays/Profile/Header/LevelProgressBar.cs +++ b/osu.Game/Overlays/Profile/Header/LevelProgressBar.cs @@ -53,13 +53,13 @@ namespace osu.Game.Overlays.Profile.Header } }; - User.BindValueChanged(updateProgress); + User.BindValueChanged(user => updateProgress(user.NewValue)); } - private void updateProgress(ValueChangedEvent user) + private void updateProgress(User user) { - levelProgressBar.Length = user.NewValue?.Statistics?.Level.Progress / 100f ?? 0; - levelProgressText.Text = user.NewValue?.Statistics?.Level.Progress.ToString("0'%'"); + levelProgressBar.Length = user?.Statistics?.Level.Progress / 100f ?? 0; + levelProgressText.Text = user?.Statistics?.Level.Progress.ToString("0'%'"); } } } diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 80721af42f..50e19d430b 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -17,6 +17,10 @@ namespace osu.Game.Overlays.Profile.Header { public class TopHeaderContainer : CompositeDrawable { + private const float avatar_size = 110; + + public readonly Bindable User = new Bindable(); + private SupporterIcon supporterTag; private UpdateableAvatar avatar; private OsuSpriteText usernameText; @@ -26,15 +30,10 @@ namespace osu.Game.Overlays.Profile.Header private OsuSpriteText userCountryText; private FillFlowContainer userStats; - private const float avatar_size = 110; - - public readonly Bindable User = new Bindable(); - [BackgroundDependencyLoader] private void load(OsuColour colours) { Height = 150; - User.ValueChanged += e => updateDisplay(e.NewValue); InternalChildren = new Drawable[] { @@ -146,21 +145,23 @@ namespace osu.Game.Overlays.Profile.Header Spacing = new Vector2(0, 2) } }; + + User.BindValueChanged(user => updateUser(user.NewValue)); } - private void updateDisplay(User user) + private void updateUser(User user) { avatar.User = user; - usernameText.Text = user.Username; - openUserExternally.Link = $@"https://osu.ppy.sh/users/{user.Id}"; - userFlag.Country = user.Country; - userCountryText.Text = user.Country?.FullName ?? "Alien"; - supporterTag.SupporterLevel = user.SupportLevel; - titleText.Text = user.Title; - titleText.Colour = OsuColour.FromHex(user.Colour ?? "fff"); + usernameText.Text = user?.Username ?? string.Empty; + openUserExternally.Link = $@"https://osu.ppy.sh/users/{user?.Id ?? 0}"; + userFlag.Country = user?.Country; + userCountryText.Text = user?.Country?.FullName ?? "Alien"; + supporterTag.SupporterLevel = user?.SupportLevel ?? 0; + titleText.Text = user?.Title ?? string.Empty; + titleText.Colour = OsuColour.FromHex(user?.Colour ?? "fff"); userStats.Clear(); - if (user.Statistics != null) + if (user?.Statistics != null) { userStats.Add(new UserStatsLine("Ranked Score", user.Statistics.RankedScore.ToString("#,##0"))); userStats.Add(new UserStatsLine("Hit Accuracy", Math.Round(user.Statistics.Accuracy, 2).ToString("#0.00'%'"))); From 4adf590036e6b9a6674175e0a15870079601373b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 26 Apr 2019 12:41:00 +0900 Subject: [PATCH 0542/2854] Combine hover/active state handling in tab control --- .../Profile/Header/ProfileHeaderTabControl.cs | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs index c6b66b48d0..3b16b102d5 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs +++ b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs @@ -24,10 +24,10 @@ namespace osu.Game.Overlays.Profile.Header get => accentColour; set { - if (accentColour == value) return; + if (accentColour == value) + return; accentColour = value; - bar.Colour = value; foreach (TabItem tabItem in TabContainer) @@ -76,10 +76,13 @@ namespace osu.Game.Overlays.Profile.Header get => accentColour; set { - accentColour = value; + if (accentColour == value) + return; + accentColour = value; bar.Colour = value; - if (!Active.Value) text.Colour = value; + + updateState(); } } @@ -112,37 +115,40 @@ namespace osu.Game.Overlays.Profile.Header protected override bool OnHover(HoverEvent e) { - if (!Active.Value) - onActivated(true); - return base.OnHover(e); + base.OnHover(e); + + updateState(); + + return true; } protected override void OnHoverLost(HoverLostEvent e) { base.OnHoverLost(e); - if (!Active.Value) - OnDeactivated(); + updateState(); } - protected override void OnActivated() - { - onActivated(); - } + protected override void OnActivated() => updateState(); - protected override void OnDeactivated() - { - text.FadeColour(AccentColour, 120, Easing.InQuad); - bar.ResizeHeightTo(0, 120, Easing.InQuad); - text.Font = text.Font.With(weight: FontWeight.Medium); - } + protected override void OnDeactivated() => updateState(); - private void onActivated(bool fake = false) + private void updateState() { - text.FadeColour(Color4.White, 120, Easing.InQuad); - bar.ResizeHeightTo(7.5f, 120, Easing.InQuad); - if (!fake) - text.Font = text.Font.With(weight: FontWeight.Bold); + if (Active.Value || IsHovered) + { + text.FadeColour(Color4.White, 120, Easing.InQuad); + bar.ResizeHeightTo(7.5f, 120, Easing.InQuad); + + if (Active.Value) + text.Font = text.Font.With(weight: FontWeight.Bold); + } + else + { + text.FadeColour(AccentColour, 120, Easing.InQuad); + bar.ResizeHeightTo(0, 120, Easing.InQuad); + text.Font = text.Font.With(weight: FontWeight.Medium); + } } } } From 7047303b0fcf68378f6ea315570ab8a279d0061e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 26 Apr 2019 13:29:58 +0900 Subject: [PATCH 0543/2854] Add tooltips to all buttons --- .../Profile/Header/CentreHeaderContainer.cs | 63 ++----------------- .../Profile/Header/ExpandDetailsButton.cs | 45 +++++++++++++ .../Overlays/Profile/Header/FriendButton.cs | 58 +++++++++++++++++ .../Profile/Header/ProfileHeaderButton.cs | 7 ++- .../Profile/Header/ProfileMessageButton.cs | 2 + 5 files changed, 114 insertions(+), 61 deletions(-) create mode 100644 osu.Game/Overlays/Profile/Header/ExpandDetailsButton.cs create mode 100644 osu.Game/Overlays/Profile/Header/FriendButton.cs diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index 7964d25db6..1d947383a1 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -3,14 +3,11 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Game.Users; using osuTK; @@ -21,7 +18,6 @@ namespace osu.Game.Overlays.Profile.Header public readonly BindableBool DetailsVisible = new BindableBool(true); public readonly Bindable User = new Bindable(); - private OsuSpriteText followerText; private OverlinedInfoContainer hiddenDetailGlobal; private OverlinedInfoContainer hiddenDetailCountry; @@ -35,7 +31,6 @@ namespace osu.Game.Overlays.Profile.Header { Container hiddenDetailContainer; Container expandedDetailContainer; - SpriteIcon expandButtonIcon; InternalChildren = new Drawable[] { @@ -54,37 +49,10 @@ namespace osu.Game.Overlays.Profile.Header Spacing = new Vector2(10, 0), Children = new Drawable[] { - new ProfileHeaderButton + new FriendButton { RelativeSizeAxes = Axes.Y, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Direction = FillDirection.Horizontal, - Padding = new MarginPadding { Right = 10 }, - Children = new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.Solid.User, - FillMode = FillMode.Fit, - Size = new Vector2(50, 14) - }, - followerText = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Font = OsuFont.GetFont(weight: FontWeight.Bold) - } - } - } - } + User = { BindTarget = User } }, new ProfileMessageButton { @@ -99,22 +67,12 @@ namespace osu.Game.Overlays.Profile.Header RelativeSizeAxes = Axes.Y, Padding = new MarginPadding { Vertical = 10 }, Width = UserProfileOverlay.CONTENT_X_MARGIN, - Child = new ExpandButton + Child = new ExpandDetailsButton { RelativeSizeAxes = Axes.Y, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Action = () => DetailsVisible.Toggle(), - Children = new Drawable[] - { - expandButtonIcon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(20, 12), - Icon = FontAwesome.Solid.ChevronUp, - }, - } + DetailsVisible = { BindTarget = DetailsVisible } }, }, new Container @@ -175,7 +133,6 @@ namespace osu.Game.Overlays.Profile.Header DetailsVisible.BindValueChanged(visible => { - expandButtonIcon.Icon = visible.NewValue ? FontAwesome.Solid.ChevronUp : FontAwesome.Solid.ChevronDown; hiddenDetailContainer.Alpha = visible.NewValue ? 1 : 0; expandedDetailContainer.Alpha = visible.NewValue ? 0 : 1; }, true); @@ -185,20 +142,8 @@ namespace osu.Game.Overlays.Profile.Header private void updateDisplay(User user) { - followerText.Text = user?.FollowerCount?.Length > 0 ? user.FollowerCount[0].ToString("#,##0") : "0"; - hiddenDetailGlobal.Content = user?.Statistics?.Ranks.Global?.ToString("#,##0") ?? "-"; hiddenDetailCountry.Content = user?.Statistics?.Ranks.Country?.ToString("#,##0") ?? "-"; } - - private class ExpandButton : ProfileHeaderButton - { - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - IdleColour = colours.CommunityUserGrayGreen; - HoverColour = colours.CommunityUserGrayGreen.Darken(0.2f); - } - } } } diff --git a/osu.Game/Overlays/Profile/Header/ExpandDetailsButton.cs b/osu.Game/Overlays/Profile/Header/ExpandDetailsButton.cs new file mode 100644 index 0000000000..dc507be0b1 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/ExpandDetailsButton.cs @@ -0,0 +1,45 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osuTK; + +namespace osu.Game.Overlays.Profile.Header +{ + public class ExpandDetailsButton : ProfileHeaderButton + { + public readonly BindableBool DetailsVisible = new BindableBool(); + + public override string TooltipText => DetailsVisible.Value ? "collapse" : "expand"; + + private SpriteIcon icon; + + public ExpandDetailsButton() + { + Action = () => DetailsVisible.Toggle(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + IdleColour = colours.CommunityUserGrayGreen; + HoverColour = colours.CommunityUserGrayGreen.Darken(0.2f); + + Child = icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(20, 12) + }; + + DetailsVisible.BindValueChanged(visible => updateState(visible.NewValue), true); + } + + private void updateState(bool detailsVisible) => icon.Icon = detailsVisible ? FontAwesome.Solid.ChevronUp : FontAwesome.Solid.ChevronDown; + } +} diff --git a/osu.Game/Overlays/Profile/Header/FriendButton.cs b/osu.Game/Overlays/Profile/Header/FriendButton.cs new file mode 100644 index 0000000000..3b2f192fb1 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/FriendButton.cs @@ -0,0 +1,58 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Overlays.Profile.Header +{ + public class FriendButton : ProfileHeaderButton + { + public readonly Bindable User = new Bindable(); + + public override string TooltipText => "friends"; + + private OsuSpriteText followerText; + + [BackgroundDependencyLoader] + private void load() + { + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Direction = FillDirection.Horizontal, + Padding = new MarginPadding { Right = 10 }, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.Solid.User, + FillMode = FillMode.Fit, + Size = new Vector2(50, 14) + }, + followerText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(weight: FontWeight.Bold) + } + } + }; + + User.BindValueChanged(user => updateFollowers(user.NewValue), true); + } + + private void updateFollowers(User user) => followerText.Text = user?.FollowerCount?.Length > 0 ? user.FollowerCount[0].ToString("#,##0") : "0"; + } +} diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs b/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs index 300767cf0d..ddf2338873 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs +++ b/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -11,8 +12,10 @@ using osuTK.Graphics; namespace osu.Game.Overlays.Profile.Header { - public class ProfileHeaderButton : OsuHoverContainer + public abstract class ProfileHeaderButton : OsuHoverContainer, IHasTooltip { + public abstract string TooltipText { get; } + private readonly Box background; private readonly Container content; @@ -20,7 +23,7 @@ namespace osu.Game.Overlays.Profile.Header protected override IEnumerable EffectTargets => new[] { background }; - public ProfileHeaderButton() + protected ProfileHeaderButton() { AutoSizeAxes = Axes.X; diff --git a/osu.Game/Overlays/Profile/Header/ProfileMessageButton.cs b/osu.Game/Overlays/Profile/Header/ProfileMessageButton.cs index 3121eae727..162d49cf1b 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileMessageButton.cs +++ b/osu.Game/Overlays/Profile/Header/ProfileMessageButton.cs @@ -16,6 +16,8 @@ namespace osu.Game.Overlays.Profile.Header { public readonly Bindable User = new Bindable(); + public override string TooltipText => "send message"; + [Resolved(CanBeNull = true)] private ChannelManager channelManager { get; set; } From 2f4bf423a4d4ef4d56a91046ef4d0e70074188ee Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 26 Apr 2019 13:49:44 +0900 Subject: [PATCH 0544/2854] Renamespace --- osu.Game.Tests/Visual/Online/TestCaseRankGraph.cs | 2 +- osu.Game.Tests/Visual/Online/TestCaseUserProfile.cs | 2 +- .../Visual/Online/TestCaseUserProfileHeader.cs | 1 + .../Overlays/Profile/Header/CentreHeaderContainer.cs | 5 +++-- .../{FriendButton.cs => Components/AddFriendButton.cs} | 4 ++-- .../Profile/Header/{ => Components}/DrawableBadge.cs | 2 +- .../Header/{ => Components}/ExpandDetailsButton.cs | 2 +- .../Profile/Header/{ => Components}/LevelBadge.cs | 2 +- .../Header/{ => Components}/LevelProgressBar.cs | 2 +- .../MessageUserButton.cs} | 6 +++--- .../Header/{ => Components}/OverlinedInfoContainer.cs | 2 +- .../Header/{ => Components}/OverlinedTotalPlayTime.cs | 2 +- .../Header/{ => Components}/ProfileHeaderButton.cs | 2 +- .../Profile/Header/{ => Components}/RankGraph.cs | 2 +- .../Profile/Header/{ => Components}/SupporterIcon.cs | 2 +- .../Overlays/Profile/Header/DetailHeaderContainer.cs | 1 + .../Overlays/Profile/Header/MedalHeaderContainer.cs | 1 + osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 1 + osu.Game/Overlays/Profile/ProfileHeader.cs | 10 +++++----- osu.Game/Overlays/Profile/ProfileSection.cs | 2 +- osu.Game/Users/UserPanel.cs | 2 +- 21 files changed, 30 insertions(+), 25 deletions(-) rename osu.Game/Overlays/Profile/Header/{FriendButton.cs => Components/AddFriendButton.cs} (94%) rename osu.Game/Overlays/Profile/Header/{ => Components}/DrawableBadge.cs (95%) rename osu.Game/Overlays/Profile/Header/{ => Components}/ExpandDetailsButton.cs (96%) rename osu.Game/Overlays/Profile/Header/{ => Components}/LevelBadge.cs (96%) rename osu.Game/Overlays/Profile/Header/{ => Components}/LevelProgressBar.cs (97%) rename osu.Game/Overlays/Profile/Header/{ProfileMessageButton.cs => Components/MessageUserButton.cs} (91%) rename osu.Game/Overlays/Profile/Header/{ => Components}/OverlinedInfoContainer.cs (97%) rename osu.Game/Overlays/Profile/Header/{ => Components}/OverlinedTotalPlayTime.cs (97%) rename osu.Game/Overlays/Profile/Header/{ => Components}/ProfileHeaderButton.cs (96%) rename osu.Game/Overlays/Profile/Header/{ => Components}/RankGraph.cs (99%) rename osu.Game/Overlays/Profile/Header/{ => Components}/SupporterIcon.cs (97%) diff --git a/osu.Game.Tests/Visual/Online/TestCaseRankGraph.cs b/osu.Game.Tests/Visual/Online/TestCaseRankGraph.cs index dff018bf91..a92b788e83 100644 --- a/osu.Game.Tests/Visual/Online/TestCaseRankGraph.cs +++ b/osu.Game.Tests/Visual/Online/TestCaseRankGraph.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Profile.Header; +using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Users; using osuTK; diff --git a/osu.Game.Tests/Visual/Online/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/Online/TestCaseUserProfile.cs index c455c092ed..0789c14b32 100644 --- a/osu.Game.Tests/Visual/Online/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/Online/TestCaseUserProfile.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Overlays.Profile; -using osu.Game.Overlays.Profile.Header; +using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Users; namespace osu.Game.Tests.Visual.Online diff --git a/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs index 0f1e954224..5f5ba89186 100644 --- a/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs @@ -9,6 +9,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile.Header; +using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Users; namespace osu.Game.Tests.Visual.Online diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index 1d947383a1..c1ad2011f8 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Game.Graphics; +using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Users; using osuTK; @@ -49,12 +50,12 @@ namespace osu.Game.Overlays.Profile.Header Spacing = new Vector2(10, 0), Children = new Drawable[] { - new FriendButton + new AddFriendButton { RelativeSizeAxes = Axes.Y, User = { BindTarget = User } }, - new ProfileMessageButton + new MessageUserButton { User = { BindTarget = User } }, diff --git a/osu.Game/Overlays/Profile/Header/FriendButton.cs b/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs similarity index 94% rename from osu.Game/Overlays/Profile/Header/FriendButton.cs rename to osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs index 3b2f192fb1..2e4fd6fe3d 100644 --- a/osu.Game/Overlays/Profile/Header/FriendButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs @@ -11,9 +11,9 @@ using osu.Game.Graphics.Sprites; using osu.Game.Users; using osuTK; -namespace osu.Game.Overlays.Profile.Header +namespace osu.Game.Overlays.Profile.Header.Components { - public class FriendButton : ProfileHeaderButton + public class AddFriendButton : ProfileHeaderButton { public readonly Bindable User = new Bindable(); diff --git a/osu.Game/Overlays/Profile/Header/DrawableBadge.cs b/osu.Game/Overlays/Profile/Header/Components/DrawableBadge.cs similarity index 95% rename from osu.Game/Overlays/Profile/Header/DrawableBadge.cs rename to osu.Game/Overlays/Profile/Header/Components/DrawableBadge.cs index 75a8f4e415..ea259fe49a 100644 --- a/osu.Game/Overlays/Profile/Header/DrawableBadge.cs +++ b/osu.Game/Overlays/Profile/Header/Components/DrawableBadge.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics.Textures; using osu.Game.Users; using osuTK; -namespace osu.Game.Overlays.Profile.Header +namespace osu.Game.Overlays.Profile.Header.Components { public class DrawableBadge : CompositeDrawable, IHasTooltip { diff --git a/osu.Game/Overlays/Profile/Header/ExpandDetailsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs similarity index 96% rename from osu.Game/Overlays/Profile/Header/ExpandDetailsButton.cs rename to osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs index dc507be0b1..089228b2cd 100644 --- a/osu.Game/Overlays/Profile/Header/ExpandDetailsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osuTK; -namespace osu.Game.Overlays.Profile.Header +namespace osu.Game.Overlays.Profile.Header.Components { public class ExpandDetailsButton : ProfileHeaderButton { diff --git a/osu.Game/Overlays/Profile/Header/LevelBadge.cs b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs similarity index 96% rename from osu.Game/Overlays/Profile/Header/LevelBadge.cs rename to osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs index cc05926be4..8069937810 100644 --- a/osu.Game/Overlays/Profile/Header/LevelBadge.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs @@ -12,7 +12,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Users; -namespace osu.Game.Overlays.Profile.Header +namespace osu.Game.Overlays.Profile.Header.Components { public class LevelBadge : CompositeDrawable, IHasTooltip { diff --git a/osu.Game/Overlays/Profile/Header/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs similarity index 97% rename from osu.Game/Overlays/Profile/Header/LevelProgressBar.cs rename to osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs index c043efb423..6a6532764f 100644 --- a/osu.Game/Overlays/Profile/Header/LevelProgressBar.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs @@ -12,7 +12,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Users; using osuTK.Graphics; -namespace osu.Game.Overlays.Profile.Header +namespace osu.Game.Overlays.Profile.Header.Components { public class LevelProgressBar : CompositeDrawable, IHasTooltip { diff --git a/osu.Game/Overlays/Profile/Header/ProfileMessageButton.cs b/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs similarity index 91% rename from osu.Game/Overlays/Profile/Header/ProfileMessageButton.cs rename to osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs index 162d49cf1b..cc6edcdd6a 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileMessageButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs @@ -10,9 +10,9 @@ using osu.Game.Online.Chat; using osu.Game.Users; using osuTK; -namespace osu.Game.Overlays.Profile.Header +namespace osu.Game.Overlays.Profile.Header.Components { - public class ProfileMessageButton : ProfileHeaderButton + public class MessageUserButton : ProfileHeaderButton { public readonly Bindable User = new Bindable(); @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Profile.Header [Resolved] private IAPIProvider apiProvider { get; set; } - public ProfileMessageButton() + public MessageUserButton() { Content.Alpha = 0; RelativeSizeAxes = Axes.Y; diff --git a/osu.Game/Overlays/Profile/Header/OverlinedInfoContainer.cs b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs similarity index 97% rename from osu.Game/Overlays/Profile/Header/OverlinedInfoContainer.cs rename to osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs index 2eb84c9d71..c40ddca688 100644 --- a/osu.Game/Overlays/Profile/Header/OverlinedInfoContainer.cs +++ b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs @@ -8,7 +8,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK.Graphics; -namespace osu.Game.Overlays.Profile.Header +namespace osu.Game.Overlays.Profile.Header.Components { public class OverlinedInfoContainer : CompositeDrawable { diff --git a/osu.Game/Overlays/Profile/Header/OverlinedTotalPlayTime.cs b/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs similarity index 97% rename from osu.Game/Overlays/Profile/Header/OverlinedTotalPlayTime.cs rename to osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs index 80c25ef4e5..2c88a83680 100644 --- a/osu.Game/Overlays/Profile/Header/OverlinedTotalPlayTime.cs +++ b/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics.Cursor; using osu.Game.Graphics; using osu.Game.Users; -namespace osu.Game.Overlays.Profile.Header +namespace osu.Game.Overlays.Profile.Header.Components { public class OverlinedTotalPlayTime : CompositeDrawable, IHasTooltip { diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs similarity index 96% rename from osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs rename to osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs index ddf2338873..1650f11523 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileHeaderButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osuTK.Graphics; -namespace osu.Game.Overlays.Profile.Header +namespace osu.Game.Overlays.Profile.Header.Components { public abstract class ProfileHeaderButton : OsuHoverContainer, IHasTooltip { diff --git a/osu.Game/Overlays/Profile/Header/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs similarity index 99% rename from osu.Game/Overlays/Profile/Header/RankGraph.cs rename to osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index d66f2306a0..bb54d0ac51 100644 --- a/osu.Game/Overlays/Profile/Header/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -17,7 +17,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Users; using osuTK; -namespace osu.Game.Overlays.Profile.Header +namespace osu.Game.Overlays.Profile.Header.Components { public class RankGraph : Container, IHasCustomTooltip { diff --git a/osu.Game/Overlays/Profile/Header/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs similarity index 97% rename from osu.Game/Overlays/Profile/Header/SupporterIcon.cs rename to osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs index 569ed252b7..99b45aca93 100644 --- a/osu.Game/Overlays/Profile/Header/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; -namespace osu.Game.Overlays.Profile.Header +namespace osu.Game.Overlays.Profile.Header.Components { public class SupporterIcon : CompositeDrawable, IHasTooltip { diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 62e57fef79..89a9d7ddd1 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Scoring; using osu.Game.Users; using osuTK; diff --git a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs index 69b1203b9f..1e214b2d0c 100644 --- a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; +using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Users; using osuTK; using osuTK.Graphics; diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 50e19d430b..ce02e61d82 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Users; using osuTK; diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 7969c645ec..f5233cf70c 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -2,16 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Overlays.Profile.Header; -using osu.Game.Users; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Profile.Header; +using osu.Game.Users; namespace osu.Game.Overlays.Profile { diff --git a/osu.Game/Overlays/Profile/ProfileSection.cs b/osu.Game/Overlays/Profile/ProfileSection.cs index 6da736432f..4d891384e8 100644 --- a/osu.Game/Overlays/Profile/ProfileSection.cs +++ b/osu.Game/Overlays/Profile/ProfileSection.cs @@ -2,13 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osuTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Users; +using osuTK; using osuTK.Graphics; namespace osu.Game.Overlays.Profile diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index eb7f0941fb..1a10e035cd 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -18,7 +18,7 @@ using osu.Game.Graphics.UserInterface; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Containers; -using osu.Game.Overlays.Profile.Header; +using osu.Game.Overlays.Profile.Header.Components; namespace osu.Game.Users { From 50a775145cb3eb8de776799c35ae35f1c03715d6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 26 Apr 2019 13:56:26 +0900 Subject: [PATCH 0545/2854] Separate variables --- osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 89a9d7ddd1..9bb0affe7d 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -21,9 +21,11 @@ namespace osu.Game.Overlays.Profile.Header { public class DetailHeaderContainer : CompositeDrawable { - private OverlinedInfoContainer medalInfo, ppInfo; private readonly Dictionary scoreRankInfos = new Dictionary(); - private OverlinedInfoContainer detailGlobalRank, detailCountryRank; + private OverlinedInfoContainer medalInfo; + private OverlinedInfoContainer ppInfo; + private OverlinedInfoContainer detailGlobalRank; + private OverlinedInfoContainer detailCountryRank; private RankGraph rankGraph; public readonly Bindable User = new Bindable(); From 32e71a6314c485107d24a9de9e2e4598d246fe88 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 26 Apr 2019 16:15:42 +0900 Subject: [PATCH 0546/2854] Fix incorrect seeking behaviour of TrackVirtualManual --- osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index 78f9103a74..c558275f62 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets; +using osuTK; namespace osu.Game.Tests.Beatmaps { @@ -67,9 +68,10 @@ namespace osu.Game.Tests.Beatmaps public override bool Seek(double seek) { - offset = Math.Min(seek, Length); + offset = MathHelper.Clamp(seek, 0, Length); lastReferenceTime = null; - return true; + + return offset == seek; } public override void Start() From 41d0b00120c9da14bbbac7fb514e64e779cb6ab0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 27 Apr 2019 16:43:23 +0900 Subject: [PATCH 0547/2854] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 74ed9f91dd..3b47005049 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 9fff64c61c..20810886f3 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 27c1c368ac715470e22b8dccfc5b42eed2745464 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 27 Apr 2019 20:31:00 +0900 Subject: [PATCH 0548/2854] Remove unused using --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index fd65d8f9cc..d72c334ed3 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.UI; From 27ba89444ee7af1a82988fea3b3ecae91e7f1d8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 28 Apr 2019 01:08:14 +0900 Subject: [PATCH 0549/2854] Remove unnecessary using statements --- osu.Game/Overlays/Profile/ProfileHeader.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 8c220b2cb4..f5233cf70c 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -12,8 +12,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Profile.Header; using osu.Game.Users; -using Humanizer; -using osu.Framework.Graphics.Effects; namespace osu.Game.Overlays.Profile { From 8ab514933673967a02ee873cb34d40755ceac527 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 28 Apr 2019 20:11:36 +0900 Subject: [PATCH 0550/2854] SupporterLevel -> SupportLevel --- osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs | 2 +- osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 2 +- osu.Game/Users/UserPanel.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs index 99b45aca93..d4ccef8b69 100644 --- a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Profile.Header.Components public string TooltipText => "osu!supporter"; - public int SupporterLevel + public int SupportLevel { set { diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index ce02e61d82..c1fe430bdd 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -157,7 +157,7 @@ namespace osu.Game.Overlays.Profile.Header openUserExternally.Link = $@"https://osu.ppy.sh/users/{user?.Id ?? 0}"; userFlag.Country = user?.Country; userCountryText.Text = user?.Country?.FullName ?? "Alien"; - supporterTag.SupporterLevel = user?.SupportLevel ?? 0; + supporterTag.SupportLevel = user?.SupportLevel ?? 0; titleText.Text = user?.Title ?? string.Empty; titleText.Colour = OsuColour.FromHex(user?.Colour ?? "fff"); diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 75069273a5..47571b673d 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -191,7 +191,7 @@ namespace osu.Game.Users infoContainer.Add(new SupporterIcon { Height = 20f, - SupporterLevel = user.SupportLevel + SupportLevel = user.SupportLevel }); } From f9f6e1f04ae8cfbc3d1851a7746814baf5b189e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 28 Apr 2019 20:13:28 +0900 Subject: [PATCH 0551/2854] Clamp values to avoid potentially weird element --- .../Overlays/Profile/Header/Components/SupporterIcon.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs index d4ccef8b69..cb12a62702 100644 --- a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; +using osuTK; namespace osu.Game.Overlays.Profile.Header.Components { @@ -23,7 +24,9 @@ namespace osu.Game.Overlays.Profile.Header.Components { set { - if (value == 0) + int count = MathHelper.Clamp(value, 0, 3); + + if (count == 0) { content.Hide(); } @@ -31,7 +34,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { content.Show(); iconContainer.Clear(); - for (int i = 0; i < value; i++) + for (int i = 0; i < count; i++) { iconContainer.Add(new SpriteIcon { From 5d50316ae7565979ecb40604c74c7da0649b632c Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 30 Apr 2019 18:12:27 +0300 Subject: [PATCH 0552/2854] Add xmldoc --- osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs index 34e8d858f6..1b62dbdacf 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs @@ -13,6 +13,9 @@ namespace osu.Game.Rulesets.Mods { void ApplyToScoreProcessor(ScoreProcessor scoreProcessor); + /// + /// Adjusts a rank value passed by and returns it. + /// ScoreRank AdjustRank(ScoreRank rank, double accuracy); } } From 665558c297984e2a05590337440ee81bea864784 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 30 Apr 2019 18:44:06 +0300 Subject: [PATCH 0553/2854] Remove unnecessary comment --- osu.Game/Rulesets/Mods/ModHidden.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index c55c45c9fa..cda919c894 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -36,11 +36,10 @@ namespace osu.Game.Rulesets.Mods public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { - // Default value of ScoreProcessor's Rank in Hidden Mod should bes SS+ + // Default value of @@ -182,13 +168,6 @@ namespace osu.Game.Overlays { content.Show(); content.ShowBuild(populatedBuild); - - if (scroll.Current > scroll.GetChildPosInContent(content)) - scroll.ScrollTo(content); - - if (isAtListing) - savedScrollPosition = scroll.Current; - isAtListing = false; } if (build.Versions != null) From dd2d58d4f7c64726cd4ef1b277954d83ad55912c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 May 2019 17:15:51 +0900 Subject: [PATCH 0745/2854] Split out ChangelogContent into two classes --- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 30 ++++++++ .../Overlays/Changelog/ChangelogContent.cs | 60 +--------------- .../Overlays/Changelog/ChangelogListing.cs | 70 +++++++++++++++++++ osu.Game/Overlays/ChangelogOverlay.cs | 37 +++++----- 4 files changed, 118 insertions(+), 79 deletions(-) create mode 100644 osu.Game/Overlays/Changelog/ChangelogBuild.cs create mode 100644 osu.Game/Overlays/Changelog/ChangelogListing.cs diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs new file mode 100644 index 0000000000..0f3e76021b --- /dev/null +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Game.Online.API.Requests.Responses; + +namespace osu.Game.Overlays.Changelog +{ + public class ChangelogBuild : ChangelogContent + { + private readonly APIChangelogBuild changelogBuild; + + public ChangelogBuild(APIChangelogBuild changelogBuild) + { + this.changelogBuild = changelogBuild; + } + + [BackgroundDependencyLoader] + private void load() + { + var changelogContentGroup = new ChangelogContentGroup(changelogBuild); + changelogContentGroup.GenerateText(changelogBuild.ChangelogEntries); + changelogContentGroup.UpdateChevronTooltips(changelogBuild.Versions.Previous?.DisplayVersion, + changelogBuild.Versions.Next?.DisplayVersion); + changelogContentGroup.BuildSelected += SelectBuild; + + Add(changelogContentGroup); + } + } +} diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 45fb98add0..92c33567a8 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -3,20 +3,17 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Game.Online.API.Requests.Responses; using System; -using System.Collections.Generic; -using osuTK.Graphics; namespace osu.Game.Overlays.Changelog { public class ChangelogContent : FillFlowContainer { - private ChangelogContentGroup changelogContentGroup; - public event Action BuildSelected; + public void SelectBuild(APIChangelogBuild build) => BuildSelected?.Invoke(build); + public ChangelogContent() { RelativeSizeAxes = Axes.X; @@ -24,58 +21,5 @@ namespace osu.Game.Overlays.Changelog Direction = FillDirection.Vertical; Padding = new MarginPadding { Bottom = 100 }; } - - public void ShowListing(List changelog) - { - DateTime currentDate = new DateTime(); - Clear(); - - foreach (APIChangelogBuild build in changelog) - { - if (build.CreatedAt.Date != currentDate) - { - if (Children.Count != 0) - { - Add(new Box - { - RelativeSizeAxes = Axes.X, - Height = 2, - Colour = new Color4(17, 17, 17, 255), - Margin = new MarginPadding { Top = 30 }, - }); - } - - changelogContentGroup = new ChangelogContentGroup(build, true); - changelogContentGroup.BuildSelected += b => BuildSelected?.Invoke(b); - changelogContentGroup.GenerateText(build.ChangelogEntries); - Add(changelogContentGroup); - currentDate = build.CreatedAt.Date; - } - else - { - changelogContentGroup.Add(new Box - { - RelativeSizeAxes = Axes.X, - Height = 1, - Colour = new Color4(32, 24, 35, 255), - Margin = new MarginPadding { Top = 30 }, - }); - - changelogContentGroup = new ChangelogContentGroup(build, false); - changelogContentGroup.BuildSelected += b => BuildSelected?.Invoke(b); - changelogContentGroup.GenerateText(build.ChangelogEntries); - Add(changelogContentGroup); - } - } - } - - public void ShowBuild(APIChangelogBuild changelogBuild) - { - Child = changelogContentGroup = new ChangelogContentGroup(changelogBuild); - changelogContentGroup.GenerateText(changelogBuild.ChangelogEntries); - changelogContentGroup.UpdateChevronTooltips(changelogBuild.Versions.Previous?.DisplayVersion, - changelogBuild.Versions.Next?.DisplayVersion); - changelogContentGroup.BuildSelected += b => BuildSelected?.Invoke(b); - } } } diff --git a/osu.Game/Overlays/Changelog/ChangelogListing.cs b/osu.Game/Overlays/Changelog/ChangelogListing.cs new file mode 100644 index 0000000000..8e615d1dc9 --- /dev/null +++ b/osu.Game/Overlays/Changelog/ChangelogListing.cs @@ -0,0 +1,70 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Online.API.Requests.Responses; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Changelog +{ + public class ChangelogListing : ChangelogContent + { + private readonly List entries; + + public ChangelogListing(List entries) + { + this.entries = entries; + } + + [BackgroundDependencyLoader] + private void load() + { + DateTime currentDate = new DateTime(); + Clear(); + + ChangelogContentGroup changelogContentGroup = null; + + foreach (APIChangelogBuild build in entries) + { + if (build.CreatedAt.Date != currentDate) + { + if (Children.Count != 0) + { + Add(new Box + { + RelativeSizeAxes = Axes.X, + Height = 2, + Colour = new Color4(17, 17, 17, 255), + Margin = new MarginPadding { Top = 30 }, + }); + } + + changelogContentGroup = new ChangelogContentGroup(build, true); + changelogContentGroup.BuildSelected += SelectBuild; + changelogContentGroup.GenerateText(build.ChangelogEntries); + Add(changelogContentGroup); + currentDate = build.CreatedAt.Date; + } + else + { + changelogContentGroup?.Add(new Box + { + RelativeSizeAxes = Axes.X, + Height = 1, + Colour = new Color4(32, 24, 35, 255), + Margin = new MarginPadding { Top = 30 }, + }); + + changelogContentGroup = new ChangelogContentGroup(build, false); + changelogContentGroup.BuildSelected += SelectBuild; + changelogContentGroup.GenerateText(build.ChangelogEntries); + Add(changelogContentGroup); + } + } + } + } +} diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index afa4f4185e..6ef239733d 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -23,15 +24,15 @@ namespace osu.Game.Overlays private BadgeDisplay badges; - private ChangelogContent listing; - private ChangelogContent content; + private Container content; private SampleChannel sampleBack; + private List builds; + [BackgroundDependencyLoader] private void load(AudioManager audio, OsuColour colour) { - // these possibly need adjusting? Waves.FirstWaveColour = colour.Violet; Waves.SecondWaveColour = OsuColour.FromHex(@"8F03BF"); Waves.ThirdWaveColour = OsuColour.FromHex(@"600280"); @@ -57,8 +58,11 @@ namespace osu.Game.Overlays { header = new ChangelogHeader(), badges = new BadgeDisplay(), - listing = new ChangelogContent(), - content = new ChangelogContent() + content = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } }, }, }, @@ -73,10 +77,7 @@ namespace osu.Game.Overlays ShowBuild(e.NewValue.LatestBuild); }; - listing.BuildSelected += ShowBuild; - content.BuildSelected += ShowBuild; - - sampleBack = audio.Sample.Get(@"UI/generic-select-soft"); // @"UI/screen-back" feels non-fitting here + sampleBack = audio.Sample.Get(@"UI/generic-select-soft"); } protected override void LoadComplete() @@ -102,7 +103,7 @@ namespace osu.Game.Overlays switch (action) { case GlobalAction.Back: - if (listing.Alpha == 1) + if (content.Child is ChangelogContent) { State = Visibility.Hidden; } @@ -129,7 +130,8 @@ namespace osu.Game.Overlays res.Builds.ForEach(b => b.UpdateStream = res.Streams.Find(s => s.Id == b.UpdateStream.Id)); res.Streams.ForEach(s => s.LatestBuild.UpdateStream = res.Streams.Find(s2 => s2.Id == s.LatestBuild.UpdateStream.Id)); - listing.ShowListing(res.Builds); + builds = res.Builds; + ShowListing(); badges.Populate(res.Streams); }; @@ -139,10 +141,8 @@ namespace osu.Game.Overlays public void ShowListing() { header.ShowListing(); - - content.Hide(); badges.Current.Value = null; - listing.Show(); + content.Child = new ChangelogListing(builds); } /// @@ -162,13 +162,8 @@ namespace osu.Game.Overlays header.ShowBuild(build.UpdateStream.DisplayName, build.DisplayVersion); badges.Current.Value = build.UpdateStream; - listing.Hide(); - - void displayBuild(APIChangelogBuild populatedBuild) - { - content.Show(); - content.ShowBuild(populatedBuild); - } + void displayBuild(APIChangelogBuild populatedBuild) => + content.Child = new ChangelogBuild(populatedBuild); if (build.Versions != null) displayBuild(build); From c5c1896a11e9acebe93e30b57ae7d43e6f374f07 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 May 2019 17:20:50 +0900 Subject: [PATCH 0746/2854] Use new colour palette --- osu.Game/Overlays/ChangelogOverlay.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 6ef239733d..c9a9aee895 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -33,10 +33,10 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load(AudioManager audio, OsuColour colour) { - Waves.FirstWaveColour = colour.Violet; - Waves.SecondWaveColour = OsuColour.FromHex(@"8F03BF"); - Waves.ThirdWaveColour = OsuColour.FromHex(@"600280"); - Waves.FourthWaveColour = OsuColour.FromHex(@"300140"); + Waves.FirstWaveColour = colour.GreyVioletLight; + Waves.SecondWaveColour = colour.GreyViolet; + Waves.ThirdWaveColour = colour.GreyVioletDark; + Waves.FourthWaveColour = colour.GreyVioletDarker; Children = new Drawable[] { From c41ec20236a7c57da0a62dfc53bd162c49e6abad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 May 2019 17:47:28 +0900 Subject: [PATCH 0747/2854] Improve load and switch logic between views --- .../Online/TestSceneChangelogOverlay.cs | 2 + osu.Game/Overlays/Changelog/ChangelogBuild.cs | 24 +++++- .../Overlays/Changelog/ChangelogContent.cs | 2 +- osu.Game/Overlays/ChangelogOverlay.cs | 75 +++++++++++-------- 4 files changed, 68 insertions(+), 35 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index 0e4f31c217..e1cb6e6a85 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -22,6 +22,8 @@ namespace osu.Game.Tests.Visual.Online typeof(StreamBadge), typeof(ChangelogHeader), typeof(ChangelogContent), + typeof(ChangelogListing), + typeof(ChangelogBuild), typeof(ChangelogContentGroup), typeof(Breadcrumb), typeof(BreadcrumbListing), diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 0f3e76021b..0af117c11e 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -1,14 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Threading; +using System.Threading.Tasks; using osu.Framework.Allocation; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Changelog { public class ChangelogBuild : ChangelogContent { - private readonly APIChangelogBuild changelogBuild; + private APIChangelogBuild changelogBuild; public ChangelogBuild(APIChangelogBuild changelogBuild) { @@ -16,8 +20,24 @@ namespace osu.Game.Overlays.Changelog } [BackgroundDependencyLoader] - private void load() + private void load(CancellationToken? cancellation, IAPIProvider api) { + var req = new GetChangelogBuildRequest(changelogBuild.UpdateStream.Name, changelogBuild.Version); + bool complete = false; + + req.Success += res => + { + changelogBuild = res; + complete = true; + }; + + req.Failure += _ => complete = true; + + api.Queue(req); + + while (!complete && cancellation?.IsCancellationRequested != true) + Task.Delay(1); + var changelogContentGroup = new ChangelogContentGroup(changelogBuild); changelogContentGroup.GenerateText(changelogBuild.ChangelogEntries); changelogContentGroup.UpdateChevronTooltips(changelogBuild.Versions.Previous?.DisplayVersion, diff --git a/osu.Game/Overlays/Changelog/ChangelogContent.cs b/osu.Game/Overlays/Changelog/ChangelogContent.cs index 92c33567a8..f8d5bbd66c 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContent.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContent.cs @@ -10,7 +10,7 @@ namespace osu.Game.Overlays.Changelog { public class ChangelogContent : FillFlowContainer { - public event Action BuildSelected; + public Action BuildSelected; public void SelectBuild(APIChangelogBuild build) => BuildSelected?.Invoke(build); diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index c9a9aee895..b8449f618d 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; +using System.Threading; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -24,7 +26,7 @@ namespace osu.Game.Overlays private BadgeDisplay badges; - private Container content; + private Container content; private SampleChannel sampleBack; @@ -58,7 +60,7 @@ namespace osu.Game.Overlays { header = new ChangelogHeader(), badges = new BadgeDisplay(), - content = new Container + content = new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -103,7 +105,7 @@ namespace osu.Game.Overlays switch (action) { case GlobalAction.Back: - if (content.Child is ChangelogContent) + if (content.Child is ChangelogListing) { State = Visibility.Hidden; } @@ -119,30 +121,14 @@ namespace osu.Game.Overlays return false; } - private void fetchListing() - { - header.ShowListing(); - - var req = new GetChangelogRequest(); - req.Success += res => - { - // remap streams to builds to ensure model equality - res.Builds.ForEach(b => b.UpdateStream = res.Streams.Find(s => s.Id == b.UpdateStream.Id)); - res.Streams.ForEach(s => s.LatestBuild.UpdateStream = res.Streams.Find(s2 => s2.Id == s.LatestBuild.UpdateStream.Id)); - - builds = res.Builds; - ShowListing(); - badges.Populate(res.Streams); - }; - - API.Queue(req); - } - public void ShowListing() { + if (content.Children.FirstOrDefault() is ChangelogListing) + return; + header.ShowListing(); badges.Current.Value = null; - content.Child = new ChangelogListing(builds); + loadContent(new ChangelogListing(builds)); } /// @@ -162,17 +148,42 @@ namespace osu.Game.Overlays header.ShowBuild(build.UpdateStream.DisplayName, build.DisplayVersion); badges.Current.Value = build.UpdateStream; - void displayBuild(APIChangelogBuild populatedBuild) => - content.Child = new ChangelogBuild(populatedBuild); + loadContent(new ChangelogBuild(build)); + } - if (build.Versions != null) - displayBuild(build); - else + private void fetchListing() + { + var req = new GetChangelogRequest(); + req.Success += res => { - var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); - req.Success += displayBuild; - API.Queue(req); - } + // remap streams to builds to ensure model equality + res.Builds.ForEach(b => b.UpdateStream = res.Streams.Find(s => s.Id == b.UpdateStream.Id)); + res.Streams.ForEach(s => s.LatestBuild.UpdateStream = res.Streams.Find(s2 => s2.Id == s.LatestBuild.UpdateStream.Id)); + + builds = res.Builds; + badges.Populate(res.Streams); + + ShowListing(); + }; + + API.Queue(req); + } + + private CancellationTokenSource loadContentTask; + + private void loadContent(ChangelogContent newContent) + { + content.FadeTo(0.2f, 300, Easing.OutQuint); + + loadContentTask?.Cancel(); + + LoadComponentAsync(newContent, c => + { + content.FadeIn(300, Easing.OutQuint); + + c.BuildSelected = ShowBuild; + content.Child = c; + }, (loadContentTask = new CancellationTokenSource()).Token); } } } From 0b076c9ca0807f2c4e2e38c17c5b85064cee5464 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 May 2019 17:49:05 +0900 Subject: [PATCH 0748/2854] Only fetch after initial pop in --- osu.Game/Overlays/ChangelogOverlay.cs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index b8449f618d..f4c0338436 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -82,22 +82,12 @@ namespace osu.Game.Overlays sampleBack = audio.Sample.Get(@"UI/generic-select-soft"); } - protected override void LoadComplete() - { - base.LoadComplete(); - fetchListing(); - } - protected override void PopIn() { base.PopIn(); - FadeEdgeEffectTo(0.25f, WaveContainer.APPEAR_DURATION, Easing.In); - } - protected override void PopOut() - { - base.PopOut(); - FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); + if (!initialFetchPerformed) + fetchListing(); } public override bool OnPressed(GlobalAction action) @@ -151,8 +141,12 @@ namespace osu.Game.Overlays loadContent(new ChangelogBuild(build)); } + private bool initialFetchPerformed; + private void fetchListing() { + initialFetchPerformed = true; + var req = new GetChangelogRequest(); req.Success += res => { @@ -165,6 +159,7 @@ namespace osu.Game.Overlays ShowListing(); }; + req.Failure += _ => initialFetchPerformed = false; API.Queue(req); } From dbc42fd59e1b1b786a84b8d672b2502d5924ce95 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 May 2019 18:09:11 +0900 Subject: [PATCH 0749/2854] Remove StreamColour class and implement locally --- osu.Game/Graphics/StreamColour.cs | 43 ------------------- .../API/Requests/Responses/APIUpdateStream.cs | 32 ++++++++++++++ .../Changelog/ChangelogContentGroup.cs | 4 +- osu.Game/Overlays/Changelog/StreamBadge.cs | 2 +- 4 files changed, 35 insertions(+), 46 deletions(-) delete mode 100644 osu.Game/Graphics/StreamColour.cs diff --git a/osu.Game/Graphics/StreamColour.cs b/osu.Game/Graphics/StreamColour.cs deleted file mode 100644 index b7740c3be0..0000000000 --- a/osu.Game/Graphics/StreamColour.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics.Colour; -using System.Collections.Generic; -using osuTK.Graphics; - -namespace osu.Game.Graphics -{ - public class StreamColour - { - public static readonly Color4 STABLE = new Color4(102, 204, 255, 255); - public static readonly Color4 STABLEFALLBACK = new Color4(34, 153, 187, 255); - public static readonly Color4 BETA = new Color4(255, 221, 85, 255); - public static readonly Color4 CUTTINGEDGE = new Color4(238, 170, 0, 255); - public static readonly Color4 LAZER = new Color4(237, 18, 33, 255); - public static readonly Color4 WEB = new Color4(136, 102, 238, 255); - - private static readonly Dictionary colours = new Dictionary - { - { "stable40", STABLE }, - { "Stable", STABLE }, - { "stable", STABLEFALLBACK }, - { "Stable Fallback", STABLEFALLBACK }, - { "beta40", BETA }, - { "Beta", BETA }, - { "cuttingedge", CUTTINGEDGE }, - { "Cutting Edge", CUTTINGEDGE }, - { "lazer", LAZER }, - { "Lazer", LAZER }, - { "web", WEB }, - }; - - public static ColourInfo FromStreamName(string name) - { - if (!string.IsNullOrEmpty(name)) - if (colours.TryGetValue(name, out ColourInfo colour)) - return colour; - - return new Color4(0, 0, 0, 255); - } - } -} diff --git a/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs b/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs index 25f1a413d6..4c65b562dd 100644 --- a/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs +++ b/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs @@ -3,6 +3,8 @@ using System; using Newtonsoft.Json; +using osu.Framework.Graphics.Colour; +using osuTK.Graphics; namespace osu.Game.Online.API.Requests.Responses { @@ -30,5 +32,35 @@ namespace osu.Game.Online.API.Requests.Responses return Id == other.Id; } + + public ColourInfo Colour + { + get + { + switch (Name) + { + case "stable40": + return new Color4(102, 204, 255, 255); + + case "stable": + return new Color4(34, 153, 187, 255); + + case "beta40": + return new Color4(255, 221, 85, 255); + + case "cuttingedge": + return new Color4(238, 170, 0, 255); + + case "lazer": + return new Color4(237, 18, 33, 255); + + case "web": + return new Color4(136, 102, 238, 255); + + default: + return new Color4(0, 0, 0, 255); + } + } + } } } diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 9f3da7eade..52186ec37c 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Changelog { Text = build.DisplayVersion, Font = OsuFont.GetFont(weight: FontWeight.Light, size: 24), - Colour = StreamColour.FromStreamName(build.UpdateStream.Name), + Colour = build.UpdateStream.Colour, }, } }, @@ -156,7 +156,7 @@ namespace osu.Game.Overlays.Changelog { Text = build.DisplayVersion, Font = OsuFont.GetFont(weight: FontWeight.Light, size: 19), - Colour = StreamColour.FromStreamName(build.UpdateStream.Name), + Colour = build.UpdateStream.Colour, }, }, } diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index 2be6aba926..61bdf9f537 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -73,7 +73,7 @@ namespace osu.Game.Overlays.Changelog lineBadge = new LineBadge(false) { Anchor = Anchor.TopCentre, - Colour = StreamColour.FromStreamName(stream.Name), + Colour = stream.Colour, UncollapsedSize = 4, CollapsedSize = 2, }, From 9bc3aa3d467677a7516e41041edb1fbee024a4b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 May 2019 19:15:25 +0900 Subject: [PATCH 0750/2854] Move new classes for now --- .../UserInterface => Overlays/Changelog/Components}/LineBadge.cs | 0 .../Changelog/Components}/TooltipIconButton.cs | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename osu.Game/{Graphics/UserInterface => Overlays/Changelog/Components}/LineBadge.cs (100%) rename osu.Game/{Graphics/UserInterface => Overlays/Changelog/Components}/TooltipIconButton.cs (100%) diff --git a/osu.Game/Graphics/UserInterface/LineBadge.cs b/osu.Game/Overlays/Changelog/Components/LineBadge.cs similarity index 100% rename from osu.Game/Graphics/UserInterface/LineBadge.cs rename to osu.Game/Overlays/Changelog/Components/LineBadge.cs diff --git a/osu.Game/Graphics/UserInterface/TooltipIconButton.cs b/osu.Game/Overlays/Changelog/Components/TooltipIconButton.cs similarity index 100% rename from osu.Game/Graphics/UserInterface/TooltipIconButton.cs rename to osu.Game/Overlays/Changelog/Components/TooltipIconButton.cs From 2d56413e3509401ede5fb916ef57adf220cff84c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 May 2019 19:15:39 +0900 Subject: [PATCH 0751/2854] Update namespaces --- osu.Game.Tests/Visual/UserInterface/TestSceneLineBadge.cs | 2 +- osu.Game/Overlays/Changelog/ChangelogContentGroup.cs | 2 +- osu.Game/Overlays/Changelog/Components/LineBadge.cs | 2 +- osu.Game/Overlays/Changelog/Components/TooltipIconButton.cs | 6 +++--- osu.Game/Overlays/Changelog/Header/Breadcrumb.cs | 2 +- osu.Game/Overlays/Changelog/StreamBadge.cs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLineBadge.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLineBadge.cs index 9f80cd40cf..14e7b45ee6 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLineBadge.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLineBadge.cs @@ -4,7 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Changelog.Components; using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs index 52186ec37c..44c2c4cf9c 100644 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs @@ -6,11 +6,11 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; using System; using System.Collections.Generic; using System.Text.RegularExpressions; +using osu.Game.Overlays.Changelog.Components; using osuTK; using osuTK.Graphics; diff --git a/osu.Game/Overlays/Changelog/Components/LineBadge.cs b/osu.Game/Overlays/Changelog/Components/LineBadge.cs index fc87acab0a..84cd712eef 100644 --- a/osu.Game/Overlays/Changelog/Components/LineBadge.cs +++ b/osu.Game/Overlays/Changelog/Components/LineBadge.cs @@ -4,7 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; -namespace osu.Game.Graphics.UserInterface +namespace osu.Game.Overlays.Changelog.Components { /// /// A simple rounded expandable line. Set its diff --git a/osu.Game/Overlays/Changelog/Components/TooltipIconButton.cs b/osu.Game/Overlays/Changelog/Components/TooltipIconButton.cs index 7287794f89..5721481685 100644 --- a/osu.Game/Overlays/Changelog/Components/TooltipIconButton.cs +++ b/osu.Game/Overlays/Changelog/Components/TooltipIconButton.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -8,12 +9,11 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; -using System; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; using osuTK; -namespace osu.Game.Graphics.UserInterface +namespace osu.Game.Overlays.Changelog.Components { /// /// An icon with an action upon click that can be disabled. diff --git a/osu.Game/Overlays/Changelog/Header/Breadcrumb.cs b/osu.Game/Overlays/Changelog/Header/Breadcrumb.cs index ae902043e3..c9b1430b5d 100644 --- a/osu.Game/Overlays/Changelog/Header/Breadcrumb.cs +++ b/osu.Game/Overlays/Changelog/Header/Breadcrumb.cs @@ -9,9 +9,9 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; -using osu.Game.Graphics.UserInterface; using System; using osu.Game.Graphics; +using osu.Game.Overlays.Changelog.Components; namespace osu.Game.Overlays.Changelog.Header { diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index 61bdf9f537..ce2ae8baf6 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -9,9 +9,9 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; using System; +using osu.Game.Overlays.Changelog.Components; using osuTK.Graphics; namespace osu.Game.Overlays.Changelog From ccbf3ff81209ffcaa637390d4486faf794739411 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 May 2019 22:35:23 +0900 Subject: [PATCH 0752/2854] Update framework --- .../Visual/Gameplay/TestScenePlayerReferenceLeaking.cs | 8 ++++++-- osu.Game/osu.Game.csproj | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs index d941ad54c0..c75fb2567b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs @@ -24,7 +24,9 @@ namespace osu.Game.Tests.Visual.Gameplay GC.WaitForPendingFinalizers(); int count = 0; - workingWeakReferences.ForEachAlive(_ => count++); + foreach (var unused in workingWeakReferences) + count++; + return count == 1; }); @@ -34,7 +36,9 @@ namespace osu.Game.Tests.Visual.Gameplay GC.WaitForPendingFinalizers(); int count = 0; - playerWeakReferences.ForEachAlive(_ => count++); + foreach (var unused in playerWeakReferences) + count++; + return count == 1; }); } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 66d298f8c1..fcfaf71d6a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + From a538c94feddce714e266403df7ab881194551ff8 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 17 May 2019 21:40:46 +0300 Subject: [PATCH 0753/2854] Update NuGet packages --- .../osu.Game.Rulesets.Catch.Tests.csproj | 4 ++-- .../osu.Game.Rulesets.Mania.Tests.csproj | 4 ++-- .../osu.Game.Rulesets.Osu.Tests.csproj | 4 ++-- .../osu.Game.Rulesets.Taiko.Tests.csproj | 4 ++-- osu.Game.Tests/osu.Game.Tests.csproj | 4 ++-- osu.Game/osu.Game.csproj | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 3f8b3bf086..265ecb7688 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -2,8 +2,8 @@ - - + + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index fd17285a38..dbade6ff8d 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -2,8 +2,8 @@ - - + + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index 8c31db9a7d..a99a93c3e9 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -2,8 +2,8 @@ - - + + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index 72ce6c947b..216cc0222f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -2,8 +2,8 @@ - - + + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 938e1ae0f8..11d70ee7be 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -3,8 +3,8 @@ - - + + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 66d298f8c1..7c005980c6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -17,7 +17,7 @@ - + From 9106200c65edde6363e0542b2b6f79634936980d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 May 2019 12:08:22 +0900 Subject: [PATCH 0754/2854] Bump framework version --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index fcfaf71d6a..0cff6117d8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index ccec475d98..d947d0dfb9 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 5e499cf55d1f2ea5378b939f1a0c96c221535b54 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 May 2019 12:45:06 +0900 Subject: [PATCH 0755/2854] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index bf76fbcc94..e18f4b8771 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,7 +14,7 @@ - + From d407981ae4f065cda08b1c95eff155ef71c22145 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 May 2019 12:58:13 +0900 Subject: [PATCH 0756/2854] Increase fastlane processing interval This seems to be getting longer as we go. May be related to having many versions released under one testflight build? --- fastlane/Fastfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 3f64bcdf19..48c16caf0f 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -35,7 +35,7 @@ platform :ios do changelog.gsub!('$BUILD_ID', options[:build]) pilot( - wait_processing_interval: 900, + wait_processing_interval: 1800, changelog: changelog, ipa: './osu.iOS/bin/iPhone/Release/osu.iOS.ipa' ) From bc462532993d2abd5e7c071c1d8ba98d50061231 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 May 2019 20:18:07 +0900 Subject: [PATCH 0757/2854] Update test case naming --- osu.Game.Tournament.Tests/LadderTestCase.cs | 2 +- osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs | 2 +- osu.Game.Tournament.Tests/TestCaseGameplay.cs | 2 +- osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs | 2 +- osu.Game.Tournament.Tests/TestCaseMatchPairings.cs | 2 +- osu.Game.Tournament.Tests/TestCaseSceneManager.cs | 2 +- osu.Game.Tournament.Tests/TestCaseSchedule.cs | 2 +- osu.Game.Tournament.Tests/TestCaseShowcase.cs | 2 +- osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tournament.Tests/LadderTestCase.cs b/osu.Game.Tournament.Tests/LadderTestCase.cs index fc827150bd..450626fd43 100644 --- a/osu.Game.Tournament.Tests/LadderTestCase.cs +++ b/osu.Game.Tournament.Tests/LadderTestCase.cs @@ -6,7 +6,7 @@ using osu.Game.Tests.Visual; namespace osu.Game.Tournament.Tests { - public class LadderTestCase : OsuTestCase + public class LadderTestCase : OsuTestScene { [Resolved] protected LadderInfo Ladder { get; private set; } diff --git a/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs b/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs index 0a23b1e82f..6ebdcc511b 100644 --- a/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs +++ b/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs @@ -15,7 +15,7 @@ using osu.Game.Tournament.Components; namespace osu.Game.Tournament.Tests { - public class TestCaseBeatmapPanel : OsuTestCase + public class TestCaseBeatmapPanel : OsuTestScene { [Resolved] private IAPIProvider api { get; set; } diff --git a/osu.Game.Tournament.Tests/TestCaseGameplay.cs b/osu.Game.Tournament.Tests/TestCaseGameplay.cs index 8e435de5e6..c881f09cb9 100644 --- a/osu.Game.Tournament.Tests/TestCaseGameplay.cs +++ b/osu.Game.Tournament.Tests/TestCaseGameplay.cs @@ -9,7 +9,7 @@ using osu.Game.Tournament.Screens.Gameplay; namespace osu.Game.Tournament.Tests { - public class TestCaseGameplay : OsuTestCase + public class TestCaseGameplay : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { diff --git a/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs b/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs index 0c3c189cf5..cec6095598 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs @@ -14,7 +14,7 @@ using osuTK; namespace osu.Game.Tournament.Tests { - public class TestCaseMatchChatDisplay : OsuTestCase + public class TestCaseMatchChatDisplay : OsuTestScene { private readonly Channel testChannel = new Channel(); diff --git a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs index 2dce0c6017..e7a329d35f 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs @@ -11,7 +11,7 @@ using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament.Tests { - public class TestCaseMatchPairings : OsuTestCase + public class TestCaseMatchPairings : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { diff --git a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs index 97d2018e3d..7c1b794e16 100644 --- a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseSceneManager.cs @@ -8,7 +8,7 @@ using osu.Game.Tournament.Screens; namespace osu.Game.Tournament.Tests { - public class TestCaseSceneManager : OsuTestCase + public class TestCaseSceneManager : OsuTestScene { [BackgroundDependencyLoader] private void load(Storage storage) diff --git a/osu.Game.Tournament.Tests/TestCaseSchedule.cs b/osu.Game.Tournament.Tests/TestCaseSchedule.cs index f9dc447077..b5a80d7bee 100644 --- a/osu.Game.Tournament.Tests/TestCaseSchedule.cs +++ b/osu.Game.Tournament.Tests/TestCaseSchedule.cs @@ -9,7 +9,7 @@ using osu.Game.Tournament.Screens.Schedule; namespace osu.Game.Tournament.Tests { - public class TestCaseSchedule : OsuTestCase + public class TestCaseSchedule : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { diff --git a/osu.Game.Tournament.Tests/TestCaseShowcase.cs b/osu.Game.Tournament.Tests/TestCaseShowcase.cs index 51877cdfb6..c0816e3594 100644 --- a/osu.Game.Tournament.Tests/TestCaseShowcase.cs +++ b/osu.Game.Tournament.Tests/TestCaseShowcase.cs @@ -9,7 +9,7 @@ using osu.Game.Tournament.Screens.Showcase; namespace osu.Game.Tournament.Tests { - public class TestCaseShowcase : OsuTestCase + public class TestCaseShowcase : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index 4a65846d68..b76fc261f0 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -6,7 +6,7 @@ - + From 30e36627cf37f1e8784764df7857dccce129533d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 May 2019 21:37:46 +0900 Subject: [PATCH 0758/2854] Remove redundant code --- .../Screens/Gameplay/Components/MatchHeader.cs | 2 +- .../Screens/Gameplay/Components/MatchScoreDisplay.cs | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index 51467842eb..10c1c006cf 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components public class MatchHeader : Container { [BackgroundDependencyLoader] - private void load(LadderInfo ladder) + private void load() { RelativeSizeAxes = Axes.X; Height = 95; diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs index f8b887b952..62a785398f 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.IPC; -using osu.Game.Tournament.Screens.Ladder.Components; using osuTK.Graphics; namespace osu.Game.Tournament.Screens.Gameplay.Components @@ -22,8 +21,6 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components private const float bar_height = 20; - private readonly Bindable currentMatch = new Bindable(); - private readonly BindableInt score1 = new BindableInt(); private readonly BindableInt score2 = new BindableInt(); @@ -78,8 +75,6 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components [BackgroundDependencyLoader] private void load(LadderInfo ladder, MatchIPCInfo ipc) { - currentMatch.BindTo(ladder.CurrentMatch); - score1.BindValueChanged(_ => updateScores()); score1.BindTo(ipc.Score1); From 069245e7ab31685389cb3e591c4319136aaa2d58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 May 2019 21:40:02 +0900 Subject: [PATCH 0759/2854] Update header colour to match TWC --- osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index 10c1c006cf..22aa6995cf 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -210,7 +210,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components { new Box { - Colour = new Color4(95, 41, 60, 255), + Colour = new Color4(47, 71, 67, 255), RelativeSizeAxes = Axes.Both, }, new OsuSpriteText From f81c66db63ce5081b545da7e7f6bbbf53beb82b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 May 2019 21:46:03 +0900 Subject: [PATCH 0760/2854] Hotfix to fix chat scrolling to end --- osu.Game/Overlays/Chat/DrawableChannel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index aec78b962f..6c30b8cc36 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -81,7 +81,7 @@ namespace osu.Game.Overlays.Chat ChatLineFlow.AddRange(displayMessages.Select(CreateChatLine)); - if (scroll.IsScrolledToEnd(10) || !ChatLineFlow.Children.Any() || newMessages.Any(m => m is LocalMessage)) + //if (scroll.IsScrolledToEnd(10) || !ChatLineFlow.Children.Any() || newMessages.Any(m => m is LocalMessage)) scrollToEnd(); var staleMessages = ChatLineFlow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray(); From 3fa1545ea445f7fe8aca58153e03a839d2a74091 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 19 May 2019 02:09:08 +0900 Subject: [PATCH 0761/2854] Huge refactor pass focusing on ChangelogContent --- .../Online/TestSceneChangelogOverlay.cs | 2 +- .../Graphics/Containers/OsuHoverContainer.cs | 3 +- .../BeatmapSet/Scores/TopScoreUserSection.cs | 2 +- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 176 +++++++++-- .../Changelog/ChangelogContentGroup.cs | 280 ------------------ .../Overlays/Changelog/ChangelogListing.cs | 32 +- .../Changelog/ChangelogSingleBuild.cs | 118 ++++++++ .../Overlays/Changelog/Header/Breadcrumb.cs | 3 +- osu.Game/Overlays/Changelog/StreamBadge.cs | 7 +- osu.Game/Overlays/ChangelogOverlay.cs | 2 +- 10 files changed, 294 insertions(+), 331 deletions(-) delete mode 100644 osu.Game/Overlays/Changelog/ChangelogContentGroup.cs create mode 100644 osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index e1cb6e6a85..4ac5514019 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -23,8 +23,8 @@ namespace osu.Game.Tests.Visual.Online typeof(ChangelogHeader), typeof(ChangelogContent), typeof(ChangelogListing), + typeof(ChangelogSingleBuild), typeof(ChangelogBuild), - typeof(ChangelogContentGroup), typeof(Breadcrumb), typeof(BreadcrumbListing), typeof(BreadcrumbRelease), diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs index d5ae7cba57..b1fe1e81f1 100644 --- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs +++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs @@ -22,7 +22,8 @@ namespace osu.Game.Graphics.Containers protected override bool OnHover(HoverEvent e) { - EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint)); + if (Action != null) + EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint)); return base.OnHover(e); } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index e70bf4c572..89da0fc254 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -83,7 +83,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, }, - date = new SpriteText + date = new OsuSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 0af117c11e..09706a419e 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -1,50 +1,168 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Threading; -using System.Threading.Tasks; -using osu.Framework.Allocation; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests.Responses; +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using osu.Game.Graphics.Sprites; +using osuTK.Graphics; namespace osu.Game.Overlays.Changelog { - public class ChangelogBuild : ChangelogContent + public class ChangelogBuild : FillFlowContainer { - private APIChangelogBuild changelogBuild; + public Action SelectBuild; - public ChangelogBuild(APIChangelogBuild changelogBuild) + protected readonly APIChangelogBuild Build; + + public readonly FillFlowContainer ChangelogEntries; + + public ChangelogBuild(APIChangelogBuild build) { - this.changelogBuild = changelogBuild; - } + Build = build; - [BackgroundDependencyLoader] - private void load(CancellationToken? cancellation, IAPIProvider api) - { - var req = new GetChangelogBuildRequest(changelogBuild.UpdateStream.Name, changelogBuild.Version); - bool complete = false; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + Padding = new MarginPadding { Horizontal = 70 }; - req.Success += res => + Children = new Drawable[] { - changelogBuild = res; - complete = true; + CreateHeader(), + ChangelogEntries = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + }, }; - req.Failure += _ => complete = true; + var categories = new SortedDictionary>(); - api.Queue(req); + // sort entries by category + foreach (APIChangelogEntry entry in build.ChangelogEntries) + { + if (!categories.ContainsKey(entry.Category)) + categories.Add(entry.Category, new List { entry }); + else + categories[entry.Category].Add(entry); + } - while (!complete && cancellation?.IsCancellationRequested != true) - Task.Delay(1); + foreach (KeyValuePair> category in categories) + { + ChangelogEntries.Add(new OsuSpriteText + { + Text = category.Key, + Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 24), + Margin = new MarginPadding { Top = 35, Bottom = 15 }, + }); - var changelogContentGroup = new ChangelogContentGroup(changelogBuild); - changelogContentGroup.GenerateText(changelogBuild.ChangelogEntries); - changelogContentGroup.UpdateChevronTooltips(changelogBuild.Versions.Previous?.DisplayVersion, - changelogBuild.Versions.Next?.DisplayVersion); - changelogContentGroup.BuildSelected += SelectBuild; + foreach (APIChangelogEntry entry in category.Value) + { + LinkFlowContainer title = new LinkFlowContainer + { + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Vertical = 5 }, + }; - Add(changelogContentGroup); + title.AddIcon(FontAwesome.Solid.Check, t => + { + t.Font = OsuFont.GetFont(size: 12); + t.Padding = new MarginPadding { Left = -17, Right = 5 }; + }); + + title.AddText(entry.Title, t => { t.Font = OsuFont.GetFont(size: 18); }); + + if (!string.IsNullOrEmpty(entry.Repository)) + { + title.AddText(" (", t => t.Font = OsuFont.GetFont(size: 18)); + title.AddLink($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", + entry.GithubUrl, Online.Chat.LinkAction.External, null, + null, t => { t.Font = OsuFont.GetFont(size: 18); }); + title.AddText(")", t => t.Font = OsuFont.GetFont(size: 18)); + } + + title.AddText(" by ", t => t.Font = OsuFont.GetFont(size: 14)); + + if (entry.GithubUser.GithubUrl != null) + title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, + Online.Chat.LinkAction.External, null, null, + t => t.Font = OsuFont.GetFont(size: 14)); + else + title.AddText(entry.GithubUser.DisplayName, t => t.Font = OsuFont.GetFont(size: 12)); + + ChangelogEntries.Add(title); + + if (!string.IsNullOrEmpty(entry.MessageHtml)) + { + TextFlowContainer messageContainer = new TextFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }; + + // todo: use markdown parsing once API returns markdown + messageContainer.AddText(Regex.Replace(entry.MessageHtml, @"<(.|\n)*?>", string.Empty), t => + { + t.Font = OsuFont.GetFont(size: 12); + t.Colour = new Color4(235, 184, 254, 255); + }); + + ChangelogEntries.Add(messageContainer); + } + } + } } + + protected virtual FillFlowContainer CreateHeader() => new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Top = 20 }, + Children = new Drawable[] + { + new OsuHoverContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Action = () => SelectBuild?.Invoke(Build), + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Horizontal = 40 }, + Children = new[] + { + new OsuSpriteText + { + Text = Build.UpdateStream.DisplayName, + Font = OsuFont.GetFont(weight: FontWeight.Medium, size: 19), + }, + new OsuSpriteText + { + Text = " ", + Font = OsuFont.GetFont(weight: FontWeight.Medium, size: 19), + }, + new OsuSpriteText + { + Text = Build.DisplayVersion, + Font = OsuFont.GetFont(weight: FontWeight.Light, size: 19), + Colour = Build.UpdateStream.Colour, + }, + } + } + }, + } + }; } } diff --git a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs b/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs deleted file mode 100644 index 44c2c4cf9c..0000000000 --- a/osu.Game/Overlays/Changelog/ChangelogContentGroup.cs +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Online.API.Requests.Responses; -using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; -using osu.Game.Overlays.Changelog.Components; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Overlays.Changelog -{ - public class ChangelogContentGroup : FillFlowContainer - { - private readonly TooltipIconButton chevronPrevious, chevronNext; - - private readonly SortedDictionary> categories = - new SortedDictionary>(); - - public event Action BuildSelected; - - public readonly FillFlowContainer ChangelogEntries; - - public ChangelogContentGroup(APIChangelogBuild build) - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Direction = FillDirection.Vertical; - Padding = new MarginPadding { Horizontal = 70 }; - Children = new Drawable[] - { - new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Margin = new MarginPadding { Top = 20 }, - Children = new Drawable[] - { - chevronPrevious = new TooltipIconButton - { - IsEnabled = false, - Icon = FontAwesome.Solid.ChevronLeft, - Size = new Vector2(24), - Action = () => - { - BuildSelected?.Invoke(build.Versions.Previous); - chevronPrevious.IsEnabled = false; - }, - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Horizontal = 40 }, - Children = new[] - { - new SpriteText - { - Text = build.UpdateStream.DisplayName, - Font = OsuFont.GetFont(weight: FontWeight.Medium, size: 24), - }, - new SpriteText - { - Text = " ", - Font = OsuFont.GetFont(weight: FontWeight.Medium, size: 24), - }, - new SpriteText - { - Text = build.DisplayVersion, - Font = OsuFont.GetFont(weight: FontWeight.Light, size: 24), - Colour = build.UpdateStream.Colour, - }, - } - }, - chevronNext = new TooltipIconButton - { - IsEnabled = false, - Icon = FontAwesome.Solid.ChevronRight, - Size = new Vector2(24), - Action = () => - { - BuildSelected?.Invoke(build.Versions.Next); - chevronNext.IsEnabled = false; - }, - }, - } - }, - new SpriteText - { - // do we need .ToUniversalTime() here? - // also, this should be a temporary solution to weekdays in >localized< date strings - Text = build.CreatedAt.Date.ToLongDateString().Replace(build.CreatedAt.ToString("dddd") + ", ", ""), - Font = OsuFont.GetFont(weight: FontWeight.Medium, size: 14), - Colour = OsuColour.FromHex(@"FD5"), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Margin = new MarginPadding { Top = 5 }, - }, - ChangelogEntries = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - }, - }; - } - - public ChangelogContentGroup(APIChangelogBuild build, bool newDate) - { - OsuHoverContainer clickableBuildText; - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Direction = FillDirection.Vertical; - Padding = new MarginPadding { Horizontal = 70 }; - Children = new Drawable[] - { - new SpriteText - { - // do we need .ToUniversalTime() here? - // also, this should be a temporary solution to weekdays in >localized< date strings - Text = build.CreatedAt.Date.ToLongDateString().Replace(build.CreatedAt.ToString("dddd") + ", ", ""), - Font = OsuFont.GetFont(weight: FontWeight.Light, size: 24), - Colour = OsuColour.FromHex(@"FD5"), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Margin = new MarginPadding { Top = 20 }, - Alpha = newDate ? 1 : 0, - }, - clickableBuildText = new OsuHoverContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = 20 }, - Action = () => BuildSelected?.Invoke(build), - Child = new FillFlowContainer - { - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5), - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new SpriteText - { - Text = build.UpdateStream.DisplayName, - Font = OsuFont.GetFont(weight: FontWeight.Medium, size: 19), - }, - new SpriteText - { - Text = build.DisplayVersion, - Font = OsuFont.GetFont(weight: FontWeight.Light, size: 19), - Colour = build.UpdateStream.Colour, - }, - }, - } - }, - ChangelogEntries = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - }, - }; - - // we may not want double clicks, - // can be clicked again only after a delay - clickableBuildText.Action += () => - { - clickableBuildText.Action = null; - clickableBuildText.FadeTo(0.5f, 500); - Scheduler.AddDelayed(() => - { - clickableBuildText.Action = () => BuildSelected?.Invoke(build); - clickableBuildText.FadeIn(500); - }, 2000); - }; - } - - public void UpdateChevronTooltips(string previousVersion, string nextVersion) - { - if (!string.IsNullOrEmpty(previousVersion)) - { - chevronPrevious.TooltipText = previousVersion; - chevronPrevious.IsEnabled = true; - } - - if (!string.IsNullOrEmpty(nextVersion)) - { - chevronNext.TooltipText = nextVersion; - chevronNext.IsEnabled = true; - } - } - - public void GenerateText(List changelogEntries) - { - // sort entries by category - foreach (APIChangelogEntry entry in changelogEntries) - { - if (!categories.ContainsKey(entry.Category)) - categories.Add(entry.Category, new List { entry }); - else - categories[entry.Category].Add(entry); - } - - foreach (KeyValuePair> category in categories) - { - ChangelogEntries.Add(new SpriteText - { - Text = category.Key, - Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 24), - Margin = new MarginPadding { Top = 35, Bottom = 15 }, - }); - - foreach (APIChangelogEntry entry in category.Value) - { - LinkFlowContainer title = new LinkFlowContainer - { - Direction = FillDirection.Full, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Vertical = 5 }, - }; - - title.AddIcon(FontAwesome.Solid.Check, t => - { - t.Font = OsuFont.GetFont(size: 12); - t.Padding = new MarginPadding { Left = -17, Right = 5 }; - }); - - title.AddText(entry.Title, t => { t.Font = OsuFont.GetFont(size: 18); }); - - if (!string.IsNullOrEmpty(entry.Repository)) - { - title.AddText(" (", t => t.Font = OsuFont.GetFont(size: 18)); - title.AddLink($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", - entry.GithubUrl, Online.Chat.LinkAction.External, null, - null, t => { t.Font = OsuFont.GetFont(size: 18); }); - title.AddText(")", t => t.Font = OsuFont.GetFont(size: 18)); - } - - title.AddText(" by ", t => t.Font = OsuFont.GetFont(size: 14)); - - if (entry.GithubUser.GithubUrl != null) - title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, - Online.Chat.LinkAction.External, null, null, - t => t.Font = OsuFont.GetFont(size: 14)); - else - title.AddText(entry.GithubUser.DisplayName, t => t.Font = OsuFont.GetFont(size: 12)); - - ChangelogEntries.Add(title); - - if (!string.IsNullOrEmpty(entry.MessageHtml)) - { - TextFlowContainer messageContainer = new TextFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - }; - - // todo: use markdown parsing once API returns markdown - messageContainer.AddText(Regex.Replace(entry.MessageHtml, @"<(.|\n)*?>", string.Empty), t => - { - t.Font = OsuFont.GetFont(size: 12); - t.Colour = new Color4(235, 184, 254, 255); - }); - - ChangelogEntries.Add(messageContainer); - } - } - } - } - } -} diff --git a/osu.Game/Overlays/Changelog/ChangelogListing.cs b/osu.Game/Overlays/Changelog/ChangelogListing.cs index 8e615d1dc9..907c122232 100644 --- a/osu.Game/Overlays/Changelog/ChangelogListing.cs +++ b/osu.Game/Overlays/Changelog/ChangelogListing.cs @@ -6,6 +6,8 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; using osuTK.Graphics; @@ -23,10 +25,7 @@ namespace osu.Game.Overlays.Changelog [BackgroundDependencyLoader] private void load() { - DateTime currentDate = new DateTime(); - Clear(); - - ChangelogContentGroup changelogContentGroup = null; + DateTime currentDate = DateTime.MinValue; foreach (APIChangelogBuild build in entries) { @@ -43,27 +42,32 @@ namespace osu.Game.Overlays.Changelog }); } - changelogContentGroup = new ChangelogContentGroup(build, true); - changelogContentGroup.BuildSelected += SelectBuild; - changelogContentGroup.GenerateText(build.ChangelogEntries); - Add(changelogContentGroup); + Add(new OsuSpriteText + { + // do we need .ToUniversalTime() here? + // also, this should be a temporary solution to weekdays in >localized< date strings + Text = build.CreatedAt.Date.ToLongDateString().Replace(build.CreatedAt.ToString("dddd") + ", ", ""), + Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 24), + Colour = OsuColour.FromHex(@"FD5"), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding { Top = 15 }, + }); + currentDate = build.CreatedAt.Date; } else { - changelogContentGroup?.Add(new Box + Add(new Box { RelativeSizeAxes = Axes.X, Height = 1, Colour = new Color4(32, 24, 35, 255), Margin = new MarginPadding { Top = 30 }, }); - - changelogContentGroup = new ChangelogContentGroup(build, false); - changelogContentGroup.BuildSelected += SelectBuild; - changelogContentGroup.GenerateText(build.ChangelogEntries); - Add(changelogContentGroup); } + + Add(new ChangelogBuild(build) { SelectBuild = SelectBuild }); } } } diff --git a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs new file mode 100644 index 0000000000..af4603fddf --- /dev/null +++ b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs @@ -0,0 +1,118 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays.Changelog.Components; +using osuTK; + +namespace osu.Game.Overlays.Changelog +{ + public class ChangelogSingleBuild : ChangelogContent + { + private APIChangelogBuild build; + + public ChangelogSingleBuild(APIChangelogBuild build) + { + this.build = build; + } + + [BackgroundDependencyLoader] + private void load(CancellationToken? cancellation, IAPIProvider api) + { + var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); + bool complete = false; + + req.Success += res => + { + build = res; + complete = true; + }; + + req.Failure += _ => complete = true; + + api.Queue(req); + + while (!complete && cancellation?.IsCancellationRequested != true) + Task.Delay(1); + + Children = new Drawable[] + { + new ChangelogBuildWithNavigation(build) { SelectBuild = SelectBuild }, + }; + } + + public class ChangelogBuildWithNavigation : ChangelogBuild + { + public ChangelogBuildWithNavigation(APIChangelogBuild build) + : base(build) + { + } + + protected override FillFlowContainer CreateHeader() + { + var fill = base.CreateHeader(); + + foreach (var existing in fill.Children.OfType()) + { + existing.Scale = new Vector2(1.25f); + existing.Action = null; + + existing.Add(new OsuSpriteText + { + // do we need .ToUniversalTime() here? + // also, this should be a temporary solution to weekdays in >localized< date strings + Text = Build.CreatedAt.Date.ToLongDateString().Replace(Build.CreatedAt.ToString("dddd") + ", ", ""), + Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 14), + Colour = OsuColour.FromHex(@"FD5"), + Anchor = Anchor.BottomCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding { Top = 5 }, + }); + } + + TooltipIconButton left, right; + + fill.AddRange(new[] + { + left = new TooltipIconButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.ChevronLeft, + Size = new Vector2(24), + TooltipText = Build.Versions?.Previous?.DisplayVersion, + IsEnabled = Build.Versions?.Previous != null, + Action = () => { SelectBuild?.Invoke(Build.Versions.Previous); }, + }, + right = new TooltipIconButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.ChevronRight, + Size = new Vector2(24), + TooltipText = Build.Versions?.Next?.DisplayVersion, + IsEnabled = Build.Versions?.Next != null, + Action = () => { SelectBuild?.Invoke(Build.Versions.Next); }, + }, + }); + + fill.SetLayoutPosition(left, -1); + fill.SetLayoutPosition(right, 1); + + return fill; + } + } + } +} diff --git a/osu.Game/Overlays/Changelog/Header/Breadcrumb.cs b/osu.Game/Overlays/Changelog/Header/Breadcrumb.cs index c9b1430b5d..f960111a53 100644 --- a/osu.Game/Overlays/Changelog/Header/Breadcrumb.cs +++ b/osu.Game/Overlays/Changelog/Header/Breadcrumb.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using System; using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osu.Game.Overlays.Changelog.Components; namespace osu.Game.Overlays.Changelog.Header @@ -33,7 +34,7 @@ namespace osu.Game.Overlays.Changelog.Header RelativeSizeAxes = Axes.Y; Children = new Drawable[] { - Text = new SpriteText + Text = new OsuSpriteText { Font = OsuFont.GetFont(size: 16), Text = displayText, diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index ce2ae8baf6..bdddc1f968 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -11,6 +11,7 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Online.API.Requests.Responses; using System; +using osu.Game.Graphics.Sprites; using osu.Game.Overlays.Changelog.Components; using osuTK.Graphics; @@ -51,18 +52,18 @@ namespace osu.Game.Overlays.Changelog Direction = FillDirection.Vertical, Children = new[] { - new SpriteText + new OsuSpriteText { Text = stream.DisplayName, Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 12), Margin = new MarginPadding { Top = 6 }, }, - new SpriteText + new OsuSpriteText { Text = stream.LatestBuild.DisplayVersion, Font = OsuFont.GetFont(weight: FontWeight.Light, size: 16), }, - new SpriteText + new OsuSpriteText { Text = stream.LatestBuild.Users > 0 ? $"{stream.LatestBuild.Users:N0} users online" : null, Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 10), diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index f4c0338436..22c362d49d 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -138,7 +138,7 @@ namespace osu.Game.Overlays header.ShowBuild(build.UpdateStream.DisplayName, build.DisplayVersion); badges.Current.Value = build.UpdateStream; - loadContent(new ChangelogBuild(build)); + loadContent(new ChangelogSingleBuild(build)); } private bool initialFetchPerformed; From dae315ec0a458fd7390f7b97d6439c8445da5709 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 19 May 2019 11:28:24 +0900 Subject: [PATCH 0762/2854] Move TooltipText to OsuClickableContainer --- osu.Game/Graphics/Containers/OsuClickableContainer.cs | 5 ++++- osu.Game/Online/Chat/DrawableLinkCompiler.cs | 5 +---- osu.Game/Overlays/BeatmapSet/AuthorInfo.cs | 7 ------- .../Profile/Header/Components/ProfileHeaderButton.cs | 5 +---- .../Overlays/Profile/Sections/BeatmapMetadataContainer.cs | 5 +---- osu.Game/Users/Avatar.cs | 5 ++--- 6 files changed, 9 insertions(+), 23 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuClickableContainer.cs b/osu.Game/Graphics/Containers/OsuClickableContainer.cs index e4d30cebb7..6dbe340efb 100644 --- a/osu.Game/Graphics/Containers/OsuClickableContainer.cs +++ b/osu.Game/Graphics/Containers/OsuClickableContainer.cs @@ -4,11 +4,12 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Game.Graphics.UserInterface; namespace osu.Game.Graphics.Containers { - public class OsuClickableContainer : ClickableContainer + public class OsuClickableContainer : ClickableContainer, IHasTooltip { private readonly HoverSampleSet sampleSet; @@ -23,6 +24,8 @@ namespace osu.Game.Graphics.Containers this.sampleSet = sampleSet; } + public virtual string TooltipText { get; set; } + [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game/Online/Chat/DrawableLinkCompiler.cs b/osu.Game/Online/Chat/DrawableLinkCompiler.cs index d34ec8091c..d27a3fbffe 100644 --- a/osu.Game/Online/Chat/DrawableLinkCompiler.cs +++ b/osu.Game/Online/Chat/DrawableLinkCompiler.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics.Cursor; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -16,7 +15,7 @@ namespace osu.Game.Online.Chat /// /// An invisible drawable that brings multiple pieces together to form a consumable clickable link. /// - public class DrawableLinkCompiler : OsuHoverContainer, IHasTooltip + public class DrawableLinkCompiler : OsuHoverContainer { /// /// Each word part of a chat link (split for word-wrap support). @@ -40,8 +39,6 @@ namespace osu.Game.Online.Chat protected override IEnumerable EffectTargets => Parts; - public string TooltipText { get; set; } - private class LinkHoverSounds : HoverClickSounds { private readonly List parts; diff --git a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs index abe954aa80..7331faa618 100644 --- a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs +++ b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs @@ -9,8 +9,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Users; using osuTK; using osuTK.Graphics; -using osu.Game.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; @@ -129,10 +127,5 @@ namespace osu.Game.Overlays.BeatmapSet }; } } - - private class ClickableArea : OsuClickableContainer, IHasTooltip - { - public string TooltipText => @"View Profile"; - } } } diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs index 1650f11523..ddcf011277 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -12,10 +11,8 @@ using osuTK.Graphics; namespace osu.Game.Overlays.Profile.Header.Components { - public abstract class ProfileHeaderButton : OsuHoverContainer, IHasTooltip + public abstract class ProfileHeaderButton : OsuHoverContainer { - public abstract string TooltipText { get; } - private readonly Box background; private readonly Container content; diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs index bb55816880..16326900f1 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -16,7 +15,7 @@ namespace osu.Game.Overlays.Profile.Sections /// /// Display artist/title/mapper information, commonly used as the left portion of a profile or score display row (see ). /// - public class BeatmapMetadataContainer : OsuHoverContainer, IHasTooltip + public class BeatmapMetadataContainer : OsuHoverContainer { private readonly BeatmapInfo beatmap; @@ -27,8 +26,6 @@ namespace osu.Game.Overlays.Profile.Sections TooltipText = $"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title}"; } - public string TooltipText { get; } - [BackgroundDependencyLoader(true)] private void load(BeatmapSetOverlay beatmapSetOverlay) { diff --git a/osu.Game/Users/Avatar.cs b/osu.Game/Users/Avatar.cs index 3df5957ff9..8937f94768 100644 --- a/osu.Game/Users/Avatar.cs +++ b/osu.Game/Users/Avatar.cs @@ -6,7 +6,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; @@ -72,9 +71,9 @@ namespace osu.Game.Users game?.ShowUser(user.Id); } - private class ClickableArea : OsuClickableContainer, IHasTooltip + private class ClickableArea : OsuClickableContainer { - public string TooltipText => Enabled.Value ? @"View Profile" : null; + public override string TooltipText => Enabled.Value ? @"View Profile" : null; protected override bool OnClick(ClickEvent e) { From 1c85fcbc81dca20cf9d72aa72e3dd5e0703a4d50 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 19 May 2019 11:28:34 +0900 Subject: [PATCH 0763/2854] Remove usage of TooltipIconButton completely --- .../Changelog/ChangelogSingleBuild.cs | 52 ++++++--- .../Changelog/Components/TooltipIconButton.cs | 100 ------------------ 2 files changed, 36 insertions(+), 116 deletions(-) delete mode 100644 osu.Game/Overlays/Changelog/Components/TooltipIconButton.cs diff --git a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs index af4603fddf..50a7946ee7 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs @@ -1,20 +1,22 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Overlays.Changelog.Components; using osuTK; namespace osu.Game.Overlays.Changelog @@ -82,29 +84,19 @@ namespace osu.Game.Overlays.Changelog }); } - TooltipIconButton left, right; + NavigationIconButton left, right; fill.AddRange(new[] { - left = new TooltipIconButton + left = new NavigationIconButton(Build.Versions?.Previous) { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, Icon = FontAwesome.Solid.ChevronLeft, - Size = new Vector2(24), - TooltipText = Build.Versions?.Previous?.DisplayVersion, - IsEnabled = Build.Versions?.Previous != null, - Action = () => { SelectBuild?.Invoke(Build.Versions.Previous); }, + SelectBuild = b => SelectBuild(b) }, - right = new TooltipIconButton + right = new NavigationIconButton(Build.Versions?.Next) { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, Icon = FontAwesome.Solid.ChevronRight, - Size = new Vector2(24), - TooltipText = Build.Versions?.Next?.DisplayVersion, - IsEnabled = Build.Versions?.Next != null, - Action = () => { SelectBuild?.Invoke(Build.Versions.Next); }, + SelectBuild = b => SelectBuild(b) }, }); @@ -114,5 +106,33 @@ namespace osu.Game.Overlays.Changelog return fill; } } + + private class NavigationIconButton : IconButton + { + public Action SelectBuild; + + public NavigationIconButton(APIChangelogBuild build) + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + if (build == null) return; + + TooltipText = build.DisplayVersion; + + Action = () => + { + SelectBuild?.Invoke(build); + Enabled.Value = false; + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + HoverColour = colours.GreyVioletLight.Opacity(0.6f); + FlashColour = colours.GreyVioletLighter; + } + } } } diff --git a/osu.Game/Overlays/Changelog/Components/TooltipIconButton.cs b/osu.Game/Overlays/Changelog/Components/TooltipIconButton.cs deleted file mode 100644 index 5721481685..0000000000 --- a/osu.Game/Overlays/Changelog/Components/TooltipIconButton.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Events; -using osuTK; - -namespace osu.Game.Overlays.Changelog.Components -{ - /// - /// An icon with an action upon click that can be disabled. - /// - public class TooltipIconButton : Container, IHasTooltip - { - private readonly SpriteIcon icon; - private SampleChannel sampleHover; - private SampleChannel sampleClick; - - /// - /// The action to fire upon click if is set to true. - /// - public Action Action; - - private bool isEnabled; - - /// - /// If set to true, upon click the will execute. - /// - public bool IsEnabled - { - get => isEnabled; - set - { - isEnabled = value; - icon.FadeTo(value ? 1 : 0.5f, 250); - } - } - - public IconUsage Icon - { - get => icon.Icon; - set => icon.Icon = value; - } - - public TooltipIconButton() - { - isEnabled = true; - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - }, - icon = new SpriteIcon - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.8f), - } - }; - } - - protected override bool OnClick(ClickEvent e) - { - if (isEnabled) - { - sampleClick?.Play(); - Action?.Invoke(); - } - - return base.OnClick(e); - } - - protected override bool OnHover(HoverEvent e) - { - if (isEnabled) - sampleHover?.Play(); - return base.OnHover(e); - } - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); - sampleClick = audio.Sample.Get(@"UI/generic-select-soft"); - } - - public string TooltipText { get; set; } - } -} From 39e03ae7058f3bd18821fb23190bdd2900286ab2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 19 May 2019 11:58:47 +0900 Subject: [PATCH 0764/2854] Fix tests failing when not logged in --- osu.Game/Overlays/Changelog/ChangelogListing.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Changelog/ChangelogListing.cs b/osu.Game/Overlays/Changelog/ChangelogListing.cs index 907c122232..ebfcf76738 100644 --- a/osu.Game/Overlays/Changelog/ChangelogListing.cs +++ b/osu.Game/Overlays/Changelog/ChangelogListing.cs @@ -27,6 +27,8 @@ namespace osu.Game.Overlays.Changelog { DateTime currentDate = DateTime.MinValue; + if (entries == null) return; + foreach (APIChangelogBuild build in entries) { if (build.CreatedAt.Date != currentDate) From d8ed402779e20483c2d32efed137f1ae20d68aed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 May 2019 14:47:46 +0900 Subject: [PATCH 0765/2854] Make use of ValueChanged.OldValue --- .../Components/TournamentBeatmapPanel.cs | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 8ab0dc2459..818d25d559 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; @@ -146,10 +145,15 @@ namespace osu.Game.Tournament.Components private void matchChanged(ValueChangedEvent pairing) { + if (pairing.OldValue != null) + pairing.OldValue.PicksBans.CollectionChanged -= picksBansOnCollectionChanged; pairing.NewValue.PicksBans.CollectionChanged += picksBansOnCollectionChanged; updateState(); } + private void picksBansOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + => updateState(); + private BeatmapChoice choice; private void updateState() @@ -197,19 +201,5 @@ namespace osu.Game.Tournament.Components Alpha = 1; } } - - private void picksBansOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - var list = (ObservableCollection)sender; - - if (sender != currentMatch.Value.PicksBans) - { - // todo: we need a last attribute in bindable valuechanged events badly. - list.CollectionChanged -= picksBansOnCollectionChanged; - return; - } - - updateState(); - } } } From 084c2252cbb34ff9f5793d3717eb066d3ea9376c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 May 2019 14:48:33 +0900 Subject: [PATCH 0766/2854] Use less DI where we already have access to LadderInfo --- osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs | 4 ++-- osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 7e91ded848..0e3a9b0dfd 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -94,10 +94,10 @@ namespace osu.Game.Tournament.Screens.MapPool } [BackgroundDependencyLoader] - private void load(LadderInfo ladder, MatchIPCInfo ipc) + private void load(MatchIPCInfo ipc) { currentMatch.BindValueChanged(matchChanged); - currentMatch.BindTo(ladder.CurrentMatch); + currentMatch.BindTo(LadderInfo.CurrentMatch); ipc.Beatmap.BindValueChanged(beatmapChanged); } diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index d2ff632da0..7000d776f0 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro private readonly Bindable currentMatch = new Bindable(); [BackgroundDependencyLoader] - private void load(LadderInfo ladder, Storage storage) + private void load(Storage storage) { RelativeSizeAxes = Axes.Both; @@ -43,7 +43,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro }; currentMatch.BindValueChanged(matchChanged); - currentMatch.BindTo(ladder.CurrentMatch); + currentMatch.BindTo(LadderInfo.CurrentMatch); } private void matchChanged(ValueChangedEvent pairing) From 455301de2c1501433836411385bd1a82cda6220f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 May 2019 15:58:40 +0900 Subject: [PATCH 0767/2854] Use OsuColour for profile overlay --- osu.Game/Graphics/OsuColour.cs | 12 +----------- .../Overlays/Profile/Header/BottomHeaderContainer.cs | 4 ++-- .../Overlays/Profile/Header/CentreHeaderContainer.cs | 2 +- .../Profile/Header/Components/ExpandDetailsButton.cs | 4 ++-- .../Overlays/Profile/Header/Components/RankGraph.cs | 4 ++-- .../Profile/Header/Components/SupporterIcon.cs | 2 +- .../Overlays/Profile/Header/DetailHeaderContainer.cs | 2 +- .../Overlays/Profile/Header/MedalHeaderContainer.cs | 2 +- .../Overlays/Profile/Header/TopHeaderContainer.cs | 6 +++--- osu.Game/Overlays/Profile/ProfileHeader.cs | 4 ++-- 10 files changed, 16 insertions(+), 26 deletions(-) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index a73a8bcbc1..d337455176 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -77,7 +77,7 @@ namespace osu.Game.Graphics public readonly Color4 Seafoam = FromHex(@"05ffa2"); public readonly Color4 GreySeafoamLighter = FromHex(@"9ebab1"); - public readonly Color4 GreySeafoamLight = FromHex(@"4d7365"); + public readonly Color4 GreySeafoamLight = FromHex(@"4e7466"); public readonly Color4 GreySeafoam = FromHex(@"33413c"); public readonly Color4 GreySeafoamDark = FromHex(@"2c3532"); public readonly Color4 GreySeafoamDarker = FromHex(@"1e2422"); @@ -136,15 +136,5 @@ namespace osu.Game.Graphics public readonly Color4 ChatBlue = FromHex(@"17292e"); public readonly Color4 ContextMenuGray = FromHex(@"223034"); - - public readonly Color4 CommunityUserGreenLight = FromHex(@"deff87"); - public readonly Color4 CommunityUserGreen = FromHex(@"05ffa2"); - public readonly Color4 CommunityUserGreenDark = FromHex(@"a6cc00"); - public readonly Color4 CommunityUserGrayGreenLighter = FromHex(@"9ebab1"); - public readonly Color4 CommunityUserGrayGreenLight = FromHex(@"77998e"); - public readonly Color4 CommunityUserGrayGreen = FromHex(@"4e7466"); - public readonly Color4 CommunityUserGrayGreenDark = FromHex(@"33413c"); - public readonly Color4 CommunityUserGrayGreenDarker = FromHex(@"2c3532"); - public readonly Color4 CommunityUserGrayGreenDarkest = FromHex(@"1e2422"); } } diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index 633085960b..ffbb9ad218 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -35,14 +35,14 @@ namespace osu.Game.Overlays.Profile.Header [BackgroundDependencyLoader] private void load(OsuColour colours) { - iconColour = colours.CommunityUserGrayGreenLighter; + iconColour = colours.GreySeafoamLighter; InternalChildren = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.CommunityUserGrayGreenDarker, + Colour = colours.GreySeafoamDark, }, new FillFlowContainer { diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index b441775393..68fd77dd84 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Profile.Header new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.CommunityUserGrayGreenDark + Colour = colours.GreySeafoam }, new FillFlowContainer { diff --git a/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs index 089228b2cd..46d24608ed 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs @@ -27,8 +27,8 @@ namespace osu.Game.Overlays.Profile.Header.Components [BackgroundDependencyLoader] private void load(OsuColour colours) { - IdleColour = colours.CommunityUserGrayGreen; - HoverColour = colours.CommunityUserGrayGreen.Darken(0.2f); + IdleColour = colours.GreySeafoamLight; + HoverColour = colours.GreySeafoamLight.Darken(0.2f); Child = icon = new SpriteIcon { diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index 1dabf167e3..85ea2a175a 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -154,7 +154,7 @@ namespace osu.Game.Overlays.Profile.Header.Components [BackgroundDependencyLoader] private void load(OsuColour colours) { - ballBg.Colour = colours.CommunityUserGrayGreenDarkest; + ballBg.Colour = colours.GreySeafoamDarker; movingBall.BorderColour = colours.Yellow; movingBar.Colour = colours.Yellow; } @@ -249,7 +249,7 @@ namespace osu.Game.Overlays.Profile.Header.Components [BackgroundDependencyLoader] private void load(OsuColour colours) { - background.Colour = colours.CommunityUserGrayGreenDarker; + background.Colour = colours.GreySeafoamDark; } public void Refresh() diff --git a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs index 97454d7327..c5e61f68f4 100644 --- a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private void load(OsuColour colours) { background.Colour = colours.Pink; - iconContainer.Colour = colours.CommunityUserGrayGreenDark; + iconContainer.Colour = colours.GreySeafoam; } } } diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index e41c90be45..f26cc360a2 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -65,7 +65,7 @@ namespace osu.Game.Overlays.Profile.Header new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.CommunityUserGrayGreenDarkest, + Colour = colours.GreySeafoamDarker, }, fillFlow = new FillFlowContainer { diff --git a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs index 25d04195b2..67229a80c0 100644 --- a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Profile.Header new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.CommunityUserGrayGreenDarkest, + Colour = colours.GreySeafoamDarker, }, new Container //artificial shadow { diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 2ac7f3cc96..6fe55e2368 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Profile.Header new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.CommunityUserGrayGreenDarker, + Colour = colours.GreySeafoamDark, }, new FillFlowContainer { @@ -107,7 +107,7 @@ namespace osu.Game.Overlays.Profile.Header RelativeSizeAxes = Axes.X, Height = 1.5f, Margin = new MarginPadding { Top = 10 }, - Colour = colours.CommunityUserGrayGreenLighter, + Colour = colours.GreySeafoamLighter, }, new Container { @@ -125,7 +125,7 @@ namespace osu.Game.Overlays.Profile.Header Margin = new MarginPadding { Left = 40 }, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - Colour = colours.CommunityUserGrayGreenLighter, + Colour = colours.GreySeafoamLighter, } } }, diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 2d8c47b11a..f2ac94b7ff 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.Profile [BackgroundDependencyLoader] private void load(OsuColour colours) { - infoTabControl.AccentColour = colours.CommunityUserGreen; + infoTabControl.AccentColour = colours.Seafoam; } public Bindable User = new Bindable(); @@ -145,7 +145,7 @@ namespace osu.Game.Overlays.Profile [BackgroundDependencyLoader] private void load(OsuColour colours) { - AccentColour = colours.CommunityUserGreen; + AccentColour = colours.Seafoam; } } } From a5bd3262beabe43ff5a4e39eba5e29398f0e58b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 May 2019 18:02:13 +0900 Subject: [PATCH 0768/2854] Move UserProfileOverlay's header into an abstract implementation --- .../Online/TestSceneUserProfileHeader.cs | 2 +- osu.Game/Overlays/OverlayHeader.cs | 71 ++++++++ ...eaderTabControl.cs => HeaderTabControl.cs} | 12 +- osu.Game/Overlays/Profile/ProfileHeader.cs | 165 +++++++----------- 4 files changed, 142 insertions(+), 108 deletions(-) create mode 100644 osu.Game/Overlays/OverlayHeader.cs rename osu.Game/Overlays/Profile/Header/{ProfileHeaderTabControl.cs => HeaderTabControl.cs} (92%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs index 14c81558c1..730140faed 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Online typeof(ProfileHeader), typeof(RankGraph), typeof(LineGraph), - typeof(ProfileHeaderTabControl), + typeof(HeaderTabControl), typeof(CentreHeaderContainer), typeof(BottomHeaderContainer), typeof(DetailHeaderContainer), diff --git a/osu.Game/Overlays/OverlayHeader.cs b/osu.Game/Overlays/OverlayHeader.cs new file mode 100644 index 0000000000..fe50d4a2be --- /dev/null +++ b/osu.Game/Overlays/OverlayHeader.cs @@ -0,0 +1,71 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Profile.Header; + +namespace osu.Game.Overlays +{ + public abstract class OverlayHeader : Container + { + protected readonly HeaderTabControl TabControl; + + private const float cover_height = 150; + private const float cover_info_height = 75; + + protected OverlayHeader() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + Height = cover_height, + Masking = true, + Child = CreateBackground() + }, + new Container + { + Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, + Y = cover_height, + Height = cover_info_height, + RelativeSizeAxes = Axes.X, + Anchor = Anchor.TopLeft, + Origin = Anchor.BottomLeft, + Depth = -float.MaxValue, + Children = new Drawable[] + { + CreateTitle().With(t => t.X = -ScreenTitle.ICON_WIDTH), + TabControl = new HeaderTabControl + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = cover_info_height - 30, + Margin = new MarginPadding { Left = -UserProfileOverlay.CONTENT_X_MARGIN }, + Padding = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN } + } + } + }, + new Container + { + Margin = new MarginPadding { Top = cover_height }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = CreateContent() + } + }; + } + + protected abstract Drawable CreateBackground(); + + protected abstract Drawable CreateContent(); + + protected abstract ScreenTitle CreateTitle(); + } +} diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs b/osu.Game/Overlays/Profile/Header/HeaderTabControl.cs similarity index 92% rename from osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs rename to osu.Game/Overlays/Profile/Header/HeaderTabControl.cs index 3b16b102d5..1169ef7013 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs +++ b/osu.Game/Overlays/Profile/Header/HeaderTabControl.cs @@ -13,7 +13,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays.Profile.Header { - public class ProfileHeaderTabControl : TabControl + public class HeaderTabControl : TabControl { private readonly Box bar; @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Profile.Header foreach (TabItem tabItem in TabContainer) { - ((ProfileHeaderTabItem)tabItem).AccentColour = value; + ((HeaderTabItem)tabItem).AccentColour = value; } } } @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.Profile.Header set => TabContainer.Padding = value; } - public ProfileHeaderTabControl() + public HeaderTabControl() { TabContainer.Masking = false; TabContainer.Spacing = new Vector2(15, 0); @@ -59,12 +59,12 @@ namespace osu.Game.Overlays.Profile.Header protected override Dropdown CreateDropdown() => null; - protected override TabItem CreateTabItem(string value) => new ProfileHeaderTabItem(value) + protected override TabItem CreateTabItem(string value) => new HeaderTabItem(value) { AccentColour = AccentColour }; - private class ProfileHeaderTabItem : TabItem + private class HeaderTabItem : TabItem { private readonly OsuSpriteText text; private readonly Drawable bar; @@ -86,7 +86,7 @@ namespace osu.Game.Overlays.Profile.Header } } - public ProfileHeaderTabItem(string value) + public HeaderTabItem(string value) : base(value) { AutoSizeAxes = Axes.X; diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index f2ac94b7ff..702bd6c2c4 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -15,124 +15,85 @@ using osu.Game.Users; namespace osu.Game.Overlays.Profile { - public class ProfileHeader : Container + public class ProfileHeader : OverlayHeader { - private readonly UserCoverBackground coverContainer; - private readonly ProfileHeaderTabControl infoTabControl; + private UserCoverBackground coverContainer; - private const float cover_height = 150; - private const float cover_info_height = 75; + public Bindable User = new Bindable(); + + private CentreHeaderContainer centreHeaderContainer; + private DetailHeaderContainer detailHeaderContainer; public ProfileHeader() { - CentreHeaderContainer centreHeaderContainer; - DetailHeaderContainer detailHeaderContainer; + User.ValueChanged += e => updateDisplay(e.NewValue); - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - Height = cover_height, - Masking = true, - Children = new Drawable[] - { - coverContainer = new UserCoverBackground - { - RelativeSizeAxes = Axes.Both, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(OsuColour.FromHex("222").Opacity(0.8f), OsuColour.FromHex("222").Opacity(0.2f)) - }, - } - }, - new Container - { - Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, - Y = cover_height, - Height = cover_info_height, - RelativeSizeAxes = Axes.X, - Anchor = Anchor.TopLeft, - Origin = Anchor.BottomLeft, - Depth = -float.MaxValue, - Children = new Drawable[] - { - new ProfileHeaderTitle - { - X = -ScreenTitle.ICON_WIDTH, - }, - infoTabControl = new ProfileHeaderTabControl - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - Height = cover_info_height - 30, - Margin = new MarginPadding { Left = -UserProfileOverlay.CONTENT_X_MARGIN }, - Padding = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN } - } - } - }, - new FillFlowContainer - { - Margin = new MarginPadding { Top = cover_height }, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new TopHeaderContainer - { - RelativeSizeAxes = Axes.X, - User = { BindTarget = User }, - }, - centreHeaderContainer = new CentreHeaderContainer - { - RelativeSizeAxes = Axes.X, - User = { BindTarget = User }, - }, - detailHeaderContainer = new DetailHeaderContainer - { - RelativeSizeAxes = Axes.X, - User = { BindTarget = User }, - }, - new MedalHeaderContainer - { - RelativeSizeAxes = Axes.X, - User = { BindTarget = User }, - }, - new BottomHeaderContainer - { - RelativeSizeAxes = Axes.X, - User = { BindTarget = User }, - }, - } - } - }; - - infoTabControl.AddItem("Info"); - infoTabControl.AddItem("Modding"); + TabControl.AddItem("Info"); + TabControl.AddItem("Modding"); centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Expanded = visible.NewValue, true); - User.ValueChanged += e => updateDisplay(e.NewValue); } [BackgroundDependencyLoader] private void load(OsuColour colours) { - infoTabControl.AccentColour = colours.Seafoam; + TabControl.AccentColour = colours.Seafoam; } - public Bindable User = new Bindable(); + protected override Drawable CreateBackground() => + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + coverContainer = new UserCoverBackground + { + RelativeSizeAxes = Axes.Both, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(OsuColour.FromHex("222").Opacity(0.8f), OsuColour.FromHex("222").Opacity(0.2f)) + }, + } + }; - private void updateDisplay(User user) + protected override Drawable CreateContent() => new FillFlowContainer { - coverContainer.User = user; - } + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new TopHeaderContainer + { + RelativeSizeAxes = Axes.X, + User = { BindTarget = User }, + }, + centreHeaderContainer = new CentreHeaderContainer + { + RelativeSizeAxes = Axes.X, + User = { BindTarget = User }, + }, + detailHeaderContainer = new DetailHeaderContainer + { + RelativeSizeAxes = Axes.X, + User = { BindTarget = User }, + }, + new MedalHeaderContainer + { + RelativeSizeAxes = Axes.X, + User = { BindTarget = User }, + }, + new BottomHeaderContainer + { + RelativeSizeAxes = Axes.X, + User = { BindTarget = User }, + }, + } + }; + + protected override ScreenTitle CreateTitle() => new ProfileHeaderTitle(); private class ProfileHeaderTitle : ScreenTitle { @@ -148,5 +109,7 @@ namespace osu.Game.Overlays.Profile AccentColour = colours.Seafoam; } } + + private void updateDisplay(User user) => coverContainer.User = user; } } From 6a8a743eaa5d5e5f630d05ac23a12051b67c7ddd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 May 2019 18:02:27 +0900 Subject: [PATCH 0769/2854] Begin to consume abstract header implementation --- .../Overlays/Changelog/ChangelogHeader.cs | 104 ++++++++++-------- 1 file changed, 60 insertions(+), 44 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 94046d5762..9ef3316985 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -10,12 +10,13 @@ using osu.Framework.Graphics.Textures; using osu.Game.Graphics.Sprites; using osu.Game.Overlays.Changelog.Header; using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; using osuTK; using osuTK.Graphics; namespace osu.Game.Overlays.Changelog { - public class ChangelogHeader : Container + public class ChangelogHeader : OverlayHeader { private OsuSpriteText titleStream; private BreadcrumbListing listing; @@ -26,35 +27,36 @@ namespace osu.Game.Overlays.Changelog public event ListingSelectedEventHandler ListingSelected; - private const float cover_height = 150; private const float title_height = 50; private const float icon_size = 50; private const float icon_margin = 20; private const float version_height = 40; - [BackgroundDependencyLoader] - private void load(OsuColour colours, TextureStore textures) + public void ShowBuild(string displayName, string displayVersion) { - RelativeSizeAxes = Axes.X; - Height = cover_height; + listing.Deactivate(); + releaseStream.ShowBuild($"{displayName} {displayVersion}"); + titleStream.Text = displayName; + titleStream.FlashColour(Color4.White, 500, Easing.OutQuad); + chevron.MoveToX(0, 100).FadeIn(100); + } + public void ShowListing() + { + releaseStream.Deactivate(); + listing.Activate(); + titleStream.Text = "Listing"; + titleStream.FlashColour(Color4.White, 500, Easing.OutQuad); + chevron.MoveToX(-20, 100).FadeOut(100); + } + + protected override Drawable CreateBackground() => new HeaderBackground(); + + protected override Drawable CreateContent() => new Container + { + RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new Container - { - RelativeSizeAxes = Axes.X, - Height = cover_height, - Masking = true, - Children = new Drawable[] - { - new Sprite - { - RelativeSizeAxes = Axes.Both, - Texture = textures.Get(@"Headers/changelog"), - FillMode = FillMode.Fill, - }, - } - }, new Container { Height = title_height, @@ -67,7 +69,7 @@ namespace osu.Game.Overlays.Changelog { X = icon_margin, Masking = true, - BorderColour = colours.Violet, + //BorderColour = colours.Violet, BorderThickness = 3, MaskingSmoothness = 1, Size = new Vector2(50), @@ -76,7 +78,7 @@ namespace osu.Game.Overlays.Changelog new Sprite { RelativeSizeAxes = Axes.Both, - Texture = textures.Get(@"Icons/changelog"), + //Texture = textures.Get(@"Icons/changelog"), Size = new Vector2(0.8f), Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -84,7 +86,7 @@ namespace osu.Game.Overlays.Changelog new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.Violet, + //Colour = colours.Violet, Alpha = 0, AlwaysPresent = true, }, @@ -108,7 +110,7 @@ namespace osu.Game.Overlays.Changelog { Text = "Listing", Font = OsuFont.GetFont(weight: FontWeight.Light, size: 30), - Colour = colours.Violet, + //Colour = colours.Violet, }, } } @@ -123,7 +125,7 @@ namespace osu.Game.Overlays.Changelog Direction = FillDirection.Horizontal, Children = new Drawable[] { - listing = new BreadcrumbListing(colours.Violet) + listing = new BreadcrumbListing( /*colours.Violet*/ Color4.WhiteSmoke) { Action = () => ListingSelected?.Invoke() }, @@ -145,14 +147,14 @@ namespace osu.Game.Overlays.Changelog Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(7), - Colour = colours.Violet, + // Colour = colours.Violet, Icon = FontAwesome.Solid.ChevronRight, Alpha = 0, X = -200, }, }, }, - releaseStream = new BreadcrumbRelease(colours.Violet, "Lazer") + releaseStream = new BreadcrumbRelease( /*colours.Violet*/ Color4.WhiteSmoke, "Lazer") { Action = () => titleStream.FlashColour(Color4.White, 500, Easing.OutQuad) } @@ -160,31 +162,45 @@ namespace osu.Game.Overlays.Changelog }, new Box { - Colour = colours.Violet, + //Colour = colours.Violet, RelativeSizeAxes = Axes.X, Height = 2, Anchor = Anchor.BottomLeft, Origin = Anchor.CentreLeft, }, - }; + } + }; + + protected override ScreenTitle CreateTitle() => new ChangelogHeaderTitle(); + + public class HeaderBackground : Sprite + { + public HeaderBackground() + { + RelativeSizeAxes = Axes.Both; + FillMode = FillMode.Fill; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + Texture = textures.Get(@"Headers/changelog"); + } } - public void ShowBuild(string displayName, string displayVersion) + private class ChangelogHeaderTitle : ScreenTitle { - listing.Deactivate(); - releaseStream.ShowBuild($"{displayName} {displayVersion}"); - titleStream.Text = displayName; - titleStream.FlashColour(Color4.White, 500, Easing.OutQuad); - chevron.MoveToX(0, 100).FadeIn(100); - } + public ChangelogHeaderTitle() + { + Title = "Changelog"; + Section = "Listing"; + } - public void ShowListing() - { - releaseStream.Deactivate(); - listing.Activate(); - titleStream.Text = "Listing"; - titleStream.FlashColour(Color4.White, 500, Easing.OutQuad); - chevron.MoveToX(-20, 100).FadeOut(100); + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.Seafoam; + } } } } From 6c26d6fdf908513348548c4c148f73f16b1bb27e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 11:13:36 +0900 Subject: [PATCH 0770/2854] Remove unnecessary getters from ScreenTitle --- osu.Game/Graphics/UserInterface/ScreenTitle.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ScreenTitle.cs b/osu.Game/Graphics/UserInterface/ScreenTitle.cs index 34c70f2bed..3c3aa8fe85 100644 --- a/osu.Game/Graphics/UserInterface/ScreenTitle.cs +++ b/osu.Game/Graphics/UserInterface/ScreenTitle.cs @@ -19,19 +19,16 @@ namespace osu.Game.Graphics.UserInterface protected IconUsage Icon { - get => iconSprite.Icon; set => iconSprite.Icon = value; } protected string Title { - get => titleText.Text; set => titleText.Text = value; } protected string Section { - get => pageText.Text; set => pageText.Text = value; } From 808b45ac645ce2488dff52de7f55428f386e4044 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 11:50:03 +0900 Subject: [PATCH 0771/2854] Allow custom icon specification in ScreenTitle Not all icons are available in fonts so IconUsage alone is not enough to cover all scenarios. --- .../Graphics/UserInterface/ScreenTitle.cs | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ScreenTitle.cs b/osu.Game/Graphics/UserInterface/ScreenTitle.cs index 3c3aa8fe85..7b39238e5e 100644 --- a/osu.Game/Graphics/UserInterface/ScreenTitle.cs +++ b/osu.Game/Graphics/UserInterface/ScreenTitle.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -12,14 +13,24 @@ namespace osu.Game.Graphics.UserInterface { public abstract class ScreenTitle : CompositeDrawable, IHasAccentColour { - private readonly SpriteIcon iconSprite; + public const float ICON_WIDTH = ICON_SIZE + icon_spacing; + + protected const float ICON_SIZE = 25; + + private SpriteIcon iconSprite; private readonly OsuSpriteText titleText, pageText; - public const float ICON_WIDTH = icon_size + icon_spacing; - private const float icon_size = 25, icon_spacing = 10; + + private const float icon_spacing = 10; protected IconUsage Icon { - set => iconSprite.Icon = value; + set + { + if (iconSprite == null) + throw new InvalidOperationException($"Cannot use {nameof(Icon)} with a custom {nameof(CreateIcon)} function."); + + iconSprite.Icon = value; + } } protected string Title @@ -38,6 +49,11 @@ namespace osu.Game.Graphics.UserInterface set => pageText.Colour = value; } + protected virtual Drawable CreateIcon() => iconSprite = new SpriteIcon + { + Size = new Vector2(ICON_SIZE), + }; + protected ScreenTitle() { AutoSizeAxes = Axes.Both; @@ -48,12 +64,9 @@ namespace osu.Game.Graphics.UserInterface { AutoSizeAxes = Axes.Both, Spacing = new Vector2(icon_spacing, 0), - Children = new Drawable[] + Children = new[] { - iconSprite = new SpriteIcon - { - Size = new Vector2(icon_size), - }, + CreateIcon(), new FillFlowContainer { AutoSizeAxes = Axes.Both, From aca0fc80a8e25679226b267a9919375d4afff748 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 12:45:20 +0900 Subject: [PATCH 0772/2854] Set HeaderTabControl's default AccentColour to non-transparent Avoids items disappearing if no accent colour is set. --- osu.Game/Overlays/Profile/Header/HeaderTabControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/HeaderTabControl.cs b/osu.Game/Overlays/Profile/Header/HeaderTabControl.cs index 1169ef7013..5fd9195945 100644 --- a/osu.Game/Overlays/Profile/Header/HeaderTabControl.cs +++ b/osu.Game/Overlays/Profile/Header/HeaderTabControl.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Profile.Header { private readonly Box bar; - private Color4 accentColour; + private Color4 accentColour = Color4.White; public Color4 AccentColour { From 58a3480b6aa428734449262be6046a12657581cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 12:52:50 +0900 Subject: [PATCH 0773/2854] Update ChangelogHeader to work again with OverlayHeader --- .../Requests/Responses/APIChangelogBuild.cs | 2 + .../Overlays/Changelog/ChangelogHeader.cs | 237 +++++++----------- osu.Game/Overlays/ChangelogOverlay.cs | 9 +- 3 files changed, 104 insertions(+), 144 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs b/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs index 3377800c2b..504c65928d 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs @@ -41,5 +41,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty("previous")] public APIChangelogBuild Previous { get; set; } } + + public override string ToString() => $"{UpdateStream.DisplayName} {DisplayVersion}"; } } diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 9ef3316985..4f406daabe 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -1,53 +1,72 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Game.Graphics.Sprites; -using osu.Game.Overlays.Changelog.Header; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests.Responses; using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays.Changelog { public class ChangelogHeader : OverlayHeader { - private OsuSpriteText titleStream; - private BreadcrumbListing listing; - private SpriteIcon chevron; - private BreadcrumbRelease releaseStream; + public Action ListingSelected; - public delegate void ListingSelectedEventHandler(); + private const string listing_string = "Listing"; - public event ListingSelectedEventHandler ListingSelected; - - private const float title_height = 50; - private const float icon_size = 50; - private const float icon_margin = 20; - private const float version_height = 40; - - public void ShowBuild(string displayName, string displayVersion) + public ChangelogHeader() { - listing.Deactivate(); - releaseStream.ShowBuild($"{displayName} {displayVersion}"); - titleStream.Text = displayName; - titleStream.FlashColour(Color4.White, 500, Easing.OutQuad); - chevron.MoveToX(0, 100).FadeIn(100); + TabControl.AddItem(listing_string); + TabControl.Current.ValueChanged += e => + { + if (e.NewValue == listing_string) + ListingSelected?.Invoke(); + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + TabControl.AccentColour = colours.Violet; + } + + private APIChangelogBuild displayedBuild; + + private ChangelogHeaderTitle title; + + public void ShowBuild(APIChangelogBuild build) + { + hideBuildTab(); + + displayedBuild = build; + + TabControl.AddItem(build.ToString()); + TabControl.Current.Value = build.ToString(); + + title.Version = build.UpdateStream.DisplayName; } public void ShowListing() { - releaseStream.Deactivate(); - listing.Activate(); - titleStream.Text = "Listing"; - titleStream.FlashColour(Color4.White, 500, Easing.OutQuad); - chevron.MoveToX(-20, 100).FadeOut(100); + hideBuildTab(); + + title.Version = null; + } + + private void hideBuildTab() + { + if (displayedBuild != null) + { + TabControl.RemoveItem(displayedBuild.ToString()); + displayedBuild = null; + } } protected override Drawable CreateBackground() => new HeaderBackground(); @@ -57,121 +76,11 @@ namespace osu.Game.Overlays.Changelog RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new Container - { - Height = title_height, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Y = -version_height, - Children = new Drawable[] - { - new CircularContainer - { - X = icon_margin, - Masking = true, - //BorderColour = colours.Violet, - BorderThickness = 3, - MaskingSmoothness = 1, - Size = new Vector2(50), - Children = new Drawable[] - { - new Sprite - { - RelativeSizeAxes = Axes.Both, - //Texture = textures.Get(@"Icons/changelog"), - Size = new Vector2(0.8f), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - //Colour = colours.Violet, - Alpha = 0, - AlwaysPresent = true, - }, - } - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - X = icon_size + icon_margin * 2, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = "Changelog ", - Font = OsuFont.GetFont(weight: FontWeight.Light, size: 30), - }, - titleStream = new OsuSpriteText - { - Text = "Listing", - Font = OsuFont.GetFont(weight: FontWeight.Light, size: 30), - //Colour = colours.Violet, - }, - } - } - } - }, - new FillFlowContainer // Listing > Lazer 2018.713.1 - { - X = 2 * icon_margin + icon_size, - Height = version_height, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - listing = new BreadcrumbListing( /*colours.Violet*/ Color4.WhiteSmoke) - { - Action = () => ListingSelected?.Invoke() - }, - new Container // without a container, moving the chevron wont work - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding - { - Top = 10, - Left = 15, - Right = 18, - Bottom = 15, - }, - Children = new Drawable[] - { - chevron = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(7), - // Colour = colours.Violet, - Icon = FontAwesome.Solid.ChevronRight, - Alpha = 0, - X = -200, - }, - }, - }, - releaseStream = new BreadcrumbRelease( /*colours.Violet*/ Color4.WhiteSmoke, "Lazer") - { - Action = () => titleStream.FlashColour(Color4.White, 500, Easing.OutQuad) - } - }, - }, - new Box - { - //Colour = colours.Violet, - RelativeSizeAxes = Axes.X, - Height = 2, - Anchor = Anchor.BottomLeft, - Origin = Anchor.CentreLeft, - }, + // todo: move badge display here } }; - protected override ScreenTitle CreateTitle() => new ChangelogHeaderTitle(); + protected override ScreenTitle CreateTitle() => title = new ChangelogHeaderTitle(); public class HeaderBackground : Sprite { @@ -190,16 +99,64 @@ namespace osu.Game.Overlays.Changelog private class ChangelogHeaderTitle : ScreenTitle { + public string Version + { + set => Section = value ?? listing_string; + } + public ChangelogHeaderTitle() { Title = "Changelog"; - Section = "Listing"; + Version = null; } [BackgroundDependencyLoader] private void load(OsuColour colours) { - AccentColour = colours.Seafoam; + AccentColour = colours.Violet; + } + + protected override Drawable CreateIcon() => new ChangelogIcon(); + + internal class ChangelogIcon : CompositeDrawable + { + private const float circle_allowance = 0.8f; + + [BackgroundDependencyLoader] + private void load(TextureStore textures, OsuColour colours) + { + Size = new Vector2(ICON_SIZE / circle_allowance); + + InternalChildren = new Drawable[] + { + new CircularContainer + { + Masking = true, + BorderColour = colours.Violet, + BorderThickness = 3, + MaskingSmoothness = 1, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Sprite + { + RelativeSizeAxes = Axes.Both, + Texture = textures.Get(@"Icons/changelog"), + Size = new Vector2(circle_allowance), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Violet, + Alpha = 0, + AlwaysPresent = true, + }, + } + }, + }; + } } } } diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 22c362d49d..430fa569f1 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -58,7 +58,10 @@ namespace osu.Game.Overlays Direction = FillDirection.Vertical, Children = new Drawable[] { - header = new ChangelogHeader(), + header = new ChangelogHeader + { + ListingSelected = ShowListing, + }, badges = new BadgeDisplay(), content = new Container { @@ -70,8 +73,6 @@ namespace osu.Game.Overlays }, }; - header.ListingSelected += ShowListing; - // todo: better badges.Current.ValueChanged += e => { @@ -135,7 +136,7 @@ namespace osu.Game.Overlays return; } - header.ShowBuild(build.UpdateStream.DisplayName, build.DisplayVersion); + header.ShowBuild(build); badges.Current.Value = build.UpdateStream; loadContent(new ChangelogSingleBuild(build)); From a131875a7b40346b62f67175a40016fe7c998c0a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 13:34:35 +0900 Subject: [PATCH 0774/2854] Use bindables the whole way --- .../Requests/Responses/APIChangelogBuild.cs | 4 +- .../API/Requests/Responses/APIUpdateStream.cs | 8 +- .../Overlays/Changelog/ChangelogHeader.cs | 42 +++++----- osu.Game/Overlays/ChangelogOverlay.cs | 81 ++++++++++--------- 4 files changed, 65 insertions(+), 70 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs b/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs index 504c65928d..36c9cc610f 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; namespace osu.Game.Online.API.Requests.Responses { - public class APIChangelogBuild + public class APIChangelogBuild : IEquatable { [JsonProperty("id")] public long Id { get; set; } @@ -42,6 +42,8 @@ namespace osu.Game.Online.API.Requests.Responses public APIChangelogBuild Previous { get; set; } } + public bool Equals(APIChangelogBuild other) => this.Id == other?.Id; + public override string ToString() => $"{UpdateStream.DisplayName} {DisplayVersion}"; } } diff --git a/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs b/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs index 4c65b562dd..0c3fee88c7 100644 --- a/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs +++ b/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs @@ -25,13 +25,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty("latest_build")] public APIChangelogBuild LatestBuild { get; set; } - public bool Equals(APIUpdateStream other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - - return Id == other.Id; - } + public bool Equals(APIUpdateStream other) => this.Id == other?.Id; public ColourInfo Colour { diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 4f406daabe..ccc976c4dc 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -17,6 +18,8 @@ namespace osu.Game.Overlays.Changelog { public class ChangelogHeader : OverlayHeader { + public readonly Bindable Current = new Bindable(); + public Action ListingSelected; private const string listing_string = "Listing"; @@ -29,6 +32,8 @@ namespace osu.Game.Overlays.Changelog if (e.NewValue == listing_string) ListingSelected?.Invoke(); }; + + Current.ValueChanged += showBuild; } [BackgroundDependencyLoader] @@ -37,35 +42,24 @@ namespace osu.Game.Overlays.Changelog TabControl.AccentColour = colours.Violet; } - private APIChangelogBuild displayedBuild; - private ChangelogHeaderTitle title; - public void ShowBuild(APIChangelogBuild build) + private void showBuild(ValueChangedEvent e) { - hideBuildTab(); + if (e.OldValue != null) + TabControl.RemoveItem(e.OldValue.ToString()); - displayedBuild = build; - - TabControl.AddItem(build.ToString()); - TabControl.Current.Value = build.ToString(); - - title.Version = build.UpdateStream.DisplayName; - } - - public void ShowListing() - { - hideBuildTab(); - - title.Version = null; - } - - private void hideBuildTab() - { - if (displayedBuild != null) + if (e.NewValue != null) { - TabControl.RemoveItem(displayedBuild.ToString()); - displayedBuild = null; + TabControl.AddItem(e.NewValue.ToString()); + TabControl.Current.Value = e.NewValue.ToString(); + + title.Version = e.NewValue.UpdateStream.DisplayName; + } + else + { + TabControl.Current.Value = listing_string; + title.Version = null; } } diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 430fa569f1..34347d8e0a 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -1,12 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; -using System.Linq; using System.Threading; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -32,6 +34,23 @@ namespace osu.Game.Overlays private List builds; + public readonly Bindable Current = new Bindable(); + + public void ShowListing() => Current.Value = null; + + /// + /// Fetches and shows a specific build from a specific update stream. + /// + /// Must contain at least and + /// . If and + /// are specified, the header will instantly display them. + public void ShowBuild([NotNull] APIChangelogBuild build) + { + if (build == null) throw new ArgumentNullException(nameof(build)); + + Current.Value = build; + } + [BackgroundDependencyLoader] private void load(AudioManager audio, OsuColour colour) { @@ -73,22 +92,30 @@ namespace osu.Game.Overlays }, }; - // todo: better badges.Current.ValueChanged += e => { - if (e.NewValue?.LatestBuild != null) + if (e.NewValue != null) ShowBuild(e.NewValue.LatestBuild); }; sampleBack = audio.Sample.Get(@"UI/generic-select-soft"); - } - protected override void PopIn() - { - base.PopIn(); + header.Current.BindTo(Current); - if (!initialFetchPerformed) - fetchListing(); + Current.BindValueChanged(e => + { + if (e.NewValue != null) + { + badges.Current.Value = e.NewValue.UpdateStream; + + loadContent(new ChangelogSingleBuild(e.NewValue)); + } + else + { + badges.Current.Value = null; + loadContent(new ChangelogListing(builds)); + } + }); } public override bool OnPressed(GlobalAction action) @@ -96,13 +123,13 @@ namespace osu.Game.Overlays switch (action) { case GlobalAction.Back: - if (content.Child is ChangelogListing) + if (Current.Value == null) { State = Visibility.Hidden; } else { - ShowListing(); + Current.Value = null; sampleBack?.Play(); } @@ -112,34 +139,12 @@ namespace osu.Game.Overlays return false; } - public void ShowListing() + protected override void PopIn() { - if (content.Children.FirstOrDefault() is ChangelogListing) - return; + base.PopIn(); - header.ShowListing(); - badges.Current.Value = null; - loadContent(new ChangelogListing(builds)); - } - - /// - /// Fetches and shows a specific build from a specific update stream. - /// - /// Must contain at least and - /// . If and - /// are specified, the header will instantly display them. - public void ShowBuild(APIChangelogBuild build) - { - if (build == null) - { - ShowListing(); - return; - } - - header.ShowBuild(build); - badges.Current.Value = build.UpdateStream; - - loadContent(new ChangelogSingleBuild(build)); + if (!initialFetchPerformed) + fetchListing(); } private bool initialFetchPerformed; @@ -158,7 +163,7 @@ namespace osu.Game.Overlays builds = res.Builds; badges.Populate(res.Streams); - ShowListing(); + Current.TriggerChange(); }; req.Failure += _ => initialFetchPerformed = false; From 9a769c9f15801ff781621b160a953e7c5c777c32 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 13:36:21 +0900 Subject: [PATCH 0775/2854] Move OverlayHeaderTabControl to correct namespace --- osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs | 3 ++- osu.Game/Overlays/OverlayHeader.cs | 5 ++--- .../HeaderTabControl.cs => OverlayHeaderTabControl.cs} | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) rename osu.Game/Overlays/{Profile/Header/HeaderTabControl.cs => OverlayHeaderTabControl.cs} (97%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs index 730140faed..d9230090fc 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Overlays; using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile.Header; using osu.Game.Overlays.Profile.Header.Components; @@ -21,7 +22,7 @@ namespace osu.Game.Tests.Visual.Online typeof(ProfileHeader), typeof(RankGraph), typeof(LineGraph), - typeof(HeaderTabControl), + typeof(OverlayHeaderTabControl), typeof(CentreHeaderContainer), typeof(BottomHeaderContainer), typeof(DetailHeaderContainer), diff --git a/osu.Game/Overlays/OverlayHeader.cs b/osu.Game/Overlays/OverlayHeader.cs index fe50d4a2be..2e032db2ba 100644 --- a/osu.Game/Overlays/OverlayHeader.cs +++ b/osu.Game/Overlays/OverlayHeader.cs @@ -4,13 +4,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Profile.Header; namespace osu.Game.Overlays { public abstract class OverlayHeader : Container { - protected readonly HeaderTabControl TabControl; + protected readonly OverlayHeaderTabControl TabControl; private const float cover_height = 150; private const float cover_info_height = 75; @@ -41,7 +40,7 @@ namespace osu.Game.Overlays Children = new Drawable[] { CreateTitle().With(t => t.X = -ScreenTitle.ICON_WIDTH), - TabControl = new HeaderTabControl + TabControl = new OverlayHeaderTabControl { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, diff --git a/osu.Game/Overlays/Profile/Header/HeaderTabControl.cs b/osu.Game/Overlays/OverlayHeaderTabControl.cs similarity index 97% rename from osu.Game/Overlays/Profile/Header/HeaderTabControl.cs rename to osu.Game/Overlays/OverlayHeaderTabControl.cs index 5fd9195945..21b42cfbf4 100644 --- a/osu.Game/Overlays/Profile/Header/HeaderTabControl.cs +++ b/osu.Game/Overlays/OverlayHeaderTabControl.cs @@ -11,9 +11,9 @@ using osu.Game.Graphics.UserInterface; using osuTK; using osuTK.Graphics; -namespace osu.Game.Overlays.Profile.Header +namespace osu.Game.Overlays { - public class HeaderTabControl : TabControl + public class OverlayHeaderTabControl : TabControl { private readonly Box bar; @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.Profile.Header set => TabContainer.Padding = value; } - public HeaderTabControl() + public OverlayHeaderTabControl() { TabContainer.Masking = false; TabContainer.Spacing = new Vector2(15, 0); From 340b207fa0013b9fee8d99bdf4e5f17967bcd160 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 13:37:35 +0900 Subject: [PATCH 0776/2854] Delete breadcrumb implementation --- .../Online/TestSceneChangelogOverlay.cs | 4 - .../UserInterface/TestSceneTextBadgePair.cs | 57 -------- .../Overlays/Changelog/Header/Breadcrumb.cs | 127 ------------------ .../Changelog/Header/BreadcrumbListing.cs | 66 --------- .../Changelog/Header/BreadcrumbRelease.cs | 35 ----- 5 files changed, 289 deletions(-) delete mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneTextBadgePair.cs delete mode 100644 osu.Game/Overlays/Changelog/Header/Breadcrumb.cs delete mode 100644 osu.Game/Overlays/Changelog/Header/BreadcrumbListing.cs delete mode 100644 osu.Game/Overlays/Changelog/Header/BreadcrumbRelease.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index 4ac5514019..c97ef384d3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -7,7 +7,6 @@ using NUnit.Framework; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.Changelog; -using osu.Game.Overlays.Changelog.Header; namespace osu.Game.Tests.Visual.Online { @@ -25,9 +24,6 @@ namespace osu.Game.Tests.Visual.Online typeof(ChangelogListing), typeof(ChangelogSingleBuild), typeof(ChangelogBuild), - typeof(Breadcrumb), - typeof(BreadcrumbListing), - typeof(BreadcrumbRelease), }; protected override void LoadComplete() diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneTextBadgePair.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneTextBadgePair.cs deleted file mode 100644 index 67b2b9854d..0000000000 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneTextBadgePair.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Overlays.Changelog.Header; -using osuTK.Graphics; - -namespace osu.Game.Tests.Visual.UserInterface -{ - public class TestSceneTextBadgePair : OsuTestScene - { - public TestSceneTextBadgePair() - { - Breadcrumb breadcrumb; - - Add(new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 250, - Height = 50, - Children = new Drawable[] - { - new Box - { - Colour = Color4.Gray, - Alpha = 0.5f, - RelativeSizeAxes = Axes.Both, - }, - breadcrumb = new TestBadgePair(Color4.DeepSkyBlue, "Test") - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } - } - }); - - AddStep(@"Deactivate", breadcrumb.Deactivate); - AddStep(@"Activate", breadcrumb.Activate); - AddStep(@"Hide text", () => breadcrumb.HideText(200)); - AddStep(@"Show text", () => breadcrumb.ShowText(200)); - AddStep(@"Different text", () => breadcrumb.ShowText(200, "This one's a little bit wider")); - AddStep(@"Different text", () => breadcrumb.ShowText(200, "Ok?..")); - } - - private class TestBadgePair : Breadcrumb - { - public TestBadgePair(ColourInfo badgeColour, string displayText = "Listing", bool startCollapsed = true) - : base(badgeColour, displayText, startCollapsed) - { - } - } - } -} diff --git a/osu.Game/Overlays/Changelog/Header/Breadcrumb.cs b/osu.Game/Overlays/Changelog/Header/Breadcrumb.cs deleted file mode 100644 index f960111a53..0000000000 --- a/osu.Game/Overlays/Changelog/Header/Breadcrumb.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Events; -using System; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Overlays.Changelog.Components; - -namespace osu.Game.Overlays.Changelog.Header -{ - public abstract class Breadcrumb : Container - { - protected SpriteText Text; - protected LineBadge LineBadge; - - public bool IsActivated { get; protected set; } - - public Action Action; - - private SampleChannel sampleHover; - private SampleChannel sampleActivate; - - protected Breadcrumb(ColourInfo badgeColour, string displayText = "Listing", bool startCollapsed = true) - { - AutoSizeAxes = Axes.X; - RelativeSizeAxes = Axes.Y; - Children = new Drawable[] - { - Text = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 16), - Text = displayText, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Margin = new MarginPadding { Top = 5, Bottom = 15 }, - }, - LineBadge = new LineBadge(startCollapsed) - { - CollapsedSize = 2, - UncollapsedSize = 10, - Colour = badgeColour, - Anchor = Anchor.BottomCentre, - Origin = Anchor.Centre, - } - }; - } - - public virtual void Deactivate() - { - if (!IsActivated) - return; - - IsActivated = false; - LineBadge.Collapse(); - Text.Font = Text.Font.With(weight: FontWeight.Regular); - } - - public virtual void Activate() - { - if (IsActivated) - return; - - IsActivated = true; - LineBadge.Uncollapse(); - Text.Font = Text.Font.With(weight: FontWeight.Bold); - } - - public void SetTextColour(ColourInfo newColour, double duration = 0, Easing easing = Easing.None) - { - Text.FadeColour(newColour, duration, easing); - } - - public void HideText(double duration = 0, Easing easing = Easing.InOutCubic) - { - LineBadge.Collapse(); - Text.MoveToY(20, duration, easing) - .FadeOut(duration, easing); - } - - public void ShowText(double duration = 0, string displayText = null, Easing easing = Easing.InOutCubic) - { - LineBadge.Collapse(); - Text.MoveToY(20, duration, easing) - .FadeOut(duration, easing) - .Then() - .MoveToY(0, duration, easing) - .FadeIn(duration, easing); - - Scheduler.AddDelayed(() => - { - Text.Text = displayText; - LineBadge.Uncollapse(); - }, duration); - } - - protected override bool OnHover(HoverEvent e) - { - if (!IsActivated) - sampleHover?.Play(); - return base.OnHover(e); - } - - protected override bool OnClick(ClickEvent e) - { - Action?.Invoke(); - Activate(); - sampleActivate?.Play(); - - return true; - } - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); - sampleActivate = audio.Sample.Get(@"UI/generic-select-soft"); - } - } -} diff --git a/osu.Game/Overlays/Changelog/Header/BreadcrumbListing.cs b/osu.Game/Overlays/Changelog/Header/BreadcrumbListing.cs deleted file mode 100644 index 50db916e7e..0000000000 --- a/osu.Game/Overlays/Changelog/Header/BreadcrumbListing.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osuTK.Graphics; - -namespace osu.Game.Overlays.Changelog.Header -{ - public class BreadcrumbListing : Breadcrumb - { - private readonly ColourInfo badgeColour; - - public BreadcrumbListing(ColourInfo badgeColour) - : base(badgeColour, "Listing", false) - { - this.badgeColour = badgeColour; - Text.Font = Text.Font.With(weight: FontWeight.Bold); - Text.Anchor = Anchor.TopCentre; - Text.Origin = Anchor.TopCentre; - - AutoSizeAxes = Axes.None; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Activate(); - Width = Text.DrawWidth; - } - - public override void Activate() - { - if (IsActivated) - return; - - base.Activate(); - SetTextColour(Color4.White, 100); - } - - public override void Deactivate() - { - if (!IsActivated) - return; - - base.Deactivate(); - SetTextColour(badgeColour, 100); - } - - protected override bool OnHover(HoverEvent e) - { - LineBadge.Uncollapse(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - if (!IsActivated) - LineBadge.Collapse(); - base.OnHoverLost(e); - } - } -} diff --git a/osu.Game/Overlays/Changelog/Header/BreadcrumbRelease.cs b/osu.Game/Overlays/Changelog/Header/BreadcrumbRelease.cs deleted file mode 100644 index 43711af61b..0000000000 --- a/osu.Game/Overlays/Changelog/Header/BreadcrumbRelease.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics.Colour; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Changelog.Header -{ - public class BreadcrumbRelease : Breadcrumb - { - private const float transition_duration = 125; - - public BreadcrumbRelease(ColourInfo badgeColour, string displayText) - : base(badgeColour, displayText) - { - Text.Font = Text.Font.With(weight: FontWeight.Bold); - Text.Y = 20; - Text.Alpha = 0; - } - - public void ShowBuild(string displayText = null) - { - ShowText(transition_duration, displayText); - IsActivated = true; - } - - public override void Deactivate() - { - if (!IsActivated) - return; - - HideText(transition_duration); - } - } -} From 85a41cf6c3bf4cad37b8f06b9e84ffc36059051a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 May 2019 17:36:29 +0900 Subject: [PATCH 0777/2854] Allow chaining of loadComponentSingleFile --- osu.Game/OsuGame.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index d108bb45f3..8cdddd6736 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -588,7 +588,7 @@ namespace osu.Game private Task asyncLoadStream; - private void loadComponentSingleFile(T d, Action add, bool cache = false) + private T loadComponentSingleFile(T d, Action add, bool cache = false) where T : Drawable { if (cache) @@ -636,6 +636,8 @@ namespace osu.Game } }); }); + + return d; } public bool OnPressed(GlobalAction action) From bc962bf8f0bf6db4660217163e7a85a601709606 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 19 May 2019 11:28:24 +0900 Subject: [PATCH 0778/2854] Move TooltipText to OsuClickableContainer --- osu.Game/Graphics/Containers/OsuClickableContainer.cs | 5 ++++- osu.Game/Online/Chat/DrawableLinkCompiler.cs | 5 +---- osu.Game/Overlays/BeatmapSet/AuthorInfo.cs | 7 ------- .../Profile/Header/Components/ProfileHeaderButton.cs | 5 +---- .../Overlays/Profile/Sections/BeatmapMetadataContainer.cs | 5 +---- osu.Game/Users/Avatar.cs | 5 ++--- 6 files changed, 9 insertions(+), 23 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuClickableContainer.cs b/osu.Game/Graphics/Containers/OsuClickableContainer.cs index e4d30cebb7..6dbe340efb 100644 --- a/osu.Game/Graphics/Containers/OsuClickableContainer.cs +++ b/osu.Game/Graphics/Containers/OsuClickableContainer.cs @@ -4,11 +4,12 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Game.Graphics.UserInterface; namespace osu.Game.Graphics.Containers { - public class OsuClickableContainer : ClickableContainer + public class OsuClickableContainer : ClickableContainer, IHasTooltip { private readonly HoverSampleSet sampleSet; @@ -23,6 +24,8 @@ namespace osu.Game.Graphics.Containers this.sampleSet = sampleSet; } + public virtual string TooltipText { get; set; } + [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game/Online/Chat/DrawableLinkCompiler.cs b/osu.Game/Online/Chat/DrawableLinkCompiler.cs index d34ec8091c..d27a3fbffe 100644 --- a/osu.Game/Online/Chat/DrawableLinkCompiler.cs +++ b/osu.Game/Online/Chat/DrawableLinkCompiler.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics.Cursor; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -16,7 +15,7 @@ namespace osu.Game.Online.Chat /// /// An invisible drawable that brings multiple pieces together to form a consumable clickable link. /// - public class DrawableLinkCompiler : OsuHoverContainer, IHasTooltip + public class DrawableLinkCompiler : OsuHoverContainer { /// /// Each word part of a chat link (split for word-wrap support). @@ -40,8 +39,6 @@ namespace osu.Game.Online.Chat protected override IEnumerable EffectTargets => Parts; - public string TooltipText { get; set; } - private class LinkHoverSounds : HoverClickSounds { private readonly List parts; diff --git a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs index abe954aa80..7331faa618 100644 --- a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs +++ b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs @@ -9,8 +9,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Users; using osuTK; using osuTK.Graphics; -using osu.Game.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; @@ -129,10 +127,5 @@ namespace osu.Game.Overlays.BeatmapSet }; } } - - private class ClickableArea : OsuClickableContainer, IHasTooltip - { - public string TooltipText => @"View Profile"; - } } } diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs index 1650f11523..ddcf011277 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -12,10 +11,8 @@ using osuTK.Graphics; namespace osu.Game.Overlays.Profile.Header.Components { - public abstract class ProfileHeaderButton : OsuHoverContainer, IHasTooltip + public abstract class ProfileHeaderButton : OsuHoverContainer { - public abstract string TooltipText { get; } - private readonly Box background; private readonly Container content; diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs index bb55816880..16326900f1 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -16,7 +15,7 @@ namespace osu.Game.Overlays.Profile.Sections /// /// Display artist/title/mapper information, commonly used as the left portion of a profile or score display row (see ). /// - public class BeatmapMetadataContainer : OsuHoverContainer, IHasTooltip + public class BeatmapMetadataContainer : OsuHoverContainer { private readonly BeatmapInfo beatmap; @@ -27,8 +26,6 @@ namespace osu.Game.Overlays.Profile.Sections TooltipText = $"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title}"; } - public string TooltipText { get; } - [BackgroundDependencyLoader(true)] private void load(BeatmapSetOverlay beatmapSetOverlay) { diff --git a/osu.Game/Users/Avatar.cs b/osu.Game/Users/Avatar.cs index 3df5957ff9..8937f94768 100644 --- a/osu.Game/Users/Avatar.cs +++ b/osu.Game/Users/Avatar.cs @@ -6,7 +6,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; @@ -72,9 +71,9 @@ namespace osu.Game.Users game?.ShowUser(user.Id); } - private class ClickableArea : OsuClickableContainer, IHasTooltip + private class ClickableArea : OsuClickableContainer { - public string TooltipText => Enabled.Value ? @"View Profile" : null; + public override string TooltipText => Enabled.Value ? @"View Profile" : null; protected override bool OnClick(ClickEvent e) { From 247dad7e86515e0872d62882de10fc996430b727 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 May 2019 15:58:40 +0900 Subject: [PATCH 0779/2854] Use OsuColour for profile overlay --- osu.Game/Graphics/OsuColour.cs | 10 ---------- .../Overlays/Profile/Header/BottomHeaderContainer.cs | 4 ++-- .../Overlays/Profile/Header/CentreHeaderContainer.cs | 2 +- .../Profile/Header/Components/ExpandDetailsButton.cs | 4 ++-- .../Overlays/Profile/Header/Components/RankGraph.cs | 4 ++-- .../Profile/Header/Components/SupporterIcon.cs | 2 +- .../Overlays/Profile/Header/DetailHeaderContainer.cs | 2 +- .../Overlays/Profile/Header/MedalHeaderContainer.cs | 2 +- osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 6 +++--- osu.Game/Overlays/Profile/ProfileHeader.cs | 4 ++-- 10 files changed, 15 insertions(+), 25 deletions(-) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index a73a8bcbc1..53693a1e38 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -136,15 +136,5 @@ namespace osu.Game.Graphics public readonly Color4 ChatBlue = FromHex(@"17292e"); public readonly Color4 ContextMenuGray = FromHex(@"223034"); - - public readonly Color4 CommunityUserGreenLight = FromHex(@"deff87"); - public readonly Color4 CommunityUserGreen = FromHex(@"05ffa2"); - public readonly Color4 CommunityUserGreenDark = FromHex(@"a6cc00"); - public readonly Color4 CommunityUserGrayGreenLighter = FromHex(@"9ebab1"); - public readonly Color4 CommunityUserGrayGreenLight = FromHex(@"77998e"); - public readonly Color4 CommunityUserGrayGreen = FromHex(@"4e7466"); - public readonly Color4 CommunityUserGrayGreenDark = FromHex(@"33413c"); - public readonly Color4 CommunityUserGrayGreenDarker = FromHex(@"2c3532"); - public readonly Color4 CommunityUserGrayGreenDarkest = FromHex(@"1e2422"); } } diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index 633085960b..ffbb9ad218 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -35,14 +35,14 @@ namespace osu.Game.Overlays.Profile.Header [BackgroundDependencyLoader] private void load(OsuColour colours) { - iconColour = colours.CommunityUserGrayGreenLighter; + iconColour = colours.GreySeafoamLighter; InternalChildren = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.CommunityUserGrayGreenDarker, + Colour = colours.GreySeafoamDark, }, new FillFlowContainer { diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index b441775393..68fd77dd84 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Profile.Header new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.CommunityUserGrayGreenDark + Colour = colours.GreySeafoam }, new FillFlowContainer { diff --git a/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs index 089228b2cd..46d24608ed 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs @@ -27,8 +27,8 @@ namespace osu.Game.Overlays.Profile.Header.Components [BackgroundDependencyLoader] private void load(OsuColour colours) { - IdleColour = colours.CommunityUserGrayGreen; - HoverColour = colours.CommunityUserGrayGreen.Darken(0.2f); + IdleColour = colours.GreySeafoamLight; + HoverColour = colours.GreySeafoamLight.Darken(0.2f); Child = icon = new SpriteIcon { diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index 1dabf167e3..85ea2a175a 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -154,7 +154,7 @@ namespace osu.Game.Overlays.Profile.Header.Components [BackgroundDependencyLoader] private void load(OsuColour colours) { - ballBg.Colour = colours.CommunityUserGrayGreenDarkest; + ballBg.Colour = colours.GreySeafoamDarker; movingBall.BorderColour = colours.Yellow; movingBar.Colour = colours.Yellow; } @@ -249,7 +249,7 @@ namespace osu.Game.Overlays.Profile.Header.Components [BackgroundDependencyLoader] private void load(OsuColour colours) { - background.Colour = colours.CommunityUserGrayGreenDarker; + background.Colour = colours.GreySeafoamDark; } public void Refresh() diff --git a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs index 97454d7327..c5e61f68f4 100644 --- a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private void load(OsuColour colours) { background.Colour = colours.Pink; - iconContainer.Colour = colours.CommunityUserGrayGreenDark; + iconContainer.Colour = colours.GreySeafoam; } } } diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index e41c90be45..f26cc360a2 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -65,7 +65,7 @@ namespace osu.Game.Overlays.Profile.Header new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.CommunityUserGrayGreenDarkest, + Colour = colours.GreySeafoamDarker, }, fillFlow = new FillFlowContainer { diff --git a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs index 25d04195b2..67229a80c0 100644 --- a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Profile.Header new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.CommunityUserGrayGreenDarkest, + Colour = colours.GreySeafoamDarker, }, new Container //artificial shadow { diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 2ac7f3cc96..6fe55e2368 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Profile.Header new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.CommunityUserGrayGreenDarker, + Colour = colours.GreySeafoamDark, }, new FillFlowContainer { @@ -107,7 +107,7 @@ namespace osu.Game.Overlays.Profile.Header RelativeSizeAxes = Axes.X, Height = 1.5f, Margin = new MarginPadding { Top = 10 }, - Colour = colours.CommunityUserGrayGreenLighter, + Colour = colours.GreySeafoamLighter, }, new Container { @@ -125,7 +125,7 @@ namespace osu.Game.Overlays.Profile.Header Margin = new MarginPadding { Left = 40 }, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - Colour = colours.CommunityUserGrayGreenLighter, + Colour = colours.GreySeafoamLighter, } } }, diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 2d8c47b11a..f2ac94b7ff 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.Profile [BackgroundDependencyLoader] private void load(OsuColour colours) { - infoTabControl.AccentColour = colours.CommunityUserGreen; + infoTabControl.AccentColour = colours.Seafoam; } public Bindable User = new Bindable(); @@ -145,7 +145,7 @@ namespace osu.Game.Overlays.Profile [BackgroundDependencyLoader] private void load(OsuColour colours) { - AccentColour = colours.CommunityUserGreen; + AccentColour = colours.Seafoam; } } } From 5a9c3ab9fa203d228a579c850c22e30ffd2af435 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 May 2019 18:02:13 +0900 Subject: [PATCH 0780/2854] Move UserProfileOverlay's header into an abstract implementation --- .../Online/TestSceneUserProfileHeader.cs | 2 +- osu.Game/Overlays/OverlayHeader.cs | 71 ++++++++ ...eaderTabControl.cs => HeaderTabControl.cs} | 12 +- osu.Game/Overlays/Profile/ProfileHeader.cs | 165 +++++++----------- 4 files changed, 142 insertions(+), 108 deletions(-) create mode 100644 osu.Game/Overlays/OverlayHeader.cs rename osu.Game/Overlays/Profile/Header/{ProfileHeaderTabControl.cs => HeaderTabControl.cs} (92%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs index 14c81558c1..730140faed 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Online typeof(ProfileHeader), typeof(RankGraph), typeof(LineGraph), - typeof(ProfileHeaderTabControl), + typeof(HeaderTabControl), typeof(CentreHeaderContainer), typeof(BottomHeaderContainer), typeof(DetailHeaderContainer), diff --git a/osu.Game/Overlays/OverlayHeader.cs b/osu.Game/Overlays/OverlayHeader.cs new file mode 100644 index 0000000000..fe50d4a2be --- /dev/null +++ b/osu.Game/Overlays/OverlayHeader.cs @@ -0,0 +1,71 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Profile.Header; + +namespace osu.Game.Overlays +{ + public abstract class OverlayHeader : Container + { + protected readonly HeaderTabControl TabControl; + + private const float cover_height = 150; + private const float cover_info_height = 75; + + protected OverlayHeader() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + Height = cover_height, + Masking = true, + Child = CreateBackground() + }, + new Container + { + Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, + Y = cover_height, + Height = cover_info_height, + RelativeSizeAxes = Axes.X, + Anchor = Anchor.TopLeft, + Origin = Anchor.BottomLeft, + Depth = -float.MaxValue, + Children = new Drawable[] + { + CreateTitle().With(t => t.X = -ScreenTitle.ICON_WIDTH), + TabControl = new HeaderTabControl + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = cover_info_height - 30, + Margin = new MarginPadding { Left = -UserProfileOverlay.CONTENT_X_MARGIN }, + Padding = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN } + } + } + }, + new Container + { + Margin = new MarginPadding { Top = cover_height }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = CreateContent() + } + }; + } + + protected abstract Drawable CreateBackground(); + + protected abstract Drawable CreateContent(); + + protected abstract ScreenTitle CreateTitle(); + } +} diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs b/osu.Game/Overlays/Profile/Header/HeaderTabControl.cs similarity index 92% rename from osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs rename to osu.Game/Overlays/Profile/Header/HeaderTabControl.cs index 3b16b102d5..1169ef7013 100644 --- a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs +++ b/osu.Game/Overlays/Profile/Header/HeaderTabControl.cs @@ -13,7 +13,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays.Profile.Header { - public class ProfileHeaderTabControl : TabControl + public class HeaderTabControl : TabControl { private readonly Box bar; @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Profile.Header foreach (TabItem tabItem in TabContainer) { - ((ProfileHeaderTabItem)tabItem).AccentColour = value; + ((HeaderTabItem)tabItem).AccentColour = value; } } } @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.Profile.Header set => TabContainer.Padding = value; } - public ProfileHeaderTabControl() + public HeaderTabControl() { TabContainer.Masking = false; TabContainer.Spacing = new Vector2(15, 0); @@ -59,12 +59,12 @@ namespace osu.Game.Overlays.Profile.Header protected override Dropdown CreateDropdown() => null; - protected override TabItem CreateTabItem(string value) => new ProfileHeaderTabItem(value) + protected override TabItem CreateTabItem(string value) => new HeaderTabItem(value) { AccentColour = AccentColour }; - private class ProfileHeaderTabItem : TabItem + private class HeaderTabItem : TabItem { private readonly OsuSpriteText text; private readonly Drawable bar; @@ -86,7 +86,7 @@ namespace osu.Game.Overlays.Profile.Header } } - public ProfileHeaderTabItem(string value) + public HeaderTabItem(string value) : base(value) { AutoSizeAxes = Axes.X; diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index f2ac94b7ff..702bd6c2c4 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -15,124 +15,85 @@ using osu.Game.Users; namespace osu.Game.Overlays.Profile { - public class ProfileHeader : Container + public class ProfileHeader : OverlayHeader { - private readonly UserCoverBackground coverContainer; - private readonly ProfileHeaderTabControl infoTabControl; + private UserCoverBackground coverContainer; - private const float cover_height = 150; - private const float cover_info_height = 75; + public Bindable User = new Bindable(); + + private CentreHeaderContainer centreHeaderContainer; + private DetailHeaderContainer detailHeaderContainer; public ProfileHeader() { - CentreHeaderContainer centreHeaderContainer; - DetailHeaderContainer detailHeaderContainer; + User.ValueChanged += e => updateDisplay(e.NewValue); - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - Height = cover_height, - Masking = true, - Children = new Drawable[] - { - coverContainer = new UserCoverBackground - { - RelativeSizeAxes = Axes.Both, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(OsuColour.FromHex("222").Opacity(0.8f), OsuColour.FromHex("222").Opacity(0.2f)) - }, - } - }, - new Container - { - Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, - Y = cover_height, - Height = cover_info_height, - RelativeSizeAxes = Axes.X, - Anchor = Anchor.TopLeft, - Origin = Anchor.BottomLeft, - Depth = -float.MaxValue, - Children = new Drawable[] - { - new ProfileHeaderTitle - { - X = -ScreenTitle.ICON_WIDTH, - }, - infoTabControl = new ProfileHeaderTabControl - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - Height = cover_info_height - 30, - Margin = new MarginPadding { Left = -UserProfileOverlay.CONTENT_X_MARGIN }, - Padding = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN } - } - } - }, - new FillFlowContainer - { - Margin = new MarginPadding { Top = cover_height }, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new TopHeaderContainer - { - RelativeSizeAxes = Axes.X, - User = { BindTarget = User }, - }, - centreHeaderContainer = new CentreHeaderContainer - { - RelativeSizeAxes = Axes.X, - User = { BindTarget = User }, - }, - detailHeaderContainer = new DetailHeaderContainer - { - RelativeSizeAxes = Axes.X, - User = { BindTarget = User }, - }, - new MedalHeaderContainer - { - RelativeSizeAxes = Axes.X, - User = { BindTarget = User }, - }, - new BottomHeaderContainer - { - RelativeSizeAxes = Axes.X, - User = { BindTarget = User }, - }, - } - } - }; - - infoTabControl.AddItem("Info"); - infoTabControl.AddItem("Modding"); + TabControl.AddItem("Info"); + TabControl.AddItem("Modding"); centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Expanded = visible.NewValue, true); - User.ValueChanged += e => updateDisplay(e.NewValue); } [BackgroundDependencyLoader] private void load(OsuColour colours) { - infoTabControl.AccentColour = colours.Seafoam; + TabControl.AccentColour = colours.Seafoam; } - public Bindable User = new Bindable(); + protected override Drawable CreateBackground() => + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + coverContainer = new UserCoverBackground + { + RelativeSizeAxes = Axes.Both, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(OsuColour.FromHex("222").Opacity(0.8f), OsuColour.FromHex("222").Opacity(0.2f)) + }, + } + }; - private void updateDisplay(User user) + protected override Drawable CreateContent() => new FillFlowContainer { - coverContainer.User = user; - } + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new TopHeaderContainer + { + RelativeSizeAxes = Axes.X, + User = { BindTarget = User }, + }, + centreHeaderContainer = new CentreHeaderContainer + { + RelativeSizeAxes = Axes.X, + User = { BindTarget = User }, + }, + detailHeaderContainer = new DetailHeaderContainer + { + RelativeSizeAxes = Axes.X, + User = { BindTarget = User }, + }, + new MedalHeaderContainer + { + RelativeSizeAxes = Axes.X, + User = { BindTarget = User }, + }, + new BottomHeaderContainer + { + RelativeSizeAxes = Axes.X, + User = { BindTarget = User }, + }, + } + }; + + protected override ScreenTitle CreateTitle() => new ProfileHeaderTitle(); private class ProfileHeaderTitle : ScreenTitle { @@ -148,5 +109,7 @@ namespace osu.Game.Overlays.Profile AccentColour = colours.Seafoam; } } + + private void updateDisplay(User user) => coverContainer.User = user; } } From aba945934dceb67a30c89b6f07b2887bf497135f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 13:36:21 +0900 Subject: [PATCH 0781/2854] Move OverlayHeaderTabControl to correct namespace --- osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs | 3 ++- osu.Game/Overlays/OverlayHeader.cs | 5 ++--- .../HeaderTabControl.cs => OverlayHeaderTabControl.cs} | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) rename osu.Game/Overlays/{Profile/Header/HeaderTabControl.cs => OverlayHeaderTabControl.cs} (97%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs index 730140faed..d9230090fc 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Overlays; using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile.Header; using osu.Game.Overlays.Profile.Header.Components; @@ -21,7 +22,7 @@ namespace osu.Game.Tests.Visual.Online typeof(ProfileHeader), typeof(RankGraph), typeof(LineGraph), - typeof(HeaderTabControl), + typeof(OverlayHeaderTabControl), typeof(CentreHeaderContainer), typeof(BottomHeaderContainer), typeof(DetailHeaderContainer), diff --git a/osu.Game/Overlays/OverlayHeader.cs b/osu.Game/Overlays/OverlayHeader.cs index fe50d4a2be..2e032db2ba 100644 --- a/osu.Game/Overlays/OverlayHeader.cs +++ b/osu.Game/Overlays/OverlayHeader.cs @@ -4,13 +4,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Profile.Header; namespace osu.Game.Overlays { public abstract class OverlayHeader : Container { - protected readonly HeaderTabControl TabControl; + protected readonly OverlayHeaderTabControl TabControl; private const float cover_height = 150; private const float cover_info_height = 75; @@ -41,7 +40,7 @@ namespace osu.Game.Overlays Children = new Drawable[] { CreateTitle().With(t => t.X = -ScreenTitle.ICON_WIDTH), - TabControl = new HeaderTabControl + TabControl = new OverlayHeaderTabControl { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, diff --git a/osu.Game/Overlays/Profile/Header/HeaderTabControl.cs b/osu.Game/Overlays/OverlayHeaderTabControl.cs similarity index 97% rename from osu.Game/Overlays/Profile/Header/HeaderTabControl.cs rename to osu.Game/Overlays/OverlayHeaderTabControl.cs index 1169ef7013..0587a9bb2b 100644 --- a/osu.Game/Overlays/Profile/Header/HeaderTabControl.cs +++ b/osu.Game/Overlays/OverlayHeaderTabControl.cs @@ -11,9 +11,9 @@ using osu.Game.Graphics.UserInterface; using osuTK; using osuTK.Graphics; -namespace osu.Game.Overlays.Profile.Header +namespace osu.Game.Overlays { - public class HeaderTabControl : TabControl + public class OverlayHeaderTabControl : TabControl { private readonly Box bar; @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.Profile.Header set => TabContainer.Padding = value; } - public HeaderTabControl() + public OverlayHeaderTabControl() { TabContainer.Masking = false; TabContainer.Spacing = new Vector2(15, 0); From db817b3b2510385d11a46828892fa4b6f0cb6635 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 12:45:20 +0900 Subject: [PATCH 0782/2854] Set HeaderTabControl's default AccentColour to non-transparent Avoids items disappearing if no accent colour is set. --- osu.Game/Overlays/OverlayHeaderTabControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OverlayHeaderTabControl.cs b/osu.Game/Overlays/OverlayHeaderTabControl.cs index 0587a9bb2b..21b42cfbf4 100644 --- a/osu.Game/Overlays/OverlayHeaderTabControl.cs +++ b/osu.Game/Overlays/OverlayHeaderTabControl.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays { private readonly Box bar; - private Color4 accentColour; + private Color4 accentColour = Color4.White; public Color4 AccentColour { From e3ae858c876cc5f7db899d8c78e609be6184e718 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 May 2019 14:01:41 +0900 Subject: [PATCH 0783/2854] Adjust testcase to avoid potential rounding issues --- .../Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 42e421d1ad..d6259a1fdf 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -219,10 +219,10 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(controlPoints.SamplePointAt(2500).SampleBank, Is.EqualTo("normal")); Assert.That(controlPoints.SamplePointAt(3500).SampleBank, Is.EqualTo("drum")); - Assert.That(controlPoints.TimingPointAt(500).BeatLength, Is.EqualTo(500)); - Assert.That(controlPoints.TimingPointAt(1500).BeatLength, Is.EqualTo(500)); - Assert.That(controlPoints.TimingPointAt(2500).BeatLength, Is.EqualTo(250)); - Assert.That(controlPoints.TimingPointAt(3500).BeatLength, Is.EqualTo(500)); + Assert.That(controlPoints.TimingPointAt(500).BeatLength, Is.EqualTo(500).Within(0.1)); + Assert.That(controlPoints.TimingPointAt(1500).BeatLength, Is.EqualTo(500).Within(0.1)); + Assert.That(controlPoints.TimingPointAt(2500).BeatLength, Is.EqualTo(250).Within(0.1)); + Assert.That(controlPoints.TimingPointAt(3500).BeatLength, Is.EqualTo(500).Within(0.1)); } } From e7c8c4f787fac5005521a92a05ef5a3f805af4f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 14:02:17 +0900 Subject: [PATCH 0784/2854] Fix incorrectly changed colour --- osu.Game/Graphics/OsuColour.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index d337455176..53693a1e38 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -77,7 +77,7 @@ namespace osu.Game.Graphics public readonly Color4 Seafoam = FromHex(@"05ffa2"); public readonly Color4 GreySeafoamLighter = FromHex(@"9ebab1"); - public readonly Color4 GreySeafoamLight = FromHex(@"4e7466"); + public readonly Color4 GreySeafoamLight = FromHex(@"4d7365"); public readonly Color4 GreySeafoam = FromHex(@"33413c"); public readonly Color4 GreySeafoamDark = FromHex(@"2c3532"); public readonly Color4 GreySeafoamDarker = FromHex(@"1e2422"); From a9447eaf7b2beee0e7f99589855dc39201303f71 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 14:02:34 +0900 Subject: [PATCH 0785/2854] Remove redundant prefixes --- osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs | 2 +- osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs b/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs index 36c9cc610f..40f1b791f9 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs @@ -42,7 +42,7 @@ namespace osu.Game.Online.API.Requests.Responses public APIChangelogBuild Previous { get; set; } } - public bool Equals(APIChangelogBuild other) => this.Id == other?.Id; + public bool Equals(APIChangelogBuild other) => Id == other?.Id; public override string ToString() => $"{UpdateStream.DisplayName} {DisplayVersion}"; } diff --git a/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs b/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs index 0c3fee88c7..ef204c7687 100644 --- a/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs +++ b/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs @@ -25,7 +25,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty("latest_build")] public APIChangelogBuild LatestBuild { get; set; } - public bool Equals(APIUpdateStream other) => this.Id == other?.Id; + public bool Equals(APIUpdateStream other) => Id == other?.Id; public ColourInfo Colour { From 89e62c3d3025d743f187c4897e29709903897312 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 14:07:40 +0900 Subject: [PATCH 0786/2854] Only play OsuHoverContainer hover effect if action is present --- osu.Game/Graphics/Containers/OsuHoverContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs index d5ae7cba57..b1fe1e81f1 100644 --- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs +++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs @@ -22,7 +22,8 @@ namespace osu.Game.Graphics.Containers protected override bool OnHover(HoverEvent e) { - EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint)); + if (Action != null) + EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint)); return base.OnHover(e); } From 587e4bb5d54585428d4ba3fadbedf2790f4e85bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 11:13:36 +0900 Subject: [PATCH 0787/2854] Remove unnecessary getters from ScreenTitle --- osu.Game/Graphics/UserInterface/ScreenTitle.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ScreenTitle.cs b/osu.Game/Graphics/UserInterface/ScreenTitle.cs index b9d9b5427d..18b79d9ec2 100644 --- a/osu.Game/Graphics/UserInterface/ScreenTitle.cs +++ b/osu.Game/Graphics/UserInterface/ScreenTitle.cs @@ -19,19 +19,16 @@ namespace osu.Game.Graphics.UserInterface protected IconUsage Icon { - get => iconSprite.Icon; set => iconSprite.Icon = value; } protected string Title { - get => titleText.Text; set => titleText.Text = value; } protected string Section { - get => pageText.Text; set => pageText.Text = value; } From e0da919fe920ad2688b2b95471dc76f30b8f38fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 11:50:03 +0900 Subject: [PATCH 0788/2854] Allow custom icon specification in ScreenTitle Not all icons are available in fonts so IconUsage alone is not enough to cover all scenarios. --- .../Graphics/UserInterface/ScreenTitle.cs | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ScreenTitle.cs b/osu.Game/Graphics/UserInterface/ScreenTitle.cs index 18b79d9ec2..fe1936d4e8 100644 --- a/osu.Game/Graphics/UserInterface/ScreenTitle.cs +++ b/osu.Game/Graphics/UserInterface/ScreenTitle.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -12,14 +13,24 @@ namespace osu.Game.Graphics.UserInterface { public abstract class ScreenTitle : CompositeDrawable, IHasAccentColour { - private readonly SpriteIcon iconSprite; + public const float ICON_WIDTH = ICON_SIZE + icon_spacing; + + protected const float ICON_SIZE = 25; + + private SpriteIcon iconSprite; private readonly OsuSpriteText titleText, pageText; - public const float ICON_WIDTH = icon_size + icon_spacing; - private const float icon_size = 25, icon_spacing = 10; + + private const float icon_spacing = 10; protected IconUsage Icon { - set => iconSprite.Icon = value; + set + { + if (iconSprite == null) + throw new InvalidOperationException($"Cannot use {nameof(Icon)} with a custom {nameof(CreateIcon)} function."); + + iconSprite.Icon = value; + } } protected string Title @@ -38,6 +49,11 @@ namespace osu.Game.Graphics.UserInterface set => pageText.Colour = value; } + protected virtual Drawable CreateIcon() => iconSprite = new SpriteIcon + { + Size = new Vector2(ICON_SIZE), + }; + protected ScreenTitle() { AutoSizeAxes = Axes.Both; @@ -48,12 +64,9 @@ namespace osu.Game.Graphics.UserInterface { AutoSizeAxes = Axes.Both, Spacing = new Vector2(icon_spacing, 0), - Children = new Drawable[] + Children = new[] { - iconSprite = new SpriteIcon - { - Size = new Vector2(icon_size), - }, + CreateIcon(), new FillFlowContainer { AutoSizeAxes = Axes.Both, From 4ac2acbfc0a969d9693c3bf62d3af7fd5f542956 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 14:13:52 +0900 Subject: [PATCH 0789/2854] Fix usages of SpriteText (instead of OsuSpriteText) --- osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs | 4 ++-- .../Visual/Background/TestSceneBackgroundScreenBeatmap.cs | 4 ++-- osu.Game.Tests/Visual/Gameplay/TestSceneScoreCounter.cs | 3 ++- osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs | 4 ++-- osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs | 3 ++- osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs | 4 ++-- .../Visual/UserInterface/TestSceneNotificationOverlay.cs | 3 ++- osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs | 2 +- 8 files changed, 15 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs index 0d143198dd..b2613a59d5 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs @@ -11,10 +11,10 @@ using osu.Framework.Extensions.Color4Extensions; 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.ControlPoints; using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables; @@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Mania.Tests content = new Container { RelativeSizeAxes = Axes.Both } } }, - new SpriteText + new OsuSpriteText { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs index 10e3dc10c8..c9bdcf928f 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs @@ -9,7 +9,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Input.States; using osu.Framework.Platform; @@ -19,6 +18,7 @@ using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Scoring; @@ -240,7 +240,7 @@ namespace osu.Game.Tests.Visual.Background { player.StoryboardEnabled.Value = false; player.ReplacesBackground.Value = false; - player.CurrentStoryboardContainer.Add(new SpriteText + player.CurrentStoryboardContainer.Add(new OsuSpriteText { Size = new Vector2(250, 50), Alpha = 1, diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoreCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoreCounter.cs index 0c9e3fcd73..080a287b48 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoreCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoreCounter.cs @@ -5,6 +5,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; +using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Play.HUD; using osuTK; @@ -53,7 +54,7 @@ namespace osu.Game.Tests.Visual.Gameplay }; Add(stars); - SpriteText starsLabel = new SpriteText + SpriteText starsLabel = new OsuSpriteText { Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs index 7d6edd0d12..c7a0df6e9f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs @@ -7,9 +7,9 @@ using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osu.Game.Skinning; using osuTK.Graphics; @@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Gameplay Colour = Color4.Black, RelativeSizeAxes = Axes.Both, }, - new SpriteText + new OsuSpriteText { Font = OsuFont.Default.With(size: 40), Anchor = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs index d93daba4d4..364c986723 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; +using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; using osu.Game.Overlays.Chat.Tabs; using osu.Game.Users; @@ -61,7 +62,7 @@ namespace osu.Game.Tests.Visual.Online Anchor = Anchor.TopLeft, Children = new Drawable[] { - currentText = new SpriteText + currentText = new OsuSpriteText { Text = "Currently selected channel:" } diff --git a/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs b/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs index 53ce25ebb3..a68fd0ef40 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs +++ b/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs @@ -4,9 +4,9 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; using osu.Framework.Testing; +using osu.Game.Graphics.Sprites; using osu.Game.Screens; using osu.Game.Screens.Play; using osuTK.Graphics; @@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load() { - AddInternal(new SpriteText + AddInternal(new OsuSpriteText { Text = screenText, Colour = Color4.White, diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index 0cb7c2484d..71033fcd2f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; +using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; @@ -40,7 +41,7 @@ namespace osu.Game.Tests.Visual.UserInterface Origin = Anchor.TopRight }); - SpriteText displayedCount = new SpriteText(); + SpriteText displayedCount = new OsuSpriteText(); Content.Add(displayedCount); diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index e70bf4c572..89da0fc254 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -83,7 +83,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, }, - date = new SpriteText + date = new OsuSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, From 7e38aabe7518e47ef3dd010c4f77241a424bfc5a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 May 2019 14:27:41 +0900 Subject: [PATCH 0790/2854] Remove equivalence check from controlpoint parsing --- .../Formats/LegacyBeatmapDecoderTest.cs | 57 ++++++++++++++++--- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 9 --- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index d6259a1fdf..5fd5fe342d 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -170,27 +170,66 @@ namespace osu.Game.Tests.Beatmaps.Formats var controlPoints = beatmap.ControlPointInfo; Assert.AreEqual(4, controlPoints.TimingPoints.Count); - var timingPoint = controlPoints.TimingPoints[0]; + Assert.AreEqual(42, controlPoints.DifficultyPoints.Count); + Assert.AreEqual(42, controlPoints.SamplePoints.Count); + Assert.AreEqual(42, controlPoints.EffectPoints.Count); + + var timingPoint = controlPoints.TimingPointAt(0); + Assert.AreEqual(956, timingPoint.Time); + Assert.AreEqual(329.67032967033, timingPoint.BeatLength); + Assert.AreEqual(TimeSignatures.SimpleQuadruple, timingPoint.TimeSignature); + + timingPoint = controlPoints.TimingPointAt(48428); Assert.AreEqual(956, timingPoint.Time); Assert.AreEqual(329.67032967033d, timingPoint.BeatLength); Assert.AreEqual(TimeSignatures.SimpleQuadruple, timingPoint.TimeSignature); - Assert.AreEqual(5, controlPoints.DifficultyPoints.Count); - var difficultyPoint = controlPoints.DifficultyPoints[0]; - Assert.AreEqual(116999, difficultyPoint.Time); - Assert.AreEqual(0.75000000000000189d, difficultyPoint.SpeedMultiplier); + timingPoint = controlPoints.TimingPointAt(119637); + Assert.AreEqual(119637, timingPoint.Time); + Assert.AreEqual(659.340659340659, timingPoint.BeatLength); + Assert.AreEqual(TimeSignatures.SimpleQuadruple, timingPoint.TimeSignature); - Assert.AreEqual(34, controlPoints.SamplePoints.Count); - var soundPoint = controlPoints.SamplePoints[0]; + var difficultyPoint = controlPoints.DifficultyPointAt(0); + Assert.AreEqual(0, difficultyPoint.Time); + Assert.AreEqual(1.0, difficultyPoint.SpeedMultiplier); + + difficultyPoint = controlPoints.DifficultyPointAt(48428); + Assert.AreEqual(48428, difficultyPoint.Time); + Assert.AreEqual(1.0, difficultyPoint.SpeedMultiplier); + + difficultyPoint = controlPoints.DifficultyPointAt(116999); + Assert.AreEqual(116999, difficultyPoint.Time); + Assert.AreEqual(0.75, difficultyPoint.SpeedMultiplier, 0.1); + + var soundPoint = controlPoints.SamplePointAt(0); Assert.AreEqual(956, soundPoint.Time); Assert.AreEqual("soft", soundPoint.SampleBank); Assert.AreEqual(60, soundPoint.SampleVolume); - Assert.AreEqual(8, controlPoints.EffectPoints.Count); - var effectPoint = controlPoints.EffectPoints[0]; + soundPoint = controlPoints.SamplePointAt(53373); + Assert.AreEqual(53373, soundPoint.Time); + Assert.AreEqual("soft", soundPoint.SampleBank); + Assert.AreEqual(60, soundPoint.SampleVolume); + + soundPoint = controlPoints.SamplePointAt(119637); + Assert.AreEqual(119637, soundPoint.Time); + Assert.AreEqual("soft", soundPoint.SampleBank); + Assert.AreEqual(80, soundPoint.SampleVolume); + + var effectPoint = controlPoints.EffectPointAt(0); + Assert.AreEqual(0, effectPoint.Time); + Assert.IsFalse(effectPoint.KiaiMode); + Assert.IsFalse(effectPoint.OmitFirstBarLine); + + effectPoint = controlPoints.EffectPointAt(53703); Assert.AreEqual(53703, effectPoint.Time); Assert.IsTrue(effectPoint.KiaiMode); Assert.IsFalse(effectPoint.OmitFirstBarLine); + + effectPoint = controlPoints.EffectPointAt(119637); + Assert.AreEqual(119637, effectPoint.Time); + Assert.IsFalse(effectPoint.KiaiMode); + Assert.IsFalse(effectPoint.OmitFirstBarLine); } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index a5c3d8a4de..3cd425ea44 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -426,9 +426,6 @@ namespace osu.Game.Beatmaps.Formats { var existing = beatmap.ControlPointInfo.DifficultyPointAt(newPoint.Time); - if (newPoint.EquivalentTo(existing)) - return; - if (existing.Time == newPoint.Time) { // autogenerated points should not replace non-autogenerated. @@ -446,9 +443,6 @@ namespace osu.Game.Beatmaps.Formats { var existing = beatmap.ControlPointInfo.EffectPointAt(newPoint.Time); - if (newPoint.EquivalentTo(existing)) - return; - if (existing.Time == newPoint.Time) { // autogenerated points should not replace non-autogenerated. @@ -466,9 +460,6 @@ namespace osu.Game.Beatmaps.Formats { var existing = beatmap.ControlPointInfo.SamplePointAt(newPoint.Time); - if (newPoint.EquivalentTo(existing)) - return; - if (existing.Time == newPoint.Time) { // autogenerated points should not replace non-autogenerated. From 7a56fe84f20433063d811290c5afedb381cd7d5f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 May 2019 14:27:57 +0900 Subject: [PATCH 0791/2854] Remove ControlPoint.EquivalentTo --- osu.Game/Beatmaps/ControlPoints/ControlPoint.cs | 10 +--------- .../Beatmaps/ControlPoints/DifficultyControlPoint.cs | 10 +++++----- .../Beatmaps/ControlPoints/EffectControlPoint.cs | 12 ++++++------ .../Beatmaps/ControlPoints/SampleControlPoint.cs | 11 +++++------ .../Beatmaps/ControlPoints/TimingControlPoint.cs | 11 +++++------ osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 9 ++++----- 6 files changed, 26 insertions(+), 37 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index 5e538126b3..abe7e5e803 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -19,15 +19,7 @@ namespace osu.Game.Beatmaps.ControlPoints public int CompareTo(ControlPoint other) => Time.CompareTo(other.Time); - /// - /// Whether this provides the same parametric changes as another . - /// Basically an equality check without considering the . - /// - /// The to compare to. - /// Whether this is equivalent to . - public virtual bool EquivalentTo(ControlPoint other) => true; - public bool Equals(ControlPoint other) - => EquivalentTo(other) && Time.Equals(other?.Time); + => Time.Equals(other?.Time); } } diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index 013271d597..a3e3121575 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -1,11 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osuTK; namespace osu.Game.Beatmaps.ControlPoints { - public class DifficultyControlPoint : ControlPoint + public class DifficultyControlPoint : ControlPoint, IEquatable { /// /// The speed multiplier at this control point. @@ -18,9 +19,8 @@ namespace osu.Game.Beatmaps.ControlPoints private double speedMultiplier = 1; - public override bool EquivalentTo(ControlPoint other) - => base.EquivalentTo(other) - && other is DifficultyControlPoint difficulty - && SpeedMultiplier.Equals(difficulty.SpeedMultiplier); + public bool Equals(DifficultyControlPoint other) + => base.Equals(other) + && SpeedMultiplier.Equals(other?.SpeedMultiplier); } } diff --git a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs index 3978b7b4b0..354d86dc13 100644 --- a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; + namespace osu.Game.Beatmaps.ControlPoints { - public class EffectControlPoint : ControlPoint + public class EffectControlPoint : ControlPoint, IEquatable { /// /// Whether this control point enables Kiai mode. @@ -15,10 +17,8 @@ namespace osu.Game.Beatmaps.ControlPoints /// public bool OmitFirstBarLine; - public override bool EquivalentTo(ControlPoint other) - => base.EquivalentTo(other) - && other is EffectControlPoint effect - && KiaiMode.Equals(effect.KiaiMode) - && OmitFirstBarLine.Equals(effect.OmitFirstBarLine); + public bool Equals(EffectControlPoint other) + => base.Equals(other) + && KiaiMode == other?.KiaiMode && OmitFirstBarLine == other.OmitFirstBarLine; } } diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index 241ce90740..4c45bef862 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -1,11 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Game.Audio; namespace osu.Game.Beatmaps.ControlPoints { - public class SampleControlPoint : ControlPoint + public class SampleControlPoint : ControlPoint, IEquatable { public const string DEFAULT_BANK = "normal"; @@ -44,10 +45,8 @@ namespace osu.Game.Beatmaps.ControlPoints return newSampleInfo; } - public override bool EquivalentTo(ControlPoint other) - => base.EquivalentTo(other) - && other is SampleControlPoint sample - && SampleBank.Equals(sample.SampleBank) - && SampleVolume.Equals(sample.SampleVolume); + public bool Equals(SampleControlPoint other) + => base.Equals(other) + && string.Equals(SampleBank, other?.SampleBank) && SampleVolume == other?.SampleVolume; } } diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index 9ec27bdfdf..e5815a3f3b 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -1,12 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osuTK; using osu.Game.Beatmaps.Timing; namespace osu.Game.Beatmaps.ControlPoints { - public class TimingControlPoint : ControlPoint + public class TimingControlPoint : ControlPoint, IEquatable { /// /// The time signature at this control point. @@ -24,10 +25,8 @@ namespace osu.Game.Beatmaps.ControlPoints private double beatLength = 1000; - public override bool EquivalentTo(ControlPoint other) - => base.EquivalentTo(other) - && other is TimingControlPoint timing - && TimeSignature.Equals(timing.TimeSignature) - && BeatLength.Equals(timing.BeatLength); + public bool Equals(TimingControlPoint other) + => base.Equals(other) + && TimeSignature == other?.TimeSignature && beatLength.Equals(other.beatLength); } } diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index ad5089958c..854d3b41d6 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -189,7 +189,7 @@ namespace osu.Game.Beatmaps.Formats Foreground = 3 } - internal class LegacySampleControlPoint : SampleControlPoint + internal class LegacySampleControlPoint : SampleControlPoint, IEquatable { public int CustomSampleBank; @@ -203,10 +203,9 @@ namespace osu.Game.Beatmaps.Formats return baseInfo; } - public override bool EquivalentTo(ControlPoint other) - => base.EquivalentTo(other) - && other is LegacySampleControlPoint legacy - && CustomSampleBank == legacy.CustomSampleBank; + public bool Equals(LegacySampleControlPoint other) + => base.Equals(other) + && CustomSampleBank == other?.CustomSampleBank; } } } From 24a7e624df1464eedd64b3c56bf999bb76c8f15a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 14:35:11 +0900 Subject: [PATCH 0792/2854] Only propagate badge value changes if not the current UpdateStream --- osu.Game/Overlays/ChangelogOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 34347d8e0a..daee91416c 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -94,7 +94,7 @@ namespace osu.Game.Overlays badges.Current.ValueChanged += e => { - if (e.NewValue != null) + if (e.NewValue?.LatestBuild != null && e.NewValue != Current.Value?.UpdateStream) ShowBuild(e.NewValue.LatestBuild); }; From a18e0b3b2faa0d94601021a688ee88efe09cb15b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 14:46:12 +0900 Subject: [PATCH 0793/2854] Fix test scene --- .../Visual/Online/TestSceneChangelogOverlay.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index c97ef384d3..2c941d4a48 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -41,7 +41,17 @@ namespace osu.Game.Tests.Visual.Online changelog.ShowBuild(new APIChangelogBuild { Version = "2018.712.0", + DisplayVersion = "2018.712.0", UpdateStream = new APIUpdateStream { Name = "lazer" }, + ChangelogEntries = new List() + { + new APIChangelogEntry + { + Category = "Test", + Title = "Title", + MessageHtml = "Message", + } + } }); changelog.Show(); }); From aed4634fe00fd91343bcff6701f84cfc85976814 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 May 2019 14:48:14 +0900 Subject: [PATCH 0794/2854] Consolidate loader animation tests --- .../Visual/Menus/TestSceneLoaderAnimation.cs | 32 ++----------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneLoaderAnimation.cs b/osu.Game.Tests/Visual/Menus/TestSceneLoaderAnimation.cs index 813d4df708..000832b784 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneLoaderAnimation.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneLoaderAnimation.cs @@ -53,38 +53,12 @@ namespace osu.Game.Tests.Visual.Menus } [Test] - public void TestShortLoad() + public void TestDelayedLoad() { - bool logoVisible = false; - AddStep("begin loading", () => LoadScreen(loader = new TestLoader())); - AddWaitStep("wait", 3); - AddStep("finish loading", () => - { - logoVisible = loader.Logo?.Alpha > 0; - loader.AllowLoad.Set(); - }); - + AddUntilStep("wait for logo visible", () => loader.Logo?.Alpha > 0); + AddStep("finish loading", () => loader.AllowLoad.Set()); AddAssert("loaded", () => loader.Logo != null && loader.ScreenLoaded); - AddAssert("logo was visible", () => logoVisible); - AddUntilStep("logo gone", () => loader.Logo?.Alpha == 0); - } - - [Test] - public void TestLongLoad() - { - bool logoVisible = false; - - AddStep("begin loading", () => LoadScreen(loader = new TestLoader())); - AddWaitStep("wait", 10); - AddStep("finish loading", () => - { - logoVisible = loader.Logo?.Alpha > 0; - loader.AllowLoad.Set(); - }); - - AddAssert("loaded", () => loader.Logo != null && loader.ScreenLoaded); - AddAssert("logo was visible", () => logoVisible); AddUntilStep("logo gone", () => loader.Logo?.Alpha == 0); } From 5ed1540a12bac009c075e6cbd77744824d203acd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 15:02:31 +0900 Subject: [PATCH 0795/2854] Handle unhover state change better --- .../Graphics/Containers/OsuHoverContainer.cs | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs index b1fe1e81f1..c4f85926ee 100644 --- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs +++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs @@ -20,19 +20,41 @@ namespace osu.Game.Graphics.Containers protected virtual IEnumerable EffectTargets => new[] { Content }; + public OsuHoverContainer() + { + Enabled.ValueChanged += e => + { + if (!e.NewValue) unhover(); + }; + } + + private bool isHovered; + protected override bool OnHover(HoverEvent e) { - if (Action != null) - EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint)); + if (!Enabled.Value) + return false; + + EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint)); + isHovered = true; + return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - EffectTargets.ForEach(d => d.FadeColour(IdleColour, FADE_DURATION, Easing.OutQuint)); + unhover(); base.OnHoverLost(e); } + private void unhover() + { + if (!isHovered) return; + + isHovered = false; + EffectTargets.ForEach(d => d.FadeColour(IdleColour, FADE_DURATION, Easing.OutQuint)); + } + [BackgroundDependencyLoader] private void load(OsuColour colours) { From 236b8cec279f4e415330e5d736b3e064984e05b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2019 15:31:36 +0900 Subject: [PATCH 0796/2854] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e18f4b8771..98b158134d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index d947d0dfb9..b7549eeaef 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From e76d785c10542947a6ccf8773035b4ea6ee64319 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 May 2019 16:05:59 +0900 Subject: [PATCH 0797/2854] Move method above nested class --- osu.Game/Overlays/Profile/ProfileHeader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 702bd6c2c4..76613c156d 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -95,6 +95,8 @@ namespace osu.Game.Overlays.Profile protected override ScreenTitle CreateTitle() => new ProfileHeaderTitle(); + private void updateDisplay(User user) => coverContainer.User = user; + private class ProfileHeaderTitle : ScreenTitle { public ProfileHeaderTitle() @@ -109,7 +111,5 @@ namespace osu.Game.Overlays.Profile AccentColour = colours.Seafoam; } } - - private void updateDisplay(User user) => coverContainer.User = user; } } From 9e1f2d4fbcb3ab924f8b2a73c6314eb936688f48 Mon Sep 17 00:00:00 2001 From: Welsar55 Date: Tue, 21 May 2019 21:48:09 -0500 Subject: [PATCH 0798/2854] Added ability to reset all mods by pressing 1 as present on stable. --- osu.Game/Overlays/Mods/ModSection.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 50400e254f..f584eff0f9 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -57,7 +57,11 @@ namespace osu.Game.Overlays.Mods protected override bool OnKeyDown(KeyDownEvent e) { - if (ToggleKeys != null) + if(e.Key == Key.Number1) + { + DeselectAll(); + } + else if (ToggleKeys != null) { var index = Array.IndexOf(ToggleKeys, e.Key); if (index > -1 && index < buttons.Length) From 555822a68da554b9f3e63e4436f6b6e3d8189ab7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2019 13:28:41 +0900 Subject: [PATCH 0799/2854] Remove unnecessary brackets --- osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index 2c941d4a48..6db289efd7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Online Version = "2018.712.0", DisplayVersion = "2018.712.0", UpdateStream = new APIUpdateStream { Name = "lazer" }, - ChangelogEntries = new List() + ChangelogEntries = new List { new APIChangelogEntry { From 92c991494d8dab709e8adb9bacba8d7c86093811 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2019 16:33:50 +0900 Subject: [PATCH 0800/2854] Fix (and rename) ExpandingBar --- ...eLineBadge.cs => TestSceneExpandingBar.cs} | 25 +++-- .../Graphics/UserInterface/ExpandingBar.cs | 101 ++++++++++++++++++ .../Changelog/Components/LineBadge.cs | 86 --------------- osu.Game/Overlays/Changelog/StreamBadge.cs | 16 +-- osu.Game/Overlays/OverlayHeaderTabControl.cs | 17 ++- 5 files changed, 129 insertions(+), 116 deletions(-) rename osu.Game.Tests/Visual/UserInterface/{TestSceneLineBadge.cs => TestSceneExpandingBar.cs} (60%) create mode 100644 osu.Game/Graphics/UserInterface/ExpandingBar.cs delete mode 100644 osu.Game/Overlays/Changelog/Components/LineBadge.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLineBadge.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingBar.cs similarity index 60% rename from osu.Game.Tests/Visual/UserInterface/TestSceneLineBadge.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneExpandingBar.cs index 14e7b45ee6..974dbf0282 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLineBadge.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingBar.cs @@ -4,17 +4,17 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Overlays.Changelog.Components; +using osu.Game.Graphics.UserInterface; using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface { - public class TestSceneLineBadge : OsuTestScene + public class TestSceneExpandingBar : OsuTestScene { - public TestSceneLineBadge() + public TestSceneExpandingBar() { Container container; - LineBadge lineBadge; + ExpandingBar expandingBar; Add(container = new Container { @@ -28,24 +28,23 @@ namespace osu.Game.Tests.Visual.UserInterface Alpha = 0.5f, RelativeSizeAxes = Axes.Both, }, - lineBadge = new LineBadge + expandingBar = new ExpandingBar { Anchor = Anchor.Centre, - UncollapsedSize = 10, + ExpandedSize = 10, CollapsedSize = 2, Colour = Color4.DeepSkyBlue, } } }); - AddStep(@"", () => { }); - AddStep(@"Collapse", () => lineBadge.Collapse()); - AddStep(@"Uncollapse", () => lineBadge.Uncollapse()); + AddStep(@"Collapse", () => expandingBar.Collapse()); + AddStep(@"Uncollapse", () => expandingBar.Expand()); AddSliderStep(@"Resize container", 1, 300, 150, value => container.ResizeTo(value)); - AddStep(@"Horizontal", () => lineBadge.IsHorizontal = true); - AddStep(@"Anchor top", () => lineBadge.Anchor = Anchor.TopCentre); - AddStep(@"Vertical", () => lineBadge.IsHorizontal = false); - AddStep(@"Anchor left", () => lineBadge.Anchor = Anchor.CentreLeft); + AddStep(@"Horizontal", () => expandingBar.RelativeSizeAxes = Axes.X); + AddStep(@"Anchor top", () => expandingBar.Anchor = Anchor.TopCentre); + AddStep(@"Vertical", () => expandingBar.RelativeSizeAxes = Axes.Y); + AddStep(@"Anchor left", () => expandingBar.Anchor = Anchor.CentreLeft); } } } diff --git a/osu.Game/Graphics/UserInterface/ExpandingBar.cs b/osu.Game/Graphics/UserInterface/ExpandingBar.cs new file mode 100644 index 0000000000..439a6002d8 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/ExpandingBar.cs @@ -0,0 +1,101 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osuTK; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// A rounded bar which can be expanded or collapsed. + /// Generally used for tabs or breadcrumbs. + /// + public class ExpandingBar : Circle + { + private bool isCollapsed; + + public bool IsCollapsed + { + get => isCollapsed; + set + { + if (value == isCollapsed) + return; + + isCollapsed = value; + updateState(); + } + } + + private float expandedSize = 4; + + public float ExpandedSize + { + get => expandedSize; + set + { + if (value == expandedSize) + return; + + expandedSize = value; + updateState(); + } + } + + private float collapsedSize = 2; + + public float CollapsedSize + { + get => collapsedSize; + set + { + if (value == collapsedSize) + return; + + collapsedSize = value; + updateState(); + } + } + + public override Axes RelativeSizeAxes + { + get => base.RelativeSizeAxes; + set + { + base.RelativeSizeAxes = Axes.None; + Size = Vector2.Zero; + + base.RelativeSizeAxes = value; + updateState(); + } + } + + public ExpandingBar() + { + RelativeSizeAxes = Axes.X; + Origin = Anchor.Centre; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + updateState(); + } + + public void Collapse() => IsCollapsed = true; + + public void Expand() => IsCollapsed = false; + + private void updateState() + { + float newSize = IsCollapsed ? CollapsedSize : ExpandedSize; + Easing easingType = IsCollapsed ? Easing.Out : Easing.OutElastic; + + if (RelativeSizeAxes == Axes.X) + this.ResizeHeightTo(newSize, 400, easingType); + else + this.ResizeWidthTo(newSize, 400, easingType); + } + } +} diff --git a/osu.Game/Overlays/Changelog/Components/LineBadge.cs b/osu.Game/Overlays/Changelog/Components/LineBadge.cs deleted file mode 100644 index 84cd712eef..0000000000 --- a/osu.Game/Overlays/Changelog/Components/LineBadge.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Overlays.Changelog.Components -{ - /// - /// A simple rounded expandable line. Set its - /// property to the center of the edge it's meant stick with. By default, - /// takes up the full parent's axis defined by . - /// - public class LineBadge : Circle - { - public float UncollapsedSize; - public float CollapsedSize; - - public bool IsCollapsed { get; private set; } - private bool isHorizontal; - - /// - /// Automatically sets the RelativeSizeAxes and switches X and Y size components when changed. - /// - public bool IsHorizontal - { - get => isHorizontal; - set - { - if (value == isHorizontal) - return; - - if (IsLoaded) - { - FinishTransforms(); - var height = Height; - var width = Width; - RelativeSizeAxes = value ? Axes.X : Axes.Y; - Width = height; - Height = width; - } - else - RelativeSizeAxes = value ? Axes.X : Axes.Y; - - isHorizontal = value; - } - } - - /// Whether to initialize with the - /// or the . - public LineBadge(bool startCollapsed = true) - { - IsCollapsed = startCollapsed; - RelativeSizeAxes = Axes.X; - isHorizontal = true; - Origin = Anchor.Centre; - } - - protected override void LoadComplete() - { - if (isHorizontal) - Height = IsCollapsed ? CollapsedSize : UncollapsedSize; - else - Width = IsCollapsed ? CollapsedSize : UncollapsedSize; - base.LoadComplete(); - } - - public void Collapse(float transitionDuration = 400, Easing easing = Easing.Out) - { - IsCollapsed = true; - if (IsHorizontal) - this.ResizeHeightTo(CollapsedSize, transitionDuration, easing); - else - this.ResizeWidthTo(CollapsedSize, transitionDuration, easing); - } - - public void Uncollapse(float transitionDuration = 400, Easing easing = Easing.OutElastic) - { - IsCollapsed = false; - if (IsHorizontal) - this.ResizeHeightTo(UncollapsedSize, transitionDuration, easing); - else - this.ResizeWidthTo(UncollapsedSize, transitionDuration, easing); - } - } -} diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index bdddc1f968..e3c5cba496 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -12,7 +12,7 @@ using osu.Game.Graphics; using osu.Game.Online.API.Requests.Responses; using System; using osu.Game.Graphics.Sprites; -using osu.Game.Overlays.Changelog.Components; +using osu.Game.Graphics.UserInterface; using osuTK.Graphics; namespace osu.Game.Overlays.Changelog @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Changelog private bool isActivated; - private readonly LineBadge lineBadge; + private readonly ExpandingBar expandingBar; private SampleChannel sampleClick; private SampleChannel sampleHover; @@ -71,11 +71,11 @@ namespace osu.Game.Overlays.Changelog }, } }, - lineBadge = new LineBadge(false) + expandingBar = new ExpandingBar { Anchor = Anchor.TopCentre, Colour = stream.Colour, - UncollapsedSize = 4, + ExpandedSize = 4, CollapsedSize = 2, }, }; @@ -87,7 +87,7 @@ namespace osu.Game.Overlays.Changelog { isActivated = true; this.FadeIn(transition_duration); - lineBadge.Uncollapse(); + expandingBar.Expand(); if (!withoutFiringUpdates) Selected?.Invoke(); } @@ -100,7 +100,7 @@ namespace osu.Game.Overlays.Changelog if (!IsHovered) { this.FadeTo(0.5f, transition_duration); - lineBadge.Collapse(200); + expandingBar.Collapse(); } } @@ -116,7 +116,7 @@ namespace osu.Game.Overlays.Changelog sampleHover?.Play(); DisableDim(); this.FadeIn(transition_duration); - lineBadge.Uncollapse(); + expandingBar.Expand(); return base.OnHover(e); } @@ -125,7 +125,7 @@ namespace osu.Game.Overlays.Changelog if (!isActivated) { this.FadeTo(0.5f, transition_duration); - lineBadge.Collapse(200); + expandingBar.Collapse(); } else EnableDim(); diff --git a/osu.Game/Overlays/OverlayHeaderTabControl.cs b/osu.Game/Overlays/OverlayHeaderTabControl.cs index 21b42cfbf4..dfe7e52420 100644 --- a/osu.Game/Overlays/OverlayHeaderTabControl.cs +++ b/osu.Game/Overlays/OverlayHeaderTabControl.cs @@ -67,7 +67,7 @@ namespace osu.Game.Overlays private class HeaderTabItem : TabItem { private readonly OsuSpriteText text; - private readonly Drawable bar; + private readonly ExpandingBar bar; private Color4 accentColour; @@ -92,7 +92,7 @@ namespace osu.Game.Overlays AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; - Children = new[] + Children = new Drawable[] { text = new OsuSpriteText { @@ -102,12 +102,11 @@ namespace osu.Game.Overlays Text = value, Font = OsuFont.GetFont() }, - bar = new Circle + bar = new ExpandingBar { - RelativeSizeAxes = Axes.X, - Height = 0, - Origin = Anchor.CentreLeft, - Anchor = Anchor.BottomLeft, + Anchor = Anchor.BottomCentre, + ExpandedSize = 7.5f, + CollapsedSize = 0 }, new HoverClickSounds() }; @@ -138,7 +137,7 @@ namespace osu.Game.Overlays if (Active.Value || IsHovered) { text.FadeColour(Color4.White, 120, Easing.InQuad); - bar.ResizeHeightTo(7.5f, 120, Easing.InQuad); + bar.Expand(); if (Active.Value) text.Font = text.Font.With(weight: FontWeight.Bold); @@ -146,7 +145,7 @@ namespace osu.Game.Overlays else { text.FadeColour(AccentColour, 120, Easing.InQuad); - bar.ResizeHeightTo(0, 120, Easing.InQuad); + bar.Collapse(); text.Font = text.Font.With(weight: FontWeight.Medium); } } From 1d5b7cdec0240dab30d65d24b10b490d54a76674 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2019 16:44:47 +0900 Subject: [PATCH 0801/2854] Add ExpandingBar UI element --- .../UserInterface/TestSceneExpandingBar.cs | 50 +++++++++ .../Graphics/UserInterface/ExpandingBar.cs | 101 ++++++++++++++++++ osu.Game/Overlays/OverlayHeaderTabControl.cs | 17 ++- 3 files changed, 159 insertions(+), 9 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneExpandingBar.cs create mode 100644 osu.Game/Graphics/UserInterface/ExpandingBar.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingBar.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingBar.cs new file mode 100644 index 0000000000..974dbf0282 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingBar.cs @@ -0,0 +1,50 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.UserInterface; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneExpandingBar : OsuTestScene + { + public TestSceneExpandingBar() + { + Container container; + ExpandingBar expandingBar; + + Add(container = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Gray, + Alpha = 0.5f, + RelativeSizeAxes = Axes.Both, + }, + expandingBar = new ExpandingBar + { + Anchor = Anchor.Centre, + ExpandedSize = 10, + CollapsedSize = 2, + Colour = Color4.DeepSkyBlue, + } + } + }); + + AddStep(@"Collapse", () => expandingBar.Collapse()); + AddStep(@"Uncollapse", () => expandingBar.Expand()); + AddSliderStep(@"Resize container", 1, 300, 150, value => container.ResizeTo(value)); + AddStep(@"Horizontal", () => expandingBar.RelativeSizeAxes = Axes.X); + AddStep(@"Anchor top", () => expandingBar.Anchor = Anchor.TopCentre); + AddStep(@"Vertical", () => expandingBar.RelativeSizeAxes = Axes.Y); + AddStep(@"Anchor left", () => expandingBar.Anchor = Anchor.CentreLeft); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/ExpandingBar.cs b/osu.Game/Graphics/UserInterface/ExpandingBar.cs new file mode 100644 index 0000000000..439a6002d8 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/ExpandingBar.cs @@ -0,0 +1,101 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osuTK; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// A rounded bar which can be expanded or collapsed. + /// Generally used for tabs or breadcrumbs. + /// + public class ExpandingBar : Circle + { + private bool isCollapsed; + + public bool IsCollapsed + { + get => isCollapsed; + set + { + if (value == isCollapsed) + return; + + isCollapsed = value; + updateState(); + } + } + + private float expandedSize = 4; + + public float ExpandedSize + { + get => expandedSize; + set + { + if (value == expandedSize) + return; + + expandedSize = value; + updateState(); + } + } + + private float collapsedSize = 2; + + public float CollapsedSize + { + get => collapsedSize; + set + { + if (value == collapsedSize) + return; + + collapsedSize = value; + updateState(); + } + } + + public override Axes RelativeSizeAxes + { + get => base.RelativeSizeAxes; + set + { + base.RelativeSizeAxes = Axes.None; + Size = Vector2.Zero; + + base.RelativeSizeAxes = value; + updateState(); + } + } + + public ExpandingBar() + { + RelativeSizeAxes = Axes.X; + Origin = Anchor.Centre; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + updateState(); + } + + public void Collapse() => IsCollapsed = true; + + public void Expand() => IsCollapsed = false; + + private void updateState() + { + float newSize = IsCollapsed ? CollapsedSize : ExpandedSize; + Easing easingType = IsCollapsed ? Easing.Out : Easing.OutElastic; + + if (RelativeSizeAxes == Axes.X) + this.ResizeHeightTo(newSize, 400, easingType); + else + this.ResizeWidthTo(newSize, 400, easingType); + } + } +} diff --git a/osu.Game/Overlays/OverlayHeaderTabControl.cs b/osu.Game/Overlays/OverlayHeaderTabControl.cs index 21b42cfbf4..dfe7e52420 100644 --- a/osu.Game/Overlays/OverlayHeaderTabControl.cs +++ b/osu.Game/Overlays/OverlayHeaderTabControl.cs @@ -67,7 +67,7 @@ namespace osu.Game.Overlays private class HeaderTabItem : TabItem { private readonly OsuSpriteText text; - private readonly Drawable bar; + private readonly ExpandingBar bar; private Color4 accentColour; @@ -92,7 +92,7 @@ namespace osu.Game.Overlays AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; - Children = new[] + Children = new Drawable[] { text = new OsuSpriteText { @@ -102,12 +102,11 @@ namespace osu.Game.Overlays Text = value, Font = OsuFont.GetFont() }, - bar = new Circle + bar = new ExpandingBar { - RelativeSizeAxes = Axes.X, - Height = 0, - Origin = Anchor.CentreLeft, - Anchor = Anchor.BottomLeft, + Anchor = Anchor.BottomCentre, + ExpandedSize = 7.5f, + CollapsedSize = 0 }, new HoverClickSounds() }; @@ -138,7 +137,7 @@ namespace osu.Game.Overlays if (Active.Value || IsHovered) { text.FadeColour(Color4.White, 120, Easing.InQuad); - bar.ResizeHeightTo(7.5f, 120, Easing.InQuad); + bar.Expand(); if (Active.Value) text.Font = text.Font.With(weight: FontWeight.Bold); @@ -146,7 +145,7 @@ namespace osu.Game.Overlays else { text.FadeColour(AccentColour, 120, Easing.InQuad); - bar.ResizeHeightTo(0, 120, Easing.InQuad); + bar.Collapse(); text.Font = text.Font.With(weight: FontWeight.Medium); } } From 9f9e86f18c92d65773714532180fc0cb566bf67b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2019 17:04:21 +0900 Subject: [PATCH 0802/2854] Rename classes and fix back-to-front state --- .../Visual/Online/TestSceneChangelogOverlay.cs | 2 +- osu.Game/Overlays/Changelog/StreamBadge.cs | 1 + .../{BadgeDisplay.cs => StreamBadgeArea.cs} | 4 ++-- osu.Game/Overlays/ChangelogOverlay.cs | 12 ++++++------ 4 files changed, 10 insertions(+), 9 deletions(-) rename osu.Game/Overlays/Changelog/{BadgeDisplay.cs => StreamBadgeArea.cs} (96%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index 6db289efd7..2e8e05fb0f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Online public override IReadOnlyList RequiredTypes => new[] { - typeof(BadgeDisplay), + typeof(StreamBadgeArea), typeof(StreamBadge), typeof(ChangelogHeader), typeof(ChangelogContent), diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/StreamBadge.cs index e3c5cba496..ef1dda5b41 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/StreamBadge.cs @@ -77,6 +77,7 @@ namespace osu.Game.Overlays.Changelog Colour = stream.Colour, ExpandedSize = 4, CollapsedSize = 2, + IsCollapsed = true }, }; } diff --git a/osu.Game/Overlays/Changelog/BadgeDisplay.cs b/osu.Game/Overlays/Changelog/StreamBadgeArea.cs similarity index 96% rename from osu.Game/Overlays/Changelog/BadgeDisplay.cs rename to osu.Game/Overlays/Changelog/StreamBadgeArea.cs index 9b0f152eb0..9d8fc6773d 100644 --- a/osu.Game/Overlays/Changelog/BadgeDisplay.cs +++ b/osu.Game/Overlays/Changelog/StreamBadgeArea.cs @@ -12,7 +12,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays.Changelog { - public class BadgeDisplay : CompositeDrawable + public class StreamBadgeArea : CompositeDrawable { private const float vertical_padding = 20; private const float horizontal_padding = 85; @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Changelog private readonly FillFlowContainer badgesContainer; - public BadgeDisplay() + public StreamBadgeArea() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index daee91416c..4fea4bd220 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays { private ChangelogHeader header; - private BadgeDisplay badges; + private StreamBadgeArea streamBadges; private Container content; @@ -81,7 +81,7 @@ namespace osu.Game.Overlays { ListingSelected = ShowListing, }, - badges = new BadgeDisplay(), + streamBadges = new StreamBadgeArea(), content = new Container { RelativeSizeAxes = Axes.X, @@ -92,7 +92,7 @@ namespace osu.Game.Overlays }, }; - badges.Current.ValueChanged += e => + streamBadges.Current.ValueChanged += e => { if (e.NewValue?.LatestBuild != null && e.NewValue != Current.Value?.UpdateStream) ShowBuild(e.NewValue.LatestBuild); @@ -106,13 +106,13 @@ namespace osu.Game.Overlays { if (e.NewValue != null) { - badges.Current.Value = e.NewValue.UpdateStream; + streamBadges.Current.Value = e.NewValue.UpdateStream; loadContent(new ChangelogSingleBuild(e.NewValue)); } else { - badges.Current.Value = null; + streamBadges.Current.Value = null; loadContent(new ChangelogListing(builds)); } }); @@ -161,7 +161,7 @@ namespace osu.Game.Overlays res.Streams.ForEach(s => s.LatestBuild.UpdateStream = res.Streams.Find(s2 => s2.Id == s.LatestBuild.UpdateStream.Id)); builds = res.Builds; - badges.Populate(res.Streams); + streamBadges.Populate(res.Streams); Current.TriggerChange(); }; From b588638740470bdc97668a9a96bea58aa5f1590b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2019 19:51:16 +0900 Subject: [PATCH 0803/2854] Use TabControl instead of custom logic --- .../Online/TestSceneChangelogOverlay.cs | 4 +- .../Overlays/Changelog/StreamBadgeArea.cs | 83 ------------------- .../{StreamBadge.cs => UpdateStreamBadge.cs} | 35 ++------ .../Changelog/UpdateStreamBadgeArea.cs | 80 ++++++++++++++++++ osu.Game/Overlays/ChangelogOverlay.cs | 12 +-- 5 files changed, 97 insertions(+), 117 deletions(-) delete mode 100644 osu.Game/Overlays/Changelog/StreamBadgeArea.cs rename osu.Game/Overlays/Changelog/{StreamBadge.cs => UpdateStreamBadge.cs} (82%) create mode 100644 osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index 2e8e05fb0f..d1a7730bee 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -17,8 +17,8 @@ namespace osu.Game.Tests.Visual.Online public override IReadOnlyList RequiredTypes => new[] { - typeof(StreamBadgeArea), - typeof(StreamBadge), + typeof(UpdateStreamBadgeArea), + typeof(UpdateStreamBadge), typeof(ChangelogHeader), typeof(ChangelogContent), typeof(ChangelogListing), diff --git a/osu.Game/Overlays/Changelog/StreamBadgeArea.cs b/osu.Game/Overlays/Changelog/StreamBadgeArea.cs deleted file mode 100644 index 9d8fc6773d..0000000000 --- a/osu.Game/Overlays/Changelog/StreamBadgeArea.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; -using osu.Game.Online.API.Requests.Responses; -using System.Collections.Generic; -using osu.Framework.Bindables; -using osuTK.Graphics; - -namespace osu.Game.Overlays.Changelog -{ - public class StreamBadgeArea : CompositeDrawable - { - private const float vertical_padding = 20; - private const float horizontal_padding = 85; - - public readonly Bindable Current = new Bindable(); - - private readonly FillFlowContainer badgesContainer; - - public StreamBadgeArea() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - InternalChildren = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = new Color4(32, 24, 35, 255), - }, - badgesContainer = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Vertical = vertical_padding, Horizontal = horizontal_padding }, - }, - }; - - Current.ValueChanged += e => - { - foreach (StreamBadge streamBadge in badgesContainer) - { - if (!IsHovered || e.NewValue.Id == streamBadge.Stream.Id) - streamBadge.Activate(); - else - streamBadge.Deactivate(); - } - }; - } - - public void Populate(List streams) - { - Current.Value = null; - - foreach (APIUpdateStream updateStream in streams) - { - var streamBadge = new StreamBadge(updateStream); - streamBadge.Selected += () => Current.Value = updateStream; - badgesContainer.Add(streamBadge); - } - } - - protected override bool OnHover(HoverEvent e) - { - foreach (StreamBadge streamBadge in badgesContainer.Children) - streamBadge.EnableDim(); - - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - foreach (StreamBadge streamBadge in badgesContainer.Children) - streamBadge.DisableDim(); - - base.OnHoverLost(e); - } - } -} diff --git a/osu.Game/Overlays/Changelog/StreamBadge.cs b/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs similarity index 82% rename from osu.Game/Overlays/Changelog/StreamBadge.cs rename to osu.Game/Overlays/Changelog/UpdateStreamBadge.cs index ef1dda5b41..9500f080e7 100644 --- a/osu.Game/Overlays/Changelog/StreamBadge.cs +++ b/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs @@ -10,35 +10,30 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Online.API.Requests.Responses; -using System; +using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osuTK.Graphics; namespace osu.Game.Overlays.Changelog { - public class StreamBadge : ClickableContainer + public class UpdateStreamBadge : TabItem { private const float badge_height = 66.5f; private const float badge_width = 100; private const float transition_duration = 100; - public event Action Selected; - - private bool isActivated; + private readonly bool isActivated; private readonly ExpandingBar expandingBar; private SampleChannel sampleClick; private SampleChannel sampleHover; - public readonly APIUpdateStream Stream; - private readonly FillFlowContainer text; - public StreamBadge(APIUpdateStream stream) + public UpdateStreamBadge(APIUpdateStream stream) + : base(stream) { - Stream = stream; - Height = badge_height; Width = stream.IsFeatured ? badge_width * 2 : badge_width; Padding = new MarginPadding(5); @@ -82,32 +77,20 @@ namespace osu.Game.Overlays.Changelog }; } - /// In case we don't want to - /// fire the event. - public void Activate(bool withoutFiringUpdates = true) + protected override void OnActivated() { - isActivated = true; this.FadeIn(transition_duration); expandingBar.Expand(); - if (!withoutFiringUpdates) - Selected?.Invoke(); } - public void Deactivate() + protected override void OnDeactivated() { - isActivated = false; - DisableDim(); - - if (!IsHovered) - { - this.FadeTo(0.5f, transition_duration); - expandingBar.Collapse(); - } + this.FadeTo(0.5f, transition_duration); + expandingBar.Collapse(); } protected override bool OnClick(ClickEvent e) { - Activate(false); sampleClick?.Play(); return base.OnClick(e); } diff --git a/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs b/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs new file mode 100644 index 0000000000..4d357ed944 --- /dev/null +++ b/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs @@ -0,0 +1,80 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Input.Events; +using osu.Game.Online.API.Requests.Responses; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Changelog +{ + public class UpdateStreamBadgeArea : TabControl + { + private const float vertical_padding = 20; + private const float horizontal_padding = 85; + + public UpdateStreamBadgeArea() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + AddInternal(new Box + { + Colour = Color4.Black, + Alpha = 0.12f, + RelativeSizeAxes = Axes.Both, + }); + } + + public void Populate(List streams) + { + Current.Value = null; + + foreach (APIUpdateStream updateStream in streams) + { + AddItem(updateStream); + } + } + + protected override bool OnHover(HoverEvent e) + { + foreach (UpdateStreamBadge streamBadge in TabContainer.Children.OfType()) + streamBadge.EnableDim(); + + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + foreach (UpdateStreamBadge streamBadge in TabContainer.Children.OfType()) + streamBadge.DisableDim(); + + base.OnHoverLost(e); + } + + protected override TabFillFlowContainer CreateTabFlow() + { + var flow = base.CreateTabFlow(); + + flow.RelativeSizeAxes = Axes.X; + flow.AutoSizeAxes = Axes.Y; + flow.AllowMultiline = true; + flow.Padding = new MarginPadding + { + Vertical = vertical_padding, + Horizontal = horizontal_padding, + }; + + return flow; + } + + protected override Dropdown CreateDropdown() => null; + + protected override TabItem CreateTabItem(APIUpdateStream value) => + new UpdateStreamBadge(value); + } +} diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 4fea4bd220..6176e93609 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays { private ChangelogHeader header; - private StreamBadgeArea streamBadges; + private UpdateStreamBadgeArea updateStreamBadges; private Container content; @@ -81,7 +81,7 @@ namespace osu.Game.Overlays { ListingSelected = ShowListing, }, - streamBadges = new StreamBadgeArea(), + updateStreamBadges = new UpdateStreamBadgeArea(), content = new Container { RelativeSizeAxes = Axes.X, @@ -92,7 +92,7 @@ namespace osu.Game.Overlays }, }; - streamBadges.Current.ValueChanged += e => + updateStreamBadges.Current.ValueChanged += e => { if (e.NewValue?.LatestBuild != null && e.NewValue != Current.Value?.UpdateStream) ShowBuild(e.NewValue.LatestBuild); @@ -106,13 +106,13 @@ namespace osu.Game.Overlays { if (e.NewValue != null) { - streamBadges.Current.Value = e.NewValue.UpdateStream; + updateStreamBadges.Current.Value = e.NewValue.UpdateStream; loadContent(new ChangelogSingleBuild(e.NewValue)); } else { - streamBadges.Current.Value = null; + updateStreamBadges.Current.Value = null; loadContent(new ChangelogListing(builds)); } }); @@ -161,7 +161,7 @@ namespace osu.Game.Overlays res.Streams.ForEach(s => s.LatestBuild.UpdateStream = res.Streams.Find(s2 => s2.Id == s.LatestBuild.UpdateStream.Id)); builds = res.Builds; - streamBadges.Populate(res.Streams); + updateStreamBadges.Populate(res.Streams); Current.TriggerChange(); }; From 66f5dbaa9f2f4fea5eaa6870aa57f3fb10f983cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2019 23:44:37 +0900 Subject: [PATCH 0804/2854] Fix badge state regressions from tab control usage --- .../Overlays/Changelog/UpdateStreamBadge.cs | 134 ++++++++++-------- .../Changelog/UpdateStreamBadgeArea.cs | 9 +- 2 files changed, 79 insertions(+), 64 deletions(-) diff --git a/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs b/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs index 9500f080e7..925412f94d 100644 --- a/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs +++ b/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -23,71 +24,70 @@ namespace osu.Game.Overlays.Changelog private const float badge_width = 100; private const float transition_duration = 100; - private readonly bool isActivated; - private readonly ExpandingBar expandingBar; private SampleChannel sampleClick; private SampleChannel sampleHover; private readonly FillFlowContainer text; + public readonly Bindable SelectedTab = new Bindable(); + + private readonly Container fadeContainer; + public UpdateStreamBadge(APIUpdateStream stream) : base(stream) { Height = badge_height; Width = stream.IsFeatured ? badge_width * 2 : badge_width; Padding = new MarginPadding(5); - isActivated = true; - Children = new Drawable[] + Child = fadeContainer = new Container { - text = new FillFlowContainer + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new[] + text = new FillFlowContainer { - new OsuSpriteText + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new[] { - Text = stream.DisplayName, - Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 12), - Margin = new MarginPadding { Top = 6 }, - }, - new OsuSpriteText - { - Text = stream.LatestBuild.DisplayVersion, - Font = OsuFont.GetFont(weight: FontWeight.Light, size: 16), - }, - new OsuSpriteText - { - Text = stream.LatestBuild.Users > 0 ? $"{stream.LatestBuild.Users:N0} users online" : null, - Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 10), - Colour = new Color4(203, 164, 218, 255), - }, - } - }, - expandingBar = new ExpandingBar - { - Anchor = Anchor.TopCentre, - Colour = stream.Colour, - ExpandedSize = 4, - CollapsedSize = 2, - IsCollapsed = true - }, + new OsuSpriteText + { + Text = stream.DisplayName, + Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 12), + Margin = new MarginPadding { Top = 6 }, + }, + new OsuSpriteText + { + Text = stream.LatestBuild.DisplayVersion, + Font = OsuFont.GetFont(weight: FontWeight.Light, size: 16), + }, + new OsuSpriteText + { + Text = stream.LatestBuild.Users > 0 ? $"{stream.LatestBuild.Users:N0} users online" : null, + Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 10), + Colour = new Color4(203, 164, 218, 255), + }, + } + }, + expandingBar = new ExpandingBar + { + Anchor = Anchor.TopCentre, + Colour = stream.Colour, + ExpandedSize = 4, + CollapsedSize = 2, + IsCollapsed = true + }, + } }; + + SelectedTab.ValueChanged += _ => updateState(); } - protected override void OnActivated() - { - this.FadeIn(transition_duration); - expandingBar.Expand(); - } + protected override void OnActivated() => updateState(); - protected override void OnDeactivated() - { - this.FadeTo(0.5f, transition_duration); - expandingBar.Collapse(); - } + protected override void OnDeactivated() => updateState(); protected override bool OnClick(ClickEvent e) { @@ -98,28 +98,46 @@ namespace osu.Game.Overlays.Changelog protected override bool OnHover(HoverEvent e) { sampleHover?.Play(); - DisableDim(); - this.FadeIn(transition_duration); - expandingBar.Expand(); + updateState(); + return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - if (!isActivated) - { - this.FadeTo(0.5f, transition_duration); - expandingBar.Collapse(); - } - else - EnableDim(); - + updateState(); base.OnHoverLost(e); } - public void EnableDim() => text.FadeTo(0.5f, transition_duration); + private void updateState() + { + if (Active.Value || IsHovered || SelectedTab.Value == null) + { + expandingBar.Expand(); + fadeContainer.FadeTo(1, transition_duration); + } + else + { + expandingBar.Collapse(); + fadeContainer.FadeTo(0.5f, transition_duration); + } - public void DisableDim() => text.FadeIn(transition_duration); + text.FadeTo(externalDimRequested && !IsHovered ? 0.5f : 1, transition_duration); + } + + private bool externalDimRequested; + + public void EnableDim() + { + externalDimRequested = true; + updateState(); + } + + public void DisableDim() + { + externalDimRequested = false; + updateState(); + } [BackgroundDependencyLoader] private void load(AudioManager audio) diff --git a/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs b/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs index 4d357ed944..925be48cd1 100644 --- a/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs +++ b/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs @@ -14,9 +14,6 @@ namespace osu.Game.Overlays.Changelog { public class UpdateStreamBadgeArea : TabControl { - private const float vertical_padding = 20; - private const float horizontal_padding = 85; - public UpdateStreamBadgeArea() { RelativeSizeAxes = Axes.X; @@ -65,8 +62,8 @@ namespace osu.Game.Overlays.Changelog flow.AllowMultiline = true; flow.Padding = new MarginPadding { - Vertical = vertical_padding, - Horizontal = horizontal_padding, + Vertical = 20, + Horizontal = 85, }; return flow; @@ -75,6 +72,6 @@ namespace osu.Game.Overlays.Changelog protected override Dropdown CreateDropdown() => null; protected override TabItem CreateTabItem(APIUpdateStream value) => - new UpdateStreamBadge(value); + new UpdateStreamBadge(value) { SelectedTab = { BindTarget = Current } }; } } From 661fc01e7d88920f8e6cc62f9052d577d3d5234e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2019 23:49:54 +0900 Subject: [PATCH 0805/2854] Fix date string --- osu.Game/Overlays/Changelog/ChangelogListing.cs | 4 +--- osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogListing.cs b/osu.Game/Overlays/Changelog/ChangelogListing.cs index ebfcf76738..20b7a32eba 100644 --- a/osu.Game/Overlays/Changelog/ChangelogListing.cs +++ b/osu.Game/Overlays/Changelog/ChangelogListing.cs @@ -46,9 +46,7 @@ namespace osu.Game.Overlays.Changelog Add(new OsuSpriteText { - // do we need .ToUniversalTime() here? - // also, this should be a temporary solution to weekdays in >localized< date strings - Text = build.CreatedAt.Date.ToLongDateString().Replace(build.CreatedAt.ToString("dddd") + ", ", ""), + Text = build.CreatedAt.Date.ToString("dd MMM yyyy"), Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 24), Colour = OsuColour.FromHex(@"FD5"), Anchor = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs index 50a7946ee7..98fe68c015 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs @@ -73,9 +73,7 @@ namespace osu.Game.Overlays.Changelog existing.Add(new OsuSpriteText { - // do we need .ToUniversalTime() here? - // also, this should be a temporary solution to weekdays in >localized< date strings - Text = Build.CreatedAt.Date.ToLongDateString().Replace(Build.CreatedAt.ToString("dddd") + ", ", ""), + Text = Build.CreatedAt.Date.ToString("dd MMM yyyy"), Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 14), Colour = OsuColour.FromHex(@"FD5"), Anchor = Anchor.BottomCentre, From 81e42041e6fab9e336a2f5f18601335d613a3a3f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2019 23:56:50 +0900 Subject: [PATCH 0806/2854] Move update streams inside header content --- osu.Game/Overlays/Changelog/ChangelogHeader.cs | 16 ++++++++++++++-- osu.Game/Overlays/ChangelogOverlay.cs | 18 +----------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index ccc976c4dc..9be6eef295 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -22,6 +22,8 @@ namespace osu.Game.Overlays.Changelog public Action ListingSelected; + public UpdateStreamBadgeArea Streams; + private const string listing_string = "Listing"; public ChangelogHeader() @@ -34,6 +36,12 @@ namespace osu.Game.Overlays.Changelog }; Current.ValueChanged += showBuild; + + Streams.Current.ValueChanged += e => + { + if (e.NewValue?.LatestBuild != null && e.NewValue != Current.Value?.UpdateStream) + Current.Value = e.NewValue.LatestBuild; + }; } [BackgroundDependencyLoader] @@ -54,11 +62,14 @@ namespace osu.Game.Overlays.Changelog TabControl.AddItem(e.NewValue.ToString()); TabControl.Current.Value = e.NewValue.ToString(); + Streams.Current.Value = e.NewValue.UpdateStream; + title.Version = e.NewValue.UpdateStream.DisplayName; } else { TabControl.Current.Value = listing_string; + Streams.Current.Value = null; title.Version = null; } } @@ -67,10 +78,11 @@ namespace osu.Game.Overlays.Changelog protected override Drawable CreateContent() => new Container { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Children = new Drawable[] { - // todo: move badge display here + Streams = new UpdateStreamBadgeArea(), } }; diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 6176e93609..865c6b8de2 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -26,8 +26,6 @@ namespace osu.Game.Overlays { private ChangelogHeader header; - private UpdateStreamBadgeArea updateStreamBadges; - private Container content; private SampleChannel sampleBack; @@ -81,7 +79,6 @@ namespace osu.Game.Overlays { ListingSelected = ShowListing, }, - updateStreamBadges = new UpdateStreamBadgeArea(), content = new Container { RelativeSizeAxes = Axes.X, @@ -92,12 +89,6 @@ namespace osu.Game.Overlays }, }; - updateStreamBadges.Current.ValueChanged += e => - { - if (e.NewValue?.LatestBuild != null && e.NewValue != Current.Value?.UpdateStream) - ShowBuild(e.NewValue.LatestBuild); - }; - sampleBack = audio.Sample.Get(@"UI/generic-select-soft"); header.Current.BindTo(Current); @@ -105,16 +96,9 @@ namespace osu.Game.Overlays Current.BindValueChanged(e => { if (e.NewValue != null) - { - updateStreamBadges.Current.Value = e.NewValue.UpdateStream; - loadContent(new ChangelogSingleBuild(e.NewValue)); - } else - { - updateStreamBadges.Current.Value = null; loadContent(new ChangelogListing(builds)); - } }); } @@ -161,7 +145,7 @@ namespace osu.Game.Overlays res.Streams.ForEach(s => s.LatestBuild.UpdateStream = res.Streams.Find(s2 => s2.Id == s.LatestBuild.UpdateStream.Id)); builds = res.Builds; - updateStreamBadges.Populate(res.Streams); + header.Streams.Populate(res.Streams); Current.TriggerChange(); }; From ba98c68cbd2c017927d35790350dec0031f16377 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2019 00:11:26 +0900 Subject: [PATCH 0807/2854] Add support for osu! user links --- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 09706a419e..2a207efe01 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; using osu.Game.Graphics.Sprites; +using osu.Game.Users; using osuTK.Graphics; namespace osu.Game.Overlays.Changelog @@ -84,18 +85,21 @@ namespace osu.Game.Overlays.Changelog if (!string.IsNullOrEmpty(entry.Repository)) { title.AddText(" (", t => t.Font = OsuFont.GetFont(size: 18)); - title.AddLink($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", - entry.GithubUrl, Online.Chat.LinkAction.External, null, - null, t => { t.Font = OsuFont.GetFont(size: 18); }); + title.AddLink($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", entry.GithubUrl, Online.Chat.LinkAction.External, + creationParameters: t => { t.Font = OsuFont.GetFont(size: 18); }); title.AddText(")", t => t.Font = OsuFont.GetFont(size: 18)); } title.AddText(" by ", t => t.Font = OsuFont.GetFont(size: 14)); - if (entry.GithubUser.GithubUrl != null) - title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, - Online.Chat.LinkAction.External, null, null, - t => t.Font = OsuFont.GetFont(size: 14)); + if (entry.GithubUser.UserId != null) + title.AddUserLink(new User + { + Username = entry.GithubUser.OsuUsername, + Id = entry.GithubUser.UserId.Value + }, t => t.Font = OsuFont.GetFont(size: 14)); + else if (entry.GithubUser.GithubUrl != null) + title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, Online.Chat.LinkAction.External, null, null, t => t.Font = OsuFont.GetFont(size: 14)); else title.AddText(entry.GithubUser.DisplayName, t => t.Font = OsuFont.GetFont(size: 12)); From c96d7bfb672196072efadd39fb1bdf2c63e99725 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2019 00:13:47 +0900 Subject: [PATCH 0808/2854] Centralise font specification --- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 2a207efe01..6d0b7a8739 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; @@ -65,6 +65,9 @@ namespace osu.Game.Overlays.Changelog }); foreach (APIChangelogEntry entry in category.Value) + var fontMedium = OsuFont.GetFont(size: 14); + var fontSmall = OsuFont.GetFont(size: 12); + { LinkFlowContainer title = new LinkFlowContainer { @@ -76,51 +79,51 @@ namespace osu.Game.Overlays.Changelog title.AddIcon(FontAwesome.Solid.Check, t => { - t.Font = OsuFont.GetFont(size: 12); + t.Font = fontSmall; t.Padding = new MarginPadding { Left = -17, Right = 5 }; }); - title.AddText(entry.Title, t => { t.Font = OsuFont.GetFont(size: 18); }); + title.AddText(entry.Title, t => { t.Font = fontLarge; }); if (!string.IsNullOrEmpty(entry.Repository)) { - title.AddText(" (", t => t.Font = OsuFont.GetFont(size: 18)); + title.AddText(" (", t => t.Font = fontLarge); title.AddLink($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", entry.GithubUrl, Online.Chat.LinkAction.External, - creationParameters: t => { t.Font = OsuFont.GetFont(size: 18); }); - title.AddText(")", t => t.Font = OsuFont.GetFont(size: 18)); + creationParameters: t => { t.Font = fontLarge; }); + title.AddText(")", t => t.Font = fontLarge); } - title.AddText(" by ", t => t.Font = OsuFont.GetFont(size: 14)); + title.AddText(" by ", t => t.Font = fontMedium); if (entry.GithubUser.UserId != null) title.AddUserLink(new User { Username = entry.GithubUser.OsuUsername, Id = entry.GithubUser.UserId.Value - }, t => t.Font = OsuFont.GetFont(size: 14)); + }, t => t.Font = fontMedium); else if (entry.GithubUser.GithubUrl != null) - title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, Online.Chat.LinkAction.External, null, null, t => t.Font = OsuFont.GetFont(size: 14)); + title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, Online.Chat.LinkAction.External, null, null, t => t.Font = fontMedium); else - title.AddText(entry.GithubUser.DisplayName, t => t.Font = OsuFont.GetFont(size: 12)); + title.AddText(entry.GithubUser.DisplayName, t => t.Font = fontSmall); ChangelogEntries.Add(title); if (!string.IsNullOrEmpty(entry.MessageHtml)) { - TextFlowContainer messageContainer = new TextFlowContainer + TextFlowContainer message = new TextFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, }; // todo: use markdown parsing once API returns markdown - messageContainer.AddText(Regex.Replace(entry.MessageHtml, @"<(.|\n)*?>", string.Empty), t => + message.AddText(Regex.Replace(entry.MessageHtml, @"<(.|\n)*?>", string.Empty), t => { - t.Font = OsuFont.GetFont(size: 12); + t.Font = fontSmall; t.Colour = new Color4(235, 184, 254, 255); }); - ChangelogEntries.Add(messageContainer); + ChangelogEntries.Add(message); } } } From a0ddc6d77a3930bad3abb540b2a91e2caed863da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2019 00:13:59 +0900 Subject: [PATCH 0809/2854] Use linq instead of a temporary sorted list --- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 6d0b7a8739..a76211a0ae 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; @@ -8,7 +8,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests.Responses; using System; -using System.Collections.Generic; +using System.Linq; using System.Text.RegularExpressions; using osu.Game.Graphics.Sprites; using osu.Game.Users; @@ -44,30 +44,20 @@ namespace osu.Game.Overlays.Changelog }, }; - var categories = new SortedDictionary>(); - - // sort entries by category - foreach (APIChangelogEntry entry in build.ChangelogEntries) - { - if (!categories.ContainsKey(entry.Category)) - categories.Add(entry.Category, new List { entry }); - else - categories[entry.Category].Add(entry); - } - - foreach (KeyValuePair> category in categories) + foreach (var categoryEntries in build.ChangelogEntries.GroupBy(b => b.Category).OrderBy(c => c.Key)) { ChangelogEntries.Add(new OsuSpriteText { - Text = category.Key, + Text = categoryEntries.Key, Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 24), Margin = new MarginPadding { Top = 35, Bottom = 15 }, }); - foreach (APIChangelogEntry entry in category.Value) + var fontLarge = OsuFont.GetFont(size: 18); var fontMedium = OsuFont.GetFont(size: 14); var fontSmall = OsuFont.GetFont(size: 12); + foreach (APIChangelogEntry entry in categoryEntries) { LinkFlowContainer title = new LinkFlowContainer { From cb620082802164d24b793ae990134db7f5857d93 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2019 11:23:24 +0900 Subject: [PATCH 0810/2854] Cleanup pass --- osu.Game/Graphics/OsuColour.cs | 2 + .../Changelog/UpdateStreamBadgeArea.cs | 5 +-- osu.Game/Overlays/ChangelogOverlay.cs | 37 +++++++++---------- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 53693a1e38..63ec24f84f 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -40,8 +40,10 @@ namespace osu.Game.Graphics // See https://github.com/ppy/osu-web/blob/master/resources/assets/less/colors.less public readonly Color4 PurpleLighter = FromHex(@"eeeeff"); public readonly Color4 PurpleLight = FromHex(@"aa88ff"); + public readonly Color4 PurpleLightAlternative = FromHex(@"cba4da"); public readonly Color4 Purple = FromHex(@"8866ee"); public readonly Color4 PurpleDark = FromHex(@"6644cc"); + public readonly Color4 PurpleDarkAlternative = FromHex(@"312436"); public readonly Color4 PurpleDarker = FromHex(@"441188"); public readonly Color4 PinkLighter = FromHex(@"ffddee"); diff --git a/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs b/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs index 925be48cd1..f564f03652 100644 --- a/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs +++ b/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs @@ -31,10 +31,7 @@ namespace osu.Game.Overlays.Changelog { Current.Value = null; - foreach (APIUpdateStream updateStream in streams) - { - AddItem(updateStream); - } + foreach (APIUpdateStream updateStream in streams) AddItem(updateStream); } protected override bool OnHover(HoverEvent e) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 865c6b8de2..17d2ab93dc 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -18,12 +18,13 @@ using osu.Game.Input.Bindings; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Changelog; -using osuTK.Graphics; namespace osu.Game.Overlays { public class ChangelogOverlay : FullscreenOverlay { + public readonly Bindable Current = new Bindable(); + private ChangelogHeader header; private Container content; @@ -32,23 +33,6 @@ namespace osu.Game.Overlays private List builds; - public readonly Bindable Current = new Bindable(); - - public void ShowListing() => Current.Value = null; - - /// - /// Fetches and shows a specific build from a specific update stream. - /// - /// Must contain at least and - /// . If and - /// are specified, the header will instantly display them. - public void ShowBuild([NotNull] APIChangelogBuild build) - { - if (build == null) throw new ArgumentNullException(nameof(build)); - - Current.Value = build; - } - [BackgroundDependencyLoader] private void load(AudioManager audio, OsuColour colour) { @@ -62,7 +46,7 @@ namespace osu.Game.Overlays new Box { RelativeSizeAxes = Axes.Both, - Colour = new Color4(49, 36, 54, 255), + Colour = colour.PurpleDarkAlternative, }, new ScrollContainer { @@ -102,6 +86,21 @@ namespace osu.Game.Overlays }); } + public void ShowListing() => Current.Value = null; + + /// + /// Fetches and shows a specific build from a specific update stream. + /// + /// Must contain at least and + /// . If and + /// are specified, the header will instantly display them. + public void ShowBuild([NotNull] APIChangelogBuild build) + { + if (build == null) throw new ArgumentNullException(nameof(build)); + + Current.Value = build; + } + public override bool OnPressed(GlobalAction action) { switch (action) From 5a887dabfe660e40ed0d962e392d095c4b0dcdfa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2019 11:38:13 +0900 Subject: [PATCH 0811/2854] Prepare changelog api requests to work when not logged in --- .../Changelog/ChangelogSingleBuild.cs | 23 +++++++++------- osu.Game/Overlays/ChangelogOverlay.cs | 26 +++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs index 98fe68c015..81c7905e84 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs @@ -33,26 +33,31 @@ namespace osu.Game.Overlays.Changelog [BackgroundDependencyLoader] private void load(CancellationToken? cancellation, IAPIProvider api) { - var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); bool complete = false; + var req = new GetChangelogBuildRequest(build.UpdateStream.Name, build.Version); req.Success += res => { build = res; complete = true; }; - req.Failure += _ => complete = true; - api.Queue(req); + Task.Run(() => req.Perform(api)); - while (!complete && cancellation?.IsCancellationRequested != true) - Task.Delay(1); - - Children = new Drawable[] + while (!complete) { - new ChangelogBuildWithNavigation(build) { SelectBuild = SelectBuild }, - }; + if (cancellation?.IsCancellationRequested == true) + { + req.Cancel(); + return; + } + + Task.Delay(1); + } + + if (build != null) + Child = new ChangelogBuildWithNavigation(build) { SelectBuild = SelectBuild }; } public class ChangelogBuildWithNavigation : ChangelogBuild diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 17d2ab93dc..6ce82d342a 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Threading; +using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -136,21 +137,24 @@ namespace osu.Game.Overlays { initialFetchPerformed = true; - var req = new GetChangelogRequest(); - req.Success += res => + Task.Run(() => { - // remap streams to builds to ensure model equality - res.Builds.ForEach(b => b.UpdateStream = res.Streams.Find(s => s.Id == b.UpdateStream.Id)); - res.Streams.ForEach(s => s.LatestBuild.UpdateStream = res.Streams.Find(s2 => s2.Id == s.LatestBuild.UpdateStream.Id)); + var req = new GetChangelogRequest(); + req.Success += res => + { + // remap streams to builds to ensure model equality + res.Builds.ForEach(b => b.UpdateStream = res.Streams.Find(s => s.Id == b.UpdateStream.Id)); + res.Streams.ForEach(s => s.LatestBuild.UpdateStream = res.Streams.Find(s2 => s2.Id == s.LatestBuild.UpdateStream.Id)); - builds = res.Builds; - header.Streams.Populate(res.Streams); + builds = res.Builds; + header.Streams.Populate(res.Streams); - Current.TriggerChange(); - }; - req.Failure += _ => initialFetchPerformed = false; + Current.TriggerChange(); + }; + req.Failure += _ => initialFetchPerformed = false; - API.Queue(req); + req.Perform(API); + }); } private CancellationTokenSource loadContentTask; From 372c5b081c985261d3f8cc0e43247600a6a646e9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2019 12:07:22 +0900 Subject: [PATCH 0812/2854] Fix audio preview buttons not correctly handling load failure states Closes #4593. --- osu.Game/Audio/PreviewTrack.cs | 25 ++++++++++++++++--------- osu.Game/Overlays/Direct/PlayButton.cs | 10 ++++++++-- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/osu.Game/Audio/PreviewTrack.cs b/osu.Game/Audio/PreviewTrack.cs index 3b21bdefc4..22ce7d4711 100644 --- a/osu.Game/Audio/PreviewTrack.cs +++ b/osu.Game/Audio/PreviewTrack.cs @@ -28,7 +28,8 @@ namespace osu.Game.Audio private void load() { track = GetTrack(); - track.Completed += () => Schedule(Stop); + if (track != null) + track.Completed += () => Schedule(Stop); } /// @@ -56,19 +57,25 @@ namespace osu.Game.Audio /// /// Starts playing this . /// - public void Start() => startDelegate = Schedule(() => + /// Whether the track is started or already playing. + public bool Start() { if (track == null) - return; + return false; - if (hasStarted) - return; + startDelegate = Schedule(() => + { + if (hasStarted) + return; - hasStarted = true; + hasStarted = true; - track.Restart(); - Started?.Invoke(); - }); + track.Restart(); + Started?.Invoke(); + }); + + return true; + } /// /// Stops playing this . diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 6daebb3c15..2a77e7ca26 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -129,7 +129,7 @@ namespace osu.Game.Overlays.Direct if (Preview != null) { - Preview.Start(); + attemptStart(); return; } @@ -147,7 +147,7 @@ namespace osu.Game.Overlays.Direct // user may have changed their mind. if (Playing.Value) - preview.Start(); + attemptStart(); }); } else @@ -157,6 +157,12 @@ namespace osu.Game.Overlays.Direct } } + private void attemptStart() + { + if (Preview?.Start() != true) + Playing.Value = false; + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From 7229975fef4b4ea6d365e459fe3d33f90bbefd47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2019 12:41:45 +0900 Subject: [PATCH 0813/2854] Further minor refactoring --- osu.Game/Overlays/Changelog/UpdateStreamBadge.cs | 8 +++++--- osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs b/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs index 925412f94d..c39e6a6784 100644 --- a/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs +++ b/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using Humanizer; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -14,6 +15,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osuTK; using osuTK.Graphics; namespace osu.Game.Overlays.Changelog @@ -37,9 +39,9 @@ namespace osu.Game.Overlays.Changelog public UpdateStreamBadge(APIUpdateStream stream) : base(stream) { - Height = badge_height; - Width = stream.IsFeatured ? badge_width * 2 : badge_width; + Size = new Vector2(stream.IsFeatured ? badge_width * 2 : badge_width, badge_height); Padding = new MarginPadding(5); + Child = fadeContainer = new Container { RelativeSizeAxes = Axes.Both, @@ -65,7 +67,7 @@ namespace osu.Game.Overlays.Changelog }, new OsuSpriteText { - Text = stream.LatestBuild.Users > 0 ? $"{stream.LatestBuild.Users:N0} users online" : null, + Text = stream.LatestBuild.Users > 0 ? $"{stream.LatestBuild.Users:N0} {"user".Pluralize(stream.LatestBuild.Users == 1)} online" : null, Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 10), Colour = new Color4(203, 164, 218, 255), }, diff --git a/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs b/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs index f564f03652..2b48811bd6 100644 --- a/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs +++ b/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs @@ -31,7 +31,8 @@ namespace osu.Game.Overlays.Changelog { Current.Value = null; - foreach (APIUpdateStream updateStream in streams) AddItem(updateStream); + foreach (APIUpdateStream updateStream in streams) + AddItem(updateStream); } protected override bool OnHover(HoverEvent e) From edfe47fb0268fdadd0848595cdcc1c7f046a32ed Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 May 2019 13:37:25 +0900 Subject: [PATCH 0814/2854] Rename button --- osu.Game.Tests/Visual/UserInterface/TestSceneExpandingBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingBar.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingBar.cs index 974dbf0282..f92aae43d2 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingBar.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingBar.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.UserInterface }); AddStep(@"Collapse", () => expandingBar.Collapse()); - AddStep(@"Uncollapse", () => expandingBar.Expand()); + AddStep(@"Expand", () => expandingBar.Expand()); AddSliderStep(@"Resize container", 1, 300, 150, value => container.ResizeTo(value)); AddStep(@"Horizontal", () => expandingBar.RelativeSizeAxes = Axes.X); AddStep(@"Anchor top", () => expandingBar.Anchor = Anchor.TopCentre); From 7971261fbc6ea50a6b488b082538e16349057896 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 May 2019 15:59:22 +0900 Subject: [PATCH 0815/2854] Don't create a new drawable for the first background --- .../Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs index bd80919851..2d83c1de01 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs @@ -39,7 +39,7 @@ namespace osu.Game.Beatmaps.Drawables { // If DelayedLoadUnloadWrapper is attempting to RELOAD the same content (Beatmap), that means that it was // previously UNLOADED and thus its children have been disposed of, so we need to recreate them here. - if (lastModel == Beatmap.Value) + if (lastModel != null && lastModel == Beatmap.Value) return CreateDrawable(Beatmap.Value); // If the model has changed since the previous unload (or if there was no load), then we can safely use the given content From f0d7728e81d91d4704bf180998e20b286a505574 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 May 2019 17:01:13 +0900 Subject: [PATCH 0816/2854] Fix failures with a null-model --- .../Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs index 2d83c1de01..96786f5f49 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs @@ -32,6 +32,7 @@ namespace osu.Game.Beatmaps.Drawables protected virtual double UnloadDelay => 10000; private BeatmapInfo lastModel; + private bool firstLoad = true; protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Drawable content, double timeBeforeLoad) { @@ -39,11 +40,12 @@ namespace osu.Game.Beatmaps.Drawables { // If DelayedLoadUnloadWrapper is attempting to RELOAD the same content (Beatmap), that means that it was // previously UNLOADED and thus its children have been disposed of, so we need to recreate them here. - if (lastModel != null && lastModel == Beatmap.Value) + if (!firstLoad && lastModel == Beatmap.Value) return CreateDrawable(Beatmap.Value); // If the model has changed since the previous unload (or if there was no load), then we can safely use the given content lastModel = Beatmap.Value; + firstLoad = false; return content; }, timeBeforeLoad, UnloadDelay); } From 9c6fbebb67040cf8ce367480cbda5a29ef647652 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2019 17:55:18 +0900 Subject: [PATCH 0817/2854] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 98b158134d..b77c724d1b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index b7549eeaef..fc047aa5f0 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From acaf2f9fbb538a3aa335ce1319ea624a33486a91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2019 18:54:42 +0900 Subject: [PATCH 0818/2854] Show changelog from new build notification --- osu.Desktop/Overlays/VersionManager.cs | 25 +++++++----- osu.Game/Overlays/ChangelogOverlay.cs | 56 +++++++++++++++++++++----- 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index e9c5d06f3c..7e4257c23b 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -96,33 +95,37 @@ namespace osu.Desktop.Overlays var version = game.Version; var lastVersion = config.Get(OsuSetting.Version); - if (game.IsDeployedBuild && version != lastVersion) + //if (game.IsDeployedBuild && 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)) - notificationOverlay.Post(new UpdateCompleteNotification(version, host.OpenUrlExternally)); + notificationOverlay.Post(new UpdateCompleteNotification(version)); } } private class UpdateCompleteNotification : SimpleNotification { - public UpdateCompleteNotification(string version, Action openUrl = null) + private readonly string version; + + public UpdateCompleteNotification(string version) { + this.version = version; Text = $"You are now running osu!lazer {version}.\nClick to see what's new!"; - Icon = FontAwesome.Solid.CheckSquare; - Activated = delegate - { - openUrl?.Invoke($"https://osu.ppy.sh/home/changelog/lazer/{version}"); - return true; - }; } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, ChangelogOverlay changelog) { + Icon = FontAwesome.Solid.CheckSquare; IconBackgound.Colour = colours.BlueDark; + + Activated = delegate + { + changelog.ShowBuild("lazer", version); + return true; + }; } } diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 6ce82d342a..a957227c6b 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -34,6 +34,8 @@ namespace osu.Game.Overlays private List builds; + private List streams; + [BackgroundDependencyLoader] private void load(AudioManager audio, OsuColour colour) { @@ -87,7 +89,11 @@ namespace osu.Game.Overlays }); } - public void ShowListing() => Current.Value = null; + public void ShowListing() + { + Current.Value = null; + State = Visibility.Visible; + } /// /// Fetches and shows a specific build from a specific update stream. @@ -100,6 +106,27 @@ namespace osu.Game.Overlays if (build == null) throw new ArgumentNullException(nameof(build)); Current.Value = build; + State = Visibility.Visible; + } + + public void ShowBuild([NotNull] string updateStream, [NotNull] string version) + { + if (updateStream == null) throw new ArgumentNullException(nameof(updateStream)); + if (version == null) throw new ArgumentNullException(nameof(version)); + + performAfterFetch(() => + { + var build = builds.Find(b => b.Version == version && b.UpdateStream.Name == updateStream) + ?? streams.Find(s => s.Name == updateStream)?.LatestBuild; + + if (build != null) + { + Current.Value = build; + State = Visibility.Visible; + } + }); + + State = Visibility.Visible; } public override bool OnPressed(GlobalAction action) @@ -127,15 +154,23 @@ namespace osu.Game.Overlays { base.PopIn(); - if (!initialFetchPerformed) - fetchListing(); + if (initialFetchTask == null) + // fetch and refresh to show listing, if no other request was made via Show methods + performAfterFetch(() => Current.TriggerChange()); } - private bool initialFetchPerformed; + private Task initialFetchTask; - private void fetchListing() + private void performAfterFetch(Action action) => fetchListing()?.ContinueWith(_ => Schedule(action)); + + private Task fetchListing() { - initialFetchPerformed = true; + if (initialFetchTask != null) + return initialFetchTask; + + var tcs = new TaskCompletionSource(); + + initialFetchTask = tcs.Task; Task.Run(() => { @@ -147,14 +182,17 @@ namespace osu.Game.Overlays res.Streams.ForEach(s => s.LatestBuild.UpdateStream = res.Streams.Find(s2 => s2.Id == s.LatestBuild.UpdateStream.Id)); builds = res.Builds; + streams = res.Streams; + header.Streams.Populate(res.Streams); - Current.TriggerChange(); + tcs.SetResult(true); }; - req.Failure += _ => initialFetchPerformed = false; - + req.Failure += _ => initialFetchTask = null; req.Perform(API); }); + + return initialFetchTask; } private CancellationTokenSource loadContentTask; From e034b3d514af9ec9e5e6d9976ecd8617ffb0b1dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2019 19:08:44 +0900 Subject: [PATCH 0819/2854] Use TaskCompletionSource in a better manner --- osu.Desktop/Overlays/VersionManager.cs | 5 +---- osu.Game/Overlays/ChangelogOverlay.cs | 12 +++++------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 7e4257c23b..2bba1723ec 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Platform; using osu.Game; using osu.Game.Configuration; using osu.Game.Graphics; @@ -24,15 +23,13 @@ namespace osu.Desktop.Overlays private OsuConfigManager config; private OsuGameBase game; private NotificationOverlay notificationOverlay; - private GameHost host; [BackgroundDependencyLoader] - private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config, GameHost host) + private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config) { notificationOverlay = notification; this.config = config; this.game = game; - this.host = host; AutoSizeAxes = Axes.Both; Anchor = Anchor.BottomCentre; diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index a957227c6b..552e213a45 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -168,12 +168,10 @@ namespace osu.Game.Overlays if (initialFetchTask != null) return initialFetchTask; - var tcs = new TaskCompletionSource(); - - initialFetchTask = tcs.Task; - - Task.Run(() => + return initialFetchTask = Task.Run(async () => { + var tcs = new TaskCompletionSource(); + var req = new GetChangelogRequest(); req.Success += res => { @@ -190,9 +188,9 @@ namespace osu.Game.Overlays }; req.Failure += _ => initialFetchTask = null; req.Perform(API); - }); - return initialFetchTask; + await tcs.Task; + }); } private CancellationTokenSource loadContentTask; From 492dd3eee266cb59c3882cc868d2762dc6cb6fba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 May 2019 10:53:02 +0900 Subject: [PATCH 0820/2854] Restore accidentally commented conditional --- osu.Desktop/Overlays/VersionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 2bba1723ec..d2aad99f41 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -92,7 +92,7 @@ namespace osu.Desktop.Overlays var version = game.Version; var lastVersion = config.Get(OsuSetting.Version); - //if (game.IsDeployedBuild && version != lastVersion) + if (game.IsDeployedBuild && version != lastVersion) { config.Set(OsuSetting.Version, version); From a272004610ec31fa7499ccfb69ccd2df4f737bc8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 May 2019 11:04:36 +0900 Subject: [PATCH 0821/2854] Use a more friendly set method for tab control --- osu.Game/Overlays/Changelog/ChangelogHeader.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 9be6eef295..fca62fbb44 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -62,7 +63,7 @@ namespace osu.Game.Overlays.Changelog TabControl.AddItem(e.NewValue.ToString()); TabControl.Current.Value = e.NewValue.ToString(); - Streams.Current.Value = e.NewValue.UpdateStream; + Streams.Current.Value = Streams.Items.FirstOrDefault(s => s.Name == e.NewValue.UpdateStream.Name); title.Version = e.NewValue.UpdateStream.DisplayName; } From cda97a61fa751d6f2b2b643d37a6fa994d1b9b86 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Fri, 24 May 2019 19:43:53 +0300 Subject: [PATCH 0822/2854] Add a bit of smoothness to the rank graph --- .../Visual/Online/TestSceneRankGraph.cs | 25 ++++++++ .../Profile/Header/Components/RankGraph.cs | 63 ++++++++++--------- 2 files changed, 58 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs b/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs index c04a4249cc..709e75ab13 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs @@ -31,6 +31,7 @@ namespace osu.Game.Tests.Visual.Online var data = new int[89]; var dataWithZeros = new int[89]; var smallData = new int[89]; + var edgyData = new int[89]; for (int i = 0; i < 89; i++) data[i] = dataWithZeros[i] = (i + 1) * 1000; @@ -41,6 +42,14 @@ namespace osu.Game.Tests.Visual.Online for (int i = 79; i < 89; i++) smallData[i] = 100000 - i * 1000; + bool edge = true; + + for (int i = 0; i < 20; i++) + { + edgyData[i] = 100000 + (edge ? 1000 : -1000) * (i + 1); + edge = !edge; + } + Add(new Container { Anchor = Anchor.Centre, @@ -120,6 +129,22 @@ namespace osu.Game.Tests.Visual.Online } }; }); + + AddStep("graph with edges", () => + { + graph.User.Value = new User + { + Statistics = new UserStatistics + { + Ranks = new UserStatistics.UserRanks { Global = 12000 }, + PP = 12345, + }, + RankHistory = new User.RankHistoryData + { + Data = edgyData, + } + }; + }); } } } diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index 85ea2a175a..5ad6db9ff1 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -96,7 +96,7 @@ namespace osu.Game.Overlays.Profile.Header.Components if (ranks?.Length > 1) { graph.UpdateBallPosition(e.MousePosition.X); - graph.ShowBall(); + graph.ShowBar(); } return base.OnHover(e); @@ -114,7 +114,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { if (ranks?.Length > 1) { - graph.HideBall(); + graph.HideBar(); } base.OnHoverLost(e); @@ -123,31 +123,41 @@ namespace osu.Game.Overlays.Profile.Header.Components private class RankChartLineGraph : LineGraph { private readonly CircularContainer movingBall; + private readonly Container bar; private readonly Box ballBg; - private readonly Box movingBar; + private readonly Box line; public Action OnBallMove; public RankChartLineGraph() { - Add(movingBar = new Box + Add(bar = new Container { Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.Y, - Width = 1.5f, + AutoSizeAxes = Axes.X, Alpha = 0, RelativePositionAxes = Axes.Both, - }); - - Add(movingBall = new CircularContainer - { - Origin = Anchor.Centre, - Size = new Vector2(18), - Alpha = 0, - Masking = true, - BorderThickness = 4, - RelativePositionAxes = Axes.Both, - Child = ballBg = new Box { RelativeSizeAxes = Axes.Both } + Children = new Drawable[] + { + line = new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Width = 1.5f, + }, + movingBall = new CircularContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + Size = new Vector2(18), + Masking = true, + BorderThickness = 4, + RelativePositionAxes = Axes.Y, + Child = ballBg = new Box { RelativeSizeAxes = Axes.Both } + } + } }); } @@ -155,29 +165,22 @@ namespace osu.Game.Overlays.Profile.Header.Components private void load(OsuColour colours) { ballBg.Colour = colours.GreySeafoamDarker; - movingBall.BorderColour = colours.Yellow; - movingBar.Colour = colours.Yellow; + movingBall.BorderColour = line.Colour = colours.Yellow; } public void UpdateBallPosition(float mouseXPosition) { + int duration = 200; int index = calculateIndex(mouseXPosition); - movingBall.Position = calculateBallPosition(index); - movingBar.X = movingBall.X; + Vector2 position = calculateBallPosition(index); + movingBall.MoveToY(position.Y, duration, Easing.OutQuint); + bar.MoveToX(position.X, duration, Easing.OutQuint); OnBallMove.Invoke(index); } - public void ShowBall() - { - movingBall.FadeIn(fade_duration); - movingBar.FadeIn(fade_duration); - } + public void ShowBar() => bar.FadeIn(fade_duration); - public void HideBall() - { - movingBall.FadeOut(fade_duration); - movingBar.FadeOut(fade_duration); - } + public void HideBar() => bar.FadeOut(fade_duration); private int calculateIndex(float mouseXPosition) => (int)Math.Round(mouseXPosition / DrawWidth * (DefaultValueCount - 1)); From 69ada11f4137ec09a2ffd22276a2f6e0680c4cbc Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Fri, 24 May 2019 20:01:47 +0300 Subject: [PATCH 0823/2854] use constant value --- osu.Game/Overlays/Profile/Header/Components/RankGraph.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index 5ad6db9ff1..5f79386b76 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -170,7 +170,7 @@ namespace osu.Game.Overlays.Profile.Header.Components public void UpdateBallPosition(float mouseXPosition) { - int duration = 200; + const int duration = 200; int index = calculateIndex(mouseXPosition); Vector2 position = calculateBallPosition(index); movingBall.MoveToY(position.Y, duration, Easing.OutQuint); From b9f6372c3fa240bec91c25ded502e4f3e9c484fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 25 May 2019 22:11:11 +0900 Subject: [PATCH 0824/2854] Fix Aquatico font lookups --- osu.Game/Graphics/OsuFont.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/OsuFont.cs b/osu.Game/Graphics/OsuFont.cs index 86a14a720e..6c4b46c3ad 100644 --- a/osu.Game/Graphics/OsuFont.cs +++ b/osu.Game/Graphics/OsuFont.cs @@ -73,7 +73,7 @@ namespace osu.Game.Graphics string weightString = weight.ToString(); // Only exo has an explicit "regular" weight, other fonts do not - if (family != GetFamilyString(Typeface.Exo) && weight == FontWeight.Regular) + if (weight == FontWeight.Regular && family != GetFamilyString(Typeface.Exo) && family != GetFamilyString(Typeface.Aquatico)) weightString = string.Empty; return weightString; From 5d77ae4a1e54a548548449b20da3d77fbdb86d9a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 May 2019 10:25:17 +0900 Subject: [PATCH 0825/2854] Fix regression in FileBasedIPC implementation --- osu.Game.Tournament/IPC/FileBasedIPC.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 1086991eb4..8be10e2089 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Logging; +using osu.Framework.Platform; using osu.Framework.Platform.Windows; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; @@ -26,17 +27,17 @@ namespace osu.Game.Tournament.IPC private int lastBeatmapId; [BackgroundDependencyLoader] - private void load(LadderInfo ladder) + private void load(LadderInfo ladder, GameHost host) { StableStorage stable; try { - stable = new StableStorage(); + stable = new StableStorage(host as DesktopGameHost); } - catch + catch (Exception e) { - Logger.Log("Stable installation could not be found; disabling file based IPC"); + Logger.Error(e, "Stable installation could not be found; disabling file based IPC"); return; } @@ -170,8 +171,8 @@ namespace osu.Game.Tournament.IPC } } - public StableStorage() - : base(string.Empty, null) + public StableStorage(DesktopGameHost host) + : base(string.Empty, host) { } } From 53b22453305f3ed1dc676412115485fcfb5bbf4b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 May 2019 01:36:07 +0900 Subject: [PATCH 0826/2854] Move common settings sub-panel logic to own class --- osu.Game/Overlays/KeyBindingPanel.cs | 88 +--------------------- osu.Game/Overlays/SettingsPanel.cs | 2 - osu.Game/Overlays/SettingsSubPanel.cs | 103 ++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 89 deletions(-) create mode 100644 osu.Game/Overlays/SettingsSubPanel.cs diff --git a/osu.Game/Overlays/KeyBindingPanel.cs b/osu.Game/Overlays/KeyBindingPanel.cs index 301c8faca2..928bd080fa 100644 --- a/osu.Game/Overlays/KeyBindingPanel.cs +++ b/osu.Game/Overlays/KeyBindingPanel.cs @@ -3,22 +3,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; using osu.Game.Overlays.KeyBinding; using osu.Game.Overlays.Settings; using osu.Game.Rulesets; -using osu.Game.Screens.Ranking; -using osuTK; namespace osu.Game.Overlays { - public class KeyBindingPanel : SettingsPanel + public class KeyBindingPanel : SettingsSubPanel { protected override Drawable CreateHeader() => new SettingsHeader("key configuration", "Customise your keys!"); @@ -29,84 +21,6 @@ namespace osu.Game.Overlays foreach (var ruleset in rulesets.AvailableRulesets) AddSection(new RulesetBindingsSection(ruleset)); - - AddInternal(new BackButton - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Action = Hide - }); - } - - public KeyBindingPanel() - : base(true) - { - } - - private class BackButton : OsuClickableContainer, IKeyBindingHandler - { - private AspectContainer aspect; - - [BackgroundDependencyLoader] - private void load() - { - Size = new Vector2(Sidebar.DEFAULT_WIDTH); - Children = new Drawable[] - { - aspect = new AspectContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - Children = new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Y = -15, - Size = new Vector2(15), - Shadow = true, - Icon = FontAwesome.Solid.ChevronLeft - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Y = 15, - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), - Text = @"back", - }, - } - } - }; - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - aspect.ScaleTo(0.75f, 2000, Easing.OutQuint); - return base.OnMouseDown(e); - } - - protected override bool OnMouseUp(MouseUpEvent e) - { - aspect.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(e); - } - - public bool OnPressed(GlobalAction action) - { - switch (action) - { - case GlobalAction.Back: - Click(); - return true; - } - - return false; - } - - public bool OnReleased(GlobalAction action) => false; } } } diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 85b74c0fad..474f529bb1 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -28,8 +28,6 @@ namespace osu.Game.Overlays protected const float WIDTH = 400; - private const float sidebar_padding = 10; - protected Container ContentContainer; protected override Container Content => ContentContainer; diff --git a/osu.Game/Overlays/SettingsSubPanel.cs b/osu.Game/Overlays/SettingsSubPanel.cs new file mode 100644 index 0000000000..576be71ee6 --- /dev/null +++ b/osu.Game/Overlays/SettingsSubPanel.cs @@ -0,0 +1,103 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Input.Bindings; +using osu.Game.Overlays.Settings; +using osu.Game.Screens.Ranking; +using osuTK; + +namespace osu.Game.Overlays +{ + public abstract class SettingsSubPanel : SettingsPanel + { + protected SettingsSubPanel() + : base(true) + { + } + + [BackgroundDependencyLoader] + private void load() + { + AddInternal(new BackButton + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Action = Hide + }); + } + + private class BackButton : OsuClickableContainer, IKeyBindingHandler + { + private AspectContainer aspect; + + [BackgroundDependencyLoader] + private void load() + { + Size = new Vector2(Sidebar.DEFAULT_WIDTH); + Children = new Drawable[] + { + aspect = new AspectContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Y = -15, + Size = new Vector2(15), + Shadow = true, + Icon = FontAwesome.Solid.ChevronLeft + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Y = 15, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), + Text = @"back", + }, + } + } + }; + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + aspect.ScaleTo(0.75f, 2000, Easing.OutQuint); + return base.OnMouseDown(e); + } + + protected override bool OnMouseUp(MouseUpEvent e) + { + aspect.ScaleTo(1, 1000, Easing.OutElastic); + return base.OnMouseUp(e); + } + + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.Back: + Click(); + return true; + } + + return false; + } + + public bool OnReleased(GlobalAction action) => false; + } + } +} From 02e2fb963a9c25a0bda62120b892b2714d0e2495 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 May 2019 01:45:37 +0900 Subject: [PATCH 0827/2854] Tidy up how subpanels are handled in SettingsOverlay --- osu.Game/Overlays/SettingsOverlay.cs | 34 +++++++++++++++++----------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 4f3a71a1b3..6e3eaae0a1 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -8,13 +8,12 @@ using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings.Sections; using osuTK.Graphics; using System.Collections.Generic; +using System.Linq; namespace osu.Game.Overlays { public class SettingsOverlay : SettingsPanel { - private readonly KeyBindingPanel keyBindingPanel; - protected override IEnumerable CreateSections() => new SettingsSection[] { new GeneralSection(), @@ -22,29 +21,37 @@ namespace osu.Game.Overlays new GameplaySection(), new AudioSection(), new SkinSection(), - new InputSection(keyBindingPanel), + new InputSection(createSubPanel(new KeyBindingPanel())), new OnlineSection(), new MaintenanceSection(), new DebugSection(), }; + private readonly List subPanels = new List(); + protected override Drawable CreateHeader() => new SettingsHeader("settings", "Change the way osu! behaves"); protected override Drawable CreateFooter() => new SettingsFooter(); public SettingsOverlay() : base(true) { - keyBindingPanel = new KeyBindingPanel - { - Depth = 1, - Anchor = Anchor.TopRight, - }; - keyBindingPanel.StateChanged += keyBindingPanelStateChanged; } - public override bool AcceptsFocus => keyBindingPanel.State != Visibility.Visible; + public override bool AcceptsFocus => subPanels.All(s => s.State != Visibility.Visible); - private void keyBindingPanelStateChanged(Visibility visibility) + private T createSubPanel(T subPanel) + where T : SettingsSubPanel + { + subPanel.Depth = 1; + subPanel.Anchor = Anchor.TopRight; + subPanel.StateChanged += subPanelStateChanged; + + subPanels.Add(subPanel); + + return subPanel; + } + + private void subPanelStateChanged(Visibility visibility) { switch (visibility) { @@ -66,12 +73,13 @@ namespace osu.Game.Overlays } } - protected override float ExpandedPosition => keyBindingPanel.State == Visibility.Visible ? -WIDTH : base.ExpandedPosition; + protected override float ExpandedPosition => subPanels.Any(s => s.State == Visibility.Visible) ? -WIDTH : base.ExpandedPosition; [BackgroundDependencyLoader] private void load() { - ContentContainer.Add(keyBindingPanel); + foreach (var s in subPanels) + ContentContainer.Add(s); } } } From e7b9d1efa32c7d7d46991071247b8ab3a860ec48 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 May 2019 00:55:12 +0900 Subject: [PATCH 0828/2854] Isolate alpha usage in OsuCheckbox --- osu.Game/Graphics/UserInterface/OsuCheckbox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index de3d93d845..2944fc87af 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -78,7 +78,7 @@ namespace osu.Game.Graphics.UserInterface Current.DisabledChanged += disabled => { - Alpha = disabled ? 0.3f : 1; + labelSpriteText.Alpha = Nub.Alpha = disabled ? 0.3f : 1; }; } From 127858d39855c7670377b3ff4e32af02fa864966 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 25 May 2019 15:00:53 +0900 Subject: [PATCH 0829/2854] Store databased settings based on string keys rather than ints Allows for rearranging/removal from enums without consequence. --- .../Configuration/DatabasedConfigManager.cs | 19 +++++++++++++++++-- osu.Game/Configuration/DatabasedSetting.cs | 11 +++-------- osu.Game/Configuration/SettingsStore.cs | 6 ++++++ .../Migrations/20180125143340_Settings.cs | 2 +- .../Migrations/OsuDbContextModelSnapshot.cs | 4 ++-- 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs index 8f1780cab5..d5cdd7e4bc 100644 --- a/osu.Game/Configuration/DatabasedConfigManager.cs +++ b/osu.Game/Configuration/DatabasedConfigManager.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Game.Rulesets; @@ -19,6 +20,8 @@ namespace osu.Game.Configuration private readonly RulesetInfo ruleset; + private readonly bool legacySettingsExist; + protected DatabasedConfigManager(SettingsStore settings, RulesetInfo ruleset = null, int? variant = null) { this.settings = settings; @@ -26,6 +29,7 @@ namespace osu.Game.Configuration this.variant = variant; databasedSettings = settings.Query(ruleset?.ID, variant); + legacySettingsExist = databasedSettings.Any(s => int.TryParse(s.Key, out var _)); InitialiseDefaults(); } @@ -43,7 +47,18 @@ namespace osu.Game.Configuration { base.AddBindable(lookup, bindable); - var setting = databasedSettings.Find(s => (int)s.Key == (int)(object)lookup); + if (legacySettingsExist) + { + var legacySetting = databasedSettings.Find(s => s.Key == ((int)(object)lookup).ToString()); + + if (legacySetting != null) + { + bindable.Parse(legacySetting.Value); + settings.Delete(legacySetting); + } + } + + var setting = databasedSettings.Find(s => s.Key == lookup.ToString()); if (setting != null) { @@ -53,7 +68,7 @@ namespace osu.Game.Configuration { settings.Update(setting = new DatabasedSetting { - Key = lookup, + Key = lookup.ToString(), Value = bindable.Value, RulesetID = ruleset?.ID, Variant = variant, diff --git a/osu.Game/Configuration/DatabasedSetting.cs b/osu.Game/Configuration/DatabasedSetting.cs index d56ac49358..3e0a9ecd28 100644 --- a/osu.Game/Configuration/DatabasedSetting.cs +++ b/osu.Game/Configuration/DatabasedSetting.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.ComponentModel.DataAnnotations.Schema; @@ -16,11 +16,7 @@ namespace osu.Game.Configuration public int? Variant { get; set; } [Column("Key")] - public int IntKey - { - get => (int)Key; - private set => Key = value; - } + public string Key { get; set; } [Column("Value")] public string StringValue @@ -29,10 +25,9 @@ namespace osu.Game.Configuration set => Value = value; } - public object Key; public object Value; - public DatabasedSetting(object key, object value) + public DatabasedSetting(string key, object value) { Key = key; Value = value; diff --git a/osu.Game/Configuration/SettingsStore.cs b/osu.Game/Configuration/SettingsStore.cs index f15fd1f17b..f8c9bdeaf8 100644 --- a/osu.Game/Configuration/SettingsStore.cs +++ b/osu.Game/Configuration/SettingsStore.cs @@ -37,5 +37,11 @@ namespace osu.Game.Configuration SettingChanged?.Invoke(); } + + public void Delete(DatabasedSetting setting) + { + using (var usage = ContextFactory.GetForWrite()) + usage.Context.Remove(setting); + } } } diff --git a/osu.Game/Migrations/20180125143340_Settings.cs b/osu.Game/Migrations/20180125143340_Settings.cs index 2e2768dc7c..166d3c086d 100644 --- a/osu.Game/Migrations/20180125143340_Settings.cs +++ b/osu.Game/Migrations/20180125143340_Settings.cs @@ -16,7 +16,7 @@ namespace osu.Game.Migrations { ID = table.Column(type: "INTEGER", nullable: false) .Annotation("Sqlite:Autoincrement", true), - Key = table.Column(type: "INTEGER", nullable: false), + Key = table.Column(type: "TEXT", nullable: false), RulesetID = table.Column(type: "INTEGER", nullable: true), Value = table.Column(type: "TEXT", nullable: true), Variant = table.Column(type: "INTEGER", nullable: true) diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index 8430e00e4f..f942d357e8 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -14,7 +14,7 @@ namespace osu.Game.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.2.1-servicing-10028"); + .HasAnnotation("ProductVersion", "2.2.4-servicing-10062"); modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => { @@ -198,7 +198,7 @@ namespace osu.Game.Migrations b.Property("ID") .ValueGeneratedOnAdd(); - b.Property("IntKey") + b.Property("Key") .HasColumnName("Key"); b.Property("RulesetID"); From 31e6a4fa5942a081f730e1ba2db676a045c9ccc5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 25 May 2019 15:09:31 +0900 Subject: [PATCH 0830/2854] Add optional skin foreign key to databased settings --- osu.Game/Configuration/DatabasedSetting.cs | 2 + .../20190525060824_SkinSettings.Designer.cs | 498 ++++++++++++++++++ .../Migrations/20190525060824_SkinSettings.cs | 45 ++ .../Migrations/OsuDbContextModelSnapshot.cs | 11 + osu.Game/Skinning/SkinInfo.cs | 3 + osu.Game/Skinning/SkinStore.cs | 6 + 6 files changed, 565 insertions(+) create mode 100644 osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs create mode 100644 osu.Game/Migrations/20190525060824_SkinSettings.cs diff --git a/osu.Game/Configuration/DatabasedSetting.cs b/osu.Game/Configuration/DatabasedSetting.cs index d56ac49358..035fc73f4f 100644 --- a/osu.Game/Configuration/DatabasedSetting.cs +++ b/osu.Game/Configuration/DatabasedSetting.cs @@ -15,6 +15,8 @@ namespace osu.Game.Configuration public int? Variant { get; set; } + public int? SkinInfoID { get; set; } + [Column("Key")] public int IntKey { diff --git a/osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs b/osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs new file mode 100644 index 0000000000..348c42adb9 --- /dev/null +++ b/osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs @@ -0,0 +1,498 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20190525060824_SkinSettings")] + partial class SkinSettings + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.4-servicing-10062"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("Status"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.Property("Status"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Key") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("SkinInfoID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("SkinInfoID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("ScoreInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("ScoreInfoID"); + + b.ToTable("ScoreFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Accuracy") + .HasColumnType("DECIMAL(1,4)"); + + b.Property("BeatmapInfoID"); + + b.Property("Combo"); + + b.Property("Date"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MaxCombo"); + + b.Property("ModsJson") + .HasColumnName("Mods"); + + b.Property("OnlineScoreID"); + + b.Property("PP"); + + b.Property("Rank"); + + b.Property("RulesetID"); + + b.Property("StatisticsJson") + .HasColumnName("Statistics"); + + b.Property("TotalScore"); + + b.Property("UserID") + .HasColumnName("UserID"); + + b.Property("UserString") + .HasColumnName("User"); + + b.HasKey("ID"); + + b.HasIndex("BeatmapInfoID"); + + b.HasIndex("OnlineScoreID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("ScoreInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Settings") + .HasForeignKey("SkinInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Scoring.ScoreInfo") + .WithMany("Files") + .HasForeignKey("ScoreInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") + .WithMany("Scores") + .HasForeignKey("BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20190525060824_SkinSettings.cs b/osu.Game/Migrations/20190525060824_SkinSettings.cs new file mode 100644 index 0000000000..8bd429ca5c --- /dev/null +++ b/osu.Game/Migrations/20190525060824_SkinSettings.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class SkinSettings : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SkinInfoID", + table: "Settings", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Settings_SkinInfoID", + table: "Settings", + column: "SkinInfoID"); + + // unsupported by sqlite + + // migrationBuilder.AddForeignKey( + // name: "FK_Settings_SkinInfo_SkinInfoID", + // table: "Settings", + // column: "SkinInfoID", + // principalTable: "SkinInfo", + // principalColumn: "ID", + // onDelete: ReferentialAction.Restrict); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Settings_SkinInfo_SkinInfoID", + table: "Settings"); + + migrationBuilder.DropIndex( + name: "IX_Settings_SkinInfoID", + table: "Settings"); + + migrationBuilder.DropColumn( + name: "SkinInfoID", + table: "Settings"); + } + } +} diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index 8430e00e4f..d03c2358b5 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -203,6 +203,8 @@ namespace osu.Game.Migrations b.Property("RulesetID"); + b.Property("SkinInfoID"); + b.Property("StringValue") .HasColumnName("Value"); @@ -210,6 +212,8 @@ namespace osu.Game.Migrations b.HasKey("ID"); + b.HasIndex("SkinInfoID"); + b.HasIndex("RulesetID", "Variant"); b.ToTable("Settings"); @@ -442,6 +446,13 @@ namespace osu.Game.Migrations .HasForeignKey("MetadataID"); }); + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Settings") + .HasForeignKey("SkinInfoID"); + }); + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => { b.HasOne("osu.Game.IO.FileInfo", "FileInfo") diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index 07318b473a..187ea910a7 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using osu.Game.Configuration; using osu.Game.Database; namespace osu.Game.Skinning @@ -19,6 +20,8 @@ namespace osu.Game.Skinning public List Files { get; set; } + public List Settings { get; set; } + public bool DeletePending { get; set; } public string FullName => $"\"{Name}\" by {Creator}"; diff --git a/osu.Game/Skinning/SkinStore.cs b/osu.Game/Skinning/SkinStore.cs index 31cadb0a24..153eeda130 100644 --- a/osu.Game/Skinning/SkinStore.cs +++ b/osu.Game/Skinning/SkinStore.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; +using Microsoft.EntityFrameworkCore; using osu.Framework.Platform; using osu.Game.Database; @@ -12,5 +14,9 @@ namespace osu.Game.Skinning : base(contextFactory, storage) { } + + protected override IQueryable AddIncludesForDeletion(IQueryable query) => + base.AddIncludesForDeletion(query) + .Include(s => s.Settings); // don't include FileInfo. these are handled by the FileStore itself. } } From e59a00ac6eb1f30cb368392aedb299177fd8a168 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 28 May 2019 14:04:33 +0900 Subject: [PATCH 0831/2854] Remove excessive selection updating --- .../SongSelect/TestScenePlaySongSelect.cs | 28 ++++++++++++++++++- osu.Game/Screens/Select/SongSelect.cs | 8 +++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 7e962dbc06..85811e3a0a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -209,7 +209,33 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("start not requested", () => !startRequested); } - private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray()))); + [Test] + public void TestAddNewBeatmap() + { + const int test_count = 10; + int beatmapChangedCount = 0; + createSongSelect(); + AddStep("Setup counter", () => + { + beatmapChangedCount = 0; + songSelect.Carousel.BeatmapSetsChanged += () => beatmapChangedCount++; + }); + AddRepeatStep($"Create beatmaps {test_count} times", () => + { + manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == 0).ToArray())); + + Scheduler.AddDelayed(() => + { + // Wait for debounce + songSelect.Carousel.SelectNextRandom(); + }, 400); + }, test_count); + + AddAssert($"Beatmap changed {test_count} times", () => beatmapChangedCount == test_count); + } + + private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", + () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray()))); private static int importId; private int getImportId() => ++importId; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index fed1f7a944..f30618ce3f 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -597,11 +597,17 @@ namespace osu.Game.Screens.Select { bindBindables(); + // As a selection was already obtained, do not attempt to update the selected beatmap. + if (Carousel.SelectedBeatmapSet != null) + return; + + // Attempt to select the current beatmap on the carousel, if it is valid to be selected. if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false && Beatmap.Value.BeatmapSetInfo?.Protected == false && Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo, false)) return; - if (Carousel.SelectedBeatmapSet == null && !Carousel.SelectNextRandom()) + // If the current active beatmap could not be selected, select a new random beatmap. + if (!Carousel.SelectNextRandom()) { // in the case random selection failed, we want to trigger selectionChanged // to show the dummy beatmap (we have nothing else to display). From 436760de967e59400822e25356b502294bd487e3 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 28 May 2019 14:34:52 +0900 Subject: [PATCH 0832/2854] Change test name --- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 85811e3a0a..8e3fe57d92 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -210,7 +210,7 @@ namespace osu.Game.Tests.Visual.SongSelect } [Test] - public void TestAddNewBeatmap() + public void TestAddNewBeatmapWhileSelectingRandom() { const int test_count = 10; int beatmapChangedCount = 0; From 4ca34bd5e8a53752a37c1401c686757a67c943c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 May 2019 17:06:01 +0900 Subject: [PATCH 0833/2854] Update osu! in line with audio subsystem refactor --- .../TestScenePreviewTrackManager.cs | 4 ++-- osu.Game/Audio/PreviewTrackManager.cs | 20 +++++++++---------- osu.Game/Beatmaps/BindableBeatmap.cs | 2 +- .../Containers/OsuFocusedOverlayContainer.cs | 4 ++-- osu.Game/Graphics/ScreenshotManager.cs | 2 +- .../UserInterface/HoverClickSounds.cs | 2 +- .../Graphics/UserInterface/HoverSounds.cs | 2 +- .../Graphics/UserInterface/OsuCheckbox.cs | 4 ++-- osu.Game/Graphics/UserInterface/OsuMenu.cs | 4 ++-- .../Graphics/UserInterface/OsuSliderBar.cs | 2 +- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Overlays/MedalOverlay.cs | 2 +- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 ++-- osu.Game/Screens/Menu/Button.cs | 4 ++-- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- osu.Game/Screens/Menu/Intro.cs | 4 ++-- osu.Game/Screens/Menu/OsuLogo.cs | 4 ++-- osu.Game/Screens/Multi/Multiplayer.cs | 2 +- osu.Game/Screens/OsuScreen.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Play/SkipOverlay.cs | 2 +- .../Select/Carousel/DrawableCarouselItem.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 8 ++++---- osu.Game/Skinning/LegacySkin.cs | 4 ++-- osu.Game/Skinning/SkinnableSound.cs | 2 +- osu.Game/osu.Game.csproj | 4 +++- osu.sln | 12 +++++++++++ 27 files changed, 61 insertions(+), 47 deletions(-) diff --git a/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs b/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs index 94412455a0..e85e879ef5 100644 --- a/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs +++ b/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs @@ -111,11 +111,11 @@ namespace osu.Game.Tests.Visual.Components private class TestPreviewTrackManager : PreviewTrackManager { - protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) => new TestPreviewTrack(beatmapSetInfo, trackManager); + protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackStore trackStore) => new TestPreviewTrack(beatmapSetInfo, trackStore); protected class TestPreviewTrack : TrackManagerPreviewTrack { - public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) + public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackStore trackManager) : base(beatmapSetInfo, trackManager) { } diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index 99c0d70ac9..695051f508 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -20,19 +20,19 @@ namespace osu.Game.Audio private readonly BindableDouble muteBindable = new BindableDouble(); private AudioManager audio; - private TrackManager trackManager; + private TrackStore trackStore; private TrackManagerPreviewTrack current; [BackgroundDependencyLoader] private void load(AudioManager audio, FrameworkConfigManager config) { - trackManager = new TrackManager(new OnlineStore()); + trackStore = new TrackStore(new OnlineStore()); this.audio = audio; - audio.AddItem(trackManager); + audio.AddItem(trackStore); - config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume); + config.BindWith(FrameworkSetting.VolumeMusic, trackStore.Volume); } /// @@ -42,19 +42,19 @@ namespace osu.Game.Audio /// The playable . public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo) { - var track = CreatePreviewTrack(beatmapSetInfo, trackManager); + var track = CreatePreviewTrack(beatmapSetInfo, trackStore); track.Started += () => { current?.Stop(); current = track; - audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); + audio.Tracks.AddAdjustment(AdjustableProperty.Volume, muteBindable); }; track.Stopped += () => { current = null; - audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); + audio.Tracks.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); }; return track; @@ -81,16 +81,16 @@ namespace osu.Game.Audio /// /// Creates the . /// - protected virtual TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) => new TrackManagerPreviewTrack(beatmapSetInfo, trackManager); + protected virtual TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackStore trackStore) => new TrackManagerPreviewTrack(beatmapSetInfo, trackStore); protected class TrackManagerPreviewTrack : PreviewTrack { public IPreviewTrackOwner Owner { get; private set; } private readonly BeatmapSetInfo beatmapSetInfo; - private readonly TrackManager trackManager; + private readonly TrackStore trackManager; - public TrackManagerPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) + public TrackManagerPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackStore trackManager) { this.beatmapSetInfo = beatmapSetInfo; this.trackManager = trackManager; diff --git a/osu.Game/Beatmaps/BindableBeatmap.cs b/osu.Game/Beatmaps/BindableBeatmap.cs index 27bad65062..dcce18b1be 100644 --- a/osu.Game/Beatmaps/BindableBeatmap.cs +++ b/osu.Game/Beatmaps/BindableBeatmap.cs @@ -56,7 +56,7 @@ namespace osu.Game.Beatmaps lastBeatmap.RecycleTrack(); } - audioManager.Track.AddItem(beatmap.Track); + audioManager.Tracks.AddItem(beatmap.Track); } lastBeatmap = beatmap; diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 3f84f77081..8b34459710 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -51,8 +51,8 @@ namespace osu.Game.Graphics.Containers if (osuGame != null) OverlayActivationMode.BindTo(osuGame.OverlayActivationMode); - samplePopIn = audio.Sample.Get(@"UI/overlay-pop-in"); - samplePopOut = audio.Sample.Get(@"UI/overlay-pop-out"); + samplePopIn = audio.Samples.Get(@"UI/overlay-pop-in"); + samplePopOut = audio.Samples.Get(@"UI/overlay-pop-out"); StateChanged += onStateChanged; } diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 24a98e6dc9..5ad5e5569a 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -51,7 +51,7 @@ namespace osu.Game.Graphics screenshotFormat = config.GetBindable(OsuSetting.ScreenshotFormat); captureMenuCursor = config.GetBindable(OsuSetting.ScreenshotCaptureMenuCursor); - shutter = audio.Sample.Get("UI/shutter"); + shutter = audio.Samples.Get("UI/shutter"); } public bool OnPressed(GlobalAction action) diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs index cbbaa6d303..70d988f60e 100644 --- a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs @@ -31,7 +31,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(AudioManager audio) { - sampleClick = audio.Sample.Get($@"UI/generic-select{SampleSet.GetDescription()}"); + sampleClick = audio.Samples.Get($@"UI/generic-select{SampleSet.GetDescription()}"); } } } diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index b246092a7f..f1ac8ced6e 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -37,7 +37,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(AudioManager audio) { - sampleHover = audio.Sample.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); + sampleHover = audio.Samples.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); } } diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index 2944fc87af..cd1147e3d3 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -112,8 +112,8 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(AudioManager audio) { - sampleChecked = audio.Sample.Get(@"UI/check-on"); - sampleUnchecked = audio.Sample.Get(@"UI/check-off"); + sampleChecked = audio.Samples.Get(@"UI/check-on"); + sampleUnchecked = audio.Samples.Get(@"UI/check-off"); } } } diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs index 32994be78a..f8234cb81f 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs @@ -71,8 +71,8 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(AudioManager audio) { - sampleHover = audio.Sample.Get(@"UI/generic-hover"); - sampleClick = audio.Sample.Get(@"UI/generic-select"); + sampleHover = audio.Samples.Get(@"UI/generic-hover"); + sampleClick = audio.Samples.Get(@"UI/generic-select"); BackgroundColour = Color4.Transparent; BackgroundColourHover = OsuColour.FromHex(@"172023"); diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index c3c447ef83..5c706781e6 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -86,7 +86,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(AudioManager audio, OsuColour colours) { - sample = audio.Sample.Get(@"UI/sliderbar-notch"); + sample = audio.Samples.Get(@"UI/sliderbar-notch"); AccentColour = colours.Pink; } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 7b9aed8364..40cb26ec54 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -193,7 +193,7 @@ namespace osu.Game // tracks play so loud our samples can't keep up. // this adds a global reduction of track volume for the time being. - Audio.Track.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8)); + Audio.Tracks.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8)); beatmap = new OsuBindableBeatmap(defaultBeatmap, Audio); diff --git a/osu.Game/Overlays/MedalOverlay.cs b/osu.Game/Overlays/MedalOverlay.cs index 6d82db5603..1f15c773f4 100644 --- a/osu.Game/Overlays/MedalOverlay.cs +++ b/osu.Game/Overlays/MedalOverlay.cs @@ -145,7 +145,7 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load(OsuColour colours, TextureStore textures, AudioManager audio) { - getSample = audio.Sample.Get(@"MedalSplash/medal-get"); + getSample = audio.Samples.Get(@"MedalSplash/medal-get"); innerSpin.Texture = outerSpin.Texture = textures.Get(@"MedalSplash/disc-spin"); disc.EdgeEffect = leftStrip.EdgeEffect = rightStrip.EdgeEffect = new EdgeEffectParameters diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 97769fe5aa..b675a35970 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -56,8 +56,8 @@ namespace osu.Game.Overlays.Mods Ruleset.BindTo(ruleset); if (mods != null) SelectedMods.BindTo(mods); - sampleOn = audio.Sample.Get(@"UI/check-on"); - sampleOff = audio.Sample.Get(@"UI/check-off"); + sampleOn = audio.Samples.Get(@"UI/check-on"); + sampleOff = audio.Samples.Get(@"UI/check-off"); } protected override void LoadComplete() diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index 7d48f619d9..badd1e0549 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -182,9 +182,9 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader] private void load(AudioManager audio) { - sampleHover = audio.Sample.Get(@"Menu/button-hover"); + sampleHover = audio.Samples.Get(@"Menu/button-hover"); if (!string.IsNullOrEmpty(sampleName)) - sampleClick = audio.Sample.Get($@"Menu/{sampleName}"); + sampleClick = audio.Samples.Get($@"Menu/{sampleName}"); } protected override bool OnMouseDown(MouseDownEvent e) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index a098d42c83..11b637801a 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -150,7 +150,7 @@ namespace osu.Game.Screens.Menu if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle); - sampleBack = audio.Sample.Get(@"Menu/button-back-select"); + sampleBack = audio.Samples.Get(@"Menu/button-back-select"); } private void onMulti() diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index 2392d650a0..98a2fe8f13 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -76,8 +76,8 @@ namespace osu.Game.Screens.Menu introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); track = introBeatmap.Track; - welcome = audio.Sample.Get(@"welcome"); - seeya = audio.Sample.Get(@"seeya"); + welcome = audio.Samples.Get(@"welcome"); + seeya = audio.Samples.Get(@"seeya"); } private const double delay_step_one = 2300; diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 4631f4e222..479b3d80b6 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -255,8 +255,8 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader] private void load(TextureStore textures, AudioManager audio) { - sampleClick = audio.Sample.Get(@"Menu/osu-logo-select"); - sampleBeat = audio.Sample.Get(@"Menu/osu-logo-heartbeat"); + sampleClick = audio.Samples.Get(@"Menu/osu-logo-select"); + sampleBeat = audio.Samples.Get(@"Menu/osu-logo-heartbeat"); logo.Texture = textures.Get(@"Menu/logo"); ripple.Texture = textures.Get(@"Menu/logo"); diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 155665e0d5..9e5c11e098 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -255,7 +255,7 @@ namespace osu.Game.Screens.Multi if (!track.IsRunning) { - game.Audio.AddItemToList(track); + game.Audio.AddItem(track); track.Seek(Beatmap.Value.Metadata.PreviewTime); track.Start(); } diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 9d53e43b80..f7b90e9966 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -99,7 +99,7 @@ namespace osu.Game.Screens [BackgroundDependencyLoader(true)] private void load(OsuGame osu, AudioManager audio) { - sampleExit = audio.Sample.Get(@"UI/screen-back"); + sampleExit = audio.Samples.Get(@"UI/screen-back"); } public virtual bool OnPressed(GlobalAction action) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 30214d1b9c..cf743ee4f7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -103,7 +103,7 @@ namespace osu.Game.Screens.Play if (working == null) return; - sampleRestart = audio.Sample.Get(@"Gameplay/restart"); + sampleRestart = audio.Samples.Get(@"Gameplay/restart"); mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); showStoryboard = config.GetBindable(OsuSetting.ShowStoryboard); diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index e3c56e1c2c..4ecc15f22b 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -234,7 +234,7 @@ namespace osu.Game.Screens.Play colourNormal = colours.Yellow; colourHover = colours.YellowDark; - sampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection"); + sampleConfirm = audio.Samples.Get(@"SongSelect/confirm-selection"); Children = new Drawable[] { diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs index f1d6343e72..b906bd935c 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs @@ -69,7 +69,7 @@ namespace osu.Game.Screens.Select.Carousel } }; - sampleHover = audio.Sample.Get($@"SongSelect/song-ping-variation-{RNG.Next(1, 5)}"); + sampleHover = audio.Samples.Get($@"SongSelect/song-ping-variation-{RNG.Next(1, 5)}"); hoverLayer.Colour = colours.Blue.Opacity(0.1f); } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index fed1f7a944..b266a73eb5 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -242,9 +242,9 @@ namespace osu.Game.Screens.Select dialogOverlay = dialog; - sampleChangeDifficulty = audio.Sample.Get(@"SongSelect/select-difficulty"); - sampleChangeBeatmap = audio.Sample.Get(@"SongSelect/select-expand"); - SampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection"); + sampleChangeDifficulty = audio.Samples.Get(@"SongSelect/select-difficulty"); + sampleChangeBeatmap = audio.Samples.Get(@"SongSelect/select-expand"); + SampleConfirm = audio.Samples.Get(@"SongSelect/confirm-selection"); Carousel.LoadBeatmapSetsFromManager(this.beatmaps); @@ -582,7 +582,7 @@ namespace osu.Game.Screens.Select { // Ensure the track is added to the TrackManager, since it is removed after the player finishes the map. // Using AddItemToList rather than AddItem so that it doesn't attempt to register adjustment dependencies more than once. - Game.Audio.Track.AddItemToList(track); + Game.Audio.Tracks.AddItem(track); track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; track.Restart(); } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index ea4a777b47..9f31783a6b 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -21,7 +21,7 @@ namespace osu.Game.Skinning { protected TextureStore Textures; - protected SampleManager Samples; + protected SampleStore Samples; public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) : this(skin, new LegacySkinResourceStore(skin, storage), audioManager, "skin.ini") @@ -38,7 +38,7 @@ namespace osu.Game.Skinning else Configuration = new SkinConfiguration(); - Samples = audioManager.GetSampleManager(storage); + Samples = audioManager.GetSampleStore(storage); Textures = new TextureStore(new TextureLoaderStore(storage)); } diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index d6f3625be8..5e8a0ea43f 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -39,7 +39,7 @@ namespace osu.Game.Skinning { var ch = loadChannel(s, skin.GetSample); if (ch == null && allowFallback) - ch = loadChannel(s, audio.Sample.Get); + ch = loadChannel(s, audio.Samples.Get); return ch; }).Where(c => c != null).ToArray(); } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b77c724d1b..185aac4311 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,10 +15,12 @@ - + + + diff --git a/osu.sln b/osu.sln index 3c38309d86..3a60016bda 100644 --- a/osu.sln +++ b/osu.sln @@ -25,6 +25,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Taiko.Tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Osu.Tests", "osu.Game.Rulesets.Osu.Tests\osu.Game.Rulesets.Osu.Tests.csproj", "{DECCCC75-67AD-4C3D-BB84-FD0E01323511}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework", "..\osu-framework\osu.Framework\osu.Framework.csproj", "{7CCA1C51-AAC8-4153-BAC6-F0E4976602C0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework.NativeLibs", "..\osu-framework\osu.Framework.NativeLibs\osu.Framework.NativeLibs.csproj", "{8EFF6D45-9A38-40B6-9FDC-963DE8472576}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -79,6 +83,14 @@ Global {DECCCC75-67AD-4C3D-BB84-FD0E01323511}.Debug|Any CPU.Build.0 = Debug|Any CPU {DECCCC75-67AD-4C3D-BB84-FD0E01323511}.Release|Any CPU.ActiveCfg = Release|Any CPU {DECCCC75-67AD-4C3D-BB84-FD0E01323511}.Release|Any CPU.Build.0 = Release|Any CPU + {7CCA1C51-AAC8-4153-BAC6-F0E4976602C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7CCA1C51-AAC8-4153-BAC6-F0E4976602C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7CCA1C51-AAC8-4153-BAC6-F0E4976602C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7CCA1C51-AAC8-4153-BAC6-F0E4976602C0}.Release|Any CPU.Build.0 = Release|Any CPU + {8EFF6D45-9A38-40B6-9FDC-963DE8472576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8EFF6D45-9A38-40B6-9FDC-963DE8472576}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8EFF6D45-9A38-40B6-9FDC-963DE8472576}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8EFF6D45-9A38-40B6-9FDC-963DE8472576}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 23b5d303606b5edfcea4e25837b6bed927f8ea8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 May 2019 17:10:46 +0900 Subject: [PATCH 0834/2854] Remove csproj changes --- osu.Game/osu.Game.csproj | 4 +--- osu.sln | 12 ------------ 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 185aac4311..b77c724d1b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,12 +15,10 @@ + - - - diff --git a/osu.sln b/osu.sln index 3a60016bda..3c38309d86 100644 --- a/osu.sln +++ b/osu.sln @@ -25,10 +25,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Taiko.Tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Osu.Tests", "osu.Game.Rulesets.Osu.Tests\osu.Game.Rulesets.Osu.Tests.csproj", "{DECCCC75-67AD-4C3D-BB84-FD0E01323511}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework", "..\osu-framework\osu.Framework\osu.Framework.csproj", "{7CCA1C51-AAC8-4153-BAC6-F0E4976602C0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework.NativeLibs", "..\osu-framework\osu.Framework.NativeLibs\osu.Framework.NativeLibs.csproj", "{8EFF6D45-9A38-40B6-9FDC-963DE8472576}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -83,14 +79,6 @@ Global {DECCCC75-67AD-4C3D-BB84-FD0E01323511}.Debug|Any CPU.Build.0 = Debug|Any CPU {DECCCC75-67AD-4C3D-BB84-FD0E01323511}.Release|Any CPU.ActiveCfg = Release|Any CPU {DECCCC75-67AD-4C3D-BB84-FD0E01323511}.Release|Any CPU.Build.0 = Release|Any CPU - {7CCA1C51-AAC8-4153-BAC6-F0E4976602C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7CCA1C51-AAC8-4153-BAC6-F0E4976602C0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7CCA1C51-AAC8-4153-BAC6-F0E4976602C0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7CCA1C51-AAC8-4153-BAC6-F0E4976602C0}.Release|Any CPU.Build.0 = Release|Any CPU - {8EFF6D45-9A38-40B6-9FDC-963DE8472576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8EFF6D45-9A38-40B6-9FDC-963DE8472576}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8EFF6D45-9A38-40B6-9FDC-963DE8472576}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8EFF6D45-9A38-40B6-9FDC-963DE8472576}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 1a871af5520113372c38d70740337dc794fe2b30 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 28 May 2019 19:15:29 +0900 Subject: [PATCH 0835/2854] Fix hide selection, add test --- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 12 ++++++++++++ osu.Game/Screens/Select/BeatmapCarousel.cs | 5 ++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 8e3fe57d92..c33528c3bf 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -234,6 +234,18 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert($"Beatmap changed {test_count} times", () => beatmapChangedCount == test_count); } + [Test] + public void TestHideSetSelectsCorrectBeatmap() + { + int? previousID = null; + createSongSelect(); + importForRuleset(0); + AddStep("Move to last difficulty", () => songSelect.Carousel.SelectBeatmap(songSelect.Carousel.BeatmapSets.First().Beatmaps.Last())); + AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmap.ID); + AddStep("Hide first beatmap", () => manager.Hide(songSelect.Carousel.SelectedBeatmapSet.Beatmaps.First())); + AddAssert("Selected beatmap has not changed", () => songSelect.Carousel.SelectedBeatmap.ID == previousID); + } + private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray()))); diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 63ad3b6ab2..0b3d0c448b 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -158,6 +158,9 @@ namespace osu.Game.Screens.Select var newSet = createCarouselSet(beatmapSet); + // Since we're about to remove the selected beatmap, store its ID so we can go back if needed. + var previouslySelectedID = selectedBeatmap?.Beatmap.ID; + if (existingSet != null) root.RemoveChild(existingSet); @@ -173,7 +176,7 @@ namespace osu.Game.Screens.Select //check if we can/need to maintain our current selection. if (hadSelection) - select((CarouselItem)newSet.Beatmaps.FirstOrDefault(b => b.Beatmap.ID == selectedBeatmap?.Beatmap.ID) ?? newSet); + select((CarouselItem)newSet.Beatmaps.FirstOrDefault(b => b.Beatmap.ID == previouslySelectedID) ?? newSet); itemsCache.Invalidate(); Schedule(() => BeatmapSetsChanged?.Invoke()); From 6ca3bd086f54894a0f02ca29ea7dfcd13bc91786 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 28 May 2019 17:04:05 +0300 Subject: [PATCH 0836/2854] ShowMore button update --- .../Beatmaps/PaginatedBeatmapContainer.cs | 4 +- .../PaginatedMostPlayedBeatmapContainer.cs | 4 +- .../Profile/Sections/PaginatedContainer.cs | 161 +++++++++++++++--- .../Sections/Ranks/PaginatedScoreContainer.cs | 10 +- .../PaginatedRecentActivityContainer.cs | 4 +- 5 files changed, 146 insertions(+), 37 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index 0fc1398f5d..db291d0731 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -34,8 +34,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps request = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); request.Success += sets => Schedule(() => { - ShowMoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0); - ShowMoreLoading.Hide(); + MoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0); + MoreButton.IsLoading = false; if (!sets.Any() && VisiblePages == 1) { diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index f2eb32c53b..0f86e0900e 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -29,8 +29,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical request = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); request.Success += beatmaps => Schedule(() => { - ShowMoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0); - ShowMoreLoading.Hide(); + MoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0); + MoreButton.IsLoading = false; if (!beatmaps.Any() && VisiblePages == 1) { diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 46c65b9db7..1ebc51b11f 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -7,11 +7,15 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Rulesets; +using osu.Framework.Input.Events; +using System; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osuTK.Graphics; using osu.Game.Users; namespace osu.Game.Overlays.Profile.Sections @@ -19,8 +23,7 @@ namespace osu.Game.Overlays.Profile.Sections public class PaginatedContainer : FillFlowContainer { protected readonly FillFlowContainer ItemsContainer; - protected readonly OsuHoverContainer ShowMoreButton; - protected readonly LoadingAnimation ShowMoreLoading; + protected readonly ShowMoreButton MoreButton; protected readonly OsuSpriteText MissingText; protected int VisiblePages; @@ -45,38 +48,25 @@ namespace osu.Game.Overlays.Profile.Sections new OsuSpriteText { Text = header, - Font = OsuFont.GetFont(size: 15, weight: FontWeight.Regular, italics: true), + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold), Margin = new MarginPadding { Top = 10, Bottom = 10 }, }, ItemsContainer = new FillFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Bottom = 10 } + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 2), }, - ShowMoreButton = new OsuHoverContainer + MoreButton = new ShowMoreButton { Alpha = 0, + Margin = new MarginPadding { Top = 10 }, Action = ShowMore, - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Child = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 14), - Text = "show more", - Padding = new MarginPadding { Vertical = 10, Horizontal = 15 }, - } - }, - ShowMoreLoading = new LoadingAnimation - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Size = new Vector2(14), }, MissingText = new OsuSpriteText { - Font = OsuFont.GetFont(size: 14), + Font = OsuFont.GetFont(size: 15), Text = missing, Alpha = 0, }, @@ -97,16 +87,135 @@ namespace osu.Game.Overlays.Profile.Sections { VisiblePages = 0; ItemsContainer.Clear(); - ShowMoreButton.Hide(); if (e.NewValue != null) ShowMore(); } - protected virtual void ShowMore() + protected virtual void ShowMore() => MoreButton.IsLoading = true; + + protected class ShowMoreButton : CircularContainer { - ShowMoreLoading.Show(); - ShowMoreButton.Hide(); + private const int duration = 300; + private Color4 idleColour; + private Color4 hoveredColour; + + public Action Action; + private readonly Box background; + private readonly LoadingAnimation loading; + private readonly FillFlowContainer content; + + private bool isLoading; + public bool IsLoading + { + set + { + isLoading = value; + + if (value) + { + loading.FadeIn(duration, Easing.OutQuint); + content.FadeOut(duration, Easing.OutQuint); + } + else + { + loading.FadeOut(duration, Easing.OutQuint); + content.FadeIn(duration, Easing.OutQuint); + } + } + get + { + return isLoading; + } + } + + public ShowMoreButton() + { + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; + Masking = true; + Size = new Vector2(140, 30); + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + content = new FillFlowContainer + { + Alpha = 0, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(7), + Children = new Drawable[] + { + new ChevronIcon(), + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold), + Text = "show more".ToUpper(), + }, + new ChevronIcon(), + } + }, + loading = new LoadingAnimation + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(20) + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colors) + { + background.Colour = idleColour = colors.GreySeafoam; + hoveredColour = colors.GreySeafoamLight; + } + + protected override bool OnHover(HoverEvent e) + { + background.FadeColour(hoveredColour, duration, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + background.FadeColour(idleColour, duration, Easing.OutQuint); + base.OnHoverLost(e); + } + + protected override bool OnClick(ClickEvent e) + { + Action.Invoke(); + return base.OnClick(e); + } + + private class ChevronIcon : SpriteIcon + { + private const int bottom_margin = 2; + private const int icon_size = 8; + + public ChevronIcon() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + Margin = new MarginPadding { Bottom = bottom_margin }; + Size = new Vector2(icon_size); + Icon = FontAwesome.Solid.ChevronDown; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colors) + { + Colour = colors.Yellow; + } + } } } } diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 470bed2854..1d9e3d1cc1 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Online.API.Requests; using osu.Game.Users; @@ -9,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Bindables; +using osu.Framework.Graphics; namespace osu.Game.Overlays.Profile.Sections.Ranks { @@ -41,8 +41,8 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks if (!scores.Any() && VisiblePages == 1) { - ShowMoreButton.Hide(); - ShowMoreLoading.Hide(); + MoreButton.Hide(); + MoreButton.IsLoading = false; MissingText.Show(); return; } @@ -63,8 +63,8 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks LoadComponentsAsync(drawableScores, s => { MissingText.Hide(); - ShowMoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0); - ShowMoreLoading.Hide(); + MoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0); + MoreButton.IsLoading = false; ItemsContainer.AddRange(s); }); diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index 4b4acb8fbc..38134ad660 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -27,8 +27,8 @@ namespace osu.Game.Overlays.Profile.Sections.Recent request = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); request.Success += activities => Schedule(() => { - ShowMoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0); - ShowMoreLoading.Hide(); + MoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0); + MoreButton.IsLoading = false; if (!activities.Any() && VisiblePages == 1) { From 857eb9b83a5a5cfab6865d06807ad58af5aa1aa1 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 28 May 2019 17:21:34 +0300 Subject: [PATCH 0837/2854] Fix CI stuff --- osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 1ebc51b11f..7e13a90f25 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -106,6 +106,7 @@ namespace osu.Game.Overlays.Profile.Sections private readonly FillFlowContainer content; private bool isLoading; + public bool IsLoading { set @@ -123,10 +124,7 @@ namespace osu.Game.Overlays.Profile.Sections content.FadeIn(duration, Easing.OutQuint); } } - get - { - return isLoading; - } + get => isLoading; } public ShowMoreButton() From a20eda7b5f99eb4bd35b88c3c73bcf3e1d4dca95 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 May 2019 23:54:42 +0900 Subject: [PATCH 0838/2854] Fix remaining cases to work without things --- .../TestScenePreviewTrackManager.cs | 5 ++- osu.Game/Audio/PreviewTrackManager.cs | 11 +++--- .../Beatmaps/BeatmapManager_WorkingBeatmap.cs | 18 ++++++--- osu.Game/Beatmaps/BindableBeatmap.cs | 38 +++++++------------ osu.Game/Beatmaps/WorkingBeatmap.cs | 6 ++- osu.Game/OsuGameBase.cs | 11 +++--- osu.Game/Overlays/MusicController.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 3 -- osu.Game/Skinning/LegacySkin.cs | 9 ++++- osu.Game/Tests/Visual/OsuTestScene.cs | 12 ++---- 10 files changed, 58 insertions(+), 57 deletions(-) diff --git a/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs b/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs index e85e879ef5..0784725ea4 100644 --- a/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs +++ b/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs @@ -5,6 +5,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Containers; +using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -111,11 +112,11 @@ namespace osu.Game.Tests.Visual.Components private class TestPreviewTrackManager : PreviewTrackManager { - protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackStore trackStore) => new TestPreviewTrack(beatmapSetInfo, trackStore); + protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, IResourceStore trackStore) => new TestPreviewTrack(beatmapSetInfo, trackStore); protected class TestPreviewTrack : TrackManagerPreviewTrack { - public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackStore trackManager) + public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, IResourceStore trackManager) : base(beatmapSetInfo, trackManager) { } diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index 695051f508..4b7277c3fd 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -20,17 +20,16 @@ namespace osu.Game.Audio private readonly BindableDouble muteBindable = new BindableDouble(); private AudioManager audio; - private TrackStore trackStore; + private IAdjustableResourceStore trackStore; private TrackManagerPreviewTrack current; [BackgroundDependencyLoader] private void load(AudioManager audio, FrameworkConfigManager config) { - trackStore = new TrackStore(new OnlineStore()); + trackStore = audio.GetTrackStore(new OnlineStore()); this.audio = audio; - audio.AddItem(trackStore); config.BindWith(FrameworkSetting.VolumeMusic, trackStore.Volume); } @@ -81,16 +80,16 @@ namespace osu.Game.Audio /// /// Creates the . /// - protected virtual TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackStore trackStore) => new TrackManagerPreviewTrack(beatmapSetInfo, trackStore); + protected virtual TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, IResourceStore trackStore) => new TrackManagerPreviewTrack(beatmapSetInfo, trackStore); protected class TrackManagerPreviewTrack : PreviewTrack { public IPreviewTrackOwner Owner { get; private set; } private readonly BeatmapSetInfo beatmapSetInfo; - private readonly TrackStore trackManager; + private readonly IResourceStore trackManager; - public TrackManagerPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackStore trackManager) + public TrackManagerPreviewTrack(BeatmapSetInfo beatmapSetInfo, IResourceStore trackManager) { this.beatmapSetInfo = beatmapSetInfo; this.trackManager = trackManager; diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 0bdab22dd2..cfeb6b0a92 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -20,14 +20,13 @@ namespace osu.Game.Beatmaps protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap { private readonly IResourceStore store; - private readonly AudioManager audioManager; public BeatmapManagerWorkingBeatmap(IResourceStore store, TextureStore textureStore, BeatmapInfo beatmapInfo, AudioManager audioManager) : base(beatmapInfo) { this.store = store; this.textureStore = textureStore; - this.audioManager = audioManager; + AudioManager = audioManager; } protected override IBeatmap GetBeatmap() @@ -47,6 +46,8 @@ namespace osu.Game.Beatmaps private TextureStore textureStore; + private IAdjustableResourceStore trackStore; + protected override bool BackgroundStillValid(Texture b) => false; // bypass lazy logic. we want to return a new background each time for refcounting purposes. protected override Texture GetBackground() @@ -68,8 +69,7 @@ namespace osu.Game.Beatmaps { try { - var trackData = store.GetStream(getPathForFile(Metadata.AudioFile)); - return trackData == null ? null : new TrackBass(trackData); + return (trackStore ?? (trackStore = AudioManager.GetTrackStore(store))).Get(getPathForFile(Metadata.AudioFile)); } catch { @@ -77,6 +77,14 @@ namespace osu.Game.Beatmaps } } + public override void RecycleTrack() + { + base.RecycleTrack(); + + trackStore?.Dispose(); + trackStore = null; + } + public override void TransferTo(WorkingBeatmap other) { base.TransferTo(other); @@ -135,7 +143,7 @@ namespace osu.Game.Beatmaps try { - skin = new LegacyBeatmapSkin(BeatmapInfo, store, audioManager); + skin = new LegacyBeatmapSkin(BeatmapInfo, store, AudioManager); } catch (Exception e) { diff --git a/osu.Game/Beatmaps/BindableBeatmap.cs b/osu.Game/Beatmaps/BindableBeatmap.cs index dcce18b1be..6614a6f2fb 100644 --- a/osu.Game/Beatmaps/BindableBeatmap.cs +++ b/osu.Game/Beatmaps/BindableBeatmap.cs @@ -1,11 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Diagnostics; using JetBrains.Annotations; using osu.Framework.Audio; -using osu.Framework.Audio.Track; using osu.Framework.Bindables; namespace osu.Game.Beatmaps @@ -16,32 +14,26 @@ namespace osu.Game.Beatmaps /// public abstract class BindableBeatmap : NonNullableBindable { - private AudioManager audioManager; + protected AudioManager AudioManager; + private WorkingBeatmap lastBeatmap; - protected BindableBeatmap(WorkingBeatmap defaultValue) + protected BindableBeatmap(WorkingBeatmap defaultValue, AudioManager audioManager) : base(defaultValue) { + // we don't want to attempt to update tracks if we are a bound copy. + if (audioManager != null) + { + AudioManager = audioManager; + ValueChanged += b => updateAudioTrack(b.NewValue); + + // If the track has changed prior to this being called, let's register it + if (Value != Default) + updateAudioTrack(Value); + } } - /// - /// Registers an for s to be added to. - /// - /// The to register. - protected void RegisterAudioManager([NotNull] AudioManager audioManager) - { - if (this.audioManager != null) throw new InvalidOperationException($"Cannot register multiple {nameof(AudioManager)}s."); - - this.audioManager = audioManager; - - ValueChanged += b => registerAudioTrack(b.NewValue); - - // If the track has changed prior to this being called, let's register it - if (Value != Default) - registerAudioTrack(Value); - } - - private void registerAudioTrack(WorkingBeatmap beatmap) + private void updateAudioTrack(WorkingBeatmap beatmap) { var trackLoaded = lastBeatmap?.TrackLoaded ?? false; @@ -55,8 +47,6 @@ namespace osu.Game.Beatmaps lastBeatmap.RecycleTrack(); } - - audioManager.Tracks.AddItem(beatmap.Track); } lastBeatmap = beatmap; diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 4b0720d867..288bd0773c 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -11,6 +11,7 @@ using osu.Framework.IO.File; using System.IO; using System.Linq; using System.Threading; +using osu.Framework.Audio; using osu.Game.IO.Serialization; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; @@ -150,6 +151,9 @@ namespace osu.Game.Beatmaps public bool SkinLoaded => skin.IsResultAvailable; public Skin Skin => skin.Value; + + public AudioManager AudioManager { get; set; } + protected virtual Skin GetSkin() => new DefaultSkin(); private readonly RecyclableLazy skin; @@ -175,7 +179,7 @@ namespace osu.Game.Beatmaps /// Eagerly dispose of the audio track associated with this (if any). /// Accessing track again will load a fresh instance. /// - public void RecycleTrack() => track.Recycle(); + public virtual void RecycleTrack() => track.Recycle(); public class RecyclableLazy { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 40cb26ec54..8e663de8c5 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -281,20 +281,19 @@ namespace osu.Game private class OsuBindableBeatmap : BindableBeatmap { - public OsuBindableBeatmap(WorkingBeatmap defaultValue, AudioManager audioManager) - : this(defaultValue) + public OsuBindableBeatmap(WorkingBeatmap defaultValue) + : base(defaultValue, null) { - RegisterAudioManager(audioManager); } - public OsuBindableBeatmap(WorkingBeatmap defaultValue) - : base(defaultValue) + public OsuBindableBeatmap(WorkingBeatmap defaultValue, AudioManager audioManager) + : base(defaultValue, audioManager) { } public override BindableBeatmap GetBoundCopy() { - var copy = new OsuBindableBeatmap(Default); + var copy = new OsuBindableBeatmap(Default, AudioManager); copy.BindTo(this); return copy; } diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index ea3e1ca00c..d7b915efe3 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -350,7 +350,7 @@ namespace osu.Game.Overlays direction = last > next ? TransformDirection.Prev : TransformDirection.Next; } - current.Track.Completed -= currentTrackCompleted; + //current.Track.Completed -= currentTrackCompleted; } current = beatmap.NewValue; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index b266a73eb5..6d5be607f4 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -580,9 +580,6 @@ namespace osu.Game.Screens.Select if (!track.IsRunning || restart) { - // Ensure the track is added to the TrackManager, since it is removed after the player finishes the map. - // Using AddItemToList rather than AddItem so that it doesn't attempt to register adjustment dependencies more than once. - Game.Audio.Tracks.AddItem(track); track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; track.Restart(); } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 9f31783a6b..8d38f944d0 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -21,7 +21,7 @@ namespace osu.Game.Skinning { protected TextureStore Textures; - protected SampleStore Samples; + protected IResourceStore Samples; public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) : this(skin, new LegacySkinResourceStore(skin, storage), audioManager, "skin.ini") @@ -42,6 +42,13 @@ namespace osu.Game.Skinning Textures = new TextureStore(new TextureLoaderStore(storage)); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + Textures?.Dispose(); + Samples?.Dispose(); + } + public override Drawable GetDrawableComponent(string componentName) { switch (componentName) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 9b775fd498..806b73b517 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual { [Cached(typeof(Bindable))] [Cached(typeof(IBindable))] - private readonly OsuTestBeatmap beatmap = new OsuTestBeatmap(new DummyWorkingBeatmap()); + private readonly OsuTestBeatmap beatmap = new OsuTestBeatmap(new DummyWorkingBeatmap(), null); protected BindableBeatmap Beatmap => beatmap; @@ -52,8 +52,6 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(AudioManager audioManager, RulesetStore rulesets) { - beatmap.SetAudioManager(audioManager); - Ruleset.Value = rulesets.AvailableRulesets.First(); } @@ -95,16 +93,14 @@ namespace osu.Game.Tests.Visual private class OsuTestBeatmap : BindableBeatmap { - public OsuTestBeatmap(WorkingBeatmap defaultValue) - : base(defaultValue) + public OsuTestBeatmap(WorkingBeatmap defaultValue, AudioManager audioManager) + : base(defaultValue, audioManager) { } - public void SetAudioManager(AudioManager audioManager) => RegisterAudioManager(audioManager); - public override BindableBeatmap GetBoundCopy() { - var copy = new OsuTestBeatmap(Default); + var copy = new OsuTestBeatmap(Default, AudioManager); copy.BindTo(this); return copy; } From d4f3c60dd4d5b3c6fe8a899c6af07b8964fc1228 Mon Sep 17 00:00:00 2001 From: tangalbert919 Date: Tue, 28 May 2019 11:22:52 -0500 Subject: [PATCH 0839/2854] Update resources and framework for Android --- osu.Android.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 028b4bda83..6a4a29fcf8 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,7 +63,7 @@ - - + + From 19fbab68928128641a15681a84f1611d58cf2870 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 28 May 2019 19:39:31 +0300 Subject: [PATCH 0840/2854] Applied suggested changes --- .../Sections/Beatmaps/PaginatedBeatmapContainer.cs | 2 -- .../Historical/PaginatedMostPlayedBeatmapContainer.cs | 2 -- .../Overlays/Profile/Sections/PaginatedContainer.cs | 10 ++++++---- .../Profile/Sections/Ranks/PaginatedScoreContainer.cs | 2 -- .../Recent/PaginatedRecentActivityContainer.cs | 2 -- 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index db291d0731..b6b0e605d7 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -29,8 +29,6 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps protected override void ShowMore() { - base.ShowMore(); - request = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); request.Success += sets => Schedule(() => { diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index 0f86e0900e..6085b0bc05 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -24,8 +24,6 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected override void ShowMore() { - base.ShowMore(); - request = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); request.Success += beatmaps => Schedule(() => { diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 7e13a90f25..99229a9bce 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -20,7 +20,7 @@ using osu.Game.Users; namespace osu.Game.Overlays.Profile.Sections { - public class PaginatedContainer : FillFlowContainer + public abstract class PaginatedContainer : FillFlowContainer { protected readonly FillFlowContainer ItemsContainer; protected readonly ShowMoreButton MoreButton; @@ -92,7 +92,7 @@ namespace osu.Game.Overlays.Profile.Sections ShowMore(); } - protected virtual void ShowMore() => MoreButton.IsLoading = true; + protected abstract void ShowMore(); protected class ShowMoreButton : CircularContainer { @@ -109,8 +109,11 @@ namespace osu.Game.Overlays.Profile.Sections public bool IsLoading { + get => isLoading; set { + if (isLoading == value) + return; isLoading = value; if (value) @@ -124,7 +127,6 @@ namespace osu.Game.Overlays.Profile.Sections content.FadeIn(duration, Easing.OutQuint); } } - get => isLoading; } public ShowMoreButton() @@ -141,7 +143,6 @@ namespace osu.Game.Overlays.Profile.Sections }, content = new FillFlowContainer { - Alpha = 0, Anchor = Anchor.Centre, Origin = Anchor.Centre, AutoSizeAxes = Axes.Both, @@ -190,6 +191,7 @@ namespace osu.Game.Overlays.Profile.Sections protected override bool OnClick(ClickEvent e) { + IsLoading = true; Action.Invoke(); return base.OnClick(e); } diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 1d9e3d1cc1..a149cfa12e 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -31,8 +31,6 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks protected override void ShowMore() { - base.ShowMore(); - request = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); request.Success += scores => Schedule(() => { diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index 38134ad660..b72aec7a44 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -22,8 +22,6 @@ namespace osu.Game.Overlays.Profile.Sections.Recent protected override void ShowMore() { - base.ShowMore(); - request = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); request.Success += activities => Schedule(() => { From 5169e31d54ea77eeabfc4fe14333fdb91d53147e Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 28 May 2019 19:53:00 +0300 Subject: [PATCH 0841/2854] Fix CI issues --- osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 99229a9bce..6e0729c7c5 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Profile.Sections protected APIRequest RetrievalRequest; protected RulesetStore Rulesets; - public PaginatedContainer(Bindable user, string header, string missing) + protected PaginatedContainer(Bindable user, string header, string missing) { User.BindTo(user); @@ -114,6 +114,7 @@ namespace osu.Game.Overlays.Profile.Sections { if (isLoading == value) return; + isLoading = value; if (value) From 4f091417189baf51bf504cf401a914fe8d568234 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 29 May 2019 12:22:34 +0900 Subject: [PATCH 0842/2854] remove extra bool --- osu.Game/Screens/Select/BeatmapCarousel.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 0b3d0c448b..6e3bec106f 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -152,15 +152,15 @@ namespace osu.Game.Screens.Select { Schedule(() => { + int? previouslySelectedID = null; CarouselBeatmapSet existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.ID == beatmapSet.ID); - bool hadSelection = existingSet?.State?.Value == CarouselItemState.Selected; + // Since we're about to remove the selected beatmap, store its ID so we can go back if needed. + if (existingSet?.State?.Value == CarouselItemState.Selected) + previouslySelectedID = selectedBeatmap?.Beatmap.ID; var newSet = createCarouselSet(beatmapSet); - // Since we're about to remove the selected beatmap, store its ID so we can go back if needed. - var previouslySelectedID = selectedBeatmap?.Beatmap.ID; - if (existingSet != null) root.RemoveChild(existingSet); @@ -175,7 +175,7 @@ namespace osu.Game.Screens.Select applyActiveCriteria(false, false); //check if we can/need to maintain our current selection. - if (hadSelection) + if (previouslySelectedID != null) select((CarouselItem)newSet.Beatmaps.FirstOrDefault(b => b.Beatmap.ID == previouslySelectedID) ?? newSet); itemsCache.Invalidate(); From 08ab1e5df7ee4fc1bd48231bbcb2f0a35f9e680a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 May 2019 16:43:15 +0900 Subject: [PATCH 0843/2854] Use new ITrackStore interface --- .../Visual/Components/TestScenePreviewTrackManager.cs | 5 +++-- osu.Game/Audio/PreviewTrackManager.cs | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs b/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs index 0784725ea4..c966eb53d7 100644 --- a/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs +++ b/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; @@ -112,11 +112,12 @@ namespace osu.Game.Tests.Visual.Components private class TestPreviewTrackManager : PreviewTrackManager { - protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, IResourceStore trackStore) => new TestPreviewTrack(beatmapSetInfo, trackStore); + protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TestPreviewTrack(beatmapSetInfo, trackStore); protected class TestPreviewTrack : TrackManagerPreviewTrack { public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, IResourceStore trackManager) + public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackManager) : base(beatmapSetInfo, trackManager) { } diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index 4b7277c3fd..d479483508 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -20,7 +20,7 @@ namespace osu.Game.Audio private readonly BindableDouble muteBindable = new BindableDouble(); private AudioManager audio; - private IAdjustableResourceStore trackStore; + private ITrackStore trackStore; private TrackManagerPreviewTrack current; @@ -80,16 +80,16 @@ namespace osu.Game.Audio /// /// Creates the . /// - protected virtual TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, IResourceStore trackStore) => new TrackManagerPreviewTrack(beatmapSetInfo, trackStore); + protected virtual TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TrackManagerPreviewTrack(beatmapSetInfo, trackStore); protected class TrackManagerPreviewTrack : PreviewTrack { public IPreviewTrackOwner Owner { get; private set; } private readonly BeatmapSetInfo beatmapSetInfo; - private readonly IResourceStore trackManager; + private readonly ITrackStore trackManager; - public TrackManagerPreviewTrack(BeatmapSetInfo beatmapSetInfo, IResourceStore trackManager) + public TrackManagerPreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackManager) { this.beatmapSetInfo = beatmapSetInfo; this.trackManager = trackManager; From a1cc8c448fb197bc40c6598b023a2c1693cc4923 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 May 2019 16:43:27 +0900 Subject: [PATCH 0844/2854] Update TrackVirtual consumption --- .../TestScenePreviewTrackManager.cs | 9 ++-- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 31 +++++++++++++- .../WorkingBeatmap_VirtualBeatmapTrack.cs | 41 ------------------- 4 files changed, 35 insertions(+), 48 deletions(-) delete mode 100644 osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs diff --git a/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs b/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs index c966eb53d7..df6740421b 100644 --- a/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs +++ b/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs @@ -1,11 +1,10 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Containers; -using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -116,13 +115,15 @@ namespace osu.Game.Tests.Visual.Components protected class TestPreviewTrack : TrackManagerPreviewTrack { - public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, IResourceStore trackManager) + private readonly ITrackStore trackManager; + public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackManager) : base(beatmapSetInfo, trackManager) { + this.trackManager = trackManager; } - protected override Track GetTrack() => new TrackVirtual { Length = 100000 }; + protected override Track GetTrack() => trackManager.GetVirtual(100000); } } } diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 7d25ca3ede..b35e98085a 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -43,7 +43,7 @@ namespace osu.Game.Beatmaps protected override Texture GetBackground() => game?.Textures.Get(@"Backgrounds/bg4"); - protected override Track GetTrack() => new TrackVirtual { Length = 1000 }; + protected override Track GetTrack() => game?.Audio.Tracks.GetVirtual(1000); private class DummyRulesetInfo : RulesetInfo { diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 288bd0773c..6c8f283923 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -15,12 +15,13 @@ using osu.Framework.Audio; using osu.Game.IO.Serialization; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.UI; using osu.Game.Skinning; namespace osu.Game.Beatmaps { - public abstract partial class WorkingBeatmap : IDisposable + public abstract class WorkingBeatmap : IDisposable { public readonly BeatmapInfo BeatmapInfo; @@ -47,13 +48,39 @@ namespace osu.Game.Beatmaps return b; }); - track = new RecyclableLazy(() => GetTrack() ?? new VirtualBeatmapTrack(Beatmap)); + track = new RecyclableLazy(() => GetTrack() ?? GetVirtualTrack(Beatmap)); background = new RecyclableLazy(GetBackground, BackgroundStillValid); waveform = new RecyclableLazy(GetWaveform); storyboard = new RecyclableLazy(GetStoryboard); skin = new RecyclableLazy(GetSkin); } + protected virtual Track GetVirtualTrack(IBeatmap beatmap) + { + const double excess_length = 1000; + + var lastObject = beatmap.HitObjects.LastOrDefault(); + + double length; + + switch (lastObject) + { + case null: + length = excess_length; + break; + + case IHasEndTime endTime: + length = endTime.EndTime + excess_length; + break; + + default: + length = lastObject.StartTime + excess_length; + break; + } + + return AudioManager.Tracks.GetVirtual(length); + } + /// /// Saves the . /// diff --git a/osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs b/osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs deleted file mode 100644 index 1e237a2b53..0000000000 --- a/osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Linq; -using osu.Framework.Audio.Track; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Beatmaps -{ - public partial class WorkingBeatmap - { - /// - /// A type of which provides a valid length based on the s of an . - /// - protected class VirtualBeatmapTrack : TrackVirtual - { - private const double excess_length = 1000; - - public VirtualBeatmapTrack(IBeatmap beatmap) - { - var lastObject = beatmap.HitObjects.LastOrDefault(); - - switch (lastObject) - { - case null: - Length = excess_length; - break; - - case IHasEndTime endTime: - Length = endTime.EndTime + excess_length; - break; - - default: - Length = lastObject.StartTime + excess_length; - break; - } - } - } - } -} From 7e9f5a0939d41a262a9d19da778b54e140032346 Mon Sep 17 00:00:00 2001 From: HoLLy Date: Wed, 29 May 2019 11:22:51 +0200 Subject: [PATCH 0845/2854] Add Skills to DifficultyAttributes --- .../Difficulty/CatchDifficultyCalculator.cs | 5 +++-- .../Difficulty/ManiaDifficultyCalculator.cs | 3 ++- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 5 +++-- .../Difficulty/TaikoDifficultyCalculator.cs | 3 ++- osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs | 5 ++++- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index d6a1ed632b..44e1a8e5cc 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { if (beatmap.HitObjects.Count == 0) - return new CatchDifficultyAttributes { Mods = mods }; + return new CatchDifficultyAttributes { Mods = mods, Skills = skills }; // this is the same as osu!, so there's potential to share the implementation... maybe double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; @@ -41,7 +41,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty StarRating = Math.Sqrt(skills[0].DifficultyValue()) * star_scaling_factor, Mods = mods, ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0, - MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet)) + MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet)), + Skills = skills }; } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 59fed1031f..4a9c22d339 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { if (beatmap.HitObjects.Count == 0) - return new ManiaDifficultyAttributes { Mods = mods }; + return new ManiaDifficultyAttributes { Mods = mods, Skills = skills }; return new ManiaDifficultyAttributes { @@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty Mods = mods, // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate, + Skills = skills }; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index e2a1542574..c197933233 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { if (beatmap.HitObjects.Count == 0) - return new OsuDifficultyAttributes { Mods = mods }; + return new OsuDifficultyAttributes { Mods = mods, Skills = skills }; double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; @@ -50,7 +50,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty SpeedStrain = speedRating, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, OverallDifficulty = (80 - hitWindowGreat) / 6, - MaxCombo = maxCombo + MaxCombo = maxCombo, + Skills = skills }; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 685ad9949b..c8f3e18911 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { if (beatmap.HitObjects.Count == 0) - return new TaikoDifficultyAttributes { Mods = mods }; + return new TaikoDifficultyAttributes { Mods = mods, Skills = skills }; return new TaikoDifficultyAttributes { @@ -36,6 +36,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate, MaxCombo = beatmap.HitObjects.Count(h => h is Hit), + Skills = skills }; } diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs index d808ee528e..b4b4bb9cd1 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Difficulty @@ -8,6 +9,7 @@ namespace osu.Game.Rulesets.Difficulty public class DifficultyAttributes { public Mod[] Mods; + public Skill[] Skills; public double StarRating; @@ -15,9 +17,10 @@ namespace osu.Game.Rulesets.Difficulty { } - public DifficultyAttributes(Mod[] mods, double starRating) + public DifficultyAttributes(Mod[] mods, Skill[] skills, double starRating) { Mods = mods; + Skills = skills; StarRating = starRating; } } From 2a295545a79b89c18447e0aee599eb5a6c27a04e Mon Sep 17 00:00:00 2001 From: HoLLy Date: Wed, 29 May 2019 11:25:25 +0200 Subject: [PATCH 0846/2854] Don't mutate strainPeaks --- osu.Game/Rulesets/Difficulty/Skills/Skill.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/Skills/Skill.cs b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs index e8020ed185..227f2f4018 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/Skill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Utils; @@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// /// The peak strain for each section of the beatmap. /// - public IList StrainPeaks => strainPeaks; + public IReadOnlyList StrainPeaks => strainPeaks; /// /// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other. @@ -84,13 +85,12 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// public double DifficultyValue() { - strainPeaks.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. - double difficulty = 0; double weight = 1; // Difficulty is the weighted sum of the highest strains from every section. - foreach (double strain in strainPeaks) + // We're sorting from highest to lowest strain. + foreach (double strain in strainPeaks.OrderByDescending(d => d)) { difficulty += strain * weight; weight *= DecayWeight; From 8bcb4485edc4e8d290f22b02693645954bb256b1 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Wed, 29 May 2019 19:00:20 +0300 Subject: [PATCH 0847/2854] implement UnderscoredLinkContainer --- .../Sections/UnderscoredLinkContainer.cs | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs diff --git a/osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs b/osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs new file mode 100644 index 0000000000..087bd03837 --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs @@ -0,0 +1,79 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Sprites; +using System; +using System.Collections.Generic; + +namespace osu.Game.Overlays.Profile.Sections +{ + public class UnderscoredLinkContainer : Container + { + private const int duration = 200; + public Action ClickAction; + private readonly Container underscore; + private readonly FillFlowContainer textContent; + + public IReadOnlyList Text + { + get => textContent.Children; + set + { + textContent.Clear(); + textContent.AddRange(value); + } + } + + public UnderscoredLinkContainer() + { + AutoSizeAxes = Axes.Both; + Child = new Container + { + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + underscore = new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = 1, + Alpha = 0, + AlwaysPresent = true, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + } + }, + textContent = new FillFlowContainer + { + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + }, + }, + }; + } + + protected override bool OnHover(HoverEvent e) + { + underscore.FadeIn(duration, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + underscore.FadeOut(duration, Easing.OutQuint); + base.OnHoverLost(e); + } + + protected override bool OnClick(ClickEvent e) + { + ClickAction?.Invoke(); + return base.OnClick(e); + } + } +} From 52fad723a206ea5d86bb1384405aca64af106293 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Wed, 29 May 2019 19:51:59 +0300 Subject: [PATCH 0848/2854] Implement DrawableMostPlayedBeatmap --- .../Online/TestSceneHistoricalSection.cs | 2 +- .../Historical/DrawableMostPlayedBeatmap.cs | 190 ++++++++++++++++++ .../Historical/DrawableMostPlayedRow.cs | 77 ------- .../PaginatedMostPlayedBeatmapContainer.cs | 2 +- 4 files changed, 192 insertions(+), 79 deletions(-) create mode 100644 osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs delete mode 100644 osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedRow.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs index 455807649a..f309112f0d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual.Online { typeof(HistoricalSection), typeof(PaginatedMostPlayedBeatmapContainer), - typeof(DrawableMostPlayedRow), + typeof(DrawableMostPlayedBeatmap), typeof(DrawableProfileRow) }; diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs new file mode 100644 index 0000000000..e8ce11555f --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs @@ -0,0 +1,190 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; +using osu.Framework.Localisation; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Profile.Sections.Historical +{ + public class DrawableMostPlayedBeatmap : Container + { + private readonly BeatmapInfo beatmap; + private readonly OsuSpriteText mapperText; + private readonly int playCount; + private readonly Box background; + private Color4 idleBackgroundColour; + private Color4 hoveredBackgroundColour; + private const int duration = 200; + private const int cover_width = 100; + private const int corner_radius = 10; + private readonly SpriteIcon icon; + private readonly OsuSpriteText playCountText; + private readonly UnderscoredLinkContainer mapper; + private readonly UnderscoredLinkContainer beatmapName; + + public DrawableMostPlayedBeatmap(BeatmapInfo beatmap, int playCount) + { + this.beatmap = beatmap; + this.playCount = playCount; + + RelativeSizeAxes = Axes.X; + Height = 60; + Masking = true; + CornerRadius = corner_radius; + Children = new Drawable[] + { + new UpdateableBeatmapSetCover + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Y, + Width = cover_width, + BeatmapSet = beatmap.BeatmapSet, + CoverType = BeatmapSetCoverType.List, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = cover_width - corner_radius }, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = corner_radius, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = 15, Right = 20 }, + Children = new Drawable[] + { + beatmapName = new UnderscoredLinkContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Bottom = 2 }, + Text = new OsuSpriteText[] + { + new OsuSpriteText + { + Text = new LocalisedString(( + $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ", + $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] ")), + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold) + }, + new OsuSpriteText + { + Text = "by " + new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)), + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular) + }, + } + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.TopLeft, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Top = 2 }, + Children = new Drawable[] + { + mapperText = new OsuSpriteText + { + Text = "mapped by ", + Font = OsuFont.GetFont(size: 15, weight: FontWeight.Regular), + }, + mapper = new UnderscoredLinkContainer + { + Text = new OsuSpriteText[] + { + new OsuSpriteText + { + Text = beatmap.Metadata.Author.Username, + Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold), + } + } + }, + } + }, + new FillFlowContainer + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + icon = new SpriteIcon + { + Icon = FontAwesome.Solid.CaretRight, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(20), + }, + playCountText = new OsuSpriteText + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Text = playCount.ToString(), + Font = OsuFont.GetFont(size: 30, weight: FontWeight.Regular, fixedWidth: true), + }, + } + } + } + }, + } + } + } + } + }; + } + + [BackgroundDependencyLoader(permitNulls: true)] + private void load(OsuColour colors, UserProfileOverlay userProfileOverlay, BeatmapSetOverlay beatmapSetOverlay) + { + idleBackgroundColour = background.Colour = colors.GreySeafoam; + hoveredBackgroundColour = colors.GreySeafoamLight; + mapperText.Colour = mapper.Colour = colors.GreySeafoamLighter; + icon.Colour = playCountText.Colour = colors.Yellow; + + mapper.ClickAction = () => userProfileOverlay.ShowUser(beatmap.Metadata.Author.Id); + beatmapName.ClickAction = () => + { + if (beatmap.OnlineBeatmapID != null) + beatmapSetOverlay?.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value); + else if (beatmap.BeatmapSet?.OnlineBeatmapSetID != null) + beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.BeatmapSet.OnlineBeatmapSetID.Value); + }; + } + + protected override bool OnHover(HoverEvent e) + { + background.FadeColour(hoveredBackgroundColour, duration, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + background.FadeColour(idleBackgroundColour, duration, Easing.OutQuint); + base.OnHoverLost(e); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedRow.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedRow.cs deleted file mode 100644 index 1b286f92d3..0000000000 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedRow.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osuTK; - -namespace osu.Game.Overlays.Profile.Sections.Historical -{ - public class DrawableMostPlayedRow : DrawableProfileRow - { - private readonly BeatmapInfo beatmap; - private readonly int playCount; - - public DrawableMostPlayedRow(BeatmapInfo beatmap, int playCount) - { - this.beatmap = beatmap; - this.playCount = playCount; - } - - protected override Drawable CreateLeftVisual() => new UpdateableBeatmapSetCover - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(80, 50), - BeatmapSet = beatmap.BeatmapSet, - CoverType = BeatmapSetCoverType.List, - }; - - [BackgroundDependencyLoader] - private void load() - { - LeftFlowContainer.Add(new BeatmapMetadataContainer(beatmap)); - LeftFlowContainer.Add(new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 12)) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - }.With(d => - { - d.AddText("mapped by "); - d.AddUserLink(beatmap.Metadata.Author); - })); - - RightFlowContainer.Add(new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new[] - { - new OsuSpriteText - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Text = playCount.ToString(), - Font = OsuFont.GetFont(size: 18, weight: FontWeight.SemiBold, italics: true) - }, - new OsuSpriteText - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Text = @"times played ", - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular, italics: true) - }, - } - }); - } - } -} diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index f2eb32c53b..13fe20d063 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical foreach (var beatmap in beatmaps) { - ItemsContainer.Add(new DrawableMostPlayedRow(beatmap.GetBeatmapInfo(Rulesets), beatmap.PlayCount)); + ItemsContainer.Add(new DrawableMostPlayedBeatmap(beatmap.GetBeatmapInfo(Rulesets), beatmap.PlayCount)); } }); From 6efa61b992a44cf4c3004ca9dc284da8952c90ba Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Wed, 29 May 2019 20:24:01 +0300 Subject: [PATCH 0849/2854] Split UnderscoredLinkContainer in different classes --- .../Historical/DrawableMostPlayedBeatmap.cs | 20 ++++--------- .../Sections/UnderscoredBeatmapLink.cs | 30 +++++++++++++++++++ .../Sections/UnderscoredLinkContainer.cs | 7 +++-- .../Profile/Sections/UnderscoredUserLink.cs | 23 ++++++++++++++ 4 files changed, 62 insertions(+), 18 deletions(-) create mode 100644 osu.Game/Overlays/Profile/Sections/UnderscoredBeatmapLink.cs create mode 100644 osu.Game/Overlays/Profile/Sections/UnderscoredUserLink.cs diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs index e8ce11555f..50f419e07c 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs @@ -30,8 +30,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private const int corner_radius = 10; private readonly SpriteIcon icon; private readonly OsuSpriteText playCountText; - private readonly UnderscoredLinkContainer mapper; - private readonly UnderscoredLinkContainer beatmapName; + private readonly UnderscoredUserLink mapper; public DrawableMostPlayedBeatmap(BeatmapInfo beatmap, int playCount) { @@ -76,7 +75,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Padding = new MarginPadding { Left = 15, Right = 20 }, Children = new Drawable[] { - beatmapName = new UnderscoredLinkContainer + new UnderscoredBeatmapLink(beatmap) { Anchor = Anchor.CentreLeft, Origin = Anchor.BottomLeft, @@ -111,7 +110,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Text = "mapped by ", Font = OsuFont.GetFont(size: 15, weight: FontWeight.Regular), }, - mapper = new UnderscoredLinkContainer + mapper = new UnderscoredUserLink(beatmap.Metadata.Author.Id) { Text = new OsuSpriteText[] { @@ -157,22 +156,13 @@ namespace osu.Game.Overlays.Profile.Sections.Historical }; } - [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuColour colors, UserProfileOverlay userProfileOverlay, BeatmapSetOverlay beatmapSetOverlay) + [BackgroundDependencyLoader] + private void load(OsuColour colors) { idleBackgroundColour = background.Colour = colors.GreySeafoam; hoveredBackgroundColour = colors.GreySeafoamLight; mapperText.Colour = mapper.Colour = colors.GreySeafoamLighter; icon.Colour = playCountText.Colour = colors.Yellow; - - mapper.ClickAction = () => userProfileOverlay.ShowUser(beatmap.Metadata.Author.Id); - beatmapName.ClickAction = () => - { - if (beatmap.OnlineBeatmapID != null) - beatmapSetOverlay?.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value); - else if (beatmap.BeatmapSet?.OnlineBeatmapSetID != null) - beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.BeatmapSet.OnlineBeatmapSetID.Value); - }; } protected override bool OnHover(HoverEvent e) diff --git a/osu.Game/Overlays/Profile/Sections/UnderscoredBeatmapLink.cs b/osu.Game/Overlays/Profile/Sections/UnderscoredBeatmapLink.cs new file mode 100644 index 0000000000..370d6d84ef --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/UnderscoredBeatmapLink.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Game.Beatmaps; + +namespace osu.Game.Overlays.Profile.Sections +{ + public class UnderscoredBeatmapLink : UnderscoredLinkContainer + { + private readonly BeatmapInfo beatmap; + + public UnderscoredBeatmapLink(BeatmapInfo beatmap) + { + this.beatmap = beatmap; + } + + [BackgroundDependencyLoader(true)] + private void load(BeatmapSetOverlay beatmapSetOverlay) + { + ClickAction = () => + { + if (beatmap.OnlineBeatmapID != null) + beatmapSetOverlay?.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value); + else if (beatmap.BeatmapSet?.OnlineBeatmapSetID != null) + beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.BeatmapSet.OnlineBeatmapSetID.Value); + }; + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs b/osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs index 087bd03837..8daf0bd24d 100644 --- a/osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs @@ -11,13 +11,14 @@ using System.Collections.Generic; namespace osu.Game.Overlays.Profile.Sections { - public class UnderscoredLinkContainer : Container + public abstract class UnderscoredLinkContainer : Container { private const int duration = 200; - public Action ClickAction; private readonly Container underscore; private readonly FillFlowContainer textContent; + protected Action ClickAction; + public IReadOnlyList Text { get => textContent.Children; @@ -28,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Sections } } - public UnderscoredLinkContainer() + protected UnderscoredLinkContainer() { AutoSizeAxes = Axes.Both; Child = new Container diff --git a/osu.Game/Overlays/Profile/Sections/UnderscoredUserLink.cs b/osu.Game/Overlays/Profile/Sections/UnderscoredUserLink.cs new file mode 100644 index 0000000000..f50bc7f7ba --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/UnderscoredUserLink.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; + +namespace osu.Game.Overlays.Profile.Sections +{ + public class UnderscoredUserLink : UnderscoredLinkContainer + { + private readonly long userId; + + public UnderscoredUserLink(long userId) + { + this.userId = userId; + } + + [BackgroundDependencyLoader(true)] + private void load(UserProfileOverlay userProfileOverlay) + { + ClickAction = () => userProfileOverlay?.ShowUser(userId); + } + } +} From 1baf922f2c3dace2bfa09f12e169cf8c078cb00b Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Wed, 29 May 2019 20:36:14 +0300 Subject: [PATCH 0850/2854] CI fixes --- .../Sections/Historical/DrawableMostPlayedBeatmap.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs index 50f419e07c..34b6884fe0 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs @@ -19,9 +19,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { public class DrawableMostPlayedBeatmap : Container { - private readonly BeatmapInfo beatmap; private readonly OsuSpriteText mapperText; - private readonly int playCount; private readonly Box background; private Color4 idleBackgroundColour; private Color4 hoveredBackgroundColour; @@ -34,9 +32,6 @@ namespace osu.Game.Overlays.Profile.Sections.Historical public DrawableMostPlayedBeatmap(BeatmapInfo beatmap, int playCount) { - this.beatmap = beatmap; - this.playCount = playCount; - RelativeSizeAxes = Axes.X; Height = 60; Masking = true; @@ -80,7 +75,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Anchor = Anchor.CentreLeft, Origin = Anchor.BottomLeft, Margin = new MarginPadding { Bottom = 2 }, - Text = new OsuSpriteText[] + Text = new[] { new OsuSpriteText { @@ -112,7 +107,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical }, mapper = new UnderscoredUserLink(beatmap.Metadata.Author.Id) { - Text = new OsuSpriteText[] + Text = new[] { new OsuSpriteText { From 97dbc95bc64d7a10506bdcc00f271f218ddf3853 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Wed, 29 May 2019 21:02:20 +0300 Subject: [PATCH 0851/2854] Kudosu section update --- .../Profile/Sections/Kudosu/KudosuInfo.cs | 75 ++++++++----------- 1 file changed, 31 insertions(+), 44 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index aeea5118a7..87a241936a 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -3,53 +3,38 @@ using osu.Framework.Bindables; using osuTK; -using osuTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Users; +using osu.Framework.Allocation; namespace osu.Game.Overlays.Profile.Sections.Kudosu { public class KudosuInfo : Container { private readonly Bindable user = new Bindable(); - public KudosuInfo(Bindable user) { this.user.BindTo(user); - CountSection total; CountSection avaliable; - RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Masking = true; CornerRadius = 3; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0f, 1f), - Radius = 3f, - Colour = Color4.Black.Opacity(0.2f), - }; Children = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.2f) - }, new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5, 0), Children = new[] { total = new CountSection( @@ -63,31 +48,29 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu } } }; - this.user.ValueChanged += u => { total.Count = u.NewValue?.Kudosu.Total ?? 0; avaliable.Count = u.NewValue?.Kudosu.Available ?? 0; }; } - protected override bool OnClick(ClickEvent e) => true; - private class CountSection : Container { private readonly OsuSpriteText valueText; + private readonly OsuTextFlowContainer descriptionText; + private readonly Box lineBackground; public new int Count { set => valueText.Text = value.ToString(); } - public CountSection(string header, string description) { RelativeSizeAxes = Axes.X; Width = 0.5f; AutoSizeAxes = Axes.Y; - Padding = new MarginPadding { Horizontal = 10, Top = 10, Bottom = 20 }; + Padding = new MarginPadding { Top = 10, Bottom = 20 }; Child = new FillFlowContainer { AutoSizeAxes = Axes.Y, @@ -96,31 +79,28 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu Spacing = new Vector2(0, 5), Children = new Drawable[] { - new FillFlowContainer + new CircularContainer { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5, 0), - Children = new Drawable[] + Masking = true, + RelativeSizeAxes = Axes.X, + Height = 5, + Child = lineBackground = new Box { - new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Text = header + ":", - Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular, italics: true) - }, - valueText = new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Text = "0", - Font = OsuFont.GetFont(size: 40, weight: FontWeight.Regular, italics: true), - UseFullGlyphHeight = false, - } + RelativeSizeAxes = Axes.Both, } }, - new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 19)) + new OsuSpriteText + { + Text = header, + Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold) + }, + valueText = new OsuSpriteText + { + Text = "0", + Font = OsuFont.GetFont(size: 50, weight: FontWeight.Light), + UseFullGlyphHeight = false, + }, + descriptionText = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 17)) { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -129,6 +109,13 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu } }; } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + lineBackground.Colour = colours.Yellow; + descriptionText.Colour = colours.GreySeafoamLighter; + } } } } From 9a13c52ffd860e2c05289ec5fe0a2e27bf06a717 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Wed, 29 May 2019 21:19:03 +0300 Subject: [PATCH 0852/2854] Add missing lines --- osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index 87a241936a..c8b803eb98 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -18,6 +18,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu public class KudosuInfo : Container { private readonly Bindable user = new Bindable(); + public KudosuInfo(Bindable user) { this.user.BindTo(user); @@ -54,7 +55,9 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu avaliable.Count = u.NewValue?.Kudosu.Available ?? 0; }; } + protected override bool OnClick(ClickEvent e) => true; + private class CountSection : Container { private readonly OsuSpriteText valueText; @@ -65,6 +68,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { set => valueText.Text = value.ToString(); } + public CountSection(string header, string description) { RelativeSizeAxes = Axes.X; From e5999dd9b1daa448638ca88c5090c0b0d10b42bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 May 2019 16:49:18 +0900 Subject: [PATCH 0853/2854] Update font sizes to match web --- osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index c8b803eb98..a07ecc3bf3 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -96,15 +96,15 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu new OsuSpriteText { Text = header, - Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold) + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold) }, valueText = new OsuSpriteText { Text = "0", - Font = OsuFont.GetFont(size: 50, weight: FontWeight.Light), + Font = OsuFont.GetFont(size: 40, weight: FontWeight.Light), UseFullGlyphHeight = false, }, - descriptionText = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 17)) + descriptionText = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 14)) { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, From c950f37497da2b3bee4f65fbbb32f845ac445c32 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 May 2019 16:57:54 +0900 Subject: [PATCH 0854/2854] Ad missing link --- .../Profile/Sections/Kudosu/KudosuInfo.cs | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index a07ecc3bf3..aabfa56ee6 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -38,14 +38,8 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu Spacing = new Vector2(5, 0), Children = new[] { - total = new CountSection( - "Total Kudosu Earned", - "Based on how much of a contribution the user has made to beatmap moderation. See this link for more information." - ), - avaliable = new CountSection( - "Kudosu Avaliable", - "Kudosu can be traded for kudosu stars, which will help your beatmap get more attention. This is the number of kudosu you haven't traded in yet." - ), + total = new CountTotal(), + avaliable = new CountAvailable() } } }; @@ -58,10 +52,30 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu protected override bool OnClick(ClickEvent e) => true; + private class CountAvailable : CountSection + { + public CountAvailable() + : base("Kudosu Avaliable") + { + DescriptionText.Text = "Kudosu can be traded for kudosu stars, which will help your beatmap get more attention. This is the number of kudosu you haven't traded in yet."; + } + } + + private class CountTotal : CountSection + { + public CountTotal() + : base("Total Kudosu Earned") + { + DescriptionText.AddText("Based on how much of a contribution the user has made to beatmap moderation. See "); + DescriptionText.AddLink("this link", "https://osu.ppy.sh/wiki/Kudosu"); + DescriptionText.AddText(" for more information."); + } + } + private class CountSection : Container { private readonly OsuSpriteText valueText; - private readonly OsuTextFlowContainer descriptionText; + protected readonly LinkFlowContainer DescriptionText; private readonly Box lineBackground; public new int Count @@ -69,7 +83,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu set => valueText.Text = value.ToString(); } - public CountSection(string header, string description) + public CountSection(string header) { RelativeSizeAxes = Axes.X; Width = 0.5f; @@ -104,11 +118,10 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu Font = OsuFont.GetFont(size: 40, weight: FontWeight.Light), UseFullGlyphHeight = false, }, - descriptionText = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 14)) + DescriptionText = new LinkFlowContainer(t => t.Font = t.Font.With(size: 14)) { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Text = description } } }; @@ -118,7 +131,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu private void load(OsuColour colours) { lineBackground.Colour = colours.Yellow; - descriptionText.Colour = colours.GreySeafoamLighter; + DescriptionText.Colour = colours.GreySeafoamLighter; } } } From 73fb28f9f7fbc082e2fd58b787d759c175f98b35 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Thu, 30 May 2019 22:48:27 +0300 Subject: [PATCH 0855/2854] Make the button inherit from OsuHoverContainer --- .../Profile/Sections/PaginatedContainer.cs | 93 +++++++++---------- 1 file changed, 42 insertions(+), 51 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 6e0729c7c5..504f80fc97 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -12,11 +12,11 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Framework.Input.Events; -using System; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osuTK.Graphics; using osu.Game.Users; +using osu.Game.Graphics.Containers; +using System.Collections.Generic; namespace osu.Game.Overlays.Profile.Sections { @@ -94,17 +94,14 @@ namespace osu.Game.Overlays.Profile.Sections protected abstract void ShowMore(); - protected class ShowMoreButton : CircularContainer + protected class ShowMoreButton : OsuHoverContainer { - private const int duration = 300; - private Color4 idleColour; - private Color4 hoveredColour; - - public Action Action; private readonly Box background; private readonly LoadingAnimation loading; private readonly FillFlowContainer content; + protected override IEnumerable EffectTargets => new[] { background }; + private bool isLoading; public bool IsLoading @@ -119,13 +116,13 @@ namespace osu.Game.Overlays.Profile.Sections if (value) { - loading.FadeIn(duration, Easing.OutQuint); - content.FadeOut(duration, Easing.OutQuint); + loading.FadeIn(FADE_DURATION, Easing.OutQuint); + content.FadeOut(FADE_DURATION, Easing.OutQuint); } else { - loading.FadeOut(duration, Easing.OutQuint); - content.FadeIn(duration, Easing.OutQuint); + loading.FadeOut(FADE_DURATION, Easing.OutQuint); + content.FadeIn(FADE_DURATION, Easing.OutQuint); } } } @@ -134,66 +131,60 @@ namespace osu.Game.Overlays.Profile.Sections { Anchor = Anchor.TopCentre; Origin = Anchor.TopCentre; - Masking = true; - Size = new Vector2(140, 30); + AutoSizeAxes = Axes.Both; Children = new Drawable[] { - background = new Box + new CircularContainer { - RelativeSizeAxes = Axes.Both, - }, - content = new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(7), + Masking = true, + Size = new Vector2(140, 30), Children = new Drawable[] { - new ChevronIcon(), - new OsuSpriteText + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + content = new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold), - Text = "show more".ToUpper(), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(7), + Children = new Drawable[] + { + new ChevronIcon(), + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), + Text = "show more".ToUpper(), + }, + new ChevronIcon(), + } + }, + loading = new LoadingAnimation + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(12) }, - new ChevronIcon(), } - }, - loading = new LoadingAnimation - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(20) - }, + } }; } [BackgroundDependencyLoader] private void load(OsuColour colors) { - background.Colour = idleColour = colors.GreySeafoam; - hoveredColour = colors.GreySeafoamLight; - } - - protected override bool OnHover(HoverEvent e) - { - background.FadeColour(hoveredColour, duration, Easing.OutQuint); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - background.FadeColour(idleColour, duration, Easing.OutQuint); - base.OnHoverLost(e); + IdleColour = colors.GreySeafoam; + HoverColour = colors.GreySeafoamLight; } protected override bool OnClick(ClickEvent e) { IsLoading = true; - Action.Invoke(); return base.OnClick(e); } From 2933169614bee615d4f8e75205d3414620c00be9 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Thu, 30 May 2019 22:55:59 +0300 Subject: [PATCH 0856/2854] Move the button into a separate class --- .../Profile/Sections/PaginatedContainer.cs | 122 ---------------- .../Profile/Sections/ShowMoreButton.cs | 134 ++++++++++++++++++ 2 files changed, 134 insertions(+), 122 deletions(-) create mode 100644 osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 504f80fc97..11803b0dc1 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -8,15 +8,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Rulesets; -using osu.Framework.Input.Events; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Game.Users; -using osu.Game.Graphics.Containers; -using System.Collections.Generic; namespace osu.Game.Overlays.Profile.Sections { @@ -93,121 +87,5 @@ namespace osu.Game.Overlays.Profile.Sections } protected abstract void ShowMore(); - - protected class ShowMoreButton : OsuHoverContainer - { - private readonly Box background; - private readonly LoadingAnimation loading; - private readonly FillFlowContainer content; - - protected override IEnumerable EffectTargets => new[] { background }; - - private bool isLoading; - - public bool IsLoading - { - get => isLoading; - set - { - if (isLoading == value) - return; - - isLoading = value; - - if (value) - { - loading.FadeIn(FADE_DURATION, Easing.OutQuint); - content.FadeOut(FADE_DURATION, Easing.OutQuint); - } - else - { - loading.FadeOut(FADE_DURATION, Easing.OutQuint); - content.FadeIn(FADE_DURATION, Easing.OutQuint); - } - } - } - - public ShowMoreButton() - { - Anchor = Anchor.TopCentre; - Origin = Anchor.TopCentre; - AutoSizeAxes = Axes.Both; - Children = new Drawable[] - { - new CircularContainer - { - Masking = true, - Size = new Vector2(140, 30), - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - }, - content = new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(7), - Children = new Drawable[] - { - new ChevronIcon(), - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), - Text = "show more".ToUpper(), - }, - new ChevronIcon(), - } - }, - loading = new LoadingAnimation - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(12) - }, - } - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colors) - { - IdleColour = colors.GreySeafoam; - HoverColour = colors.GreySeafoamLight; - } - - protected override bool OnClick(ClickEvent e) - { - IsLoading = true; - return base.OnClick(e); - } - - private class ChevronIcon : SpriteIcon - { - private const int bottom_margin = 2; - private const int icon_size = 8; - - public ChevronIcon() - { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - Margin = new MarginPadding { Bottom = bottom_margin }; - Size = new Vector2(icon_size); - Icon = FontAwesome.Solid.ChevronDown; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colors) - { - Colour = colors.Yellow; - } - } - } } } diff --git a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs new file mode 100644 index 0000000000..5979c971d1 --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs @@ -0,0 +1,134 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osuTK; +using System.Collections.Generic; + +namespace osu.Game.Overlays.Profile.Sections +{ + public class ShowMoreButton : OsuHoverContainer + { + private readonly Box background; + private readonly LoadingAnimation loading; + private readonly FillFlowContainer content; + + protected override IEnumerable EffectTargets => new[] { background }; + + private bool isLoading; + + public bool IsLoading + { + get => isLoading; + set + { + if (isLoading == value) + return; + + isLoading = value; + + if (value) + { + loading.FadeIn(FADE_DURATION, Easing.OutQuint); + content.FadeOut(FADE_DURATION, Easing.OutQuint); + } + else + { + loading.FadeOut(FADE_DURATION, Easing.OutQuint); + content.FadeIn(FADE_DURATION, Easing.OutQuint); + } + } + } + + public ShowMoreButton() + { + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; + AutoSizeAxes = Axes.Both; + Children = new Drawable[] + { + new CircularContainer + { + Masking = true, + Size = new Vector2(140, 30), + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + content = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(7), + Children = new Drawable[] + { + new ChevronIcon(), + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), + Text = "show more".ToUpper(), + }, + new ChevronIcon(), + } + }, + loading = new LoadingAnimation + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(12) + }, + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colors) + { + IdleColour = colors.GreySeafoam; + HoverColour = colors.GreySeafoamLight; + } + + protected override bool OnClick(ClickEvent e) + { + IsLoading = true; + return base.OnClick(e); + } + + private class ChevronIcon : SpriteIcon + { + private const int bottom_margin = 2; + private const int icon_size = 8; + + public ChevronIcon() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + Margin = new MarginPadding { Bottom = bottom_margin }; + Size = new Vector2(icon_size); + Icon = FontAwesome.Solid.ChevronDown; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colors) + { + Colour = colors.Yellow; + } + } + } +} From fe9e53e383e5f2d35e382daffad3f5ba1b253a53 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Thu, 30 May 2019 23:07:04 +0300 Subject: [PATCH 0857/2854] Add a testcase --- .../Visual/Online/TestSceneShowMoreButton.cs | 31 +++++++++ .../Graphics/Containers/OsuHoverContainer.cs | 6 +- .../Profile/Sections/PaginatedContainer.cs | 2 + .../Profile/Sections/ShowMoreButton.cs | 68 +++++++++---------- 4 files changed, 70 insertions(+), 37 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs new file mode 100644 index 0000000000..8289e4a09c --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Overlays.Profile.Sections; +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneShowMoreButton : OsuTestScene + { + private readonly ShowMoreButton button; + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ShowMoreButton), + }; + + public TestSceneShowMoreButton() + { + Add(button = new ShowMoreButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + + AddStep("switch loading state", () => button.IsLoading = !button.IsLoading); + } + } +} diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs index c4f85926ee..eb2d926424 100644 --- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs +++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs @@ -24,7 +24,8 @@ namespace osu.Game.Graphics.Containers { Enabled.ValueChanged += e => { - if (!e.NewValue) unhover(); + if (!e.NewValue) + unhover(); }; } @@ -49,7 +50,8 @@ namespace osu.Game.Graphics.Containers private void unhover() { - if (!isHovered) return; + if (!isHovered) + return; isHovered = false; EffectTargets.ForEach(d => d.FadeColour(IdleColour, FADE_DURATION, Easing.OutQuint)); diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 11803b0dc1..8639acfc94 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -54,6 +54,8 @@ namespace osu.Game.Overlays.Profile.Sections }, MoreButton = new ShowMoreButton { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, Alpha = 0, Margin = new MarginPadding { Top = 10 }, Action = ShowMore, diff --git a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs index 5979c971d1..31c73aaa96 100644 --- a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs +++ b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs @@ -51,49 +51,47 @@ namespace osu.Game.Overlays.Profile.Sections public ShowMoreButton() { - Anchor = Anchor.TopCentre; - Origin = Anchor.TopCentre; AutoSizeAxes = Axes.Both; Children = new Drawable[] { - new CircularContainer + new CircularContainer + { + Masking = true, + Size = new Vector2(140, 30), + Children = new Drawable[] { - Masking = true, - Size = new Vector2(140, 30), - Children = new Drawable[] + background = new Box { - background = new Box + RelativeSizeAxes = Axes.Both, + }, + content = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(7), + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - }, - content = new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(7), - Children = new Drawable[] + new ChevronIcon(), + new OsuSpriteText { - new ChevronIcon(), - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), - Text = "show more".ToUpper(), - }, - new ChevronIcon(), - } - }, - loading = new LoadingAnimation - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(12) - }, - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), + Text = "show more".ToUpper(), + }, + new ChevronIcon(), + } + }, + loading = new LoadingAnimation + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(12) + }, } + } }; } From cfa0ef6fd9c410cf0f2d7c32c9613daf5c179e62 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Thu, 30 May 2019 23:22:08 +0300 Subject: [PATCH 0858/2854] convert field to a local variable --- osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs index 8289e4a09c..0d6d378f4c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs @@ -10,8 +10,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneShowMoreButton : OsuTestScene { - private readonly ShowMoreButton button; - public override IReadOnlyList RequiredTypes => new[] { typeof(ShowMoreButton), @@ -19,6 +17,8 @@ namespace osu.Game.Tests.Visual.Online public TestSceneShowMoreButton() { + ShowMoreButton button; + Add(button = new ShowMoreButton { Anchor = Anchor.Centre, From b32ffab58054309c25c2a86895bfce998af3b987 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Fri, 31 May 2019 02:19:09 +0300 Subject: [PATCH 0859/2854] Make the DrawableMostPlayedBeatmap inherit OsuHoverContainer --- .../Historical/DrawableMostPlayedBeatmap.cs | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs index 34b6884fe0..98872d6141 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs @@ -6,32 +6,33 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; -using osuTK.Graphics; +using System.Collections.Generic; namespace osu.Game.Overlays.Profile.Sections.Historical { - public class DrawableMostPlayedBeatmap : Container + public class DrawableMostPlayedBeatmap : OsuHoverContainer { private readonly OsuSpriteText mapperText; private readonly Box background; - private Color4 idleBackgroundColour; - private Color4 hoveredBackgroundColour; - private const int duration = 200; private const int cover_width = 100; private const int corner_radius = 10; private readonly SpriteIcon icon; private readonly OsuSpriteText playCountText; private readonly UnderscoredUserLink mapper; + protected override IEnumerable EffectTargets => new[] { background }; + public DrawableMostPlayedBeatmap(BeatmapInfo beatmap, int playCount) { + Enabled.Value = true; //manually enabled, because we have no action + RelativeSizeAxes = Axes.X; Height = 60; Masking = true; @@ -154,22 +155,10 @@ namespace osu.Game.Overlays.Profile.Sections.Historical [BackgroundDependencyLoader] private void load(OsuColour colors) { - idleBackgroundColour = background.Colour = colors.GreySeafoam; - hoveredBackgroundColour = colors.GreySeafoamLight; + IdleColour = colors.GreySeafoam; + HoverColour = colors.GreySeafoamLight; mapperText.Colour = mapper.Colour = colors.GreySeafoamLighter; icon.Colour = playCountText.Colour = colors.Yellow; } - - protected override bool OnHover(HoverEvent e) - { - background.FadeColour(hoveredBackgroundColour, duration, Easing.OutQuint); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - background.FadeColour(idleBackgroundColour, duration, Easing.OutQuint); - base.OnHoverLost(e); - } } } From f780c80c17d406f7c6518066930072de27083727 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 May 2019 13:03:35 +0900 Subject: [PATCH 0860/2854] Fix bar not expanding/collapsing correctly --- osu.Game/Overlays/Changelog/UpdateStreamBadge.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs b/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs index c39e6a6784..4f1d95bc2c 100644 --- a/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs +++ b/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs @@ -84,7 +84,7 @@ namespace osu.Game.Overlays.Changelog } }; - SelectedTab.ValueChanged += _ => updateState(); + SelectedTab.BindValueChanged(_ => updateState(), true); } protected override void OnActivated() => updateState(); @@ -113,7 +113,13 @@ namespace osu.Game.Overlays.Changelog private void updateState() { - if (Active.Value || IsHovered || SelectedTab.Value == null) + // Expand based on the local state + bool shouldExpand = Active.Value || IsHovered; + + // Expand based on whether no build is selected and the badge area is hovered + shouldExpand |= SelectedTab.Value == null && !externalDimRequested; + + if (shouldExpand) { expandingBar.Expand(); fadeContainer.FadeTo(1, transition_duration); From 4dc77d64a32c37397e3adea7f7ea0a485af3154a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 May 2019 13:23:50 +0900 Subject: [PATCH 0861/2854] Fix overlay group + depth --- osu.Game/OsuGame.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2bbd0b8303..ba9abcdefc 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -435,9 +435,9 @@ namespace osu.Game loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal, true); loadComponentSingleFile(chatOverlay = new ChatOverlay(), overlayContent.Add, true); loadComponentSingleFile(settings = new SettingsOverlay { GetToolbarHeight = () => ToolbarOffset }, leftFloatingOverlayContent.Add, true); + var changelogOverlay = loadComponentSingleFile(new ChangelogOverlay(), overlayContent.Add, true); loadComponentSingleFile(userProfile = new UserProfileOverlay(), overlayContent.Add, true); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay(), overlayContent.Add, true); - var changelogOverlay = loadComponentSingleFile(new ChangelogOverlay(), overlayContent.Add, true); loadComponentSingleFile(new LoginOverlay { @@ -479,7 +479,7 @@ namespace osu.Game } // eventually informational overlays should be displayed in a stack, but for now let's only allow one to stay open at a time. - var informationalOverlays = new OverlayContainer[] { beatmapSetOverlay, userProfile, changelogOverlay }; + var informationalOverlays = new OverlayContainer[] { beatmapSetOverlay, userProfile }; overlays.AddRange(informationalOverlays); foreach (var overlay in informationalOverlays) @@ -493,7 +493,7 @@ namespace osu.Game } // ensure only one of these overlays are open at once. - var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, social, direct }; + var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, social, direct, changelogOverlay }; overlays.AddRange(singleDisplayOverlays); foreach (var overlay in singleDisplayOverlays) From 57d648df6dcd0a44c63e46a19dceb264f233ad40 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 May 2019 13:38:48 +0900 Subject: [PATCH 0862/2854] Add comment + fix spinlocking --- osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs index 81c7905e84..36ae5a756c 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs @@ -43,6 +43,7 @@ namespace osu.Game.Overlays.Changelog }; req.Failure += _ => complete = true; + // This is done on a separate thread to support cancellation below Task.Run(() => req.Perform(api)); while (!complete) @@ -53,7 +54,7 @@ namespace osu.Game.Overlays.Changelog return; } - Task.Delay(1); + Thread.Sleep(10); } if (build != null) From e7ae9c249fdd7ef3fee5f0b5384986a5abd3f0de Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 May 2019 13:53:55 +0900 Subject: [PATCH 0863/2854] Fix size of release stream separator in listing --- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 4 +++- osu.Game/Overlays/Changelog/ChangelogListing.cs | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index a76211a0ae..57615332da 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -18,6 +18,8 @@ namespace osu.Game.Overlays.Changelog { public class ChangelogBuild : FillFlowContainer { + public const float HORIZONTAL_PADDING = 70; + public Action SelectBuild; protected readonly APIChangelogBuild Build; @@ -31,7 +33,7 @@ namespace osu.Game.Overlays.Changelog RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; - Padding = new MarginPadding { Horizontal = 70 }; + Padding = new MarginPadding { Horizontal = HORIZONTAL_PADDING }; Children = new Drawable[] { diff --git a/osu.Game/Overlays/Changelog/ChangelogListing.cs b/osu.Game/Overlays/Changelog/ChangelogListing.cs index 20b7a32eba..1856f93205 100644 --- a/osu.Game/Overlays/Changelog/ChangelogListing.cs +++ b/osu.Game/Overlays/Changelog/ChangelogListing.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -58,12 +59,17 @@ namespace osu.Game.Overlays.Changelog } else { - Add(new Box + Add(new Container { RelativeSizeAxes = Axes.X, Height = 1, - Colour = new Color4(32, 24, 35, 255), + Padding = new MarginPadding { Horizontal = ChangelogBuild.HORIZONTAL_PADDING }, Margin = new MarginPadding { Top = 30 }, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new Color4(32, 24, 35, 255), + } }); } From d7ccf939d8098483e4ab2ea5935e44c32286fdc3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 May 2019 13:54:40 +0900 Subject: [PATCH 0864/2854] General refactoring --- osu.Game/Overlays/Changelog/ChangelogListing.cs | 6 +++--- osu.Game/Overlays/Changelog/UpdateStreamBadge.cs | 14 +++++++------- osu.Game/Overlays/ChangelogOverlay.cs | 11 ++++------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogListing.cs b/osu.Game/Overlays/Changelog/ChangelogListing.cs index 1856f93205..41d8228475 100644 --- a/osu.Game/Overlays/Changelog/ChangelogListing.cs +++ b/osu.Game/Overlays/Changelog/ChangelogListing.cs @@ -47,12 +47,12 @@ namespace osu.Game.Overlays.Changelog Add(new OsuSpriteText { - Text = build.CreatedAt.Date.ToString("dd MMM yyyy"), - Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 24), - Colour = OsuColour.FromHex(@"FD5"), Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Margin = new MarginPadding { Top = 15 }, + Text = build.CreatedAt.Date.ToString("dd MMM yyyy"), + Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 24), + Colour = OsuColour.FromHex(@"FD5"), }); currentDate = build.CreatedAt.Date; diff --git a/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs b/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs index 4f1d95bc2c..514e75c31a 100644 --- a/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs +++ b/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs @@ -87,6 +87,13 @@ namespace osu.Game.Overlays.Changelog SelectedTab.BindValueChanged(_ => updateState(), true); } + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleClick = audio.Sample.Get(@"UI/generic-select-soft"); + sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); + } + protected override void OnActivated() => updateState(); protected override void OnDeactivated() => updateState(); @@ -146,12 +153,5 @@ namespace osu.Game.Overlays.Changelog externalDimRequested = false; updateState(); } - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - sampleClick = audio.Sample.Get(@"UI/generic-select-soft"); - sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); - } } } diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 552e213a45..7d791b2a88 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -120,10 +120,7 @@ namespace osu.Game.Overlays ?? streams.Find(s => s.Name == updateStream)?.LatestBuild; if (build != null) - { - Current.Value = build; - State = Visibility.Visible; - } + ShowBuild(build); }); State = Visibility.Visible; @@ -193,13 +190,13 @@ namespace osu.Game.Overlays }); } - private CancellationTokenSource loadContentTask; + private CancellationTokenSource loadContentCancellation; private void loadContent(ChangelogContent newContent) { content.FadeTo(0.2f, 300, Easing.OutQuint); - loadContentTask?.Cancel(); + loadContentCancellation?.Cancel(); LoadComponentAsync(newContent, c => { @@ -207,7 +204,7 @@ namespace osu.Game.Overlays c.BuildSelected = ShowBuild; content.Child = c; - }, (loadContentTask = new CancellationTokenSource()).Token); + }, (loadContentCancellation = new CancellationTokenSource()).Token); } } } From 465aa4e0f60db2ed81df136cf0da5c6613c4beef Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 31 May 2019 14:06:13 +0900 Subject: [PATCH 0865/2854] Prevent idle state from being updated incorrectly --- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index a098d42c83..868d37d922 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -176,7 +176,7 @@ namespace osu.Game.Screens.Menu private void updateIdleState(bool isIdle) { - if (isIdle && State != ButtonSystemState.Exit) + if (isIdle && State != ButtonSystemState.Exit && State != ButtonSystemState.EnteringMode) State = ButtonSystemState.Initial; } From 1629534a0c79a3b6fae32820a17acb2f1dd3cc68 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 May 2019 22:07:14 +0900 Subject: [PATCH 0866/2854] More disposal? --- osu.Game/Skinning/SkinnableSound.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 5e8a0ea43f..e88e088f5e 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -58,5 +58,13 @@ namespace osu.Game.Skinning return null; } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + foreach (var c in channels) + c.Dispose(); + } } } From 80d65f9a3ba2e3e4d6486ca92f915f9ad8da24f5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 May 2019 14:33:18 +0900 Subject: [PATCH 0867/2854] Update resource stores with GetAvailableResources --- osu.Game/IO/Archives/ArchiveReader.cs | 2 ++ osu.Game/Skinning/LegacySkin.cs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/osu.Game/IO/Archives/ArchiveReader.cs b/osu.Game/IO/Archives/ArchiveReader.cs index a561523799..4ee7a19ebc 100644 --- a/osu.Game/IO/Archives/ArchiveReader.cs +++ b/osu.Game/IO/Archives/ArchiveReader.cs @@ -15,6 +15,8 @@ namespace osu.Game.IO.Archives /// public abstract Stream GetStream(string name); + public IEnumerable GetAvailableResources() => Filenames; + public abstract void Dispose(); /// diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 8d38f944d0..7b658f86d0 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -140,6 +141,8 @@ namespace osu.Game.Skinning return path == null ? null : underlyingStore.GetStream(path); } + public IEnumerable GetAvailableResources() => source.Files.Select(f => f.Filename); + byte[] IResourceStore.Get(string name) => GetAsync(name).Result; public Task GetAsync(string name) From f33a5bc54cee705bcdb8d8d77f1f1e314b2f322c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 May 2019 14:40:53 +0900 Subject: [PATCH 0868/2854] Centralise and update WorkingBeatmap creation in test cases --- .../TestSceneAutoJuiceStream.cs | 4 +- .../TestSceneBananaShower.cs | 4 +- .../TestSceneCatchStacker.cs | 4 +- .../TestSceneHyperDash.cs | 4 +- .../TestSceneHitCircleLongCombo.cs | 4 +- .../TestSceneSliderInput.cs | 5 +- .../TestSceneTaikoPlayfield.cs | 5 +- .../TestSceneBackgroundScreenBeatmap.cs | 5 +- .../Visual/Editor/TestSceneEditorCompose.cs | 3 +- .../Editor/TestSceneEditorComposeTimeline.cs | 5 +- .../Editor/TestSceneEditorSeekSnapping.cs | 5 +- .../Editor/TestSceneEditorSummaryTimeline.cs | 3 +- .../Editor/TestSceneHitObjectComposer.cs | 3 +- .../Visual/Editor/TestScenePlaybackControl.cs | 5 +- .../Visual/Editor/TestSceneWaveform.cs | 10 +- .../Visual/Gameplay/TestScenePlayerLoader.cs | 5 +- .../TestScenePlayerReferenceLeaking.cs | 5 +- .../TestSceneMatchSettingsOverlay.cs | 5 +- .../Visual/Online/TestSceneDirectPanel.cs | 3 +- .../SongSelect/TestSceneBeatmapDetailArea.cs | 14 +- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 5 +- .../SongSelect/TestScenePlaySongSelect.cs | 5 +- osu.Game.Tests/WaveformTestBeatmap.cs | 13 +- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- .../Beatmaps/BeatmapManager_WorkingBeatmap.cs | 3 +- osu.Game/Beatmaps/BindableBeatmap.cs | 26 +-- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 6 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 7 +- osu.Game/OsuGameBase.cs | 16 +- osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 112 +--------- osu.Game/Tests/Visual/AllPlayersTestScene.cs | 21 +- osu.Game/Tests/Visual/EditorTestScene.cs | 5 +- osu.Game/Tests/Visual/OsuTestScene.cs | 198 ++++++++++++++++-- osu.Game/Tests/Visual/PlayerTestScene.cs | 8 +- 34 files changed, 278 insertions(+), 250 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index 9cec0d280d..ab3c040b4e 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -20,14 +20,14 @@ namespace osu.Game.Rulesets.Catch.Tests { } - protected override IBeatmap CreateBeatmap(Ruleset ruleset) + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { var beatmap = new Beatmap { BeatmapInfo = new BeatmapInfo { BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 }, - Ruleset = ruleset.RulesetInfo + Ruleset = ruleset } }; diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs index 035bbe4b4e..0ad72412fc 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs @@ -29,14 +29,14 @@ namespace osu.Game.Rulesets.Catch.Tests { } - protected override IBeatmap CreateBeatmap(Ruleset ruleset) + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { var beatmap = new Beatmap { BeatmapInfo = new BeatmapInfo { BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 }, - Ruleset = ruleset.RulesetInfo + Ruleset = ruleset } }; diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs index 7d7528372a..9ce46ad6ba 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs @@ -16,14 +16,14 @@ namespace osu.Game.Rulesets.Catch.Tests { } - protected override IBeatmap CreateBeatmap(Ruleset ruleset) + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { var beatmap = new Beatmap { BeatmapInfo = new BeatmapInfo { BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 }, - Ruleset = ruleset.RulesetInfo + Ruleset = ruleset } }; diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs index 7393f75e5a..9cbff8c5d3 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs @@ -23,13 +23,13 @@ namespace osu.Game.Rulesets.Catch.Tests AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash); } - protected override IBeatmap CreateBeatmap(Ruleset ruleset) + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { var beatmap = new Beatmap { BeatmapInfo = { - Ruleset = ruleset.RulesetInfo, + Ruleset = ruleset, BaseDifficulty = new BeatmapDifficulty { CircleSize = 3.6f } } }; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs index 921246751c..399cf22599 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs @@ -17,14 +17,14 @@ namespace osu.Game.Rulesets.Osu.Tests { } - protected override IBeatmap CreateBeatmap(Ruleset ruleset) + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { var beatmap = new Beatmap { BeatmapInfo = new BeatmapInfo { BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 }, - Ruleset = ruleset.RulesetInfo + Ruleset = ruleset } }; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs index 193cfe9c94..2eb783233a 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs @@ -20,7 +20,6 @@ using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play; -using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Visual; using osuTK; @@ -299,7 +298,7 @@ namespace osu.Game.Rulesets.Osu.Tests { AddStep("load player", () => { - Beatmap.Value = new TestWorkingBeatmap(new Beatmap + Beatmap.Value = CreateWorkingBeatmap(new Beatmap { HitObjects = { @@ -323,7 +322,7 @@ namespace osu.Game.Rulesets.Osu.Tests BaseDifficulty = new BeatmapDifficulty { SliderTickRate = 3 }, Ruleset = new OsuRuleset().RulesetInfo }, - }, Clock); + }); var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } }); diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs index 3634ec7d4a..6f9856df83 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs @@ -18,7 +18,6 @@ using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.UI; -using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Visual; using osuTK; using osu.Game.Rulesets.Scoring; @@ -64,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Tests var controlPointInfo = new ControlPointInfo(); controlPointInfo.TimingPoints.Add(new TimingControlPoint()); - WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap + WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap { HitObjects = new List { new CentreHit() }, BeatmapInfo = new BeatmapInfo @@ -79,7 +78,7 @@ namespace osu.Game.Rulesets.Taiko.Tests Ruleset = new TaikoRuleset().RulesetInfo }, ControlPointInfo = controlPointInfo - }, Clock); + }); Add(playfieldContainer = new Container { diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs index c9bdcf928f..7104a420a3 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Events; @@ -54,7 +55,7 @@ namespace osu.Game.Tests.Visual.Background private RulesetStore rulesets; [BackgroundDependencyLoader] - private void load(GameHost host) + private void load(GameHost host, AudioManager audio) { factory = new DatabaseContextFactory(LocalStorage); factory.ResetDatabase(); @@ -68,7 +69,7 @@ namespace osu.Game.Tests.Visual.Background usage.Migrate(); Dependencies.Cache(rulesets = new RulesetStore(factory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, null, host, Beatmap.Default)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, Beatmap.Default)); Dependencies.Cache(new OsuConfigManager(LocalStorage)); manager.Import(TestResources.GetTestBeatmapForImport()); diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorCompose.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorCompose.cs index b537cb0beb..608df1965e 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorCompose.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorCompose.cs @@ -7,7 +7,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit.Compose; -using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual.Editor { @@ -19,7 +18,7 @@ namespace osu.Game.Tests.Visual.Editor [BackgroundDependencyLoader] private void load() { - Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, Clock); + Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); Child = new ComposeScreen(); } } diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs index 154c58dd99..a8c2362910 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -30,9 +31,9 @@ namespace osu.Game.Tests.Visual.Editor }; [BackgroundDependencyLoader] - private void load() + private void load(AudioManager audio) { - Beatmap.Value = new WaveformTestBeatmap(); + Beatmap.Value = new WaveformTestBeatmap(audio); Children = new Drawable[] { diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorSeekSnapping.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorSeekSnapping.cs index 590fa59107..b997d6aaeb 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorSeekSnapping.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorSeekSnapping.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Tests.Beatmaps; using osuTK; using osuTK.Graphics; @@ -48,7 +47,7 @@ namespace osu.Game.Tests.Visual.Editor } }; - Beatmap.Value = new TestWorkingBeatmap(testBeatmap, Clock); + Beatmap.Value = CreateWorkingBeatmap(testBeatmap); Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = Clock }; } diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorSummaryTimeline.cs index f20c921ff2..2e04eb50ca 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorSummaryTimeline.cs @@ -8,7 +8,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit.Components.Timelines.Summary; -using osu.Game.Tests.Beatmaps; using osuTK; namespace osu.Game.Tests.Visual.Editor @@ -21,7 +20,7 @@ namespace osu.Game.Tests.Visual.Editor [BackgroundDependencyLoader] private void load() { - Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, null); + Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); Add(new SummaryTimeline { diff --git a/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs index 47aa059b62..7accbe2fa8 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs @@ -18,7 +18,6 @@ using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Compose.Components; -using osu.Game.Tests.Beatmaps; using osuTK; namespace osu.Game.Tests.Visual.Editor @@ -45,7 +44,7 @@ namespace osu.Game.Tests.Visual.Editor [BackgroundDependencyLoader] private void load() { - Beatmap.Value = new TestWorkingBeatmap(new Beatmap + Beatmap.Value = CreateWorkingBeatmap(new Beatmap { HitObjects = new List { diff --git a/osu.Game.Tests/Visual/Editor/TestScenePlaybackControl.cs b/osu.Game.Tests/Visual/Editor/TestScenePlaybackControl.cs index 126ab98291..0d4fe4366d 100644 --- a/osu.Game.Tests/Visual/Editor/TestScenePlaybackControl.cs +++ b/osu.Game.Tests/Visual/Editor/TestScenePlaybackControl.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; @@ -7,7 +7,6 @@ using osu.Framework.Graphics; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Screens.Edit.Components; -using osu.Game.Tests.Beatmaps; using osuTK; namespace osu.Game.Tests.Visual.Editor @@ -29,7 +28,7 @@ namespace osu.Game.Tests.Visual.Editor Size = new Vector2(200, 100) }; - Beatmap.Value = new TestWorkingBeatmap(new Beatmap(), Clock); + Beatmap.Value = CreateWorkingBeatmap(new Beatmap()); Child = playback; } diff --git a/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs b/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs index e93789b1d3..6e2500d711 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Graphics; using osu.Framework.Graphics.Audio; @@ -19,10 +20,13 @@ namespace osu.Game.Tests.Visual.Editor { private WorkingBeatmap waveformBeatmap; + private OsuGameBase game; + [BackgroundDependencyLoader] - private void load() + private void load(AudioManager audio, OsuGameBase game) { - waveformBeatmap = new WaveformTestBeatmap(); + waveformBeatmap = new WaveformTestBeatmap(audio); + this.game = game; } [TestCase(1f)] @@ -91,7 +95,7 @@ namespace osu.Game.Tests.Visual.Editor Child = graph = new TestWaveformGraph { RelativeSizeAxes = Axes.Both, - Waveform = new DummyWorkingBeatmap().Waveform, + Waveform = new DummyWorkingBeatmap(game).Waveform, }, }; }); diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 5c26f733ab..daee3a520c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -16,7 +16,6 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens; using osu.Game.Screens.Play; -using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual.Gameplay { @@ -29,7 +28,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void Setup() => Schedule(() => { InputManager.Child = stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }; - Beatmap.Value = new TestWorkingBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), Clock); + Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); }); [Test] diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs index c75fb2567b..65b56319e8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs @@ -3,7 +3,6 @@ using System; using osu.Framework.Lists; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Screens.Play; @@ -43,9 +42,9 @@ namespace osu.Game.Tests.Visual.Gameplay }); } - protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock clock) + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) { - var working = base.CreateWorkingBeatmap(beatmap, clock); + var working = base.CreateWorkingBeatmap(beatmap); workingWeakReferences.Add(working); return working; } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs index 21b97fe73b..76a0604818 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs @@ -19,6 +19,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMatchSettingsOverlay : MultiplayerTestScene { + [Resolved] + private OsuGameBase game { get; set; } + public override IReadOnlyList RequiredTypes => new[] { typeof(MatchSettingsOverlay) @@ -57,7 +60,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("set name", () => Room.Name.Value = "Room name"); AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value); - AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap().BeatmapInfo })); + AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap(game).BeatmapInfo })); AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value); AddStep("clear name", () => Room.Name.Value = ""); diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs index a3d932a383..8b67892fbb 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Overlays.Direct; using osu.Game.Rulesets.Osu; -using osu.Game.Tests.Beatmaps; using osuTK; namespace osu.Game.Tests.Visual.Online @@ -25,7 +24,7 @@ namespace osu.Game.Tests.Visual.Online [BackgroundDependencyLoader] private void load() { - var beatmap = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, null); + var beatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); beatmap.BeatmapSetInfo.OnlineInfo.HasVideo = true; beatmap.BeatmapSetInfo.OnlineInfo.HasStoryboard = true; diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs index cf4362ba28..d398423b9a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Screens.Select; @@ -18,7 +19,8 @@ namespace osu.Game.Tests.Visual.SongSelect { public override IReadOnlyList RequiredTypes => new[] { typeof(BeatmapDetails) }; - public TestSceneBeatmapDetailArea() + [BackgroundDependencyLoader] + private void load(OsuGameBase game) { BeatmapDetailArea detailsArea; Add(detailsArea = new BeatmapDetailArea @@ -28,7 +30,7 @@ namespace osu.Game.Tests.Visual.SongSelect Size = new Vector2(550f, 450f), }); - AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap + AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(game) { BeatmapInfo = { @@ -56,7 +58,7 @@ namespace osu.Game.Tests.Visual.SongSelect } ); - AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap + AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap(game) { BeatmapInfo = { @@ -82,7 +84,7 @@ namespace osu.Game.Tests.Visual.SongSelect } }); - AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap + AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap(game) { BeatmapInfo = { @@ -107,7 +109,7 @@ namespace osu.Game.Tests.Visual.SongSelect } }); - AddStep("fails+retries", () => detailsArea.Beatmap = new DummyWorkingBeatmap + AddStep("fails+retries", () => detailsArea.Beatmap = new DummyWorkingBeatmap(game) { BeatmapInfo = { @@ -133,7 +135,7 @@ namespace osu.Game.Tests.Visual.SongSelect } }); - AddStep("null metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap + AddStep("null metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(game) { BeatmapInfo = { diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index b1ed5c46c2..9969795ecf 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; @@ -18,7 +18,6 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Taiko; using osu.Game.Screens.Select; -using osu.Game.Tests.Beatmaps; using osuTK; namespace osu.Game.Tests.Visual.SongSelect @@ -136,7 +135,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () => { infoBefore = infoWedge.Info; - infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : new TestWorkingBeatmap(b); + infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : CreateWorkingBeatmap(b); }); AddUntilStep("wait for async load", () => infoWedge.Info != infoBefore); diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 7e962dbc06..ebee358730 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Text; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.MathUtils; @@ -79,7 +80,7 @@ namespace osu.Game.Tests.Visual.SongSelect } [BackgroundDependencyLoader] - private void load(GameHost host) + private void load(GameHost host, AudioManager audio) { factory = new DatabaseContextFactory(LocalStorage); factory.ResetDatabase(); @@ -93,7 +94,7 @@ namespace osu.Game.Tests.Visual.SongSelect usage.Migrate(); Dependencies.Cache(rulesets = new RulesetStore(factory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, null, host, defaultBeatmap = Beatmap.Default)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default)); Beatmap.SetDefault(); } diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs index f66b374cd7..36cc1e5ad2 100644 --- a/osu.Game.Tests/WaveformTestBeatmap.cs +++ b/osu.Game.Tests/WaveformTestBeatmap.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; +using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; @@ -19,12 +20,14 @@ namespace osu.Game.Tests { private readonly ZipArchiveReader reader; private readonly Stream stream; + private readonly ITrackStore trackStore; - public WaveformTestBeatmap() - : base(new BeatmapInfo()) + public WaveformTestBeatmap(AudioManager audioManager) + : base(new BeatmapInfo(), audioManager) { stream = TestResources.GetTestBeatmapStream(); reader = new ZipArchiveReader(stream); + trackStore = audioManager.GetTrackStore(reader); } public override void Dispose() @@ -32,17 +35,17 @@ namespace osu.Game.Tests base.Dispose(); stream?.Dispose(); reader?.Dispose(); + trackStore?.Dispose(); } protected override IBeatmap GetBeatmap() => createTestBeatmap(); protected override Texture GetBackground() => null; - protected override Waveform GetWaveform() => new Waveform(getAudioStream()); + protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(reader.Filenames.First(f => f.EndsWith(".mp3")))); - protected override Track GetTrack() => new TrackBass(getAudioStream()); + protected override Track GetTrack() => trackStore.Get(reader.Filenames.First(f => f.EndsWith(".mp3"))); - private Stream getAudioStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".mp3"))); private Stream getBeatmapStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".osu"))); private Beatmap createTestBeatmap() diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 798bca3ada..0200dd44ac 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -429,7 +429,7 @@ namespace osu.Game.Beatmaps private readonly IBeatmap beatmap; public DummyConversionBeatmap(IBeatmap beatmap) - : base(beatmap.BeatmapInfo) + : base(beatmap.BeatmapInfo, null) { this.beatmap = beatmap; } diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index cfeb6b0a92..e1cc5db3ad 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -22,11 +22,10 @@ namespace osu.Game.Beatmaps private readonly IResourceStore store; public BeatmapManagerWorkingBeatmap(IResourceStore store, TextureStore textureStore, BeatmapInfo beatmapInfo, AudioManager audioManager) - : base(beatmapInfo) + : base(beatmapInfo, audioManager) { this.store = store; this.textureStore = textureStore; - AudioManager = audioManager; } protected override IBeatmap GetBeatmap() diff --git a/osu.Game/Beatmaps/BindableBeatmap.cs b/osu.Game/Beatmaps/BindableBeatmap.cs index 6614a6f2fb..1e69a17ef7 100644 --- a/osu.Game/Beatmaps/BindableBeatmap.cs +++ b/osu.Game/Beatmaps/BindableBeatmap.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Diagnostics; -using JetBrains.Annotations; -using osu.Framework.Audio; using osu.Framework.Bindables; namespace osu.Game.Beatmaps @@ -14,29 +12,20 @@ namespace osu.Game.Beatmaps /// public abstract class BindableBeatmap : NonNullableBindable { - protected AudioManager AudioManager; - private WorkingBeatmap lastBeatmap; - protected BindableBeatmap(WorkingBeatmap defaultValue, AudioManager audioManager) + protected BindableBeatmap(WorkingBeatmap defaultValue) : base(defaultValue) { - // we don't want to attempt to update tracks if we are a bound copy. - if (audioManager != null) - { - AudioManager = audioManager; - ValueChanged += b => updateAudioTrack(b.NewValue); - - // If the track has changed prior to this being called, let's register it - if (Value != Default) - updateAudioTrack(Value); - } + BindValueChanged(b => updateAudioTrack(b.NewValue), true); } private void updateAudioTrack(WorkingBeatmap beatmap) { var trackLoaded = lastBeatmap?.TrackLoaded ?? false; + //beatmap.AudioManager = AudioManager; + // compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo) if (!trackLoaded || lastBeatmap?.Track != beatmap.Track) { @@ -51,12 +40,5 @@ namespace osu.Game.Beatmaps lastBeatmap = beatmap; } - - /// - /// Retrieve a new instance weakly bound to this . - /// If you are further binding to events of the retrieved , ensure a local reference is held. - /// - [NotNull] - public new abstract BindableBeatmap GetBoundCopy(); } } diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index b35e98085a..72b477713a 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -18,7 +18,7 @@ namespace osu.Game.Beatmaps { private readonly OsuGameBase game; - public DummyWorkingBeatmap(OsuGameBase game = null) + public DummyWorkingBeatmap(OsuGameBase game) : base(new BeatmapInfo { Metadata = new BeatmapMetadata @@ -34,7 +34,7 @@ namespace osu.Game.Beatmaps OverallDifficulty = 0, }, Ruleset = new DummyRulesetInfo() - }) + }, game.Audio) { this.game = game; } @@ -43,7 +43,7 @@ namespace osu.Game.Beatmaps protected override Texture GetBackground() => game?.Textures.Get(@"Backgrounds/bg4"); - protected override Track GetTrack() => game?.Audio.Tracks.GetVirtual(1000); + protected override Track GetTrack() => game?.Audio?.Tracks.GetVirtual(1000); private class DummyRulesetInfo : RulesetInfo { diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 6c8f283923..cf1acaf46b 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -29,8 +29,11 @@ namespace osu.Game.Beatmaps public readonly BeatmapMetadata Metadata; - protected WorkingBeatmap(BeatmapInfo beatmapInfo) + protected AudioManager AudioManager { get; } + + protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager) { + AudioManager = audioManager; BeatmapInfo = beatmapInfo; BeatmapSetInfo = beatmapInfo.BeatmapSet; Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); @@ -179,8 +182,6 @@ namespace osu.Game.Beatmaps public bool SkinLoaded => skin.IsResultAvailable; public Skin Skin => skin.Value; - public AudioManager AudioManager { get; set; } - protected virtual Skin GetSkin() => new DefaultSkin(); private readonly RecyclableLazy skin; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 8e663de8c5..39f5144cf8 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -195,7 +195,7 @@ namespace osu.Game // this adds a global reduction of track volume for the time being. Audio.Tracks.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8)); - beatmap = new OsuBindableBeatmap(defaultBeatmap, Audio); + beatmap = new OsuBindableBeatmap(defaultBeatmap); dependencies.CacheAs>(beatmap); dependencies.CacheAs(beatmap); @@ -282,21 +282,9 @@ namespace osu.Game private class OsuBindableBeatmap : BindableBeatmap { public OsuBindableBeatmap(WorkingBeatmap defaultValue) - : base(defaultValue, null) + : base(defaultValue) { } - - public OsuBindableBeatmap(WorkingBeatmap defaultValue, AudioManager audioManager) - : base(defaultValue, audioManager) - { - } - - public override BindableBeatmap GetBoundCopy() - { - var copy = new OsuBindableBeatmap(Default, AudioManager); - copy.BindTo(this); - return copy; - } } private class OsuUserInputManager : UserInputManager diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index c558275f62..0ef35879e3 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -1,134 +1,30 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; -using osu.Framework.Timing; using osu.Game.Beatmaps; -using osu.Game.Rulesets; -using osuTK; namespace osu.Game.Tests.Beatmaps { public class TestWorkingBeatmap : WorkingBeatmap { - private readonly TrackVirtualManual track; private readonly IBeatmap beatmap; - /// - /// Create an instance which creates a for the provided ruleset when requested. - /// - /// The target ruleset. - /// A clock which should be used instead of a stopwatch for virtual time progression. - public TestWorkingBeatmap(RulesetInfo ruleset, IFrameBasedClock referenceClock) - : this(new TestBeatmap(ruleset), referenceClock) - { - } - /// /// Create an instance which provides the when requested. /// /// The beatmap - /// An optional clock which should be used instead of a stopwatch for virtual time progression. - public TestWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock referenceClock = null) - : base(beatmap.BeatmapInfo) + public TestWorkingBeatmap(IBeatmap beatmap) + : base(beatmap.BeatmapInfo, null) { this.beatmap = beatmap; - - if (referenceClock != null) - track = new TrackVirtualManual(referenceClock); } protected override IBeatmap GetBeatmap() => beatmap; + protected override Texture GetBackground() => null; - protected override Track GetTrack() => track; - /// - /// A virtual track which tracks a reference clock. - /// - public class TrackVirtualManual : Track - { - private readonly IFrameBasedClock referenceClock; - - private readonly ManualClock clock = new ManualClock(); - - private bool running; - - /// - /// Local offset added to the reference clock to resolve correct time. - /// - private double offset; - - public TrackVirtualManual(IFrameBasedClock referenceClock) - { - this.referenceClock = referenceClock; - Length = double.PositiveInfinity; - } - - public override bool Seek(double seek) - { - offset = MathHelper.Clamp(seek, 0, Length); - lastReferenceTime = null; - - return offset == seek; - } - - public override void Start() - { - running = true; - } - - public override void Reset() - { - Seek(0); - base.Reset(); - } - - public override void Stop() - { - if (running) - { - running = false; - // on stopping, the current value should be transferred out of the clock, as we can no longer rely on - // the referenceClock (which will still be counting time). - offset = clock.CurrentTime; - lastReferenceTime = null; - } - } - - public override bool IsRunning => running; - - private double? lastReferenceTime; - - public override double CurrentTime => clock.CurrentTime; - - protected override void UpdateState() - { - base.UpdateState(); - - if (running) - { - double refTime = referenceClock.CurrentTime; - - if (!lastReferenceTime.HasValue) - { - // if the clock just started running, the current value should be transferred to the offset - // (to zero the progression of time). - offset -= refTime; - } - - lastReferenceTime = refTime; - } - - clock.CurrentTime = Math.Min((lastReferenceTime ?? 0) + offset, Length); - - if (CurrentTime >= Length) - { - Stop(); - RaiseCompleted(); - } - } - } + protected override Track GetTrack() => null; } } diff --git a/osu.Game/Tests/Visual/AllPlayersTestScene.cs b/osu.Game/Tests/Visual/AllPlayersTestScene.cs index 454fbe1222..b7d1979b0d 100644 --- a/osu.Game/Tests/Visual/AllPlayersTestScene.cs +++ b/osu.Game/Tests/Visual/AllPlayersTestScene.cs @@ -4,13 +4,10 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Screens; -using osu.Framework.Timing; -using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play; -using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { @@ -50,26 +47,20 @@ namespace osu.Game.Tests.Visual protected abstract void AddCheckSteps(); - protected virtual IBeatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo); - - protected virtual WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock clock) => - new TestWorkingBeatmap(beatmap, Clock); - - private Player loadPlayerFor(RulesetInfo ri) + private Player loadPlayerFor(RulesetInfo rulesetInfo) { - Ruleset.Value = ri; - var r = ri.CreateInstance(); + Ruleset.Value = rulesetInfo; + var ruleset = rulesetInfo.CreateInstance(); - var beatmap = CreateBeatmap(r); - var working = CreateWorkingBeatmap(beatmap, Clock); + var working = CreateWorkingBeatmap(rulesetInfo); Beatmap.Value = working; - Mods.Value = new[] { r.GetAllMods().First(m => m is ModNoFail) }; + Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) }; Player?.Exit(); Player = null; - Player = CreatePlayer(r); + Player = CreatePlayer(ruleset); LoadScreen(Player); diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index 14c0f0950f..75bbb3e110 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -6,7 +6,6 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Game.Rulesets; using osu.Game.Screens.Edit; -using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { @@ -24,7 +23,7 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load() { - Beatmap.Value = new TestWorkingBeatmap(ruleset.RulesetInfo, null); + Beatmap.Value = CreateWorkingBeatmap(ruleset.RulesetInfo); LoadScreen(new Editor()); } diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 806b73b517..d8a63d23e1 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -3,15 +3,21 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Platform; using osu.Framework.Testing; +using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Tests.Beatmaps; +using osuTK; namespace osu.Game.Tests.Visual { @@ -19,7 +25,7 @@ namespace osu.Game.Tests.Visual { [Cached(typeof(Bindable))] [Cached(typeof(IBindable))] - private readonly OsuTestBeatmap beatmap = new OsuTestBeatmap(new DummyWorkingBeatmap(), null); + private OsuTestBeatmap beatmap; protected BindableBeatmap Beatmap => beatmap; @@ -39,7 +45,10 @@ namespace osu.Game.Tests.Visual protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { // This is the earliest we can get OsuGameBase, which is used by the dummy working beatmap to find textures - beatmap.Default = new DummyWorkingBeatmap(parent.Get()); + beatmap = new OsuTestBeatmap(new DummyWorkingBeatmap(parent.Get())) + { + Default = new DummyWorkingBeatmap(parent.Get()) + }; return Dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); } @@ -49,8 +58,19 @@ namespace osu.Game.Tests.Visual localStorage = new Lazy(() => new NativeStorage($"{GetType().Name}-{Guid.NewGuid()}")); } + [Resolved] + private AudioManager audio { get; set; } + + protected virtual IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset); + + protected WorkingBeatmap CreateWorkingBeatmap(RulesetInfo ruleset = null) => + CreateWorkingBeatmap(CreateBeatmap(ruleset)); + + protected virtual WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) => + new ClockBackedTestWorkingBeatmap(beatmap, Clock, audio); + [BackgroundDependencyLoader] - private void load(AudioManager audioManager, RulesetStore rulesets) + private void load(RulesetStore rulesets) { Ruleset.Value = rulesets.AvailableRulesets.First(); } @@ -59,7 +79,8 @@ namespace osu.Game.Tests.Visual { base.Dispose(isDisposing); - beatmap?.Value.Track.Stop(); + if (beatmap?.Value.TrackLoaded == true) + beatmap.Value.Track.Stop(); if (localStorage.IsValueCreated) { @@ -76,6 +97,164 @@ namespace osu.Game.Tests.Visual protected override ITestSceneTestRunner CreateRunner() => new OsuTestSceneTestRunner(); + public class ClockBackedTestWorkingBeatmap : TestWorkingBeatmap + { + private readonly Track track; + + private readonly TrackVirtualStore store; + + /// + /// Create an instance which creates a for the provided ruleset when requested. + /// + /// The target ruleset. + /// A clock which should be used instead of a stopwatch for virtual time progression. + /// Audio manager. Required if a reference clock isn't provided. + public ClockBackedTestWorkingBeatmap(RulesetInfo ruleset, IFrameBasedClock referenceClock, AudioManager audio) + : this(new TestBeatmap(ruleset), referenceClock, audio) + { + } + + /// + /// Create an instance which provides the when requested. + /// + /// The beatmap + /// An optional clock which should be used instead of a stopwatch for virtual time progression. + /// Audio manager. Required if a reference clock isn't provided. + /// The length of the returned virtual track. + public ClockBackedTestWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock referenceClock, AudioManager audio, double length = 60000) + : base(beatmap) + { + if (referenceClock != null) + { + store = new TrackVirtualStore(referenceClock); + audio.AddItem(store); + track = store.GetVirtual(length); + } + else + track = audio?.Tracks.GetVirtual(length); + } + + public override void Dispose() + { + base.Dispose(); + store?.Dispose(); + } + + protected override Track GetTrack() => track; + + public class TrackVirtualStore : AudioCollectionManager, ITrackStore + { + private readonly IFrameBasedClock referenceClock; + + public TrackVirtualStore(IFrameBasedClock referenceClock) + { + this.referenceClock = referenceClock; + } + + public Track Get(string name) => throw new NotImplementedException(); + + public Task GetAsync(string name) => throw new NotImplementedException(); + + public Stream GetStream(string name) => throw new NotImplementedException(); + + public IEnumerable GetAvailableResources() => throw new NotImplementedException(); + + public Track GetVirtual(double length = Double.PositiveInfinity) + { + var track = new TrackVirtualManual(referenceClock) { Length = length }; + AddItem(track); + return track; + } + } + + /// + /// A virtual track which tracks a reference clock. + /// + public class TrackVirtualManual : Track + { + private readonly IFrameBasedClock referenceClock; + + private readonly ManualClock clock = new ManualClock(); + + private bool running; + + /// + /// Local offset added to the reference clock to resolve correct time. + /// + private double offset; + + public TrackVirtualManual(IFrameBasedClock referenceClock) + { + this.referenceClock = referenceClock; + Length = double.PositiveInfinity; + } + + public override bool Seek(double seek) + { + offset = MathHelper.Clamp(seek, 0, Length); + lastReferenceTime = null; + + return offset == seek; + } + + public override void Start() + { + running = true; + } + + public override void Reset() + { + Seek(0); + base.Reset(); + } + + public override void Stop() + { + if (running) + { + running = false; + // on stopping, the current value should be transferred out of the clock, as we can no longer rely on + // the referenceClock (which will still be counting time). + offset = clock.CurrentTime; + lastReferenceTime = null; + } + } + + public override bool IsRunning => running; + + private double? lastReferenceTime; + + public override double CurrentTime => clock.CurrentTime; + + protected override void UpdateState() + { + base.UpdateState(); + + if (running) + { + double refTime = referenceClock.CurrentTime; + + if (!lastReferenceTime.HasValue) + { + // if the clock just started running, the current value should be transferred to the offset + // (to zero the progression of time). + offset -= refTime; + } + + lastReferenceTime = refTime; + } + + clock.CurrentTime = Math.Min((lastReferenceTime ?? 0) + offset, Length); + + if (CurrentTime >= Length) + { + Stop(); + RaiseCompleted(); + } + } + } + } + public class OsuTestSceneTestRunner : OsuGameBase, ITestSceneTestRunner { private TestSceneTestRunner.TestRunner runner; @@ -93,17 +272,10 @@ namespace osu.Game.Tests.Visual private class OsuTestBeatmap : BindableBeatmap { - public OsuTestBeatmap(WorkingBeatmap defaultValue, AudioManager audioManager) - : base(defaultValue, audioManager) + public OsuTestBeatmap(WorkingBeatmap defaultValue) + : base(defaultValue) { } - - public override BindableBeatmap GetBoundCopy() - { - var copy = new OsuTestBeatmap(Default, AudioManager); - copy.BindTo(this); - return copy; - } } } } diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index 0c39194088..03e17a819c 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -4,12 +4,10 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Testing; -using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play; -using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { @@ -39,15 +37,13 @@ namespace osu.Game.Tests.Visual AddUntilStep("player loaded", () => Player.IsLoaded && Player.Alpha == 1); } - protected virtual IBeatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo); - protected virtual bool AllowFail => false; private void loadPlayer() { - var beatmap = CreateBeatmap(ruleset); + var beatmap = CreateBeatmap(ruleset.RulesetInfo); - Beatmap.Value = new TestWorkingBeatmap(beatmap, Clock); + Beatmap.Value = CreateWorkingBeatmap(beatmap); if (!AllowFail) Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) }; From b52276c4892dd0585ad81ac014ab1fe4f969eb5b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 May 2019 14:51:12 +0900 Subject: [PATCH 0869/2854] Pass individual components to DummyWorkingBeatmap, not game --- osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs | 3 ++- .../Multiplayer/TestSceneMatchSettingsOverlay.cs | 3 +-- .../Visual/SongSelect/TestSceneBeatmapDetailArea.cs | 10 +++++----- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 13 +++++++------ osu.Game/OsuGameBase.cs | 2 +- osu.Game/Tests/Visual/OsuTestScene.cs | 9 ++++++--- 6 files changed, 22 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs b/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs index 6e2500d711..7b27998d7f 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Osu; using osuTK.Graphics; namespace osu.Game.Tests.Visual.Editor @@ -95,7 +96,7 @@ namespace osu.Game.Tests.Visual.Editor Child = graph = new TestWaveformGraph { RelativeSizeAxes = Axes.Both, - Waveform = new DummyWorkingBeatmap(game).Waveform, + Waveform = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).Waveform, }, }; }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs index 76a0604818..de4f4d9d25 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs @@ -8,7 +8,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; @@ -60,7 +59,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("set name", () => Room.Name.Value = "Room name"); AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value); - AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap(game).BeatmapInfo })); + AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = CreateBeatmap(Ruleset.Value).BeatmapInfo })); AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value); AddStep("clear name", () => Room.Name.Value = ""); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs index d398423b9a..8395ece457 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.SongSelect Size = new Vector2(550f, 450f), }); - AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(game) + AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) { BeatmapInfo = { @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.SongSelect } ); - AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap(game) + AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) { BeatmapInfo = { @@ -84,7 +84,7 @@ namespace osu.Game.Tests.Visual.SongSelect } }); - AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap(game) + AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) { BeatmapInfo = { @@ -109,7 +109,7 @@ namespace osu.Game.Tests.Visual.SongSelect } }); - AddStep("fails+retries", () => detailsArea.Beatmap = new DummyWorkingBeatmap(game) + AddStep("fails+retries", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) { BeatmapInfo = { @@ -135,7 +135,7 @@ namespace osu.Game.Tests.Visual.SongSelect } }); - AddStep("null metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(game) + AddStep("null metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) { BeatmapInfo = { diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 72b477713a..9202c617bf 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics.Textures; @@ -16,9 +17,9 @@ namespace osu.Game.Beatmaps { public class DummyWorkingBeatmap : WorkingBeatmap { - private readonly OsuGameBase game; + private readonly TextureStore textures; - public DummyWorkingBeatmap(OsuGameBase game) + public DummyWorkingBeatmap(AudioManager audio, TextureStore textures) : base(new BeatmapInfo { Metadata = new BeatmapMetadata @@ -34,16 +35,16 @@ namespace osu.Game.Beatmaps OverallDifficulty = 0, }, Ruleset = new DummyRulesetInfo() - }, game.Audio) + }, audio) { - this.game = game; + this.textures = textures; } protected override IBeatmap GetBeatmap() => new Beatmap(); - protected override Texture GetBackground() => game?.Textures.Get(@"Backgrounds/bg4"); + protected override Texture GetBackground() => textures?.Get(@"Backgrounds/bg4"); - protected override Track GetTrack() => game?.Audio?.Tracks.GetVirtual(1000); + protected override Track GetTrack() => GetVirtualTrack(Beatmap); private class DummyRulesetInfo : RulesetInfo { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 39f5144cf8..f9128687d6 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -161,7 +161,7 @@ namespace osu.Game dependencies.CacheAs(API); - var defaultBeatmap = new DummyWorkingBeatmap(this); + var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index d8a63d23e1..c8798448ae 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -10,6 +10,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; +using osu.Framework.Graphics.Textures; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Timing; @@ -45,9 +46,11 @@ namespace osu.Game.Tests.Visual protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { // This is the earliest we can get OsuGameBase, which is used by the dummy working beatmap to find textures - beatmap = new OsuTestBeatmap(new DummyWorkingBeatmap(parent.Get())) + var working = new DummyWorkingBeatmap(parent.Get(), parent.Get()); + + beatmap = new OsuTestBeatmap(working) { - Default = new DummyWorkingBeatmap(parent.Get()) + Default = working }; return Dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); @@ -63,7 +66,7 @@ namespace osu.Game.Tests.Visual protected virtual IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset); - protected WorkingBeatmap CreateWorkingBeatmap(RulesetInfo ruleset = null) => + protected WorkingBeatmap CreateWorkingBeatmap(RulesetInfo ruleset) => CreateWorkingBeatmap(CreateBeatmap(ruleset)); protected virtual WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) => From 7bed4eb23b73ec49bba20136d5aa6c257cb474f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 May 2019 14:57:11 +0900 Subject: [PATCH 0870/2854] Tidy up WaveformTestBeatmap --- osu.Game.Tests/WaveformTestBeatmap.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs index 36cc1e5ad2..fdb91b7c5b 100644 --- a/osu.Game.Tests/WaveformTestBeatmap.cs +++ b/osu.Game.Tests/WaveformTestBeatmap.cs @@ -42,9 +42,11 @@ namespace osu.Game.Tests protected override Texture GetBackground() => null; - protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(reader.Filenames.First(f => f.EndsWith(".mp3")))); + protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(firstAudioFile)); - protected override Track GetTrack() => trackStore.Get(reader.Filenames.First(f => f.EndsWith(".mp3"))); + protected override Track GetTrack() => trackStore.Get(firstAudioFile); + + private string firstAudioFile => reader.Filenames.First(f => f.EndsWith(".mp3")); private Stream getBeatmapStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".osu"))); From 55c0c6a1bbffebe94d7a620689e42d3525d3f84a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 31 May 2019 17:43:58 +0200 Subject: [PATCH 0871/2854] Show changelog for current build by clicking on settings footer in settings overlay. --- osu.Game/Overlays/Settings/SettingsFooter.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index e8c2c1ffe8..33ad5b101b 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -17,8 +18,11 @@ namespace osu.Game.Overlays.Settings { public class SettingsFooter : FillFlowContainer { + private OsuGameBase game; + private ChangelogOverlay changelog; + [BackgroundDependencyLoader] - private void load(OsuGameBase game, OsuColour colours, RulesetStore rulesets) + private void load(OsuGameBase game, OsuColour colours, RulesetStore rulesets, ChangelogOverlay changelog) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -67,6 +71,17 @@ namespace osu.Game.Overlays.Settings Colour = DebugUtils.IsDebug ? colours.Red : Color4.White, }, }; + + this.game = game; + this.changelog = changelog; + } + + protected override bool OnClick(ClickEvent e) + { + if (!game.IsDeployedBuild) return base.OnClick(e); + + changelog?.ShowBuild("lazer", game.Version); + return base.OnClick(e); } } } From 0625f51e65447596f6f1a0a77faf130968958b84 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 31 May 2019 22:42:09 +0200 Subject: [PATCH 0872/2854] Allow dependencies to be null in certain cases (Unit tests) --- osu.Game/Overlays/Settings/SettingsFooter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index 33ad5b101b..317ba2f92d 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Settings private OsuGameBase game; private ChangelogOverlay changelog; - [BackgroundDependencyLoader] + [BackgroundDependencyLoader(true)] private void load(OsuGameBase game, OsuColour colours, RulesetStore rulesets, ChangelogOverlay changelog) { RelativeSizeAxes = Axes.X; From 58564579e4f679e64d470eeda0a85dfe6e70b670 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 1 Jun 2019 08:46:38 +0200 Subject: [PATCH 0873/2854] Invert if statement --- osu.Game/Overlays/Settings/SettingsFooter.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index 317ba2f92d..3e0eb6ffde 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -78,10 +78,11 @@ namespace osu.Game.Overlays.Settings protected override bool OnClick(ClickEvent e) { - if (!game.IsDeployedBuild) return base.OnClick(e); - - changelog?.ShowBuild("lazer", game.Version); - return base.OnClick(e); + if (game.IsDeployedBuild) + { + changelog?.ShowBuild("lazer", game.Version); + } + return true; } } } From 0a867e37aff8782cdc97e1e57f41703bfa429312 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 2 Jun 2019 12:40:18 +0200 Subject: [PATCH 0874/2854] Resolve dependencies via Resolved Attribute --- osu.Game/Overlays/Settings/SettingsFooter.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index 3e0eb6ffde..306512802b 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -18,11 +18,14 @@ namespace osu.Game.Overlays.Settings { public class SettingsFooter : FillFlowContainer { - private OsuGameBase game; - private ChangelogOverlay changelog; + [Resolved] + private OsuGameBase game { get; set; } + + [Resolved(CanBeNull = true)] + private ChangelogOverlay changelog { get; set; } [BackgroundDependencyLoader(true)] - private void load(OsuGameBase game, OsuColour colours, RulesetStore rulesets, ChangelogOverlay changelog) + private void load(OsuGameBase game, OsuColour colours, RulesetStore rulesets) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -71,9 +74,6 @@ namespace osu.Game.Overlays.Settings Colour = DebugUtils.IsDebug ? colours.Red : Color4.White, }, }; - - this.game = game; - this.changelog = changelog; } protected override bool OnClick(ClickEvent e) @@ -82,6 +82,7 @@ namespace osu.Game.Overlays.Settings { changelog?.ShowBuild("lazer", game.Version); } + return true; } } From d8f45f7299dd1531f7b5b485e9feaa6b7a7ba9ba Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 2 Jun 2019 15:17:03 +0200 Subject: [PATCH 0875/2854] Disallow null references for dependencies loaded via load() --- osu.Game/Overlays/Settings/SettingsFooter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index 306512802b..8403f70f49 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Settings [Resolved(CanBeNull = true)] private ChangelogOverlay changelog { get; set; } - [BackgroundDependencyLoader(true)] + [BackgroundDependencyLoader] private void load(OsuGameBase game, OsuColour colours, RulesetStore rulesets) { RelativeSizeAxes = Axes.X; From 115a75e4c6604a214316e7e97752547b3610faff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jun 2019 13:16:05 +0900 Subject: [PATCH 0876/2854] Use a constant for lazer variables --- osu.Desktop/Overlays/VersionManager.cs | 2 +- osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs | 2 +- osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs | 2 +- osu.Game/OsuGameBase.cs | 2 ++ osu.Game/Overlays/Settings/SettingsFooter.cs | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index d2aad99f41..5a8cf32f14 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -120,7 +120,7 @@ namespace osu.Desktop.Overlays Activated = delegate { - changelog.ShowBuild("lazer", version); + changelog.ShowBuild(OsuGameBase.CLIENT_STREAM_NAME, version); return true; }; } diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index d1a7730bee..0655611230 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Online { Version = "2018.712.0", DisplayVersion = "2018.712.0", - UpdateStream = new APIUpdateStream { Name = "lazer" }, + UpdateStream = new APIUpdateStream { Name = OsuGameBase.CLIENT_STREAM_NAME }, ChangelogEntries = new List { new APIChangelogEntry diff --git a/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs b/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs index ef204c7687..d9e48373bb 100644 --- a/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs +++ b/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs @@ -45,7 +45,7 @@ namespace osu.Game.Online.API.Requests.Responses case "cuttingedge": return new Color4(238, 170, 0, 255); - case "lazer": + case OsuGameBase.CLIENT_STREAM_NAME: return new Color4(237, 18, 33, 255); case "web": diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 7b9aed8364..00672e82bd 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -44,6 +44,8 @@ namespace osu.Game /// public class OsuGameBase : Framework.Game, ICanAcceptFiles { + public const string CLIENT_STREAM_NAME = "lazer"; + protected OsuConfigManager LocalConfig; protected BeatmapManager BeatmapManager; diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index 8403f70f49..298991712b 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Settings { if (game.IsDeployedBuild) { - changelog?.ShowBuild("lazer", game.Version); + changelog?.ShowBuild(OsuGameBase.CLIENT_STREAM_NAME, game.Version); } return true; From b249fb35446d1176a5bcdec7487fe1d949551e2e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jun 2019 13:37:29 +0900 Subject: [PATCH 0877/2854] Update test scene to support dynamic compilation --- ...stSceneSettings.cs => TestSceneSettingsPanel.cs} | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) rename osu.Game.Tests/Visual/Settings/{TestSceneSettings.cs => TestSceneSettingsPanel.cs} (71%) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs similarity index 71% rename from osu.Game.Tests/Visual/Settings/TestSceneSettings.cs rename to osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index 964754f8d0..27e3cc1590 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -1,20 +1,29 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Game.Overlays; +using osu.Game.Overlays.Settings; namespace osu.Game.Tests.Visual.Settings { [TestFixture] - public class TestSceneSettings : OsuTestScene + public class TestSceneSettingsPanel : OsuTestScene { private readonly SettingsPanel settings; private readonly DialogOverlay dialogOverlay; - public TestSceneSettings() + public override IReadOnlyList RequiredTypes => new[] + { + typeof(SettingsFooter), + typeof(SettingsOverlay), + }; + + public TestSceneSettingsPanel() { settings = new SettingsOverlay { From 4e5788959ee6e72953e1a17a01ff20adc90e0840 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jun 2019 13:38:06 +0900 Subject: [PATCH 0878/2854] Make clickable text actually a button --- osu.Game/Overlays/Settings/SettingsFooter.cs | 47 ++++++++++++++------ 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index 298991712b..7a7f7bdf73 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -5,10 +5,10 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -18,9 +18,6 @@ namespace osu.Game.Overlays.Settings { public class SettingsFooter : FillFlowContainer { - [Resolved] - private OsuGameBase game { get; set; } - [Resolved(CanBeNull = true)] private ChangelogOverlay changelog { get; set; } @@ -65,25 +62,49 @@ namespace osu.Game.Overlays.Settings Text = game.Name, Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold), }, - new OsuSpriteText + new BuildDisplay(game.Version, DebugUtils.IsDebug) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Font = OsuFont.GetFont(size: 14), - Text = game.Version, - Colour = DebugUtils.IsDebug ? colours.Red : Color4.White, - }, + } }; } - protected override bool OnClick(ClickEvent e) + private class BuildDisplay : OsuAnimatedButton { - if (game.IsDeployedBuild) + private readonly string version; + private readonly bool isDebug; + + [Resolved] + private OsuColour colours { get; set; } + + public BuildDisplay(string version, bool isDebug) { - changelog?.ShowBuild(OsuGameBase.CLIENT_STREAM_NAME, game.Version); + this.version = version; + this.isDebug = isDebug; + + Content.RelativeSizeAxes = Axes.Y; + Content.AutoSizeAxes = AutoSizeAxes = Axes.X; + Height = 20; } - return true; + [BackgroundDependencyLoader(true)] + private void load(ChangelogOverlay changelog) + { + if (!isDebug) + Action = () => changelog?.ShowBuild(OsuGameBase.CLIENT_STREAM_NAME, version); + + Add(new OsuSpriteText + { + Font = OsuFont.GetFont(size: 16), + + Text = version, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Padding = new MarginPadding(5), + Colour = isDebug ? colours.Red : Color4.White, + }); + } } } } From 3ef17a54f671b181bdd2bd599f3be9d818abac58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jun 2019 13:53:24 +0900 Subject: [PATCH 0879/2854] Fix sizing of OsuAnimatedButton and OsuClickableContainer Was incorrect under some combinations of relative and autosize usage. --- .../TestSceneOsuAnimatedButton.cs | 73 +++++++++++++++++++ .../Containers/OsuClickableContainer.cs | 2 +- .../UserInterface/OsuAnimatedButton.cs | 6 ++ 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneOsuAnimatedButton.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuAnimatedButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuAnimatedButton.cs new file mode 100644 index 0000000000..4bee3907f5 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuAnimatedButton.cs @@ -0,0 +1,73 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osuTK; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneOsuAnimatedButton : GridTestScene + { + public TestSceneOsuAnimatedButton() + : base(3, 2) + { + Cell(0).Add(new BaseContainer("relative sized") + { + RelativeSizeAxes = Axes.Both, + }); + + Cell(1).Add(new BaseContainer("auto sized") + { + AutoSizeAxes = Axes.Both + }); + + Cell(2).Add(new BaseContainer("relative Y auto X") + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X + }); + + Cell(3).Add(new BaseContainer("relative X auto Y") + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }); + + Cell(4).Add(new BaseContainer("fixed") + { + Size = new Vector2(100), + }); + + Cell(5).Add(new BaseContainer("fixed") + { + Size = new Vector2(100, 50), + }); + + AddToggleStep("toggle enabled", toggle => + { + for (int i = 0; i < 6; i++) + ((BaseContainer)Cell(i).Child).Action = toggle ? () => { } : (Action)null; + }); + } + + public class BaseContainer : OsuAnimatedButton + { + public BaseContainer(string text) + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Add(new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = text + }); + } + } + } +} diff --git a/osu.Game/Graphics/Containers/OsuClickableContainer.cs b/osu.Game/Graphics/Containers/OsuClickableContainer.cs index 6dbe340efb..1f31e4cdda 100644 --- a/osu.Game/Graphics/Containers/OsuClickableContainer.cs +++ b/osu.Game/Graphics/Containers/OsuClickableContainer.cs @@ -31,7 +31,7 @@ namespace osu.Game.Graphics.Containers { if (AutoSizeAxes != Axes.None) { - content.RelativeSizeAxes = RelativeSizeAxes; + content.RelativeSizeAxes = (Axes.Both & ~AutoSizeAxes); content.AutoSizeAxes = AutoSizeAxes; } diff --git a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs index a8041c79fc..236b72766f 100644 --- a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs @@ -74,6 +74,12 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { + if (AutoSizeAxes != Axes.None) + { + content.RelativeSizeAxes = (Axes.Both & ~AutoSizeAxes); + content.AutoSizeAxes = AutoSizeAxes; + } + Enabled.BindValueChanged(enabled => this.FadeColour(enabled.NewValue ? Color4.White : colours.Gray9, 200, Easing.OutQuint), true); } From 65e3b7c2ae35840c53f0bbd06c388997af9a2054 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jun 2019 13:58:55 +0900 Subject: [PATCH 0880/2854] Remove unused DI --- osu.Game/Overlays/Settings/SettingsFooter.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index 7a7f7bdf73..b5ee4b4f0c 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -18,9 +18,6 @@ namespace osu.Game.Overlays.Settings { public class SettingsFooter : FillFlowContainer { - [Resolved(CanBeNull = true)] - private ChangelogOverlay changelog { get; set; } - [BackgroundDependencyLoader] private void load(OsuGameBase game, OsuColour colours, RulesetStore rulesets) { From 171fc14776f193958722b24e573edefc3b493a3a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jun 2019 16:33:45 +0900 Subject: [PATCH 0881/2854] Fix editor regressions --- osu.Game/Graphics/UserInterface/IconButton.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Graphics/UserInterface/IconButton.cs b/osu.Game/Graphics/UserInterface/IconButton.cs index 6414e488e8..052e9194fa 100644 --- a/osu.Game/Graphics/UserInterface/IconButton.cs +++ b/osu.Game/Graphics/UserInterface/IconButton.cs @@ -66,6 +66,7 @@ namespace osu.Game.Graphics.UserInterface set { Content.RelativeSizeAxes = Axes.None; + Content.AutoSizeAxes = Axes.None; Content.Size = value; } } From 491c9e96e078004025613c05faa97a49faa89f37 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jun 2019 16:41:27 +0900 Subject: [PATCH 0882/2854] Fix tests not ending execution after some exceptions --- osu.Game/Tests/Visual/OsuTestScene.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 9b775fd498..d03b490bd4 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -61,7 +61,8 @@ namespace osu.Game.Tests.Visual { base.Dispose(isDisposing); - beatmap?.Value.Track.Stop(); + if (beatmap?.Value.TrackLoaded == true) + beatmap.Value.Track.Stop(); if (localStorage.IsValueCreated) { From 17d04545fad1d6977fdd8a67761d46e4fb5bb059 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jun 2019 16:45:18 +0900 Subject: [PATCH 0883/2854] Localise GridTestScene as an OsuGridTestScene --- .../Visual/UserInterface/TestSceneLoadingAnimation.cs | 3 +-- .../Visual/UserInterface/TestSceneOsuAnimatedButton.cs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingAnimation.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingAnimation.cs index b9a6d74f19..b0233d35f9 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingAnimation.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingAnimation.cs @@ -3,13 +3,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; -using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface { - public class TestSceneLoadingAnimation : GridTestScene //todo: this should be an OsuTestScene + public class TestSceneLoadingAnimation : OsuGridTestScene { public TestSceneLoadingAnimation() : base(2, 2) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuAnimatedButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuAnimatedButton.cs index 4bee3907f5..6a41d08f01 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuAnimatedButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuAnimatedButton.cs @@ -3,14 +3,13 @@ using System; using osu.Framework.Graphics; -using osu.Framework.Testing; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osuTK; namespace osu.Game.Tests.Visual.UserInterface { - public class TestSceneOsuAnimatedButton : GridTestScene + public class TestSceneOsuAnimatedButton : OsuGridTestScene { public TestSceneOsuAnimatedButton() : base(3, 2) From e32f62db5bdf473579ee062a67d7c765fb95e2ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jun 2019 16:48:44 +0900 Subject: [PATCH 0884/2854] Add missing file --- .../Visual/UserInterface/OsuGridTestScene.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/OsuGridTestScene.cs diff --git a/osu.Game.Tests/Visual/UserInterface/OsuGridTestScene.cs b/osu.Game.Tests/Visual/UserInterface/OsuGridTestScene.cs new file mode 100644 index 0000000000..096ac951de --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/OsuGridTestScene.cs @@ -0,0 +1,50 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Tests.Visual.UserInterface +{ + /// + /// An abstract test case which exposes small cells arranged in a grid. + /// Useful for displaying multiple configurations of a tested component at a glance. + /// + public abstract class OsuGridTestScene : OsuTestScene + { + private readonly Drawable[,] cells; + + /// + /// The amount of rows in the grid. + /// + protected readonly int Rows; + + /// + /// The amount of columns in the grid. + /// + protected readonly int Cols; + + /// + /// Constructs a grid test case with the given dimensions. + /// + protected OsuGridTestScene(int rows, int cols) + { + Rows = rows; + Cols = cols; + + GridContainer testContainer; + Add(testContainer = new GridContainer { RelativeSizeAxes = Axes.Both }); + + cells = new Drawable[rows, cols]; + for (int r = 0; r < rows; r++) + for (int c = 0; c < cols; c++) + cells[r, c] = new Container { RelativeSizeAxes = Axes.Both }; + + testContainer.Content = cells.ToJagged(); + } + + protected Container Cell(int index) => (Container)cells[index / Cols, index % Cols]; + protected Container Cell(int row, int col) => (Container)cells[row, col]; + } +} From 1eab4e179ddb5583ded246af115edafdc41b89c1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jun 2019 17:05:35 +0900 Subject: [PATCH 0885/2854] Add sample action to test so hover effect is visible --- osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs index 0d6d378f4c..936842bdfa 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs @@ -23,6 +23,7 @@ namespace osu.Game.Tests.Visual.Online { Anchor = Anchor.Centre, Origin = Anchor.Centre, + Action = () => { } }); AddStep("switch loading state", () => button.IsLoading = !button.IsLoading); From c4f4f32db8a24f97922000610172e672d5e03e48 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jun 2019 17:06:07 +0900 Subject: [PATCH 0886/2854] Shorten fade duration --- osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs index 31c73aaa96..6b7c37b42d 100644 --- a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs +++ b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs @@ -18,6 +18,8 @@ namespace osu.Game.Overlays.Profile.Sections { public class ShowMoreButton : OsuHoverContainer { + private const float fade_duration = 200; + private readonly Box background; private readonly LoadingAnimation loading; private readonly FillFlowContainer content; @@ -38,13 +40,13 @@ namespace osu.Game.Overlays.Profile.Sections if (value) { - loading.FadeIn(FADE_DURATION, Easing.OutQuint); - content.FadeOut(FADE_DURATION, Easing.OutQuint); + loading.FadeIn(fade_duration, Easing.OutQuint); + content.FadeOut(fade_duration, Easing.OutQuint); } else { - loading.FadeOut(FADE_DURATION, Easing.OutQuint); - content.FadeIn(FADE_DURATION, Easing.OutQuint); + loading.FadeOut(fade_duration, Easing.OutQuint); + content.FadeIn(fade_duration, Easing.OutQuint); } } } From 633c3b74ec5b746de3565a2dfe234641cfae1bea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jun 2019 17:10:18 +0900 Subject: [PATCH 0887/2854] Don't handle clicks when in a loading state --- osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs index 6b7c37b42d..328a1fa6b7 100644 --- a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs +++ b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -38,6 +38,8 @@ namespace osu.Game.Overlays.Profile.Sections isLoading = value; + Enabled.Value = !isLoading; + if (value) { loading.FadeIn(fade_duration, Easing.OutQuint); From 4e6d7137aa370d89c7a8381ffb1e41c357a77c10 Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Mon, 3 Jun 2019 17:25:19 +0800 Subject: [PATCH 0888/2854] disallow current user from opening their own private channel --- osu.Game/Online/Chat/ChannelManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 2efc9f4968..3af11ff20f 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -81,6 +81,9 @@ namespace osu.Game.Online.Chat if (user == null) throw new ArgumentNullException(nameof(user)); + if (user.Id == api.LocalUser.Value.Id) + return; + CurrentChannel.Value = JoinedChannels.FirstOrDefault(c => c.Type == ChannelType.PM && c.Users.Count == 1 && c.Users.Any(u => u.Id == user.Id)) ?? new Channel(user); } From 516575a132fead92342b62aa02da527ebbccabe2 Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Mon, 3 Jun 2019 18:54:29 +0800 Subject: [PATCH 0889/2854] don't create "Start Chat" option when the sender is the local user --- osu.Game/Overlays/Chat/ChatLine.cs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 66a6672ab1..86bbe91d35 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -14,6 +15,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; using osu.Game.Online.Chat; using osu.Game.Users; using osuTK; @@ -201,6 +203,9 @@ namespace osu.Game.Overlays.Chat private Action startChatAction; + [Resolved] + private IAPIProvider api { get; set; } + public MessageSender(User sender) { this.sender = sender; @@ -213,11 +218,21 @@ namespace osu.Game.Overlays.Chat startChatAction = () => chatManager?.OpenPrivateChannel(sender); } - public MenuItem[] ContextMenuItems => new MenuItem[] + public MenuItem[] ContextMenuItems { - new OsuMenuItem("View Profile", MenuItemType.Highlighted, Action), - new OsuMenuItem("Start Chat", MenuItemType.Standard, startChatAction), - }; + get + { + List items = new List + { + new OsuMenuItem("View Profile", MenuItemType.Highlighted, Action) + }; + + if (sender.Id != api.LocalUser.Value.Id) + items.Add(new OsuMenuItem("Start Chat", MenuItemType.Standard, startChatAction)); + + return items.ToArray(); + } + } } private static readonly Color4[] username_colours = From 194bb80354d7f8d49ed64ba26ac493e53c092151 Mon Sep 17 00:00:00 2001 From: Welsar55 Date: Mon, 3 Jun 2019 11:09:21 -0500 Subject: [PATCH 0890/2854] Added close button and indictors of hotkeys to buttons --- osu.Game/Overlays/Mods/ModSection.cs | 6 +----- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 25 +++++++++++++++++++++- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index f584eff0f9..50400e254f 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -57,11 +57,7 @@ namespace osu.Game.Overlays.Mods protected override bool OnKeyDown(KeyDownEvent e) { - if(e.Key == Key.Number1) - { - DeselectAll(); - } - else if (ToggleKeys != null) + if (ToggleKeys != null) { var index = Array.IndexOf(ToggleKeys, e.Key); if (index > -1 && index < buttons.Length) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 97769fe5aa..c304dc2eb3 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osuTK; +using osuTK.Input; using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; @@ -23,6 +24,7 @@ using osu.Game.Rulesets; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods.Sections; using osu.Game.Screens; +using osu.Framework.Input.Events; namespace osu.Game.Overlays.Mods { @@ -33,6 +35,7 @@ namespace osu.Game.Overlays.Mods protected Color4 LowMultiplierColour, HighMultiplierColour; protected readonly TriangleButton DeselectAllButton; + protected readonly TriangleButton CloseButton; protected readonly OsuSpriteText MultiplierLabel, UnrankedLabel; private readonly FillFlowContainer footerContainer; @@ -192,6 +195,16 @@ namespace osu.Game.Overlays.Mods refreshSelectedMods(); } + protected override bool OnKeyDown(KeyDownEvent e) + { + if (e.Key == Key.Number1) + DeselectAll(); + else if (e.Key == Key.Number2) + PopOut(); + + return base.OnKeyDown(e); + } + private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray(); public ModSelectOverlay() @@ -357,13 +370,23 @@ namespace osu.Game.Overlays.Mods DeselectAllButton = new TriangleButton { Width = 180, - Text = "Deselect All", + Text = "1. Deselect All", Action = DeselectAll, Margin = new MarginPadding { Right = 20 } }, + CloseButton = new TriangleButton + { + Width = 180, + Text = "2. Close", + Action = PopOut, + Margin = new MarginPadding + { + Right = 20 + } + }, new OsuSpriteText { Text = @"Score Multiplier:", From fe6b4112c6e5244779ada7ed0eea1ca805522c50 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 4 Jun 2019 01:47:45 +0300 Subject: [PATCH 0891/2854] Adjust colors to match web design --- osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs index 328a1fa6b7..485595798d 100644 --- a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs +++ b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs @@ -102,8 +102,8 @@ namespace osu.Game.Overlays.Profile.Sections [BackgroundDependencyLoader] private void load(OsuColour colors) { - IdleColour = colors.GreySeafoam; - HoverColour = colors.GreySeafoamLight; + IdleColour = colors.GreySeafoamDark; + HoverColour = colors.GreySeafoam; } protected override bool OnClick(ClickEvent e) From 2c713712820d9f9a3152e1fc67143eb1fb730fca Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 4 Jun 2019 02:06:15 +0300 Subject: [PATCH 0892/2854] Fix endless loading state --- osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs index 485595798d..82554faac8 100644 --- a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs +++ b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs @@ -38,8 +38,6 @@ namespace osu.Game.Overlays.Profile.Sections isLoading = value; - Enabled.Value = !isLoading; - if (value) { loading.FadeIn(fade_duration, Easing.OutQuint); @@ -108,6 +106,9 @@ namespace osu.Game.Overlays.Profile.Sections protected override bool OnClick(ClickEvent e) { + if (IsLoading) + return true; + IsLoading = true; return base.OnClick(e); } From d5a2ebf79f3f4b6e7633b7faf92b4a4ce35aa9c9 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 4 Jun 2019 04:04:33 +0300 Subject: [PATCH 0893/2854] Fix endless loading state part 2 --- .../Overlays/Profile/Sections/ShowMoreButton.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs index 82554faac8..a1dcfc036e 100644 --- a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs +++ b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs @@ -38,6 +38,8 @@ namespace osu.Game.Overlays.Profile.Sections isLoading = value; + Enabled.Value = !isLoading; + if (value) { loading.FadeIn(fade_duration, Easing.OutQuint); @@ -106,11 +108,14 @@ namespace osu.Game.Overlays.Profile.Sections protected override bool OnClick(ClickEvent e) { - if (IsLoading) - return true; + var clickResult = base.OnClick(e); - IsLoading = true; - return base.OnClick(e); + if (IsLoading) + return clickResult; + + IsLoading |= clickResult; + + return clickResult; } private class ChevronIcon : SpriteIcon From e8315085c0021a1fcdd1e2fff515673915c11ddc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Jun 2019 10:26:21 +0900 Subject: [PATCH 0894/2854] Better handle OnClick --- .../Profile/Sections/ShowMoreButton.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs index a1dcfc036e..5ed546c62b 100644 --- a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs +++ b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs @@ -108,14 +108,18 @@ namespace osu.Game.Overlays.Profile.Sections protected override bool OnClick(ClickEvent e) { - var clickResult = base.OnClick(e); + if (!Enabled.Value) + return false; - if (IsLoading) - return clickResult; - - IsLoading |= clickResult; - - return clickResult; + try + { + return base.OnClick(e); + } + finally + { + // run afterwards as this will disable this button. + IsLoading = true; + } } private class ChevronIcon : SpriteIcon From a5a025de6867edbfb70cf13df58353da4ea044cc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Jun 2019 10:26:35 +0900 Subject: [PATCH 0895/2854] Add proper tests --- .../Visual/Online/TestSceneShowMoreButton.cs | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs index 936842bdfa..bccb263600 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs @@ -17,16 +17,39 @@ namespace osu.Game.Tests.Visual.Online public TestSceneShowMoreButton() { - ShowMoreButton button; + ShowMoreButton button = null; + + int fireCount = 0; Add(button = new ShowMoreButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Action = () => { } + Action = () => + { + fireCount++; + // ReSharper disable once AccessToModifiedClosure + // ReSharper disable once PossibleNullReferenceException + Scheduler.AddDelayed(() => button.IsLoading = false, 2000); + } }); - AddStep("switch loading state", () => button.IsLoading = !button.IsLoading); + AddStep("click button", () => button.Click()); + + AddAssert("action fired once", () => fireCount == 1); + AddAssert("is in loading state", () => button.IsLoading); + + AddStep("click button", () => button.Click()); + + AddAssert("action not fired", () => fireCount == 1); + AddAssert("is in loading state", () => button.IsLoading); + + AddUntilStep("wait for loaded", () => !button.IsLoading); + + AddStep("click button", () => button.Click()); + + AddAssert("action fired twice", () => fireCount == 2); + AddAssert("is in loading state", () => button.IsLoading); } } } From cea353975fd46ff4eeb52dd9970eb2fa6fed19f3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 4 Jun 2019 11:04:28 +0900 Subject: [PATCH 0896/2854] Update with further framework-side changes --- .../Visual/Editor/TestSceneWaveform.cs | 5 +---- ...tSceneUpdateableBeatmapBackgroundSprite.cs | 2 +- .../Beatmaps/BeatmapManager_WorkingBeatmap.cs | 2 +- .../UpdateableBeatmapBackgroundSprite.cs | 21 +++---------------- .../Overlays/Changelog/UpdateStreamBadge.cs | 4 ++-- osu.Game/Overlays/ChangelogOverlay.cs | 2 +- .../Screens/Multi/Match/Components/Header.cs | 2 +- 7 files changed, 10 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs b/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs index 7b27998d7f..e2762f3d5f 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs @@ -21,13 +21,10 @@ namespace osu.Game.Tests.Visual.Editor { private WorkingBeatmap waveformBeatmap; - private OsuGameBase game; - [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuGameBase game) + private void load(AudioManager audio) { waveformBeatmap = new WaveformTestBeatmap(audio); - this.game = game; } [TestCase(1f)] diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs index 23065629a6..d39358a972 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.UserInterface TestUpdateableBeatmapBackgroundSprite background = null; AddStep("load null beatmap", () => Child = background = new TestUpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both }); - AddUntilStep("wait for load", () => background.ContentLoaded); + AddAssert("no content", () => !background.ContentLoaded); } [Test] diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index e1cc5db3ad..4b1bddbf0d 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -45,7 +45,7 @@ namespace osu.Game.Beatmaps private TextureStore textureStore; - private IAdjustableResourceStore trackStore; + private ITrackStore trackStore; protected override bool BackgroundStillValid(Texture b) => false; // bypass lazy logic. we want to return a new background each time for refcounting purposes. diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs index 96786f5f49..1fd3502799 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -31,24 +32,8 @@ namespace osu.Game.Beatmaps.Drawables /// protected virtual double UnloadDelay => 10000; - private BeatmapInfo lastModel; - private bool firstLoad = true; - - protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Drawable content, double timeBeforeLoad) - { - return new DelayedLoadUnloadWrapper(() => - { - // If DelayedLoadUnloadWrapper is attempting to RELOAD the same content (Beatmap), that means that it was - // previously UNLOADED and thus its children have been disposed of, so we need to recreate them here. - if (!firstLoad && lastModel == Beatmap.Value) - return CreateDrawable(Beatmap.Value); - - // If the model has changed since the previous unload (or if there was no load), then we can safely use the given content - lastModel = Beatmap.Value; - firstLoad = false; - return content; - }, timeBeforeLoad, UnloadDelay); - } + protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func createContentFunc, double timeBeforeLoad) + => new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad, UnloadDelay); protected override Drawable CreateDrawable(BeatmapInfo model) { diff --git a/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs b/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs index 514e75c31a..52b77604d9 100644 --- a/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs +++ b/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs @@ -90,8 +90,8 @@ namespace osu.Game.Overlays.Changelog [BackgroundDependencyLoader] private void load(AudioManager audio) { - sampleClick = audio.Sample.Get(@"UI/generic-select-soft"); - sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); + sampleClick = audio.Samples.Get(@"UI/generic-select-soft"); + sampleHover = audio.Samples.Get(@"UI/generic-hover-soft"); } protected override void OnActivated() => updateState(); diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 7d791b2a88..4a6d53b480 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -76,7 +76,7 @@ namespace osu.Game.Overlays }, }; - sampleBack = audio.Sample.Get(@"UI/generic-select-soft"); + sampleBack = audio.Samples.Get(@"UI/generic-select-soft"); header.Current.BindTo(Current); diff --git a/osu.Game/Screens/Multi/Match/Components/Header.cs b/osu.Game/Screens/Multi/Match/Components/Header.cs index 2a6074882d..73994fa369 100644 --- a/osu.Game/Screens/Multi/Match/Components/Header.cs +++ b/osu.Game/Screens/Multi/Match/Components/Header.cs @@ -137,7 +137,7 @@ namespace osu.Game.Screens.Multi.Match.Components private class BackgroundSprite : UpdateableBeatmapBackgroundSprite { - protected override double FadeDuration => 200; + protected override double TransformDuration => 200; } } } From 474191fcec71e49c6fb1ca95c3b5972c76dbbb2c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 4 Jun 2019 11:13:21 +0900 Subject: [PATCH 0897/2854] Update framework --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b77c724d1b..f84bb64fbf 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + From 4763a41c7ed32f6920a8ac0edf357edc292017fd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 4 Jun 2019 11:25:18 +0900 Subject: [PATCH 0898/2854] Cleanups --- .../Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs | 3 --- osu.Game/Beatmaps/BindableBeatmap.cs | 2 -- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 6 +++--- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs index de4f4d9d25..8091e93471 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs @@ -18,9 +18,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMatchSettingsOverlay : MultiplayerTestScene { - [Resolved] - private OsuGameBase game { get; set; } - public override IReadOnlyList RequiredTypes => new[] { typeof(MatchSettingsOverlay) diff --git a/osu.Game/Beatmaps/BindableBeatmap.cs b/osu.Game/Beatmaps/BindableBeatmap.cs index 1e69a17ef7..af627cc6a9 100644 --- a/osu.Game/Beatmaps/BindableBeatmap.cs +++ b/osu.Game/Beatmaps/BindableBeatmap.cs @@ -24,8 +24,6 @@ namespace osu.Game.Beatmaps { var trackLoaded = lastBeatmap?.TrackLoaded ?? false; - //beatmap.AudioManager = AudioManager; - // compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo) if (!trackLoaded || lastBeatmap?.Track != beatmap.Track) { diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 9202c617bf..3a4c677bd1 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -44,7 +44,7 @@ namespace osu.Game.Beatmaps protected override Texture GetBackground() => textures?.Get(@"Backgrounds/bg4"); - protected override Track GetTrack() => GetVirtualTrack(Beatmap); + protected override Track GetTrack() => GetVirtualTrack(); private class DummyRulesetInfo : RulesetInfo { diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index cf1acaf46b..328763fc9f 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -51,18 +51,18 @@ namespace osu.Game.Beatmaps return b; }); - track = new RecyclableLazy(() => GetTrack() ?? GetVirtualTrack(Beatmap)); + track = new RecyclableLazy(() => GetTrack() ?? GetVirtualTrack()); background = new RecyclableLazy(GetBackground, BackgroundStillValid); waveform = new RecyclableLazy(GetWaveform); storyboard = new RecyclableLazy(GetStoryboard); skin = new RecyclableLazy(GetSkin); } - protected virtual Track GetVirtualTrack(IBeatmap beatmap) + protected virtual Track GetVirtualTrack() { const double excess_length = 1000; - var lastObject = beatmap.HitObjects.LastOrDefault(); + var lastObject = Beatmap.HitObjects.LastOrDefault(); double length; From b8fc53512453ed87730c399e23ed0b5134ec7e5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Jun 2019 12:08:23 +0900 Subject: [PATCH 0899/2854] Fix blueprint tests crashing due to out-of-order operations --- osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs index c1561ffea1..2b177e264f 100644 --- a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs +++ b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs @@ -15,19 +15,18 @@ namespace osu.Game.Tests.Visual [Cached(Type = typeof(IPlacementHandler))] public abstract class PlacementBlueprintTestScene : OsuTestScene, IPlacementHandler { - protected readonly Container HitObjectContainer; + protected Container HitObjectContainer; private PlacementBlueprint currentBlueprint; protected PlacementBlueprintTestScene() { - Beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize = 2; - Add(HitObjectContainer = CreateHitObjectContainer()); } [BackgroundDependencyLoader] private void load() { + Beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize = 2; Add(currentBlueprint = CreateBlueprint()); } From 9c214c3f0ec9e8a09f46b2a276a043d55dd38c67 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Jun 2019 16:13:16 +0900 Subject: [PATCH 0900/2854] Add animation on failing --- .../Visual/Gameplay/TestSceneFailAnimation.cs | 50 ++++++++ .../Visual/Gameplay/TestScenePause.cs | 2 +- osu.Game/Rulesets/UI/DrawableRuleset.cs | 7 +- osu.Game/Screens/Play/FailAnimation.cs | 113 ++++++++++++++++++ osu.Game/Screens/Play/Player.cs | 26 +++- 5 files changed, 191 insertions(+), 7 deletions(-) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs create mode 100644 osu.Game/Screens/Play/FailAnimation.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs new file mode 100644 index 0000000000..4878587dcd --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs @@ -0,0 +1,50 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneFailAnimation : AllPlayersTestScene + { + protected override Player CreatePlayer(Ruleset ruleset) + { + Mods.Value = Array.Empty(); + return new FailPlayer(); + } + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(AllPlayersTestScene), + typeof(TestPlayer), + typeof(Player), + }; + + protected override void AddCheckSteps() + { + AddUntilStep("wait for fail", () => Player.HasFailed); + AddUntilStep("wait for fail overlay", () => ((FailPlayer)Player).FailOverlay.State == Visibility.Visible); + } + + private class FailPlayer : TestPlayer + { + public new FailOverlay FailOverlay => base.FailOverlay; + + public FailPlayer() + : base(false, false) + { + } + + protected override void LoadComplete() + { + base.LoadComplete(); + ScoreProcessor.FailConditions += _ => true; + } + } + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index b6f8638f4a..12e91df77c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -113,7 +113,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestPauseAfterFail() { AddUntilStep("wait for fail", () => Player.HasFailed); - AddAssert("fail overlay shown", () => Player.FailOverlayVisible); + AddUntilStep("fail overlay shown", () => Player.FailOverlayVisible); confirmClockRunning(false); diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 7db24d36a5..52fba9cab3 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.UI /// /// The playfield. /// - public Playfield Playfield => playfield.Value; + public override Playfield Playfield => playfield.Value; /// /// Place to put drawables above hit objects but below UI. @@ -342,6 +342,11 @@ namespace osu.Game.Rulesets.UI /// public readonly BindableBool IsPaused = new BindableBool(); + /// + /// The playfield. + /// + public abstract Playfield Playfield { get; } + /// /// The frame-stable clock which is being used for playfield display. /// diff --git a/osu.Game/Screens/Play/FailAnimation.cs b/osu.Game/Screens/Play/FailAnimation.cs new file mode 100644 index 0000000000..a3caffb620 --- /dev/null +++ b/osu.Game/Screens/Play/FailAnimation.cs @@ -0,0 +1,113 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Audio; +using osu.Framework.Bindables; +using osu.Game.Rulesets.UI; +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Audio.Sample; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects.Drawables; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Play +{ + /// + /// Manage the animation to be applied when a player fails. + /// Single file; automatically disposed after use. + /// + public class FailAnimation : Component + { + public Action OnComplete; + + private readonly DrawableRuleset drawableRuleset; + + private readonly BindableDouble trackFreq = new BindableDouble(1); + + private Track track; + + private const float duration = 2500; + + private SampleChannel failSample; + + public FailAnimation(DrawableRuleset drawableRuleset) + { + this.drawableRuleset = drawableRuleset; + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio, IBindable beatmap) + { + track = beatmap.Value.Track; + failSample = audio.Samples.Get(@"Gameplay/failsound"); + } + + private bool started; + + /// + /// Start the fail animation playing. + /// + /// Thrown if started more than once. + public void Start() + { + if (started) throw new InvalidOperationException("Animation cannot be started more than once."); + + started = true; + + failSample.Play(); + + this.TransformBindableTo(trackFreq, 0, duration).OnComplete(_ => + { + OnComplete?.Invoke(); + Expire(); + }); + + track.AddAdjustment(AdjustableProperty.Frequency, trackFreq); + + applyToPlayfield(drawableRuleset.Playfield); + drawableRuleset.Playfield.HitObjectContainer.FlashColour(Color4.Red, 500); + drawableRuleset.Playfield.HitObjectContainer.FadeOut(duration / 2); + } + + protected override void Update() + { + base.Update(); + + if (!started) + return; + + applyToPlayfield(drawableRuleset.Playfield); + } + + private readonly List appliedObjects = new List(); + + private void applyToPlayfield(Playfield playfield) + { + foreach (var nested in playfield.NestedPlayfields) + applyToPlayfield(nested); + + foreach (DrawableHitObject obj in playfield.HitObjectContainer.AliveObjects) + { + if (appliedObjects.Contains(obj)) + continue; + + obj.RotateTo(RNG.NextSingle(-90, 90), duration); + obj.ScaleTo(obj.Scale * 0.5f, duration); + obj.MoveToOffset(new Vector2(0, 400), duration); + appliedObjects.Add(obj); + } + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + track?.RemoveAdjustment(AdjustableProperty.Frequency, trackFreq); + } + } +} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index cf743ee4f7..d8389fa6d9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -173,7 +173,8 @@ namespace osu.Game.Screens.Play fadeOut(true); Restart(); }, - } + }, + failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, } }; DrawableRuleset.HasReplayLoaded.BindValueChanged(e => HUDOverlay.HoldToQuit.PauseOnFocusLost = !e.NewValue && PauseOnFocusLost, true); @@ -345,13 +346,13 @@ namespace osu.Game.Screens.Play protected FailOverlay FailOverlay { get; private set; } + private FailAnimation failAnimation; + private bool onFail() { if (Mods.Value.OfType().Any(m => !m.AllowFail)) return false; - GameplayClockContainer.Stop(); - HasFailed = true; // There is a chance that we could be in a paused state as the ruleset's internal clock (see FrameStabilityContainer) @@ -360,9 +361,17 @@ namespace osu.Game.Screens.Play if (PauseOverlay.State == Visibility.Visible) PauseOverlay.Hide(); + failAnimation.Start(); + return true; + } + + // Called back when the transform finishes + private void onFailComplete() + { + GameplayClockContainer.Stop(); + FailOverlay.Retries = RestartCount; FailOverlay.Show(); - return true; } #endregion @@ -489,6 +498,13 @@ namespace osu.Game.Screens.Play // still want to block if we are within the cooldown period and not already paused. return true; + if (HasFailed && ValidForResume && !FailOverlay.IsPresent) + // ValidForResume is false when restarting + { + failAnimation.FinishTransforms(true); + return true; + } + GameplayClockContainer.ResetLocalAdjustments(); fadeOut(); From 7d2a75b3502702aa035a7200fbd30b5885534199 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Jun 2019 18:37:26 +0900 Subject: [PATCH 0901/2854] Dim music volume when holding to confirm --- osu.Game/Overlays/HoldToConfirmOverlay.cs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/HoldToConfirmOverlay.cs b/osu.Game/Overlays/HoldToConfirmOverlay.cs index fb38ddcbd1..fdc6f096bc 100644 --- a/osu.Game/Overlays/HoldToConfirmOverlay.cs +++ b/osu.Game/Overlays/HoldToConfirmOverlay.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers; @@ -17,6 +19,11 @@ namespace osu.Game.Overlays { private Box overlay; + private readonly BindableDouble audioVolume = new BindableDouble(1); + + [Resolved] + private AudioManager audio { get; set; } + [BackgroundDependencyLoader] private void load() { @@ -33,7 +40,19 @@ namespace osu.Game.Overlays } }; - Progress.ValueChanged += p => overlay.Alpha = (float)p.NewValue; + Progress.ValueChanged += p => + { + audioVolume.Value = 1 - p.NewValue; + overlay.Alpha = (float)p.NewValue; + }; + + audio.Tracks.AddAdjustment(AdjustableProperty.Volume, audioVolume); + } + + protected override void Dispose(bool isDisposing) + { + audio.Tracks.RemoveAdjustment(AdjustableProperty.Volume, audioVolume); + base.Dispose(isDisposing); } } } From ff647940ca09fd7b0107edb198d44c1d481087e7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 4 Jun 2019 19:25:34 +0900 Subject: [PATCH 0902/2854] Fix incorrect assertion --- .../UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs index d39358a972..f59458ef8d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.UserInterface TestUpdateableBeatmapBackgroundSprite background = null; AddStep("load null beatmap", () => Child = background = new TestUpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both }); - AddAssert("no content", () => !background.ContentLoaded); + AddUntilStep("content loaded", () => background.ContentLoaded); } [Test] From 2e3d392a9f66470b6fe7fb3194c92f7b196d8243 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Jun 2019 22:12:55 +0900 Subject: [PATCH 0903/2854] Mark OsuButton as abstract Not being used directly, so we probably shouldn't support it for now. --- osu.Game/Graphics/UserInterface/OsuButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index 7a27f825f6..494d4e4262 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -17,11 +17,11 @@ namespace osu.Game.Graphics.UserInterface /// /// A button with added default sound effects. /// - public class OsuButton : Button + public abstract class OsuButton : Button { private Box hover; - public OsuButton() + protected OsuButton() { Height = 40; From 0abb48882cdeb636d20aa9030582642458dbb545 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 4 Jun 2019 16:22:54 +0300 Subject: [PATCH 0904/2854] Implement GamemodeControl --- .../Header/Components/GamemodeControl.cs | 142 ++++++++++++++++++ osu.Game/Overlays/Profile/ProfileHeader.cs | 12 +- 2 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs diff --git a/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs b/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs new file mode 100644 index 0000000000..5909082fc8 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs @@ -0,0 +1,142 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Profile.Header.Components +{ + public class GamemodeControl : TabControl + { + protected override Dropdown CreateDropdown() => null; + + protected override TabItem CreateTabItem(string value) => new GamemodeTabItem(value) + { + AccentColour = AccentColour + }; + + private Color4 accentColour = Color4.White; + + public Color4 AccentColour + { + get => accentColour; + set + { + if (accentColour == value) + return; + + accentColour = value; + + foreach (TabItem tabItem in TabContainer) + { + ((GamemodeTabItem)tabItem).AccentColour = value; + } + } + } + + public GamemodeControl() + { + TabContainer.Masking = false; + TabContainer.Spacing = new Vector2(15, 0); + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + foreach (var r in rulesets.AvailableRulesets) + AddItem(r.Name); + } + + protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer + { + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + }; + + private class GamemodeTabItem : TabItem + { + private readonly OsuSpriteText text; + + private Color4 accentColour; + + public Color4 AccentColour + { + get => accentColour; + set + { + if (accentColour == value) + return; + + accentColour = value; + + updateState(); + } + } + + public GamemodeTabItem(string value) + : base(value) + { + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + text = new OsuSpriteText + { + Margin = new MarginPadding { Bottom = 10 }, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Text = value, + Font = OsuFont.GetFont() + }, + new HoverClickSounds() + }; + } + + protected override bool OnHover(HoverEvent e) + { + base.OnHover(e); + + updateState(); + + return true; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + + updateState(); + } + + protected override void OnActivated() => updateState(); + + protected override void OnDeactivated() => updateState(); + + private void updateState() + { + if (Active.Value || IsHovered) + { + text.FadeColour(Color4.White, 120, Easing.InQuad); + + if (Active.Value) + text.Font = text.Font.With(weight: FontWeight.Bold); + } + else + { + text.FadeColour(AccentColour, 120, Easing.InQuad); + text.Font = text.Font.With(weight: FontWeight.Medium); + } + } + } + } +} diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 76613c156d..46751eea25 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Profile.Header; +using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Users; namespace osu.Game.Overlays.Profile @@ -18,6 +19,7 @@ namespace osu.Game.Overlays.Profile public class ProfileHeader : OverlayHeader { private UserCoverBackground coverContainer; + private readonly GamemodeControl gamemodeControl; public Bindable User = new Bindable(); @@ -32,12 +34,20 @@ namespace osu.Game.Overlays.Profile TabControl.AddItem("Modding"); centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Expanded = visible.NewValue, true); + + Add(gamemodeControl = new GamemodeControl + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Y = 100, + Margin = new MarginPadding { Right = 30 }, + }); } [BackgroundDependencyLoader] private void load(OsuColour colours) { - TabControl.AccentColour = colours.Seafoam; + TabControl.AccentColour = gamemodeControl.AccentColour = colours.Seafoam; } protected override Drawable CreateBackground() => From e9c4b521afe76f4b0ec5af79fb0607eb1c1e51e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Jun 2019 22:26:11 +0900 Subject: [PATCH 0905/2854] Test github "funding" button --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..0c6b80e97e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: https://osu.ppy.sh/home/support From a0f7f69f463eb8edefd68ed71b94dd74cb8f104d Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 4 Jun 2019 17:51:56 +0300 Subject: [PATCH 0906/2854] retrieve user's default playmode --- .../Header/Components/GamemodeControl.cs | 64 +++++++++++++++++-- osu.Game/Overlays/Profile/ProfileHeader.cs | 12 +++- 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs b/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs index 5909082fc8..f66023c958 100644 --- a/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs +++ b/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics; @@ -46,7 +47,7 @@ namespace osu.Game.Overlays.Profile.Header.Components public GamemodeControl() { TabContainer.Masking = false; - TabContainer.Spacing = new Vector2(15, 0); + TabContainer.Spacing = new Vector2(10, 0); AutoSizeAxes = Axes.Both; } @@ -54,7 +55,20 @@ namespace osu.Game.Overlays.Profile.Header.Components private void load(RulesetStore rulesets) { foreach (var r in rulesets.AvailableRulesets) - AddItem(r.Name); + AddItem(r.ShortName); + //AddItem(r.Name); + } + + public void SetDefaultGamemode(string gamemode) + { + foreach (GamemodeTabItem i in TabContainer) + { + if (i.Value == gamemode) + { + i.IsDefault = true; + return; + } + } } protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer @@ -66,6 +80,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private class GamemodeTabItem : TabItem { private readonly OsuSpriteText text; + private readonly SpriteIcon icon; private Color4 accentColour; @@ -83,6 +98,22 @@ namespace osu.Game.Overlays.Profile.Header.Components } } + private bool isDefault; + + public bool IsDefault + { + get => isDefault; + set + { + if (isDefault == value) + return; + + isDefault = value; + + icon.FadeTo(isDefault ? 1 : 0, 100, Easing.OutQuint); + } + } + public GamemodeTabItem(string value) : base(value) { @@ -90,13 +121,32 @@ namespace osu.Game.Overlays.Profile.Header.Components Children = new Drawable[] { - text = new OsuSpriteText + new FillFlowContainer { - Margin = new MarginPadding { Bottom = 10 }, + AutoSizeAxes = Axes.Both, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, - Text = value, - Font = OsuFont.GetFont() + Direction = FillDirection.Horizontal, + Spacing = new Vector2(3, 0), + Children = new Drawable[] + { + text = new OsuSpriteText + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Text = value, + Font = OsuFont.GetFont() + }, + icon = new SpriteIcon + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Alpha = 0, + AlwaysPresent = true, + Icon = FontAwesome.Solid.Star, + Size = new Vector2(12), + }, + } }, new HoverClickSounds() }; @@ -127,6 +177,7 @@ namespace osu.Game.Overlays.Profile.Header.Components if (Active.Value || IsHovered) { text.FadeColour(Color4.White, 120, Easing.InQuad); + icon.FadeColour(Color4.White, 120, Easing.InQuad); if (Active.Value) text.Font = text.Font.With(weight: FontWeight.Bold); @@ -134,6 +185,7 @@ namespace osu.Game.Overlays.Profile.Header.Components else { text.FadeColour(AccentColour, 120, Easing.InQuad); + icon.FadeColour(AccentColour, 120, Easing.InQuad); text.Font = text.Font.With(weight: FontWeight.Medium); } } diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 46751eea25..23a31614a7 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -37,6 +37,7 @@ namespace osu.Game.Overlays.Profile Add(gamemodeControl = new GamemodeControl { + Alpha = 0, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Y = 100, @@ -105,7 +106,16 @@ namespace osu.Game.Overlays.Profile protected override ScreenTitle CreateTitle() => new ProfileHeaderTitle(); - private void updateDisplay(User user) => coverContainer.User = user; + private void updateDisplay(User user) + { + coverContainer.User = user; + + string playMode = user.PlayMode; + + gamemodeControl.Current.Value = playMode; + gamemodeControl.SetDefaultGamemode(playMode); + gamemodeControl.FadeInFromZero(100, Easing.OutQuint); + } private class ProfileHeaderTitle : ScreenTitle { From 367fdcf51987368bf3c7a79988f50af4f4cac599 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 4 Jun 2019 18:07:52 +0300 Subject: [PATCH 0907/2854] Make GamemodeControl depend on rulesets --- .../Header/Components/GamemodeControl.cs | 24 ++++++++++--------- osu.Game/Overlays/Profile/ProfileHeader.cs | 1 - 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs b/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs index f66023c958..d0caeea62e 100644 --- a/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs +++ b/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs @@ -16,11 +16,11 @@ using osuTK.Graphics; namespace osu.Game.Overlays.Profile.Header.Components { - public class GamemodeControl : TabControl + public class GamemodeControl : TabControl { - protected override Dropdown CreateDropdown() => null; + protected override Dropdown CreateDropdown() => null; - protected override TabItem CreateTabItem(string value) => new GamemodeTabItem(value) + protected override TabItem CreateTabItem(RulesetInfo value) => new GamemodeTabItem(value) { AccentColour = AccentColour }; @@ -37,9 +37,9 @@ namespace osu.Game.Overlays.Profile.Header.Components accentColour = value; - foreach (TabItem tabItem in TabContainer) + foreach (GamemodeTabItem tabItem in TabContainer) { - ((GamemodeTabItem)tabItem).AccentColour = value; + tabItem.AccentColour = value; } } } @@ -55,17 +55,19 @@ namespace osu.Game.Overlays.Profile.Header.Components private void load(RulesetStore rulesets) { foreach (var r in rulesets.AvailableRulesets) - AddItem(r.ShortName); - //AddItem(r.Name); + { + AddItem(r); + } } public void SetDefaultGamemode(string gamemode) { foreach (GamemodeTabItem i in TabContainer) { - if (i.Value == gamemode) + if (i.Value.ShortName == gamemode) { i.IsDefault = true; + Current.Value = i.Value; return; } } @@ -77,7 +79,7 @@ namespace osu.Game.Overlays.Profile.Header.Components AutoSizeAxes = Axes.Both, }; - private class GamemodeTabItem : TabItem + private class GamemodeTabItem : TabItem { private readonly OsuSpriteText text; private readonly SpriteIcon icon; @@ -114,7 +116,7 @@ namespace osu.Game.Overlays.Profile.Header.Components } } - public GamemodeTabItem(string value) + public GamemodeTabItem(RulesetInfo value) : base(value) { AutoSizeAxes = Axes.Both; @@ -134,7 +136,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { Origin = Anchor.Centre, Anchor = Anchor.Centre, - Text = value, + Text = value.Name, Font = OsuFont.GetFont() }, icon = new SpriteIcon diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 23a31614a7..85541cd0ae 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -112,7 +112,6 @@ namespace osu.Game.Overlays.Profile string playMode = user.PlayMode; - gamemodeControl.Current.Value = playMode; gamemodeControl.SetDefaultGamemode(playMode); gamemodeControl.FadeInFromZero(100, Easing.OutQuint); } From d0d846469a6dcc4fec55767b5252c4eaef1b11f3 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 4 Jun 2019 18:14:03 +0300 Subject: [PATCH 0908/2854] Move GamemodeTabItem to a distinct class --- .../Header/Components/GamemodeControl.cs | 119 ---------------- .../Header/Components/GamemodeTabItem.cs | 131 ++++++++++++++++++ 2 files changed, 131 insertions(+), 119 deletions(-) create mode 100644 osu.Game/Overlays/Profile/Header/Components/GamemodeTabItem.cs diff --git a/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs b/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs index d0caeea62e..bca4cd0cfa 100644 --- a/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs +++ b/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs @@ -4,12 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -78,119 +73,5 @@ namespace osu.Game.Overlays.Profile.Header.Components Direction = FillDirection.Horizontal, AutoSizeAxes = Axes.Both, }; - - private class GamemodeTabItem : TabItem - { - private readonly OsuSpriteText text; - private readonly SpriteIcon icon; - - private Color4 accentColour; - - public Color4 AccentColour - { - get => accentColour; - set - { - if (accentColour == value) - return; - - accentColour = value; - - updateState(); - } - } - - private bool isDefault; - - public bool IsDefault - { - get => isDefault; - set - { - if (isDefault == value) - return; - - isDefault = value; - - icon.FadeTo(isDefault ? 1 : 0, 100, Easing.OutQuint); - } - } - - public GamemodeTabItem(RulesetInfo value) - : base(value) - { - AutoSizeAxes = Axes.Both; - - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(3, 0), - Children = new Drawable[] - { - text = new OsuSpriteText - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Text = value.Name, - Font = OsuFont.GetFont() - }, - icon = new SpriteIcon - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Alpha = 0, - AlwaysPresent = true, - Icon = FontAwesome.Solid.Star, - Size = new Vector2(12), - }, - } - }, - new HoverClickSounds() - }; - } - - protected override bool OnHover(HoverEvent e) - { - base.OnHover(e); - - updateState(); - - return true; - } - - protected override void OnHoverLost(HoverLostEvent e) - { - base.OnHoverLost(e); - - updateState(); - } - - protected override void OnActivated() => updateState(); - - protected override void OnDeactivated() => updateState(); - - private void updateState() - { - if (Active.Value || IsHovered) - { - text.FadeColour(Color4.White, 120, Easing.InQuad); - icon.FadeColour(Color4.White, 120, Easing.InQuad); - - if (Active.Value) - text.Font = text.Font.With(weight: FontWeight.Bold); - } - else - { - text.FadeColour(AccentColour, 120, Easing.InQuad); - icon.FadeColour(AccentColour, 120, Easing.InQuad); - text.Font = text.Font.With(weight: FontWeight.Medium); - } - } - } } } diff --git a/osu.Game/Overlays/Profile/Header/Components/GamemodeTabItem.cs b/osu.Game/Overlays/Profile/Header/Components/GamemodeTabItem.cs new file mode 100644 index 0000000000..b6e27da522 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/Components/GamemodeTabItem.cs @@ -0,0 +1,131 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Profile.Header.Components +{ + public class GamemodeTabItem : TabItem + { + private readonly OsuSpriteText text; + private readonly SpriteIcon icon; + + private Color4 accentColour; + + public Color4 AccentColour + { + get => accentColour; + set + { + if (accentColour == value) + return; + + accentColour = value; + + updateState(); + } + } + + private bool isDefault; + + public bool IsDefault + { + get => isDefault; + set + { + if (isDefault == value) + return; + + isDefault = value; + + icon.FadeTo(isDefault ? 1 : 0, 100, Easing.OutQuint); + } + } + + public GamemodeTabItem(RulesetInfo value) + : base(value) + { + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(3, 0), + Children = new Drawable[] + { + text = new OsuSpriteText + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Text = value.Name, + Font = OsuFont.GetFont() + }, + icon = new SpriteIcon + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Alpha = 0, + AlwaysPresent = true, + Icon = FontAwesome.Solid.Star, + Size = new Vector2(12), + }, + } + }, + new HoverClickSounds() + }; + } + + protected override bool OnHover(HoverEvent e) + { + base.OnHover(e); + + updateState(); + + return true; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + + updateState(); + } + + protected override void OnActivated() => updateState(); + + protected override void OnDeactivated() => updateState(); + + private void updateState() + { + if (Active.Value || IsHovered) + { + text.FadeColour(Color4.White, 120, Easing.InQuad); + icon.FadeColour(Color4.White, 120, Easing.InQuad); + + if (Active.Value) + text.Font = text.Font.With(weight: FontWeight.Bold); + } + else + { + text.FadeColour(AccentColour, 120, Easing.InQuad); + icon.FadeColour(AccentColour, 120, Easing.InQuad); + text.Font = text.Font.With(weight: FontWeight.Medium); + } + } + } +} From 0c48aec265275d24cef316e072ec1018a7eac30f Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 4 Jun 2019 18:37:31 +0300 Subject: [PATCH 0909/2854] Split SetDefaultGamemode into two functions --- .../Header/Components/GamemodeControl.cs | 11 +++- .../Header/Components/GamemodeTabItem.cs | 54 +++++++++---------- osu.Game/Overlays/Profile/ProfileHeader.cs | 1 + 3 files changed, 37 insertions(+), 29 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs b/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs index bca4cd0cfa..59c8ec8ecb 100644 --- a/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs +++ b/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs @@ -59,9 +59,16 @@ namespace osu.Game.Overlays.Profile.Header.Components { foreach (GamemodeTabItem i in TabContainer) { - if (i.Value.ShortName == gamemode) + i.IsDefault = i.Value.ShortName == gamemode; + } + } + + public void SelectDefaultGamemode() + { + foreach (GamemodeTabItem i in TabContainer) + { + if (i.IsDefault) { - i.IsDefault = true; Current.Value = i.Value; return; } diff --git a/osu.Game/Overlays/Profile/Header/Components/GamemodeTabItem.cs b/osu.Game/Overlays/Profile/Header/Components/GamemodeTabItem.cs index b6e27da522..688109ad2f 100644 --- a/osu.Game/Overlays/Profile/Header/Components/GamemodeTabItem.cs +++ b/osu.Game/Overlays/Profile/Header/Components/GamemodeTabItem.cs @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Profile.Header.Components isDefault = value; - icon.FadeTo(isDefault ? 1 : 0, 100, Easing.OutQuint); + icon.FadeTo(isDefault ? 1 : 0, 200, Easing.OutQuint); } } @@ -59,34 +59,34 @@ namespace osu.Game.Overlays.Profile.Header.Components Children = new Drawable[] { - new FillFlowContainer + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(3, 0), + Children = new Drawable[] { - AutoSizeAxes = Axes.Both, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(3, 0), - Children = new Drawable[] + text = new OsuSpriteText { - text = new OsuSpriteText - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Text = value.Name, - Font = OsuFont.GetFont() - }, - icon = new SpriteIcon - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Alpha = 0, - AlwaysPresent = true, - Icon = FontAwesome.Solid.Star, - Size = new Vector2(12), - }, - } - }, - new HoverClickSounds() + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Text = value.Name, + Font = OsuFont.GetFont() + }, + icon = new SpriteIcon + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Alpha = 0, + AlwaysPresent = true, + Icon = FontAwesome.Solid.Star, + Size = new Vector2(12), + }, + } + }, + new HoverClickSounds() }; } diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 85541cd0ae..b0c1f9a587 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -113,6 +113,7 @@ namespace osu.Game.Overlays.Profile string playMode = user.PlayMode; gamemodeControl.SetDefaultGamemode(playMode); + gamemodeControl.SelectDefaultGamemode(); gamemodeControl.FadeInFromZero(100, Easing.OutQuint); } From 8dea191998e901c60b1867bd893bcad44c76e958 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 4 Jun 2019 18:37:41 +0300 Subject: [PATCH 0910/2854] Add a testcase --- .../Visual/Online/TestSceneGamemodeControl.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs b/osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs new file mode 100644 index 0000000000..02eaef09b8 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs @@ -0,0 +1,48 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.MathUtils; +using osu.Game.Graphics; +using osu.Game.Overlays.Profile.Header.Components; +using osuTK.Graphics; +using System; +using System.Collections.Generic; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneGamemodeControl : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(GamemodeControl), + typeof(GamemodeTabItem), + }; + + private readonly GamemodeControl control; + + public TestSceneGamemodeControl() + { + Child = control = new GamemodeControl + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + + AddStep("set osu! as default", () => control.SetDefaultGamemode("osu")); + AddStep("set mania as default", () => control.SetDefaultGamemode("mania")); + AddStep("set taiko as default", () => control.SetDefaultGamemode("taiko")); + AddStep("set catch as default", () => control.SetDefaultGamemode("fruits")); + AddStep("select default gamemode", () => control.SelectDefaultGamemode()); + + AddStep("set random colour", () => control.AccentColour = new Color4(RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), 1)); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + control.AccentColour = colours.Seafoam; + } + } +} From 8260b61db5ecc418a937a83303b49bcdd245570c Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 4 Jun 2019 19:02:09 +0300 Subject: [PATCH 0911/2854] Fix CI issues --- .../Profile/Header/Components/GamemodeControl.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs b/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs index 59c8ec8ecb..3beee674fd 100644 --- a/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs +++ b/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs @@ -32,9 +32,9 @@ namespace osu.Game.Overlays.Profile.Header.Components accentColour = value; - foreach (GamemodeTabItem tabItem in TabContainer) + foreach (TabItem tabItem in TabContainer) { - tabItem.AccentColour = value; + ((GamemodeTabItem)tabItem).AccentColour = value; } } } @@ -57,19 +57,19 @@ namespace osu.Game.Overlays.Profile.Header.Components public void SetDefaultGamemode(string gamemode) { - foreach (GamemodeTabItem i in TabContainer) + foreach (TabItem tabItem in TabContainer) { - i.IsDefault = i.Value.ShortName == gamemode; + ((GamemodeTabItem)tabItem).IsDefault = ((GamemodeTabItem)tabItem).Value.ShortName == gamemode; } } public void SelectDefaultGamemode() { - foreach (GamemodeTabItem i in TabContainer) + foreach (TabItem tabItem in TabContainer) { - if (i.IsDefault) + if (((GamemodeTabItem)tabItem).IsDefault) { - Current.Value = i.Value; + Current.Value = ((GamemodeTabItem)tabItem).Value; return; } } From e9403bf2f7a59987bfccc8a97c692d1519f19e21 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 4 Jun 2019 19:33:55 +0300 Subject: [PATCH 0912/2854] Move GamemodeControl to UserProfileOverlay --- .../Visual/Online/TestSceneGamemodeControl.cs | 8 ------- .../Header/Components/GamemodeControl.cs | 5 +++- osu.Game/Overlays/Profile/ProfileHeader.cs | 24 ++----------------- osu.Game/Overlays/UserProfileOverlay.cs | 20 ++++++++++++++-- 4 files changed, 24 insertions(+), 33 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs b/osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs index 02eaef09b8..7e3ddbfd3d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs @@ -1,10 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.MathUtils; -using osu.Game.Graphics; using osu.Game.Overlays.Profile.Header.Components; using osuTK.Graphics; using System; @@ -38,11 +36,5 @@ namespace osu.Game.Tests.Visual.Online AddStep("set random colour", () => control.AccentColour = new Color4(RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), 1)); } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - control.AccentColour = colours.Seafoam; - } } } diff --git a/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs b/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs index 3beee674fd..56f84741f3 100644 --- a/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs +++ b/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -47,12 +48,14 @@ namespace osu.Game.Overlays.Profile.Header.Components } [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) + private void load(RulesetStore rulesets, OsuColour colours) { foreach (var r in rulesets.AvailableRulesets) { AddItem(r); } + + AccentColour = colours.Seafoam; } public void SetDefaultGamemode(string gamemode) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index b0c1f9a587..76613c156d 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Profile.Header; -using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Users; namespace osu.Game.Overlays.Profile @@ -19,7 +18,6 @@ namespace osu.Game.Overlays.Profile public class ProfileHeader : OverlayHeader { private UserCoverBackground coverContainer; - private readonly GamemodeControl gamemodeControl; public Bindable User = new Bindable(); @@ -34,21 +32,12 @@ namespace osu.Game.Overlays.Profile TabControl.AddItem("Modding"); centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Expanded = visible.NewValue, true); - - Add(gamemodeControl = new GamemodeControl - { - Alpha = 0, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Y = 100, - Margin = new MarginPadding { Right = 30 }, - }); } [BackgroundDependencyLoader] private void load(OsuColour colours) { - TabControl.AccentColour = gamemodeControl.AccentColour = colours.Seafoam; + TabControl.AccentColour = colours.Seafoam; } protected override Drawable CreateBackground() => @@ -106,16 +95,7 @@ namespace osu.Game.Overlays.Profile protected override ScreenTitle CreateTitle() => new ProfileHeaderTitle(); - private void updateDisplay(User user) - { - coverContainer.User = user; - - string playMode = user.PlayMode; - - gamemodeControl.SetDefaultGamemode(playMode); - gamemodeControl.SelectDefaultGamemode(); - gamemodeControl.FadeInFromZero(100, Easing.OutQuint); - } + private void updateDisplay(User user) => coverContainer.User = user; private class ProfileHeaderTitle : ScreenTitle { diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 8a133a1d1e..f61ca0affc 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -11,6 +11,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Profile; +using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Overlays.Profile.Sections; using osu.Game.Users; using osuTK; @@ -25,6 +26,7 @@ namespace osu.Game.Overlays protected ProfileHeader Header; private SectionsContainer sectionsContainer; private ProfileTabControl tabs; + private GamemodeControl gamemodeControl; public const float CONTENT_X_MARGIN = 70; @@ -32,7 +34,8 @@ namespace osu.Game.Overlays public void ShowUser(User user, bool fetchOnline = true) { - if (user == User.SYSTEM_USER) return; + if (user == User.SYSTEM_USER) + return; Show(); @@ -77,7 +80,7 @@ namespace osu.Game.Overlays { Colour = OsuColour.Gray(34), RelativeSizeAxes = Axes.Both - } + }, }); sectionsContainer.SelectedSection.ValueChanged += section => { @@ -118,6 +121,15 @@ namespace osu.Game.Overlays } sectionsContainer.ScrollToTop(); + + Header.Add(gamemodeControl = new GamemodeControl + { + Alpha = 0, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Y = 100, + Margin = new MarginPadding { Right = 30 }, + }); } private void userLoadComplete(User user) @@ -139,6 +151,10 @@ namespace osu.Game.Overlays } } } + + gamemodeControl.SetDefaultGamemode(user.PlayMode); + gamemodeControl.SelectDefaultGamemode(); + gamemodeControl.FadeInFromZero(100, Easing.OutQuint); } private class ProfileTabControl : PageTabControl From 54800aa4dfb932bb751366f967251f5b07423a30 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 4 Jun 2019 19:45:46 +0300 Subject: [PATCH 0913/2854] make the variable local --- osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs b/osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs index 7e3ddbfd3d..a2a75566d5 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs @@ -18,10 +18,10 @@ namespace osu.Game.Tests.Visual.Online typeof(GamemodeTabItem), }; - private readonly GamemodeControl control; - public TestSceneGamemodeControl() { + GamemodeControl control; + Child = control = new GamemodeControl { Anchor = Anchor.Centre, @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("set mania as default", () => control.SetDefaultGamemode("mania")); AddStep("set taiko as default", () => control.SetDefaultGamemode("taiko")); AddStep("set catch as default", () => control.SetDefaultGamemode("fruits")); - AddStep("select default gamemode", () => control.SelectDefaultGamemode()); + AddStep("select default gamemode", control.SelectDefaultGamemode); AddStep("set random colour", () => control.AccentColour = new Color4(RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), 1)); } From 05aeb6697393cf6dcbd0cf14afc25c957991c24f Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 4 Jun 2019 20:20:07 +0300 Subject: [PATCH 0914/2854] Fix possible crash due to null user or playmode --- osu.Game/Overlays/UserProfileOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index f61ca0affc..58d1fe4046 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -152,8 +152,9 @@ namespace osu.Game.Overlays } } - gamemodeControl.SetDefaultGamemode(user.PlayMode); + gamemodeControl.SetDefaultGamemode(user?.PlayMode ?? "osu"); gamemodeControl.SelectDefaultGamemode(); + gamemodeControl.FadeInFromZero(100, Easing.OutQuint); } From e20a8992655f86b57ed442fda10c011435773482 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 4 Jun 2019 21:46:43 +0300 Subject: [PATCH 0915/2854] remove excessive null check --- osu.Game/Overlays/UserProfileOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 58d1fe4046..1d8775ad04 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -152,7 +152,7 @@ namespace osu.Game.Overlays } } - gamemodeControl.SetDefaultGamemode(user?.PlayMode ?? "osu"); + gamemodeControl.SetDefaultGamemode(user.PlayMode ?? "osu"); gamemodeControl.SelectDefaultGamemode(); gamemodeControl.FadeInFromZero(100, Easing.OutQuint); From 5f4d7437bcdafc03434d62446683b5d1692940ae Mon Sep 17 00:00:00 2001 From: Arphox Date: Tue, 4 Jun 2019 21:30:49 +0200 Subject: [PATCH 0916/2854] Fix the issue When Enabled's value has been changed to true, it will now check if it is currently howered, and if yes, it will fade in correctly. --- osu.Game/Graphics/Containers/OsuHoverContainer.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs index eb2d926424..d7dcd5b699 100644 --- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs +++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs @@ -24,6 +24,9 @@ namespace osu.Game.Graphics.Containers { Enabled.ValueChanged += e => { + if (e.NewValue && isHovered) + fadeIn(); + if (!e.NewValue) unhover(); }; @@ -33,11 +36,12 @@ namespace osu.Game.Graphics.Containers protected override bool OnHover(HoverEvent e) { + isHovered = true; + if (!Enabled.Value) return false; - EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint)); - isHovered = true; + fadeIn(); return base.OnHover(e); } @@ -69,5 +73,10 @@ namespace osu.Game.Graphics.Containers base.LoadComplete(); EffectTargets.ForEach(d => d.FadeColour(IdleColour)); } + + private void fadeIn() + { + EffectTargets.ForEach(d => d.FadeColour(Color4.Black, FADE_DURATION * 10, Easing.OutQuint)); + } } } From 900cd5c4847c91a3bf0b1e20612505db343755d5 Mon Sep 17 00:00:00 2001 From: Arphox Date: Tue, 4 Jun 2019 21:37:10 +0200 Subject: [PATCH 0917/2854] Restore original values in FadeColour method call --- osu.Game/Graphics/Containers/OsuHoverContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs index d7dcd5b699..0e4a5ae5c0 100644 --- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs +++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs @@ -76,7 +76,7 @@ namespace osu.Game.Graphics.Containers private void fadeIn() { - EffectTargets.ForEach(d => d.FadeColour(Color4.Black, FADE_DURATION * 10, Easing.OutQuint)); + EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint)); } } } From a6dc5606bc588a52e03b13e9e62916617d95ecc2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 5 Jun 2019 18:17:43 +0900 Subject: [PATCH 0918/2854] Allow beatmapsets to be sorted by date added --- osu.Game/Beatmaps/BeatmapManager.cs | 1 + osu.Game/Beatmaps/BeatmapSetInfo.cs | 3 + ...AddDateAddedColumnToBeatmapSet.Designer.cs | 489 ++++++++++++++++++ ...05091246_AddDateAddedColumnToBeatmapSet.cs | 24 + .../Migrations/OsuDbContextModelSnapshot.cs | 2 + .../Select/Carousel/CarouselBeatmapSet.cs | 3 + 6 files changed, 522 insertions(+) create mode 100644 osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs create mode 100644 osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.cs diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 0200dd44ac..b6fe7f88fa 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -342,6 +342,7 @@ namespace osu.Game.Beatmaps OnlineBeatmapSetID = beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID, Beatmaps = new List(), Metadata = beatmap.Metadata, + DateAdded = DateTimeOffset.UtcNow }; } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index e111f77ba1..390236e053 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; @@ -20,6 +21,8 @@ namespace osu.Game.Beatmaps set => onlineBeatmapSetID = value > 0 ? value : null; } + public DateTimeOffset DateAdded { get; set; } + public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None; public BeatmapMetadata Metadata { get; set; } diff --git a/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs b/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs new file mode 100644 index 0000000000..9477369aa0 --- /dev/null +++ b/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs @@ -0,0 +1,489 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20190605091246_AddDateAddedColumnToBeatmapSet")] + partial class AddDateAddedColumnToBeatmapSet + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.4-servicing-10062"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("Status"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.Property("Status"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Key") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("ScoreInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("ScoreInfoID"); + + b.ToTable("ScoreFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Accuracy") + .HasColumnType("DECIMAL(1,4)"); + + b.Property("BeatmapInfoID"); + + b.Property("Combo"); + + b.Property("Date"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MaxCombo"); + + b.Property("ModsJson") + .HasColumnName("Mods"); + + b.Property("OnlineScoreID"); + + b.Property("PP"); + + b.Property("Rank"); + + b.Property("RulesetID"); + + b.Property("StatisticsJson") + .HasColumnName("Statistics"); + + b.Property("TotalScore"); + + b.Property("UserID") + .HasColumnName("UserID"); + + b.Property("UserString") + .HasColumnName("User"); + + b.HasKey("ID"); + + b.HasIndex("BeatmapInfoID"); + + b.HasIndex("OnlineScoreID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("ScoreInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Scoring.ScoreInfo") + .WithMany("Files") + .HasForeignKey("ScoreInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") + .WithMany("Scores") + .HasForeignKey("BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.cs b/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.cs new file mode 100644 index 0000000000..55dc18b6a3 --- /dev/null +++ b/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.cs @@ -0,0 +1,24 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class AddDateAddedColumnToBeatmapSet : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DateAdded", + table: "BeatmapSetInfo", + nullable: false, + defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0))); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DateAdded", + table: "BeatmapSetInfo"); + } + } +} diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index f942d357e8..a94b6df33a 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -166,6 +166,8 @@ namespace osu.Game.Migrations b.Property("ID") .ValueGeneratedOnAdd(); + b.Property("DateAdded"); + b.Property("DeletePending"); b.Property("Hash"); diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 5c334b126c..f1951e27ab 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -45,6 +45,9 @@ namespace osu.Game.Screens.Select.Carousel case SortMode.Author: return string.Compare(BeatmapSet.Metadata.Author.Username, otherSet.BeatmapSet.Metadata.Author.Username, StringComparison.InvariantCultureIgnoreCase); + case SortMode.DateAdded: + return otherSet.BeatmapSet.DateAdded.CompareTo(BeatmapSet.DateAdded); + case SortMode.Difficulty: return BeatmapSet.MaxStarDifficulty.CompareTo(otherSet.BeatmapSet.MaxStarDifficulty); } From da20be9a4be511ffaf1a194d4bcb7ce72eb888d7 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 5 Jun 2019 16:59:08 +0200 Subject: [PATCH 0919/2854] Fetch IAPIProvider via Resolved attribute --- osu.Game/Screens/OsuScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 1402aa1cce..c08d66ce10 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -114,7 +114,8 @@ namespace osu.Game.Screens [Resolved(canBeNull: true)] private OsuLogo logo { get; set; } - private IAPIProvider api; + [Resolved(canBeNull: true)] + private IAPIProvider api { get; set; } protected OsuScreen() { @@ -128,7 +129,6 @@ namespace osu.Game.Screens private void load(OsuGame osu, AudioManager audio, IAPIProvider provider) { sampleExit = audio.Sample.Get(@"UI/screen-back"); - api = provider; } public virtual bool OnPressed(GlobalAction action) From c04c6693c271020409596b796c5c00e8f8a6c3ed Mon Sep 17 00:00:00 2001 From: Welsar55 Date: Wed, 5 Jun 2019 13:01:21 -0500 Subject: [PATCH 0920/2854] Change close action from PopOut to Hide and switched to TriangleButton.Click() --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index c304dc2eb3..a7ba87e72a 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -198,9 +198,9 @@ namespace osu.Game.Overlays.Mods protected override bool OnKeyDown(KeyDownEvent e) { if (e.Key == Key.Number1) - DeselectAll(); + DeselectAllButton.Click(); else if (e.Key == Key.Number2) - PopOut(); + CloseButton.Click(); return base.OnKeyDown(e); } @@ -381,7 +381,7 @@ namespace osu.Game.Overlays.Mods { Width = 180, Text = "2. Close", - Action = PopOut, + Action = Hide, Margin = new MarginPadding { Right = 20 From 02283380c4b6ee648a741d695a35fa4cc5d6acf4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Jun 2019 13:33:30 +0900 Subject: [PATCH 0921/2854] Use manual migration --- .../Migrations/20190525060824_SkinSettings.cs | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/osu.Game/Migrations/20190525060824_SkinSettings.cs b/osu.Game/Migrations/20190525060824_SkinSettings.cs index 8bd429ca5c..99237419b7 100644 --- a/osu.Game/Migrations/20190525060824_SkinSettings.cs +++ b/osu.Game/Migrations/20190525060824_SkinSettings.cs @@ -6,25 +6,34 @@ namespace osu.Game.Migrations { protected override void Up(MigrationBuilder migrationBuilder) { - migrationBuilder.AddColumn( - name: "SkinInfoID", - table: "Settings", - nullable: true); + migrationBuilder.Sql(@"create table Settings_dg_tmp + ( + ID INTEGER not null + constraint PK_Settings + primary key autoincrement, + Key TEXT not null, + RulesetID INTEGER, + Value TEXT, + Variant INTEGER, + SkinInfoID int + constraint Settings_SkinInfo_ID_fk + references SkinInfo + on delete restrict + ); - migrationBuilder.CreateIndex( - name: "IX_Settings_SkinInfoID", - table: "Settings", - column: "SkinInfoID"); + insert into Settings_dg_tmp(ID, Key, RulesetID, Value, Variant) select ID, Key, RulesetID, Value, Variant from Settings; - // unsupported by sqlite + drop table Settings; - // migrationBuilder.AddForeignKey( - // name: "FK_Settings_SkinInfo_SkinInfoID", - // table: "Settings", - // column: "SkinInfoID", - // principalTable: "SkinInfo", - // principalColumn: "ID", - // onDelete: ReferentialAction.Restrict); + alter table Settings_dg_tmp rename to Settings; + + create index IX_Settings_RulesetID_Variant + on Settings (RulesetID, Variant); + + create index Settings_SkinInfoID_index + on Settings (SkinInfoID); + + "); } protected override void Down(MigrationBuilder migrationBuilder) From ae438213a52d2a51586e7cd16e131cffa46bd052 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 6 Jun 2019 16:32:43 +0900 Subject: [PATCH 0922/2854] Remove secondary buffered container from slider body --- .../Objects/Drawables/Pieces/SliderBody.cs | 28 ++----------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 25e1aebd18..33b3667c4f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -2,13 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Lines; -using osu.Framework.Graphics.Primitives; using osuTK; using osuTK.Graphics; -using osuTK.Graphics.ES30; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -19,8 +16,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private readonly SliderPath path; protected Path Path => path; - private readonly BufferedContainer container; - public float PathRadius { get => path.PathRadius; @@ -44,8 +39,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces return; path.AccentColour = value; - - container.ForceRedraw(); } } @@ -61,8 +54,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces return; path.BorderColour = value; - - container.ForceRedraw(); } } @@ -78,23 +69,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces return; path.BorderSize = value; - - container.ForceRedraw(); } } - public Quad PathDrawQuad => container.ScreenSpaceDrawQuad; - protected SliderBody() { - InternalChild = container = new BufferedContainer - { - RelativeSizeAxes = Axes.Both, - CacheDrawnFrameBuffer = true, - Child = path = new SliderPath { Blending = BlendingMode.None } - }; - - container.Attach(RenderbufferInternalFormat.DepthComponent16); + InternalChild = path = new SliderPath(); } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos); @@ -103,11 +83,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces /// Sets the vertices of the path which should be drawn by this . /// /// The vertices - protected void SetVertices(IReadOnlyList vertices) - { - path.Vertices = vertices; - container.ForceRedraw(); - } + protected void SetVertices(IReadOnlyList vertices) => path.Vertices = vertices; private class SliderPath : SmoothPath { From c7d0fcd42ad4b171b0f7b5e1c273fb1bcc08607a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 6 Jun 2019 16:33:14 +0900 Subject: [PATCH 0923/2854] Update drawnodes --- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 3 ++- osu.Game/Graphics/Backgrounds/Triangles.cs | 4 ++-- osu.Game/Rulesets/Mods/ModFlashlight.cs | 2 +- osu.Game/Screens/Menu/LogoVisualisation.cs | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 1b8fa0de01..1bc22da8ac 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -210,7 +210,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Vector2 pos = parts[i].Position; float localTime = parts[i].Time; - texture.DrawQuad( + DrawQuad( + texture, new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y), DrawColourInfo.Colour, null, diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index e2c7693700..29113e0e2f 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -214,7 +214,6 @@ namespace osu.Game.Graphics.Backgrounds base.Draw(vertexAction); shader.Bind(); - texture.TextureGL.Bind(); Vector2 localInflationAmount = edge_smoothness * DrawInfo.MatrixInverse.ExtractScale().Xy; @@ -231,7 +230,8 @@ namespace osu.Game.Graphics.Backgrounds ColourInfo colourInfo = DrawColourInfo.Colour; colourInfo.ApplyChild(particle.Colour); - texture.DrawTriangle( + DrawTriangle( + texture, triangle, colourInfo, null, diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index e174a25df3..405d21c711 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -179,7 +179,7 @@ namespace osu.Game.Rulesets.Mods shader.GetUniform("flashlightSize").UpdateValue(ref flashlightSize); shader.GetUniform("flashlightDim").UpdateValue(ref flashlightDim); - Texture.WhitePixel.DrawQuad(screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction); + DrawQuad(Texture.WhitePixel, screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction); shader.Unbind(); } diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 2925689d20..c6de5857c2 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -189,7 +189,6 @@ namespace osu.Game.Screens.Menu base.Draw(vertexAction); shader.Bind(); - texture.TextureGL.Bind(); Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy; @@ -224,7 +223,8 @@ namespace osu.Game.Screens.Menu Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix) ); - texture.DrawQuad( + DrawQuad( + texture, rectangle, colourInfo, null, From 4d035afcc6f93d808c28f4ebec36b26be2f1aee5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 6 Jun 2019 16:49:42 +0900 Subject: [PATCH 0924/2854] Add setting to bypass front-to-back --- osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs index b671d0e0fd..f063898a9f 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs @@ -31,6 +31,11 @@ namespace osu.Game.Overlays.Settings.Sections.Debug LabelText = "Bypass caching (slow)", Bindable = config.GetBindable(DebugSetting.BypassCaching) }, + new SettingsCheckbox + { + LabelText = "Bypass front-to-back render pass", + Bindable = config.GetBindable(DebugSetting.BypassFrontToBackPass) + } }; } } From ac9a3e54a60bf1e97977fb9a9fad2f6c4e5be28b Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 6 Jun 2019 18:07:19 +0900 Subject: [PATCH 0925/2854] Fix cursor issue with stopped gameplay clock --- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 1b8fa0de01..341975c167 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -54,7 +54,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor for (int i = 0; i < max_sprites; i++) { - parts[i].InvalidationID = 0; + // InvalidationID 1 forces an update of each part of the cursor trail the first time ApplyState is ran on the draw node + parts[i].InvalidationID = 1; parts[i].WasUpdated = true; } } From 2a90af1d4e785e2d5cdf37c93243ed027257dbcf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Jun 2019 19:15:51 +0900 Subject: [PATCH 0926/2854] Update readme with direct download links --- README.md | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index abddb1faa1..91ea34e999 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ This project is still heavily under development, but is in a state where users a We are accepting bug reports (please report with as much detail as possible). Feature requests are welcome as long as you read and understand the contribution guidelines listed below. +Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh/home/changelog). + ## Requirements - A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed. @@ -20,17 +22,24 @@ We are accepting bug reports (please report with as much detail as possible). Fe ### Releases -If you are not interested in developing the game, please head over to the [releases](https://github.com/ppy/osu/releases) to download a precompiled build with automatic updating enabled. +![](https://puu.sh/DCmvA/f6a74f5fbb.png) -- Windows (x64) users should download and run `install.exe`. -- macOS users (10.12 "Sierra" and higher) should download and run `osu.app.zip`. -- iOS users can join the [TestFlight beta program](https://t.co/xQJmHkfC18). +If you are not interested in developing the game, you can consume our [binary releases](https://github.com/ppy/osu/releases). + +**Latest build:*** + +| [Windows (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | +| ------------- | ------------- | + +- **Linux** users are recommended to self-compile until we have official deployment in place. +- **iOS** users can join the [TestFlight beta program](https://t.co/xQJmHkfC18) (note that due to high demand this is reulgarly full). +- **Android** users can self-compile, and expect a public beta soon. If your platform is not listed above, there is still a chance you can manually build it by following the instructions below. ### Downloading the source code -Clone the repository **including submodules**: +Clone the repository: ```shell git clone https://github.com/ppy/osu @@ -45,7 +54,7 @@ git pull ### Building -Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this provided below. +Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this provided [below](#contributing). > Visual Studio Code users must run the `Restore` task before any build attempt. From 6bf6e221491f95b1bb0b44e87cb22e0530ff948f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 6 Jun 2019 20:33:03 +0900 Subject: [PATCH 0927/2854] Update framework --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f84bb64fbf..55fa20188c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + From 8c1a62536cb15a559befbff2383259b687b39454 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Jun 2019 21:13:01 +0900 Subject: [PATCH 0928/2854] Update framework --- osu.iOS.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.iOS.props b/osu.iOS.props index fc047aa5f0..68f21df8ba 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 923f9fb6cdb1b2480420c43448ff048596023ffe Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Fri, 7 Jun 2019 01:43:26 +0300 Subject: [PATCH 0929/2854] Naming adjustments --- .../Visual/Online/TestSceneGamemodeControl.cs | 40 ------------------- .../Online/TestSceneProfileRulesetSelector.cs | 40 +++++++++++++++++++ ...deControl.cs => ProfileRulesetSelector.cs} | 14 +++---- .../{GamemodeTabItem.cs => RulesetTabItem.cs} | 4 +- osu.Game/Overlays/UserProfileOverlay.cs | 10 ++--- 5 files changed, 54 insertions(+), 54 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs create mode 100644 osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs rename osu.Game/Overlays/Profile/Header/Components/{GamemodeControl.cs => ProfileRulesetSelector.cs} (82%) rename osu.Game/Overlays/Profile/Header/Components/{GamemodeTabItem.cs => RulesetTabItem.cs} (97%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs b/osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs deleted file mode 100644 index a2a75566d5..0000000000 --- a/osu.Game.Tests/Visual/Online/TestSceneGamemodeControl.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.MathUtils; -using osu.Game.Overlays.Profile.Header.Components; -using osuTK.Graphics; -using System; -using System.Collections.Generic; - -namespace osu.Game.Tests.Visual.Online -{ - public class TestSceneGamemodeControl : OsuTestScene - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(GamemodeControl), - typeof(GamemodeTabItem), - }; - - public TestSceneGamemodeControl() - { - GamemodeControl control; - - Child = control = new GamemodeControl - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; - - AddStep("set osu! as default", () => control.SetDefaultGamemode("osu")); - AddStep("set mania as default", () => control.SetDefaultGamemode("mania")); - AddStep("set taiko as default", () => control.SetDefaultGamemode("taiko")); - AddStep("set catch as default", () => control.SetDefaultGamemode("fruits")); - AddStep("select default gamemode", control.SelectDefaultGamemode); - - AddStep("set random colour", () => control.AccentColour = new Color4(RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), 1)); - } - } -} diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs new file mode 100644 index 0000000000..687cbbebad --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs @@ -0,0 +1,40 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.MathUtils; +using osu.Game.Overlays.Profile.Header.Components; +using osuTK.Graphics; +using System; +using System.Collections.Generic; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneProfileRulesetSelector : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ProfileRulesetSelector), + typeof(RulesetTabItem), + }; + + public TestSceneProfileRulesetSelector() + { + ProfileRulesetSelector selector; + + Child = selector = new ProfileRulesetSelector + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + + AddStep("set osu! as default", () => selector.SetDefaultGamemode("osu")); + AddStep("set mania as default", () => selector.SetDefaultGamemode("mania")); + AddStep("set taiko as default", () => selector.SetDefaultGamemode("taiko")); + AddStep("set catch as default", () => selector.SetDefaultGamemode("fruits")); + AddStep("select default gamemode", selector.SelectDefaultGamemode); + + AddStep("set random colour", () => selector.AccentColour = new Color4(RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), 1)); + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs similarity index 82% rename from osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs rename to osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs index 56f84741f3..b189878b0d 100644 --- a/osu.Game/Overlays/Profile/Header/Components/GamemodeControl.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs @@ -12,11 +12,11 @@ using osuTK.Graphics; namespace osu.Game.Overlays.Profile.Header.Components { - public class GamemodeControl : TabControl + public class ProfileRulesetSelector : TabControl { protected override Dropdown CreateDropdown() => null; - protected override TabItem CreateTabItem(RulesetInfo value) => new GamemodeTabItem(value) + protected override TabItem CreateTabItem(RulesetInfo value) => new RulesetTabItem(value) { AccentColour = AccentColour }; @@ -35,12 +35,12 @@ namespace osu.Game.Overlays.Profile.Header.Components foreach (TabItem tabItem in TabContainer) { - ((GamemodeTabItem)tabItem).AccentColour = value; + ((RulesetTabItem)tabItem).AccentColour = value; } } } - public GamemodeControl() + public ProfileRulesetSelector() { TabContainer.Masking = false; TabContainer.Spacing = new Vector2(10, 0); @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { foreach (TabItem tabItem in TabContainer) { - ((GamemodeTabItem)tabItem).IsDefault = ((GamemodeTabItem)tabItem).Value.ShortName == gamemode; + ((RulesetTabItem)tabItem).IsDefault = ((RulesetTabItem)tabItem).Value.ShortName == gamemode; } } @@ -70,9 +70,9 @@ namespace osu.Game.Overlays.Profile.Header.Components { foreach (TabItem tabItem in TabContainer) { - if (((GamemodeTabItem)tabItem).IsDefault) + if (((RulesetTabItem)tabItem).IsDefault) { - Current.Value = ((GamemodeTabItem)tabItem).Value; + Current.Value = ((RulesetTabItem)tabItem).Value; return; } } diff --git a/osu.Game/Overlays/Profile/Header/Components/GamemodeTabItem.cs b/osu.Game/Overlays/Profile/Header/Components/RulesetTabItem.cs similarity index 97% rename from osu.Game/Overlays/Profile/Header/Components/GamemodeTabItem.cs rename to osu.Game/Overlays/Profile/Header/Components/RulesetTabItem.cs index 688109ad2f..0a6f2f5123 100644 --- a/osu.Game/Overlays/Profile/Header/Components/GamemodeTabItem.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RulesetTabItem.cs @@ -15,7 +15,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays.Profile.Header.Components { - public class GamemodeTabItem : TabItem + public class RulesetTabItem : TabItem { private readonly OsuSpriteText text; private readonly SpriteIcon icon; @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Profile.Header.Components } } - public GamemodeTabItem(RulesetInfo value) + public RulesetTabItem(RulesetInfo value) : base(value) { AutoSizeAxes = Axes.Both; diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 1d8775ad04..ec81193896 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays protected ProfileHeader Header; private SectionsContainer sectionsContainer; private ProfileTabControl tabs; - private GamemodeControl gamemodeControl; + private ProfileRulesetSelector rulesetSelector; public const float CONTENT_X_MARGIN = 70; @@ -122,7 +122,7 @@ namespace osu.Game.Overlays sectionsContainer.ScrollToTop(); - Header.Add(gamemodeControl = new GamemodeControl + Header.Add(rulesetSelector = new ProfileRulesetSelector { Alpha = 0, Anchor = Anchor.TopRight, @@ -152,10 +152,10 @@ namespace osu.Game.Overlays } } - gamemodeControl.SetDefaultGamemode(user.PlayMode ?? "osu"); - gamemodeControl.SelectDefaultGamemode(); + rulesetSelector.SetDefaultGamemode(user.PlayMode ?? "osu"); + rulesetSelector.SelectDefaultGamemode(); - gamemodeControl.FadeInFromZero(100, Easing.OutQuint); + rulesetSelector.FadeInFromZero(100, Easing.OutQuint); } private class ProfileTabControl : PageTabControl From 210437042fa0b7ee8613f52059c3c4c94f353706 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Fri, 7 Jun 2019 02:39:36 +0300 Subject: [PATCH 0930/2854] Remove useless update calls in ToolbarRulesetSelector --- osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index 84a41b6547..90412ec1d1 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -31,6 +31,7 @@ namespace osu.Game.Overlays.Toolbar public ToolbarRulesetSelector() { RelativeSizeAxes = Axes.Y; + AutoSizeAxes = Axes.X; Children = new[] { @@ -111,12 +112,6 @@ namespace osu.Game.Overlays.Toolbar private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300); - protected override void Update() - { - base.Update(); - Size = new Vector2(modeButtons.DrawSize.X, 1); - } - private void rulesetChanged(ValueChangedEvent e) { foreach (ToolbarRulesetButton m in modeButtons.Children.Cast()) From e2118299e93ac4e657c1994d09a7617ca262bbe5 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 7 Jun 2019 10:36:36 +0900 Subject: [PATCH 0931/2854] update comment --- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 341975c167..888c77442f 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -54,7 +54,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor for (int i = 0; i < max_sprites; i++) { - // InvalidationID 1 forces an update of each part of the cursor trail the first time ApplyState is ran on the draw node + // InvalidationID 1 forces an update of each part of the cursor trail the first time ApplyState is run on the draw node + // This is to prevent garbage data from being sent to the vertex shader, resulting in visual issues on some platforms parts[i].InvalidationID = 1; parts[i].WasUpdated = true; } From 9f740f69bb8ab61459dc72bd7c6dce4062e78759 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jun 2019 11:20:39 +0900 Subject: [PATCH 0932/2854] Fix preview tracks muting themselves Closes #4937 --- osu.Game/Audio/PreviewTrackManager.cs | 57 ++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index d479483508..6e162ca95e 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -1,6 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; @@ -12,22 +16,24 @@ using osu.Game.Beatmaps; namespace osu.Game.Audio { - /// - /// A central store for the retrieval of s. - /// public class PreviewTrackManager : Component { private readonly BindableDouble muteBindable = new BindableDouble(); private AudioManager audio; - private ITrackStore trackStore; + private PreviewTrackStore trackStore; private TrackManagerPreviewTrack current; [BackgroundDependencyLoader] private void load(AudioManager audio, FrameworkConfigManager config) { - trackStore = audio.GetTrackStore(new OnlineStore()); + // this is a temporary solution to get around muting ourselves. + // todo: update this once we have a BackgroundTrackManager or similar. + trackStore = new PreviewTrackStore(new OnlineStore()); + + audio.AddItem(trackStore); + trackStore.AddAdjustment(AdjustableProperty.Volume, audio.VolumeTrack); this.audio = audio; @@ -103,5 +109,46 @@ namespace osu.Game.Audio protected override Track GetTrack() => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo?.OnlineBeatmapSetID}.mp3"); } + + private class PreviewTrackStore : AudioCollectionManager, ITrackStore + { + private readonly IResourceStore store; + + internal PreviewTrackStore(IResourceStore store) + { + this.store = store; + } + + public Track GetVirtual(double length = double.PositiveInfinity) + { + if (IsDisposed) throw new ObjectDisposedException($"Cannot retrieve items for an already disposed {nameof(PreviewTrackStore)}"); + + var track = new TrackVirtual(length); + AddItem(track); + return track; + } + + public Track Get(string name) + { + if (IsDisposed) throw new ObjectDisposedException($"Cannot retrieve items for an already disposed {nameof(PreviewTrackStore)}"); + + if (string.IsNullOrEmpty(name)) return null; + + var dataStream = store.GetStream(name); + + if (dataStream == null) + return null; + + Track track = new TrackBass(dataStream); + AddItem(track); + return track; + } + + public Task GetAsync(string name) => Task.Run(() => Get(name)); + + public Stream GetStream(string name) => store.GetStream(name); + + public IEnumerable GetAvailableResources() => store.GetAvailableResources(); + } } } From 64d5aa318fd7baa6c5610e849cd9f7568ab7ae09 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jun 2019 11:45:58 +0900 Subject: [PATCH 0933/2854] Apply rebased changes --- .../TestSceneOsuHoverContainer.cs | 216 ++++++++++++++++++ .../Graphics/Containers/OsuHoverContainer.cs | 30 ++- 2 files changed, 234 insertions(+), 12 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs new file mode 100644 index 0000000000..9fe1a4cd89 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs @@ -0,0 +1,216 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.UserInterface +{ + [TestFixture] + public class TestSceneOsuHoverContainer : ManualInputManagerTestScene + { + private OsuHoverTestContainer hoverContainer; + private OsuSpriteText textContainer; + private ColourInfo currentColour => textContainer.DrawColourInfo.Colour; + private ColourInfo idleColour => hoverContainer.IdleColourPublic; + private ColourInfo hoverColour => hoverContainer.HoverColourPublic; + + public TestSceneOsuHoverContainer() + { + setupUI(); + } + + [SetUp] + public void TestSceneOsuHoverContainer_SetUp() => Schedule(() => setupUI()); + + private void setupUI() + { + Child = hoverContainer = new OsuHoverTestContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Children = new[] + { + textContainer = new OsuSpriteText + { + Text = "Test", + Font = OsuFont.GetFont(weight: FontWeight.Medium, size: 20), + }, + } + } + }; + } + + [Description("Checks IsHovered property value on a container when it is hovered/unhovered.")] + [TestCase(true, TestName = "Enabled_Check_IsHovered")] + [TestCase(false, TestName = "Disabled_Check_IsHovered")] + public void Check_IsHovered_HasProperValue(bool isEnabled) + { + moveOut(); + setContainerEnabledTo(isEnabled); + + checkNotHovered(); + + moveToText(); + checkHovered(); + + moveOut(); + checkNotHovered(); + + moveToText(); + checkHovered(); + + moveOut(); + checkNotHovered(); + + ReturnUserInput(); + } + + [Test] + [Description("Checks colour fading on an enabled container when it is hovered/unhovered.")] + public void WhenEnabled_Fades() + { + moveOut(); + enableContainer(); + + checkColour(idleColour); + + moveToText(); + waitUntilColourIs(hoverColour); + + moveOut(); + waitUntilColourIs(idleColour); + + moveToText(); + waitUntilColourIs(hoverColour); + + moveOut(); + waitUntilColourIs(idleColour); + + ReturnUserInput(); + } + + [Test] + [Description("Checks colour fading on a disabled container when it is hovered/unhovered.")] + public void WhenDisabled_DoesNotFade() + { + moveOut(); + disableContainer(); + + checkColour(idleColour); + + moveToText(); + checkColour(idleColour); + + moveOut(); + checkColour(idleColour); + + moveToText(); + checkColour(idleColour); + + moveOut(); + checkColour(idleColour); + + ReturnUserInput(); + } + + [Test] + [Description("Checks that when a disabled & hovered container gets enabled, colour fading happens")] + public void WhileHovering_WhenGetsEnabled_Fades() + { + moveOut(); + disableContainer(); + checkColour(idleColour); + + moveToText(); + checkColour(idleColour); + + enableContainer(); + waitUntilColourIs(hoverColour); + } + + [Test] + [Description("Checks that when an enabled & hovered container gets disabled, colour fading happens")] + public void WhileHovering_WhenGetsDisabled_Fades() + { + moveOut(); + enableContainer(); + checkColour(idleColour); + + moveToText(); + waitUntilColourIs(hoverColour); + + disableContainer(); + waitUntilColourIs(idleColour); + } + + [Test] + [Description("Checks that when a hovered container gets enabled and disabled multiple times, colour fading happens")] + public void WhileHovering_WhenEnabledChangesMultipleTimes_Fades() + { + moveOut(); + enableContainer(); + checkColour(idleColour); + + moveToText(); + waitUntilColourIs(hoverColour); + + disableContainer(); + waitUntilColourIs(idleColour); + + enableContainer(); + waitUntilColourIs(hoverColour); + + disableContainer(); + waitUntilColourIs(idleColour); + } + + private void enableContainer() => setContainerEnabledTo(true); + + private void disableContainer() => setContainerEnabledTo(false); + + private void setContainerEnabledTo(bool newValue) + { + string word = newValue ? "Enable" : "Disable"; + AddStep($"{word} container", () => hoverContainer.Enabled.Value = newValue); + } + + private void moveToText() => AddStep("Move mouse to text", () => InputManager.MoveMouseTo(hoverContainer)); + + private void moveOut() => AddStep("Move out", doMoveOut); + + private void checkHovered() => AddAssert("Check hovered", () => hoverContainer.IsHovered); + + private void checkNotHovered() => AddAssert("Check not hovered", () => !hoverContainer.IsHovered); + + private void checkColour(ColourInfo expectedColour) + => AddAssert($"Check colour to be '{expectedColour}'", () => currentColour.Equals(expectedColour)); + + private void waitUntilColourIs(ColourInfo expectedColour) + => AddUntilStep($"Wait until hover colour is {expectedColour}", () => currentColour.Equals(expectedColour)); + + /// + /// Moves the cursor to top left corner of the screen + /// + private void doMoveOut() + => InputManager.MoveMouseTo(new Vector2(InputManager.ScreenSpaceDrawQuad.TopLeft.X, InputManager.ScreenSpaceDrawQuad.TopLeft.Y)); + + private sealed class OsuHoverTestContainer : OsuHoverContainer + { + public Color4 HoverColourPublic => HoverColour; + public Color4 IdleColourPublic => IdleColour; + } + } +} diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs index 0e4a5ae5c0..4ea28f74b9 100644 --- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs +++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs @@ -24,11 +24,13 @@ namespace osu.Game.Graphics.Containers { Enabled.ValueChanged += e => { - if (e.NewValue && isHovered) - fadeIn(); - - if (!e.NewValue) - unhover(); + if (isHovered) + { + if (e.NewValue) + fadeIn(); + else + fadeOut(); + } }; } @@ -36,6 +38,9 @@ namespace osu.Game.Graphics.Containers protected override bool OnHover(HoverEvent e) { + if (isHovered) + return false; + isHovered = true; if (!Enabled.Value) @@ -47,18 +52,14 @@ namespace osu.Game.Graphics.Containers } protected override void OnHoverLost(HoverLostEvent e) - { - unhover(); - base.OnHoverLost(e); - } - - private void unhover() { if (!isHovered) return; isHovered = false; - EffectTargets.ForEach(d => d.FadeColour(IdleColour, FADE_DURATION, Easing.OutQuint)); + fadeOut(); + + base.OnHoverLost(e); } [BackgroundDependencyLoader] @@ -78,5 +79,10 @@ namespace osu.Game.Graphics.Containers { EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint)); } + + private void fadeOut() + { + EffectTargets.ForEach(d => d.FadeColour(IdleColour, FADE_DURATION, Easing.OutQuint)); + } } } From 694f2e3a4f9e5902d685b9d7cac0de42d5546384 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jun 2019 11:59:07 +0900 Subject: [PATCH 0934/2854] Tidy up test scene's setup usage --- .../TestSceneOsuHoverContainer.cs | 77 +++++++++---------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs index 9fe1a4cd89..79aa6189af 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs @@ -20,18 +20,9 @@ namespace osu.Game.Tests.Visual.UserInterface private OsuHoverTestContainer hoverContainer; private OsuSpriteText textContainer; private ColourInfo currentColour => textContainer.DrawColourInfo.Colour; - private ColourInfo idleColour => hoverContainer.IdleColourPublic; - private ColourInfo hoverColour => hoverContainer.HoverColourPublic; - - public TestSceneOsuHoverContainer() - { - setupUI(); - } [SetUp] - public void TestSceneOsuHoverContainer_SetUp() => Schedule(() => setupUI()); - - private void setupUI() + public void SetUp() => Schedule(() => { Child = hoverContainer = new OsuHoverTestContainer { @@ -51,12 +42,12 @@ namespace osu.Game.Tests.Visual.UserInterface } } }; - } + }); [Description("Checks IsHovered property value on a container when it is hovered/unhovered.")] [TestCase(true, TestName = "Enabled_Check_IsHovered")] [TestCase(false, TestName = "Disabled_Check_IsHovered")] - public void Check_IsHovered_HasProperValue(bool isEnabled) + public void TestIsHoveredHasProperValue(bool isEnabled) { moveOut(); setContainerEnabledTo(isEnabled); @@ -80,101 +71,101 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] [Description("Checks colour fading on an enabled container when it is hovered/unhovered.")] - public void WhenEnabled_Fades() + public void TestTransitionWhileEnabled() { moveOut(); enableContainer(); - checkColour(idleColour); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); moveToText(); - waitUntilColourIs(hoverColour); + waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR); moveOut(); - waitUntilColourIs(idleColour); + waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR); moveToText(); - waitUntilColourIs(hoverColour); + waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR); moveOut(); - waitUntilColourIs(idleColour); + waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR); ReturnUserInput(); } [Test] [Description("Checks colour fading on a disabled container when it is hovered/unhovered.")] - public void WhenDisabled_DoesNotFade() + public void TestNoTransitionWhileDisabled() { moveOut(); disableContainer(); - checkColour(idleColour); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); moveToText(); - checkColour(idleColour); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); moveOut(); - checkColour(idleColour); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); moveToText(); - checkColour(idleColour); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); moveOut(); - checkColour(idleColour); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); ReturnUserInput(); } [Test] [Description("Checks that when a disabled & hovered container gets enabled, colour fading happens")] - public void WhileHovering_WhenGetsEnabled_Fades() + public void TestBecomesEnabledTransition() { moveOut(); disableContainer(); - checkColour(idleColour); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); moveToText(); - checkColour(idleColour); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); enableContainer(); - waitUntilColourIs(hoverColour); + waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR); } [Test] [Description("Checks that when an enabled & hovered container gets disabled, colour fading happens")] - public void WhileHovering_WhenGetsDisabled_Fades() + public void TestBecomesDisabledTransition() { moveOut(); enableContainer(); - checkColour(idleColour); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); moveToText(); - waitUntilColourIs(hoverColour); + waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR); disableContainer(); - waitUntilColourIs(idleColour); + waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR); } [Test] [Description("Checks that when a hovered container gets enabled and disabled multiple times, colour fading happens")] - public void WhileHovering_WhenEnabledChangesMultipleTimes_Fades() + public void TestDisabledChangesMultipleTimes() { moveOut(); enableContainer(); - checkColour(idleColour); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); moveToText(); - waitUntilColourIs(hoverColour); + waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR); disableContainer(); - waitUntilColourIs(idleColour); + waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR); enableContainer(); - waitUntilColourIs(hoverColour); + waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR); disableContainer(); - waitUntilColourIs(idleColour); + waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR); } private void enableContainer() => setContainerEnabledTo(true); @@ -209,8 +200,14 @@ namespace osu.Game.Tests.Visual.UserInterface private sealed class OsuHoverTestContainer : OsuHoverContainer { - public Color4 HoverColourPublic => HoverColour; - public Color4 IdleColourPublic => IdleColour; + public static readonly Color4 HOVER_COLOUR = Color4.Red; + public static readonly Color4 IDLE_COLOUR = Color4.Green; + + public OsuHoverTestContainer() + { + HoverColour = HOVER_COLOUR; + IdleColour = IDLE_COLOUR; + } } } } From 58174425eda10e15e64f39f5ce4f6165ea8d4045 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jun 2019 12:37:10 +0900 Subject: [PATCH 0935/2854] Make visual test more visible --- .../TestSceneOsuHoverContainer.cs | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs index 79aa6189af..6b2bca9b83 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs @@ -4,11 +4,8 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; @@ -18,8 +15,7 @@ namespace osu.Game.Tests.Visual.UserInterface public class TestSceneOsuHoverContainer : ManualInputManagerTestScene { private OsuHoverTestContainer hoverContainer; - private OsuSpriteText textContainer; - private ColourInfo currentColour => textContainer.DrawColourInfo.Colour; + private Box colourContainer; [SetUp] public void SetUp() => Schedule(() => @@ -28,19 +24,11 @@ namespace osu.Game.Tests.Visual.UserInterface { Anchor = Anchor.Centre, Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Child = new FillFlowContainer + Size = new Vector2(100), + Child = colourContainer = new Box { - AutoSizeAxes = Axes.Both, - Children = new[] - { - textContainer = new OsuSpriteText - { - Text = "Test", - Font = OsuFont.GetFont(weight: FontWeight.Medium, size: 20), - }, - } - } + RelativeSizeAxes = Axes.Both, + }, }; }); @@ -192,6 +180,8 @@ namespace osu.Game.Tests.Visual.UserInterface private void waitUntilColourIs(ColourInfo expectedColour) => AddUntilStep($"Wait until hover colour is {expectedColour}", () => currentColour.Equals(expectedColour)); + private ColourInfo currentColour => colourContainer.DrawColourInfo.Colour; + /// /// Moves the cursor to top left corner of the screen /// From 748c0e5c012e266dd4ca15f1c786de6654c3aefd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jun 2019 12:42:01 +0900 Subject: [PATCH 0936/2854] Set default state of test to enabled --- .../Visual/UserInterface/TestSceneOsuHoverContainer.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs index 6b2bca9b83..3613122165 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs @@ -22,6 +22,7 @@ namespace osu.Game.Tests.Visual.UserInterface { Child = hoverContainer = new OsuHoverTestContainer { + Enabled = { Value = true }, Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(100), @@ -30,6 +31,8 @@ namespace osu.Game.Tests.Visual.UserInterface RelativeSizeAxes = Axes.Both, }, }; + + doMoveOut(); }); [Description("Checks IsHovered property value on a container when it is hovered/unhovered.")] @@ -37,7 +40,6 @@ namespace osu.Game.Tests.Visual.UserInterface [TestCase(false, TestName = "Disabled_Check_IsHovered")] public void TestIsHoveredHasProperValue(bool isEnabled) { - moveOut(); setContainerEnabledTo(isEnabled); checkNotHovered(); @@ -61,7 +63,6 @@ namespace osu.Game.Tests.Visual.UserInterface [Description("Checks colour fading on an enabled container when it is hovered/unhovered.")] public void TestTransitionWhileEnabled() { - moveOut(); enableContainer(); checkColour(OsuHoverTestContainer.IDLE_COLOUR); @@ -85,7 +86,6 @@ namespace osu.Game.Tests.Visual.UserInterface [Description("Checks colour fading on a disabled container when it is hovered/unhovered.")] public void TestNoTransitionWhileDisabled() { - moveOut(); disableContainer(); checkColour(OsuHoverTestContainer.IDLE_COLOUR); @@ -109,7 +109,6 @@ namespace osu.Game.Tests.Visual.UserInterface [Description("Checks that when a disabled & hovered container gets enabled, colour fading happens")] public void TestBecomesEnabledTransition() { - moveOut(); disableContainer(); checkColour(OsuHoverTestContainer.IDLE_COLOUR); @@ -124,7 +123,6 @@ namespace osu.Game.Tests.Visual.UserInterface [Description("Checks that when an enabled & hovered container gets disabled, colour fading happens")] public void TestBecomesDisabledTransition() { - moveOut(); enableContainer(); checkColour(OsuHoverTestContainer.IDLE_COLOUR); @@ -139,7 +137,6 @@ namespace osu.Game.Tests.Visual.UserInterface [Description("Checks that when a hovered container gets enabled and disabled multiple times, colour fading happens")] public void TestDisabledChangesMultipleTimes() { - moveOut(); enableContainer(); checkColour(OsuHoverTestContainer.IDLE_COLOUR); From 6f6b134ec8266c2d7ea5931052261d29ea7ad9c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jun 2019 12:52:49 +0900 Subject: [PATCH 0937/2854] Remove return user input calls --- .../Visual/UserInterface/TestSceneOsuHoverContainer.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs index 3613122165..dbef7d1686 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs @@ -55,8 +55,6 @@ namespace osu.Game.Tests.Visual.UserInterface moveOut(); checkNotHovered(); - - ReturnUserInput(); } [Test] @@ -78,8 +76,6 @@ namespace osu.Game.Tests.Visual.UserInterface moveOut(); waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR); - - ReturnUserInput(); } [Test] @@ -101,8 +97,6 @@ namespace osu.Game.Tests.Visual.UserInterface moveOut(); checkColour(OsuHoverTestContainer.IDLE_COLOUR); - - ReturnUserInput(); } [Test] From 0fc2c596b650bedc892695e46926c47691810f4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jun 2019 12:53:51 +0900 Subject: [PATCH 0938/2854] Add toggle for input priority in manual input tests --- .../Visual/ManualInputManagerTestScene.cs | 92 ++++++++++++++++++- 1 file changed, 88 insertions(+), 4 deletions(-) diff --git a/osu.Game/Tests/Visual/ManualInputManagerTestScene.cs b/osu.Game/Tests/Visual/ManualInputManagerTestScene.cs index a7a7f88ff7..460df8b84c 100644 --- a/osu.Game/Tests/Visual/ManualInputManagerTestScene.cs +++ b/osu.Game/Tests/Visual/ManualInputManagerTestScene.cs @@ -3,8 +3,13 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Testing.Input; using osu.Game.Graphics.Cursor; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Tests.Visual { @@ -15,12 +20,77 @@ namespace osu.Game.Tests.Visual protected readonly ManualInputManager InputManager; + private readonly TriangleButton buttonTest; + private readonly TriangleButton buttonLocal; + protected ManualInputManagerTestScene() { - base.Content.Add(InputManager = new ManualInputManager + base.Content.AddRange(new Drawable[] { - UseParentInput = true, - Child = content = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }, + InputManager = new ManualInputManager + { + UseParentInput = true, + Child = content = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }, + }, + new Container + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Margin = new MarginPadding(5), + CornerRadius = 5, + Masking = true, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, + Alpha = 0.5f, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Margin = new MarginPadding(5), + Spacing = new Vector2(5), + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "Input Priority" + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding(5), + Spacing = new Vector2(5), + Direction = FillDirection.Horizontal, + + Children = new Drawable[] + { + buttonLocal = new TriangleButton + { + Text = "local", + Size = new Vector2(50, 30), + Action = returnUserInput + }, + buttonTest = new TriangleButton + { + Text = "test", + Size = new Vector2(50, 30), + Action = returnTestInput + }, + } + }, + } + }, + } + }, }); } @@ -29,7 +99,21 @@ namespace osu.Game.Tests.Visual /// protected void ReturnUserInput() { - AddStep("Return user input", () => InputManager.UseParentInput = true); + AddStep("Return user input", returnUserInput); } + + protected override void Update() + { + base.Update(); + + buttonTest.Enabled.Value = InputManager.UseParentInput; + buttonLocal.Enabled.Value = !InputManager.UseParentInput; + } + + private void returnUserInput() => + InputManager.UseParentInput = true; + + private void returnTestInput() => + InputManager.UseParentInput = false; } } From 1374da7c41b1f364b4012d047d6df7e2ed18de07 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jun 2019 12:54:57 +0900 Subject: [PATCH 0939/2854] Remove all calls to return user input --- osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs | 1 - osu.Game/Tests/Visual/ManualInputManagerTestScene.cs | 8 -------- 2 files changed, 9 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs index 590ee4e720..8fe31b7ad6 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs @@ -84,7 +84,6 @@ namespace osu.Game.Tests.Visual.UserInterface testLocalCursor(); testUserCursorOverride(); testMultipleLocalCursors(); - ReturnUserInput(); } /// diff --git a/osu.Game/Tests/Visual/ManualInputManagerTestScene.cs b/osu.Game/Tests/Visual/ManualInputManagerTestScene.cs index 460df8b84c..86191609a4 100644 --- a/osu.Game/Tests/Visual/ManualInputManagerTestScene.cs +++ b/osu.Game/Tests/Visual/ManualInputManagerTestScene.cs @@ -94,14 +94,6 @@ namespace osu.Game.Tests.Visual }); } - /// - /// Returns input back to the user. - /// - protected void ReturnUserInput() - { - AddStep("Return user input", returnUserInput); - } - protected override void Update() { base.Update(); From 60b70c0f45e11d7114d1769b62f8ef4f1e06c40a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jun 2019 14:11:21 +0900 Subject: [PATCH 0940/2854] Use lambda for simple functions --- osu.Game/Graphics/Containers/OsuHoverContainer.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs index 4ea28f74b9..67af79c763 100644 --- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs +++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs @@ -75,14 +75,8 @@ namespace osu.Game.Graphics.Containers EffectTargets.ForEach(d => d.FadeColour(IdleColour)); } - private void fadeIn() - { - EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint)); - } + private void fadeIn() => EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint)); - private void fadeOut() - { - EffectTargets.ForEach(d => d.FadeColour(IdleColour, FADE_DURATION, Easing.OutQuint)); - } + private void fadeOut() => EffectTargets.ForEach(d => d.FadeColour(IdleColour, FADE_DURATION, Easing.OutQuint)); } } From 2531250f890245d4df9b8f70b666304027b3faea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jun 2019 14:58:04 +0900 Subject: [PATCH 0941/2854] Fix paginated layouts only showing one column even if enough space is available for more --- osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 8639acfc94..b459afcb49 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -49,7 +49,6 @@ namespace osu.Game.Overlays.Profile.Sections { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, Spacing = new Vector2(0, 2), }, MoreButton = new ShowMoreButton From 0ce5c7468fcf155dea67469182fae578abecdaf7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jun 2019 15:31:23 +0900 Subject: [PATCH 0942/2854] Use switch and consume/block input --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index a7ba87e72a..501679af03 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -197,10 +197,16 @@ namespace osu.Game.Overlays.Mods protected override bool OnKeyDown(KeyDownEvent e) { - if (e.Key == Key.Number1) - DeselectAllButton.Click(); - else if (e.Key == Key.Number2) - CloseButton.Click(); + switch (e.Key) + { + case Key.Number1: + DeselectAllButton.Click(); + return true; + + case Key.Number2: + CloseButton.Click(); + return true; + } return base.OnKeyDown(e); } From b914bb1e2eb9aa0c451addd8d60d076d15e64f9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jun 2019 15:32:48 +0900 Subject: [PATCH 0943/2854] Remove key hints for now A proper design for this will come in the future. --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 501679af03..0e37e800ca 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -376,7 +376,7 @@ namespace osu.Game.Overlays.Mods DeselectAllButton = new TriangleButton { Width = 180, - Text = "1. Deselect All", + Text = "Deselect All", Action = DeselectAll, Margin = new MarginPadding { @@ -386,7 +386,7 @@ namespace osu.Game.Overlays.Mods CloseButton = new TriangleButton { Width = 180, - Text = "2. Close", + Text = "Close", Action = Hide, Margin = new MarginPadding { From 8f30c9b0a3ec210f6af2cd4e3f8353adda90a740 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jun 2019 15:58:24 +0900 Subject: [PATCH 0944/2854] Fix file layout of ModSelectOverlay --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 365 +++++++++++---------- 1 file changed, 186 insertions(+), 179 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index b57e98d09e..dec58f4c9e 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -1,43 +1,40 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osuTK; -using osuTK.Input; -using osuTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Mods; using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; -using osu.Game.Rulesets; +using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods.Sections; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Screens; -using osu.Framework.Input.Events; +using osuTK; +using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Overlays.Mods { public class ModSelectOverlay : WaveOverlayContainer { - private const float content_width = 0.8f; - - protected Color4 LowMultiplierColour, HighMultiplierColour; - protected readonly TriangleButton DeselectAllButton; protected readonly TriangleButton CloseButton; - protected readonly OsuSpriteText MultiplierLabel, UnrankedLabel; - private readonly FillFlowContainer footerContainer; + + protected readonly OsuSpriteText MultiplierLabel; + protected readonly OsuSpriteText UnrankedLabel; protected override bool BlockNonPositionalInput => false; @@ -49,170 +46,14 @@ namespace osu.Game.Overlays.Mods protected readonly IBindable Ruleset = new Bindable(); - [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, IBindable ruleset, AudioManager audio, Bindable> mods) - { - LowMultiplierColour = colours.Red; - HighMultiplierColour = colours.Green; - UnrankedLabel.Colour = colours.Blue; + protected Color4 LowMultiplierColour; + protected Color4 HighMultiplierColour; - Ruleset.BindTo(ruleset); - if (mods != null) SelectedMods.BindTo(mods); - - sampleOn = audio.Samples.Get(@"UI/check-on"); - sampleOff = audio.Samples.Get(@"UI/check-off"); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Ruleset.BindValueChanged(rulesetChanged, true); - SelectedMods.BindValueChanged(selectedModsChanged, true); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - Ruleset.UnbindAll(); - SelectedMods.UnbindAll(); - } - - private void rulesetChanged(ValueChangedEvent e) - { - if (e.NewValue == null) return; - - var instance = e.NewValue.CreateInstance(); - - foreach (ModSection section in ModSectionsContainer.Children) - section.Mods = instance.GetModsFor(section.ModType); - - // attempt to re-select any already selected mods. - // this may be the first time we are receiving the ruleset, in which case they will still match. - selectedModsChanged(new ValueChangedEvent>(SelectedMods.Value, SelectedMods.Value)); - - // write the mods back to the SelectedMods bindable in the case a change was not applicable. - // this generally isn't required as the previous line will perform deselection; just here for safety. - refreshSelectedMods(); - } - - private void selectedModsChanged(ValueChangedEvent> e) - { - foreach (ModSection section in ModSectionsContainer.Children) - section.SelectTypes(e.NewValue.Select(m => m.GetType()).ToList()); - - updateMods(); - } - - private void updateMods() - { - double multiplier = 1.0; - bool ranked = true; - - foreach (Mod mod in SelectedMods.Value) - { - multiplier *= mod.ScoreMultiplier; - ranked &= mod.Ranked; - } - - MultiplierLabel.Text = $"{multiplier:N2}x"; - if (multiplier > 1.0) - MultiplierLabel.FadeColour(HighMultiplierColour, 200); - else if (multiplier < 1.0) - MultiplierLabel.FadeColour(LowMultiplierColour, 200); - else - MultiplierLabel.FadeColour(Color4.White, 200); - - UnrankedLabel.FadeTo(ranked ? 0 : 1, 200); - } - - protected override void PopOut() - { - base.PopOut(); - - footerContainer.MoveToX(footerContainer.DrawSize.X, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - footerContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - - foreach (ModSection section in ModSectionsContainer.Children) - { - section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - section.ButtonsContainer.MoveToX(100f, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - section.ButtonsContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - } - } - - protected override void PopIn() - { - base.PopIn(); - - footerContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); - footerContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); - - foreach (ModSection section in ModSectionsContainer.Children) - { - section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), WaveContainer.APPEAR_DURATION, Easing.OutQuint); - section.ButtonsContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); - section.ButtonsContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); - } - } - - public void DeselectAll() - { - foreach (ModSection section in ModSectionsContainer.Children) - section.DeselectAll(); - - refreshSelectedMods(); - } - - /// - /// Deselect one or more mods. - /// - /// The types of s which should be deselected. - /// Set to true to bypass animations and update selections immediately. - public void DeselectTypes(Type[] modTypes, bool immediate = false) - { - if (modTypes.Length == 0) return; - - foreach (ModSection section in ModSectionsContainer.Children) - section.DeselectTypes(modTypes, immediate); - } + private const float content_width = 0.8f; + private readonly FillFlowContainer footerContainer; private SampleChannel sampleOn, sampleOff; - private void modButtonPressed(Mod selectedMod) - { - if (selectedMod != null) - { - if (State == Visibility.Visible) sampleOn?.Play(); - DeselectTypes(selectedMod.IncompatibleMods, true); - } - else - { - if (State == Visibility.Visible) sampleOff?.Play(); - } - - refreshSelectedMods(); - } - - protected override bool OnKeyDown(KeyDownEvent e) - { - switch (e.Key) - { - case Key.Number1: - DeselectAllButton.Click(); - return true; - - case Key.Number2: - CloseButton.Click(); - return true; - } - - return base.OnKeyDown(e); - } - - private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray(); - public ModSelectOverlay() { Waves.FirstWaveColour = OsuColour.FromHex(@"19b0e2"); @@ -430,5 +271,171 @@ namespace osu.Game.Overlays.Mods }, }; } + + [BackgroundDependencyLoader(true)] + private void load(OsuColour colours, IBindable ruleset, AudioManager audio, Bindable> mods) + { + LowMultiplierColour = colours.Red; + HighMultiplierColour = colours.Green; + UnrankedLabel.Colour = colours.Blue; + + Ruleset.BindTo(ruleset); + if (mods != null) SelectedMods.BindTo(mods); + + sampleOn = audio.Samples.Get(@"UI/check-on"); + sampleOff = audio.Samples.Get(@"UI/check-off"); + } + + public void DeselectAll() + { + foreach (var section in ModSectionsContainer.Children) + section.DeselectAll(); + + refreshSelectedMods(); + } + + /// + /// Deselect one or more mods. + /// + /// The types of s which should be deselected. + /// Set to true to bypass animations and update selections immediately. + public void DeselectTypes(Type[] modTypes, bool immediate = false) + { + if (modTypes.Length == 0) return; + + foreach (var section in ModSectionsContainer.Children) + section.DeselectTypes(modTypes, immediate); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Ruleset.BindValueChanged(rulesetChanged, true); + SelectedMods.BindValueChanged(selectedModsChanged, true); + } + + protected override void PopOut() + { + base.PopOut(); + + footerContainer.MoveToX(footerContainer.DrawSize.X, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + footerContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + + foreach (var section in ModSectionsContainer.Children) + { + section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + section.ButtonsContainer.MoveToX(100f, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + section.ButtonsContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + } + } + + protected override void PopIn() + { + base.PopIn(); + + footerContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); + footerContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); + + foreach (var section in ModSectionsContainer.Children) + { + section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), WaveContainer.APPEAR_DURATION, Easing.OutQuint); + section.ButtonsContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); + section.ButtonsContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); + } + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + switch (e.Key) + { + case Key.Number1: + DeselectAllButton.Click(); + return true; + + case Key.Number2: + CloseButton.Click(); + return true; + } + + return base.OnKeyDown(e); + } + + private void rulesetChanged(ValueChangedEvent e) + { + if (e.NewValue == null) return; + + var instance = e.NewValue.CreateInstance(); + + foreach (var section in ModSectionsContainer.Children) + section.Mods = instance.GetModsFor(section.ModType); + + // attempt to re-select any already selected mods. + // this may be the first time we are receiving the ruleset, in which case they will still match. + selectedModsChanged(new ValueChangedEvent>(SelectedMods.Value, SelectedMods.Value)); + + // write the mods back to the SelectedMods bindable in the case a change was not applicable. + // this generally isn't required as the previous line will perform deselection; just here for safety. + refreshSelectedMods(); + } + + private void selectedModsChanged(ValueChangedEvent> e) + { + foreach (var section in ModSectionsContainer.Children) + section.SelectTypes(e.NewValue.Select(m => m.GetType()).ToList()); + + updateMods(); + } + + private void updateMods() + { + var multiplier = 1.0; + var ranked = true; + + foreach (var mod in SelectedMods.Value) + { + multiplier *= mod.ScoreMultiplier; + ranked &= mod.Ranked; + } + + MultiplierLabel.Text = $"{multiplier:N2}x"; + if (multiplier > 1.0) + MultiplierLabel.FadeColour(HighMultiplierColour, 200); + else if (multiplier < 1.0) + MultiplierLabel.FadeColour(LowMultiplierColour, 200); + else + MultiplierLabel.FadeColour(Color4.White, 200); + + UnrankedLabel.FadeTo(ranked ? 0 : 1, 200); + } + + private void modButtonPressed(Mod selectedMod) + { + if (selectedMod != null) + { + if (State == Visibility.Visible) sampleOn?.Play(); + DeselectTypes(selectedMod.IncompatibleMods, true); + } + else + { + if (State == Visibility.Visible) sampleOff?.Play(); + } + + refreshSelectedMods(); + } + + private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray(); + + #region Disposal + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + Ruleset.UnbindAll(); + SelectedMods.UnbindAll(); + } + + #endregion } } From cd4648a64729383356dcf0a26538126c902241ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jun 2019 21:09:59 +0900 Subject: [PATCH 0945/2854] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 55fa20188c..654c62e1d8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 68f21df8ba..8886184a2e 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From e5b64bfa39df31f0db308655c943272e6c7b9348 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 7 Jun 2019 18:51:43 +0200 Subject: [PATCH 0946/2854] Highlight major changes in changelog overlay --- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 51 ++++++++++++++++--- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 57615332da..627eb10426 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -69,34 +69,69 @@ namespace osu.Game.Overlays.Changelog Margin = new MarginPadding { Vertical = 5 }, }; + var entryColor = entry.Major != null && (bool)entry.Major ? OsuColour.FromHex("#fd5") : Color4.White; + title.AddIcon(FontAwesome.Solid.Check, t => { t.Font = fontSmall; + t.Colour = entryColor; t.Padding = new MarginPadding { Left = -17, Right = 5 }; }); - title.AddText(entry.Title, t => { t.Font = fontLarge; }); + title.AddText(entry.Title, t => + { + t.Font = fontLarge; + t.Colour = entryColor; + }); if (!string.IsNullOrEmpty(entry.Repository)) { - title.AddText(" (", t => t.Font = fontLarge); + title.AddText(" (", t => + { + t.Font = fontLarge; + t.Colour = entryColor; + }); title.AddLink($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", entry.GithubUrl, Online.Chat.LinkAction.External, - creationParameters: t => { t.Font = fontLarge; }); - title.AddText(")", t => t.Font = fontLarge); + creationParameters: t => + { + t.Font = fontLarge; + t.Colour = entryColor; + }); + title.AddText(")", t => + { + t.Font = fontLarge; + t.Colour = entryColor; + }); } - title.AddText(" by ", t => t.Font = fontMedium); + title.AddText(" by ", t => + { + t.Font = fontMedium; + t.Colour = entryColor; + }); if (entry.GithubUser.UserId != null) title.AddUserLink(new User { Username = entry.GithubUser.OsuUsername, Id = entry.GithubUser.UserId.Value - }, t => t.Font = fontMedium); + }, t => + { + t.Font = fontMedium; + t.Colour = entryColor; + }); else if (entry.GithubUser.GithubUrl != null) - title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, Online.Chat.LinkAction.External, null, null, t => t.Font = fontMedium); + title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, Online.Chat.LinkAction.External, null, null, t => + { + t.Font = fontMedium; + t.Colour = entryColor; + }); else - title.AddText(entry.GithubUser.DisplayName, t => t.Font = fontSmall); + title.AddText(entry.GithubUser.DisplayName, t => + { + t.Font = fontSmall; + t.Colour = entryColor; + }); ChangelogEntries.Add(title); From f326264a85abacef478d45cf71fc28867b3554e2 Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Sat, 8 Jun 2019 00:42:57 +0700 Subject: [PATCH 0947/2854] Adding increase first object grow mod visibility setting --- osu.Game/Configuration/OsuConfigManager.cs | 3 +++ osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 795f0b43f7..b5a099aa3b 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -85,6 +85,8 @@ namespace osu.Game.Configuration Set(OsuSetting.IncreaseFirstObjectVisibility, true); + Set(OsuSetting.IncreaseFirstObjectGrowVisibility, true); + // Update Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer); @@ -158,6 +160,7 @@ namespace osu.Game.Configuration BeatmapSkins, BeatmapHitsounds, IncreaseFirstObjectVisibility, + IncreaseFirstObjectGrowVisibility, ScoreDisplayMode, ExternalLinkWarning, Scaling, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs index 2cf14f5aff..538b2b2761 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs @@ -20,6 +20,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay LabelText = "Increase visibility of first object with \"Hidden\" mod", Bindable = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility) }, + new SettingsCheckbox + { + LabelText = "Increase visibility of first object with \"Grow\" mod", + Bindable = config.GetBindable(OsuSetting.IncreaseFirstObjectGrowVisibility) + }, }; } } From 8ac64b5c16c0d8fe05f680a2226bc1dc37e15a6f Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Sat, 8 Jun 2019 01:46:05 +0700 Subject: [PATCH 0948/2854] Make first object not applying custom state --- osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs index a2da2bbf53..a5df36e9ff 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs @@ -2,8 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; @@ -11,7 +14,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Mods { - internal class OsuModGrow : Mod, IApplicableToDrawableHitObjects + internal class OsuModGrow : Mod, IReadFromConfig, IApplicableToDrawableHitObjects { public override string Name => "Grow"; @@ -25,9 +28,16 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; + protected Bindable IncreaseFirstObjectGrowVisibility = new Bindable(); + + public void ReadFromConfig(OsuConfigManager config) + { + IncreaseFirstObjectGrowVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectGrowVisibility); + } + public void ApplyToDrawableHitObjects(IEnumerable drawables) { - foreach (var drawable in drawables) + foreach (var drawable in drawables.Skip(IncreaseFirstObjectGrowVisibility.Value ? 1 : 0)) { switch (drawable) { From 342e39776aaa5559401e86d2b6d656f01476acab Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 7 Jun 2019 20:59:56 +0200 Subject: [PATCH 0949/2854] Move ChangelogEntries populating logic from constructor to BDL load() to use OsuColour palette +apply review suggestions. --- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 627eb10426..ae5ba3fa4e 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -13,6 +13,7 @@ using System.Text.RegularExpressions; using osu.Game.Graphics.Sprites; using osu.Game.Users; using osuTK.Graphics; +using osu.Framework.Allocation; namespace osu.Game.Overlays.Changelog { @@ -45,8 +46,12 @@ namespace osu.Game.Overlays.Changelog Direction = FillDirection.Vertical, }, }; + } - foreach (var categoryEntries in build.ChangelogEntries.GroupBy(b => b.Category).OrderBy(c => c.Key)) + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + foreach (var categoryEntries in Build.ChangelogEntries.GroupBy(b => b.Category).OrderBy(c => c.Key)) { ChangelogEntries.Add(new OsuSpriteText { @@ -69,19 +74,19 @@ namespace osu.Game.Overlays.Changelog Margin = new MarginPadding { Vertical = 5 }, }; - var entryColor = entry.Major != null && (bool)entry.Major ? OsuColour.FromHex("#fd5") : Color4.White; + var entryColour = entry.Major != null && (bool)entry.Major ? colours.YellowLight : Color4.White; title.AddIcon(FontAwesome.Solid.Check, t => { t.Font = fontSmall; - t.Colour = entryColor; + t.Colour = entryColour; t.Padding = new MarginPadding { Left = -17, Right = 5 }; }); title.AddText(entry.Title, t => { t.Font = fontLarge; - t.Colour = entryColor; + t.Colour = entryColour; }); if (!string.IsNullOrEmpty(entry.Repository)) @@ -89,25 +94,25 @@ namespace osu.Game.Overlays.Changelog title.AddText(" (", t => { t.Font = fontLarge; - t.Colour = entryColor; + t.Colour = entryColour; }); title.AddLink($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", entry.GithubUrl, Online.Chat.LinkAction.External, creationParameters: t => { t.Font = fontLarge; - t.Colour = entryColor; + t.Colour = entryColour; }); title.AddText(")", t => { t.Font = fontLarge; - t.Colour = entryColor; + t.Colour = entryColour; }); } title.AddText(" by ", t => { t.Font = fontMedium; - t.Colour = entryColor; + t.Colour = entryColour; }); if (entry.GithubUser.UserId != null) @@ -118,19 +123,19 @@ namespace osu.Game.Overlays.Changelog }, t => { t.Font = fontMedium; - t.Colour = entryColor; + t.Colour = entryColour; }); else if (entry.GithubUser.GithubUrl != null) title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, Online.Chat.LinkAction.External, null, null, t => { t.Font = fontMedium; - t.Colour = entryColor; + t.Colour = entryColour; }); else title.AddText(entry.GithubUser.DisplayName, t => { t.Font = fontSmall; - t.Colour = entryColor; + t.Colour = entryColour; }); ChangelogEntries.Add(title); From d058f7779308c20c667b796fd490c01317a3aad2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 8 Jun 2019 16:36:48 +0900 Subject: [PATCH 0950/2854] Update resources for iOS --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index 8886184a2e..3a5090d968 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -104,7 +104,7 @@ - + From e8c73f3127d953a0ca188f0e53b79066e847eb07 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 8 Jun 2019 09:45:34 +0200 Subject: [PATCH 0951/2854] Make APIChangelogEntry.Major a non-nullable property --- osu.Game/Online/API/Requests/Responses/APIChangelogEntry.cs | 2 +- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelogEntry.cs b/osu.Game/Online/API/Requests/Responses/APIChangelogEntry.cs index abaff9b7ae..140e228acd 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelogEntry.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelogEntry.cs @@ -36,7 +36,7 @@ namespace osu.Game.Online.API.Requests.Responses public string MessageHtml { get; set; } [JsonProperty("major")] - public bool? Major { get; set; } + public bool Major { get; set; } [JsonProperty("created_at")] public DateTimeOffset? CreatedAt { get; set; } diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index ae5ba3fa4e..3d145af562 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -74,7 +74,7 @@ namespace osu.Game.Overlays.Changelog Margin = new MarginPadding { Vertical = 5 }, }; - var entryColour = entry.Major != null && (bool)entry.Major ? colours.YellowLight : Color4.White; + var entryColour = entry.Major ? colours.YellowLight : Color4.White; title.AddIcon(FontAwesome.Solid.Check, t => { From d1d3cfa991b6a3c9c64b5b82128ecebfe7180fa4 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Sat, 8 Jun 2019 11:55:52 +0300 Subject: [PATCH 0952/2854] Remove ruleset selector from the user overlay --- osu.Game/Overlays/UserProfileOverlay.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index ec81193896..70ce83806e 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -11,7 +11,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Profile; -using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Overlays.Profile.Sections; using osu.Game.Users; using osuTK; @@ -26,7 +25,6 @@ namespace osu.Game.Overlays protected ProfileHeader Header; private SectionsContainer sectionsContainer; private ProfileTabControl tabs; - private ProfileRulesetSelector rulesetSelector; public const float CONTENT_X_MARGIN = 70; @@ -121,15 +119,6 @@ namespace osu.Game.Overlays } sectionsContainer.ScrollToTop(); - - Header.Add(rulesetSelector = new ProfileRulesetSelector - { - Alpha = 0, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Y = 100, - Margin = new MarginPadding { Right = 30 }, - }); } private void userLoadComplete(User user) @@ -151,11 +140,6 @@ namespace osu.Game.Overlays } } } - - rulesetSelector.SetDefaultGamemode(user.PlayMode ?? "osu"); - rulesetSelector.SelectDefaultGamemode(); - - rulesetSelector.FadeInFromZero(100, Easing.OutQuint); } private class ProfileTabControl : PageTabControl From 06dfa42a5ad09ca8ca31f128d433e56a0dc900cb Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Sat, 8 Jun 2019 18:27:40 +0300 Subject: [PATCH 0953/2854] Refactor --- .../Overlays/Toolbar/ToolbarRulesetButton.cs | 73 +++++++------ .../Toolbar/ToolbarRulesetSelector.cs | 101 +++++++++--------- 2 files changed, 90 insertions(+), 84 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs index 87b18ba9f4..efb540cd45 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs @@ -2,57 +2,68 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.UserInterface; using osu.Game.Rulesets; using osuTK.Graphics; +using osu.Framework.Graphics; namespace osu.Game.Overlays.Toolbar { - public class ToolbarRulesetButton : ToolbarButton + public class ToolbarRulesetButton : TabItem { - private RulesetInfo ruleset; + private readonly DrawableRuleset ruleset; - public RulesetInfo Ruleset + public ToolbarRulesetButton(RulesetInfo value) + : base(value) { - get => ruleset; - set + AutoSizeAxes = Axes.X; + RelativeSizeAxes = Axes.Y; + Child = ruleset = new DrawableRuleset { - ruleset = value; + Active = false, + }; - var rInstance = ruleset.CreateInstance(); + var rInstance = value.CreateInstance(); - TooltipMain = rInstance.Description; - TooltipSub = $"Play some {rInstance.Description}"; - SetIcon(rInstance.CreateIcon()); - } + ruleset.TooltipMain = rInstance.Description; + ruleset.TooltipSub = $"Play some {rInstance.Description}"; + ruleset.SetIcon(rInstance.CreateIcon()); } - public bool Active + protected override void OnActivated() => ruleset.Active = true; + + protected override void OnDeactivated() => ruleset.Active = false; + + private class DrawableRuleset : ToolbarButton { - set + public bool Active { - if (value) + set { - IconContainer.Colour = Color4.White; - IconContainer.EdgeEffect = new EdgeEffectParameters + if (value) { - Type = EdgeEffectType.Glow, - Colour = new Color4(255, 194, 224, 100), - Radius = 15, - Roundness = 15, - }; - } - else - { - IconContainer.Colour = new Color4(255, 194, 224, 255); - IconContainer.EdgeEffect = new EdgeEffectParameters(); + IconContainer.Colour = Color4.White; + IconContainer.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = new Color4(255, 194, 224, 100), + Radius = 15, + Roundness = 15, + }; + } + else + { + IconContainer.Colour = new Color4(255, 194, 224, 255); + IconContainer.EdgeEffect = new EdgeEffectParameters(); + } } } - } - protected override void LoadComplete() - { - base.LoadComplete(); - IconContainer.Scale *= 1.4f; + protected override void LoadComplete() + { + base.LoadComplete(); + IconContainer.Scale *= 1.4f; + } } } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index 90412ec1d1..abea9b217d 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -1,53 +1,54 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Caching; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osuTK; -using osuTK.Input; using osuTK.Graphics; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; using osu.Game.Rulesets; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Bindables; +using osu.Framework.Input.Events; +using osuTK.Input; +using System.Linq; namespace osu.Game.Overlays.Toolbar { - public class ToolbarRulesetSelector : Container + public class ToolbarRulesetSelector : TabControl { private const float padding = 10; - - private readonly FillFlowContainer modeButtons; private readonly Drawable modeButtonLine; - private ToolbarRulesetButton activeButton; - private RulesetStore rulesets; - private readonly Bindable ruleset = new Bindable(); + + public override bool HandleNonPositionalInput => !Current.Disabled && base.HandleNonPositionalInput; + public override bool HandlePositionalInput => !Current.Disabled && base.HandlePositionalInput; + + public override bool PropagatePositionalInputSubTree => !Current.Disabled && base.PropagatePositionalInputSubTree; + + private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300); + + protected override Dropdown CreateDropdown() => null; + + protected override TabItem CreateTabItem(RulesetInfo value) => new ToolbarRulesetButton(value); public ToolbarRulesetSelector() { RelativeSizeAxes = Axes.Y; AutoSizeAxes = Axes.X; - Children = new[] + AddRangeInternal(new Drawable[] { - new OpaqueBackground(), - modeButtons = new FillFlowContainer + new OpaqueBackground { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Direction = FillDirection.Horizontal, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Padding = new MarginPadding { Left = padding, Right = padding }, + Depth = 1, }, modeButtonLine = new Container { - Size = new Vector2(padding * 2 + ToolbarButton.WIDTH, 3), + Size = new Vector2(padding* 2 + ToolbarButton.WIDTH, 3), Anchor = Anchor.BottomLeft, Origin = Anchor.TopLeft, Masking = true, @@ -58,17 +59,22 @@ namespace osu.Game.Overlays.Toolbar Radius = 15, Roundness = 15, }, - Children = new[] + Child = new Box { - new Box - { - RelativeSizeAxes = Axes.Both, - } + RelativeSizeAxes = Axes.Both, } } - }; + }); } + protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Direction = FillDirection.Horizontal, + Padding = new MarginPadding { Left = padding, Right = padding }, + }; + [BackgroundDependencyLoader] private void load(RulesetStore rulesets, Bindable parentRuleset) { @@ -76,16 +82,13 @@ namespace osu.Game.Overlays.Toolbar foreach (var r in rulesets.AvailableRulesets) { - modeButtons.Add(new ToolbarRulesetButton - { - Ruleset = r, - Action = delegate { ruleset.Value = r; } - }); + AddItem(r); } - ruleset.ValueChanged += rulesetChanged; - ruleset.DisabledChanged += disabledChanged; - ruleset.BindTo(parentRuleset); + Current.BindTo(parentRuleset); + Current.Disabled = false; + Current.DisabledChanged += disabledChanged; + Current.BindValueChanged(rulesetChanged); } protected override bool OnKeyDown(KeyDownEvent e) @@ -98,43 +101,35 @@ namespace osu.Game.Overlays.Toolbar RulesetInfo found = rulesets.AvailableRulesets.Skip(requested).FirstOrDefault(); if (found != null) - ruleset.Value = found; + Current.Value = found; return true; } return false; } - public override bool HandleNonPositionalInput => !ruleset.Disabled && base.HandleNonPositionalInput; - public override bool HandlePositionalInput => !ruleset.Disabled && base.HandlePositionalInput; - - public override bool PropagatePositionalInputSubTree => !ruleset.Disabled && base.PropagatePositionalInputSubTree; - - private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300); + private readonly Cached activeMode = new Cached(); private void rulesetChanged(ValueChangedEvent e) { - foreach (ToolbarRulesetButton m in modeButtons.Children.Cast()) - { - bool isActive = m.Ruleset.ID == e.NewValue.ID; - m.Active = isActive; - if (isActive) - activeButton = m; - } - activeMode.Invalidate(); } - private Cached activeMode = new Cached(); - protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); if (!activeMode.IsValid) { - modeButtonLine.MoveToX(activeButton.DrawPosition.X, 200, Easing.OutQuint); - activeMode.Validate(); + foreach (TabItem tabItem in TabContainer) + { + if (tabItem.Value == Current.Value) + { + modeButtonLine.MoveToX(tabItem.DrawPosition.X, 200, Easing.OutQuint); + activeMode.Validate(); + return; + } + } } } } From 62eadf21c96964fa52f748d8b3a128f6c282c000 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Sat, 8 Jun 2019 18:38:52 +0300 Subject: [PATCH 0954/2854] Remove useless line --- osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index abea9b217d..8590c9fbc0 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -86,7 +86,6 @@ namespace osu.Game.Overlays.Toolbar } Current.BindTo(parentRuleset); - Current.Disabled = false; Current.DisabledChanged += disabledChanged; Current.BindValueChanged(rulesetChanged); } From 383b937a7e44b2eac1f20ba7b1b1a6cfce0bfae2 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 8 Jun 2019 19:10:00 +0300 Subject: [PATCH 0955/2854] Rename F grade to D --- osu.Game/Scoring/ScoreRank.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Scoring/ScoreRank.cs b/osu.Game/Scoring/ScoreRank.cs index a93d015f1b..d3479e21aa 100644 --- a/osu.Game/Scoring/ScoreRank.cs +++ b/osu.Game/Scoring/ScoreRank.cs @@ -7,10 +7,10 @@ namespace osu.Game.Scoring { public enum ScoreRank { - [Description(@"F")] + [Description(@"D")] F, - [Description(@"F")] + [Description(@"D")] D, [Description(@"C")] From 17362a368e2ab792c43e05cc992d6b729247b62e Mon Sep 17 00:00:00 2001 From: Ludde <48018938+yousef157@users.noreply.github.com> Date: Sat, 8 Jun 2019 20:10:52 +0400 Subject: [PATCH 0956/2854] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 91ea34e999..04f133fd56 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ If you are not interested in developing the game, you can consume our [binary re | ------------- | ------------- | - **Linux** users are recommended to self-compile until we have official deployment in place. -- **iOS** users can join the [TestFlight beta program](https://t.co/xQJmHkfC18) (note that due to high demand this is reulgarly full). +- **iOS** users can join the [TestFlight beta program](https://t.co/xQJmHkfC18) (note that due to high demand this is regularly full). - **Android** users can self-compile, and expect a public beta soon. If your platform is not listed above, there is still a chance you can manually build it by following the instructions below. From dfbc6528031fc340a9a718699db236172aeac207 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 8 Jun 2019 19:32:26 +0300 Subject: [PATCH 0957/2854] Use ScoreRank.D instead of F --- osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 89da0fc254..cbcf3e6160 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Text = "#1", Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, italics: true) }, - rank = new DrawableRank(ScoreRank.F) + rank = new DrawableRank(ScoreRank.D) { Anchor = Anchor.Centre, Origin = Anchor.Centre, From d3ff2c6dd5bbc9a14653e9fa6a72bb0093f6d519 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 8 Jun 2019 19:34:03 +0300 Subject: [PATCH 0958/2854] Use ScoreRank.D instead of F --- osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs index 3d75470328..9365e2c5b1 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs @@ -188,7 +188,7 @@ namespace osu.Game.Tests.Visual.SongSelect }, new ScoreInfo { - Rank = ScoreRank.F, + Rank = ScoreRank.D, Accuracy = 0.6025, MaxCombo = 244, TotalScore = 1707827, @@ -206,7 +206,7 @@ namespace osu.Game.Tests.Visual.SongSelect }, new ScoreInfo { - Rank = ScoreRank.F, + Rank = ScoreRank.D, Accuracy = 0.5140, MaxCombo = 244, TotalScore = 1707827, @@ -224,7 +224,7 @@ namespace osu.Game.Tests.Visual.SongSelect }, new ScoreInfo { - Rank = ScoreRank.F, + Rank = ScoreRank.D, Accuracy = 0.4222, MaxCombo = 244, TotalScore = 1707827, From fc8644a73e0a8cf37e90ec199f2c4b5ac2b785c5 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 8 Jun 2019 19:34:52 +0300 Subject: [PATCH 0959/2854] Use ScoreRank.D instead of F --- osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index 6815018be6..2f88a4b01d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs @@ -157,7 +157,7 @@ namespace osu.Game.Tests.Visual.Online FlagName = @"TH", }, }, - Rank = ScoreRank.F, + Rank = ScoreRank.D, PP = 160, MaxCombo = 1234, TotalScore = 123456, From a2b9dba92cce57e04a956efd1f4baa7906488373 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 8 Jun 2019 19:35:29 +0300 Subject: [PATCH 0960/2854] Remove ScoreRank.F --- osu.Game/Scoring/ScoreRank.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Scoring/ScoreRank.cs b/osu.Game/Scoring/ScoreRank.cs index d3479e21aa..696d493830 100644 --- a/osu.Game/Scoring/ScoreRank.cs +++ b/osu.Game/Scoring/ScoreRank.cs @@ -7,9 +7,6 @@ namespace osu.Game.Scoring { public enum ScoreRank { - [Description(@"D")] - F, - [Description(@"D")] D, From afc3a089536d72fe7f0b357866caa31dafb3c8fc Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Sun, 9 Jun 2019 13:11:40 +0700 Subject: [PATCH 0961/2854] Use existing setting instead Now it read IncreaseFirstObjectVisibility bindable instead --- osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs | 6 +++--- osu.Game/Configuration/OsuConfigManager.cs | 3 --- .../Overlays/Settings/Sections/Gameplay/ModsSettings.cs | 5 ----- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs index a5df36e9ff..3d64bb4ce8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs @@ -28,16 +28,16 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; - protected Bindable IncreaseFirstObjectGrowVisibility = new Bindable(); + protected Bindable IncreaseFirstObjectVisibility = new Bindable(); public void ReadFromConfig(OsuConfigManager config) { - IncreaseFirstObjectGrowVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectGrowVisibility); + IncreaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); } public void ApplyToDrawableHitObjects(IEnumerable drawables) { - foreach (var drawable in drawables.Skip(IncreaseFirstObjectGrowVisibility.Value ? 1 : 0)) + foreach (var drawable in drawables.Skip(IncreaseFirstObjectVisibility.Value ? 1 : 0)) { switch (drawable) { diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index b5a099aa3b..795f0b43f7 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -85,8 +85,6 @@ namespace osu.Game.Configuration Set(OsuSetting.IncreaseFirstObjectVisibility, true); - Set(OsuSetting.IncreaseFirstObjectGrowVisibility, true); - // Update Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer); @@ -160,7 +158,6 @@ namespace osu.Game.Configuration BeatmapSkins, BeatmapHitsounds, IncreaseFirstObjectVisibility, - IncreaseFirstObjectGrowVisibility, ScoreDisplayMode, ExternalLinkWarning, Scaling, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs index 538b2b2761..2cf14f5aff 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs @@ -20,11 +20,6 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay LabelText = "Increase visibility of first object with \"Hidden\" mod", Bindable = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility) }, - new SettingsCheckbox - { - LabelText = "Increase visibility of first object with \"Grow\" mod", - Bindable = config.GetBindable(OsuSetting.IncreaseFirstObjectGrowVisibility) - }, }; } } From 49193a2703edb7c5d35b04d0c206031e2a2708f9 Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Sun, 9 Jun 2019 13:12:41 +0700 Subject: [PATCH 0962/2854] Rename the setting label --- osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs index 2cf14f5aff..2c6b2663c6 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { new SettingsCheckbox { - LabelText = "Increase visibility of first object with \"Hidden\" mod", + LabelText = "Increase visibility of first object when visual impairment mods are enabled", Bindable = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility) }, }; From 8cdcf251b5aca691d3af18b459cbb12f5c1704b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 9 Jun 2019 16:30:04 +0900 Subject: [PATCH 0963/2854] Make local bindable private --- osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs index 3d64bb4ce8..8072dc09c1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs @@ -28,16 +28,16 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; - protected Bindable IncreaseFirstObjectVisibility = new Bindable(); + private Bindable increaseFirstObjectVisibility = new Bindable(); public void ReadFromConfig(OsuConfigManager config) { - IncreaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); + increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); } public void ApplyToDrawableHitObjects(IEnumerable drawables) { - foreach (var drawable in drawables.Skip(IncreaseFirstObjectVisibility.Value ? 1 : 0)) + foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0)) { switch (drawable) { From d500f3605ef8c71f6a40256b86fe6a5a618d0558 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 9 Jun 2019 16:47:48 +0900 Subject: [PATCH 0964/2854] Fix checkboxes with long labels overlapping nub --- .../Graphics/UserInterface/OsuCheckbox.cs | 22 ++++++++++--------- .../Overlays/Settings/SettingsCheckbox.cs | 1 - 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index cd1147e3d3..dd126d8518 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -6,10 +6,9 @@ using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; -using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.Containers; using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface @@ -33,7 +32,6 @@ namespace osu.Game.Graphics.UserInterface public string LabelText { - get => labelSpriteText?.Text; set { if (labelSpriteText != null) @@ -53,7 +51,7 @@ namespace osu.Game.Graphics.UserInterface protected readonly Nub Nub; - private readonly SpriteText labelSpriteText; + private readonly OsuTextFlowContainer labelSpriteText; private SampleChannel sampleChecked; private SampleChannel sampleUnchecked; @@ -62,24 +60,28 @@ namespace osu.Game.Graphics.UserInterface AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; + const float nub_padding = 5; + Children = new Drawable[] { - labelSpriteText = new OsuSpriteText(), + labelSpriteText = new OsuTextFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Padding = new MarginPadding { Right = Nub.EXPANDED_SIZE + nub_padding } + }, Nub = new Nub { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - Margin = new MarginPadding { Right = 5 }, + Margin = new MarginPadding { Right = nub_padding }, }, new HoverClickSounds() }; Nub.Current.BindTo(Current); - Current.DisabledChanged += disabled => - { - labelSpriteText.Alpha = Nub.Alpha = disabled ? 0.3f : 1; - }; + Current.DisabledChanged += disabled => { labelSpriteText.Alpha = Nub.Alpha = disabled ? 0.3f : 1; }; } protected override void LoadComplete() diff --git a/osu.Game/Overlays/Settings/SettingsCheckbox.cs b/osu.Game/Overlays/Settings/SettingsCheckbox.cs index 46c23c3bbf..a1501d8015 100644 --- a/osu.Game/Overlays/Settings/SettingsCheckbox.cs +++ b/osu.Game/Overlays/Settings/SettingsCheckbox.cs @@ -14,7 +14,6 @@ namespace osu.Game.Overlays.Settings public override string LabelText { - get => checkbox.LabelText; set => checkbox.LabelText = value; } } From cd89633dee31f80d9181a00467b00159ae20cf76 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 9 Jun 2019 17:07:23 +0900 Subject: [PATCH 0965/2854] Rename variable to match --- osu.Game/Graphics/UserInterface/OsuCheckbox.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index dd126d8518..5d41075725 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -34,24 +34,24 @@ namespace osu.Game.Graphics.UserInterface { set { - if (labelSpriteText != null) - labelSpriteText.Text = value; + if (labelText != null) + labelText.Text = value; } } public MarginPadding LabelPadding { - get => labelSpriteText?.Padding ?? new MarginPadding(); + get => labelText?.Padding ?? new MarginPadding(); set { - if (labelSpriteText != null) - labelSpriteText.Padding = value; + if (labelText != null) + labelText.Padding = value; } } protected readonly Nub Nub; - private readonly OsuTextFlowContainer labelSpriteText; + private readonly OsuTextFlowContainer labelText; private SampleChannel sampleChecked; private SampleChannel sampleUnchecked; @@ -64,7 +64,7 @@ namespace osu.Game.Graphics.UserInterface Children = new Drawable[] { - labelSpriteText = new OsuTextFlowContainer + labelText = new OsuTextFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, @@ -81,7 +81,7 @@ namespace osu.Game.Graphics.UserInterface Nub.Current.BindTo(Current); - Current.DisabledChanged += disabled => { labelSpriteText.Alpha = Nub.Alpha = disabled ? 0.3f : 1; }; + Current.DisabledChanged += disabled => { labelText.Alpha = Nub.Alpha = disabled ? 0.3f : 1; }; } protected override void LoadComplete() From 807d434be03898334671c6fd382cda252633d6c8 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Sun, 9 Jun 2019 17:52:02 +0930 Subject: [PATCH 0966/2854] Access WindowModes via IBindableList --- osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 58d2eb1f1e..36c4fb5252 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private Bindable scalingMode; private Bindable sizeFullscreen; - private readonly BindableList windowModes = new BindableList(); + private readonly IBindableList windowModes = new BindableList(); private OsuGameBase game; private SettingsDropdown resolutionDropdown; From a5007b94dbd1b66d97d461c830fb11ef6f784764 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 9 Jun 2019 22:29:00 +0900 Subject: [PATCH 0967/2854] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 654c62e1d8..eeb1f2bee3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,7 +14,7 @@ - + From d964f6ba9e0e166255fdedb1726472ef2e1bbd24 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 00:56:35 +0900 Subject: [PATCH 0968/2854] Tween track frequency on pause --- osu.Game/Screens/Play/GameplayClockContainer.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index c151e598f7..4b35e90f68 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -69,6 +69,7 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both; sourceClock = (IAdjustableClock)beatmap.Track ?? new StopwatchClock(); + (sourceClock as IAdjustableAudioComponent)?.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; @@ -85,8 +86,16 @@ namespace osu.Game.Screens.Play GameplayClock.IsPaused.BindTo(IsPaused); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + (sourceClock as IAdjustableAudioComponent)?.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); + } + private double totalOffset => userOffsetClock.Offset + platformOffsetClock.Offset; + private readonly BindableDouble pauseFreqAdjust = new BindableDouble(1); + [BackgroundDependencyLoader] private void load(OsuConfigManager config) { @@ -122,6 +131,8 @@ namespace osu.Game.Screens.Play Seek(GameplayClock.CurrentTime); adjustableClock.Start(); IsPaused.Value = false; + + this.TransformBindableTo(pauseFreqAdjust, 1, 200, Easing.In); } /// @@ -143,7 +154,8 @@ namespace osu.Game.Screens.Play public void Stop() { - adjustableClock.Stop(); + this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => { adjustableClock.Stop(); }); + IsPaused.Value = true; } From 59b624d4ba4a8cf89b068e5abcf4cec2a38a02b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 01:08:39 +0900 Subject: [PATCH 0969/2854] Fix test regression --- osu.Game.Tests/Visual/Gameplay/TestScenePause.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index 12e91df77c..0f5ee66f50 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -189,7 +189,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("pause overlay " + (isShown ? "shown" : "hidden"), () => Player.PauseOverlayVisible == isShown); private void confirmClockRunning(bool isRunning) => - AddAssert("clock " + (isRunning ? "running" : "stopped"), () => Player.GameplayClockContainer.GameplayClock.IsRunning == isRunning); + AddUntilStep("clock " + (isRunning ? "running" : "stopped"), () => Player.GameplayClockContainer.GameplayClock.IsRunning == isRunning); protected override bool AllowFail => true; From 5c2ea0b1a719348b5a1ae0bd251d3aecb45fa250 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 01:14:46 +0900 Subject: [PATCH 0970/2854] Move dispose to end of file --- osu.Game/Screens/Play/GameplayClockContainer.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 4b35e90f68..6a03271b86 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -86,12 +86,6 @@ namespace osu.Game.Screens.Play GameplayClock.IsPaused.BindTo(IsPaused); } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - (sourceClock as IAdjustableAudioComponent)?.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); - } - private double totalOffset => userOffsetClock.Offset + platformOffsetClock.Offset; private readonly BindableDouble pauseFreqAdjust = new BindableDouble(1); @@ -154,7 +148,7 @@ namespace osu.Game.Screens.Play public void Stop() { - this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => { adjustableClock.Stop(); }); + this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => adjustableClock.Stop()); IsPaused.Value = true; } @@ -187,5 +181,11 @@ namespace osu.Game.Screens.Play foreach (var mod in mods.OfType()) mod.ApplyToClock(sourceClock); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + (sourceClock as IAdjustableAudioComponent)?.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); + } } } From 4f6978f2aa59c7a95c9b7706ad42f6fe6c258643 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 9 Jun 2019 20:01:19 +0200 Subject: [PATCH 0971/2854] Apply review suggestions. --- osu.Game/Screens/OsuScreen.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 3f98595d0e..3b5e0fd0d6 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -64,17 +64,17 @@ namespace osu.Game.Screens /// protected UserActivity ScreenActivity { - get => activity; + get => screenActivity; set { - if (value == activity) return; + if (value == screenActivity) return; - activity = value; - setUserActivity(activity); + screenActivity = value; + setUserActivity(screenActivity); } } - private UserActivity activity; + private UserActivity screenActivity; /// /// Whether to disallow changes to game-wise Beatmap/Ruleset bindables for this screen (and all children). @@ -122,11 +122,11 @@ namespace osu.Game.Screens Anchor = Anchor.Centre; Origin = Anchor.Centre; - activity = null; + screenActivity = null; } [BackgroundDependencyLoader(true)] - private void load(OsuGame osu, AudioManager audio, IAPIProvider provider) + private void load(OsuGame osu, AudioManager audio) { sampleExit = audio.Samples.Get(@"UI/screen-back"); } @@ -152,7 +152,7 @@ namespace osu.Game.Screens sampleExit?.Play(); applyArrivingDefaults(true); - setUserActivity(activity); + setUserActivity(screenActivity); base.OnResuming(last); } From 9b8540d8180b9485c13f3043226f1840c07e8df6 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Mon, 10 Jun 2019 03:35:00 +0300 Subject: [PATCH 0972/2854] Add a testcase --- .../TestSceneToolbarRulesetSelector.cs | 35 +++++++++++++++++++ .../Toolbar/ToolbarRulesetSelector.cs | 4 +-- 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs new file mode 100644 index 0000000000..1a0e712337 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs @@ -0,0 +1,35 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays.Toolbar; +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneToolbarRulesetSelector : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ToolbarRulesetSelector), + typeof(ToolbarRulesetButton), + }; + + public TestSceneToolbarRulesetSelector() + { + ToolbarRulesetSelector selector; + + Add(new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.X, + Height = Toolbar.HEIGHT, + Child = selector = new ToolbarRulesetSelector() + + }); + } + } +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index 8590c9fbc0..f8ac610a3a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Toolbar RelativeSizeAxes = Axes.Y; AutoSizeAxes = Axes.X; - AddRangeInternal(new Drawable[] + AddRangeInternal(new[] { new OpaqueBackground { @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Toolbar }, modeButtonLine = new Container { - Size = new Vector2(padding* 2 + ToolbarButton.WIDTH, 3), + Size = new Vector2(padding * 2 + ToolbarButton.WIDTH, 3), Anchor = Anchor.BottomLeft, Origin = Anchor.TopLeft, Masking = true, From ff9dc189286ec6e8f9f48a14b12e1312ccb7839f Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Mon, 10 Jun 2019 03:54:15 +0300 Subject: [PATCH 0973/2854] TestCaseImprovements --- .../Visual/UserInterface/TestSceneToolbarRulesetSelector.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs index 1a0e712337..7d0491aa60 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs @@ -6,6 +6,8 @@ using osu.Game.Overlays.Toolbar; using System; using System.Collections.Generic; using osu.Framework.Graphics; +using System.Linq; +using osu.Framework.MathUtils; namespace osu.Game.Tests.Visual.UserInterface { @@ -28,7 +30,11 @@ namespace osu.Game.Tests.Visual.UserInterface AutoSizeAxes = Axes.X, Height = Toolbar.HEIGHT, Child = selector = new ToolbarRulesetSelector() + }); + AddStep("Select random", () => + { + selector.Current.Value = selector.Items.ElementAt(RNG.Next(selector.Items.Count())); }); } } From ec8c09dd39f86c21449638f994df3ae6449e19c2 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Mon, 10 Jun 2019 04:36:34 +0300 Subject: [PATCH 0974/2854] Fix unability to mannualy switch ruleset --- osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs index efb540cd45..defe1da5bf 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Game.Rulesets; using osuTK.Graphics; using osu.Framework.Graphics; +using osu.Framework.Input.Events; namespace osu.Game.Overlays.Toolbar { @@ -59,6 +60,12 @@ namespace osu.Game.Overlays.Toolbar } } + protected override bool OnClick(ClickEvent e) + { + Parent.Click(); + return base.OnClick(e); + } + protected override void LoadComplete() { base.LoadComplete(); From 0ebd29c8510534a973a98b3e14c86ccccfb0e0cd Mon Sep 17 00:00:00 2001 From: Albert Tang Date: Sun, 9 Jun 2019 21:15:05 -0500 Subject: [PATCH 0975/2854] Add @miterosan's suggestions --- osu.Android/Properties/AndroidManifest.xml | 14 +++++++------- .../MainActivity.cs | 3 +-- .../MainActivity.cs | 3 +-- .../MainActivity.cs | 3 +-- .../MainActivity.cs | 3 +-- osu.Game.Tests.Android/MainActivity.cs | 3 +-- 6 files changed, 12 insertions(+), 17 deletions(-) diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml index 549ba9f46a..6f77560e01 100644 --- a/osu.Android/Properties/AndroidManifest.xml +++ b/osu.Android/Properties/AndroidManifest.xml @@ -1,10 +1,10 @@  - - - - - - - + + + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs index 8430a88f15..d918305f3d 100644 --- a/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs +++ b/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs @@ -11,7 +11,6 @@ namespace osu.Game.Rulesets.Catch.Tests.Android [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] public class MainActivity : AndroidGameActivity { - protected override Framework.Game CreateGame() - => new OsuTestBrowser(); + protected override Framework.Game CreateGame() => new OsuTestBrowser(); } } diff --git a/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs index 0f1d6756bc..0a3f05ae54 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs +++ b/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs @@ -11,7 +11,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Android [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] public class MainActivity : AndroidGameActivity { - protected override Framework.Game CreateGame() - => new OsuTestBrowser(); + protected override Framework.Game CreateGame() => new OsuTestBrowser(); } } diff --git a/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs index d6f2ec546d..e6c508d99e 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs +++ b/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs @@ -11,7 +11,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Android [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] public class MainActivity : AndroidGameActivity { - protected override Framework.Game CreateGame() - => new OsuTestBrowser(); + protected override Framework.Game CreateGame() => new OsuTestBrowser(); } } diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs index cc92ad7080..1128a0d37f 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs +++ b/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs @@ -11,7 +11,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Android [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] public class MainActivity : AndroidGameActivity { - protected override Framework.Game CreateGame() - => new OsuTestBrowser(); + protected override Framework.Game CreateGame() => new OsuTestBrowser(); } } diff --git a/osu.Game.Tests.Android/MainActivity.cs b/osu.Game.Tests.Android/MainActivity.cs index 6f68c372ef..0695c8e37b 100644 --- a/osu.Game.Tests.Android/MainActivity.cs +++ b/osu.Game.Tests.Android/MainActivity.cs @@ -10,7 +10,6 @@ namespace osu.Game.Tests.Android [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = true)] public class MainActivity : AndroidGameActivity { - protected override Framework.Game CreateGame() - => new OsuTestBrowser(); + protected override Framework.Game CreateGame() => new OsuTestBrowser(); } } From f090e292c974c2147f7f34b3cc79a5461e7df817 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 May 2019 18:59:21 +0900 Subject: [PATCH 0976/2854] Move ArchiveModelManager import process to async flow --- .../Beatmaps/IO/ImportBeatmapTest.cs | 50 +++--- osu.Game.Tests/Scores/IO/ImportScoreTest.cs | 30 ++-- .../TestSceneBackgroundScreenBeatmap.cs | 2 +- ...tSceneUpdateableBeatmapBackgroundSprite.cs | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 5 + osu.Game/Beatmaps/BeatmapManager.cs | 163 +++++++++++++----- osu.Game/Database/ArchiveModelManager.cs | 158 +++++++++-------- osu.Game/Database/ICanAcceptFiles.cs | 4 +- osu.Game/IPC/ArchiveImportIPCChannel.cs | 2 +- osu.Game/OsuGameBase.cs | 5 +- .../Notifications/ProgressNotification.cs | 7 + osu.Game/Screens/Menu/Intro.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Skinning/SkinManager.cs | 6 +- 14 files changed, 273 insertions(+), 165 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index f020c2a805..4c9260f640 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -21,14 +21,14 @@ namespace osu.Game.Tests.Beatmaps.IO public class ImportBeatmapTest { [Test] - public void TestImportWhenClosed() + public async Task TestImportWhenClosed() { //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenClosed")) { try { - LoadOszIntoOsu(loadOsu(host)); + await LoadOszIntoOsu(loadOsu(host)); } finally { @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Beatmaps.IO } [Test] - public void TestImportThenDelete() + public async Task TestImportThenDelete() { //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDelete")) @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Beatmaps.IO { var osu = loadOsu(host); - var imported = LoadOszIntoOsu(osu); + var imported = await LoadOszIntoOsu(osu); deleteBeatmapSet(imported, osu); } @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Beatmaps.IO } [Test] - public void TestImportThenImport() + public async Task TestImportThenImport() { //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImport")) @@ -68,8 +68,8 @@ namespace osu.Game.Tests.Beatmaps.IO { var osu = loadOsu(host); - var imported = LoadOszIntoOsu(osu); - var importedSecondTime = LoadOszIntoOsu(osu); + var imported = await LoadOszIntoOsu(osu); + var importedSecondTime = await LoadOszIntoOsu(osu); // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash. Assert.IsTrue(imported.ID == importedSecondTime.ID); @@ -88,7 +88,7 @@ namespace osu.Game.Tests.Beatmaps.IO } [Test] - public void TestRollbackOnFailure() + public async Task TestRollbackOnFailure() { //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestRollbackOnFailure")) @@ -104,7 +104,7 @@ namespace osu.Game.Tests.Beatmaps.IO manager.ItemAdded += (_, __) => fireCount++; manager.ItemRemoved += _ => fireCount++; - var imported = LoadOszIntoOsu(osu); + var imported = await LoadOszIntoOsu(osu); Assert.AreEqual(0, fireCount -= 1); @@ -132,7 +132,7 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.AreEqual(12, manager.QueryBeatmaps(_ => true).ToList().Count); // this will trigger purging of the existing beatmap (online set id match) but should rollback due to broken osu. - manager.Import(breakTemp); + await manager.Import(breakTemp); // no events should be fired in the case of a rollback. Assert.AreEqual(0, fireCount); @@ -149,7 +149,7 @@ namespace osu.Game.Tests.Beatmaps.IO } [Test] - public void TestImportThenImportDifferentHash() + public async Task TestImportThenImportDifferentHash() { //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImportDifferentHash")) @@ -159,12 +159,12 @@ namespace osu.Game.Tests.Beatmaps.IO var osu = loadOsu(host); var manager = osu.Dependencies.Get(); - var imported = LoadOszIntoOsu(osu); + var imported = await LoadOszIntoOsu(osu); imported.Hash += "-changed"; manager.Update(imported); - var importedSecondTime = LoadOszIntoOsu(osu); + var importedSecondTime = await LoadOszIntoOsu(osu); Assert.IsTrue(imported.ID != importedSecondTime.ID); Assert.IsTrue(imported.Beatmaps.First().ID < importedSecondTime.Beatmaps.First().ID); @@ -181,7 +181,7 @@ namespace osu.Game.Tests.Beatmaps.IO } [Test] - public void TestImportThenDeleteThenImport() + public async Task TestImportThenDeleteThenImport() { //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDeleteThenImport")) @@ -190,11 +190,11 @@ namespace osu.Game.Tests.Beatmaps.IO { var osu = loadOsu(host); - var imported = LoadOszIntoOsu(osu); + var imported = await LoadOszIntoOsu(osu); deleteBeatmapSet(imported, osu); - var importedSecondTime = LoadOszIntoOsu(osu); + var importedSecondTime = await LoadOszIntoOsu(osu); // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash. Assert.IsTrue(imported.ID == importedSecondTime.ID); @@ -209,7 +209,7 @@ namespace osu.Game.Tests.Beatmaps.IO [TestCase(true)] [TestCase(false)] - public void TestImportThenDeleteThenImportWithOnlineIDMismatch(bool set) + public async Task TestImportThenDeleteThenImportWithOnlineIDMismatch(bool set) { //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"TestImportThenDeleteThenImport-{set}")) @@ -218,7 +218,7 @@ namespace osu.Game.Tests.Beatmaps.IO { var osu = loadOsu(host); - var imported = LoadOszIntoOsu(osu); + var imported = await LoadOszIntoOsu(osu); if (set) imported.OnlineBeatmapSetID = 1234; @@ -229,7 +229,7 @@ namespace osu.Game.Tests.Beatmaps.IO deleteBeatmapSet(imported, osu); - var importedSecondTime = LoadOszIntoOsu(osu); + var importedSecondTime = await LoadOszIntoOsu(osu); // check the newly "imported" beatmap has been reimported due to mismatch (even though hashes matched) Assert.IsTrue(imported.ID != importedSecondTime.ID); @@ -243,7 +243,7 @@ namespace osu.Game.Tests.Beatmaps.IO } [Test] - public void TestImportWithDuplicateBeatmapIDs() + public async Task TestImportWithDuplicateBeatmapIDs() { //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWithDuplicateBeatmapID")) @@ -284,7 +284,7 @@ namespace osu.Game.Tests.Beatmaps.IO var manager = osu.Dependencies.Get(); - var imported = manager.Import(toImport); + var imported = await manager.Import(toImport); Assert.NotNull(imported); Assert.AreEqual(null, imported.Beatmaps[0].OnlineBeatmapID); @@ -330,7 +330,7 @@ namespace osu.Game.Tests.Beatmaps.IO } [Test] - public void TestImportWhenFileOpen() + public async Task TestImportWhenFileOpen() { using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenFileOpen")) { @@ -339,7 +339,7 @@ namespace osu.Game.Tests.Beatmaps.IO var osu = loadOsu(host); var temp = TestResources.GetTestBeatmapForImport(); using (File.OpenRead(temp)) - osu.Dependencies.Get().Import(temp); + await osu.Dependencies.Get().Import(temp); ensureLoaded(osu); File.Delete(temp); Assert.IsFalse(File.Exists(temp), "We likely held a read lock on the file when we shouldn't"); @@ -351,13 +351,13 @@ namespace osu.Game.Tests.Beatmaps.IO } } - public static BeatmapSetInfo LoadOszIntoOsu(OsuGameBase osu, string path = null) + public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null) { var temp = path ?? TestResources.GetTestBeatmapForImport(); var manager = osu.Dependencies.Get(); - manager.Import(temp); + await manager.Import(temp); var imported = manager.GetAllUsableBeatmapSets(); diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs index e39f18c3cd..4babb07213 100644 --- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs +++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs @@ -23,13 +23,13 @@ namespace osu.Game.Tests.Scores.IO public class ImportScoreTest { [Test] - public void TestBasicImport() + public async Task TestBasicImport() { using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestBasicImport")) { try { - var osu = loadOsu(host); + var osu = await loadOsu(host); var toImport = new ScoreInfo { @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Scores.IO OnlineScoreID = 12345, }; - var imported = loadIntoOsu(osu, toImport); + var imported = await loadIntoOsu(osu, toImport); Assert.AreEqual(toImport.Rank, imported.Rank); Assert.AreEqual(toImport.TotalScore, imported.TotalScore); @@ -62,20 +62,20 @@ namespace osu.Game.Tests.Scores.IO } [Test] - public void TestImportMods() + public async Task TestImportMods() { using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportMods")) { try { - var osu = loadOsu(host); + var osu = await loadOsu(host); var toImport = new ScoreInfo { Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, }; - var imported = loadIntoOsu(osu, toImport); + var imported = await loadIntoOsu(osu, toImport); Assert.IsTrue(imported.Mods.Any(m => m is OsuModHardRock)); Assert.IsTrue(imported.Mods.Any(m => m is OsuModDoubleTime)); @@ -88,13 +88,13 @@ namespace osu.Game.Tests.Scores.IO } [Test] - public void TestImportStatistics() + public async Task TestImportStatistics() { using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportStatistics")) { try { - var osu = loadOsu(host); + var osu = await loadOsu(host); var toImport = new ScoreInfo { @@ -105,7 +105,7 @@ namespace osu.Game.Tests.Scores.IO } }; - var imported = loadIntoOsu(osu, toImport); + var imported = await loadIntoOsu(osu, toImport); Assert.AreEqual(toImport.Statistics[HitResult.Perfect], imported.Statistics[HitResult.Perfect]); Assert.AreEqual(toImport.Statistics[HitResult.Miss], imported.Statistics[HitResult.Miss]); @@ -117,7 +117,7 @@ namespace osu.Game.Tests.Scores.IO } } - private ScoreInfo loadIntoOsu(OsuGameBase osu, ScoreInfo score) + private async Task loadIntoOsu(OsuGameBase osu, ScoreInfo score) { var beatmapManager = osu.Dependencies.Get(); @@ -125,20 +125,24 @@ namespace osu.Game.Tests.Scores.IO score.Ruleset = new OsuRuleset().RulesetInfo; var scoreManager = osu.Dependencies.Get(); - scoreManager.Import(score); + await scoreManager.Import(score); return scoreManager.GetAllUsableScores().First(); } - private OsuGameBase loadOsu(GameHost host) + private async Task loadOsu(GameHost host) { var osu = new OsuGameBase(); + +#pragma warning disable 4014 Task.Run(() => host.Run(osu)); +#pragma warning restore 4014 + waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time"); var beatmapFile = TestResources.GetTestBeatmapForImport(); var beatmapManager = osu.Dependencies.Get(); - beatmapManager.Import(beatmapFile); + await beatmapManager.Import(beatmapFile); return osu; } diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs index 7104a420a3..8b941e4633 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs @@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.Background Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, Beatmap.Default)); Dependencies.Cache(new OsuConfigManager(LocalStorage)); - manager.Import(TestResources.GetTestBeatmapForImport()); + manager.Import(TestResources.GetTestBeatmapForImport()).Wait(); Beatmap.SetDefault(); } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs index f59458ef8d..c361598354 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.UserInterface this.api = api; this.rulesets = rulesets; - testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu); + testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu).Result; } [Test] diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 11d70ee7be..403ae16885 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -18,4 +18,9 @@ + + + ..\..\..\..\..\usr\local\share\dotnet\sdk\NuGetFallbackFolder\microsoft.win32.registry\4.5.0\ref\netstandard2.0\Microsoft.Win32.Registry.dll + + \ No newline at end of file diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index b6fe7f88fa..f5ef52a93b 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Linq.Expressions; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using osu.Framework.Audio; @@ -14,6 +16,7 @@ using osu.Framework.Extensions; using osu.Framework.Graphics.Textures; using osu.Framework.Logging; using osu.Framework.Platform; +using osu.Framework.Threading; using osu.Game.Beatmaps.Formats; using osu.Game.Database; using osu.Game.IO.Archives; @@ -72,6 +75,8 @@ namespace osu.Game.Beatmaps private readonly List currentDownloads = new List(); + private readonly BeatmapUpdateQueue updateQueue; + public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null, WorkingBeatmap defaultBeatmap = null) : base(storage, contextFactory, new BeatmapStore(contextFactory), host) @@ -86,9 +91,11 @@ namespace osu.Game.Beatmaps beatmaps = (BeatmapStore)ModelStore; beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b); beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); + + updateQueue = new BeatmapUpdateQueue(api); } - protected override void Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive) + protected override async Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default) { if (archive != null) beatmapSet.Beatmaps = createBeatmapDifficulties(archive); @@ -104,8 +111,7 @@ namespace osu.Game.Beatmaps validateOnlineIds(beatmapSet); - foreach (BeatmapInfo b in beatmapSet.Beatmaps) - fetchAndPopulateOnlineValues(b); + await Task.WhenAll(beatmapSet.Beatmaps.Select(b => updateQueue.Enqueue(new UpdateItem(b, cancellationToken)).Task).ToArray()); } protected override void PreImport(BeatmapSetInfo beatmapSet) @@ -181,10 +187,10 @@ namespace osu.Game.Beatmaps request.Success += filename => { - Task.Factory.StartNew(() => + Task.Factory.StartNew(async () => { // This gets scheduled back to the update thread, but we want the import to run in the background. - Import(downloadNotification, filename); + await Import(downloadNotification, filename); currentDownloads.Remove(request); }, TaskCreationOptions.LongRunning); }; @@ -381,47 +387,6 @@ namespace osu.Game.Beatmaps return beatmapInfos; } - /// - /// Query the API to populate missing values like OnlineBeatmapID / OnlineBeatmapSetID or (Rank-)Status. - /// - /// The beatmap to populate. - /// Whether to re-query if the provided beatmap already has populated values. - /// True if population was successful. - private bool fetchAndPopulateOnlineValues(BeatmapInfo beatmap, bool force = false) - { - if (api?.State != APIState.Online) - return false; - - if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null - && beatmap.Status != BeatmapSetOnlineStatus.None && beatmap.BeatmapSet.Status != BeatmapSetOnlineStatus.None) - return true; - - Logger.Log("Attempting online lookup for the missing values...", LoggingTarget.Database); - - try - { - var req = new GetBeatmapRequest(beatmap); - - req.Perform(api); - - var res = req.Result; - - Logger.Log($"Successfully mapped to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.", LoggingTarget.Database); - - beatmap.Status = res.Status; - beatmap.BeatmapSet.Status = res.BeatmapSet.Status; - beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; - beatmap.OnlineBeatmapID = res.OnlineBeatmapID; - - return true; - } - catch (Exception e) - { - Logger.Log($"Failed ({e})", LoggingTarget.Database); - return false; - } - } - /// /// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation. /// @@ -455,5 +420,111 @@ namespace osu.Game.Beatmaps public override bool IsImportant => false; } } + + private class BeatmapUpdateQueue + { + private readonly IAPIProvider api; + private readonly Queue queue = new Queue(); + + private int activeThreads; + + public BeatmapUpdateQueue(IAPIProvider api) + { + this.api = api; + } + + public UpdateItem Enqueue(UpdateItem item) + { + lock (queue) + { + queue.Enqueue(item); + + if (activeThreads >= 16) + return item; + + new Thread(runWork) { IsBackground = true }.Start(); + activeThreads++; + } + + return item; + } + + private void runWork() + { + while (true) + { + UpdateItem toProcess; + + lock (queue) + { + if (queue.Count == 0) + break; + + toProcess = queue.Dequeue(); + } + + toProcess.PerformUpdate(api); + } + + lock (queue) + activeThreads--; + } + } + + private class UpdateItem + { + public Task Task => tcs.Task; + + private readonly BeatmapInfo beatmap; + private readonly CancellationToken cancellationToken; + + private readonly TaskCompletionSource tcs = new TaskCompletionSource(); + + public UpdateItem(BeatmapInfo beatmap, CancellationToken cancellationToken) + { + this.beatmap = beatmap; + this.cancellationToken = cancellationToken; + } + + public void PerformUpdate(IAPIProvider api) + { + if (cancellationToken.IsCancellationRequested) + { + tcs.SetCanceled(); + return; + } + + if (api?.State != APIState.Online) + { + tcs.SetResult(false); + return; + } + + Logger.Log("Attempting online lookup for the missing values...", LoggingTarget.Database); + + var req = new GetBeatmapRequest(beatmap); + + req.Success += res => + { + Logger.Log($"Successfully mapped to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.", LoggingTarget.Database); + + beatmap.Status = res.Status; + beatmap.BeatmapSet.Status = res.BeatmapSet.Status; + beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; + beatmap.OnlineBeatmapID = res.OnlineBeatmapID; + + tcs.SetResult(true); + }; + + req.Failure += e => + { + Logger.Log($"Failed ({e})", LoggingTarget.Database); + + tcs.SetResult(false); + }; + + req.Perform(api); + } + } } } diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 54dbae9ddc..afc614772e 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -5,14 +5,17 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using osu.Framework; using osu.Framework.Extensions; +using osu.Framework.Extensions.TypeExtensions; using osu.Framework.IO.File; using osu.Framework.Logging; using osu.Framework.Platform; +using osu.Framework.Threading; using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.IPC; @@ -109,8 +112,11 @@ namespace osu.Game.Database a.Invoke(); } + private readonly ThreadedTaskScheduler importScheduler; + protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStoreWithFileIncludes modelStore, IIpcHost importHost = null) { + importScheduler = new ThreadedTaskScheduler(16, $"{GetType().ReadableName()}.Import"); ContextFactory = contextFactory; ModelStore = modelStore; @@ -130,92 +136,84 @@ namespace osu.Game.Database /// This will post notifications tracking progress. /// /// One or more archive locations on disk. - public void Import(params string[] paths) + public async Task Import(params string[] paths) { var notification = new ProgressNotification { State = ProgressNotificationState.Active }; PostNotification?.Invoke(notification); - Import(notification, paths); + + await Import(notification, paths); } - protected void Import(ProgressNotification notification, params string[] paths) + protected async Task Import(ProgressNotification notification, params string[] paths) { notification.Progress = 0; notification.Text = "Import is initialising..."; var term = $"{typeof(TModel).Name.Replace("Info", "").ToLower()}"; - List imported = new List(); + var tasks = new List(); int current = 0; foreach (string path in paths) { - if (notification.State == ProgressNotificationState.Cancelled) - // user requested abort - return; - - try + tasks.Add(Import(path, notification.CancellationToken).ContinueWith(t => { - var text = "Importing "; - - if (path.Length > 1) - text += $"{++current} of {paths.Length} {term}s.."; - else - text += $"{term}.."; - - // only show the filename if it isn't a temporary one (as those look ugly). - if (!path.Contains(Path.GetTempPath())) - text += $"\n{Path.GetFileName(path)}"; - - notification.Text = text; - - imported.Add(Import(path)); - - notification.Progress = (float)current / paths.Length; - } - catch (Exception e) - { - e = e.InnerException ?? e; - Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})"); - } - } - - if (imported.Count == 0) - { - notification.Text = "Import failed!"; - notification.State = ProgressNotificationState.Cancelled; - } - else - { - notification.CompletionText = imported.Count == 1 - ? $"Imported {imported.First()}!" - : $"Imported {current} {term}s!"; - - if (imported.Count > 0 && PresentImport != null) - { - notification.CompletionText += " Click to view."; - notification.CompletionClickAction = () => + lock (notification) { - PresentImport?.Invoke(imported); - return true; - }; - } + current++; - notification.State = ProgressNotificationState.Completed; + notification.Text = $"Imported {current} of {paths.Length} {term}s"; + notification.Progress = (float)current / paths.Length; + } + + if (t.Exception != null) + { + var e = t.Exception.InnerException ?? t.Exception; + Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})"); + } + })); } + + await Task.WhenAll(tasks); + + // if (imported.Count == 0) + // { + // notification.Text = "Import failed!"; + // notification.State = ProgressNotificationState.Cancelled; + // } + // else + // { + // notification.CompletionText = imported.Count == 1 + // ? $"Imported {imported.First()}!" + // : $"Imported {current} {term}s!"; + // + // if (imported.Count > 0 && PresentImport != null) + // { + // notification.CompletionText += " Click to view."; + // notification.CompletionClickAction = () => + // { + // PresentImport?.Invoke(imported); + // return true; + // }; + // } + // + // notification.State = ProgressNotificationState.Completed; + // } } /// /// Import one from the filesystem and delete the file on success. /// /// The archive location on disk. + /// An optional cancellation token. /// The imported model, if successful. - public TModel Import(string path) + public async Task Import(string path, CancellationToken cancellationToken = default) { TModel import; using (ArchiveReader reader = getReaderFrom(path)) - import = Import(reader); + import = await Import(reader, cancellationToken); // We may or may not want to delete the file depending on where it is stored. // e.g. reconstructing/repairing database with items from default storage. @@ -243,7 +241,8 @@ namespace osu.Game.Database /// Import an item from an . /// /// The archive to be imported. - public TModel Import(ArchiveReader archive) + /// An optional cancellation token. + public async Task Import(ArchiveReader archive, CancellationToken cancellationToken = default) { try { @@ -253,7 +252,7 @@ namespace osu.Game.Database model.Hash = computeHash(archive); - return Import(model, archive); + return await Import(model, archive, cancellationToken); } catch (Exception e) { @@ -288,7 +287,8 @@ namespace osu.Game.Database /// /// The model to be imported. /// An optional archive to use for model population. - public TModel Import(TModel item, ArchiveReader archive = null) + /// An optional cancellation token. + public async Task Import(TModel item, ArchiveReader archive = null, CancellationToken cancellationToken = default) => await Task.Factory.StartNew(async () => { delayEvents(); @@ -296,17 +296,31 @@ namespace osu.Game.Database { Logger.Log($"Importing {item}...", LoggingTarget.Database); + if (archive != null) + item.Files = createFileInfos(archive, Files); + + var localItem = item; + + try + { + await Populate(item, archive, cancellationToken); + } + catch (TaskCanceledException) + { + return item = null; + } + finally + { + if (!Delete(localItem)) + Files.Dereference(localItem.Files.Select(f => f.FileInfo).ToArray()); + } + using (var write = ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes. { try { if (!write.IsTransactionLeader) throw new InvalidOperationException($"Ensure there is no parent transaction so errors can correctly be handled by {this}"); - if (archive != null) - item.Files = createFileInfos(archive, Files); - - Populate(item, archive); - var existing = CheckForExisting(item); if (existing != null) @@ -332,6 +346,9 @@ namespace osu.Game.Database } catch (Exception e) { + if (!Delete(item)) + Files.Dereference(item.Files.Select(f => f.FileInfo).ToArray()); + write.Errors.Add(e); throw; } @@ -351,7 +368,7 @@ namespace osu.Game.Database } return item; - } + }, CancellationToken.None, TaskCreationOptions.None, importScheduler).Unwrap(); /// /// Perform an update of the specified item. @@ -516,24 +533,24 @@ namespace osu.Game.Database /// /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. /// - public Task ImportFromStableAsync() + public async Task ImportFromStableAsync() { var stable = GetStableStorage?.Invoke(); if (stable == null) { Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); - return Task.CompletedTask; + return; } if (!stable.ExistsDirectory(ImportFromStablePath)) { // This handles situations like when the user does not have a Skins folder Logger.Log($"No {ImportFromStablePath} folder available in osu!stable installation", LoggingTarget.Information, LogLevel.Error); - return Task.CompletedTask; + return; } - return Task.Factory.StartNew(() => Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning); + await Task.Run(async () => await Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray())); } #endregion @@ -552,9 +569,8 @@ namespace osu.Game.Database /// /// The model to populate. /// The archive to use as a reference for population. May be null. - protected virtual void Populate(TModel model, [CanBeNull] ArchiveReader archive) - { - } + /// An optional cancellation token. + protected virtual async Task Populate(TModel model, [CanBeNull] ArchiveReader archive, CancellationToken cancellationToken = default) => await Task.CompletedTask; /// /// Perform any final actions before the import to database executes. diff --git a/osu.Game/Database/ICanAcceptFiles.cs b/osu.Game/Database/ICanAcceptFiles.cs index f55d0c389e..b9f882468d 100644 --- a/osu.Game/Database/ICanAcceptFiles.cs +++ b/osu.Game/Database/ICanAcceptFiles.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Threading.Tasks; + namespace osu.Game.Database { /// @@ -12,7 +14,7 @@ namespace osu.Game.Database /// Import the specified paths. /// /// The files which should be imported. - void Import(params string[] paths); + Task Import(params string[] paths); /// /// An array of accepted file extensions (in the standard format of ".abc"). diff --git a/osu.Game/IPC/ArchiveImportIPCChannel.cs b/osu.Game/IPC/ArchiveImportIPCChannel.cs index fc747cd446..484db932f8 100644 --- a/osu.Game/IPC/ArchiveImportIPCChannel.cs +++ b/osu.Game/IPC/ArchiveImportIPCChannel.cs @@ -38,7 +38,7 @@ namespace osu.Game.IPC } if (importer.HandledExtensions.Contains(Path.GetExtension(path)?.ToLowerInvariant())) - importer.Import(path); + await importer.Import(path); } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index f9128687d6..d6b8caaf5b 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; @@ -268,13 +269,13 @@ namespace osu.Game private readonly List fileImporters = new List(); - public void Import(params string[] paths) + public async Task Import(params string[] paths) { var extension = Path.GetExtension(paths.First())?.ToLowerInvariant(); foreach (var importer in fileImporters) if (importer.HandledExtensions.Contains(extension)) - importer.Import(paths); + await importer.Import(paths); } public string[] HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions).ToArray(); diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 857a0bda9e..c8e081d29f 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Threading; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -36,6 +37,10 @@ namespace osu.Game.Overlays.Notifications State = state; } + private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + + public CancellationToken CancellationToken => cancellationTokenSource.Token; + public virtual ProgressNotificationState State { get => state; @@ -62,6 +67,8 @@ namespace osu.Game.Overlays.Notifications break; case ProgressNotificationState.Cancelled: + cancellationTokenSource.Cancel(); + Light.Colour = colourCancelled; Light.Pulsate = false; progressBar.Active = false; diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index 98a2fe8f13..cf5d247482 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -66,7 +66,7 @@ namespace osu.Game.Screens.Menu if (setInfo == null) { // we need to import the default menu background beatmap - setInfo = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream(@"Tracks/circles.osz"), "circles.osz")); + setInfo = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream(@"Tracks/circles.osz"), "circles.osz")).Result; setInfo.Protected = true; beatmaps.Update(setInfo); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index d8389fa6d9..949f9e5ff2 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -279,7 +279,7 @@ namespace osu.Game.Screens.Play var score = CreateScore(); if (DrawableRuleset.ReplayScore == null) - scoreManager.Import(score); + scoreManager.Import(score).Wait(); this.Push(CreateResults(score)); diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 3a4d44f608..73cc47ea47 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -71,9 +73,9 @@ namespace osu.Game.Skinning protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name }; - protected override void Populate(SkinInfo model, ArchiveReader archive) + protected override async Task Populate(SkinInfo model, ArchiveReader archive, CancellationToken cancellationToken = default) { - base.Populate(model, archive); + await base.Populate(model, archive, cancellationToken); Skin reference = getSkin(model); From 600503ec8ebdb6233beab64b0faa53f02b91af4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 12:46:21 +0900 Subject: [PATCH 0977/2854] Use Task.Run/Wait to avoid warnings --- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 4 ++-- osu.Game/Screens/Select/SongSelect.cs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index ebee358730..c2e8078bd6 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -210,7 +210,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("start not requested", () => !startRequested); } - private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray()))); + private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait()); private static int importId; private int getImportId() => ++importId; @@ -232,7 +232,7 @@ namespace osu.Game.Tests.Visual.SongSelect var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray(); for (int i = 0; i < 100; i += 10) - manager.Import(createTestBeatmapSet(i, usableRulesets)); + manager.Import(createTestBeatmapSet(i, usableRulesets)).Wait(); }); } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 6d5be607f4..196d655851 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -32,6 +32,7 @@ using osuTK.Input; using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using osu.Framework.Graphics.Sprites; namespace osu.Game.Screens.Select @@ -256,8 +257,8 @@ namespace osu.Game.Screens.Select if (!beatmaps.GetAllUsableBeatmapSets().Any() && beatmaps.StableInstallationAvailable) dialogOverlay.Push(new ImportFromStablePopup(() => { - beatmaps.ImportFromStableAsync(); - skins.ImportFromStableAsync(); + Task.Run(beatmaps.ImportFromStableAsync); + Task.Run(skins.ImportFromStableAsync); })); }); } From b4d2d0bd0b245cba425d9e5d981e3831d9269931 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 13:19:58 +0900 Subject: [PATCH 0978/2854] Simplify and combine concurrency of ArchiveModelManager --- osu.Game/Beatmaps/BeatmapManager.cs | 77 ++----------------- osu.Game/Database/ArchiveModelManager.cs | 96 ++++++++++++------------ 2 files changed, 57 insertions(+), 116 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f5ef52a93b..9e7b6d7971 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -111,7 +110,7 @@ namespace osu.Game.Beatmaps validateOnlineIds(beatmapSet); - await Task.WhenAll(beatmapSet.Beatmaps.Select(b => updateQueue.Enqueue(new UpdateItem(b, cancellationToken)).Task).ToArray()); + await Task.WhenAll(beatmapSet.Beatmaps.Select(b => updateQueue.Perform(b, cancellationToken)).ToArray()); } protected override void PreImport(BeatmapSetInfo beatmapSet) @@ -424,81 +423,24 @@ namespace osu.Game.Beatmaps private class BeatmapUpdateQueue { private readonly IAPIProvider api; - private readonly Queue queue = new Queue(); - private int activeThreads; + private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(4); public BeatmapUpdateQueue(IAPIProvider api) { this.api = api; } - public UpdateItem Enqueue(UpdateItem item) + public Task Perform(BeatmapInfo beatmap, CancellationToken cancellationToken) + => Task.Factory.StartNew(() => perform(beatmap, cancellationToken), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler); + + private void perform(BeatmapInfo beatmap, CancellationToken cancellation) { - lock (queue) - { - queue.Enqueue(item); - - if (activeThreads >= 16) - return item; - - new Thread(runWork) { IsBackground = true }.Start(); - activeThreads++; - } - - return item; - } - - private void runWork() - { - while (true) - { - UpdateItem toProcess; - - lock (queue) - { - if (queue.Count == 0) - break; - - toProcess = queue.Dequeue(); - } - - toProcess.PerformUpdate(api); - } - - lock (queue) - activeThreads--; - } - } - - private class UpdateItem - { - public Task Task => tcs.Task; - - private readonly BeatmapInfo beatmap; - private readonly CancellationToken cancellationToken; - - private readonly TaskCompletionSource tcs = new TaskCompletionSource(); - - public UpdateItem(BeatmapInfo beatmap, CancellationToken cancellationToken) - { - this.beatmap = beatmap; - this.cancellationToken = cancellationToken; - } - - public void PerformUpdate(IAPIProvider api) - { - if (cancellationToken.IsCancellationRequested) - { - tcs.SetCanceled(); + if (cancellation.IsCancellationRequested) return; - } if (api?.State != APIState.Online) - { - tcs.SetResult(false); return; - } Logger.Log("Attempting online lookup for the missing values...", LoggingTarget.Database); @@ -512,17 +454,14 @@ namespace osu.Game.Beatmaps beatmap.BeatmapSet.Status = res.BeatmapSet.Status; beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; beatmap.OnlineBeatmapID = res.OnlineBeatmapID; - - tcs.SetResult(true); }; req.Failure += e => { Logger.Log($"Failed ({e})", LoggingTarget.Database); - - tcs.SetResult(false); }; + // intentionally blocking to limit web request concurrency req.Perform(api); } } diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index afc614772e..e30e1cd3ee 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -11,7 +11,6 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using osu.Framework; using osu.Framework.Extensions; -using osu.Framework.Extensions.TypeExtensions; using osu.Framework.IO.File; using osu.Framework.Logging; using osu.Framework.Platform; @@ -32,7 +31,7 @@ namespace osu.Game.Database /// /// The model type. /// The associated file join type. - public abstract class ArchiveModelManager : ICanAcceptFiles + public abstract class ArchiveModelManager : ArchiveModelManager, ICanAcceptFiles where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : INamedFileInfo, new() { @@ -112,11 +111,8 @@ namespace osu.Game.Database a.Invoke(); } - private readonly ThreadedTaskScheduler importScheduler; - protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStoreWithFileIncludes modelStore, IIpcHost importHost = null) { - importScheduler = new ThreadedTaskScheduler(16, $"{GetType().ReadableName()}.Import"); ContextFactory = contextFactory; ModelStore = modelStore; @@ -152,55 +148,55 @@ namespace osu.Game.Database var term = $"{typeof(TModel).Name.Replace("Info", "").ToLower()}"; - var tasks = new List(); - int current = 0; - foreach (string path in paths) + var imported = new List(); + + await Task.WhenAll(paths.Select(path => Import(path, notification.CancellationToken).ContinueWith(t => { - tasks.Add(Import(path, notification.CancellationToken).ContinueWith(t => + lock (notification) { - lock (notification) - { - current++; + current++; - notification.Text = $"Imported {current} of {paths.Length} {term}s"; - notification.Progress = (float)current / paths.Length; - } + notification.Text = $"Imported {current} of {paths.Length} {term}s"; + notification.Progress = (float)current / paths.Length; + } - if (t.Exception != null) - { - var e = t.Exception.InnerException ?? t.Exception; - Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})"); - } - })); + if (t.Exception == null) + { + lock (imported) + imported.Add(t.Result); + } + else + { + var e = t.Exception.InnerException ?? t.Exception; + Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})"); + } + }))); + + if (imported.Count == 0) + { + notification.Text = "Import failed!"; + notification.State = ProgressNotificationState.Cancelled; } + else + { + notification.CompletionText = imported.Count == 1 + ? $"Imported {imported.First()}!" + : $"Imported {current} {term}s!"; - await Task.WhenAll(tasks); + if (imported.Count > 0 && PresentImport != null) + { + notification.CompletionText += " Click to view."; + notification.CompletionClickAction = () => + { + PresentImport?.Invoke(imported); + return true; + }; + } - // if (imported.Count == 0) - // { - // notification.Text = "Import failed!"; - // notification.State = ProgressNotificationState.Cancelled; - // } - // else - // { - // notification.CompletionText = imported.Count == 1 - // ? $"Imported {imported.First()}!" - // : $"Imported {current} {term}s!"; - // - // if (imported.Count > 0 && PresentImport != null) - // { - // notification.CompletionText += " Click to view."; - // notification.CompletionClickAction = () => - // { - // PresentImport?.Invoke(imported); - // return true; - // }; - // } - // - // notification.State = ProgressNotificationState.Completed; - // } + notification.State = ProgressNotificationState.Completed; + } } /// @@ -368,7 +364,7 @@ namespace osu.Game.Database } return item; - }, CancellationToken.None, TaskCreationOptions.None, importScheduler).Unwrap(); + }, CancellationToken.None, TaskCreationOptions.HideScheduler, IMPORT_SCHEDULER).Unwrap(); /// /// Perform an update of the specified item. @@ -615,4 +611,10 @@ namespace osu.Game.Database throw new InvalidFormatException($"{path} is not a valid archive"); } } + + public abstract class ArchiveModelManager + { + // allow sharing static across all generic types + protected static readonly ThreadedTaskScheduler IMPORT_SCHEDULER = new ThreadedTaskScheduler(1); + } } From 2d1a54e63489b36cfb4fa5261abd0ed95c092051 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 13:37:20 +0900 Subject: [PATCH 0979/2854] Properly implement cancellation --- osu.Game/Database/ArchiveModelManager.cs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index e30e1cd3ee..1d576ff82f 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -154,6 +154,9 @@ namespace osu.Game.Database await Task.WhenAll(paths.Select(path => Import(path, notification.CancellationToken).ContinueWith(t => { + if (notification.CancellationToken.IsCancellationRequested) + return; + lock (notification) { current++; @@ -172,7 +175,7 @@ namespace osu.Game.Database var e = t.Exception.InnerException ?? t.Exception; Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})"); } - }))); + }, TaskContinuationOptions.NotOnCanceled))); if (imported.Count == 0) { @@ -207,6 +210,8 @@ namespace osu.Game.Database /// The imported model, if successful. public async Task Import(string path, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); + TModel import; using (ArchiveReader reader = getReaderFrom(path)) import = await Import(reader, cancellationToken); @@ -240,6 +245,8 @@ namespace osu.Game.Database /// An optional cancellation token. public async Task Import(ArchiveReader archive, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); + try { var model = CreateModel(archive); @@ -250,6 +257,10 @@ namespace osu.Game.Database return await Import(model, archive, cancellationToken); } + catch (TaskCanceledException) + { + throw; + } catch (Exception e) { Logger.Error(e, $"Model creation of {archive.Name} failed.", LoggingTarget.Database); @@ -286,6 +297,8 @@ namespace osu.Game.Database /// An optional cancellation token. public async Task Import(TModel item, ArchiveReader archive = null, CancellationToken cancellationToken = default) => await Task.Factory.StartNew(async () => { + cancellationToken.ThrowIfCancellationRequested(); + delayEvents(); try @@ -301,10 +314,6 @@ namespace osu.Game.Database { await Populate(item, archive, cancellationToken); } - catch (TaskCanceledException) - { - return item = null; - } finally { if (!Delete(localItem)) @@ -364,7 +373,7 @@ namespace osu.Game.Database } return item; - }, CancellationToken.None, TaskCreationOptions.HideScheduler, IMPORT_SCHEDULER).Unwrap(); + }, cancellationToken, TaskCreationOptions.HideScheduler, IMPORT_SCHEDULER).Unwrap(); /// /// Perform an update of the specified item. From e12b03e275c40de2b3629b92da24da45545be619 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 13:46:31 +0900 Subject: [PATCH 0980/2854] Remove unnecessary reference --- osu.Game.Tests/osu.Game.Tests.csproj | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 403ae16885..11d70ee7be 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -18,9 +18,4 @@ - - - ..\..\..\..\..\usr\local\share\dotnet\sdk\NuGetFallbackFolder\microsoft.win32.registry\4.5.0\ref\netstandard2.0\Microsoft.Win32.Registry.dll - - \ No newline at end of file From b79fdfc12f2d65da2f0fd80b68fa58aa2aa11882 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 13:48:58 +0900 Subject: [PATCH 0981/2854] Fix one more instance of improperly handled cancellation --- osu.Game/Beatmaps/BeatmapManager.cs | 3 +-- osu.Game/Database/ArchiveModelManager.cs | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 9e7b6d7971..cfc6a0be28 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -436,8 +436,7 @@ namespace osu.Game.Beatmaps private void perform(BeatmapInfo beatmap, CancellationToken cancellation) { - if (cancellation.IsCancellationRequested) - return; + cancellation.ThrowIfCancellationRequested(); if (api?.State != APIState.Online) return; diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 1d576ff82f..df96d9984a 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -361,6 +361,10 @@ namespace osu.Game.Database Logger.Log($"Import of {item} successfully completed!", LoggingTarget.Database); } + catch (TaskCanceledException) + { + throw; + } catch (Exception e) { Logger.Error(e, $"Import of {item} failed and has been rolled back.", LoggingTarget.Database); From e4bad93b6668868079dad7227b868b43736e6336 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 13:52:09 +0900 Subject: [PATCH 0982/2854] Use variable for web request concurrency for clarity --- osu.Game/Beatmaps/BeatmapManager.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index cfc6a0be28..435edcf722 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -424,7 +424,9 @@ namespace osu.Game.Beatmaps { private readonly IAPIProvider api; - private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(4); + private const int update_queue_request_concurrency = 4; + + private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(update_queue_request_concurrency); public BeatmapUpdateQueue(IAPIProvider api) { @@ -455,10 +457,7 @@ namespace osu.Game.Beatmaps beatmap.OnlineBeatmapID = res.OnlineBeatmapID; }; - req.Failure += e => - { - Logger.Log($"Failed ({e})", LoggingTarget.Database); - }; + req.Failure += e => { Logger.Log($"Failed ({e})", LoggingTarget.Database); }; // intentionally blocking to limit web request concurrency req.Perform(api); From e19f4935c3cbdd6d037d093d6b41e2f7cb1bf719 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 14:13:36 +0900 Subject: [PATCH 0983/2854] Fix incorrect undo logic on exception --- osu.Game/Database/ArchiveModelManager.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index df96d9984a..83c51e2abf 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -305,8 +305,7 @@ namespace osu.Game.Database { Logger.Log($"Importing {item}...", LoggingTarget.Database); - if (archive != null) - item.Files = createFileInfos(archive, Files); + item.Files = archive != null ? createFileInfos(archive, Files) : new List(); var localItem = item; @@ -314,7 +313,7 @@ namespace osu.Game.Database { await Populate(item, archive, cancellationToken); } - finally + catch (Exception) { if (!Delete(localItem)) Files.Dereference(localItem.Files.Select(f => f.FileInfo).ToArray()); From 27163c999686b4842ac41a170b63c2d41b2fa0fc Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Mon, 10 Jun 2019 09:18:48 +0300 Subject: [PATCH 0984/2854] Fix crashes in some cases When we want to switch ruleset from outside of the selector, but it's blocked (multiplayer is a good example) --- .../Overlays/Toolbar/ToolbarRulesetSelector.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index f8ac610a3a..1f35d0f293 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -23,6 +23,7 @@ namespace osu.Game.Overlays.Toolbar private const float padding = 10; private readonly Drawable modeButtonLine; private RulesetStore rulesets; + private readonly Bindable globalRuleset = new Bindable(); public override bool HandleNonPositionalInput => !Current.Disabled && base.HandleNonPositionalInput; public override bool HandlePositionalInput => !Current.Disabled && base.HandlePositionalInput; @@ -79,17 +80,20 @@ namespace osu.Game.Overlays.Toolbar private void load(RulesetStore rulesets, Bindable parentRuleset) { this.rulesets = rulesets; + globalRuleset.BindTo(parentRuleset); foreach (var r in rulesets.AvailableRulesets) { AddItem(r); } - Current.BindTo(parentRuleset); - Current.DisabledChanged += disabledChanged; - Current.BindValueChanged(rulesetChanged); + globalRuleset.BindValueChanged(globalRulesetChanged); + globalRuleset.DisabledChanged += disabledChanged; + Current.BindValueChanged(localRulesetChanged); } + private void globalRulesetChanged(ValueChangedEvent e) => Current.Value = e.NewValue; + protected override bool OnKeyDown(KeyDownEvent e) { base.OnKeyDown(e); @@ -109,8 +113,13 @@ namespace osu.Game.Overlays.Toolbar private readonly Cached activeMode = new Cached(); - private void rulesetChanged(ValueChangedEvent e) + private void localRulesetChanged(ValueChangedEvent e) { + if (!globalRuleset.Disabled) + { + globalRuleset.Value = e.NewValue; + } + activeMode.Invalidate(); } From f31b19e0d7b2837bfaa1c26fa381d6ab1df43e41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 16:02:49 +0900 Subject: [PATCH 0985/2854] Don't unwrap exception manually --- osu.Game/Database/ArchiveModelManager.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 83c51e2abf..4c4878edee 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -172,8 +172,7 @@ namespace osu.Game.Database } else { - var e = t.Exception.InnerException ?? t.Exception; - Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})"); + Logger.Error(t.Exception, $@"Could not import ({Path.GetFileName(path)})"); } }, TaskContinuationOptions.NotOnCanceled))); From 9bdc8b47bb1e1c8ab8e57b48d0d8604d94ae5d3e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 16:13:51 +0900 Subject: [PATCH 0986/2854] Remove unnecessary async-await pair --- osu.Game/Database/ArchiveModelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 4c4878edee..fb7f0ffe5d 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -577,7 +577,7 @@ namespace osu.Game.Database /// The model to populate. /// The archive to use as a reference for population. May be null. /// An optional cancellation token. - protected virtual async Task Populate(TModel model, [CanBeNull] ArchiveReader archive, CancellationToken cancellationToken = default) => await Task.CompletedTask; + protected virtual Task Populate(TModel model, [CanBeNull] ArchiveReader archive, CancellationToken cancellationToken = default) => Task.CompletedTask; /// /// Perform any final actions before the import to database executes. From fae32b390171738feed00336df54db7046c9ecd8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 16:14:11 +0900 Subject: [PATCH 0987/2854] Return shorter class name in error messages --- osu.Game/Database/ArchiveModelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index fb7f0ffe5d..e2a07a860f 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -331,7 +331,7 @@ namespace osu.Game.Database if (CanUndelete(existing, item)) { Undelete(existing); - Logger.Log($"Found existing {typeof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database); + Logger.Log($"Found existing {nameof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database); handleEvent(() => ItemAdded?.Invoke(existing, true)); return existing; } From 02b376d962d364f7b2525e8478dd4c4e3dd7cf04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 16:14:42 +0900 Subject: [PATCH 0988/2854] Fix rollback logic not necessrily cleaning up file store --- osu.Game/Database/ArchiveModelManager.cs | 63 +++++++++++------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index e2a07a860f..45460dd11c 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -154,25 +154,22 @@ namespace osu.Game.Database await Task.WhenAll(paths.Select(path => Import(path, notification.CancellationToken).ContinueWith(t => { - if (notification.CancellationToken.IsCancellationRequested) - return; + notification.CancellationToken.ThrowIfCancellationRequested(); - lock (notification) + lock (imported) { - current++; + Interlocked.Increment(ref current); - notification.Text = $"Imported {current} of {paths.Length} {term}s"; - notification.Progress = (float)current / paths.Length; - } - - if (t.Exception == null) - { - lock (imported) + if (t.Exception == null) + { imported.Add(t.Result); - } - else - { - Logger.Error(t.Exception, $@"Could not import ({Path.GetFileName(path)})"); + notification.Text = $"Imported {current} of {paths.Length} {term}s"; + notification.Progress = (float)current / paths.Length; + } + else + { + Logger.Error(t.Exception, $@"Could not import ({Path.GetFileName(path)})"); + } } }, TaskContinuationOptions.NotOnCanceled))); @@ -300,23 +297,23 @@ namespace osu.Game.Database delayEvents(); + void rollback() + { + if (!Delete(item)) + { + // We may have not yet added the model to the underlying table, but should still clean up files. + Logger.Log($"Dereferencing files for incomplete import of {item}.", LoggingTarget.Database); + Files.Dereference(item.Files.Select(f => f.FileInfo).ToArray()); + } + } + try { Logger.Log($"Importing {item}...", LoggingTarget.Database); item.Files = archive != null ? createFileInfos(archive, Files) : new List(); - var localItem = item; - - try - { - await Populate(item, archive, cancellationToken); - } - catch (Exception) - { - if (!Delete(localItem)) - Files.Dereference(localItem.Files.Select(f => f.FileInfo).ToArray()); - } + await Populate(item, archive, cancellationToken); using (var write = ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes. { @@ -333,6 +330,8 @@ namespace osu.Game.Database Undelete(existing); Logger.Log($"Found existing {nameof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database); handleEvent(() => ItemAdded?.Invoke(existing, true)); + + rollback(); return existing; } else @@ -349,9 +348,6 @@ namespace osu.Game.Database } catch (Exception e) { - if (!Delete(item)) - Files.Dereference(item.Files.Select(f => f.FileInfo).ToArray()); - write.Errors.Add(e); throw; } @@ -359,13 +355,12 @@ namespace osu.Game.Database Logger.Log($"Import of {item} successfully completed!", LoggingTarget.Database); } - catch (TaskCanceledException) - { - throw; - } catch (Exception e) { - Logger.Error(e, $"Import of {item} failed and has been rolled back.", LoggingTarget.Database); + if (!(e is TaskCanceledException)) + Logger.Error(e, $"Import of {item} failed and has been rolled back.", LoggingTarget.Database); + + rollback(); item = null; } finally From 5b75060b94999e3a50a665a4e40cee3994162fa2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 16:45:45 +0900 Subject: [PATCH 0989/2854] Add test for rollback logic correctly dereferencing files --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 13 +++++++++++++ osu.Game/IO/FileStore.cs | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 4c9260f640..3f4f40781c 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -12,6 +12,7 @@ using osu.Framework.Platform; using osu.Game.IPC; using osu.Framework.Allocation; using osu.Game.Beatmaps; +using osu.Game.IO; using osu.Game.Tests.Resources; using SharpCompress.Archives.Zip; @@ -97,6 +98,7 @@ namespace osu.Game.Tests.Beatmaps.IO { var osu = loadOsu(host); var manager = osu.Dependencies.Get(); + var files = osu.Dependencies.Get(); int fireCount = 0; @@ -113,6 +115,12 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.AreEqual(0, fireCount -= 2); + Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count); + Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count); + Assert.AreEqual(12, manager.QueryBeatmaps(_ => true).ToList().Count); + + Assert.AreEqual(18, files.QueryFiles(_ => true).Count()); + var breakTemp = TestResources.GetTestBeatmapForImport(); MemoryStream brokenOsu = new MemoryStream(new byte[] { 1, 3, 3, 7 }); @@ -131,6 +139,8 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count); Assert.AreEqual(12, manager.QueryBeatmaps(_ => true).ToList().Count); + Assert.AreEqual(18, files.QueryFiles(_ => true).Count()); + // this will trigger purging of the existing beatmap (online set id match) but should rollback due to broken osu. await manager.Import(breakTemp); @@ -140,6 +150,9 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count); Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count); Assert.AreEqual(12, manager.QueryBeatmaps(_ => true).ToList().Count); + + Assert.AreEqual(18, files.QueryFiles(_ => true).Count()); + Assert.AreEqual(18, files.QueryFiles(f => f.ReferenceCount == 1).Count()); } finally { diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs index 458f8964f9..370d6786f5 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -2,8 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.IO; using System.Linq; +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; using osu.Framework.Extensions; using osu.Framework.IO.Stores; using osu.Framework.Logging; @@ -27,6 +30,13 @@ namespace osu.Game.IO Store = new StorageBackedResourceStore(Storage); } + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// Results from the provided query. + public IEnumerable QueryFiles(Expression> query) => ContextFactory.Get().Set().AsNoTracking().Where(f => f.ReferenceCount > 0).Where(query); + public FileInfo Add(Stream data, bool reference = true) { using (var usage = ContextFactory.GetForWrite()) From 559413f766fef4fef2f8c16e59e61a39f100d785 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 17:12:25 +0900 Subject: [PATCH 0990/2854] Avoid using ContinueWith in already async context --- osu.Game/Database/ArchiveModelManager.cs | 29 ++++++++++++++++-------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 45460dd11c..d09aa37d7e 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -152,26 +152,35 @@ namespace osu.Game.Database var imported = new List(); - await Task.WhenAll(paths.Select(path => Import(path, notification.CancellationToken).ContinueWith(t => + await Task.WhenAll(paths.Select(async path => { notification.CancellationToken.ThrowIfCancellationRequested(); - lock (imported) + try { - Interlocked.Increment(ref current); + var model = await Import(path, notification.CancellationToken); - if (t.Exception == null) + lock (imported) { - imported.Add(t.Result); + imported.Add(model); + notification.Text = $"Imported {current} of {paths.Length} {term}s"; notification.Progress = (float)current / paths.Length; } - else - { - Logger.Error(t.Exception, $@"Could not import ({Path.GetFileName(path)})"); - } } - }, TaskContinuationOptions.NotOnCanceled))); + catch (TaskCanceledException) + { + throw; + } + catch (Exception e) + { + Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})"); + } + finally + { + Interlocked.Increment(ref current); + } + })); if (imported.Count == 0) { From c8bd92659bab551a36e0c40dc1b604deca0e4fdb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 17:12:37 +0900 Subject: [PATCH 0991/2854] Clean up exception and null handling in Import process --- osu.Game/Database/ArchiveModelManager.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index d09aa37d7e..fa8301bb2e 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -252,15 +252,15 @@ namespace osu.Game.Database { cancellationToken.ThrowIfCancellationRequested(); + TModel model; + try { - var model = CreateModel(archive); + model = CreateModel(archive); if (model == null) return null; model.Hash = computeHash(archive); - - return await Import(model, archive, cancellationToken); } catch (TaskCanceledException) { @@ -271,6 +271,8 @@ namespace osu.Game.Database Logger.Error(e, $"Model creation of {archive.Name} failed.", LoggingTarget.Database); return null; } + + return await Import(model, archive, cancellationToken); } /// @@ -340,7 +342,9 @@ namespace osu.Game.Database Logger.Log($"Found existing {nameof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database); handleEvent(() => ItemAdded?.Invoke(existing, true)); + // existing item will be used; rollback new import and exit early. rollback(); + flushEvents(true); return existing; } else @@ -370,14 +374,11 @@ namespace osu.Game.Database Logger.Error(e, $"Import of {item} failed and has been rolled back.", LoggingTarget.Database); rollback(); - item = null; - } - finally - { - // we only want to flush events after we've confirmed the write context didn't have any errors. - flushEvents(item != null); + flushEvents(false); + throw; } + flushEvents(true); return item; }, cancellationToken, TaskCreationOptions.HideScheduler, IMPORT_SCHEDULER).Unwrap(); From dcdb806120c6b581af4be341b60bd84cba5ea9c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 17:26:56 +0900 Subject: [PATCH 0992/2854] Catch newly thrown exception in test --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 3f4f40781c..7f08674a95 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -135,14 +135,14 @@ namespace osu.Game.Tests.Beatmaps.IO zip.SaveTo(outStream, SharpCompress.Common.CompressionType.Deflate); } - Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count); - Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count); - Assert.AreEqual(12, manager.QueryBeatmaps(_ => true).ToList().Count); - - Assert.AreEqual(18, files.QueryFiles(_ => true).Count()); - // this will trigger purging of the existing beatmap (online set id match) but should rollback due to broken osu. - await manager.Import(breakTemp); + try + { + await manager.Import(breakTemp); + } + catch + { + } // no events should be fired in the case of a rollback. Assert.AreEqual(0, fireCount); From 28b2a516e34354eb0f2d958c2f55da91dd8d4f78 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 18:13:33 +0900 Subject: [PATCH 0993/2854] Ensure exception is only thrown once on rollback --- .../Beatmaps/IO/ImportBeatmapTest.cs | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 7f08674a95..f3680e3c1e 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -11,6 +11,7 @@ using NUnit.Framework; using osu.Framework.Platform; using osu.Game.IPC; using osu.Framework.Allocation; +using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.IO; using osu.Game.Tests.Resources; @@ -96,24 +97,31 @@ namespace osu.Game.Tests.Beatmaps.IO { try { + int itemAddRemoveFireCount = 0; + int loggedExceptionCount = 0; + + Logger.NewEntry += l => + { + if (l.Target == LoggingTarget.Database && l.Exception != null) + Interlocked.Increment(ref loggedExceptionCount); + }; + var osu = loadOsu(host); var manager = osu.Dependencies.Get(); var files = osu.Dependencies.Get(); - int fireCount = 0; - // ReSharper disable once AccessToModifiedClosure - manager.ItemAdded += (_, __) => fireCount++; - manager.ItemRemoved += _ => fireCount++; + manager.ItemAdded += (_, __) => Interlocked.Increment(ref itemAddRemoveFireCount); + manager.ItemRemoved += _ => Interlocked.Increment(ref itemAddRemoveFireCount); var imported = await LoadOszIntoOsu(osu); - Assert.AreEqual(0, fireCount -= 1); + Assert.AreEqual(0, itemAddRemoveFireCount -= 1); imported.Hash += "-changed"; manager.Update(imported); - Assert.AreEqual(0, fireCount -= 2); + Assert.AreEqual(0, itemAddRemoveFireCount -= 2); Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count); Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count); @@ -145,7 +153,7 @@ namespace osu.Game.Tests.Beatmaps.IO } // no events should be fired in the case of a rollback. - Assert.AreEqual(0, fireCount); + Assert.AreEqual(0, itemAddRemoveFireCount); Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count); Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count); @@ -153,6 +161,8 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.AreEqual(18, files.QueryFiles(_ => true).Count()); Assert.AreEqual(18, files.QueryFiles(f => f.ReferenceCount == 1).Count()); + + Assert.AreEqual(1, loggedExceptionCount); } finally { From 1aa865c3fb154d0ce88321f38cbef309c7799c22 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 10 Jun 2019 18:34:24 +0900 Subject: [PATCH 0994/2854] split out method for reuse --- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 8636890081..d6b5e0175f 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -138,7 +138,7 @@ namespace osu.Game.Tests.Visual.SongSelect { createSongSelect(); changeRuleset(2); - importForRuleset(0); + addRulesetImportStep(0); AddUntilStep("no selection", () => songSelect.Carousel.SelectedBeatmap == null); } @@ -147,8 +147,8 @@ namespace osu.Game.Tests.Visual.SongSelect { createSongSelect(); changeRuleset(2); - importForRuleset(2); - importForRuleset(1); + addRulesetImportStep(2); + addRulesetImportStep(1); AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.RulesetID == 2); changeRuleset(1); @@ -223,7 +223,7 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddRepeatStep($"Create beatmaps {test_count} times", () => { - manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == 0).ToArray())); + importForRuleset(0); Scheduler.AddDelayed(() => { @@ -240,15 +240,16 @@ namespace osu.Game.Tests.Visual.SongSelect { int? previousID = null; createSongSelect(); - importForRuleset(0); + addRulesetImportStep(0); AddStep("Move to last difficulty", () => songSelect.Carousel.SelectBeatmap(songSelect.Carousel.BeatmapSets.First().Beatmaps.Last())); AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmap.ID); AddStep("Hide first beatmap", () => manager.Hide(songSelect.Carousel.SelectedBeatmapSet.Beatmaps.First())); AddAssert("Selected beatmap has not changed", () => songSelect.Carousel.SelectedBeatmap.ID == previousID); } - private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", - () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray()))); + private void addRulesetImportStep(int id) => AddStep($"import test map for ruleset {id}", () => importForRuleset(id)); + + private void importForRuleset(int id) => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())); private static int importId; private int getImportId() => ++importId; From 12aa264657061f456405a65c0eaaf781f3461304 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 18:35:23 +0900 Subject: [PATCH 0995/2854] Consolidate tests and check for file reference counts --- .../Beatmaps/IO/ImportBeatmapTest.cs | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index f3680e3c1e..5fc05a4b2f 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -77,10 +77,8 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(imported.ID == importedSecondTime.ID); Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID); - var manager = osu.Dependencies.Get(); - - Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count); - Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count); + checkBeatmapSetCount(osu, 1); + checkSingleReferencedFileCount(osu, 18); } finally { @@ -108,7 +106,6 @@ namespace osu.Game.Tests.Beatmaps.IO var osu = loadOsu(host); var manager = osu.Dependencies.Get(); - var files = osu.Dependencies.Get(); // ReSharper disable once AccessToModifiedClosure manager.ItemAdded += (_, __) => Interlocked.Increment(ref itemAddRemoveFireCount); @@ -123,11 +120,9 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.AreEqual(0, itemAddRemoveFireCount -= 2); - Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count); - Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count); - Assert.AreEqual(12, manager.QueryBeatmaps(_ => true).ToList().Count); - - Assert.AreEqual(18, files.QueryFiles(_ => true).Count()); + checkBeatmapSetCount(osu, 1); + checkBeatmapCount(osu, 12); + checkSingleReferencedFileCount(osu, 18); var breakTemp = TestResources.GetTestBeatmapForImport(); @@ -155,12 +150,10 @@ namespace osu.Game.Tests.Beatmaps.IO // no events should be fired in the case of a rollback. Assert.AreEqual(0, itemAddRemoveFireCount); - Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count); - Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count); - Assert.AreEqual(12, manager.QueryBeatmaps(_ => true).ToList().Count); + checkBeatmapSetCount(osu, 1); + checkBeatmapCount(osu, 12); - Assert.AreEqual(18, files.QueryFiles(_ => true).Count()); - Assert.AreEqual(18, files.QueryFiles(f => f.ReferenceCount == 1).Count()); + checkSingleReferencedFileCount(osu, 18); Assert.AreEqual(1, loggedExceptionCount); } @@ -193,8 +186,7 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(imported.Beatmaps.First().ID < importedSecondTime.Beatmaps.First().ID); // only one beatmap will exist as the online set ID matched, causing purging of the first import. - Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count); - Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count); + checkBeatmapSetCount(osu, 1); } finally { @@ -396,11 +388,32 @@ namespace osu.Game.Tests.Beatmaps.IO var manager = osu.Dependencies.Get(); manager.Delete(imported); - Assert.IsTrue(manager.GetAllUsableBeatmapSets().Count == 0); - Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count); + checkBeatmapSetCount(osu, 0); + checkBeatmapSetCount(osu, 1, true); + checkSingleReferencedFileCount(osu, 0); + Assert.IsTrue(manager.QueryBeatmapSets(_ => true).First().DeletePending); } + private void checkBeatmapSetCount(OsuGameBase osu, int expected, bool includeDeletePending = false) + { + var manager = osu.Dependencies.Get(); + + Assert.AreEqual(expected, includeDeletePending + ? manager.QueryBeatmapSets(_ => true).ToList().Count + : manager.GetAllUsableBeatmapSets().Count); + } + + private void checkBeatmapCount(OsuGameBase osu, int expected) + { + Assert.AreEqual(expected, osu.Dependencies.Get().QueryBeatmaps(_ => true).ToList().Count); + } + + private void checkSingleReferencedFileCount(OsuGameBase osu, int expected) + { + Assert.AreEqual(expected, osu.Dependencies.Get().QueryFiles(f => f.ReferenceCount == 1).Count()); + } + private OsuGameBase loadOsu(GameHost host) { var osu = new OsuGameBase(); From f7a699e4a2e0031224661370ba840d7d9327df3f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 18:38:03 +0900 Subject: [PATCH 0996/2854] Better documentation for import scheduler singleton --- osu.Game/Database/ArchiveModelManager.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index fa8301bb2e..afc2690106 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -630,7 +630,15 @@ namespace osu.Game.Database public abstract class ArchiveModelManager { - // allow sharing static across all generic types - protected static readonly ThreadedTaskScheduler IMPORT_SCHEDULER = new ThreadedTaskScheduler(1); + private const int import_queue_request_concurrency = 1; + + /// + /// A singleton scheduler shared by all . + /// + /// + /// This scheduler generally performs IO and CPU intensive work so concurrency is limited harshly. + /// It is mainly being used as a queue mechanism for large imports. + /// + protected static readonly ThreadedTaskScheduler IMPORT_SCHEDULER = new ThreadedTaskScheduler(import_queue_request_concurrency); } } From 41e3e2222bc7d9c75e44a1340144c2d52e1f7acb Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 10 Jun 2019 18:40:49 +0900 Subject: [PATCH 0997/2854] fix test --- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index d6b5e0175f..bc15bc6b6d 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -219,7 +219,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("Setup counter", () => { beatmapChangedCount = 0; - songSelect.Carousel.BeatmapSetsChanged += () => beatmapChangedCount++; + songSelect.Carousel.SelectionChanged += _ => beatmapChangedCount++; }); AddRepeatStep($"Create beatmaps {test_count} times", () => { From 6cda2cdb8225000f1a7de5e8c842a8d3f0a02aaf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 18:41:56 +0900 Subject: [PATCH 0998/2854] Fix exception output to use humanised model name --- osu.Game/Database/ArchiveModelManager.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index afc2690106..93e924fab5 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -146,8 +146,6 @@ namespace osu.Game.Database notification.Progress = 0; notification.Text = "Import is initialising..."; - var term = $"{typeof(TModel).Name.Replace("Info", "").ToLower()}"; - int current = 0; var imported = new List(); @@ -164,7 +162,7 @@ namespace osu.Game.Database { imported.Add(model); - notification.Text = $"Imported {current} of {paths.Length} {term}s"; + notification.Text = $"Imported {current} of {paths.Length} {humanisedModelName}s"; notification.Progress = (float)current / paths.Length; } } @@ -191,7 +189,7 @@ namespace osu.Game.Database { notification.CompletionText = imported.Count == 1 ? $"Imported {imported.First()}!" - : $"Imported {current} {term}s!"; + : $"Imported {current} {humanisedModelName}s!"; if (imported.Count > 0 && PresentImport != null) { @@ -339,7 +337,7 @@ namespace osu.Game.Database if (CanUndelete(existing, item)) { Undelete(existing); - Logger.Log($"Found existing {nameof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database); + Logger.Log($"Found existing {humanisedModelName} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database); handleEvent(() => ItemAdded?.Invoke(existing, true)); // existing item will be used; rollback new import and exit early. @@ -610,6 +608,8 @@ namespace osu.Game.Database private DbSet queryModel() => ContextFactory.Get().Set(); + private string humanisedModelName => $"{typeof(TModel).Name.Replace("Info", "").ToLower()}"; + /// /// Creates an from a valid storage path. /// From 3fc604b60a6a2ee39b99c06beb3b0d3d6fb944fd Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Mon, 10 Jun 2019 13:18:38 +0300 Subject: [PATCH 0999/2854] Add Availability to BeatmapSetOnlineInfo --- osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs | 16 +++++++++++++++- .../API/Requests/Responses/APIBeatmapSet.cs | 7 +++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index 0ccc9a924c..e88def6321 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -65,6 +65,11 @@ namespace osu.Game.Beatmaps /// The amount of people who have favourited this beatmap set. /// public int FavouriteCount { get; set; } + + /// + /// The availability of this beatmap set. + /// + public BeatmapSetOnlineAvailability Availability { get; set; } } public class BeatmapSetOnlineCovers @@ -73,7 +78,7 @@ namespace osu.Game.Beatmaps [JsonProperty(@"cover@2x")] public string Cover { get; set; } - + public string CardLowRes { get; set; } [JsonProperty(@"card@2x")] @@ -84,4 +89,13 @@ namespace osu.Game.Beatmaps [JsonProperty(@"list@2x")] public string List { get; set; } } + + public class BeatmapSetOnlineAvailability + { + [JsonProperty(@"download_disabled")] + public bool DownloadDisabled { get; set; } + + [JsonProperty(@"more_information")] + public string ExternalLink { get; set; } + } } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 1abb7c1a7d..2b8a783e2e 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -60,6 +60,12 @@ namespace osu.Game.Online.API.Requests.Responses set => Author.Id = value; } + [JsonProperty(@"availability")] + private BeatmapSetOnlineAvailability availability { get; set; } + + [JsonProperty(@"download_unavailable")] + private bool test { get; set; } + [JsonProperty(@"beatmaps")] private IEnumerable beatmaps { get; set; } @@ -83,6 +89,7 @@ namespace osu.Game.Online.API.Requests.Responses Submitted = submitted, Ranked = ranked, LastUpdated = lastUpdated, + Availability = availability, }, Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(), }; From 54497fb1e78dd5447111cd256067f3959c1a61dd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 19:33:23 +0900 Subject: [PATCH 1000/2854] Fix prefixing spaces in BeatmapInfo's ToString when metadata is not populated yet --- osu.Game/Beatmaps/BeatmapInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 52238c26fe..3c082bb71e 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -119,7 +119,7 @@ namespace osu.Game.Beatmaps /// public List Scores { get; set; } - public override string ToString() => $"{Metadata} [{Version}]"; + public override string ToString() => $"{Metadata} [{Version}]".Trim(); public bool Equals(BeatmapInfo other) { From 29945f27c5f22bcbb36ed693d813fd34707053ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 19:33:55 +0900 Subject: [PATCH 1001/2854] Fix imported count incrementing on failures --- osu.Game/Database/ArchiveModelManager.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 93e924fab5..4e9478dc10 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -162,6 +162,7 @@ namespace osu.Game.Database { imported.Add(model); + Interlocked.Increment(ref current); notification.Text = $"Imported {current} of {paths.Length} {humanisedModelName}s"; notification.Progress = (float)current / paths.Length; } @@ -174,10 +175,6 @@ namespace osu.Game.Database { Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})"); } - finally - { - Interlocked.Increment(ref current); - } })); if (imported.Count == 0) From 6ca2fcebfce6467398d12fc69911ffb3f1c03eff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 19:34:32 +0900 Subject: [PATCH 1002/2854] Centalise and prefix all ArchiveModelManager database logging --- osu.Game/Beatmaps/BeatmapManager.cs | 28 ++++++++++-------- osu.Game/Database/ArchiveModelManager.cs | 36 +++++++++++++++--------- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 435edcf722..47411c69ec 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -110,7 +110,7 @@ namespace osu.Game.Beatmaps validateOnlineIds(beatmapSet); - await Task.WhenAll(beatmapSet.Beatmaps.Select(b => updateQueue.Perform(b, cancellationToken)).ToArray()); + await updateQueue.Perform(beatmapSet, cancellationToken); } protected override void PreImport(BeatmapSetInfo beatmapSet) @@ -127,7 +127,7 @@ namespace osu.Game.Beatmaps { Delete(existingOnlineId); beatmaps.PurgeDeletable(s => s.ID == existingOnlineId.ID); - Logger.Log($"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineBeatmapSetID}). It has been purged.", LoggingTarget.Database); + LogForModel(beatmapSet, $"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineBeatmapSetID}). It has been purged."); } } } @@ -433,23 +433,29 @@ namespace osu.Game.Beatmaps this.api = api; } - public Task Perform(BeatmapInfo beatmap, CancellationToken cancellationToken) - => Task.Factory.StartNew(() => perform(beatmap, cancellationToken), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler); - - private void perform(BeatmapInfo beatmap, CancellationToken cancellation) + public async Task Perform(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken) { - cancellation.ThrowIfCancellationRequested(); - if (api?.State != APIState.Online) return; - Logger.Log("Attempting online lookup for the missing values...", LoggingTarget.Database); + LogForModel(beatmapSet, "Performing online lookups..."); + await Task.WhenAll(beatmapSet.Beatmaps.Select(b => Perform(beatmapSet, b, cancellationToken)).ToArray()); + } + + // todo: expose this when we need to do individual difficulty lookups. + protected Task Perform(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken) + => Task.Factory.StartNew(() => perform(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler); + + private void perform(BeatmapSetInfo set, BeatmapInfo beatmap) + { + if (api?.State != APIState.Online) + return; var req = new GetBeatmapRequest(beatmap); req.Success += res => { - Logger.Log($"Successfully mapped to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.", LoggingTarget.Database); + LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}."); beatmap.Status = res.Status; beatmap.BeatmapSet.Status = res.BeatmapSet.Status; @@ -457,7 +463,7 @@ namespace osu.Game.Beatmaps beatmap.OnlineBeatmapID = res.OnlineBeatmapID; }; - req.Failure += e => { Logger.Log($"Failed ({e})", LoggingTarget.Database); }; + req.Failure += e => { LogForModel(set, $"Online retrieval failed for {beatmap}", e); }; // intentionally blocking to limit web request concurrency req.Perform(api); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 4e9478dc10..844ae622a5 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -173,7 +173,7 @@ namespace osu.Game.Database } catch (Exception e) { - Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})"); + Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})", LoggingTarget.Database); } })); @@ -227,7 +227,7 @@ namespace osu.Game.Database } catch (Exception e) { - Logger.Error(e, $@"Could not delete original file after import ({Path.GetFileName(path)})"); + LogForModel(import, $@"Could not delete original file after import ({Path.GetFileName(path)})", e); } return import; @@ -247,7 +247,7 @@ namespace osu.Game.Database { cancellationToken.ThrowIfCancellationRequested(); - TModel model; + TModel model = null; try { @@ -263,7 +263,7 @@ namespace osu.Game.Database } catch (Exception e) { - Logger.Error(e, $"Model creation of {archive.Name} failed.", LoggingTarget.Database); + LogForModel(model, $"Model creation of {archive.Name} failed.", e); return null; } @@ -277,6 +277,16 @@ namespace osu.Game.Database /// protected abstract string[] HashableFileTypes { get; } + protected static void LogForModel(TModel model, string message, Exception e = null) + { + string prefix = $"[{(model?.Hash ?? "?????").Substring(0, 5)}]"; + + if (e != null) + Logger.Error(e, $"{prefix} {message}", LoggingTarget.Database); + else + Logger.Log($"{prefix} {message}", LoggingTarget.Database); + } + /// /// Create a SHA-2 hash from the provided archive based on file content of all files matching . /// @@ -308,14 +318,14 @@ namespace osu.Game.Database if (!Delete(item)) { // We may have not yet added the model to the underlying table, but should still clean up files. - Logger.Log($"Dereferencing files for incomplete import of {item}.", LoggingTarget.Database); + LogForModel(item, "Dereferencing files for incomplete import."); Files.Dereference(item.Files.Select(f => f.FileInfo).ToArray()); } } try { - Logger.Log($"Importing {item}...", LoggingTarget.Database); + LogForModel(item, "Beginning import..."); item.Files = archive != null ? createFileInfos(archive, Files) : new List(); @@ -334,7 +344,7 @@ namespace osu.Game.Database if (CanUndelete(existing, item)) { Undelete(existing); - Logger.Log($"Found existing {humanisedModelName} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database); + LogForModel(item, $"Found existing {humanisedModelName} for {item} (ID {existing.ID}) – skipping import."); handleEvent(() => ItemAdded?.Invoke(existing, true)); // existing item will be used; rollback new import and exit early. @@ -342,11 +352,9 @@ namespace osu.Game.Database flushEvents(true); return existing; } - else - { - Delete(existing); - ModelStore.PurgeDeletable(s => s.ID == existing.ID); - } + + Delete(existing); + ModelStore.PurgeDeletable(s => s.ID == existing.ID); } PreImport(item); @@ -361,12 +369,12 @@ namespace osu.Game.Database } } - Logger.Log($"Import of {item} successfully completed!", LoggingTarget.Database); + LogForModel(item, "Import successfully completed!"); } catch (Exception e) { if (!(e is TaskCanceledException)) - Logger.Error(e, $"Import of {item} failed and has been rolled back.", LoggingTarget.Database); + LogForModel(item, "Database import or population failed and has been rolled back.", e); rollback(); flushEvents(false); From adbf4d374e6f90f5a53e3abde23fc9b14f994bad Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Mon, 10 Jun 2019 14:15:49 +0300 Subject: [PATCH 1003/2854] Redirecting ShowBeatmapSet to FetchAndShowBeatmapSet --- osu.Game/Overlays/BeatmapSetOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 3ed398d31a..b7331f551e 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -120,8 +120,8 @@ namespace osu.Game.Overlays public void ShowBeatmapSet(BeatmapSetInfo set) { - beatmapSet.Value = set; - Show(); + // Re-fetching is the correct way forward. + FetchAndShowBeatmapSet((int)set.OnlineBeatmapSetID); scroll.ScrollTo(0); } } From 2b768bef96e28f438116f9458ce61130e76c17a5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 10 Jun 2019 20:29:01 +0900 Subject: [PATCH 1004/2854] Make CursorTrail use VertexBatch --- .../UI/Cursor/CursorTrail.cs | 76 ++++++------------- 1 file changed, 22 insertions(+), 54 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 2276b9f9f4..b986076593 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -6,7 +6,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.OpenGL.Buffers; +using osu.Framework.Graphics.Batches; using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shaders; @@ -57,7 +57,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor // InvalidationID 1 forces an update of each part of the cursor trail the first time ApplyState is run on the draw node // This is to prevent garbage data from being sent to the vertex shader, resulting in visual issues on some platforms parts[i].InvalidationID = 1; - parts[i].WasUpdated = true; } } @@ -149,7 +148,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor public Vector2 Position; public float Time; public long InvalidationID; - public bool WasUpdated; } private class TrailDrawNode : DrawNode @@ -164,16 +162,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private readonly TrailPart[] parts = new TrailPart[max_sprites]; private Vector2 size; - private readonly VertexBuffer vertexBuffer = new QuadVertexBuffer(max_sprites, BufferUsageHint.DynamicDraw); + private readonly VertexBatch vertexBatch = new QuadBatch(max_sprites, 1); public TrailDrawNode(CursorTrail source) : base(source) { for (int i = 0; i < max_sprites; i++) - { parts[i].InvalidationID = 0; - parts[i].WasUpdated = false; - } } public override void ApplyState() @@ -194,56 +189,29 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor public override void Draw(Action vertexAction) { - shader.GetUniform("g_FadeClock").UpdateValue(ref time); - - int updateStart = -1, updateEnd = 0; - - for (int i = 0; i < parts.Length; ++i) - { - if (parts[i].WasUpdated) - { - if (updateStart == -1) - updateStart = i; - updateEnd = i + 1; - - int start = i * 4; - int end = start; - - Vector2 pos = parts[i].Position; - float localTime = parts[i].Time; - - DrawQuad( - texture, - new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y), - DrawColourInfo.Colour, - null, - v => vertexBuffer.Vertices[end++] = new TexturedTrailVertex - { - Position = v.Position, - TexturePosition = v.TexturePosition, - Time = localTime + 1, - Colour = v.Colour, - }); - - parts[i].WasUpdated = false; - } - else if (updateStart != -1) - { - vertexBuffer.UpdateRange(updateStart * 4, updateEnd * 4); - updateStart = -1; - } - } - - // Update all remaining vertices that have been changed. - if (updateStart != -1) - vertexBuffer.UpdateRange(updateStart * 4, updateEnd * 4); - base.Draw(vertexAction); shader.Bind(); + shader.GetUniform("g_FadeClock").UpdateValue(ref time); - texture.TextureGL.Bind(); - vertexBuffer.Draw(); + for (int i = 0; i < parts.Length; ++i) + { + Vector2 pos = parts[i].Position; + float localTime = parts[i].Time; + + DrawQuad( + texture, + new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y), + DrawColourInfo.Colour, + null, + v => vertexBatch.Add(new TexturedTrailVertex + { + Position = v.Position, + TexturePosition = v.TexturePosition, + Time = localTime + 1, + Colour = v.Colour, + })); + } shader.Unbind(); } @@ -252,7 +220,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { base.Dispose(isDisposing); - vertexBuffer.Dispose(); + vertexBatch.Dispose(); } } From e87123342cab2004c7baea09310a1ddf83b6b547 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jun 2019 23:50:44 +0900 Subject: [PATCH 1005/2854] Load results pages asynchronously Reduces performance burden when first displaying pages. Closes #4977. --- osu.Game/Screens/Ranking/Results.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index bebeaee00a..370c856d1d 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -275,7 +275,7 @@ namespace osu.Game.Screens.Ranking currentPage = page.NewValue?.CreatePage(); if (currentPage != null) - circleInner.Add(currentPage); + LoadComponentAsync(currentPage, circleInner.Add); }, true); } From 71e15fe0f112aa6fb693c54536bb486ad97eedef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Jun 2019 01:18:49 +0900 Subject: [PATCH 1006/2854] Fix incorrect xmldoc in OsuAnimatedButton --- osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs index 236b72766f..1a8fea4ff9 100644 --- a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs @@ -19,14 +19,14 @@ namespace osu.Game.Graphics.UserInterface public class OsuAnimatedButton : OsuClickableContainer { /// - /// The colour that should be flashed when the is clicked. + /// The colour that should be flashed when the is clicked. /// protected Color4 FlashColour = Color4.White.Opacity(0.3f); private Color4 hoverColour = Color4.White.Opacity(0.1f); /// - /// The background colour of the while it is hovered. + /// The background colour of the while it is hovered. /// protected Color4 HoverColour { From 3202110b80d805aed875587ac4935af6ea268cab Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Mon, 10 Jun 2019 20:17:44 +0300 Subject: [PATCH 1007/2854] Add a container for Beatmap Availability --- .../API/Requests/Responses/APIBeatmapSet.cs | 3 - .../BeatmapSet/BeatmapNotAvailable.cs | 69 +++++++++++++++++++ osu.Game/Overlays/BeatmapSet/Header.cs | 62 ++++++++++++----- 3 files changed, 112 insertions(+), 22 deletions(-) create mode 100644 osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 2b8a783e2e..82af723a9a 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -63,9 +63,6 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"availability")] private BeatmapSetOnlineAvailability availability { get; set; } - [JsonProperty(@"download_unavailable")] - private bool test { get; set; } - [JsonProperty(@"beatmaps")] private IEnumerable beatmaps { get; set; } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs b/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs new file mode 100644 index 0000000000..15463b353a --- /dev/null +++ b/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs @@ -0,0 +1,69 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Text; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class BeatmapNotAvailable : Container + { + private LinkFlowContainer linkContainer; + + public override void Show() + { + AutoSizeAxes = Axes.Both; + Margin = new MarginPadding() { Top = 10 }; + + Children = new Drawable[] + { + new Box + { + Colour = Color4.Black.Opacity(0.6f), + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Margin = new MarginPadding() { Top = 10, Left = 5, Right = 20 }, + + Children = new Drawable[] + { + new OsuSpriteText + { + Margin = new MarginPadding() { Bottom = 10, Horizontal = 5 }, + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium), + Text = "This beatmap is currently not available for download.", + Colour = Color4.Orange, + }, + linkContainer = new LinkFlowContainer(text => text.Font = OsuFont.GetFont(size: 14)) + { + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Bottom = 10, Horizontal = 5 }, + }, + }, + }, + }; + + base.Show(); + } + + public string Link + { + set => linkContainer.AddLink("Check here for more information.", value); + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index a0f71d05c0..0b24a8cdd8 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.BeatmapSet.Buttons; @@ -32,6 +33,7 @@ namespace osu.Game.Overlays.BeatmapSet private readonly UpdateableBeatmapSetCover cover; private readonly OsuSpriteText title, artist; private readonly AuthorInfo author; + private readonly BeatmapNotAvailable unavailableContainer; private readonly FillFlowContainer downloadButtonsContainer; private readonly BeatmapSetOnlineStatusPill onlineStatusPill; public Details Details; @@ -134,6 +136,7 @@ namespace osu.Game.Overlays.BeatmapSet Margin = new MarginPadding { Top = 20 }, Child = author = new AuthorInfo(), }, + unavailableContainer = new BeatmapNotAvailable(), new Container { RelativeSizeAxes = Axes.X, @@ -207,6 +210,18 @@ namespace osu.Game.Overlays.BeatmapSet downloadButtonsContainer.FadeOut(transition_duration); favouriteButton.FadeOut(transition_duration); } + + if (setInfo.NewValue?.OnlineInfo.Availability?.DownloadDisabled ?? false) + { + this.ResizeHeightTo(460, transition_duration / 2); + unavailableContainer.Show(); + unavailableContainer.Link = setInfo.NewValue.OnlineInfo.Availability.ExternalLink; + } + else + { + this.ResizeHeightTo(400, transition_duration / 2); + unavailableContainer.Hide(); + } updateDownloadButtons(); }, true); @@ -216,28 +231,37 @@ namespace osu.Game.Overlays.BeatmapSet { if (BeatmapSet.Value == null) return; - switch (State.Value) + if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) { - case DownloadState.LocallyAvailable: - // temporary for UX until new design is implemented. - downloadButtonsContainer.Child = new osu.Game.Overlays.Direct.DownloadButton(BeatmapSet.Value) - { - Width = 50, - RelativeSizeAxes = Axes.Y - }; - break; + downloadButtonsContainer.RemoveAll(x => true); + return; + } - case DownloadState.Downloading: - case DownloadState.Downloaded: - // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design. - downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); - break; + else + { + switch (State.Value) + { + case DownloadState.LocallyAvailable: + // temporary for UX until new design is implemented. + downloadButtonsContainer.Child = new osu.Game.Overlays.Direct.DownloadButton(BeatmapSet.Value) + { + Width = 50, + RelativeSizeAxes = Axes.Y + }; + break; - default: - downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); - if (BeatmapSet.Value.OnlineInfo.HasVideo) - downloadButtonsContainer.Add(new DownloadButton(BeatmapSet.Value, true)); - break; + case DownloadState.Downloading: + case DownloadState.Downloaded: + // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design. + downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); + break; + + default: + downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); + if (BeatmapSet.Value.OnlineInfo.HasVideo) + downloadButtonsContainer.Add(new DownloadButton(BeatmapSet.Value, true)); + break; + } } } } From 70fdd4ba5b4fe1cafca5dbbe84d1eca9e6cf4c57 Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Mon, 10 Jun 2019 21:13:37 +0300 Subject: [PATCH 1008/2854] Disable download button + Fix AppVeyor Errors --- osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs | 2 +- .../BeatmapSet/BeatmapNotAvailable.cs | 10 ++--- osu.Game/Overlays/BeatmapSet/Header.cs | 44 +++++++++---------- osu.Game/Overlays/BeatmapSetOverlay.cs | 2 +- osu.Game/Overlays/Direct/DownloadButton.cs | 6 +++ 5 files changed, 31 insertions(+), 33 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index e88def6321..ea3f0b61b9 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -78,7 +78,7 @@ namespace osu.Game.Beatmaps [JsonProperty(@"cover@2x")] public string Cover { get; set; } - + public string CardLowRes { get; set; } [JsonProperty(@"card@2x")] diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs b/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs index 15463b353a..b893fd0703 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs @@ -1,9 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; -using System.Text; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -11,7 +8,6 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osuTK; using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapSet @@ -23,7 +19,7 @@ namespace osu.Game.Overlays.BeatmapSet public override void Show() { AutoSizeAxes = Axes.Both; - Margin = new MarginPadding() { Top = 10 }; + Margin = new MarginPadding { Top = 10 }; Children = new Drawable[] { @@ -36,13 +32,13 @@ namespace osu.Game.Overlays.BeatmapSet { AutoSizeAxes = Axes.Both, Direction = FillDirection.Vertical, - Margin = new MarginPadding() { Top = 10, Left = 5, Right = 20 }, + Margin = new MarginPadding { Top = 10, Left = 5, Right = 20 }, Children = new Drawable[] { new OsuSpriteText { - Margin = new MarginPadding() { Bottom = 10, Horizontal = 5 }, + Margin = new MarginPadding { Bottom = 10, Horizontal = 5 }, Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium), Text = "This beatmap is currently not available for download.", Colour = Color4.Orange, diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 0b24a8cdd8..9a4e7d4754 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.BeatmapSet.Buttons; @@ -210,7 +209,7 @@ namespace osu.Game.Overlays.BeatmapSet downloadButtonsContainer.FadeOut(transition_duration); favouriteButton.FadeOut(transition_duration); } - + if (setInfo.NewValue?.OnlineInfo.Availability?.DownloadDisabled ?? false) { this.ResizeHeightTo(460, transition_duration / 2); @@ -237,31 +236,28 @@ namespace osu.Game.Overlays.BeatmapSet return; } - else + switch (State.Value) { - switch (State.Value) - { - case DownloadState.LocallyAvailable: - // temporary for UX until new design is implemented. - downloadButtonsContainer.Child = new osu.Game.Overlays.Direct.DownloadButton(BeatmapSet.Value) - { - Width = 50, - RelativeSizeAxes = Axes.Y - }; - break; + case DownloadState.LocallyAvailable: + // temporary for UX until new design is implemented. + downloadButtonsContainer.Child = new osu.Game.Overlays.Direct.DownloadButton(BeatmapSet.Value) + { + Width = 50, + RelativeSizeAxes = Axes.Y + }; + break; - case DownloadState.Downloading: - case DownloadState.Downloaded: - // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design. - downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); - break; + case DownloadState.Downloading: + case DownloadState.Downloaded: + // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design. + downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); + break; - default: - downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); - if (BeatmapSet.Value.OnlineInfo.HasVideo) - downloadButtonsContainer.Add(new DownloadButton(BeatmapSet.Value, true)); - break; - } + default: + downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); + if (BeatmapSet.Value.OnlineInfo.HasVideo) + downloadButtonsContainer.Add(new DownloadButton(BeatmapSet.Value, true)); + break; } } } diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index b7331f551e..8d3d78e79a 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -121,7 +121,7 @@ namespace osu.Game.Overlays public void ShowBeatmapSet(BeatmapSetInfo set) { // Re-fetching is the correct way forward. - FetchAndShowBeatmapSet((int)set.OnlineBeatmapSetID); + FetchAndShowBeatmapSet(set.OnlineBeatmapSetID ?? 0); scroll.ScrollTo(0); } } diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs index 3f44d854e5..deccf819ea 100644 --- a/osu.Game/Overlays/Direct/DownloadButton.cs +++ b/osu.Game/Overlays/Direct/DownloadButton.cs @@ -77,6 +77,12 @@ namespace osu.Game.Overlays.Direct { this.colours = colours; + if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) + { + button.Enabled.Value = false; + button.TooltipText = "Unavailable"; + } + button.Action = () => { switch (State.Value) From 75716af25e67f997a669096e0cddc012c6b5e881 Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Mon, 10 Jun 2019 21:14:12 +0300 Subject: [PATCH 1009/2854] Forgot to return --- osu.Game/Overlays/Direct/DownloadButton.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs index deccf819ea..33e09e95aa 100644 --- a/osu.Game/Overlays/Direct/DownloadButton.cs +++ b/osu.Game/Overlays/Direct/DownloadButton.cs @@ -81,6 +81,7 @@ namespace osu.Game.Overlays.Direct { button.Enabled.Value = false; button.TooltipText = "Unavailable"; + return; } button.Action = () => From fc3e1e6a8686ccb1a8a85af180f3da3302910ade Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Mon, 10 Jun 2019 21:50:08 +0300 Subject: [PATCH 1010/2854] Fix issue --- osu.Game/Overlays/BeatmapSetOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 8d3d78e79a..6bd2e1b72e 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -121,7 +121,7 @@ namespace osu.Game.Overlays public void ShowBeatmapSet(BeatmapSetInfo set) { // Re-fetching is the correct way forward. - FetchAndShowBeatmapSet(set.OnlineBeatmapSetID ?? 0); + FetchAndShowBeatmapSet(set?.OnlineBeatmapSetID ?? 0); scroll.ScrollTo(0); } } From 94a7794ff71382d93cd28397aecb74d8ae95697a Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 10 Jun 2019 21:54:33 +0300 Subject: [PATCH 1011/2854] Fix issue --- osu.Game/Overlays/BeatmapSetOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 8d3d78e79a..6bd2e1b72e 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -121,7 +121,7 @@ namespace osu.Game.Overlays public void ShowBeatmapSet(BeatmapSetInfo set) { // Re-fetching is the correct way forward. - FetchAndShowBeatmapSet(set.OnlineBeatmapSetID ?? 0); + FetchAndShowBeatmapSet(set?.OnlineBeatmapSetID ?? 0); scroll.ScrollTo(0); } } From d4ba67747b37f4db9a0a482a5fef54470a6bcbfc Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 10 Jun 2019 18:58:03 +0900 Subject: [PATCH 1012/2854] fix test count --- .idea/.idea.osu/.idea/.name | 1 + .../Visual/SongSelect/TestScenePlaySongSelect.cs | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 .idea/.idea.osu/.idea/.name diff --git a/.idea/.idea.osu/.idea/.name b/.idea/.idea.osu/.idea/.name new file mode 100644 index 0000000000..21cb4db60e --- /dev/null +++ b/.idea/.idea.osu/.idea/.name @@ -0,0 +1 @@ +osu \ No newline at end of file diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index bc15bc6b6d..fa73a14252 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -11,6 +11,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Extensions; +using osu.Framework.Logging; using osu.Framework.MathUtils; using osu.Framework.Platform; using osu.Framework.Screens; @@ -215,11 +216,13 @@ namespace osu.Game.Tests.Visual.SongSelect { const int test_count = 10; int beatmapChangedCount = 0; + int debounceCount = 0; createSongSelect(); - AddStep("Setup counter", () => + AddStep("Setup counters", () => { beatmapChangedCount = 0; - songSelect.Carousel.SelectionChanged += _ => beatmapChangedCount++; + debounceCount = 0; + songSelect.Carousel.SelectionChanged += _ => beatmapChangedCount += 1; }); AddRepeatStep($"Create beatmaps {test_count} times", () => { @@ -229,10 +232,14 @@ namespace osu.Game.Tests.Visual.SongSelect { // Wait for debounce songSelect.Carousel.SelectNextRandom(); + ++debounceCount; }, 400); }, test_count); - AddAssert($"Beatmap changed {test_count} times", () => beatmapChangedCount == test_count); + AddUntilStep("Debounce limit reached", () => debounceCount == test_count); + + // The selected beatmap should have changed an additional 2 times since both initially loading songselect and the first import also triggers selectionChanged + AddAssert($"Beatmap changed {test_count + 2} times", () => beatmapChangedCount == test_count + 2); } [Test] From 609a82bc948dc14f8e604aa932d71b470e8657a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Jun 2019 14:28:52 +0900 Subject: [PATCH 1013/2854] Update VisibilityContainer usage in line with framework --- osu.Desktop/OsuGameDesktop.cs | 9 ++---- .../TestSceneResumeOverlay.cs | 4 +-- osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs | 2 +- .../Visual/Gameplay/TestSceneFailAnimation.cs | 2 +- .../Gameplay/TestSceneGameplayMenuOverlay.cs | 6 ++-- .../Visual/Gameplay/TestScenePause.cs | 4 +-- .../Visual/Gameplay/TestSceneStoryboard.cs | 2 +- .../Visual/Menus/TestSceneToolbar.cs | 2 +- .../TestSceneMatchSettingsOverlay.cs | 2 +- .../Online/TestSceneAccountCreationOverlay.cs | 2 +- .../Visual/Online/TestSceneChatDisplay.cs | 2 +- .../Visual/Settings/TestSceneSettings.cs | 2 +- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 9 +++--- .../Visual/UserInterface/TestSceneCursors.cs | 4 +-- .../UserInterface/TestSceneMusicController.cs | 4 +-- .../TestSceneNotificationOverlay.cs | 2 +- .../UserInterface/TestScenePopupDialog.cs | 2 +- .../Containers/OsuFocusedOverlayContainer.cs | 12 ++++---- osu.Game/Graphics/Containers/WaveContainer.cs | 4 +-- .../Graphics/Cursor/MenuCursorContainer.cs | 2 +- .../UserInterface/BreadcrumbControl.cs | 4 +++ .../UserInterface/ProcessingOverlay.cs | 2 +- osu.Game/OsuGame.cs | 28 +++++++++---------- osu.Game/Overlays/AccountCreationOverlay.cs | 2 +- osu.Game/Overlays/BeatmapSetOverlay.cs | 2 +- osu.Game/Overlays/ChangelogOverlay.cs | 8 +++--- osu.Game/Overlays/ChatOverlay.cs | 24 ++++++++-------- osu.Game/Overlays/DialogOverlay.cs | 8 +++--- osu.Game/Overlays/DirectOverlay.cs | 2 +- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 +-- osu.Game/Overlays/Music/PlaylistOverlay.cs | 2 +- osu.Game/Overlays/MusicController.cs | 4 +-- osu.Game/Overlays/NotificationOverlay.cs | 8 +++--- .../Sections/General/LoginSettings.cs | 2 +- osu.Game/Overlays/SettingsOverlay.cs | 11 ++++---- osu.Game/Overlays/Toolbar/Toolbar.cs | 4 +-- .../Toolbar/ToolbarOverlayToggleButton.cs | 20 ++++++------- osu.Game/Overlays/VolumeOverlay.cs | 6 ++-- .../Rulesets/UI/GameplayCursorContainer.cs | 2 +- osu.Game/Screens/Menu/ButtonArea.cs | 8 ++++-- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 2 +- .../Screens/Play/HUD/PlayerSettingsOverlay.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Play/SkipOverlay.cs | 14 ++++++---- osu.Game/Screens/Play/SongProgress.cs | 2 +- osu.Game/Screens/Select/BeatmapDetails.cs | 4 +-- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 4 +-- osu.Game/osu.Game.csproj | 4 ++- osu.sln | 12 ++++++++ 50 files changed, 150 insertions(+), 127 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 00cabbadf7..975b7f9f5a 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; using osu.Desktop.Overlays; -using osu.Framework.Graphics.Containers; using osu.Framework.Platform; using osu.Game; using osuTK.Input; @@ -56,7 +55,7 @@ namespace osu.Desktop LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, v => { Add(v); - v.State = Visibility.Visible; + v.Show(); }); if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows) @@ -74,13 +73,11 @@ namespace osu.Desktop { case Intro _: case MainMenu _: - if (versionManager != null) - versionManager.State = Visibility.Visible; + versionManager?.Show(); break; default: - if (versionManager != null) - versionManager.State = Visibility.Hidden; + versionManager?.Hide(); break; } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.cs index 12a3a8d27e..8e73d6152f 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.cs @@ -46,11 +46,11 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep("move mouse away", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.TopLeft)); AddStep("click", () => osuInputManager.GameClick()); - AddAssert("not dismissed", () => !resumeFired && resume.State == Visibility.Visible); + AddAssert("not dismissed", () => !resumeFired && resume.State.Value == Visibility.Visible); AddStep("move mouse back", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.Centre)); AddStep("click", () => osuInputManager.GameClick()); - AddAssert("dismissed", () => resumeFired && resume.State == Visibility.Hidden); + AddAssert("dismissed", () => resumeFired && resume.State.Value == Visibility.Hidden); } private class ManualOsuInputManager : OsuInputManager diff --git a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs index 0d4e7edb7b..9e5df0d6b1 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.UI private GameplayCursorContainer localCursorContainer; - public override CursorContainer LocalCursor => State == Visibility.Visible ? localCursorContainer : null; + public override CursorContainer LocalCursor => State.Value == Visibility.Visible ? localCursorContainer : null; protected override string Message => "Click the orange cursor to resume"; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs index 4878587dcd..f06f72615b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs @@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected override void AddCheckSteps() { AddUntilStep("wait for fail", () => Player.HasFailed); - AddUntilStep("wait for fail overlay", () => ((FailPlayer)Player).FailOverlay.State == Visibility.Visible); + AddUntilStep("wait for fail overlay", () => ((FailPlayer)Player).FailOverlay.State.Value == Visibility.Visible); } private class FailPlayer : TestPlayer diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs index ba9c583b08..4727140d99 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Show overlay", () => pauseOverlay.Show()); AddStep("Press select", () => press(GlobalAction.Select)); - AddAssert("Overlay still open", () => pauseOverlay.State == Visibility.Visible); + AddAssert("Overlay still open", () => pauseOverlay.State.Value == Visibility.Visible); AddStep("Hide overlay", () => pauseOverlay.Hide()); } @@ -237,7 +237,7 @@ namespace osu.Game.Tests.Visual.Gameplay }); AddAssert("Action was triggered", () => triggered); - AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden); + AddAssert("Overlay is closed", () => pauseOverlay.State.Value == Visibility.Hidden); } /// @@ -272,7 +272,7 @@ namespace osu.Game.Tests.Visual.Gameplay return triggered; }); - AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden); + AddAssert("Overlay is closed", () => pauseOverlay.State.Value == Visibility.Hidden); } private void press(Key key) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index 12e91df77c..0de0fcfc38 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -203,9 +203,9 @@ namespace osu.Game.Tests.Visual.Gameplay public new HUDOverlay HUDOverlay => base.HUDOverlay; - public bool FailOverlayVisible => FailOverlay.State == Visibility.Visible; + public bool FailOverlayVisible => FailOverlay.State.Value == Visibility.Visible; - public bool PauseOverlayVisible => PauseOverlay.State == Visibility.Visible; + public bool PauseOverlayVisible => PauseOverlay.State.Value == Visibility.Visible; public override void OnEntering(IScreen last) { diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs index 213cdf5e48..ead7a4b7fc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs @@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Gameplay { Origin = Anchor.TopRight, Anchor = Anchor.TopRight, - State = Visibility.Visible, + State = { Value = Visibility.Visible }, }); AddStep("Restart", restart); diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs index 0c789d8cb7..0df6605cdd 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Menus public TestSceneToolbar() { - var toolbar = new Toolbar { State = Visibility.Visible }; + var toolbar = new Toolbar { State = { Value = Visibility.Visible } }; ToolbarNotificationButton notificationButton = null; AddStep("create toolbar", () => diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs index 8091e93471..8d842fc865 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Multiplayer settings = new TestRoomSettings { RelativeSizeAxes = Axes.Both, - State = Visibility.Visible + State = { Value = Visibility.Visible } }; Child = settings; diff --git a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs index a7e725ec3f..35449f5687 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Online api.Logout(); api.LocalUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true); - AddStep("show", () => accountCreation.State = Visibility.Visible); + AddStep("show", () => accountCreation.Show()); AddStep("logout", () => api.Logout()); } } diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatDisplay.cs index 634176e65f..2789feef3d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatDisplay.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Online Children = new Drawable[] { channelManager, - new ChatOverlay { State = Visibility.Visible } + new ChatOverlay { State = { Value = Visibility.Visible } } }; } } diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettings.cs index 964754f8d0..f97ce8c69e 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettings.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Settings { settings = new SettingsOverlay { - State = Visibility.Visible + State = { Value = Visibility.Visible } }; Add(dialogOverlay = new DialogOverlay { diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 9969795ecf..932e114580 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; @@ -7,7 +7,6 @@ using JetBrains.Annotations; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; @@ -48,7 +47,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("show", () => { - infoWedge.State = Visibility.Visible; + infoWedge.Show(); infoWedge.Beatmap = Beatmap.Value; }); @@ -57,11 +56,11 @@ namespace osu.Game.Tests.Visual.SongSelect AddWaitStep("wait for select", 3); - AddStep("hide", () => { infoWedge.State = Visibility.Hidden; }); + AddStep("hide", () => { infoWedge.Hide(); }); AddWaitStep("wait for hide", 3); - AddStep("show", () => { infoWedge.State = Visibility.Visible; }); + AddStep("show", () => { infoWedge.Show(); }); foreach (var rulesetInfo in rulesets.AvailableRulesets) { diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs index 8fe31b7ad6..e7dbbc8bc4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs @@ -176,7 +176,7 @@ namespace osu.Game.Tests.Visual.UserInterface /// Checks if a cursor is visible. /// /// The cursor to check. - private bool checkVisible(CursorContainer cursorContainer) => cursorContainer.State == Visibility.Visible; + private bool checkVisible(CursorContainer cursorContainer) => cursorContainer.State.Value == Visibility.Visible; /// /// Checks if a cursor is at the current inputmanager screen position. @@ -218,7 +218,7 @@ namespace osu.Game.Tests.Visual.UserInterface }, Cursor = new TestCursorContainer { - State = providesUserCursor ? Visibility.Hidden : Visibility.Visible, + State = { Value = providesUserCursor ? Visibility.Hidden : Visibility.Visible }, } }; } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs index a62fd6467b..2f2a40925f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs @@ -23,8 +23,8 @@ namespace osu.Game.Tests.Visual.UserInterface }; Add(mc); - AddToggleStep(@"toggle visibility", state => mc.State = state ? Visibility.Visible : Visibility.Hidden); - AddStep(@"show", () => mc.State = Visibility.Visible); + AddToggleStep(@"toggle visibility", state => mc.State.Value = state ? Visibility.Visible : Visibility.Hidden); + AddStep(@"show", () => mc.Show()); AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state); } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index 71033fcd2f..6b7427cef5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.UserInterface Content.Add(displayedCount); - void setState(Visibility state) => AddStep(state.ToString(), () => manager.State = state); + void setState(Visibility state) => AddStep(state.ToString(), () => manager.State.Value = state); void checkProgressingCount(int expected) => AddAssert($"progressing count is {expected}", () => progressingNotifications.Count == expected); manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; }; diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs index 24140125e0..9ddd8f4038 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs @@ -16,7 +16,7 @@ namespace osu.Game.Tests.Visual.UserInterface var popup = new PopupDialog { RelativeSizeAxes = Axes.Both, - State = Framework.Graphics.Containers.Visibility.Visible, + State = { Value = Framework.Graphics.Containers.Visibility.Visible }, Icon = FontAwesome.Solid.AssistiveListeningSystems, HeaderText = @"This is a test popup", BodyText = "I can say lots of stuff and even wrap my words!", diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 8b34459710..f6db3102f2 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -54,7 +54,7 @@ namespace osu.Game.Graphics.Containers samplePopIn = audio.Samples.Get(@"UI/overlay-pop-in"); samplePopOut = audio.Samples.Get(@"UI/overlay-pop-out"); - StateChanged += onStateChanged; + State.ValueChanged += onStateChanged; } /// @@ -70,7 +70,7 @@ namespace osu.Game.Graphics.Containers { if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)) { - State = Visibility.Hidden; + Hide(); return true; } @@ -82,7 +82,7 @@ namespace osu.Game.Graphics.Containers switch (action) { case GlobalAction.Back: - State = Visibility.Hidden; + Hide(); return true; case GlobalAction.Select: @@ -94,9 +94,9 @@ namespace osu.Game.Graphics.Containers public bool OnReleased(GlobalAction action) => false; - private void onStateChanged(Visibility visibility) + private void onStateChanged(ValueChangedEvent state) { - switch (visibility) + switch (state.NewValue) { case Visibility.Visible: if (OverlayActivationMode.Value != OverlayActivation.Disabled) @@ -105,7 +105,7 @@ namespace osu.Game.Graphics.Containers if (BlockScreenWideMouse && DimMainContent) osuGame?.AddBlockingOverlay(this); } else - State = Visibility.Hidden; + Hide(); break; diff --git a/osu.Game/Graphics/Containers/WaveContainer.cs b/osu.Game/Graphics/Containers/WaveContainer.cs index 464682a0ad..f87909ab17 100644 --- a/osu.Game/Graphics/Containers/WaveContainer.cs +++ b/osu.Game/Graphics/Containers/WaveContainer.cs @@ -103,7 +103,7 @@ namespace osu.Game.Graphics.Containers protected override void PopIn() { foreach (var w in wavesContainer.Children) - w.State = Visibility.Visible; + w.Show(); this.FadeIn(100, Easing.OutQuint); contentContainer.MoveToY(0, APPEAR_DURATION, Easing.OutQuint); @@ -117,7 +117,7 @@ namespace osu.Game.Graphics.Containers contentContainer.MoveToY(DrawHeight * 2f, DISAPPEAR_DURATION, Easing.In); foreach (var w in wavesContainer.Children) - w.State = Visibility.Hidden; + w.Hide(); this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint); } diff --git a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs index 92e5ba6195..b7ea1ba56a 100644 --- a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs +++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs @@ -29,7 +29,7 @@ namespace osu.Game.Graphics.Cursor { AddRangeInternal(new Drawable[] { - Cursor = new MenuCursor { State = Visibility.Hidden }, + Cursor = new MenuCursor { State = { Value = Visibility.Hidden } }, content = new Container { RelativeSizeAxes = Axes.Both } }); } diff --git a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs index f5e57e5f27..d1e55fee24 100644 --- a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs +++ b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs @@ -82,6 +82,10 @@ namespace osu.Game.Graphics.UserInterface } } + public override void Hide() => State = Visibility.Hidden; + + public override void Show() => State = Visibility.Visible; + public BreadcrumbTabItem(T value) : base(value) { diff --git a/osu.Game/Graphics/UserInterface/ProcessingOverlay.cs b/osu.Game/Graphics/UserInterface/ProcessingOverlay.cs index 8b50f4a97a..d75e78e2d9 100644 --- a/osu.Game/Graphics/UserInterface/ProcessingOverlay.cs +++ b/osu.Game/Graphics/UserInterface/ProcessingOverlay.cs @@ -34,7 +34,7 @@ namespace osu.Game.Graphics.UserInterface RelativeSizeAxes = Axes.Both, Alpha = 0.9f, }, - new LoadingAnimation { State = Visibility.Visible } + new LoadingAnimation { State = { Value = Visibility.Visible } } }; } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index ba9abcdefc..d5fbcdfee3 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -132,12 +132,12 @@ namespace osu.Game public void CloseAllOverlays(bool hideToolbarElements = true) { foreach (var overlay in overlays) - overlay.State = Visibility.Hidden; + overlay.Hide(); if (hideToolbarElements) { foreach (var overlay in toolbarElements) - overlay.State = Visibility.Hidden; + overlay.Hide(); } } @@ -461,7 +461,7 @@ namespace osu.Game loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile(externalLinkOpener = new ExternalLinkOpener(), topMostOverlayContent.Add); - chatOverlay.StateChanged += state => channelManager.HighPollRate.Value = state == Visibility.Visible; + chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible; Add(externalLinkOpener = new ExternalLinkOpener()); @@ -470,9 +470,9 @@ namespace osu.Game foreach (var overlay in singleDisplaySideOverlays) { - overlay.StateChanged += state => + overlay.State.ValueChanged += state => { - if (state == Visibility.Hidden) return; + if (state.NewValue == Visibility.Hidden) return; singleDisplaySideOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); }; @@ -484,9 +484,9 @@ namespace osu.Game foreach (var overlay in informationalOverlays) { - overlay.StateChanged += state => + overlay.State.ValueChanged += state => { - if (state == Visibility.Hidden) return; + if (state.NewValue == Visibility.Hidden) return; informationalOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); }; @@ -498,12 +498,12 @@ namespace osu.Game foreach (var overlay in singleDisplayOverlays) { - overlay.StateChanged += state => + overlay.State.ValueChanged += state => { // informational overlays should be dismissed on a show or hide of a full overlay. informationalOverlays.ForEach(o => o.Hide()); - if (state == Visibility.Hidden) return; + if (state.NewValue == Visibility.Hidden) return; singleDisplayOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); }; @@ -518,16 +518,16 @@ namespace osu.Game { float offset = 0; - if (settings.State == Visibility.Visible) + if (settings.State.Value == Visibility.Visible) offset += ToolbarButton.WIDTH / 2; - if (notifications.State == Visibility.Visible) + if (notifications.State.Value == Visibility.Visible) offset -= ToolbarButton.WIDTH / 2; screenContainer.MoveToX(offset, SettingsPanel.TRANSITION_LENGTH, Easing.OutQuint); } - settings.StateChanged += _ => updateScreenOffset(); - notifications.StateChanged += _ => updateScreenOffset(); + settings.State.ValueChanged += _ => updateScreenOffset(); + notifications.State.ValueChanged += _ => updateScreenOffset(); } public class GameIdleTracker : IdleTracker @@ -768,7 +768,7 @@ namespace osu.Game if (newOsuScreen.HideOverlaysOnEnter) CloseAllOverlays(); else - Toolbar.State = Visibility.Visible; + Toolbar.Show(); } } diff --git a/osu.Game/Overlays/AccountCreationOverlay.cs b/osu.Game/Overlays/AccountCreationOverlay.cs index 52d2917677..89d8cbde11 100644 --- a/osu.Game/Overlays/AccountCreationOverlay.cs +++ b/osu.Game/Overlays/AccountCreationOverlay.cs @@ -109,7 +109,7 @@ namespace osu.Game.Overlays break; case APIState.Online: - State = Visibility.Hidden; + Hide(); break; } } diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 3ed398d31a..e0852a890c 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -92,7 +92,7 @@ namespace osu.Game.Overlays protected override bool OnClick(ClickEvent e) { - State = Visibility.Hidden; + Hide(); return true; } diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 4a6d53b480..67f195580e 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -92,7 +92,7 @@ namespace osu.Game.Overlays public void ShowListing() { Current.Value = null; - State = Visibility.Visible; + Show(); } /// @@ -106,7 +106,7 @@ namespace osu.Game.Overlays if (build == null) throw new ArgumentNullException(nameof(build)); Current.Value = build; - State = Visibility.Visible; + Show(); } public void ShowBuild([NotNull] string updateStream, [NotNull] string version) @@ -123,7 +123,7 @@ namespace osu.Game.Overlays ShowBuild(build); }); - State = Visibility.Visible; + Show(); } public override bool OnPressed(GlobalAction action) @@ -133,7 +133,7 @@ namespace osu.Game.Overlays case GlobalAction.Back: if (Current.Value == null) { - State = Visibility.Hidden; + Hide(); } else { diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index eb95fabe02..0c1cca3d49 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -56,7 +56,7 @@ namespace osu.Game.Overlays private readonly Container channelSelectionContainer; private readonly ChannelSelectionOverlay channelSelectionOverlay; - public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceivePositionalInputAt(screenSpacePos) || channelSelectionOverlay.State == Visibility.Visible && channelSelectionOverlay.ReceivePositionalInputAt(screenSpacePos); + public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceivePositionalInputAt(screenSpacePos) || channelSelectionOverlay.State.Value == Visibility.Visible && channelSelectionOverlay.ReceivePositionalInputAt(screenSpacePos); public ChatOverlay() { @@ -130,7 +130,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Height = 1, PlaceholderText = "type your message", - Exit = () => State = Visibility.Hidden, + Exit = Hide, OnCommit = postMessage, ReleaseFocusOnCommit = false, HoldFocus = true, @@ -163,19 +163,19 @@ namespace osu.Game.Overlays }; channelTabControl.Current.ValueChanged += current => channelManager.CurrentChannel.Value = current.NewValue; - channelTabControl.ChannelSelectorActive.ValueChanged += active => channelSelectionOverlay.State = active.NewValue ? Visibility.Visible : Visibility.Hidden; - channelSelectionOverlay.StateChanged += state => + channelTabControl.ChannelSelectorActive.ValueChanged += active => channelSelectionOverlay.State.Value = active.NewValue ? Visibility.Visible : Visibility.Hidden; + channelSelectionOverlay.State.ValueChanged += state => { - if (state == Visibility.Hidden && channelManager.CurrentChannel.Value == null) + if (state.NewValue == Visibility.Hidden && channelManager.CurrentChannel.Value == null) { - channelSelectionOverlay.State = Visibility.Visible; - State = Visibility.Hidden; + channelSelectionOverlay.Show(); + Hide(); return; } - channelTabControl.ChannelSelectorActive.Value = state == Visibility.Visible; + channelTabControl.ChannelSelectorActive.Value = state.NewValue == Visibility.Visible; - if (state == Visibility.Visible) + if (state.NewValue == Visibility.Visible) { textbox.HoldFocus = false; if (1f - ChatHeight.Value < channel_selection_min_height) @@ -195,7 +195,7 @@ namespace osu.Game.Overlays { textbox.Current.Disabled = true; currentChannelContainer.Clear(false); - channelSelectionOverlay.State = Visibility.Visible; + channelSelectionOverlay.Show(); return; } @@ -253,7 +253,7 @@ namespace osu.Game.Overlays double targetChatHeight = startDragChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y; // If the channel selection screen is shown, mind its minimum height - if (channelSelectionOverlay.State == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height) + if (channelSelectionOverlay.State.Value == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height) targetChatHeight = 1f - channel_selection_min_height; ChatHeight.Value = targetChatHeight; @@ -325,7 +325,7 @@ namespace osu.Game.Overlays this.MoveToY(Height, transition_length, Easing.InSine); this.FadeOut(transition_length, Easing.InSine); - channelSelectionOverlay.State = Visibility.Hidden; + channelSelectionOverlay.Hide(); textbox.HoldFocus = false; base.PopOut(); diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 2cc1c20a10..aaae7bcf5c 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -37,8 +37,8 @@ namespace osu.Game.Overlays dialogContainer.Add(currentDialog); currentDialog.Show(); - currentDialog.StateChanged += state => onDialogOnStateChanged(dialog, state); - State = Visibility.Visible; + currentDialog.State.ValueChanged += state => onDialogOnStateChanged(dialog, state.NewValue); + Show(); } protected override bool PlaySamplesOnStateChange => false; @@ -53,7 +53,7 @@ namespace osu.Game.Overlays dialog.Delay(PopupDialog.EXIT_DURATION).Expire(); if (dialog == currentDialog) - State = Visibility.Hidden; + Hide(); } protected override void PopIn() @@ -66,7 +66,7 @@ namespace osu.Game.Overlays { base.PopOut(); - if (currentDialog?.State == Visibility.Visible) + if (currentDialog?.State.Value == Visibility.Visible) { currentDialog.Hide(); return; diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 975bf4e3ca..7dcf76e41f 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -252,7 +252,7 @@ namespace osu.Game.Overlays if (!IsLoaded) return; - if (State == Visibility.Hidden) + if (State.Value == Visibility.Hidden) return; if (API == null) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index dec58f4c9e..8e5c9588ce 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -413,12 +413,12 @@ namespace osu.Game.Overlays.Mods { if (selectedMod != null) { - if (State == Visibility.Visible) sampleOn?.Play(); + if (State.Value == Visibility.Visible) sampleOn?.Play(); DeselectTypes(selectedMod.IncompatibleMods, true); } else { - if (State == Visibility.Visible) sampleOff?.Play(); + if (State.Value == Visibility.Visible) sampleOff?.Play(); } refreshSelectedMods(); diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 4431288a1a..ec3d708645 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -71,7 +71,7 @@ namespace osu.Game.Overlays.Music { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - ExitRequested = () => State = Visibility.Hidden, + ExitRequested = Hide, FilterChanged = search => list.Filter(search), Padding = new MarginPadding(10), }, diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index d7b915efe3..85524e992c 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -200,7 +200,7 @@ namespace osu.Game.Overlays beatmaps.ItemAdded += handleBeatmapAdded; beatmaps.ItemRemoved += handleBeatmapRemoved; - playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint); + playlist.State.ValueChanged += s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint); } private ScheduledDelegate seekDelegate; @@ -449,7 +449,7 @@ namespace osu.Game.Overlays // This is here mostly as a performance fix. // If the playlist is not hidden it will update children even when the music controller is hidden (due to AlwaysPresent). - playlist.State = Visibility.Hidden; + playlist.Hide(); this.FadeOut(transition_length, Easing.OutQuint); dragContainer.ScaleTo(0.9f, transition_length, Easing.OutQuint); diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 8f75d3ebf0..2e4c504645 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -81,13 +81,13 @@ namespace osu.Game.Overlays private void updateProcessingMode() { - bool enabled = OverlayActivationMode.Value == OverlayActivation.All || State == Visibility.Visible; + bool enabled = OverlayActivationMode.Value == OverlayActivation.All || State.Value == Visibility.Visible; notificationsEnabler?.Cancel(); if (enabled) // we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed. - notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, State == Visibility.Visible ? 0 : 1000); + notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, State.Value == Visibility.Visible ? 0 : 1000); else processingPosts = false; } @@ -96,7 +96,7 @@ namespace osu.Game.Overlays { base.LoadComplete(); - StateChanged += _ => updateProcessingMode(); + State.ValueChanged += _ => updateProcessingMode(); OverlayActivationMode.BindValueChanged(_ => updateProcessingMode(), true); } @@ -128,7 +128,7 @@ namespace osu.Game.Overlays section?.Add(notification, notification.DisplayOnTop ? -runningDepth : runningDepth); if (notification.IsImportant) - State = Visibility.Visible; + Show(); updateCounts(); }); diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index 2f56ace24d..36d6a22165 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -97,7 +97,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { new LoadingAnimation { - State = Visibility.Visible, + State = { Value = Visibility.Visible }, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }, diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 6e3eaae0a1..bb84de5d3a 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -9,6 +9,7 @@ using osu.Game.Overlays.Settings.Sections; using osuTK.Graphics; using System.Collections.Generic; using System.Linq; +using osu.Framework.Bindables; namespace osu.Game.Overlays { @@ -37,23 +38,23 @@ namespace osu.Game.Overlays { } - public override bool AcceptsFocus => subPanels.All(s => s.State != Visibility.Visible); + public override bool AcceptsFocus => subPanels.All(s => s.State.Value != Visibility.Visible); private T createSubPanel(T subPanel) where T : SettingsSubPanel { subPanel.Depth = 1; subPanel.Anchor = Anchor.TopRight; - subPanel.StateChanged += subPanelStateChanged; + subPanel.State.ValueChanged += subPanelStateChanged; subPanels.Add(subPanel); return subPanel; } - private void subPanelStateChanged(Visibility visibility) + private void subPanelStateChanged(ValueChangedEvent state) { - switch (visibility) + switch (state.NewValue) { case Visibility.Visible: Background.FadeTo(0.9f, 300, Easing.OutQuint); @@ -73,7 +74,7 @@ namespace osu.Game.Overlays } } - protected override float ExpandedPosition => subPanels.Any(s => s.State == Visibility.Visible) ? -WIDTH : base.ExpandedPosition; + protected override float ExpandedPosition => subPanels.Any(s => s.State.Value == Visibility.Visible) ? -WIDTH : base.ExpandedPosition; [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 3c8b96fe8a..982fb26b6b 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -84,10 +84,10 @@ namespace osu.Game.Overlays.Toolbar } }; - StateChanged += visibility => + State.ValueChanged += visibility => { if (overlayActivationMode.Value == OverlayActivation.Disabled) - State = Visibility.Hidden; + Hide(); }; if (osuGame != null) diff --git a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs index b2ae273e31..b286cbfb1d 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -15,6 +16,8 @@ namespace osu.Game.Overlays.Toolbar private OverlayContainer stateContainer; + private readonly Bindable overlayState = new Bindable(); + public OverlayContainer StateContainer { get => stateContainer; @@ -22,10 +25,12 @@ namespace osu.Game.Overlays.Toolbar { stateContainer = value; + overlayState.UnbindBindings(); + if (stateContainer != null) { Action = stateContainer.ToggleVisibility; - stateContainer.StateChanged += stateChanged; + overlayState.BindTo(stateContainer.State); } } } @@ -40,18 +45,13 @@ namespace osu.Game.Overlays.Toolbar Depth = 2, Alpha = 0, }); + + overlayState.ValueChanged += stateChanged; } - protected override void Dispose(bool isDisposing) + private void stateChanged(ValueChangedEvent state) { - base.Dispose(isDisposing); - if (stateContainer != null) - stateContainer.StateChanged -= stateChanged; - } - - private void stateChanged(Visibility state) - { - switch (state) + switch (state.NewValue) { case Visibility.Hidden: stateBackground.FadeOut(200); diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index 34b15d958d..02e0f59f26 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -100,14 +100,14 @@ namespace osu.Game.Overlays switch (action) { case GlobalAction.DecreaseVolume: - if (State == Visibility.Hidden) + if (State.Value == Visibility.Hidden) Show(); else volumeMeterMaster.Decrease(amount, isPrecise); return true; case GlobalAction.IncreaseVolume: - if (State == Visibility.Hidden) + if (State.Value == Visibility.Hidden) Show(); else volumeMeterMaster.Increase(amount, isPrecise); @@ -126,7 +126,7 @@ namespace osu.Game.Overlays public override void Show() { - if (State == Visibility.Visible) + if (State.Value == Visibility.Visible) schedulePopOut(); base.Show(); diff --git a/osu.Game/Rulesets/UI/GameplayCursorContainer.cs b/osu.Game/Rulesets/UI/GameplayCursorContainer.cs index 41edfa0b68..ae5f9c6111 100644 --- a/osu.Game/Rulesets/UI/GameplayCursorContainer.cs +++ b/osu.Game/Rulesets/UI/GameplayCursorContainer.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.UI protected override void Update() { base.Update(); - LastFrameState = State; + LastFrameState = State.Value; } } } diff --git a/osu.Game/Screens/Menu/ButtonArea.cs b/osu.Game/Screens/Menu/ButtonArea.cs index c7650a08fa..d59996a4eb 100644 --- a/osu.Game/Screens/Menu/ButtonArea.cs +++ b/osu.Game/Screens/Menu/ButtonArea.cs @@ -56,12 +56,12 @@ namespace osu.Game.Screens.Menu case ButtonSystemState.Exit: case ButtonSystemState.Initial: case ButtonSystemState.EnteringMode: - State = Visibility.Hidden; + Hide(); break; case ButtonSystemState.TopLevel: case ButtonSystemState.Play: - State = Visibility.Visible; + Show(); break; } @@ -82,6 +82,10 @@ namespace osu.Game.Screens.Menu } } + public override void Hide() => State = Visibility.Hidden; + + public override void Show() => State = Visibility.Visible; + public event Action StateChanged; private class ButtonAreaBackground : Box, IStateful diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 456fb4faf9..c7e762714c 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -59,7 +59,7 @@ namespace osu.Game.Screens.Play { RelativeSizeAxes = Axes.Both; - StateChanged += s => selectionIndex = -1; + State.ValueChanged += s => selectionIndex = -1; } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs index e99f6d836e..b2c3952f38 100644 --- a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs +++ b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Play.HUD } }; - State = Visibility.Visible; + Show(); } protected override void PopIn() => this.FadeIn(fade_duration); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index d8389fa6d9..35ef7b3200 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -358,7 +358,7 @@ namespace osu.Game.Screens.Play // There is a chance that we could be in a paused state as the ruleset's internal clock (see FrameStabilityContainer) // could process an extra frame after the GameplayClock is stopped. // In such cases we want the fail state to precede a user triggered pause. - if (PauseOverlay.State == Visibility.Visible) + if (PauseOverlay.State.Value == Visibility.Visible) PauseOverlay.Hide(); failAnimation.Start(); diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index 4ecc15f22b..38dd179f25 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Play { this.startTime = startTime; - State = Visibility.Visible; + Show(); RelativePositionAxes = Axes.Both; RelativeSizeAxes = Axes.X; @@ -136,7 +136,7 @@ namespace osu.Game.Screens.Play protected override bool OnMouseMove(MouseMoveEvent e) { if (!e.HasAnyButtonPressed) - fadeContainer.State = Visibility.Visible; + fadeContainer.Show(); return base.OnMouseMove(e); } @@ -181,7 +181,7 @@ namespace osu.Game.Screens.Play if (!IsHovered && !IsDragged) using (BeginDelayedSequence(1000)) - scheduledHide = Schedule(() => State = Visibility.Hidden); + scheduledHide = Schedule(Hide); break; case Visibility.Hidden: @@ -196,7 +196,7 @@ namespace osu.Game.Screens.Play protected override void LoadComplete() { base.LoadComplete(); - State = Visibility.Visible; + Show(); } protected override bool OnMouseDown(MouseDownEvent e) @@ -207,9 +207,13 @@ namespace osu.Game.Screens.Play protected override bool OnMouseUp(MouseUpEvent e) { - State = Visibility.Visible; + Show(); return base.OnMouseUp(e); } + + public override void Hide() => State = Visibility.Hidden; + + public override void Show() => State = Visibility.Visible; } private class Button : OsuClickableContainer diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index d478454f00..6642efdf8b 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -106,7 +106,7 @@ namespace osu.Game.Screens.Play protected override void LoadComplete() { - State = Visibility.Visible; + Show(); replayLoaded.ValueChanged += loaded => AllowSeeking = loaded.NewValue; replayLoaded.TriggerChange(); diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index a78ab97960..378b1b1dc6 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -360,13 +360,13 @@ namespace osu.Game.Screens.Select protected override void PopIn() { this.FadeIn(transition_duration, Easing.OutQuint); - loading.State = Visibility.Visible; + loading.Show(); } protected override void PopOut() { this.FadeOut(transition_duration, Easing.OutQuint); - loading.State = Visibility.Hidden; + loading.Hide(); } } } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 1508de2730..fa9ffd0706 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Select { void removeOldInfo() { - State = beatmap == null ? Visibility.Hidden : Visibility.Visible; + State.Value = beatmap == null ? Visibility.Hidden : Visibility.Visible; Info?.FadeOut(250); Info?.Expire(); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 6d5be607f4..5390d24892 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -278,7 +278,7 @@ namespace osu.Game.Screens.Select protected virtual void ExitFromBack() { - if (ModSelect.State == Visibility.Visible) + if (ModSelect.State.Value == Visibility.Visible) { ModSelect.Hide(); return; @@ -520,7 +520,7 @@ namespace osu.Game.Screens.Select if (base.OnExiting(next)) return true; - beatmapInfoWedge.State = Visibility.Hidden; + beatmapInfoWedge.Hide(); this.FadeOut(100); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index eeb1f2bee3..fb27caca11 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,10 +15,12 @@ - + + + diff --git a/osu.sln b/osu.sln index 3c38309d86..a106ab2800 100644 --- a/osu.sln +++ b/osu.sln @@ -25,6 +25,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Taiko.Tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Osu.Tests", "osu.Game.Rulesets.Osu.Tests\osu.Game.Rulesets.Osu.Tests.csproj", "{DECCCC75-67AD-4C3D-BB84-FD0E01323511}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework", "..\osu-framework\osu.Framework\osu.Framework.csproj", "{C7D2DA3C-97BF-403C-9F75-115C8A64DAC1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework.NativeLibs", "..\osu-framework\osu.Framework.NativeLibs\osu.Framework.NativeLibs.csproj", "{35AD7F4C-81DC-4060-BFD1-C376DC4C4801}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -79,6 +83,14 @@ Global {DECCCC75-67AD-4C3D-BB84-FD0E01323511}.Debug|Any CPU.Build.0 = Debug|Any CPU {DECCCC75-67AD-4C3D-BB84-FD0E01323511}.Release|Any CPU.ActiveCfg = Release|Any CPU {DECCCC75-67AD-4C3D-BB84-FD0E01323511}.Release|Any CPU.Build.0 = Release|Any CPU + {C7D2DA3C-97BF-403C-9F75-115C8A64DAC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7D2DA3C-97BF-403C-9F75-115C8A64DAC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7D2DA3C-97BF-403C-9F75-115C8A64DAC1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7D2DA3C-97BF-403C-9F75-115C8A64DAC1}.Release|Any CPU.Build.0 = Release|Any CPU + {35AD7F4C-81DC-4060-BFD1-C376DC4C4801}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {35AD7F4C-81DC-4060-BFD1-C376DC4C4801}.Debug|Any CPU.Build.0 = Debug|Any CPU + {35AD7F4C-81DC-4060-BFD1-C376DC4C4801}.Release|Any CPU.ActiveCfg = Release|Any CPU + {35AD7F4C-81DC-4060-BFD1-C376DC4C4801}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 8de62b608e76e268fea5d03551c0ba6a9fc10949 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Jun 2019 15:22:27 +0900 Subject: [PATCH 1014/2854] Allow FullscreenOverlay to surface to front on subsequent Show requests --- osu.Game/Overlays/FullscreenOverlay.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game/Overlays/FullscreenOverlay.cs b/osu.Game/Overlays/FullscreenOverlay.cs index 9706f75087..0911ee84de 100644 --- a/osu.Game/Overlays/FullscreenOverlay.cs +++ b/osu.Game/Overlays/FullscreenOverlay.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -40,6 +41,19 @@ namespace osu.Game.Overlays }; } + public override void Show() + { + if (State.Value == Visibility.Visible) + { + // re-trigger the state changed so we can potentially surface to front + State.TriggerChange(); + } + else + { + base.Show(); + } + } + protected override void PopIn() { base.PopIn(); From 620c2311ac887eebf82c8c31583661bfe0aba517 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Jun 2019 15:39:12 +0900 Subject: [PATCH 1015/2854] Add test --- .../Online/TestSceneFullscreenOverlay.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs index 6dc3428bff..fe8437be17 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs @@ -18,8 +18,24 @@ namespace osu.Game.Tests.Visual.Online { base.LoadComplete(); + int fireCount = 0; + Add(overlay = new TestFullscreenOverlay()); - AddStep(@"toggle", overlay.ToggleVisibility); + + overlay.State.ValueChanged += _ => fireCount++; + + AddStep(@"show", overlay.Show); + + AddAssert("fire count 1", () => fireCount == 1); + + AddStep(@"show again", overlay.Show); + + // this logic is specific to FullscreenOverlay + AddAssert("fire count 2", () => fireCount == 2); + + AddStep(@"hide", overlay.Hide); + + AddAssert("fire count 3", () => fireCount == 3); } private class TestFullscreenOverlay : FullscreenOverlay From 975bb3db8a2aba107858543eb12b419f363cad44 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 11 Jun 2019 15:51:14 +0900 Subject: [PATCH 1016/2854] cleanup --- .idea/.idea.osu/.idea/.name | 1 - osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 .idea/.idea.osu/.idea/.name diff --git a/.idea/.idea.osu/.idea/.name b/.idea/.idea.osu/.idea/.name deleted file mode 100644 index 21cb4db60e..0000000000 --- a/.idea/.idea.osu/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -osu \ No newline at end of file diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index fa73a14252..198da2c5b1 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -222,7 +222,7 @@ namespace osu.Game.Tests.Visual.SongSelect { beatmapChangedCount = 0; debounceCount = 0; - songSelect.Carousel.SelectionChanged += _ => beatmapChangedCount += 1; + songSelect.Carousel.SelectionChanged += _ => beatmapChangedCount++; }); AddRepeatStep($"Create beatmaps {test_count} times", () => { From a53ade07a5ea39046b5b90d240435664010021a3 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 11 Jun 2019 15:51:57 +0900 Subject: [PATCH 1017/2854] remove unused using --- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 198da2c5b1..2664c7a42c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -11,7 +11,6 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Extensions; -using osu.Framework.Logging; using osu.Framework.MathUtils; using osu.Framework.Platform; using osu.Framework.Screens; From 07e17518e93cc81d64e9215a9b1465a0e8859be4 Mon Sep 17 00:00:00 2001 From: Arphox Date: Tue, 11 Jun 2019 10:28:16 +0200 Subject: [PATCH 1018/2854] Fix all "Maintainability" CodeFactor issues --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 4 ++-- osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 4 ++-- osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs | 2 +- osu.Game/Graphics/Containers/HoldToConfirmContainer.cs | 2 +- osu.Game/Online/API/APIAccess.cs | 2 +- osu.Game/Online/Chat/MessageFormatter.cs | 2 +- osu.Game/Overlays/ChatOverlay.cs | 2 +- osu.Game/Rulesets/Edit/SelectionBlueprint.cs | 2 +- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- osu.Game/Scoring/Legacy/LegacyScoreParser.cs | 8 ++++---- 11 files changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index e7c7fd77df..90052d9b11 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -379,8 +379,8 @@ namespace osu.Game.Rulesets.Catch.UI X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * speed, 0, 1); // Correct overshooting. - if (hyperDashDirection > 0 && hyperDashTargetPosition < X || - hyperDashDirection < 0 && hyperDashTargetPosition > X) + if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) || + (hyperDashDirection < 0 && hyperDashTargetPosition > X)) { X = hyperDashTargetPosition; SetHyperDashState(); diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index b2beda18f4..7bb1f42802 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps break; if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance - || stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance) + || (stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)) { stackBaseIndex = n; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index ec23570f54..bc5d02258f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -37,11 +37,11 @@ namespace osu.Game.Rulesets.Osu.Mods if (time < osuHit.HitObject.StartTime - relax_leniency) continue; - if (osuHit.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime || osuHit.IsHit) + if ((osuHit.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime) || osuHit.IsHit) continue; requiresHit |= osuHit is DrawableHitCircle && osuHit.IsHovered && osuHit.HitObject.HitWindows.CanBeHit(relativetime); - requiresHold |= osuHit is DrawableSlider slider && (slider.Ball.IsHovered || osuHit.IsHovered) || osuHit is DrawableSpinner; + requiresHold |= (osuHit is DrawableSlider slider && (slider.Ball.IsHovered || osuHit.IsHovered)) || osuHit is DrawableSpinner; } if (requiresHit) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs index 8fe31b7ad6..f59cb75a46 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs @@ -192,7 +192,7 @@ namespace osu.Game.Tests.Visual.UserInterface public CursorContainer Cursor { get; } public bool ProvidingUserCursor { get; } - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || SmoothTransition && !ProvidingUserCursor; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || (SmoothTransition && !ProvidingUserCursor); private readonly Box background; diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs index a5b5b7af42..cda5e150de 100644 --- a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs +++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs @@ -27,7 +27,7 @@ namespace osu.Game.Graphics.Containers protected void BeginConfirm() { - if (confirming || !AllowMultipleFires && fired) return; + if (confirming || (!AllowMultipleFires && fired)) return; confirming = true; diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 594bc1e3ca..343d6a67b7 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -37,7 +37,7 @@ namespace osu.Game.Online.API public Bindable LocalUser { get; } = new Bindable(createGuestUser()); - protected bool HasLogin => authentication.Token.Value != null || !string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password); + protected bool HasLogin => authentication.Token.Value != null || (!string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password)); private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource(); diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index e1fc65da6c..4aaffdd161 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -69,7 +69,7 @@ namespace osu.Game.Online.Chat if (displayText.Length == 0 || linkText.Length == 0) continue; // Check for encapsulated links - if (result.Links.Find(l => l.Index <= index && l.Index + l.Length >= index + m.Length || index <= l.Index && index + m.Length >= l.Index + l.Length) == null) + if (result.Links.Find(l => (l.Index <= index && l.Index + l.Length >= index + m.Length) || (index <= l.Index && index + m.Length >= l.Index + l.Length)) == null) { result.Text = result.Text.Remove(index, m.Length).Insert(index, displayText); diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index eb95fabe02..978848d9fc 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -56,7 +56,7 @@ namespace osu.Game.Overlays private readonly Container channelSelectionContainer; private readonly ChannelSelectionOverlay channelSelectionOverlay; - public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceivePositionalInputAt(screenSpacePos) || channelSelectionOverlay.State == Visibility.Visible && channelSelectionOverlay.ReceivePositionalInputAt(screenSpacePos); + public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceivePositionalInputAt(screenSpacePos) || (channelSelectionOverlay.State == Visibility.Visible && channelSelectionOverlay.ReceivePositionalInputAt(screenSpacePos)); public ChatOverlay() { diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs index e94604554c..0f77b8d584 100644 --- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Edit /// public readonly DrawableHitObject HitObject; - protected override bool ShouldBeAlive => HitObject.IsAlive && HitObject.IsPresent || State == SelectionState.Selected; + protected override bool ShouldBeAlive => (HitObject.IsAlive && HitObject.IsPresent) || State == SelectionState.Selected; public override bool HandlePositionalInput => ShouldBeAlive; public override bool RemoveWhenNotAlive => false; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index e91100608b..ec7e6dc303 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Objects.Drawables public override bool RemoveCompletedTransforms => false; protected override bool RequiresChildrenUpdate => true; - public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Clock?.CurrentTime >= LifetimeStart; + public override bool IsPresent => base.IsPresent || (State.Value == ArmedState.Idle && Clock?.CurrentTime >= LifetimeStart); public readonly Bindable State = new Bindable(); diff --git a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs index d2c9ce81c3..0fdbd56c92 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs @@ -136,9 +136,9 @@ namespace osu.Game.Scoring.Legacy score.Rank = score.Mods.Any(m => m is ModHidden || m is ModFlashlight) ? ScoreRank.XH : ScoreRank.X; else if (ratio300 > 0.9 && ratio50 <= 0.01 && countMiss == 0) score.Rank = score.Mods.Any(m => m is ModHidden || m is ModFlashlight) ? ScoreRank.SH : ScoreRank.S; - else if (ratio300 > 0.8 && countMiss == 0 || ratio300 > 0.9) + else if ((ratio300 > 0.8 && countMiss == 0) || ratio300 > 0.9) score.Rank = ScoreRank.A; - else if (ratio300 > 0.7 && countMiss == 0 || ratio300 > 0.8) + else if ((ratio300 > 0.7 && countMiss == 0) || ratio300 > 0.8) score.Rank = ScoreRank.B; else if (ratio300 > 0.6) score.Rank = ScoreRank.C; @@ -159,9 +159,9 @@ namespace osu.Game.Scoring.Legacy score.Rank = score.Mods.Any(m => m is ModHidden || m is ModFlashlight) ? ScoreRank.XH : ScoreRank.X; else if (ratio300 > 0.9 && ratio50 <= 0.01 && countMiss == 0) score.Rank = score.Mods.Any(m => m is ModHidden || m is ModFlashlight) ? ScoreRank.SH : ScoreRank.S; - else if (ratio300 > 0.8 && countMiss == 0 || ratio300 > 0.9) + else if ((ratio300 > 0.8 && countMiss == 0) || ratio300 > 0.9) score.Rank = ScoreRank.A; - else if (ratio300 > 0.7 && countMiss == 0 || ratio300 > 0.8) + else if ((ratio300 > 0.7 && countMiss == 0) || ratio300 > 0.8) score.Rank = ScoreRank.B; else if (ratio300 > 0.6) score.Rank = ScoreRank.C; From e5417416a23a0ef743a53589ee42e5362f0f6937 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 11 Jun 2019 18:24:50 +0900 Subject: [PATCH 1019/2854] Remove braces --- osu.Game/Graphics/UserInterface/OsuCheckbox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index 5d41075725..5ead5987a1 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -81,7 +81,7 @@ namespace osu.Game.Graphics.UserInterface Nub.Current.BindTo(Current); - Current.DisabledChanged += disabled => { labelText.Alpha = Nub.Alpha = disabled ? 0.3f : 1; }; + Current.DisabledChanged += disabled => labelText.Alpha = Nub.Alpha = disabled ? 0.3f : 1; } protected override void LoadComplete() From 130ff5688694921246ee1dba0faaac987c4b46b3 Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Tue, 11 Jun 2019 12:29:42 +0300 Subject: [PATCH 1020/2854] Move logic into BeatmapNotAvailable --- .../BeatmapSet/BeatmapNotAvailable.cs | 54 ++++++++++++++++--- osu.Game/Overlays/BeatmapSet/Header.cs | 36 +++++-------- 2 files changed, 59 insertions(+), 31 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs b/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs index b893fd0703..c111861206 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs @@ -5,6 +5,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -14,9 +15,36 @@ namespace osu.Game.Overlays.BeatmapSet { public class BeatmapNotAvailable : Container { - private LinkFlowContainer linkContainer; + private BeatmapSetInfo beatmapSet; - public override void Show() + public BeatmapSetInfo BeatmapSet + { + get => beatmapSet; + set + { + if (value == beatmapSet) return; + + beatmapSet = value; + + if (beatmapSet?.OnlineInfo.Availability != null) + { + Header?.ResizeHeightTo(450, 500); + Show(); + } + else + { + Header?.ResizeHeightTo(400, 500); + Hide(); + } + } + } + + public Header Header; + + private readonly OsuSpriteText text; + private readonly LinkFlowContainer link; + + public BeatmapNotAvailable() { AutoSizeAxes = Axes.Both; Margin = new MarginPadding { Top = 10 }; @@ -36,14 +64,13 @@ namespace osu.Game.Overlays.BeatmapSet Children = new Drawable[] { - new OsuSpriteText + text = new OsuSpriteText { Margin = new MarginPadding { Bottom = 10, Horizontal = 5 }, Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium), - Text = "This beatmap is currently not available for download.", Colour = Color4.Orange, }, - linkContainer = new LinkFlowContainer(text => text.Font = OsuFont.GetFont(size: 14)) + link = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 14)) { Direction = FillDirection.Full, RelativeSizeAxes = Axes.X, @@ -54,12 +81,25 @@ namespace osu.Game.Overlays.BeatmapSet }, }; + Hide(); + } + + public override void Show() + { + text.Text = BeatmapSet.OnlineInfo.Availability.DownloadDisabled + ? "This beatmap is currently not available for download." + : "Portions of this beatmap have been removed at the request of the creator or a third-party rights holder."; + + link.AddLink("Check here for more information.", BeatmapSet.OnlineInfo.Availability.ExternalLink); + base.Show(); } - public string Link + public override void Hide() { - set => linkContainer.AddLink("Check here for more information.", value); + link.RemoveAll(x => true); + + base.Hide(); } } } diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 9a4e7d4754..a53b4a2e68 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -32,10 +32,10 @@ namespace osu.Game.Overlays.BeatmapSet private readonly UpdateableBeatmapSetCover cover; private readonly OsuSpriteText title, artist; private readonly AuthorInfo author; - private readonly BeatmapNotAvailable unavailableContainer; - private readonly FillFlowContainer downloadButtonsContainer; + private readonly BeatmapNotAvailable beatmapNotAvailable; private readonly BeatmapSetOnlineStatusPill onlineStatusPill; public Details Details; + public FillFlowContainer DownloadButtonsContainer; public readonly BeatmapPicker Picker; @@ -135,7 +135,7 @@ namespace osu.Game.Overlays.BeatmapSet Margin = new MarginPadding { Top = 20 }, Child = author = new AuthorInfo(), }, - unavailableContainer = new BeatmapNotAvailable(), + beatmapNotAvailable = new BeatmapNotAvailable { Header = this }, new Container { RelativeSizeAxes = Axes.X, @@ -144,7 +144,7 @@ namespace osu.Game.Overlays.BeatmapSet Children = new Drawable[] { favouriteButton = new FavouriteButton(), - downloadButtonsContainer = new FillFlowContainer + DownloadButtonsContainer = new FillFlowContainer { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = buttons_height + buttons_spacing }, @@ -192,7 +192,7 @@ namespace osu.Game.Overlays.BeatmapSet BeatmapSet.BindValueChanged(setInfo => { - Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = setInfo.NewValue; + Picker.BeatmapSet = author.BeatmapSet = beatmapNotAvailable.BeatmapSet = Details.BeatmapSet = setInfo.NewValue; title.Text = setInfo.NewValue?.Metadata.Title ?? string.Empty; artist.Text = setInfo.NewValue?.Metadata.Artist ?? string.Empty; @@ -201,27 +201,15 @@ namespace osu.Game.Overlays.BeatmapSet if (setInfo.NewValue != null) { - downloadButtonsContainer.FadeIn(transition_duration); + DownloadButtonsContainer.FadeIn(transition_duration); favouriteButton.FadeIn(transition_duration); } else { - downloadButtonsContainer.FadeOut(transition_duration); + DownloadButtonsContainer.FadeOut(transition_duration); favouriteButton.FadeOut(transition_duration); } - if (setInfo.NewValue?.OnlineInfo.Availability?.DownloadDisabled ?? false) - { - this.ResizeHeightTo(460, transition_duration / 2); - unavailableContainer.Show(); - unavailableContainer.Link = setInfo.NewValue.OnlineInfo.Availability.ExternalLink; - } - else - { - this.ResizeHeightTo(400, transition_duration / 2); - unavailableContainer.Hide(); - } - updateDownloadButtons(); }, true); } @@ -232,7 +220,7 @@ namespace osu.Game.Overlays.BeatmapSet if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) { - downloadButtonsContainer.RemoveAll(x => true); + DownloadButtonsContainer.RemoveAll(x => true); return; } @@ -240,7 +228,7 @@ namespace osu.Game.Overlays.BeatmapSet { case DownloadState.LocallyAvailable: // temporary for UX until new design is implemented. - downloadButtonsContainer.Child = new osu.Game.Overlays.Direct.DownloadButton(BeatmapSet.Value) + DownloadButtonsContainer.Child = new osu.Game.Overlays.Direct.DownloadButton(BeatmapSet.Value) { Width = 50, RelativeSizeAxes = Axes.Y @@ -250,13 +238,13 @@ namespace osu.Game.Overlays.BeatmapSet case DownloadState.Downloading: case DownloadState.Downloaded: // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design. - downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); + DownloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); break; default: - downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); + DownloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); if (BeatmapSet.Value.OnlineInfo.HasVideo) - downloadButtonsContainer.Add(new DownloadButton(BeatmapSet.Value, true)); + DownloadButtonsContainer.Add(new DownloadButton(BeatmapSet.Value, true)); break; } } From 261badc770330dd1e086bb30880bb73f6bb90bb9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Jun 2019 19:24:54 +0900 Subject: [PATCH 1021/2854] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index eeb1f2bee3..140d0c7f0e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 3a5090d968..39be738a5c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 6a34d5575bd69b28393ab7dfb8f00448d03553ed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Jun 2019 20:44:11 +0900 Subject: [PATCH 1022/2854] Bump framework version --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 140d0c7f0e..75a464d0b8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 39be738a5c..2c25498b89 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 13cd22e397f336eeb86b1be076f4cf6aabbafc85 Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Tue, 11 Jun 2019 14:58:46 +0300 Subject: [PATCH 1023/2854] Test scene for BeatmapNotAvailable --- .../Online/TestSceneBeatmapNotAvailable.cs | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneBeatmapNotAvailable.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapNotAvailable.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapNotAvailable.cs new file mode 100644 index 0000000000..bd4570470d --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapNotAvailable.cs @@ -0,0 +1,54 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Overlays.BeatmapSet; + +namespace osu.Game.Tests.Visual.Online +{ + [TestFixture] + public class TestSceneBeatmapNotAvailable : OsuTestScene + { + public TestSceneBeatmapNotAvailable() + { + var container = new BeatmapNotAvailable(); + + Add(container); + + AddAssert("is container hidden", () => container.Alpha == 0); + AddStep("set undownloadable beatmapset", () => container.BeatmapSet = new BeatmapSetInfo + { + OnlineInfo = new BeatmapSetOnlineInfo + { + Availability = new BeatmapSetOnlineAvailability + { + DownloadDisabled = true, + ExternalLink = @"https://gist.githubusercontent.com/peppy/99e6959772083cdfde8a/raw", + }, + }, + }); + + AddAssert("is container visible", () => container.Alpha == 1); + AddStep("set downloadable beatmapset", () => container.BeatmapSet = new BeatmapSetInfo + { + OnlineInfo = new BeatmapSetOnlineInfo + { + Availability = new BeatmapSetOnlineAvailability + { + DownloadDisabled = false, + ExternalLink = @"https://gist.githubusercontent.com/peppy/99e6959772083cdfde8a/raw", + }, + }, + }); + + AddAssert("is container still visible", () => container.Alpha == 1); + AddStep("set normal beatmapset", () => container.BeatmapSet = new BeatmapSetInfo + { + OnlineInfo = new BeatmapSetOnlineInfo(), + }); + + AddAssert("is container hidden", () => container.Alpha == 0); + } + } +} From b4de51b612c62b563ec1303b84dead7a219be938 Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 11 Jun 2019 18:29:33 +0530 Subject: [PATCH 1024/2854] Create a generic base archive download manager class --- .../Database/ArchiveDownloadModelManager.cs | 117 ++++++++++++++++++ .../Online/API/ArchiveDownloadModelRequest.cs | 23 ++++ 2 files changed, 140 insertions(+) create mode 100644 osu.Game/Database/ArchiveDownloadModelManager.cs create mode 100644 osu.Game/Online/API/ArchiveDownloadModelRequest.cs diff --git a/osu.Game/Database/ArchiveDownloadModelManager.cs b/osu.Game/Database/ArchiveDownloadModelManager.cs new file mode 100644 index 0000000000..8c7a0fba87 --- /dev/null +++ b/osu.Game/Database/ArchiveDownloadModelManager.cs @@ -0,0 +1,117 @@ +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Game.Online.API; +using osu.Game.Overlays.Notifications; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace osu.Game.Database +{ + public abstract class ArchiveDownloadModelManager : ArchiveModelManager + where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete + where TFileModel : INamedFileInfo, new() + where TDownloadRequestModel : ArchiveDownloadModelRequest + { + public event Action DownloadBegan; + + public event Action DownloadFailed; + + private readonly IAPIProvider api; + + private readonly List currentDownloads = new List(); + + protected ArchiveDownloadModelManager(Storage storage, IDatabaseContextFactory contextFactory, IAPIProvider api, MutableDatabaseBackedStoreWithFileIncludes modelStore, IIpcHost importHost = null) + :base(storage, contextFactory, modelStore, importHost) + { + this.api = api; + } + + protected abstract TDownloadRequestModel CreateDownloadRequest(TModel model); + + public bool Download(TModel model) + { + var existing = GetExistingDownload(model); + + if (existing != null || api == null) return false; + + DownloadNotification notification = new DownloadNotification + { + Text = $"Downloading {model}", + }; + + var request = CreateDownloadRequest(model); + + request.DownloadProgressed += progress => + { + notification.State = ProgressNotificationState.Active; + notification.Progress = progress; + }; + + request.Success += filename => + { + Task.Factory.StartNew(() => + { + Import(notification, filename); + currentDownloads.Remove(request); + }, TaskCreationOptions.LongRunning); + }; + + request.Failure += error => + { + DownloadFailed?.Invoke(request); + + if (error is OperationCanceledException) return; + + notification.State = ProgressNotificationState.Cancelled; + // TODO: implement a Name for every model that we can use in this message + Logger.Error(error, "Download failed!"); + currentDownloads.Remove(request); + }; + + notification.CancelRequested += () => + { + request.Cancel(); + currentDownloads.Remove(request); + notification.State = ProgressNotificationState.Cancelled; + return true; + }; + + currentDownloads.Add(request); + PostNotification?.Invoke(notification); + + Task.Factory.StartNew(() => + { + try + { + request.Perform(api); + } + catch + { + } + }, TaskCreationOptions.LongRunning); + + DownloadBegan?.Invoke(request); + + return true; + } + + public TDownloadRequestModel GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Info.Equals(model)); + + private class DownloadNotification : ProgressNotification + { + public override bool IsImportant => false; + + protected override Notification CreateCompletionNotification() => new SilencedProgressCompletionNotification + { + Activated = CompletionClickAction, + Text = CompletionText + }; + + private class SilencedProgressCompletionNotification : ProgressCompletionNotification + { + public override bool IsImportant => false; + } + } + } +} diff --git a/osu.Game/Online/API/ArchiveDownloadModelRequest.cs b/osu.Game/Online/API/ArchiveDownloadModelRequest.cs new file mode 100644 index 0000000000..377166e657 --- /dev/null +++ b/osu.Game/Online/API/ArchiveDownloadModelRequest.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace osu.Game.Online.API +{ + public abstract class ArchiveDownloadModelRequest : APIDownloadRequest + where TModel : class + { + public readonly TModel Info; + + public float Progress; + + public event Action DownloadProgressed; + + protected ArchiveDownloadModelRequest(TModel model) + { + Info = model; + + Progressed += (current, total) => DownloadProgressed?.Invoke(Progress = (float)current / total); + } + } +} From 341d137f5c10c9e575419100e355fc6d312c0103 Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 11 Jun 2019 19:36:08 +0530 Subject: [PATCH 1025/2854] Make BeatmapManager inherit from new base class --- osu.Game/Beatmaps/BeatmapManager.cs | 113 +----------------- .../Database/ArchiveDownloadModelManager.cs | 29 +++-- .../Online/API/ArchiveDownloadModelRequest.cs | 2 - .../API/Requests/DownloadBeatmapSetRequest.cs | 10 +- .../Direct/DownloadTrackingComposite.cs | 4 +- 5 files changed, 25 insertions(+), 133 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index b6fe7f88fa..c4975501ed 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -27,7 +27,7 @@ namespace osu.Game.Beatmaps /// /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. /// - public partial class BeatmapManager : ArchiveModelManager + public partial class BeatmapManager : ArchiveDownloadModelManager { /// /// Fired when a single difficulty has been hidden. @@ -39,16 +39,6 @@ namespace osu.Game.Beatmaps /// public event Action BeatmapRestored; - /// - /// Fired when a beatmap download begins. - /// - public event Action BeatmapDownloadBegan; - - /// - /// Fired when a beatmap download is interrupted, due to user cancellation or other failures. - /// - public event Action BeatmapDownloadFailed; - /// /// A default representation of a WorkingBeatmap to use when no beatmap is available. /// @@ -74,7 +64,7 @@ namespace osu.Game.Beatmaps public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null, WorkingBeatmap defaultBeatmap = null) - : base(storage, contextFactory, new BeatmapStore(contextFactory), host) + : base(storage, contextFactory, api, new BeatmapStore(contextFactory), host) { this.rulesets = rulesets; this.api = api; @@ -88,6 +78,8 @@ namespace osu.Game.Beatmaps beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); } + protected override DownloadBeatmapSetRequest CreateDownloadRequest(BeatmapSetInfo set) => new DownloadBeatmapSetRequest(set, false); + protected override void Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive) { if (archive != null) @@ -153,87 +145,6 @@ namespace osu.Game.Beatmaps void resetIds() => beatmapSet.Beatmaps.ForEach(b => b.OnlineBeatmapID = null); } - /// - /// Downloads a beatmap. - /// This will post notifications tracking progress. - /// - /// The to be downloaded. - /// Whether the beatmap should be downloaded without video. Defaults to false. - /// Downloading can happen - public bool Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false) - { - var existing = GetExistingDownload(beatmapSetInfo); - - if (existing != null || api == null) return false; - - var downloadNotification = new DownloadNotification - { - Text = $"Downloading {beatmapSetInfo}", - }; - - var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo); - - request.DownloadProgressed += progress => - { - downloadNotification.State = ProgressNotificationState.Active; - downloadNotification.Progress = progress; - }; - - request.Success += filename => - { - Task.Factory.StartNew(() => - { - // This gets scheduled back to the update thread, but we want the import to run in the background. - Import(downloadNotification, filename); - currentDownloads.Remove(request); - }, TaskCreationOptions.LongRunning); - }; - - request.Failure += error => - { - BeatmapDownloadFailed?.Invoke(request); - - if (error is OperationCanceledException) return; - - downloadNotification.State = ProgressNotificationState.Cancelled; - Logger.Error(error, "Beatmap download failed!"); - currentDownloads.Remove(request); - }; - - downloadNotification.CancelRequested += () => - { - request.Cancel(); - currentDownloads.Remove(request); - downloadNotification.State = ProgressNotificationState.Cancelled; - return true; - }; - - currentDownloads.Add(request); - PostNotification?.Invoke(downloadNotification); - - // don't run in the main api queue as this is a long-running task. - Task.Factory.StartNew(() => - { - try - { - request.Perform(api); - } - catch - { - // no need to handle here as exceptions will filter down to request.Failure above. - } - }, TaskCreationOptions.LongRunning); - BeatmapDownloadBegan?.Invoke(request); - return true; - } - - /// - /// Get an existing download request if it exists. - /// - /// The whose download request is wanted. - /// The object if it exists, or null. - public DownloadBeatmapSetRequest GetExistingDownload(BeatmapSetInfo beatmap) => currentDownloads.Find(d => d.BeatmapSet.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID); - /// /// Delete a beatmap difficulty. /// @@ -439,21 +350,5 @@ namespace osu.Game.Beatmaps protected override Texture GetBackground() => null; protected override Track GetTrack() => null; } - - private class DownloadNotification : ProgressNotification - { - public override bool IsImportant => false; - - protected override Notification CreateCompletionNotification() => new SilencedProgressCompletionNotification - { - Activated = CompletionClickAction, - Text = CompletionText - }; - - private class SilencedProgressCompletionNotification : ProgressCompletionNotification - { - public override bool IsImportant => false; - } - } } } diff --git a/osu.Game/Database/ArchiveDownloadModelManager.cs b/osu.Game/Database/ArchiveDownloadModelManager.cs index 8c7a0fba87..6580da0d54 100644 --- a/osu.Game/Database/ArchiveDownloadModelManager.cs +++ b/osu.Game/Database/ArchiveDownloadModelManager.cs @@ -31,17 +31,26 @@ namespace osu.Game.Database public bool Download(TModel model) { - var existing = GetExistingDownload(model); - - if (existing != null || api == null) return false; - - DownloadNotification notification = new DownloadNotification - { - Text = $"Downloading {model}", - }; + if (!canDownload(model)) return false; var request = CreateDownloadRequest(model); + performDownloadWithRequest(request); + + return true; + } + + public TDownloadRequestModel GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Info.Equals(model)); + + private bool canDownload(TModel model) => GetExistingDownload(model) == null && api != null; + + private void performDownloadWithRequest(TDownloadRequestModel request) + { + DownloadNotification notification = new DownloadNotification + { + Text = $"Downloading {request.Info}", + }; + request.DownloadProgressed += progress => { notification.State = ProgressNotificationState.Active; @@ -92,12 +101,8 @@ namespace osu.Game.Database }, TaskCreationOptions.LongRunning); DownloadBegan?.Invoke(request); - - return true; } - public TDownloadRequestModel GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Info.Equals(model)); - private class DownloadNotification : ProgressNotification { public override bool IsImportant => false; diff --git a/osu.Game/Online/API/ArchiveDownloadModelRequest.cs b/osu.Game/Online/API/ArchiveDownloadModelRequest.cs index 377166e657..862d017228 100644 --- a/osu.Game/Online/API/ArchiveDownloadModelRequest.cs +++ b/osu.Game/Online/API/ArchiveDownloadModelRequest.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace osu.Game.Online.API { diff --git a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs index 26e8acc2fc..7d0a8f9f46 100644 --- a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs +++ b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs @@ -2,26 +2,20 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Beatmaps; -using System; namespace osu.Game.Online.API.Requests { - public class DownloadBeatmapSetRequest : APIDownloadRequest + public class DownloadBeatmapSetRequest : ArchiveDownloadModelRequest { public readonly BeatmapSetInfo BeatmapSet; - public float Progress; - - public event Action DownloadProgressed; - private readonly bool noVideo; public DownloadBeatmapSetRequest(BeatmapSetInfo set, bool noVideo) + : base(set) { this.noVideo = noVideo; BeatmapSet = set; - - Progressed += (current, total) => DownloadProgressed?.Invoke(Progress = (float)current / total); } protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}"; diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs index 37f13aefc8..9beedb195f 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs @@ -47,7 +47,7 @@ namespace osu.Game.Overlays.Direct attachDownload(beatmaps.GetExistingDownload(setInfo.NewValue)); }, true); - beatmaps.BeatmapDownloadBegan += download => + beatmaps.DownloadBegan += download => { if (download.BeatmapSet.OnlineBeatmapSetID == BeatmapSet.Value?.OnlineBeatmapSetID) attachDownload(download); @@ -65,7 +65,7 @@ namespace osu.Game.Overlays.Direct if (beatmaps != null) { - beatmaps.BeatmapDownloadBegan -= attachDownload; + beatmaps.DownloadBegan -= attachDownload; beatmaps.ItemAdded -= setAdded; } From 8ff26a8fbcf7ef1f1d3483d4dd0047807ef05ff1 Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 11 Jun 2019 19:49:10 +0530 Subject: [PATCH 1026/2854] Add license headers and xmldoc --- .../Database/ArchiveDownloadModelManager.cs | 31 +++++++++++++++++-- .../Online/API/ArchiveDownloadModelRequest.cs | 5 ++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/ArchiveDownloadModelManager.cs b/osu.Game/Database/ArchiveDownloadModelManager.cs index 6580da0d54..d0881fb251 100644 --- a/osu.Game/Database/ArchiveDownloadModelManager.cs +++ b/osu.Game/Database/ArchiveDownloadModelManager.cs @@ -1,4 +1,7 @@ -using osu.Framework.Logging; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Online.API; using osu.Game.Overlays.Notifications; @@ -8,13 +11,26 @@ using System.Threading.Tasks; namespace osu.Game.Database { + /// + /// An that has the ability to download models using an and + /// import them into the store. + /// + /// The model type. + /// The associated file join type. + /// The associated for this model. public abstract class ArchiveDownloadModelManager : ArchiveModelManager where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : INamedFileInfo, new() where TDownloadRequestModel : ArchiveDownloadModelRequest { + /// + /// Fired when a download begins. + /// public event Action DownloadBegan; + /// + /// Fired when a download is interrupted, either due to user cancellation or failure. + /// public event Action DownloadFailed; private readonly IAPIProvider api; @@ -29,6 +45,12 @@ namespace osu.Game.Database protected abstract TDownloadRequestModel CreateDownloadRequest(TModel model); + /// + /// Downloads a . + /// This will post notifications tracking progress. + /// + /// The to be downloaded. + /// Whether downloading can happen. public bool Download(TModel model) { if (!canDownload(model)) return false; @@ -40,6 +62,11 @@ namespace osu.Game.Database return true; } + /// + /// Gets an existing download request if it exists. + /// + /// The whose request is wanted. + /// The object if it exists, otherwise null. public TDownloadRequestModel GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Info.Equals(model)); private bool canDownload(TModel model) => GetExistingDownload(model) == null && api != null; @@ -73,7 +100,7 @@ namespace osu.Game.Database if (error is OperationCanceledException) return; notification.State = ProgressNotificationState.Cancelled; - // TODO: implement a Name for every model that we can use in this message + // TODO: maybe implement a Name for every model that we can use in this message? Logger.Error(error, "Download failed!"); currentDownloads.Remove(request); }; diff --git a/osu.Game/Online/API/ArchiveDownloadModelRequest.cs b/osu.Game/Online/API/ArchiveDownloadModelRequest.cs index 862d017228..7f161f1e98 100644 --- a/osu.Game/Online/API/ArchiveDownloadModelRequest.cs +++ b/osu.Game/Online/API/ArchiveDownloadModelRequest.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; namespace osu.Game.Online.API { From 802f48712d95b148efc45ae25bf8fa4fdc040423 Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 11 Jun 2019 20:14:36 +0530 Subject: [PATCH 1027/2854] Add ability to perform a download request with options --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- .../Database/ArchiveDownloadModelManager.cs | 29 +++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index c4975501ed..6f27cbd7c6 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -78,7 +78,7 @@ namespace osu.Game.Beatmaps beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); } - protected override DownloadBeatmapSetRequest CreateDownloadRequest(BeatmapSetInfo set) => new DownloadBeatmapSetRequest(set, false); + protected override DownloadBeatmapSetRequest CreateDownloadRequest(BeatmapSetInfo set, object[] options) => new DownloadBeatmapSetRequest(set, (options?.FirstOrDefault() as bool?) ?? false); protected override void Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive) { diff --git a/osu.Game/Database/ArchiveDownloadModelManager.cs b/osu.Game/Database/ArchiveDownloadModelManager.cs index d0881fb251..a4d2180559 100644 --- a/osu.Game/Database/ArchiveDownloadModelManager.cs +++ b/osu.Game/Database/ArchiveDownloadModelManager.cs @@ -43,7 +43,15 @@ namespace osu.Game.Database this.api = api; } - protected abstract TDownloadRequestModel CreateDownloadRequest(TModel model); + /// + /// Creates the download request for this . + /// The parameters will be provided when the download was initiated with extra options meant + /// to be used in the creation of the request. + /// + /// The to be downloaded. + /// Extra parameters for request creation, null if none were passed. + /// The request object. + protected abstract TDownloadRequestModel CreateDownloadRequest(TModel model, object[] options); /// /// Downloads a . @@ -55,7 +63,24 @@ namespace osu.Game.Database { if (!canDownload(model)) return false; - var request = CreateDownloadRequest(model); + var request = CreateDownloadRequest(model, null); + + performDownloadWithRequest(request); + + return true; + } + + /// + /// Downloads a with optional parameters for the download request. + /// + /// The to be downloaded. + /// Optional parameters to be used for creating the download request. + /// Whether downloading can happen. + public bool Download(TModel model, params object[] extra) + { + if (!canDownload(model)) return false; + + var request = CreateDownloadRequest(model, extra); performDownloadWithRequest(request); From 709ca03a08a3cad591a3911b01734fd100ad3923 Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 11 Jun 2019 20:21:06 +0530 Subject: [PATCH 1028/2854] Remove unused usings --- osu.Game/Beatmaps/BeatmapManager.cs | 2 -- osu.Game/Database/ArchiveDownloadModelManager.cs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 6f27cbd7c6..6ad5ce6617 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Linq.Expressions; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using osu.Framework.Audio; using osu.Framework.Audio.Track; @@ -19,7 +18,6 @@ using osu.Game.Database; using osu.Game.IO.Archives; using osu.Game.Online.API; using osu.Game.Online.API.Requests; -using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; namespace osu.Game.Beatmaps diff --git a/osu.Game/Database/ArchiveDownloadModelManager.cs b/osu.Game/Database/ArchiveDownloadModelManager.cs index a4d2180559..629cb81440 100644 --- a/osu.Game/Database/ArchiveDownloadModelManager.cs +++ b/osu.Game/Database/ArchiveDownloadModelManager.cs @@ -38,7 +38,7 @@ namespace osu.Game.Database private readonly List currentDownloads = new List(); protected ArchiveDownloadModelManager(Storage storage, IDatabaseContextFactory contextFactory, IAPIProvider api, MutableDatabaseBackedStoreWithFileIncludes modelStore, IIpcHost importHost = null) - :base(storage, contextFactory, modelStore, importHost) + : base(storage, contextFactory, modelStore, importHost) { this.api = api; } From f4dab4da85df762b3998c1ad4d157529a42d031b Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 11 Jun 2019 20:53:44 +0530 Subject: [PATCH 1029/2854] Add method to check if model exists locally already --- osu.Game/Database/ArchiveDownloadModelManager.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Database/ArchiveDownloadModelManager.cs b/osu.Game/Database/ArchiveDownloadModelManager.cs index 629cb81440..3c0de0b566 100644 --- a/osu.Game/Database/ArchiveDownloadModelManager.cs +++ b/osu.Game/Database/ArchiveDownloadModelManager.cs @@ -7,6 +7,7 @@ using osu.Game.Online.API; using osu.Game.Overlays.Notifications; using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; namespace osu.Game.Database @@ -37,10 +38,13 @@ namespace osu.Game.Database private readonly List currentDownloads = new List(); + private readonly MutableDatabaseBackedStoreWithFileIncludes modelStore; + protected ArchiveDownloadModelManager(Storage storage, IDatabaseContextFactory contextFactory, IAPIProvider api, MutableDatabaseBackedStoreWithFileIncludes modelStore, IIpcHost importHost = null) : base(storage, contextFactory, modelStore, importHost) { this.api = api; + this.modelStore = modelStore; } /// @@ -70,6 +74,13 @@ namespace osu.Game.Database return true; } + /// + /// Checks whether a given is available in the local store already. + /// + /// The whose existence needs to be checked. + /// Whether the exists locally. + public bool IsAvailableLocally(TModel model) => modelStore.ConsumableItems.Any(m => m.Equals(model)); + /// /// Downloads a with optional parameters for the download request. /// From 27054a744ed316fd623b33f4a8cfeeec9c787dc8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Jun 2019 00:35:13 +0900 Subject: [PATCH 1030/2854] Fill in thread pool names --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/Database/ArchiveModelManager.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 47411c69ec..67d3382e01 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -426,7 +426,7 @@ namespace osu.Game.Beatmaps private const int update_queue_request_concurrency = 4; - private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(update_queue_request_concurrency); + private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(update_queue_request_concurrency, nameof(BeatmapUpdateQueue)); public BeatmapUpdateQueue(IAPIProvider api) { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 844ae622a5..d764601b32 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -644,6 +644,6 @@ namespace osu.Game.Database /// This scheduler generally performs IO and CPU intensive work so concurrency is limited harshly. /// It is mainly being used as a queue mechanism for large imports. /// - protected static readonly ThreadedTaskScheduler IMPORT_SCHEDULER = new ThreadedTaskScheduler(import_queue_request_concurrency); + protected static readonly ThreadedTaskScheduler IMPORT_SCHEDULER = new ThreadedTaskScheduler(import_queue_request_concurrency, nameof(ArchiveModelManager)); } } From 06a558c4b7127107b494d8dfe9db3f50263e86e5 Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 11 Jun 2019 21:11:30 +0530 Subject: [PATCH 1031/2854] Remove unecessary third generic and change usages to match --- osu.Game/Beatmaps/BeatmapManager.cs | 6 ++---- .../Database/ArchiveDownloadModelManager.cs | 18 ++++++++---------- .../API/Requests/DownloadBeatmapSetRequest.cs | 7 +++---- .../Direct/DownloadTrackingComposite.cs | 8 ++++---- 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 6ad5ce6617..2dcd1b137c 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -25,7 +25,7 @@ namespace osu.Game.Beatmaps /// /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. /// - public partial class BeatmapManager : ArchiveDownloadModelManager + public partial class BeatmapManager : ArchiveDownloadModelManager { /// /// Fired when a single difficulty has been hidden. @@ -58,8 +58,6 @@ namespace osu.Game.Beatmaps private readonly GameHost host; - private readonly List currentDownloads = new List(); - public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null, WorkingBeatmap defaultBeatmap = null) : base(storage, contextFactory, api, new BeatmapStore(contextFactory), host) @@ -76,7 +74,7 @@ namespace osu.Game.Beatmaps beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); } - protected override DownloadBeatmapSetRequest CreateDownloadRequest(BeatmapSetInfo set, object[] options) => new DownloadBeatmapSetRequest(set, (options?.FirstOrDefault() as bool?) ?? false); + protected override ArchiveDownloadModelRequest CreateDownloadRequest(BeatmapSetInfo set, object[] options) => new DownloadBeatmapSetRequest(set, (options?.FirstOrDefault() as bool?) ?? false); protected override void Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive) { diff --git a/osu.Game/Database/ArchiveDownloadModelManager.cs b/osu.Game/Database/ArchiveDownloadModelManager.cs index 3c0de0b566..64920710bc 100644 --- a/osu.Game/Database/ArchiveDownloadModelManager.cs +++ b/osu.Game/Database/ArchiveDownloadModelManager.cs @@ -18,25 +18,23 @@ namespace osu.Game.Database /// /// The model type. /// The associated file join type. - /// The associated for this model. - public abstract class ArchiveDownloadModelManager : ArchiveModelManager + public abstract class ArchiveDownloadModelManager : ArchiveModelManager where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : INamedFileInfo, new() - where TDownloadRequestModel : ArchiveDownloadModelRequest { /// /// Fired when a download begins. /// - public event Action DownloadBegan; + public event Action> DownloadBegan; /// /// Fired when a download is interrupted, either due to user cancellation or failure. /// - public event Action DownloadFailed; + public event Action> DownloadFailed; private readonly IAPIProvider api; - private readonly List currentDownloads = new List(); + private readonly List> currentDownloads = new List>(); private readonly MutableDatabaseBackedStoreWithFileIncludes modelStore; @@ -55,7 +53,7 @@ namespace osu.Game.Database /// The to be downloaded. /// Extra parameters for request creation, null if none were passed. /// The request object. - protected abstract TDownloadRequestModel CreateDownloadRequest(TModel model, object[] options); + protected abstract ArchiveDownloadModelRequest CreateDownloadRequest(TModel model, object[] options); /// /// Downloads a . @@ -102,12 +100,12 @@ namespace osu.Game.Database /// Gets an existing download request if it exists. /// /// The whose request is wanted. - /// The object if it exists, otherwise null. - public TDownloadRequestModel GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Info.Equals(model)); + /// The object if it exists, otherwise null. + public ArchiveDownloadModelRequest GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Info.Equals(model)); private bool canDownload(TModel model) => GetExistingDownload(model) == null && api != null; - private void performDownloadWithRequest(TDownloadRequestModel request) + private void performDownloadWithRequest(ArchiveDownloadModelRequest request) { DownloadNotification notification = new DownloadNotification { diff --git a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs index 7d0a8f9f46..8d636f6730 100644 --- a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs +++ b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs @@ -7,17 +7,16 @@ namespace osu.Game.Online.API.Requests { public class DownloadBeatmapSetRequest : ArchiveDownloadModelRequest { - public readonly BeatmapSetInfo BeatmapSet; - private readonly bool noVideo; + private readonly BeatmapSetInfo set; public DownloadBeatmapSetRequest(BeatmapSetInfo set, bool noVideo) : base(set) { this.noVideo = noVideo; - BeatmapSet = set; + this.set = set; } - protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}"; + protected override string Target => $@"beatmapsets/{set.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}"; } } diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs index 9beedb195f..c1ff6ecb60 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs @@ -7,7 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; -using osu.Game.Online.API.Requests; +using osu.Game.Online.API; namespace osu.Game.Overlays.Direct { @@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Direct beatmaps.DownloadBegan += download => { - if (download.BeatmapSet.OnlineBeatmapSetID == BeatmapSet.Value?.OnlineBeatmapSetID) + if (download.Info.OnlineBeatmapSetID == BeatmapSet.Value?.OnlineBeatmapSetID) attachDownload(download); }; @@ -76,9 +76,9 @@ namespace osu.Game.Overlays.Direct #endregion - private DownloadBeatmapSetRequest attachedRequest; + private ArchiveDownloadModelRequest attachedRequest; - private void attachDownload(DownloadBeatmapSetRequest request) + private void attachDownload(ArchiveDownloadModelRequest request) { if (attachedRequest != null) { From d903ad2186ed7c6e81fc2c4982eef2e5b89e5ee3 Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 11 Jun 2019 21:30:25 +0530 Subject: [PATCH 1032/2854] Fix order --- osu.Game/Database/ArchiveDownloadModelManager.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game/Database/ArchiveDownloadModelManager.cs b/osu.Game/Database/ArchiveDownloadModelManager.cs index 64920710bc..2fb661ccce 100644 --- a/osu.Game/Database/ArchiveDownloadModelManager.cs +++ b/osu.Game/Database/ArchiveDownloadModelManager.cs @@ -72,15 +72,9 @@ namespace osu.Game.Database return true; } - /// - /// Checks whether a given is available in the local store already. - /// - /// The whose existence needs to be checked. - /// Whether the exists locally. - public bool IsAvailableLocally(TModel model) => modelStore.ConsumableItems.Any(m => m.Equals(model)); - /// /// Downloads a with optional parameters for the download request. + /// This will post notifications tracking progress. /// /// The to be downloaded. /// Optional parameters to be used for creating the download request. @@ -96,6 +90,13 @@ namespace osu.Game.Database return true; } + /// + /// Checks whether a given is available in the local store already. + /// + /// The whose existence needs to be checked. + /// Whether the exists locally. + public bool IsAvailableLocally(TModel model) => modelStore.ConsumableItems.Any(m => m.Equals(model)); + /// /// Gets an existing download request if it exists. /// From 4a6074865e270a66c170fa508a9cb1ee126f4dec Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 11 Jun 2019 22:42:57 +0530 Subject: [PATCH 1033/2854] Create interfaces for DownloadTrackingComposite to consume --- osu.Game/Beatmaps/BeatmapManager.cs | 2 + .../Database/ArchiveDownloadModelManager.cs | 28 +---------- osu.Game/Database/ArchiveModelManager.cs | 2 +- osu.Game/Database/IDownloadModelManager.cs | 47 +++++++++++++++++++ osu.Game/Database/IModelManager.cs | 20 ++++++++ 5 files changed, 72 insertions(+), 27 deletions(-) create mode 100644 osu.Game/Database/IDownloadModelManager.cs create mode 100644 osu.Game/Database/IModelManager.cs diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 2dcd1b137c..e124e38dd9 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -229,6 +229,8 @@ namespace osu.Game.Beatmaps /// Results from the provided query. public IQueryable QueryBeatmaps(Expression> query) => beatmaps.Beatmaps.AsNoTracking().Where(query); + public override bool IsAvailableLocally(BeatmapSetInfo set) => beatmaps.ConsumableItems.Any(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID && !s.DeletePending && !s.Protected); + protected override BeatmapSetInfo CreateModel(ArchiveReader reader) { // let's make sure there are actually .osu files to import. diff --git a/osu.Game/Database/ArchiveDownloadModelManager.cs b/osu.Game/Database/ArchiveDownloadModelManager.cs index 2fb661ccce..c868b8d1d5 100644 --- a/osu.Game/Database/ArchiveDownloadModelManager.cs +++ b/osu.Game/Database/ArchiveDownloadModelManager.cs @@ -18,18 +18,12 @@ namespace osu.Game.Database /// /// The model type. /// The associated file join type. - public abstract class ArchiveDownloadModelManager : ArchiveModelManager + public abstract class ArchiveDownloadModelManager : ArchiveModelManager, IDownloadModelManager where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : INamedFileInfo, new() { - /// - /// Fired when a download begins. - /// public event Action> DownloadBegan; - /// - /// Fired when a download is interrupted, either due to user cancellation or failure. - /// public event Action> DownloadFailed; private readonly IAPIProvider api; @@ -55,12 +49,6 @@ namespace osu.Game.Database /// The request object. protected abstract ArchiveDownloadModelRequest CreateDownloadRequest(TModel model, object[] options); - /// - /// Downloads a . - /// This will post notifications tracking progress. - /// - /// The to be downloaded. - /// Whether downloading can happen. public bool Download(TModel model) { if (!canDownload(model)) return false; @@ -72,13 +60,6 @@ namespace osu.Game.Database return true; } - /// - /// Downloads a with optional parameters for the download request. - /// This will post notifications tracking progress. - /// - /// The to be downloaded. - /// Optional parameters to be used for creating the download request. - /// Whether downloading can happen. public bool Download(TModel model, params object[] extra) { if (!canDownload(model)) return false; @@ -90,12 +71,7 @@ namespace osu.Game.Database return true; } - /// - /// Checks whether a given is available in the local store already. - /// - /// The whose existence needs to be checked. - /// Whether the exists locally. - public bool IsAvailableLocally(TModel model) => modelStore.ConsumableItems.Any(m => m.Equals(model)); + public virtual bool IsAvailableLocally(TModel model) => modelStore.ConsumableItems.Any(m => m.Equals(model) && !m.DeletePending); /// /// Gets an existing download request if it exists. diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 54dbae9ddc..50cb9dac8b 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -29,7 +29,7 @@ namespace osu.Game.Database /// /// The model type. /// The associated file join type. - public abstract class ArchiveModelManager : ICanAcceptFiles + public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : INamedFileInfo, new() { diff --git a/osu.Game/Database/IDownloadModelManager.cs b/osu.Game/Database/IDownloadModelManager.cs new file mode 100644 index 0000000000..3231d855ea --- /dev/null +++ b/osu.Game/Database/IDownloadModelManager.cs @@ -0,0 +1,47 @@ +using osu.Game.Online.API; +using System; +using System.Collections.Generic; +using System.Text; + +namespace osu.Game.Database +{ + public interface IDownloadModelManager : IModelManager + where TModel : class + { + /// + /// Fired when a download begins. + /// + event Action> DownloadBegan; + + /// + /// Fired when a download is interrupted, either due to user cancellation or failure. + /// + event Action> DownloadFailed; + + bool IsAvailableLocally(TModel model); + + /// + /// Downloads a . + /// This will post notifications tracking progress. + /// + /// The to be downloaded. + /// Whether downloading can happen. + bool Download(TModel model); + + /// + /// Downloads a with optional parameters for the download request. + /// This will post notifications tracking progress. + /// + /// The to be downloaded. + /// Optional parameters to be used for creating the download request. + /// Whether downloading can happen. + bool Download(TModel model, params object[] extra); + + /// + /// Checks whether a given is available in the local store already. + /// + /// The whose existence needs to be checked. + /// Whether the exists locally. + ArchiveDownloadModelRequest GetExistingDownload(TModel model); + } +} diff --git a/osu.Game/Database/IModelManager.cs b/osu.Game/Database/IModelManager.cs new file mode 100644 index 0000000000..6a0b2bde44 --- /dev/null +++ b/osu.Game/Database/IModelManager.cs @@ -0,0 +1,20 @@ + +using System; + +namespace osu.Game.Database +{ + public interface IModelManager + { + /// + /// Fired when a new becomes available in the database. + /// This is not guaranteed to run on the update thread. + /// + event Action ItemAdded; + + /// + /// Fired when a is removed from the database. + /// This is not guaranteed to run on the update thread. + /// + event Action ItemRemoved; + } +} From 6c74998487166c9723e191a33c74f97dfe36164a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 11 Jun 2019 19:24:36 +0200 Subject: [PATCH 1034/2854] Set ScreenActivity to InitialScreenActivity only when ScreenActivity hasn't been set manually before. --- osu.Game/Screens/OsuScreen.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 3b5e0fd0d6..582f97d46a 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -56,9 +56,13 @@ namespace osu.Game.Screens /// /// The to set the user's activity automatically to when this screen is entered + /// This will be automatically set to for this screen on entering unless + /// is manually set before. /// protected virtual UserActivity InitialScreenActivity => null; + private UserActivity screenActivity; + /// /// The for this screen. /// @@ -74,8 +78,6 @@ namespace osu.Game.Screens } } - private UserActivity screenActivity; - /// /// Whether to disallow changes to game-wise Beatmap/Ruleset bindables for this screen (and all children). /// @@ -170,7 +172,8 @@ namespace osu.Game.Screens backgroundStack?.Push(localBackground = CreateBackground()); - ScreenActivity = InitialScreenActivity; + if (screenActivity == null) + ScreenActivity = InitialScreenActivity; base.OnEntering(last); } From ab27d82cd581acba8e738a33704484335edd0f69 Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 11 Jun 2019 23:01:01 +0530 Subject: [PATCH 1035/2854] Make beatmap download buttons inherit BeatmapDownloadTrackingComposite - Move DownloadTrackingComposite into the online namespace --- osu.Game/Database/ArchiveModelManager.cs | 4 +- .../Direct => Online}/DownloadState.cs | 2 +- .../DownloadTrackingComposite.cs | 53 ++++++++++--------- .../BeatmapSet/Buttons/DownloadButton.cs | 3 +- osu.Game/Overlays/BeatmapSet/Header.cs | 3 +- .../BeatmapDownloadTrackingComposite.cs | 16 ++++++ osu.Game/Overlays/Direct/DownloadButton.cs | 3 +- .../Overlays/Direct/DownloadProgressBar.cs | 3 +- 8 files changed, 55 insertions(+), 32 deletions(-) rename osu.Game/{Overlays/Direct => Online}/DownloadState.cs (89%) rename osu.Game/{Overlays/Direct => Online}/DownloadTrackingComposite.cs (63%) create mode 100644 osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 50cb9dac8b..ccaba99427 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -29,7 +29,7 @@ namespace osu.Game.Database /// /// The model type. /// The associated file join type. - public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager + public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : INamedFileInfo, new() { @@ -44,7 +44,7 @@ namespace osu.Game.Database /// Fired when a new becomes available in the database. /// This is not guaranteed to run on the update thread. /// - public event ItemAddedDelegate ItemAdded; + public event Action ItemAdded; /// /// Fired when a is removed from the database. diff --git a/osu.Game/Overlays/Direct/DownloadState.cs b/osu.Game/Online/DownloadState.cs similarity index 89% rename from osu.Game/Overlays/Direct/DownloadState.cs rename to osu.Game/Online/DownloadState.cs index a301c6a3bd..72efbc286e 100644 --- a/osu.Game/Overlays/Direct/DownloadState.cs +++ b/osu.Game/Online/DownloadState.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -namespace osu.Game.Overlays.Direct +namespace osu.Game.Online { public enum DownloadState { diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs similarity index 63% rename from osu.Game/Overlays/Direct/DownloadTrackingComposite.cs rename to osu.Game/Online/DownloadTrackingComposite.cs index c1ff6ecb60..157211e6c2 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -7,18 +7,21 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; +using osu.Game.Database; using osu.Game.Online.API; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Online { /// /// A component which tracks a beatmap through potential download/import/deletion. /// - public abstract class DownloadTrackingComposite : CompositeDrawable + public abstract class DownloadTrackingComposite : CompositeDrawable + where TModel : class + where TModelManager : class, IDownloadModelManager { - public readonly Bindable BeatmapSet = new Bindable(); + public readonly Bindable ModelInfo = new Bindable(); - private BeatmapManager beatmaps; + private TModelManager manager; /// /// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded. @@ -27,34 +30,34 @@ namespace osu.Game.Overlays.Direct protected readonly Bindable Progress = new Bindable(); - protected DownloadTrackingComposite(BeatmapSetInfo beatmapSet = null) + protected DownloadTrackingComposite(TModel model = null) { - BeatmapSet.Value = beatmapSet; + ModelInfo.Value = model; } [BackgroundDependencyLoader(true)] - private void load(BeatmapManager beatmaps) + private void load(TModelManager manager) { - this.beatmaps = beatmaps; + this.manager = manager; - BeatmapSet.BindValueChanged(setInfo => + ModelInfo.BindValueChanged(modelInfo => { - if (setInfo.NewValue == null) + if (modelInfo.NewValue == null) attachDownload(null); - else if (beatmaps.GetAllUsableBeatmapSetsEnumerable().Any(s => s.OnlineBeatmapSetID == setInfo.NewValue.OnlineBeatmapSetID)) + else if (manager.IsAvailableLocally(modelInfo.NewValue)) State.Value = DownloadState.LocallyAvailable; else - attachDownload(beatmaps.GetExistingDownload(setInfo.NewValue)); + attachDownload(manager.GetExistingDownload(modelInfo.NewValue)); }, true); - beatmaps.DownloadBegan += download => + manager.DownloadBegan += download => { - if (download.Info.OnlineBeatmapSetID == BeatmapSet.Value?.OnlineBeatmapSetID) + if (download.Info.Equals(ModelInfo.Value)) attachDownload(download); }; - beatmaps.ItemAdded += setAdded; - beatmaps.ItemRemoved += setRemoved; + manager.ItemAdded += itemAdded; + manager.ItemRemoved += itemRemoved; } #region Disposal @@ -63,10 +66,10 @@ namespace osu.Game.Overlays.Direct { base.Dispose(isDisposing); - if (beatmaps != null) + if (manager != null) { - beatmaps.DownloadBegan -= attachDownload; - beatmaps.ItemAdded -= setAdded; + manager.DownloadBegan -= attachDownload; + manager.ItemAdded -= itemAdded; } State.UnbindAll(); @@ -76,9 +79,9 @@ namespace osu.Game.Overlays.Direct #endregion - private ArchiveDownloadModelRequest attachedRequest; + private ArchiveDownloadModelRequest attachedRequest; - private void attachDownload(ArchiveDownloadModelRequest request) + private void attachDownload(ArchiveDownloadModelRequest request) { if (attachedRequest != null) { @@ -118,13 +121,13 @@ namespace osu.Game.Overlays.Direct private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null)); - private void setAdded(BeatmapSetInfo s, bool existing) => setDownloadStateFromManager(s, DownloadState.LocallyAvailable); + private void itemAdded(TModel s, bool existing) => setDownloadStateFromManager(s, DownloadState.LocallyAvailable); - private void setRemoved(BeatmapSetInfo s) => setDownloadStateFromManager(s, DownloadState.NotDownloaded); + private void itemRemoved(TModel s) => setDownloadStateFromManager(s, DownloadState.NotDownloaded); - private void setDownloadStateFromManager(BeatmapSetInfo s, DownloadState state) => Schedule(() => + private void setDownloadStateFromManager(TModel s, DownloadState state) => Schedule(() => { - if (s.OnlineBeatmapSetID != BeatmapSet.Value?.OnlineBeatmapSetID) + if (s.Equals(ModelInfo.Value)) return; State.Value = state; diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs index 0a159507fe..3e8a5a8324 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Online; using osu.Game.Online.API; using osu.Game.Overlays.Direct; using osu.Game.Users; @@ -19,7 +20,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapSet.Buttons { - public class DownloadButton : DownloadTrackingComposite, IHasTooltip + public class DownloadButton : BeatmapDownloadTrackingComposite, IHasTooltip { private readonly bool noVideo; diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index a0f71d05c0..d0c6e4aa62 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -13,6 +13,7 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Online; using osu.Game.Overlays.BeatmapSet.Buttons; using osu.Game.Overlays.Direct; using osuTK; @@ -21,7 +22,7 @@ using DownloadButton = osu.Game.Overlays.BeatmapSet.Buttons.DownloadButton; namespace osu.Game.Overlays.BeatmapSet { - public class Header : DownloadTrackingComposite + public class Header : BeatmapDownloadTrackingComposite { private const float transition_duration = 200; private const float tabs_height = 50; diff --git a/osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs new file mode 100644 index 0000000000..67fc5b48bd --- /dev/null +++ b/osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs @@ -0,0 +1,16 @@ +using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Online; + +namespace osu.Game.Overlays.Direct +{ + public abstract class BeatmapDownloadTrackingComposite : DownloadTrackingComposite + { + public Bindable BeatmapSet => ModelInfo; + + public BeatmapDownloadTrackingComposite(BeatmapSetInfo set = null) + : base(set) + { + } + } +} diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs index 3f44d854e5..348ce427bd 100644 --- a/osu.Game/Overlays/Direct/DownloadButton.cs +++ b/osu.Game/Overlays/Direct/DownloadButton.cs @@ -9,11 +9,12 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Online; using osuTK; namespace osu.Game.Overlays.Direct { - public class DownloadButton : DownloadTrackingComposite + public class DownloadButton : BeatmapDownloadTrackingComposite { private readonly bool noVideo; private readonly SpriteIcon icon; diff --git a/osu.Game/Overlays/Direct/DownloadProgressBar.cs b/osu.Game/Overlays/Direct/DownloadProgressBar.cs index 57500b3531..a6cefaae84 100644 --- a/osu.Game/Overlays/Direct/DownloadProgressBar.cs +++ b/osu.Game/Overlays/Direct/DownloadProgressBar.cs @@ -7,11 +7,12 @@ using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Online; using osuTK.Graphics; namespace osu.Game.Overlays.Direct { - public class DownloadProgressBar : DownloadTrackingComposite + public class DownloadProgressBar : BeatmapDownloadTrackingComposite { private readonly ProgressBar progressBar; From 15893bbb754fa73003f578a385b9cb7fa98a3b7d Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 11 Jun 2019 19:41:48 +0200 Subject: [PATCH 1036/2854] Drop UserActivity prefix for subclasses nested in UserActivity + Change status messages. --- .../Visual/Online/TestSceneUserPanel.cs | 10 ++++---- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Select/PlaySongSelect.cs | 2 +- osu.Game/Users/UserActivity.cs | 24 +++++++++---------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs index 2c20ea98a3..18541e7637 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs @@ -61,11 +61,11 @@ namespace osu.Game.Tests.Visual.Online public void UserActivitiesTests() { AddStep("idle", () => { flyte.Activity.Value = null; }); - AddStep("spectating", () => { flyte.Activity.Value = new UserActivity.UserActivitySpectating(); }); - AddStep("solo", () => { flyte.Activity.Value = new UserActivity.UserActivitySoloGame(null, null); }); - AddStep("choosing", () => { flyte.Activity.Value = new UserActivity.UserActivityChoosingBeatmap(); }); - AddStep("editing", () => { flyte.Activity.Value = new UserActivity.UserActivityEditing(null); }); - AddStep("modding", () => { flyte.Activity.Value = new UserActivity.UserActivityModding(); }); + AddStep("spectating", () => { flyte.Activity.Value = new UserActivity.Spectating(); }); + AddStep("solo", () => { flyte.Activity.Value = new UserActivity.SoloGame(null, null); }); + AddStep("choosing", () => { flyte.Activity.Value = new UserActivity.ChoosingBeatmap(); }); + AddStep("editing", () => { flyte.Activity.Value = new UserActivity.Editing(null); }); + AddStep("modding", () => { flyte.Activity.Value = new UserActivity.Modding(); }); } } } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index a4cb659183..c6e1850af1 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens.Edit private DependencyContainer dependencies; private GameHost host; - protected override UserActivity InitialScreenActivity => new UserActivity.UserActivityEditing(Beatmap.Value.BeatmapInfo); + protected override UserActivity InitialScreenActivity => new UserActivity.Editing(Beatmap.Value.BeatmapInfo); protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 22a0e65f91..e8fa99b397 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Play { protected override bool AllowBackButton => false; // handled by HoldForMenuButton - protected override UserActivity InitialScreenActivity => new UserActivity.UserActivitySoloGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); + protected override UserActivity InitialScreenActivity => new UserActivity.SoloGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); public override float BackgroundParallaxAmount => 0.1f; diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index aba3343d69..f81bad6693 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Select public override bool AllowExternalScreenChange => true; - protected override UserActivity InitialScreenActivity => new UserActivity.UserActivityChoosingBeatmap(); + protected override UserActivity InitialScreenActivity => new UserActivity.ChoosingBeatmap(); [BackgroundDependencyLoader] private void load(OsuColour colours) diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index fe72f116ee..b088e8036d 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -12,25 +12,25 @@ namespace osu.Game.Users public abstract string Status { get; } public virtual Color4 GetAppropriateColour(OsuColour colours) => colours.GreenDarker; - public class UserActivityModding : UserActivity + public class Modding : UserActivity { public override string Status => "Modding a map"; public override Color4 GetAppropriateColour(OsuColour colours) => colours.PurpleDark; } - public class UserActivityChoosingBeatmap : UserActivity + public class ChoosingBeatmap : UserActivity { public override string Status => "Choosing a beatmap"; } - public class UserActivityMultiplayerGame : UserActivity + public class MultiplayerGame : UserActivity { - public override string Status => "Multiplaying"; + public override string Status => "Playing with others"; } - public class UserActivityEditing : UserActivity + public class Editing : UserActivity { - public UserActivityEditing(BeatmapInfo info) + public Editing(BeatmapInfo info) { Beatmap = info; } @@ -40,29 +40,29 @@ namespace osu.Game.Users public BeatmapInfo Beatmap { get; } } - public class UserActivitySoloGame : UserActivity + public class SoloGame : UserActivity { - public UserActivitySoloGame(BeatmapInfo info, Rulesets.RulesetInfo ruleset) + public SoloGame(BeatmapInfo info, Rulesets.RulesetInfo ruleset) { Beatmap = info; Ruleset = ruleset; } - public override string Status => @"Solo Game"; + public override string Status => @"Playing alone"; public BeatmapInfo Beatmap { get; } public Rulesets.RulesetInfo Ruleset { get; } } - public class UserActivitySpectating : UserActivity + public class Spectating : UserActivity { public override string Status => @"Spectating a game"; } - public class UserActivityInLobby : UserActivity + public class InLobby : UserActivity { - public override string Status => @"in Multiplayer Lobby"; + public override string Status => @"In a Multiplayer Lobby"; } } } From 41da491a7eb3d9287c7e1725214f4e456576bdd7 Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 11 Jun 2019 23:23:40 +0530 Subject: [PATCH 1037/2854] Make BeatmapSetInfo equatable --- osu.Game/Beatmaps/BeatmapSetInfo.cs | 4 +++- osu.Game/Online/DownloadTrackingComposite.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 390236e053..aa5ed48641 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -9,7 +9,7 @@ using osu.Game.Database; namespace osu.Game.Beatmaps { - public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles, ISoftDelete + public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles, ISoftDelete, IEquatable { public int ID { get; set; } @@ -46,5 +46,7 @@ namespace osu.Game.Beatmaps public override string ToString() => Metadata?.ToString() ?? base.ToString(); public bool Protected { get; set; } + + public bool Equals(BeatmapSetInfo other) => OnlineBeatmapSetID == other?.OnlineBeatmapSetID; } } diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index 157211e6c2..d233be19f5 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -16,7 +16,7 @@ namespace osu.Game.Online /// A component which tracks a beatmap through potential download/import/deletion. /// public abstract class DownloadTrackingComposite : CompositeDrawable - where TModel : class + where TModel : class, IEquatable where TModelManager : class, IDownloadModelManager { public readonly Bindable ModelInfo = new Bindable(); From 51d428ef949f68e02803339fa2c32cfc4888495b Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 11 Jun 2019 20:00:14 +0200 Subject: [PATCH 1038/2854] Refactor UserPanel status display logic --- osu.Game/Users/UserPanel.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 1f31ead1e7..04f5a0a3fb 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -231,17 +231,18 @@ namespace osu.Game.Users statusBar.ResizeHeightTo(status_height, transition_duration, Easing.OutQuint); statusBar.FadeIn(transition_duration, Easing.OutQuint); this.ResizeHeightTo(height, transition_duration, Easing.OutQuint); - - if (status is UserStatusOnline && activity != null) - { - statusMessage.Text = activity.Status; - statusBg.FadeColour(activity.GetAppropriateColour(colours), 500, Easing.OutQuint); - return; - } } - statusMessage.Text = status?.Message; - statusBg.FadeColour(status?.GetAppropriateColour(colours) ?? colours.Gray5, 500, Easing.OutQuint); + if (status is UserStatusOnline && activity != null) + { + statusMessage.Text = activity.Status; + statusBg.FadeColour(activity.GetAppropriateColour(colours), 500, Easing.OutQuint); + } + else + { + statusMessage.Text = status?.Message; + statusBg.FadeColour(status?.GetAppropriateColour(colours) ?? colours.Gray5, 500, Easing.OutQuint); + } } public MenuItem[] ContextMenuItems => new MenuItem[] From c320b6110cf463a1f5e74050e320468a26a484a1 Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 11 Jun 2019 23:47:05 +0530 Subject: [PATCH 1039/2854] Rename interface - Fix wrong inheritance in ArchiveModelManager - Add license headers --- osu.Game/Database/ArchiveDownloadModelManager.cs | 2 +- osu.Game/Database/ArchiveModelManager.cs | 4 ++-- ...{IDownloadModelManager.cs => IModelDownloader.cs} | 9 +++++---- osu.Game/Database/IModelManager.cs | 12 +++--------- 4 files changed, 11 insertions(+), 16 deletions(-) rename osu.Game/Database/{IDownloadModelManager.cs => IModelDownloader.cs} (87%) diff --git a/osu.Game/Database/ArchiveDownloadModelManager.cs b/osu.Game/Database/ArchiveDownloadModelManager.cs index c868b8d1d5..8d221bd3ea 100644 --- a/osu.Game/Database/ArchiveDownloadModelManager.cs +++ b/osu.Game/Database/ArchiveDownloadModelManager.cs @@ -18,7 +18,7 @@ namespace osu.Game.Database /// /// The model type. /// The associated file join type. - public abstract class ArchiveDownloadModelManager : ArchiveModelManager, IDownloadModelManager + public abstract class ArchiveDownloadModelManager : ArchiveModelManager, IModelDownloader where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : INamedFileInfo, new() { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 50cb9dac8b..ccaba99427 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -29,7 +29,7 @@ namespace osu.Game.Database /// /// The model type. /// The associated file join type. - public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager + public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : INamedFileInfo, new() { @@ -44,7 +44,7 @@ namespace osu.Game.Database /// Fired when a new becomes available in the database. /// This is not guaranteed to run on the update thread. /// - public event ItemAddedDelegate ItemAdded; + public event Action ItemAdded; /// /// Fired when a is removed from the database. diff --git a/osu.Game/Database/IDownloadModelManager.cs b/osu.Game/Database/IModelDownloader.cs similarity index 87% rename from osu.Game/Database/IDownloadModelManager.cs rename to osu.Game/Database/IModelDownloader.cs index 3231d855ea..bf987bb53c 100644 --- a/osu.Game/Database/IDownloadModelManager.cs +++ b/osu.Game/Database/IModelDownloader.cs @@ -1,11 +1,12 @@ -using osu.Game.Online.API; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Online.API; using System; -using System.Collections.Generic; -using System.Text; namespace osu.Game.Database { - public interface IDownloadModelManager : IModelManager + public interface IModelDownloader : IModelManager where TModel : class { /// diff --git a/osu.Game/Database/IModelManager.cs b/osu.Game/Database/IModelManager.cs index 6a0b2bde44..ee78df3db4 100644 --- a/osu.Game/Database/IModelManager.cs +++ b/osu.Game/Database/IModelManager.cs @@ -1,20 +1,14 @@ - +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System; namespace osu.Game.Database { public interface IModelManager { - /// - /// Fired when a new becomes available in the database. - /// This is not guaranteed to run on the update thread. - /// event Action ItemAdded; - /// - /// Fired when a is removed from the database. - /// This is not guaranteed to run on the update thread. - /// event Action ItemRemoved; } } From c69d3e2d388c794a0fcc33efdbc8b37999c48c68 Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 12 Jun 2019 00:02:53 +0530 Subject: [PATCH 1040/2854] Fix doc move derp --- osu.Game/Database/ArchiveDownloadModelManager.cs | 10 +++++----- osu.Game/Database/IModelDownloader.cs | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Database/ArchiveDownloadModelManager.cs b/osu.Game/Database/ArchiveDownloadModelManager.cs index 8d221bd3ea..ded44337dd 100644 --- a/osu.Game/Database/ArchiveDownloadModelManager.cs +++ b/osu.Game/Database/ArchiveDownloadModelManager.cs @@ -71,13 +71,13 @@ namespace osu.Game.Database return true; } + /// + /// Checks whether a given is available in the local store already. + /// + /// The whose existence needs to be checked. + /// Whether the exists locally. public virtual bool IsAvailableLocally(TModel model) => modelStore.ConsumableItems.Any(m => m.Equals(model) && !m.DeletePending); - /// - /// Gets an existing download request if it exists. - /// - /// The whose request is wanted. - /// The object if it exists, otherwise null. public ArchiveDownloadModelRequest GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Info.Equals(model)); private bool canDownload(TModel model) => GetExistingDownload(model) == null && api != null; diff --git a/osu.Game/Database/IModelDownloader.cs b/osu.Game/Database/IModelDownloader.cs index bf987bb53c..150ad9522f 100644 --- a/osu.Game/Database/IModelDownloader.cs +++ b/osu.Game/Database/IModelDownloader.cs @@ -39,10 +39,10 @@ namespace osu.Game.Database bool Download(TModel model, params object[] extra); /// - /// Checks whether a given is available in the local store already. + /// Gets an existing download request if it exists. /// - /// The whose existence needs to be checked. - /// Whether the exists locally. + /// The whose request is wanted. + /// The object if it exists, otherwise null. ArchiveDownloadModelRequest GetExistingDownload(TModel model); } } From 7495bc5d3a97f457407a8f561b16d4b856f058ea Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 12 Jun 2019 00:41:17 +0530 Subject: [PATCH 1041/2854] Post merge and inverted condition fix --- osu.Game/Online/DownloadTrackingComposite.cs | 4 ++-- osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index d233be19f5..0cb3acf019 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -17,7 +17,7 @@ namespace osu.Game.Online /// public abstract class DownloadTrackingComposite : CompositeDrawable where TModel : class, IEquatable - where TModelManager : class, IDownloadModelManager + where TModelManager : class, IModelDownloader { public readonly Bindable ModelInfo = new Bindable(); @@ -127,7 +127,7 @@ namespace osu.Game.Online private void setDownloadStateFromManager(TModel s, DownloadState state) => Schedule(() => { - if (s.Equals(ModelInfo.Value)) + if (!s.Equals(ModelInfo.Value)) return; State.Value = state; diff --git a/osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs index 67fc5b48bd..b6304b82c4 100644 --- a/osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs @@ -1,4 +1,7 @@ -using osu.Framework.Bindables; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Online; From ba6546038c582d1d7249a078dac56624a49ac657 Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 12 Jun 2019 00:47:02 +0530 Subject: [PATCH 1042/2854] Make ModelInfo and abstract class constructor protected - Implementing classes would be better off exposing it if necessary under a different name --- osu.Game/Online/DownloadTrackingComposite.cs | 4 +--- osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index 0cb3acf019..d07aed9206 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -2,11 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Online.API; @@ -19,7 +17,7 @@ namespace osu.Game.Online where TModel : class, IEquatable where TModelManager : class, IModelDownloader { - public readonly Bindable ModelInfo = new Bindable(); + protected readonly Bindable ModelInfo = new Bindable(); private TModelManager manager; diff --git a/osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs index b6304b82c4..6f348c4fd7 100644 --- a/osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays.Direct { public Bindable BeatmapSet => ModelInfo; - public BeatmapDownloadTrackingComposite(BeatmapSetInfo set = null) + protected BeatmapDownloadTrackingComposite(BeatmapSetInfo set = null) : base(set) { } From 35cc45c9d9dff91795a90ef8785aa3bf99fe7228 Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Tue, 11 Jun 2019 23:11:03 +0300 Subject: [PATCH 1043/2854] Small update --- osu.Game.Tests/Visual/Online/TestSceneBeatmapNotAvailable.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapNotAvailable.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapNotAvailable.cs index bd4570470d..e2a5ab1e35 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapNotAvailable.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapNotAvailable.cs @@ -1,13 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Overlays.BeatmapSet; namespace osu.Game.Tests.Visual.Online { - [TestFixture] public class TestSceneBeatmapNotAvailable : OsuTestScene { public TestSceneBeatmapNotAvailable() From eaeeffaa866fd7a16108ffc96ac7544319232cf8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Jun 2019 13:28:44 +0900 Subject: [PATCH 1044/2854] Rename to DownloadableArchiveModelManager --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- ...loadModelManager.cs => DownloadableArchiveModelManager.cs} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename osu.Game/Database/{ArchiveDownloadModelManager.cs => DownloadableArchiveModelManager.cs} (94%) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e124e38dd9..023b6c74ea 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -25,7 +25,7 @@ namespace osu.Game.Beatmaps /// /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. /// - public partial class BeatmapManager : ArchiveDownloadModelManager + public partial class BeatmapManager : DownloadableArchiveModelManager { /// /// Fired when a single difficulty has been hidden. diff --git a/osu.Game/Database/ArchiveDownloadModelManager.cs b/osu.Game/Database/DownloadableArchiveModelManager.cs similarity index 94% rename from osu.Game/Database/ArchiveDownloadModelManager.cs rename to osu.Game/Database/DownloadableArchiveModelManager.cs index ded44337dd..71bbbc4f78 100644 --- a/osu.Game/Database/ArchiveDownloadModelManager.cs +++ b/osu.Game/Database/DownloadableArchiveModelManager.cs @@ -18,7 +18,7 @@ namespace osu.Game.Database /// /// The model type. /// The associated file join type. - public abstract class ArchiveDownloadModelManager : ArchiveModelManager, IModelDownloader + public abstract class DownloadableArchiveModelManager : ArchiveModelManager, IModelDownloader where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : INamedFileInfo, new() { @@ -32,7 +32,7 @@ namespace osu.Game.Database private readonly MutableDatabaseBackedStoreWithFileIncludes modelStore; - protected ArchiveDownloadModelManager(Storage storage, IDatabaseContextFactory contextFactory, IAPIProvider api, MutableDatabaseBackedStoreWithFileIncludes modelStore, IIpcHost importHost = null) + protected DownloadableArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, IAPIProvider api, MutableDatabaseBackedStoreWithFileIncludes modelStore, IIpcHost importHost = null) : base(storage, contextFactory, modelStore, importHost) { this.api = api; From c591a6f1fadd26a0c39855a867d95c2056bd6d82 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Jun 2019 13:30:23 +0900 Subject: [PATCH 1045/2854] Rename request type to be less verbose --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/Database/DownloadableArchiveModelManager.cs | 12 ++++++------ osu.Game/Database/IModelDownloader.cs | 8 ++++---- ...loadModelRequest.cs => ArchiveDownloadRequest.cs} | 4 ++-- .../Online/API/Requests/DownloadBeatmapSetRequest.cs | 2 +- .../Overlays/Direct/DownloadTrackingComposite.cs | 4 ++-- 6 files changed, 16 insertions(+), 16 deletions(-) rename osu.Game/Online/API/{ArchiveDownloadModelRequest.cs => ArchiveDownloadRequest.cs} (78%) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 023b6c74ea..3fc3de41e6 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -74,7 +74,7 @@ namespace osu.Game.Beatmaps beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); } - protected override ArchiveDownloadModelRequest CreateDownloadRequest(BeatmapSetInfo set, object[] options) => new DownloadBeatmapSetRequest(set, (options?.FirstOrDefault() as bool?) ?? false); + protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, object[] options) => new DownloadBeatmapSetRequest(set, (options?.FirstOrDefault() as bool?) ?? false); protected override void Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive) { diff --git a/osu.Game/Database/DownloadableArchiveModelManager.cs b/osu.Game/Database/DownloadableArchiveModelManager.cs index 71bbbc4f78..c22c8d7508 100644 --- a/osu.Game/Database/DownloadableArchiveModelManager.cs +++ b/osu.Game/Database/DownloadableArchiveModelManager.cs @@ -22,13 +22,13 @@ namespace osu.Game.Database where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : INamedFileInfo, new() { - public event Action> DownloadBegan; + public event Action> DownloadBegan; - public event Action> DownloadFailed; + public event Action> DownloadFailed; private readonly IAPIProvider api; - private readonly List> currentDownloads = new List>(); + private readonly List> currentDownloads = new List>(); private readonly MutableDatabaseBackedStoreWithFileIncludes modelStore; @@ -47,7 +47,7 @@ namespace osu.Game.Database /// The to be downloaded. /// Extra parameters for request creation, null if none were passed. /// The request object. - protected abstract ArchiveDownloadModelRequest CreateDownloadRequest(TModel model, object[] options); + protected abstract ArchiveDownloadRequest CreateDownloadRequest(TModel model, object[] options); public bool Download(TModel model) { @@ -78,11 +78,11 @@ namespace osu.Game.Database /// Whether the exists locally. public virtual bool IsAvailableLocally(TModel model) => modelStore.ConsumableItems.Any(m => m.Equals(model) && !m.DeletePending); - public ArchiveDownloadModelRequest GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Info.Equals(model)); + public ArchiveDownloadRequest GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Info.Equals(model)); private bool canDownload(TModel model) => GetExistingDownload(model) == null && api != null; - private void performDownloadWithRequest(ArchiveDownloadModelRequest request) + private void performDownloadWithRequest(ArchiveDownloadRequest request) { DownloadNotification notification = new DownloadNotification { diff --git a/osu.Game/Database/IModelDownloader.cs b/osu.Game/Database/IModelDownloader.cs index 150ad9522f..83427bdc17 100644 --- a/osu.Game/Database/IModelDownloader.cs +++ b/osu.Game/Database/IModelDownloader.cs @@ -12,12 +12,12 @@ namespace osu.Game.Database /// /// Fired when a download begins. /// - event Action> DownloadBegan; + event Action> DownloadBegan; /// /// Fired when a download is interrupted, either due to user cancellation or failure. /// - event Action> DownloadFailed; + event Action> DownloadFailed; bool IsAvailableLocally(TModel model); @@ -42,7 +42,7 @@ namespace osu.Game.Database /// Gets an existing download request if it exists. /// /// The whose request is wanted. - /// The object if it exists, otherwise null. - ArchiveDownloadModelRequest GetExistingDownload(TModel model); + /// The object if it exists, otherwise null. + ArchiveDownloadRequest GetExistingDownload(TModel model); } } diff --git a/osu.Game/Online/API/ArchiveDownloadModelRequest.cs b/osu.Game/Online/API/ArchiveDownloadRequest.cs similarity index 78% rename from osu.Game/Online/API/ArchiveDownloadModelRequest.cs rename to osu.Game/Online/API/ArchiveDownloadRequest.cs index 7f161f1e98..01f066694d 100644 --- a/osu.Game/Online/API/ArchiveDownloadModelRequest.cs +++ b/osu.Game/Online/API/ArchiveDownloadRequest.cs @@ -5,7 +5,7 @@ using System; namespace osu.Game.Online.API { - public abstract class ArchiveDownloadModelRequest : APIDownloadRequest + public abstract class ArchiveDownloadRequest : APIDownloadRequest where TModel : class { public readonly TModel Info; @@ -14,7 +14,7 @@ namespace osu.Game.Online.API public event Action DownloadProgressed; - protected ArchiveDownloadModelRequest(TModel model) + protected ArchiveDownloadRequest(TModel model) { Info = model; diff --git a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs index 8d636f6730..999864a6eb 100644 --- a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs +++ b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs @@ -5,7 +5,7 @@ using osu.Game.Beatmaps; namespace osu.Game.Online.API.Requests { - public class DownloadBeatmapSetRequest : ArchiveDownloadModelRequest + public class DownloadBeatmapSetRequest : ArchiveDownloadRequest { private readonly bool noVideo; private readonly BeatmapSetInfo set; diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs index c1ff6ecb60..9d0266c00e 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs @@ -76,9 +76,9 @@ namespace osu.Game.Overlays.Direct #endregion - private ArchiveDownloadModelRequest attachedRequest; + private ArchiveDownloadRequest attachedRequest; - private void attachDownload(ArchiveDownloadModelRequest request) + private void attachDownload(ArchiveDownloadRequest request) { if (attachedRequest != null) { From 94e65a324456172971e1b387f039ec1fd7343c0d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Jun 2019 15:16:59 +0900 Subject: [PATCH 1046/2854] Fix settings checkboxes not being searchable --- osu.Game/Overlays/Settings/SettingsCheckbox.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/SettingsCheckbox.cs b/osu.Game/Overlays/Settings/SettingsCheckbox.cs index a1501d8015..a554159fd7 100644 --- a/osu.Game/Overlays/Settings/SettingsCheckbox.cs +++ b/osu.Game/Overlays/Settings/SettingsCheckbox.cs @@ -10,11 +10,14 @@ namespace osu.Game.Overlays.Settings { private OsuCheckbox checkbox; + private string labelText; + protected override Drawable CreateControl() => checkbox = new OsuCheckbox(); public override string LabelText { - set => checkbox.LabelText = value; + get => labelText; + set => checkbox.LabelText = labelText = value; } } } From fc1f778b82edc316d7c0d01eb8526ff247ea97c1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Jun 2019 15:53:53 +0900 Subject: [PATCH 1047/2854] Remove implicit null --- osu.Game/Screens/OsuScreen.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 582f97d46a..874ac35e39 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -123,8 +123,6 @@ namespace osu.Game.Screens { Anchor = Anchor.Centre; Origin = Anchor.Centre; - - screenActivity = null; } [BackgroundDependencyLoader(true)] From bb8a77d27d7a671f4a5c1b065d3cf1d2e7a3618e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Jun 2019 15:59:52 +0900 Subject: [PATCH 1048/2854] Apply all steps to same user panel so interactions can be observed --- .../Visual/Online/TestSceneUserPanel.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs index 18541e7637..30814ad9c7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs @@ -12,11 +12,12 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneUserPanel : OsuTestScene { - private readonly UserPanel flyte; private readonly UserPanel peppy; public TestSceneUserPanel() { + UserPanel flyte; + Add(new FillFlowContainer { Anchor = Anchor.Centre, @@ -60,12 +61,12 @@ namespace osu.Game.Tests.Visual.Online [Test] public void UserActivitiesTests() { - AddStep("idle", () => { flyte.Activity.Value = null; }); - AddStep("spectating", () => { flyte.Activity.Value = new UserActivity.Spectating(); }); - AddStep("solo", () => { flyte.Activity.Value = new UserActivity.SoloGame(null, null); }); - AddStep("choosing", () => { flyte.Activity.Value = new UserActivity.ChoosingBeatmap(); }); - AddStep("editing", () => { flyte.Activity.Value = new UserActivity.Editing(null); }); - AddStep("modding", () => { flyte.Activity.Value = new UserActivity.Modding(); }); + AddStep("idle", () => { peppy.Activity.Value = null; }); + AddStep("spectating", () => { peppy.Activity.Value = new UserActivity.Spectating(); }); + AddStep("solo", () => { peppy.Activity.Value = new UserActivity.SoloGame(null, null); }); + AddStep("choosing", () => { peppy.Activity.Value = new UserActivity.ChoosingBeatmap(); }); + AddStep("editing", () => { peppy.Activity.Value = new UserActivity.Editing(null); }); + AddStep("modding", () => { peppy.Activity.Value = new UserActivity.Modding(); }); } } } From 13234fb4a4b5e9a26a3480c3ac3c960fd7f56c60 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 Jun 2019 16:07:35 +0900 Subject: [PATCH 1049/2854] Adjust comments a bit --- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 6e3bec106f..cf21c78c7f 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -155,7 +155,7 @@ namespace osu.Game.Screens.Select int? previouslySelectedID = null; CarouselBeatmapSet existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.ID == beatmapSet.ID); - // Since we're about to remove the selected beatmap, store its ID so we can go back if needed. + // If the selected beatmap is about to be removed, store its ID so it can be re-selected if required if (existingSet?.State?.Value == CarouselItemState.Selected) previouslySelectedID = selectedBeatmap?.Beatmap.ID; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index dcfe1de8f2..d0645dbab6 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -594,7 +594,7 @@ namespace osu.Game.Screens.Select { bindBindables(); - // As a selection was already obtained, do not attempt to update the selected beatmap. + // If a selection was already obtained, do not attempt to update the selected beatmap. if (Carousel.SelectedBeatmapSet != null) return; From 20b43c20c8f35147f5517f0e50e9730ed20fe9ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Jun 2019 16:33:15 +0900 Subject: [PATCH 1050/2854] Rename variables to remove redundant "screen" terminology --- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/OsuScreen.cs | 33 +++++++++++------------ osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- osu.Game/Screens/Select/PlaySongSelect.cs | 2 +- osu.Game/Users/UserActivity.cs | 12 ++++----- 6 files changed, 26 insertions(+), 27 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index c6e1850af1..de0f3870c6 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens.Edit private DependencyContainer dependencies; private GameHost host; - protected override UserActivity InitialScreenActivity => new UserActivity.Editing(Beatmap.Value.BeatmapInfo); + protected override UserActivity InitialActivity => new UserActivity.Editing(Beatmap.Value.BeatmapInfo); protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 874ac35e39..fe53ad17c3 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -56,25 +56,25 @@ namespace osu.Game.Screens /// /// The to set the user's activity automatically to when this screen is entered - /// This will be automatically set to for this screen on entering unless - /// is manually set before. + /// This will be automatically set to for this screen on entering unless + /// is manually set before. /// - protected virtual UserActivity InitialScreenActivity => null; + protected virtual UserActivity InitialActivity => null; - private UserActivity screenActivity; + private UserActivity activity; /// - /// The for this screen. + /// The current for this screen. /// - protected UserActivity ScreenActivity + protected UserActivity Activity { - get => screenActivity; + get => activity; set { - if (value == screenActivity) return; + if (value == activity) return; - screenActivity = value; - setUserActivity(screenActivity); + activity = value; + updateActivity(); } } @@ -152,7 +152,7 @@ namespace osu.Game.Screens sampleExit?.Play(); applyArrivingDefaults(true); - setUserActivity(screenActivity); + updateActivity(); base.OnResuming(last); } @@ -170,8 +170,8 @@ namespace osu.Game.Screens backgroundStack?.Push(localBackground = CreateBackground()); - if (screenActivity == null) - ScreenActivity = InitialScreenActivity; + if (activity == null) + Activity = InitialActivity; base.OnEntering(last); } @@ -190,11 +190,10 @@ namespace osu.Game.Screens return false; } - private void setUserActivity(UserActivity activity) + private void updateActivity() { - if (api == null) return; - - api.LocalUser.Value.Activity.Value = activity; + if (api != null) + api.LocalUser.Value.Activity.Value = activity; } /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a545d77184..a25b8a1de7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Play { protected override bool AllowBackButton => false; // handled by HoldForMenuButton - protected override UserActivity InitialScreenActivity => new UserActivity.SoloGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); + protected override UserActivity InitialActivity => new UserActivity.SoloGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); public override float BackgroundParallaxAmount => 0.1f; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 33d100c871..9de9f5cec8 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Play private bool hideOverlays; public override bool HideOverlaysOnEnter => hideOverlays; - protected override UserActivity InitialScreenActivity => null; //shows the previous screen status + protected override UserActivity InitialActivity => null; //shows the previous screen status public override bool DisallowExternalBeatmapRulesetChanges => true; diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index f81bad6693..4df6e6a3f3 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Select public override bool AllowExternalScreenChange => true; - protected override UserActivity InitialScreenActivity => new UserActivity.ChoosingBeatmap(); + protected override UserActivity InitialActivity => new UserActivity.ChoosingBeatmap(); [BackgroundDependencyLoader] private void load(OsuColour colours) diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index b088e8036d..918c547978 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -30,18 +30,22 @@ namespace osu.Game.Users public class Editing : UserActivity { + public BeatmapInfo Beatmap { get; } + public Editing(BeatmapInfo info) { Beatmap = info; } public override string Status => @"Editing a beatmap"; - - public BeatmapInfo Beatmap { get; } } public class SoloGame : UserActivity { + public BeatmapInfo Beatmap { get; } + + public Rulesets.RulesetInfo Ruleset { get; } + public SoloGame(BeatmapInfo info, Rulesets.RulesetInfo ruleset) { Beatmap = info; @@ -49,10 +53,6 @@ namespace osu.Game.Users } public override string Status => @"Playing alone"; - - public BeatmapInfo Beatmap { get; } - - public Rulesets.RulesetInfo Ruleset { get; } } public class Spectating : UserActivity From c4f54d94bc5e8aa9d52b7e17c0dd3508693cc6bc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 Jun 2019 17:00:27 +0900 Subject: [PATCH 1051/2854] Rename methods --- osu.Game/Beatmaps/BeatmapManager.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 67d3382e01..5204bda353 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -110,7 +110,7 @@ namespace osu.Game.Beatmaps validateOnlineIds(beatmapSet); - await updateQueue.Perform(beatmapSet, cancellationToken); + await updateQueue.UpdateAsync(beatmapSet, cancellationToken); } protected override void PreImport(BeatmapSetInfo beatmapSet) @@ -433,20 +433,20 @@ namespace osu.Game.Beatmaps this.api = api; } - public async Task Perform(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken) + public async Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken) { if (api?.State != APIState.Online) return; LogForModel(beatmapSet, "Performing online lookups..."); - await Task.WhenAll(beatmapSet.Beatmaps.Select(b => Perform(beatmapSet, b, cancellationToken)).ToArray()); + await Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray()); } // todo: expose this when we need to do individual difficulty lookups. - protected Task Perform(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken) - => Task.Factory.StartNew(() => perform(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler); + protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken) + => Task.Factory.StartNew(() => update(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler); - private void perform(BeatmapSetInfo set, BeatmapInfo beatmap) + private void update(BeatmapSetInfo set, BeatmapInfo beatmap) { if (api?.State != APIState.Online) return; From fd7dc9504e6c91b9502c5e389d28677be49b81d5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 Jun 2019 17:08:50 +0900 Subject: [PATCH 1052/2854] Remove async when not required --- osu.Game/Beatmaps/BeatmapManager.cs | 10 +++++----- osu.Game/Database/ArchiveModelManager.cs | 18 +++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 5204bda353..d90657bff5 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -94,7 +94,7 @@ namespace osu.Game.Beatmaps updateQueue = new BeatmapUpdateQueue(api); } - protected override async Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default) + protected override Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default) { if (archive != null) beatmapSet.Beatmaps = createBeatmapDifficulties(archive); @@ -110,7 +110,7 @@ namespace osu.Game.Beatmaps validateOnlineIds(beatmapSet); - await updateQueue.UpdateAsync(beatmapSet, cancellationToken); + return updateQueue.UpdateAsync(beatmapSet, cancellationToken); } protected override void PreImport(BeatmapSetInfo beatmapSet) @@ -433,13 +433,13 @@ namespace osu.Game.Beatmaps this.api = api; } - public async Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken) + public Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken) { if (api?.State != APIState.Online) - return; + return Task.CompletedTask; LogForModel(beatmapSet, "Performing online lookups..."); - await Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray()); + return Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray()); } // todo: expose this when we need to do individual difficulty lookups. diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index d764601b32..0cf7816250 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -132,13 +132,13 @@ namespace osu.Game.Database /// This will post notifications tracking progress. /// /// One or more archive locations on disk. - public async Task Import(params string[] paths) + public Task Import(params string[] paths) { var notification = new ProgressNotification { State = ProgressNotificationState.Active }; PostNotification?.Invoke(notification); - await Import(notification, paths); + return Import(notification, paths); } protected async Task Import(ProgressNotification notification, params string[] paths) @@ -243,7 +243,7 @@ namespace osu.Game.Database /// /// The archive to be imported. /// An optional cancellation token. - public async Task Import(ArchiveReader archive, CancellationToken cancellationToken = default) + public Task Import(ArchiveReader archive, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); @@ -267,7 +267,7 @@ namespace osu.Game.Database return null; } - return await Import(model, archive, cancellationToken); + return Import(model, archive, cancellationToken); } /// @@ -548,24 +548,24 @@ namespace osu.Game.Database /// /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. /// - public async Task ImportFromStableAsync() + public Task ImportFromStableAsync() { var stable = GetStableStorage?.Invoke(); if (stable == null) { Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); - return; + return Task.CompletedTask; } if (!stable.ExistsDirectory(ImportFromStablePath)) { // This handles situations like when the user does not have a Skins folder Logger.Log($"No {ImportFromStablePath} folder available in osu!stable installation", LoggingTarget.Information, LogLevel.Error); - return; + return Task.CompletedTask; } - await Task.Run(async () => await Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray())); + return Task.Run(async () => await Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray())); } #endregion From 2a67944889c302fe69a87838f70d835c63b50740 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 Jun 2019 17:10:55 +0900 Subject: [PATCH 1053/2854] Remove interlocked within a lock --- osu.Game/Database/ArchiveModelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 0cf7816250..1c17adf7b7 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -161,8 +161,8 @@ namespace osu.Game.Database lock (imported) { imported.Add(model); + current++; - Interlocked.Increment(ref current); notification.Text = $"Imported {current} of {paths.Length} {humanisedModelName}s"; notification.Progress = (float)current / paths.Length; } From d4deac48ee78e025f14bace79fc2388923a4eef9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 Jun 2019 17:27:15 +0900 Subject: [PATCH 1054/2854] Improve model deletion notification text --- osu.Game/Database/ArchiveModelManager.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 54dbae9ddc..b5a9c70e47 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -392,7 +392,8 @@ namespace osu.Game.Database var notification = new ProgressNotification { Progress = 0, - CompletionText = $"Deleted all {typeof(TModel).Name.Replace("Info", "").ToLower()}s!", + Text = $"Preparing to delete all {humanisedModelName}s...", + CompletionText = $"Deleted all {humanisedModelName}s!", State = ProgressNotificationState.Active, }; @@ -409,7 +410,7 @@ namespace osu.Game.Database // user requested abort return; - notification.Text = $"Deleting ({++i} of {items.Count})"; + notification.Text = $"Deleting {humanisedModelName}s ({++i} of {items.Count})"; Delete(b); From f358fce9abe950fa0f60e69bea2776196ef64ccc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Jun 2019 18:04:57 +0900 Subject: [PATCH 1055/2854] Move activity (writable) bindable to APIAccess so it correctly transfers between users --- .../Visual/Online/TestSceneUserPanel.cs | 17 +++++++++++------ osu.Game/Online/API/APIAccess.cs | 8 ++++++++ osu.Game/Online/API/DummyAPIAccess.cs | 11 +++++++++++ osu.Game/Online/API/IAPIProvider.cs | 5 +++++ .../Settings/Sections/General/LoginSettings.cs | 6 ++---- osu.Game/Screens/OsuScreen.cs | 2 +- osu.Game/Users/User.cs | 2 +- osu.Game/Users/UserPanel.cs | 2 +- 8 files changed, 40 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs index 30814ad9c7..54f06d6ad2 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Users; @@ -61,12 +62,16 @@ namespace osu.Game.Tests.Visual.Online [Test] public void UserActivitiesTests() { - AddStep("idle", () => { peppy.Activity.Value = null; }); - AddStep("spectating", () => { peppy.Activity.Value = new UserActivity.Spectating(); }); - AddStep("solo", () => { peppy.Activity.Value = new UserActivity.SoloGame(null, null); }); - AddStep("choosing", () => { peppy.Activity.Value = new UserActivity.ChoosingBeatmap(); }); - AddStep("editing", () => { peppy.Activity.Value = new UserActivity.Editing(null); }); - AddStep("modding", () => { peppy.Activity.Value = new UserActivity.Modding(); }); + Bindable activity = new Bindable(); + + peppy.Activity.BindTo(activity); + + AddStep("idle", () => { activity.Value = null; }); + AddStep("spectating", () => { activity.Value = new UserActivity.Spectating(); }); + AddStep("solo", () => { activity.Value = new UserActivity.SoloGame(null, null); }); + AddStep("choosing", () => { activity.Value = new UserActivity.ChoosingBeatmap(); }); + AddStep("editing", () => { activity.Value = new UserActivity.Editing(null); }); + AddStep("modding", () => { activity.Value = new UserActivity.Modding(); }); } } } diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 343d6a67b7..12b38fab1e 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -37,6 +37,8 @@ namespace osu.Game.Online.API public Bindable LocalUser { get; } = new Bindable(createGuestUser()); + public Bindable Activity { get; } = new Bindable(); + protected bool HasLogin => authentication.Token.Value != null || (!string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password)); private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource(); @@ -55,6 +57,12 @@ namespace osu.Game.Online.API authentication.TokenString = config.Get(OsuSetting.Token); authentication.Token.ValueChanged += onTokenChanged; + LocalUser.BindValueChanged(u => + { + u.OldValue?.Activity.UnbindFrom(Activity); + u.NewValue.Activity.BindTo(Activity); + }, true); + var thread = new Thread(run) { Name = "APIAccess", diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index 99fde10309..6c04c77dc0 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -17,6 +17,8 @@ namespace osu.Game.Online.API Id = 1001, }); + public Bindable Activity { get; } = new Bindable(); + public bool IsLoggedIn => true; public string ProvidedUsername => LocalUser.Value.Username; @@ -41,6 +43,15 @@ namespace osu.Game.Online.API } } + public DummyAPIAccess() + { + LocalUser.BindValueChanged(u => + { + u.OldValue?.Activity.UnbindFrom(Activity); + u.NewValue.Activity.BindTo(Activity); + }, true); + } + public virtual void Queue(APIRequest request) { } diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index 7c1f850943..0cd41aee26 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -13,6 +13,11 @@ namespace osu.Game.Online.API /// Bindable LocalUser { get; } + /// + /// The current user's activity. + /// + Bindable Activity { get; } + /// /// Returns whether the local user is logged in. /// diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index 223c2aaf13..1454b6592d 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -156,7 +156,7 @@ namespace osu.Game.Overlays.Settings.Sections.General panel.Status.BindTo(api.LocalUser.Value.Status); panel.Activity.BindTo(api.LocalUser.Value.Activity); - dropdown.Current.ValueChanged += action => + dropdown.Current.BindValueChanged(action => { switch (action.NewValue) { @@ -179,9 +179,7 @@ namespace osu.Game.Overlays.Settings.Sections.General api.Logout(); break; } - }; - dropdown.Current.TriggerChange(); - + }, true); break; } diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index fe53ad17c3..e2aeb41de1 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -193,7 +193,7 @@ namespace osu.Game.Screens private void updateActivity() { if (api != null) - api.LocalUser.Value.Activity.Value = activity; + api.Activity.Value = activity; } /// diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index cf67af7bb8..c3ecd62e10 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -25,7 +25,7 @@ namespace osu.Game.Users public Bindable Status = new Bindable(); - public Bindable Activity = new Bindable(); + public IBindable Activity = new Bindable(); //public Team Team; diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 04f5a0a3fb..833c62013b 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -42,7 +42,7 @@ namespace osu.Game.Users public readonly Bindable Status = new Bindable(); - public readonly Bindable Activity = new Bindable(); + public readonly IBindable Activity = new Bindable(); public new Action Action; From 0f000fcc1402fababafc3df53eacffff17b918e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Jun 2019 19:58:26 +0900 Subject: [PATCH 1056/2854] Fix abysmal load performance when showing the social overlay --- osu.Game/Users/UserCoverBackground.cs | 52 +++++++++++++++++---------- osu.Game/Users/UserPanel.cs | 11 +++--- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/osu.Game/Users/UserCoverBackground.cs b/osu.Game/Users/UserCoverBackground.cs index dbc132995a..e583acac9f 100644 --- a/osu.Game/Users/UserCoverBackground.cs +++ b/osu.Game/Users/UserCoverBackground.cs @@ -21,31 +21,45 @@ namespace osu.Game.Users set => Model = value; } - [Resolved] - private LargeTextureStore textures { get; set; } + protected override Drawable CreateDrawable(User user) => new Cover(user); - protected override Drawable CreateDrawable(User user) + private class Cover : CompositeDrawable { - if (user == null) + private readonly User user; + + public Cover(User user) { - return new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.75f)) - }; + this.user = user; + + RelativeSizeAxes = Axes.Both; } - else + + [BackgroundDependencyLoader] + private void load(LargeTextureStore textures) { - var sprite = new Sprite + if (user == null) { - RelativeSizeAxes = Axes.Both, - Texture = textures.Get(user.CoverUrl), - FillMode = FillMode.Fill, - Anchor = Anchor.Centre, - Origin = Anchor.Centre - }; - sprite.OnLoadComplete += d => d.FadeInFromZero(400); - return sprite; + InternalChild = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.75f)) + }; + } + else + InternalChild = new Sprite + { + RelativeSizeAxes = Axes.Both, + Texture = textures.Get(user.CoverUrl), + FillMode = FillMode.Fill, + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + this.FadeInFromZero(400); } } } diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 47571b673d..3f6fce98f7 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -61,8 +61,6 @@ namespace osu.Game.Users FillFlowContainer infoContainer; - UserCoverBackground coverBackground; - AddInternal(content = new Container { RelativeSizeAxes = Axes.Both, @@ -77,13 +75,16 @@ namespace osu.Game.Users Children = new Drawable[] { - new DelayedLoadWrapper(coverBackground = new UserCoverBackground + new DelayedLoadUnloadWrapper(() => new UserCoverBackground { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, User = user, - }, 300) { RelativeSizeAxes = Axes.Both }, + }, 300, 5000) + { + RelativeSizeAxes = Axes.Both, + }, new Box { RelativeSizeAxes = Axes.Both, @@ -184,8 +185,6 @@ namespace osu.Game.Users } }); - coverBackground.OnLoadComplete += d => d.FadeInFromZero(400, Easing.Out); - if (user.IsSupporter) { infoContainer.Add(new SupporterIcon From a17d480f51b099ed88994bce1ebf5e613c0a6881 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 Jun 2019 20:41:02 +0900 Subject: [PATCH 1057/2854] Use "beatmap" as the model name --- osu.Game/Beatmaps/BeatmapManager.cs | 2 ++ osu.Game/Database/ArchiveModelManager.cs | 14 +++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index d90657bff5..3734c8d05c 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -327,6 +327,8 @@ namespace osu.Game.Beatmaps /// Results from the provided query. public IQueryable QueryBeatmaps(Expression> query) => beatmaps.Beatmaps.AsNoTracking().Where(query); + protected override string HumanisedModelName => "beatmap"; + protected override BeatmapSetInfo CreateModel(ArchiveReader reader) { // let's make sure there are actually .osu files to import. diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 20919f0899..1c8e722589 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -163,7 +163,7 @@ namespace osu.Game.Database imported.Add(model); current++; - notification.Text = $"Imported {current} of {paths.Length} {humanisedModelName}s"; + notification.Text = $"Imported {current} of {paths.Length} {HumanisedModelName}s"; notification.Progress = (float)current / paths.Length; } } @@ -186,7 +186,7 @@ namespace osu.Game.Database { notification.CompletionText = imported.Count == 1 ? $"Imported {imported.First()}!" - : $"Imported {current} {humanisedModelName}s!"; + : $"Imported {current} {HumanisedModelName}s!"; if (imported.Count > 0 && PresentImport != null) { @@ -344,7 +344,7 @@ namespace osu.Game.Database if (CanUndelete(existing, item)) { Undelete(existing); - LogForModel(item, $"Found existing {humanisedModelName} for {item} (ID {existing.ID}) – skipping import."); + LogForModel(item, $"Found existing {HumanisedModelName} for {item} (ID {existing.ID}) – skipping import."); handleEvent(() => ItemAdded?.Invoke(existing, true)); // existing item will be used; rollback new import and exit early. @@ -424,8 +424,8 @@ namespace osu.Game.Database var notification = new ProgressNotification { Progress = 0, - Text = $"Preparing to delete all {humanisedModelName}s...", - CompletionText = $"Deleted all {humanisedModelName}s!", + Text = $"Preparing to delete all {HumanisedModelName}s...", + CompletionText = $"Deleted all {HumanisedModelName}s!", State = ProgressNotificationState.Active, }; @@ -442,7 +442,7 @@ namespace osu.Game.Database // user requested abort return; - notification.Text = $"Deleting {humanisedModelName}s ({++i} of {items.Count})"; + notification.Text = $"Deleting {HumanisedModelName}s ({++i} of {items.Count})"; Delete(b); @@ -614,7 +614,7 @@ namespace osu.Game.Database private DbSet queryModel() => ContextFactory.Get().Set(); - private string humanisedModelName => $"{typeof(TModel).Name.Replace("Info", "").ToLower()}"; + protected virtual string HumanisedModelName => $"{typeof(TModel).Name.Replace("Info", "").ToLower()}"; /// /// Creates an from a valid storage path. From 03c98ff57b7f8dcb1da9e96ed476ba6196031ad5 Mon Sep 17 00:00:00 2001 From: Ludde <48018938+yousef157@users.noreply.github.com> Date: Wed, 12 Jun 2019 15:55:06 +0400 Subject: [PATCH 1058/2854] Update TestFlight --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 04f133fd56..9c63d31e15 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ If you are not interested in developing the game, you can consume our [binary re | ------------- | ------------- | - **Linux** users are recommended to self-compile until we have official deployment in place. -- **iOS** users can join the [TestFlight beta program](https://t.co/xQJmHkfC18) (note that due to high demand this is regularly full). +- **iOS** users can join the [TestFlight beta program](https://t.co/PasE1zrHhw) (new link added, please only install if you use it on a regular daily basis). - **Android** users can self-compile, and expect a public beta soon. If your platform is not listed above, there is still a chance you can manually build it by following the instructions below. From 9cd5519da3d2244b6a250d676a25fd951578197d Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 12 Jun 2019 18:35:34 +0530 Subject: [PATCH 1059/2854] Remove unused delegate, use model name in notifications, add more xmldoc - Applies a `class` constraint to the generic type in `IModelManager` - Add xmldoc --- osu.Game/Beatmaps/BeatmapManager.cs | 5 +---- osu.Game/Database/ArchiveModelManager.cs | 2 -- osu.Game/Database/DownloadableArchiveModelManager.cs | 4 ++-- osu.Game/Database/IModelDownloader.cs | 8 ++++++-- osu.Game/Database/IModelManager.cs | 5 +++++ 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index c2adf1ac5b..2cb7e8b901 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -55,8 +55,6 @@ namespace osu.Game.Beatmaps private readonly BeatmapStore beatmaps; - private readonly IAPIProvider api; - private readonly AudioManager audioManager; private readonly GameHost host; @@ -68,7 +66,6 @@ namespace osu.Game.Beatmaps : base(storage, contextFactory, api, new BeatmapStore(contextFactory), host) { this.rulesets = rulesets; - this.api = api; this.audioManager = audioManager; this.host = host; @@ -80,7 +77,7 @@ namespace osu.Game.Beatmaps updateQueue = new BeatmapUpdateQueue(api); } - + protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, object[] options) => new DownloadBeatmapSetRequest(set, (options?.FirstOrDefault() as bool?) ?? false); protected override Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 49e0330c21..434e5b9525 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -35,8 +35,6 @@ namespace osu.Game.Database where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : INamedFileInfo, new() { - public delegate void ItemAddedDelegate(TModel model, bool existing); - /// /// Set an endpoint for notifications to be posted to. /// diff --git a/osu.Game/Database/DownloadableArchiveModelManager.cs b/osu.Game/Database/DownloadableArchiveModelManager.cs index 519b22b912..4a21673d2b 100644 --- a/osu.Game/Database/DownloadableArchiveModelManager.cs +++ b/osu.Game/Database/DownloadableArchiveModelManager.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using Humanizer; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Online.API; @@ -112,8 +113,7 @@ namespace osu.Game.Database if (error is OperationCanceledException) return; notification.State = ProgressNotificationState.Cancelled; - // TODO: maybe implement a Name for every model that we can use in this message? - Logger.Error(error, "Download failed!"); + Logger.Error(error, $"{HumanisedModelName.Titleize()} download failed!"); currentDownloads.Remove(request); }; diff --git a/osu.Game/Database/IModelDownloader.cs b/osu.Game/Database/IModelDownloader.cs index 83427bdc17..42c64ba67b 100644 --- a/osu.Game/Database/IModelDownloader.cs +++ b/osu.Game/Database/IModelDownloader.cs @@ -6,6 +6,10 @@ using System; namespace osu.Game.Database { + /// + /// Represents a that can download new models from an external source. + /// + /// The model type. public interface IModelDownloader : IModelManager where TModel : class { @@ -23,7 +27,7 @@ namespace osu.Game.Database /// /// Downloads a . - /// This will post notifications tracking progress. + /// This may post notifications tracking progress. /// /// The to be downloaded. /// Whether downloading can happen. @@ -31,7 +35,7 @@ namespace osu.Game.Database /// /// Downloads a with optional parameters for the download request. - /// This will post notifications tracking progress. + /// This may post notifications tracking progress. /// /// The to be downloaded. /// Optional parameters to be used for creating the download request. diff --git a/osu.Game/Database/IModelManager.cs b/osu.Game/Database/IModelManager.cs index ee78df3db4..cb80ce49b2 100644 --- a/osu.Game/Database/IModelManager.cs +++ b/osu.Game/Database/IModelManager.cs @@ -5,7 +5,12 @@ using System; namespace osu.Game.Database { + /// + /// Represents a model manager that publishes events when s are added or removed. + /// + /// The model type. public interface IModelManager + where TModel : class { event Action ItemAdded; From efd9766fb301121f46776b1d4a64b7ce849b53c1 Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Wed, 12 Jun 2019 17:37:34 +0300 Subject: [PATCH 1060/2854] Make Header accessible by BeatmapSetOverlay --- osu.Game/Overlays/BeatmapSetOverlay.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 6bd2e1b72e..f0525aa843 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays public const float X_PADDING = 40; public const float RIGHT_WIDTH = 275; - private readonly Header header; + public readonly Header Header; private RulesetStore rulesets; @@ -60,7 +60,7 @@ namespace osu.Game.Overlays Direction = FillDirection.Vertical, Children = new Drawable[] { - header = new Header(), + Header = new Header(), info = new Info(), scores = new ScoresContainer(), }, @@ -68,10 +68,10 @@ namespace osu.Game.Overlays }, }; - header.BeatmapSet.BindTo(beatmapSet); + Header.BeatmapSet.BindTo(beatmapSet); info.BeatmapSet.BindTo(beatmapSet); - header.Picker.Beatmap.ValueChanged += b => + Header.Picker.Beatmap.ValueChanged += b => { info.Beatmap = b.NewValue; scores.Beatmap = b.NewValue; @@ -103,7 +103,7 @@ namespace osu.Game.Overlays req.Success += res => { beatmapSet.Value = res.ToBeatmapSet(rulesets); - header.Picker.Beatmap.Value = header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); + Header.Picker.Beatmap.Value = Header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); }; API.Queue(req); Show(); From d868280b2dab9a2acd16065ad9d4044bd987ece0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Jun 2019 23:51:01 +0900 Subject: [PATCH 1061/2854] Update outdated game resources in iOS project --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index 2c25498b89..5e151f916b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -104,7 +104,7 @@ - + From 50999c36771a620f2a76d8327e54feea4ffe001e Mon Sep 17 00:00:00 2001 From: Ludde <48018938+yousef157@users.noreply.github.com> Date: Wed, 12 Jun 2019 18:53:39 +0400 Subject: [PATCH 1062/2854] revert change in paranthesses --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c63d31e15..0df99f7d6b 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ If you are not interested in developing the game, you can consume our [binary re | ------------- | ------------- | - **Linux** users are recommended to self-compile until we have official deployment in place. -- **iOS** users can join the [TestFlight beta program](https://t.co/PasE1zrHhw) (new link added, please only install if you use it on a regular daily basis). +- **iOS** users can join the [TestFlight beta program](https://t.co/PasE1zrHhw) (note that due to high demand this is regularly full). - **Android** users can self-compile, and expect a public beta soon. If your platform is not listed above, there is still a chance you can manually build it by following the instructions below. From a069a3029e995ad200694abd1588c2053d59ffea Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Wed, 12 Jun 2019 18:07:57 +0300 Subject: [PATCH 1063/2854] Make DownloadButton accessible by DirectPanel --- osu.Game/Overlays/Direct/DirectGridPanel.cs | 4 +++- osu.Game/Overlays/Direct/DirectListPanel.cs | 4 +++- osu.Game/Overlays/Direct/DirectPanel.cs | 1 + osu.Game/Overlays/Direct/DownloadButton.cs | 7 ++++++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index 5756a4593d..9e7aa5372b 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -25,9 +25,11 @@ namespace osu.Game.Overlays.Direct private const float vertical_padding = 5; private FillFlowContainer bottomPanel, statusContainer; + private DownloadButton downloadButton; private PlayButton playButton; private Box progressBar; + public override DownloadButton DownloadButton => downloadButton; protected override PlayButton PlayButton => playButton; protected override Box PreviewBar => progressBar; @@ -155,7 +157,7 @@ namespace osu.Game.Overlays.Direct }, }, }, - new DownloadButton(SetInfo) + downloadButton = new DownloadButton(SetInfo) { Size = new Vector2(50, 30), Margin = new MarginPadding(horizontal_padding), diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index b731e95d54..076fed19bd 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -27,11 +27,13 @@ namespace osu.Game.Overlays.Direct private const float height = 70; private FillFlowContainer statusContainer; + private DownloadButton downloadButton; private PlayButton playButton; private Box progressBar; protected override bool FadePlayButton => false; + public override DownloadButton DownloadButton => downloadButton; protected override PlayButton PlayButton => playButton; protected override Box PreviewBar => progressBar; @@ -149,7 +151,7 @@ namespace osu.Game.Overlays.Direct Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, AutoSizeAxes = Axes.Both, - Child = new DownloadButton(SetInfo) + Child = downloadButton = new DownloadButton(SetInfo) { Size = new Vector2(height - vertical_padding * 3), Margin = new MarginPadding { Left = vertical_padding * 2, Right = vertical_padding }, diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index f413dc3771..c9fe4aaa05 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -34,6 +34,7 @@ namespace osu.Game.Overlays.Direct public PreviewTrack Preview => PlayButton.Preview; public Bindable PreviewPlaying => PlayButton.Playing; + public abstract DownloadButton DownloadButton { get; } protected abstract PlayButton PlayButton { get; } protected abstract Box PreviewBar { get; } diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs index 33e09e95aa..10e6f8a6e1 100644 --- a/osu.Game/Overlays/Direct/DownloadButton.cs +++ b/osu.Game/Overlays/Direct/DownloadButton.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; @@ -26,6 +27,8 @@ namespace osu.Game.Overlays.Direct private readonly OsuAnimatedButton button; + public readonly BindableBool Enabled = new BindableBool(true); + public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) : base(beatmapSet) { @@ -62,6 +65,8 @@ namespace osu.Game.Overlays.Direct } } }; + + Enabled.BindTo(button.Enabled); } protected override void LoadComplete() @@ -79,7 +84,7 @@ namespace osu.Game.Overlays.Direct if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) { - button.Enabled.Value = false; + Enabled.Value = false; button.TooltipText = "Unavailable"; return; } From c5c6f6b9e05ee517fc6483344d3125d675ba6ee4 Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Wed, 12 Jun 2019 18:36:47 +0300 Subject: [PATCH 1064/2854] Allow for not refetching in ShowBeatmapSet (will be used for tests) --- osu.Game/Overlays/BeatmapSetOverlay.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index f0525aa843..a44a7fa45f 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -118,10 +118,17 @@ namespace osu.Game.Overlays Show(); } - public void ShowBeatmapSet(BeatmapSetInfo set) + public void ShowBeatmapSet(BeatmapSetInfo set, bool refetch = true) { // Re-fetching is the correct way forward. - FetchAndShowBeatmapSet(set?.OnlineBeatmapSetID ?? 0); + if (refetch) + FetchAndShowBeatmapSet(set?.OnlineBeatmapSetID ?? 0); + else + { + beatmapSet.Value = set; + Show(); + } + scroll.ScrollTo(0); } } From 744f32ab35dc7a397a4ebb8954152c80bdb3b6f3 Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Wed, 12 Jun 2019 19:11:05 +0300 Subject: [PATCH 1065/2854] Fix little bug --- osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs b/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs index c111861206..b9c0a1af39 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs @@ -26,6 +26,7 @@ namespace osu.Game.Overlays.BeatmapSet beatmapSet = value; + removeLinks(); if (beatmapSet?.OnlineInfo.Availability != null) { Header?.ResizeHeightTo(450, 500); @@ -95,11 +96,6 @@ namespace osu.Game.Overlays.BeatmapSet base.Show(); } - public override void Hide() - { - link.RemoveAll(x => true); - - base.Hide(); - } + private void removeLinks() => link?.RemoveAll(x => true); } } From 7ba676ad31328fc63353d98036d471498f983afe Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 12 Jun 2019 21:56:36 +0530 Subject: [PATCH 1066/2854] Rename Info to Model --- osu.Game/Database/DownloadableArchiveModelManager.cs | 4 ++-- osu.Game/Online/API/ArchiveDownloadRequest.cs | 4 ++-- osu.Game/Overlays/Direct/DownloadTrackingComposite.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Database/DownloadableArchiveModelManager.cs b/osu.Game/Database/DownloadableArchiveModelManager.cs index 4a21673d2b..0735452ce3 100644 --- a/osu.Game/Database/DownloadableArchiveModelManager.cs +++ b/osu.Game/Database/DownloadableArchiveModelManager.cs @@ -79,7 +79,7 @@ namespace osu.Game.Database /// Whether the exists locally. public virtual bool IsAvailableLocally(TModel model) => modelStore.ConsumableItems.Any(m => m.Equals(model) && !m.DeletePending); - public ArchiveDownloadRequest GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Info.Equals(model)); + public ArchiveDownloadRequest GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Model.Equals(model)); private bool canDownload(TModel model) => GetExistingDownload(model) == null && api != null; @@ -87,7 +87,7 @@ namespace osu.Game.Database { DownloadNotification notification = new DownloadNotification { - Text = $"Downloading {request.Info}", + Text = $"Downloading {request.Model}", }; request.DownloadProgressed += progress => diff --git a/osu.Game/Online/API/ArchiveDownloadRequest.cs b/osu.Game/Online/API/ArchiveDownloadRequest.cs index 01f066694d..f1966aeb2b 100644 --- a/osu.Game/Online/API/ArchiveDownloadRequest.cs +++ b/osu.Game/Online/API/ArchiveDownloadRequest.cs @@ -8,7 +8,7 @@ namespace osu.Game.Online.API public abstract class ArchiveDownloadRequest : APIDownloadRequest where TModel : class { - public readonly TModel Info; + public readonly TModel Model; public float Progress; @@ -16,7 +16,7 @@ namespace osu.Game.Online.API protected ArchiveDownloadRequest(TModel model) { - Info = model; + Model = model; Progressed += (current, total) => DownloadProgressed?.Invoke(Progress = (float)current / total); } diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs index 9d0266c00e..494b18307e 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs @@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Direct beatmaps.DownloadBegan += download => { - if (download.Info.OnlineBeatmapSetID == BeatmapSet.Value?.OnlineBeatmapSetID) + if (download.Model.OnlineBeatmapSetID == BeatmapSet.Value?.OnlineBeatmapSetID) attachDownload(download); }; From 1a50544c9465ec75278420b8d0c22666612be8c1 Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Wed, 12 Jun 2019 20:42:52 +0300 Subject: [PATCH 1067/2854] Add tests for undownloadable / parts-removed beatmapsets --- .../Online/TestSceneBeatmapSetOverlay.cs | 306 +++++++++++++++++- .../Visual/Online/TestSceneDirectPanel.cs | 43 ++- .../BeatmapSet/BeatmapNotAvailable.cs | 1 + 3 files changed, 342 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 5910da7b88..5e7ccc7ed4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -39,6 +39,7 @@ namespace osu.Game.Tests.Visual.Online typeof(Info), typeof(PreviewButton), typeof(SuccessRate), + typeof(BeatmapNotAvailable), }; public TestSceneBeatmapSetOverlay() @@ -49,6 +50,7 @@ namespace osu.Game.Tests.Visual.Online [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { + var osu = rulesets.GetRuleset(0); var mania = rulesets.GetRuleset(3); var taiko = rulesets.GetRuleset(1); @@ -225,7 +227,7 @@ namespace osu.Game.Tests.Visual.Online }, }, }, - }); + }, false); }); AddStep(@"show second", () => @@ -396,9 +398,309 @@ namespace osu.Game.Tests.Visual.Online }, }, }, - }); + }, false); }); + AddStep(@"show parts-removed", () => + { + overlay.ShowBeatmapSet(new BeatmapSetInfo + { + Metadata = new BeatmapMetadata + { + Title = @"Sakura Kagetsu", + Artist = @"AKITO", + Source = @"DJMAX", + Tags = @"J-Trance Pasonia", + Author = new User + { + Username = @"Kharl", + Id = 452, + }, + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Availability = new BeatmapSetOnlineAvailability + { + DownloadDisabled = false, + ExternalLink = @"https://gist.githubusercontent.com/peppy/079dc3f77e316f9cd40077d411319a72/raw", + }, + Preview = @"https://b.ppy.sh/preview/119.mp3", + PlayCount = 626927, + FavouriteCount = 157, + Submitted = new DateTime(2007, 10, 24), + Ranked = new DateTime(2008, 4, 21), + Status = BeatmapSetOnlineStatus.Ranked, + BPM = 138, + Covers = new BeatmapSetOnlineCovers + { + Cover = @"https://assets.ppy.sh/beatmaps/119/covers/cover.jpg?1539847784", + }, + }, + Beatmaps = new List + { + new BeatmapInfo + { + StarDifficulty = 1.51, + Version = "Easy", + Ruleset = osu, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 4, + DrainRate = 2, + OverallDifficulty = 1, + ApproachRate = 1, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 126000, + CircleCount = 371, + SliderCount = 35, + PlayCount = 84498, + PassCount = 37482, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + new BeatmapInfo + { + StarDifficulty = 2.23, + Version = "Normal", + Ruleset = osu, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 5, + DrainRate = 4, + OverallDifficulty = 3, + ApproachRate = 3, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 126000, + CircleCount = 98, + SliderCount = 28, + PlayCount = 86427, + PassCount = 23273, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + new BeatmapInfo + { + StarDifficulty = 2.83, + Version = "Hard", + Ruleset = osu, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 6, + DrainRate = 6, + OverallDifficulty = 6, + ApproachRate = 6, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 126000, + CircleCount = 139, + SliderCount = 37, + PlayCount = 206523, + PassCount = 44366, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + new BeatmapInfo + { + StarDifficulty = 4.26, + Version = "Pasonia's Insane", + Ruleset = osu, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 6, + DrainRate = 6, + OverallDifficulty = 6, + ApproachRate = 6, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 126000, + CircleCount = 371, + SliderCount = 35, + PlayCount = 249479, + PassCount = 14042, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + }, + }, false); + }); + + AddStep(@"show undownloadable", () => + { + overlay.ShowBeatmapSet(new BeatmapSetInfo + { + Metadata = new BeatmapMetadata + { + Title = @"China Express", + Artist = @"Ryu*", + Source = @"REFLEC BEAT", + Tags = @"konami bemani lincle link iidx iidx18 iidx19 resort anthem plus la cataline mmzz", + Author = new User + { + Username = @"yeahyeahyeahhh", + Id = 58042, + }, + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Availability = new BeatmapSetOnlineAvailability + { + DownloadDisabled = true, + ExternalLink = @"https://gist.githubusercontent.com/peppy/99e6959772083cdfde8a/raw", + }, + Preview = @"https://b.ppy.sh/preview/53853.mp3", + PlayCount = 436213, + FavouriteCount = 105, + Submitted = new DateTime(2012, 7, 1), + Ranked = new DateTime(2012, 7, 18), + Status = BeatmapSetOnlineStatus.Ranked, + BPM = 171, + Covers = new BeatmapSetOnlineCovers + { + Cover = @"https://assets.ppy.sh/beatmaps/53853/covers/cover.jpg?1456498562", + }, + }, + Beatmaps = new List + { + new BeatmapInfo + { + StarDifficulty = 1.85, + Version = "Easy", + Ruleset = osu, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 3, + DrainRate = 2, + OverallDifficulty = 2, + ApproachRate = 3, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 95000, + CircleCount = 49, + SliderCount = 60, + PlayCount = 20308, + PassCount = 10233, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + new BeatmapInfo + { + StarDifficulty = 2.36, + Version = "Normal", + Ruleset = osu, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 3, + DrainRate = 2, + OverallDifficulty = 2, + ApproachRate = 5, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 96000, + CircleCount = 86, + SliderCount = 67, + PlayCount = 54015, + PassCount = 25603, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + new BeatmapInfo + { + StarDifficulty = 4.42, + Version = "Hyper", + Ruleset = osu, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 4, + DrainRate = 7, + OverallDifficulty = 6, + ApproachRate = 8, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 96000, + CircleCount = 215, + SliderCount = 120, + PlayCount = 111400, + PassCount = 12583, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + new BeatmapInfo + { + StarDifficulty = 5.05, + Version = "Another", + Ruleset = osu, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 4, + DrainRate = 7, + OverallDifficulty = 9, + ApproachRate = 9, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 96000, + CircleCount = 250, + SliderCount = 75, + PlayCount = 228253, + PassCount = 53037, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + }, + }, false); + }); + + AddAssert(@"is download button removed", () => overlay.Header.DownloadButtonsContainer.Count == 0); + AddStep(@"hide", overlay.Hide); AddStep(@"show without reload", overlay.Show); } diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs index 8b67892fbb..a2767611ac 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs @@ -6,7 +6,9 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; using osu.Game.Overlays.Direct; +using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osuTK; @@ -21,12 +23,34 @@ namespace osu.Game.Tests.Visual.Online typeof(IconPill) }; + private BeatmapSetInfo getBeatmapSet(RulesetInfo ruleset, bool downloadable) + { + var beatmap = CreateWorkingBeatmap(ruleset).BeatmapSetInfo; + beatmap.OnlineInfo.HasVideo = true; + beatmap.OnlineInfo.HasStoryboard = true; + + beatmap.OnlineInfo.Availability = new BeatmapSetOnlineAvailability + { + DownloadDisabled = !downloadable, + ExternalLink = "http://localhost", + }; + + return beatmap; + } + [BackgroundDependencyLoader] private void load() { - var beatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - beatmap.BeatmapSetInfo.OnlineInfo.HasVideo = true; - beatmap.BeatmapSetInfo.OnlineInfo.HasStoryboard = true; + var ruleset = new OsuRuleset().RulesetInfo; + + var normal = CreateWorkingBeatmap(ruleset).BeatmapSetInfo; + normal.OnlineInfo.HasVideo = true; + normal.OnlineInfo.HasStoryboard = true; + + var downloadable = getBeatmapSet(ruleset, true); + var undownloadable = getBeatmapSet(ruleset, false); + + DirectPanel undownloadableGridPanel, undownloadableListPanel; Child = new FillFlowContainer { @@ -37,10 +61,17 @@ namespace osu.Game.Tests.Visual.Online Spacing = new Vector2(0, 20), Children = new Drawable[] { - new DirectGridPanel(beatmap.BeatmapSetInfo), - new DirectListPanel(beatmap.BeatmapSetInfo) - } + new DirectGridPanel(normal), + new DirectGridPanel(downloadable), + undownloadableGridPanel = new DirectGridPanel(undownloadable), + new DirectListPanel(normal), + new DirectListPanel(downloadable), + undownloadableListPanel = new DirectListPanel(undownloadable), + }, }; + + AddAssert("is download button disabled on last grid panel", () => !undownloadableGridPanel.DownloadButton.Enabled.Value); + AddAssert("is download button disabled on last list panel", () => !undownloadableListPanel.DownloadButton.Enabled.Value); } } } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs b/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs index b9c0a1af39..31ed0d9396 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs @@ -27,6 +27,7 @@ namespace osu.Game.Overlays.BeatmapSet beatmapSet = value; removeLinks(); + if (beatmapSet?.OnlineInfo.Availability != null) { Header?.ResizeHeightTo(450, 500); From 3150b5cfb42d9c33960606698bf3ccdf824a11f9 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Wed, 12 Jun 2019 20:51:21 +0300 Subject: [PATCH 1068/2854] Naming adjustments --- osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs | 2 +- .../UserInterface/TestSceneToolbarRulesetSelector.cs | 2 +- osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs | 2 +- ...lbarRulesetButton.cs => ToolbarRulesetTabButton.cs} | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) rename osu.Game/Overlays/Toolbar/{ToolbarRulesetButton.cs => ToolbarRulesetTabButton.cs} (88%) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs index 0df6605cdd..f24589ed35 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Menus { typeof(ToolbarButton), typeof(ToolbarRulesetSelector), - typeof(ToolbarRulesetButton), + typeof(ToolbarRulesetTabButton), typeof(ToolbarNotificationButton), }; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs index 7d0491aa60..3e61da73a5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs @@ -16,7 +16,7 @@ namespace osu.Game.Tests.Visual.UserInterface public override IReadOnlyList RequiredTypes => new[] { typeof(ToolbarRulesetSelector), - typeof(ToolbarRulesetButton), + typeof(ToolbarRulesetTabButton), }; public TestSceneToolbarRulesetSelector() diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index 1f35d0f293..63cf2bba9c 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Toolbar protected override Dropdown CreateDropdown() => null; - protected override TabItem CreateTabItem(RulesetInfo value) => new ToolbarRulesetButton(value); + protected override TabItem CreateTabItem(RulesetInfo value) => new ToolbarRulesetTabButton(value); public ToolbarRulesetSelector() { diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs similarity index 88% rename from osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs rename to osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs index defe1da5bf..a5194ea752 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs @@ -10,16 +10,16 @@ using osu.Framework.Input.Events; namespace osu.Game.Overlays.Toolbar { - public class ToolbarRulesetButton : TabItem + public class ToolbarRulesetTabButton : TabItem { - private readonly DrawableRuleset ruleset; + private readonly RulesetButton ruleset; - public ToolbarRulesetButton(RulesetInfo value) + public ToolbarRulesetTabButton(RulesetInfo value) : base(value) { AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; - Child = ruleset = new DrawableRuleset + Child = ruleset = new RulesetButton { Active = false, }; @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Toolbar protected override void OnDeactivated() => ruleset.Active = false; - private class DrawableRuleset : ToolbarButton + private class RulesetButton : ToolbarButton { public bool Active { From 8d8615773ceecd629038441ad12421f6d9840bac Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Wed, 12 Jun 2019 20:54:19 +0300 Subject: [PATCH 1069/2854] Fix selection is being possible even in disabled state --- osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index 63cf2bba9c..3baf9a1e49 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -25,10 +25,10 @@ namespace osu.Game.Overlays.Toolbar private RulesetStore rulesets; private readonly Bindable globalRuleset = new Bindable(); - public override bool HandleNonPositionalInput => !Current.Disabled && base.HandleNonPositionalInput; - public override bool HandlePositionalInput => !Current.Disabled && base.HandlePositionalInput; + public override bool HandleNonPositionalInput => !globalRuleset.Disabled && base.HandleNonPositionalInput; + public override bool HandlePositionalInput => !globalRuleset.Disabled && base.HandlePositionalInput; - public override bool PropagatePositionalInputSubTree => !Current.Disabled && base.PropagatePositionalInputSubTree; + public override bool PropagatePositionalInputSubTree => !globalRuleset.Disabled && base.PropagatePositionalInputSubTree; private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300); From e5a6d920cda80b2946c7fcde80c3b8545e50f047 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Wed, 12 Jun 2019 23:23:01 +0300 Subject: [PATCH 1070/2854] Implement an abstract RulesetSelector class --- .../Toolbar/ToolbarRulesetSelector.cs | 42 +++---------- osu.Game/Rulesets/RulesetSelector.cs | 63 +++++++++++++++++++ 2 files changed, 72 insertions(+), 33 deletions(-) create mode 100644 osu.Game/Rulesets/RulesetSelector.cs diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index 3baf9a1e49..bd7ac13c9e 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; using osu.Framework.Caching; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -18,22 +17,18 @@ using System.Linq; namespace osu.Game.Overlays.Toolbar { - public class ToolbarRulesetSelector : TabControl + public class ToolbarRulesetSelector : RulesetSelector { private const float padding = 10; private readonly Drawable modeButtonLine; - private RulesetStore rulesets; - private readonly Bindable globalRuleset = new Bindable(); - public override bool HandleNonPositionalInput => !globalRuleset.Disabled && base.HandleNonPositionalInput; - public override bool HandlePositionalInput => !globalRuleset.Disabled && base.HandlePositionalInput; + public override bool HandleNonPositionalInput => !GlobalRuleset.Disabled && base.HandleNonPositionalInput; + public override bool HandlePositionalInput => !GlobalRuleset.Disabled && base.HandlePositionalInput; - public override bool PropagatePositionalInputSubTree => !globalRuleset.Disabled && base.PropagatePositionalInputSubTree; + public override bool PropagatePositionalInputSubTree => !GlobalRuleset.Disabled && base.PropagatePositionalInputSubTree; private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300); - protected override Dropdown CreateDropdown() => null; - protected override TabItem CreateTabItem(RulesetInfo value) => new ToolbarRulesetTabButton(value); public ToolbarRulesetSelector() @@ -66,6 +61,8 @@ namespace osu.Game.Overlays.Toolbar } } }); + + GlobalRuleset.DisabledChanged += disabledChanged; } protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer @@ -76,24 +73,6 @@ namespace osu.Game.Overlays.Toolbar Padding = new MarginPadding { Left = padding, Right = padding }, }; - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets, Bindable parentRuleset) - { - this.rulesets = rulesets; - globalRuleset.BindTo(parentRuleset); - - foreach (var r in rulesets.AvailableRulesets) - { - AddItem(r); - } - - globalRuleset.BindValueChanged(globalRulesetChanged); - globalRuleset.DisabledChanged += disabledChanged; - Current.BindValueChanged(localRulesetChanged); - } - - private void globalRulesetChanged(ValueChangedEvent e) => Current.Value = e.NewValue; - protected override bool OnKeyDown(KeyDownEvent e) { base.OnKeyDown(e); @@ -102,7 +81,7 @@ namespace osu.Game.Overlays.Toolbar { int requested = e.Key - Key.Number1; - RulesetInfo found = rulesets.AvailableRulesets.Skip(requested).FirstOrDefault(); + RulesetInfo found = AvaliableRulesets.AvailableRulesets.Skip(requested).FirstOrDefault(); if (found != null) Current.Value = found; return true; @@ -113,12 +92,9 @@ namespace osu.Game.Overlays.Toolbar private readonly Cached activeMode = new Cached(); - private void localRulesetChanged(ValueChangedEvent e) + protected override void OnLocalRulesetChanged(ValueChangedEvent e) { - if (!globalRuleset.Disabled) - { - globalRuleset.Value = e.NewValue; - } + base.OnLocalRulesetChanged(e); activeMode.Invalidate(); } diff --git a/osu.Game/Rulesets/RulesetSelector.cs b/osu.Game/Rulesets/RulesetSelector.cs new file mode 100644 index 0000000000..e646c2676b --- /dev/null +++ b/osu.Game/Rulesets/RulesetSelector.cs @@ -0,0 +1,63 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Bindables; +using osu.Framework.Allocation; + +namespace osu.Game.Rulesets +{ + public abstract class RulesetSelector : TabControl + { + protected RulesetStore AvaliableRulesets; + protected readonly Bindable GlobalRuleset = new Bindable(); + + protected override Dropdown CreateDropdown() => null; + + /// + /// Whether we want to change a global ruleset when local one is changed. + /// + protected virtual bool AllowGlobalRulesetChange => true; + + /// + /// Whether we want to change a local ruleset when global one is changed. + /// /// + protected virtual bool AllowLocalRulesetChange => true; + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets, Bindable parentRuleset) + { + AvaliableRulesets = rulesets; + GlobalRuleset.BindTo(parentRuleset); + + foreach (var r in rulesets.AvailableRulesets) + { + AddItem(r); + } + + GlobalRuleset.BindValueChanged(globalRulesetChanged); + Current.BindValueChanged(OnLocalRulesetChanged); + } + + private void globalRulesetChanged(ValueChangedEvent e) + { + if (AllowLocalRulesetChange) + { + OnGlobalRulesetChanged(e); + } + } + + protected virtual void OnGlobalRulesetChanged(ValueChangedEvent e) + { + Current.Value = e.NewValue; + } + + protected virtual void OnLocalRulesetChanged(ValueChangedEvent e) + { + if (!GlobalRuleset.Disabled && AllowGlobalRulesetChange) + { + GlobalRuleset.Value = e.NewValue; + } + } + } +} From 27fdda8b91c070838c9a8d7f0a4d0092b53f59df Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 12:21:49 +0900 Subject: [PATCH 1071/2854] Don't update hitobject results when rewinding --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index ec7e6dc303..fe9f6f9e51 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -243,6 +243,10 @@ namespace osu.Game.Rulesets.Objects.Drawables /// Whether a scoring result has occurred from this or any nested . protected bool UpdateResult(bool userTriggered) { + // It's possible for input to get into a bad state when rewinding gameplay, so results should not be processed + if (Time.Elapsed < 0) + return false; + judgementOccurred = false; if (AllJudged) From 44d2514f1a94b5aa49a36e9966d0d65bf8af9b72 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 14:41:10 +0900 Subject: [PATCH 1072/2854] Add test scene --- .../Gameplay/TestSceneGameplayRewinding.cs | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs new file mode 100644 index 0000000000..b3c98c9aaa --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -0,0 +1,101 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.MathUtils; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; +using osuTK; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneGameplayRewinding : PlayerTestScene + { + private RulesetExposingPlayer player => (RulesetExposingPlayer)Player; + + [Resolved] + private AudioManager audioManager { get; set; } + + public TestSceneGameplayRewinding() + : base(new OsuRuleset()) + { + } + + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) + => new ClockBackedTestWorkingBeatmap(beatmap, new FramedClock(new ManualClock { Rate = 1 }), audioManager); + + [Test] + public void TestNotJudgementsOnRewind() + { + addSeekStep(3000); + AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged)); + AddStep("clear results", () => player.AppliedResults.Clear()); + addSeekStep(0); + AddAssert("none judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged)); + AddAssert("no results triggered", () => player.AppliedResults.Count == 0); + } + + private void addSeekStep(double time) + { + AddStep($"seek to {time}", () => player.GameplayClockContainer.Seek(time)); + + // Allow 2 frames of lenience + AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, player.DrawableRuleset.FrameStableClock.CurrentTime, 32)); + } + + protected override Player CreatePlayer(Ruleset ruleset) + { + Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); + return new RulesetExposingPlayer(); + } + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) + { + var beatmap = new Beatmap + { + BeatmapInfo = { BaseDifficulty = { ApproachRate = 9 } }, + }; + + for (int i = 0; i < 15; i++) + { + beatmap.HitObjects.Add(new HitCircle + { + Position = new Vector2(256, 192), + StartTime = 1000 + 30 * i + }); + } + + return beatmap; + } + + private class RulesetExposingPlayer : Player + { + public readonly List AppliedResults = new List(); + + public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer; + + public new DrawableRuleset DrawableRuleset => base.DrawableRuleset; + + public RulesetExposingPlayer() + : base(false, false) + { + } + + [BackgroundDependencyLoader] + private void load() + { + ScoreProcessor.NewJudgement += r => AppliedResults.Add(r); + } + } + } +} From 4818187d8f6f01226eeac89e794b00e6a1a1e800 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 14:55:52 +0900 Subject: [PATCH 1073/2854] Reset result timeoffset to 0 when rewound --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index ec7e6dc303..02d29de367 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -167,6 +167,7 @@ namespace osu.Game.Rulesets.Objects.Drawables { OnRevertResult?.Invoke(this, Result); + Result.TimeOffset = 0; Result.Type = HitResult.None; State.Value = ArmedState.Idle; } From 1f2454188335222c8825365f7801eb62728892d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Jun 2019 15:43:03 +0900 Subject: [PATCH 1074/2854] Re-expose OsuButton --- osu.Game/Graphics/UserInterface/OsuButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index 494d4e4262..7a27f825f6 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -17,11 +17,11 @@ namespace osu.Game.Graphics.UserInterface /// /// A button with added default sound effects. /// - public abstract class OsuButton : Button + public class OsuButton : Button { private Box hover; - protected OsuButton() + public OsuButton() { Height = 40; From f12caaf9079126123b3eeb44f7d96de49c471a33 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 15:47:21 +0900 Subject: [PATCH 1075/2854] Increase leniency --- osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index b3c98c9aaa..0176301c03 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep($"seek to {time}", () => player.GameplayClockContainer.Seek(time)); - // Allow 2 frames of lenience - AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, player.DrawableRuleset.FrameStableClock.CurrentTime, 32)); + // Allow a few frames of lenience + AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, player.DrawableRuleset.FrameStableClock.CurrentTime, 100)); } protected override Player CreatePlayer(Ruleset ruleset) From a0503fcbe30bfa59dd9ea0d2f5820d62a3ca4fdf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Jun 2019 16:05:34 +0900 Subject: [PATCH 1076/2854] Reduce update rate of paths --- osu.Game.Tournament/Screens/Ladder/LadderScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs index 86b1884579..ffdc20c9e7 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -59,7 +59,7 @@ namespace osu.Game.Tournament.Screens.Ladder AddPairing(pairing); // todo: fix this - Scheduler.AddDelayed(() => layout.Invalidate(), 100, true); + Scheduler.AddDelayed(() => layout.Invalidate(), 1000, true); } protected virtual void AddPairing(MatchPairing pairing) From aef94ce9f15f17beca530d7425a9bd9ace22cfe9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 16:30:38 +0900 Subject: [PATCH 1077/2854] Make BeatmapMetrics non-IEnumerables --- .../Online/TestSceneBeatmapSetOverlay.cs | 60 +++++++++---------- .../SongSelect/TestSceneBeatmapDetailArea.cs | 18 +++--- .../SongSelect/TestSceneBeatmapDetails.cs | 18 +++--- .../Visual/SongSelect/TestSceneLeaderboard.cs | 6 +- osu.Game/Beatmaps/BeatmapMetrics.cs | 8 +-- 5 files changed, 55 insertions(+), 55 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 5910da7b88..f970e180d8 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -111,9 +111,9 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Ratings = Enumerable.Range(0, 11).ToArray(), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }, new BeatmapInfo @@ -138,9 +138,9 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Ratings = Enumerable.Range(0, 11).ToArray(), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }, new BeatmapInfo @@ -165,9 +165,9 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Ratings = Enumerable.Range(0, 11).ToArray(), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }, new BeatmapInfo @@ -192,9 +192,9 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Ratings = Enumerable.Range(0, 11).ToArray(), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }, new BeatmapInfo @@ -219,9 +219,9 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Ratings = Enumerable.Range(0, 11).ToArray(), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }, }, @@ -282,9 +282,9 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Ratings = Enumerable.Range(0, 11).ToArray(), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }, new BeatmapInfo @@ -309,9 +309,9 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Ratings = Enumerable.Range(0, 11).ToArray(), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }, new BeatmapInfo @@ -336,9 +336,9 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Ratings = Enumerable.Range(0, 11).ToArray(), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }, new BeatmapInfo @@ -363,9 +363,9 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Ratings = Enumerable.Range(0, 11).ToArray(), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }, new BeatmapInfo @@ -390,9 +390,9 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Ratings = Enumerable.Range(0, 11).ToArray(), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }, }, diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs index 8395ece457..f10237ec57 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs @@ -50,9 +50,9 @@ namespace osu.Game.Tests.Visual.SongSelect StarDifficulty = 5.3f, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Ratings = Enumerable.Range(0, 11).ToArray(), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, } } @@ -77,9 +77,9 @@ namespace osu.Game.Tests.Visual.SongSelect StarDifficulty = 5.3f, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Ratings = Enumerable.Range(0, 11).ToArray(), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, } }); @@ -104,7 +104,7 @@ namespace osu.Game.Tests.Visual.SongSelect StarDifficulty = 4.8f, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), + Ratings = Enumerable.Range(0, 11).ToArray(), }, } }); @@ -129,8 +129,8 @@ namespace osu.Game.Tests.Visual.SongSelect StarDifficulty = 2.91f, Metrics = new BeatmapMetrics { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, } }); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs index acbbd4e18b..26e4fc9e1c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs @@ -39,9 +39,9 @@ namespace osu.Game.Tests.Visual.SongSelect StarDifficulty = 5.3f, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Ratings = Enumerable.Range(0, 11).ToArray(), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }); @@ -62,9 +62,9 @@ namespace osu.Game.Tests.Visual.SongSelect StarDifficulty = 5.3f, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Ratings = Enumerable.Range(0, 11).ToArray(), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }); @@ -86,7 +86,7 @@ namespace osu.Game.Tests.Visual.SongSelect StarDifficulty = 4.8f, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), + Ratings = Enumerable.Range(0, 11).ToArray(), }, }); @@ -108,8 +108,8 @@ namespace osu.Game.Tests.Visual.SongSelect StarDifficulty = 2.91f, Metrics = new BeatmapMetrics { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs index 9365e2c5b1..ecfdb10b60 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs @@ -270,9 +270,9 @@ namespace osu.Game.Tests.Visual.SongSelect }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + Ratings = Enumerable.Range(0, 11).ToArray(), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }; } diff --git a/osu.Game/Beatmaps/BeatmapMetrics.cs b/osu.Game/Beatmaps/BeatmapMetrics.cs index 95413e6d2a..a31f7e26cf 100644 --- a/osu.Game/Beatmaps/BeatmapMetrics.cs +++ b/osu.Game/Beatmaps/BeatmapMetrics.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; +using System; using Newtonsoft.Json; namespace osu.Game.Beatmaps @@ -14,18 +14,18 @@ namespace osu.Game.Beatmaps /// /// Total vote counts of user ratings on a scale of 0..10 where 0 is unused (probably will be fixed at API?). /// - public IEnumerable Ratings { get; set; } + public int[] Ratings { get; set; } = Array.Empty(); /// /// Points of failure on a relative time scale (usually 0..100). /// [JsonProperty(@"fail")] - public IEnumerable Fails { get; set; } + public int[] Fails { get; set; } = Array.Empty(); /// /// Points of retry on a relative time scale (usually 0..100). /// [JsonProperty(@"exit")] - public IEnumerable Retries { get; set; } + public int[] Retries { get; set; } = Array.Empty(); } } From 4af16262e3b88bf53673e3d2c8f243b30f2c7c7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Jun 2019 16:11:15 +0900 Subject: [PATCH 1078/2854] Limit zoom range of bracket display --- ...{ScrollableContainer.cs => LadderDragContainer.cs} | 11 +++++++---- osu.Game.Tournament/Screens/Ladder/LadderScreen.cs | 7 ++----- 2 files changed, 9 insertions(+), 9 deletions(-) rename osu.Game.Tournament/Screens/Ladder/{ScrollableContainer.cs => LadderDragContainer.cs} (74%) diff --git a/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs b/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs similarity index 74% rename from osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs rename to osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs index 832e218b74..6687ca13f9 100644 --- a/osu.Game.Tournament/Screens/Ladder/ScrollableContainer.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs @@ -8,7 +8,7 @@ using osuTK; namespace osu.Game.Tournament.Screens.Ladder { - public class ScrollableContainer : Container + public class LadderDragContainer : Container { protected override bool OnDragStart(DragStartEvent e) => true; @@ -24,12 +24,15 @@ namespace osu.Game.Tournament.Screens.Ladder return true; } + private const float min_scale = 0.6f; + private const float max_scale = 1.4f; + protected override bool OnScroll(ScrollEvent e) { - var newScale = scale + e.ScrollDelta.Y / 15 * scale; - this.MoveTo(target = target - e.MousePosition * (newScale - scale), 1000, Easing.OutQuint); + var newScale = MathHelper.Clamp(scale + e.ScrollDelta.Y / 15 * scale, min_scale, max_scale); - this.ScaleTo(scale = newScale, 1000, Easing.OutQuint); + this.MoveTo(target = target - e.MousePosition * (newScale - scale), 2000, Easing.OutQuint); + this.ScaleTo(scale = newScale, 2000, Easing.OutQuint); return true; } diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs index ffdc20c9e7..e54bcffe2e 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tournament.Screens.Ladder private Container paths; private Container headings; - protected ScrollableContainer ScrollContent; + protected LadderDragContainer ScrollContent; [BackgroundDependencyLoader] private void load(OsuColour colours, Storage storage) @@ -42,7 +42,7 @@ namespace osu.Game.Tournament.Screens.Ladder RelativeSizeAxes = Axes.Both, Loop = true, }, - ScrollContent = new ScrollableContainer + ScrollContent = new LadderDragContainer { RelativeSizeAxes = Axes.Both, Children = new Drawable[] @@ -133,8 +133,5 @@ namespace osu.Game.Tournament.Screens.Ladder layout.Validate(); } - - // todo: remove after ppy/osu-framework#1980 is merged. - public override bool HandlePositionalInput => true; } } From ba475ef6c878bff4c5eb870aad62d669ac786f19 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Jun 2019 16:38:45 +0900 Subject: [PATCH 1079/2854] Remove unnecessary invalidation --- osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs b/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs index 6687ca13f9..3aa06185cb 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs @@ -36,11 +36,5 @@ namespace osu.Game.Tournament.Screens.Ladder return true; } - - protected override void Update() - { - base.Update(); - Invalidate(); - } } } From f240a157b247f0acbf2d9234c7ce60432a773dfc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 16:39:38 +0900 Subject: [PATCH 1080/2854] Deserialize API metrics --- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index edbcbed59f..bcbe060f82 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -57,6 +57,9 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"version")] private string version { get; set; } + [JsonProperty(@"failtimes")] + private BeatmapMetrics metrics { get; set; } + public BeatmapInfo ToBeatmap(RulesetStore rulesets) { var set = BeatmapSet?.ToBeatmapSet(rulesets); @@ -70,6 +73,7 @@ namespace osu.Game.Online.API.Requests.Responses Version = version, Status = Status, BeatmapSet = set, + Metrics = metrics, BaseDifficulty = new BeatmapDifficulty { DrainRate = drainRate, From 0a79b444d93036b1005f613f29753464e0af7cb3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 16:52:49 +0900 Subject: [PATCH 1081/2854] Move metrics to beatmap set --- .../Online/TestSceneBeatmapSetOverlay.cs | 12 ++--------- .../SongSelect/TestSceneBeatmapDetailArea.cs | 20 ++++++++++++------- .../SongSelect/TestSceneBeatmapDetails.cs | 19 +++++++++++------- .../Visual/SongSelect/TestSceneLeaderboard.cs | 1 - osu.Game/Beatmaps/BeatmapMetrics.cs | 7 +------ osu.Game/Beatmaps/BeatmapSetInfo.cs | 3 +++ osu.Game/Beatmaps/BeatmapSetMetrics.cs | 17 ++++++++++++++++ .../Requests/Responses/APIBeatmapMetrics.cs | 6 +++++- .../API/Requests/Responses/APIBeatmapSet.cs | 4 ++++ osu.Game/Overlays/BeatmapSet/Details.cs | 2 +- osu.Game/Screens/Select/BeatmapDetails.cs | 15 +++++++------- .../Screens/Select/Details/UserRatings.cs | 4 ++-- 12 files changed, 67 insertions(+), 43 deletions(-) create mode 100644 osu.Game/Beatmaps/BeatmapSetMetrics.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index f970e180d8..38388218c2 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -87,6 +87,7 @@ namespace osu.Game.Tests.Visual.Online Cover = @"https://assets.ppy.sh/beatmaps/415886/covers/cover.jpg?1465651778", }, }, + Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, Beatmaps = new List { new BeatmapInfo @@ -111,7 +112,6 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11).ToArray(), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, @@ -138,7 +138,6 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11).ToArray(), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, @@ -165,7 +164,6 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11).ToArray(), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, @@ -192,7 +190,6 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11).ToArray(), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, @@ -219,7 +216,6 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11).ToArray(), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, @@ -258,6 +254,7 @@ namespace osu.Game.Tests.Visual.Online Cover = @"https://assets.ppy.sh/beatmaps/625493/covers/cover.jpg?1499167472", }, }, + Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, Beatmaps = new List { new BeatmapInfo @@ -282,7 +279,6 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11).ToArray(), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, @@ -309,7 +305,6 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11).ToArray(), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, @@ -336,7 +331,6 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11).ToArray(), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, @@ -363,7 +357,6 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11).ToArray(), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, @@ -390,7 +383,6 @@ namespace osu.Game.Tests.Visual.Online }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11).ToArray(), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs index f10237ec57..7b97a27732 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs @@ -32,6 +32,10 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) { + BeatmapSetInfo = + { + Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() } + }, BeatmapInfo = { Version = "All Metrics", @@ -50,7 +54,6 @@ namespace osu.Game.Tests.Visual.SongSelect StarDifficulty = 5.3f, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11).ToArray(), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, @@ -60,6 +63,10 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) { + BeatmapSetInfo = + { + Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() } + }, BeatmapInfo = { Version = "All Metrics", @@ -77,7 +84,6 @@ namespace osu.Game.Tests.Visual.SongSelect StarDifficulty = 5.3f, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11).ToArray(), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, @@ -86,6 +92,10 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) { + BeatmapSetInfo = + { + Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() } + }, BeatmapInfo = { Version = "Only Ratings", @@ -101,11 +111,7 @@ namespace osu.Game.Tests.Visual.SongSelect OverallDifficulty = 6, ApproachRate = 6, }, - StarDifficulty = 4.8f, - Metrics = new BeatmapMetrics - { - Ratings = Enumerable.Range(0, 11).ToArray(), - }, + StarDifficulty = 4.8f } }); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs index 26e4fc9e1c..124a261521 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs @@ -23,6 +23,10 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("all metrics", () => details.Beatmap = new BeatmapInfo { + BeatmapSet = new BeatmapSetInfo + { + Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() } + }, Version = "All Metrics", Metadata = new BeatmapMetadata { @@ -39,7 +43,6 @@ namespace osu.Game.Tests.Visual.SongSelect StarDifficulty = 5.3f, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11).ToArray(), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, @@ -47,6 +50,10 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("all except source", () => details.Beatmap = new BeatmapInfo { + BeatmapSet = new BeatmapSetInfo + { + Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() } + }, Version = "All Metrics", Metadata = new BeatmapMetadata { @@ -62,7 +69,6 @@ namespace osu.Game.Tests.Visual.SongSelect StarDifficulty = 5.3f, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11).ToArray(), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, @@ -70,6 +76,10 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("ratings", () => details.Beatmap = new BeatmapInfo { + BeatmapSet = new BeatmapSetInfo + { + Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() } + }, Version = "Only Ratings", Metadata = new BeatmapMetadata { @@ -84,10 +94,6 @@ namespace osu.Game.Tests.Visual.SongSelect ApproachRate = 6, }, StarDifficulty = 4.8f, - Metrics = new BeatmapMetrics - { - Ratings = Enumerable.Range(0, 11).ToArray(), - }, }); AddStep("fails retries", () => details.Beatmap = new BeatmapInfo @@ -129,7 +135,6 @@ namespace osu.Game.Tests.Visual.SongSelect ApproachRate = 6.5f, }, StarDifficulty = 1.97f, - Metrics = new BeatmapMetrics(), }); AddStep("null beatmap", () => details.Beatmap = null); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs index ecfdb10b60..157e572606 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs @@ -270,7 +270,6 @@ namespace osu.Game.Tests.Visual.SongSelect }, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0, 11).ToArray(), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, diff --git a/osu.Game/Beatmaps/BeatmapMetrics.cs b/osu.Game/Beatmaps/BeatmapMetrics.cs index a31f7e26cf..b164aa6b30 100644 --- a/osu.Game/Beatmaps/BeatmapMetrics.cs +++ b/osu.Game/Beatmaps/BeatmapMetrics.cs @@ -7,15 +7,10 @@ using Newtonsoft.Json; namespace osu.Game.Beatmaps { /// - /// Beatmap metrics based on acculumated online data from community plays. + /// Beatmap metrics based on accumulated online data from community plays. /// public class BeatmapMetrics { - /// - /// Total vote counts of user ratings on a scale of 0..10 where 0 is unused (probably will be fixed at API?). - /// - public int[] Ratings { get; set; } = Array.Empty(); - /// /// Points of failure on a relative time scale (usually 0..100). /// diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 390236e053..c09119ab14 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -32,6 +32,9 @@ namespace osu.Game.Beatmaps [NotMapped] public BeatmapSetOnlineInfo OnlineInfo { get; set; } + [NotMapped] + public BeatmapSetMetrics Metrics { get; set; } + public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0; [NotMapped] diff --git a/osu.Game/Beatmaps/BeatmapSetMetrics.cs b/osu.Game/Beatmaps/BeatmapSetMetrics.cs new file mode 100644 index 0000000000..51c5de19a6 --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapSetMetrics.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using Newtonsoft.Json; + +namespace osu.Game.Beatmaps +{ + public class BeatmapSetMetrics + { + /// + /// Total vote counts of user ratings on a scale of 0..10 where 0 is unused (probably will be fixed at API?). + /// + [JsonProperty("ratings")] + public int[] Ratings { get; set; } = Array.Empty(); + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapMetrics.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapMetrics.cs index f049b3aed4..32a036b7c2 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapMetrics.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapMetrics.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using Newtonsoft.Json; using osu.Game.Beatmaps; @@ -19,9 +20,12 @@ namespace osu.Game.Online.API.Requests.Responses } } + public int[] Ratings { get; set; } = Array.Empty(); + //and other metrics in the beatmap set. + // Todo: What [JsonProperty(@"beatmapset")] - private BeatmapMetrics beatmapSet + private BeatmapSetMetrics beatmapSet { set => Ratings = value.Ratings; } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 1abb7c1a7d..05e647d107 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -54,6 +54,9 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"last_updated")] private DateTimeOffset lastUpdated { get; set; } + [JsonProperty(@"ratings")] + private int[] ratings { get; set; } + [JsonProperty(@"user_id")] private long creatorId { @@ -70,6 +73,7 @@ namespace osu.Game.Online.API.Requests.Responses OnlineBeatmapSetID = OnlineBeatmapSetID, Metadata = this, Status = Status, + Metrics = new BeatmapSetMetrics { Ratings = ratings }, OnlineInfo = new BeatmapSetOnlineInfo { Covers = covers, diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs index fad5c973b7..82f674ea86 100644 --- a/osu.Game/Overlays/BeatmapSet/Details.cs +++ b/osu.Game/Overlays/BeatmapSet/Details.cs @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.BeatmapSet private void updateDisplay() { - ratings.Metrics = Beatmap?.Metrics; + ratings.Metrics = Beatmap?.BeatmapSet?.Metrics; } public Details() diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 378b1b1dc6..83f9e5b063 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -18,6 +18,7 @@ using osu.Game.Screens.Select.Details; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Screens.Select { @@ -181,9 +182,10 @@ namespace osu.Game.Screens.Select tags.Text = Beatmap?.Metadata?.Tags; // metrics may have been previously fetched - if (Beatmap?.Metrics != null) + // Todo: + if (Beatmap?.BeatmapSet?.Metrics != null) { - updateMetrics(Beatmap.Metrics); + updateMetrics(new APIBeatmapMetrics { Ratings = Beatmap.BeatmapSet.Metrics.Ratings }); return; } @@ -210,22 +212,19 @@ namespace osu.Game.Screens.Select updateMetrics(); } - private void updateMetrics(BeatmapMetrics metrics = null) + private void updateMetrics(APIBeatmapMetrics metrics = null) { var hasRatings = metrics?.Ratings?.Any() ?? false; var hasRetriesFails = (metrics?.Retries?.Any() ?? false) && (metrics.Fails?.Any() ?? false); if (hasRatings) { - ratings.Metrics = metrics; + ratings.Metrics = new BeatmapSetMetrics { Ratings = metrics.Ratings }; ratingsContainer.FadeIn(transition_duration); } else { - ratings.Metrics = new BeatmapMetrics - { - Ratings = new int[10], - }; + ratings.Metrics = new BeatmapSetMetrics { Ratings = new int[10] }; ratingsContainer.FadeTo(0.25f, transition_duration); } diff --git a/osu.Game/Screens/Select/Details/UserRatings.cs b/osu.Game/Screens/Select/Details/UserRatings.cs index b17a3f79e9..c1e01e3572 100644 --- a/osu.Game/Screens/Select/Details/UserRatings.cs +++ b/osu.Game/Screens/Select/Details/UserRatings.cs @@ -20,9 +20,9 @@ namespace osu.Game.Screens.Select.Details private readonly Container graphContainer; private readonly BarGraph graph; - private BeatmapMetrics metrics; + private BeatmapSetMetrics metrics; - public BeatmapMetrics Metrics + public BeatmapSetMetrics Metrics { get => metrics; set From 583bb53f53c268b69c23ead81c67fded43569f96 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 16:57:19 +0900 Subject: [PATCH 1082/2854] Remove GetBeatmapDetailsRequest --- .../API/Requests/GetBeatmapDetailsRequest.cs | 20 ----------- .../Requests/Responses/APIBeatmapMetrics.cs | 33 ------------------- osu.Game/Screens/Select/BeatmapDetails.cs | 30 ++++++++++------- 3 files changed, 18 insertions(+), 65 deletions(-) delete mode 100644 osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs delete mode 100644 osu.Game/Online/API/Requests/Responses/APIBeatmapMetrics.cs diff --git a/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs deleted file mode 100644 index ed5efa2849..0000000000 --- a/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Beatmaps; -using osu.Game.Online.API.Requests.Responses; - -namespace osu.Game.Online.API.Requests -{ - public class GetBeatmapDetailsRequest : APIRequest - { - private readonly BeatmapInfo beatmap; - - public GetBeatmapDetailsRequest(BeatmapInfo beatmap) - { - this.beatmap = beatmap; - } - - protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}"; - } -} diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapMetrics.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapMetrics.cs deleted file mode 100644 index 32a036b7c2..0000000000 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapMetrics.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using Newtonsoft.Json; -using osu.Game.Beatmaps; - -namespace osu.Game.Online.API.Requests.Responses -{ - public class APIBeatmapMetrics : BeatmapMetrics - { - //the online API returns some metrics as a nested object. - [JsonProperty(@"failtimes")] - private BeatmapMetrics failTimes - { - set - { - Fails = value.Fails; - Retries = value.Retries; - } - } - - public int[] Ratings { get; set; } = Array.Empty(); - - //and other metrics in the beatmap set. - // Todo: What - [JsonProperty(@"beatmapset")] - private BeatmapSetMetrics beatmapSet - { - set => Ratings = value.Ratings; - } - } -} diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 83f9e5b063..1b4608b0fd 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -10,7 +10,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using System.Linq; using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osu.Framework.Threading; using osu.Framework.Graphics.Shapes; using osu.Framework.Extensions.Color4Extensions; @@ -18,7 +17,8 @@ using osu.Game.Screens.Select.Details; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.API.Requests; +using osu.Game.Rulesets; namespace osu.Game.Screens.Select { @@ -41,6 +41,9 @@ namespace osu.Game.Screens.Select private ScheduledDelegate pendingBeatmapSwitch; + [Resolved] + private RulesetStore rulesets { get; set; } + private BeatmapInfo beatmap; public BeatmapInfo Beatmap @@ -182,10 +185,9 @@ namespace osu.Game.Screens.Select tags.Text = Beatmap?.Metadata?.Tags; // metrics may have been previously fetched - // Todo: if (Beatmap?.BeatmapSet?.Metrics != null) { - updateMetrics(new APIBeatmapMetrics { Ratings = Beatmap.BeatmapSet.Metrics.Ratings }); + updateMetrics(Beatmap); return; } @@ -193,15 +195,19 @@ namespace osu.Game.Screens.Select if (Beatmap?.OnlineBeatmapID != null) { var requestedBeatmap = Beatmap; - var lookup = new GetBeatmapDetailsRequest(requestedBeatmap); + var lookup = new GetBeatmapRequest(requestedBeatmap); lookup.Success += res => { if (beatmap != requestedBeatmap) //the beatmap has been changed since we started the lookup. return; - requestedBeatmap.Metrics = res; - Schedule(() => updateMetrics(res)); + var b = res.ToBeatmap(rulesets); + + requestedBeatmap.BeatmapSet.Metrics = b.BeatmapSet.Metrics; + requestedBeatmap.Metrics = b.Metrics; + + Schedule(() => updateMetrics(requestedBeatmap)); }; lookup.Failure += e => Schedule(() => updateMetrics()); api.Queue(lookup); @@ -212,14 +218,14 @@ namespace osu.Game.Screens.Select updateMetrics(); } - private void updateMetrics(APIBeatmapMetrics metrics = null) + private void updateMetrics(BeatmapInfo beatmap = null) { - var hasRatings = metrics?.Ratings?.Any() ?? false; - var hasRetriesFails = (metrics?.Retries?.Any() ?? false) && (metrics.Fails?.Any() ?? false); + var hasRatings = beatmap?.BeatmapSet?.Metrics?.Ratings?.Any() ?? false; + var hasRetriesFails = (beatmap?.Metrics?.Retries?.Any() ?? false) && (beatmap.Metrics.Fails?.Any() ?? false); if (hasRatings) { - ratings.Metrics = new BeatmapSetMetrics { Ratings = metrics.Ratings }; + ratings.Metrics = beatmap.BeatmapSet.Metrics; ratingsContainer.FadeIn(transition_duration); } else @@ -230,7 +236,7 @@ namespace osu.Game.Screens.Select if (hasRetriesFails) { - failRetryGraph.Metrics = metrics; + failRetryGraph.Metrics = beatmap.Metrics; failRetryContainer.FadeIn(transition_duration); } else From dd7335079fd6ce03a06cc9f29d172dbc56541de1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 17:01:57 +0900 Subject: [PATCH 1083/2854] Fix beatmap set overlay not showing ratings --- osu.Game/Overlays/BeatmapSet/Details.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs index 82f674ea86..55e9500859 100644 --- a/osu.Game/Overlays/BeatmapSet/Details.cs +++ b/osu.Game/Overlays/BeatmapSet/Details.cs @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.BeatmapSet private void updateDisplay() { - ratings.Metrics = Beatmap?.BeatmapSet?.Metrics; + ratings.Metrics = BeatmapSet?.Metrics; } public Details() From eb0f0aefba7bdd76a66ed5c25c50c3f1d8622f48 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Jun 2019 17:04:57 +0900 Subject: [PATCH 1084/2854] Apply review changes --- .../Components/ControlPanel.cs | 4 ---- osu.Game.Tournament/Components/DateTextBox.cs | 3 ++- .../Components/MatchChatDisplay.cs | 4 ++-- osu.Game.Tournament/Components/SongBar.cs | 15 ++++++------- .../Components/TournamentBeatmapPanel.cs | 6 ++---- .../Ladder/Components/DrawableMatchTeam.cs | 5 ++--- .../Components/TournamentProgression.cs | 21 ++++++++++++++----- .../Screens/Ladder/LadderEditorScreen.cs | 10 ++++----- .../Screens/Ladder/LadderScreen.cs | 4 +++- osu.Game.Tournament/TournamentGameBase.cs | 4 ++-- 10 files changed, 42 insertions(+), 34 deletions(-) diff --git a/osu.Game.Tournament/Components/ControlPanel.cs b/osu.Game.Tournament/Components/ControlPanel.cs index 0d228fb3b4..a9bb1bf42f 100644 --- a/osu.Game.Tournament/Components/ControlPanel.cs +++ b/osu.Game.Tournament/Components/ControlPanel.cs @@ -39,7 +39,6 @@ namespace osu.Game.Tournament.Components { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Text = "Control Panel", Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 22) }, @@ -47,13 +46,10 @@ namespace osu.Game.Tournament.Components { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Width = 0.75f, - Position = new Vector2(0, 35f), - Direction = FillDirection.Vertical, Spacing = new Vector2(0, 5f), }, diff --git a/osu.Game.Tournament/Components/DateTextBox.cs b/osu.Game.Tournament/Components/DateTextBox.cs index f25c4b6e78..ee7e350970 100644 --- a/osu.Game.Tournament/Components/DateTextBox.cs +++ b/osu.Game.Tournament/Components/DateTextBox.cs @@ -15,7 +15,7 @@ namespace osu.Game.Tournament.Components get => bindable; set { - bindable = value; + bindable = value.GetBoundCopy(); bindable.BindValueChanged(dto => base.Bindable.Value = dto.NewValue.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"), true); } @@ -35,6 +35,7 @@ namespace osu.Game.Tournament.Components } catch { + // reset textbox content to its last valid state on a parse failure. bindable.TriggerChange(); } }; diff --git a/osu.Game.Tournament/Components/MatchChatDisplay.cs b/osu.Game.Tournament/Components/MatchChatDisplay.cs index dd567ed290..174b215732 100644 --- a/osu.Game.Tournament/Components/MatchChatDisplay.cs +++ b/osu.Game.Tournament/Components/MatchChatDisplay.cs @@ -15,8 +15,6 @@ namespace osu.Game.Tournament.Components { private readonly Bindable chatChannel = new Bindable(); - protected override ChatLine CreateMessage(Message message) => new MatchMessage(message); - private ChannelManager manager; [BackgroundDependencyLoader(true)] @@ -55,6 +53,8 @@ namespace osu.Game.Tournament.Components } } + protected override ChatLine CreateMessage(Message message) => new MatchMessage(message); + protected class MatchMessage : StandAloneMessage { public MatchMessage(Message message) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index a08333571b..c07882ddd0 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -228,21 +228,22 @@ namespace osu.Game.Tournament.Components s.Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 15); } - bool first = true; - - foreach (var t in tuples) + for (var i = 0; i < tuples.Length; i++) { - if (!first) + var tuple = tuples[i]; + + if (i > 0) + { AddText(" / ", s => { cp(s, OsuColour.Gray(0.33f)); s.Spacing = new Vector2(-2, 0); }); + } - AddText(new OsuSpriteText { Text = t.heading }, s => cp(s, OsuColour.Gray(0.33f))); + AddText(new OsuSpriteText { Text = tuple.heading }, s => cp(s, OsuColour.Gray(0.33f))); AddText(" ", s => cp(s, OsuColour.Gray(0.33f))); - AddText(new OsuSpriteText { Text = t.content }, s => cp(s, OsuColour.Gray(0.5f))); - first = false; + AddText(new OsuSpriteText { Text = tuple.content }, s => cp(s, OsuColour.Gray(0.5f))); } } } diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 818d25d559..cf826ee2c7 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -11,9 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.IO.Stores; using osu.Framework.Localisation; -using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; @@ -48,7 +46,7 @@ namespace osu.Game.Tournament.Components } [BackgroundDependencyLoader] - private void load(LadderInfo ladder, Storage storage) + private void load(LadderInfo ladder, TextureStore textures) { currentMatch.BindValueChanged(matchChanged); currentMatch.BindTo(ladder.CurrentMatch); @@ -135,7 +133,7 @@ namespace osu.Game.Tournament.Components if (!string.IsNullOrEmpty(mods)) AddInternal(new Sprite { - Texture = new LargeTextureStore(new TextureLoaderStore(new StorageBackedResourceStore(storage))).Get($"mods/{mods}"), + Texture = textures.Get($"mods/{mods}"), Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Margin = new MarginPadding(20), diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index 35b6dfb606..6d5ac74267 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -138,7 +138,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components }, true); } - //TODO: use OnClick instead once we have per-button clicks. protected override bool OnClick(ClickEvent e) { if (Team == null || editorInfo != null) return false; @@ -196,8 +195,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components return new MenuItem[] { new OsuMenuItem("Set as current", MenuItemType.Standard, setCurrent), - new OsuMenuItem("Join with", MenuItemType.Standard, () => ladderEditor.RequestJoin(pairing, false)), - new OsuMenuItem("Join with (loser)", MenuItemType.Standard, () => ladderEditor.RequestJoin(pairing, true)), + new OsuMenuItem("Join with", MenuItemType.Standard, () => ladderEditor.BeginJoin(pairing, false)), + new OsuMenuItem("Join with (loser)", MenuItemType.Standard, () => ladderEditor.BeginJoin(pairing, true)), new OsuMenuItem("Remove", MenuItemType.Destructive, () => ladderEditor.Remove(pairing)), }; } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs index 241e1d1d0b..0019dc8d79 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs @@ -5,15 +5,26 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { public class TournamentProgression { - public int Item1; - public int Item2; + public int SourceID; + public int TargetID; + + // migration + public int Item1 + { + set => SourceID = value; + } + + public int Item2 + { + set => TargetID = value; + } public bool Losers; - public TournamentProgression(int item1, int item2, bool losers = false) + public TournamentProgression(int sourceID, int targetID, bool losers = false) { - Item1 = item1; - Item2 = item2; + SourceID = sourceID; + TargetID = targetID; Losers = losers; } } diff --git a/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs index e605de9a7c..4a35f1ad30 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tournament.Screens.Ladder [BackgroundDependencyLoader] private void load() { - ((Container)InternalChild).Add(new LadderEditorSettings + Content.Add(new LadderEditorSettings { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -58,9 +58,9 @@ namespace osu.Game.Tournament.Screens.Ladder updateInfo(); } - public void RequestJoin(MatchPairing pairing, bool losers) + public void BeginJoin(MatchPairing pairing, bool losers) { - ScrollContent.Add(new JoinRequestHandler(PairingsContainer, pairing, losers, updateInfo)); + ScrollContent.Add(new JoinVisualiser(PairingsContainer, pairing, losers, updateInfo)); } public MenuItem[] ContextMenuItems @@ -91,7 +91,7 @@ namespace osu.Game.Tournament.Screens.Ladder PairingsContainer.FirstOrDefault(p => p.Pairing == pairing)?.Remove(); } - private class JoinRequestHandler : CompositeDrawable + private class JoinVisualiser : CompositeDrawable { private readonly Container pairingsContainer; public readonly MatchPairing Source; @@ -100,7 +100,7 @@ namespace osu.Game.Tournament.Screens.Ladder private ProgressionPath path; - public JoinRequestHandler(Container pairingsContainer, MatchPairing source, bool losers, Action complete) + public JoinVisualiser(Container pairingsContainer, MatchPairing source, bool losers, Action complete) { this.pairingsContainer = pairingsContainer; RelativeSizeAxes = Axes.Both; diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs index e54bcffe2e..f2d4ebbb71 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -24,6 +24,8 @@ namespace osu.Game.Tournament.Screens.Ladder protected LadderDragContainer ScrollContent; + protected Container Content; + [BackgroundDependencyLoader] private void load(OsuColour colours, Storage storage) { @@ -32,7 +34,7 @@ namespace osu.Game.Tournament.Screens.Ladder RelativeSizeAxes = Axes.Both; - InternalChild = new Container + InternalChild = Content = new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 4f54da2723..739fabca9d 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -110,8 +110,8 @@ namespace osu.Game.Tournament // assign progressions foreach (var pair in ladder.Progressions) { - var src = ladder.Pairings.FirstOrDefault(p => p.ID == pair.Item1); - var dest = ladder.Pairings.FirstOrDefault(p => p.ID == pair.Item2); + var src = ladder.Pairings.FirstOrDefault(p => p.ID == pair.SourceID); + var dest = ladder.Pairings.FirstOrDefault(p => p.ID == pair.TargetID); if (src == null) throw new InvalidOperationException(); From f54f6e552ba2c95bacf3cc83be5dbf4a083ce26b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 17:10:09 +0900 Subject: [PATCH 1085/2854] Fix beatmap details potentially using the incorrect metrics --- .../SongSelect/TestSceneBeatmapDetails.cs | 19 ++++++++++++ osu.Game/Screens/Select/BeatmapDetails.cs | 29 ++++++++++++------- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs index 124a261521..f4f3c2b8d1 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs @@ -138,6 +138,25 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddStep("null beatmap", () => details.Beatmap = null); + + AddStep("online ratings/retries/fails", () => details.Beatmap = new BeatmapInfo + { + OnlineBeatmapID = 162, + Version = "online ratings/retries/fails", + Metadata = new BeatmapMetadata + { + Source = "osu!lazer", + Tags = "this beatmap has online ratings/retries/fails", + }, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 7, + DrainRate = 1, + OverallDifficulty = 5.7f, + ApproachRate = 3.5f, + }, + StarDifficulty = 5.3f + }); } } } diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 1b4608b0fd..de78fe6572 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -185,9 +185,9 @@ namespace osu.Game.Screens.Select tags.Text = Beatmap?.Metadata?.Tags; // metrics may have been previously fetched - if (Beatmap?.BeatmapSet?.Metrics != null) + if (Beatmap?.BeatmapSet?.Metrics != null && Beatmap?.Metrics != null) { - updateMetrics(Beatmap); + updateMetrics(Beatmap.BeatmapSet.Metrics, Beatmap.Metrics); return; } @@ -195,6 +195,7 @@ namespace osu.Game.Screens.Select if (Beatmap?.OnlineBeatmapID != null) { var requestedBeatmap = Beatmap; + var lookup = new GetBeatmapRequest(requestedBeatmap); lookup.Success += res => { @@ -204,28 +205,34 @@ namespace osu.Game.Screens.Select var b = res.ToBeatmap(rulesets); - requestedBeatmap.BeatmapSet.Metrics = b.BeatmapSet.Metrics; + if (requestedBeatmap.BeatmapSet == null) + requestedBeatmap.BeatmapSet = b.BeatmapSet; + else + requestedBeatmap.BeatmapSet.Metrics = b.BeatmapSet.Metrics; + requestedBeatmap.Metrics = b.Metrics; - Schedule(() => updateMetrics(requestedBeatmap)); + Schedule(() => updateMetrics(b.BeatmapSet.Metrics, b.Metrics)); }; - lookup.Failure += e => Schedule(() => updateMetrics()); + + lookup.Failure += e => Schedule(() => updateMetrics(Beatmap?.BeatmapSet?.Metrics, Beatmap?.Metrics)); + api.Queue(lookup); loading.Show(); return; } - updateMetrics(); + updateMetrics(Beatmap?.BeatmapSet?.Metrics, Beatmap?.Metrics); } - private void updateMetrics(BeatmapInfo beatmap = null) + private void updateMetrics(BeatmapSetMetrics setMetrics, BeatmapMetrics beatmapMetrics) { - var hasRatings = beatmap?.BeatmapSet?.Metrics?.Ratings?.Any() ?? false; - var hasRetriesFails = (beatmap?.Metrics?.Retries?.Any() ?? false) && (beatmap.Metrics.Fails?.Any() ?? false); + var hasRatings = setMetrics?.Ratings?.Any() ?? false; + var hasRetriesFails = (beatmapMetrics?.Retries?.Any() ?? false) && (beatmapMetrics.Fails?.Any() ?? false); if (hasRatings) { - ratings.Metrics = beatmap.BeatmapSet.Metrics; + ratings.Metrics = setMetrics; ratingsContainer.FadeIn(transition_duration); } else @@ -236,7 +243,7 @@ namespace osu.Game.Screens.Select if (hasRetriesFails) { - failRetryGraph.Metrics = beatmap.Metrics; + failRetryGraph.Metrics = beatmapMetrics; failRetryContainer.FadeIn(transition_duration); } else From 89c68c78d1b74a65e4295a0a54fdb34c9bc079b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Jun 2019 18:06:24 +0900 Subject: [PATCH 1086/2854] Reduce size of paths --- .../Screens/Ladder/Components/ProgressionPath.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs index ddf41b0117..5468844f66 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs @@ -25,6 +25,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components protected override void LoadComplete() { base.LoadComplete(); + Vector2 getCenteredVector(Vector2 top, Vector2 bottom) => new Vector2(top.X, top.Y + (bottom.Y - top.Y) / 2); var q1 = Source.ScreenSpaceDrawQuad; @@ -56,7 +57,15 @@ namespace osu.Game.Tournament.Screens.Ladder.Components var p3 = new Vector2(p2.X, c2.Y); var p4 = new Vector2(c2.X, p3.Y); - Vertices = new[] { p1, p2, p3, p4 }.Select(ToLocalSpace).ToList(); + var points = new[] { p1, p2, p3, p4 }; + + float minX = points.Min(p => p.X); + float minY = points.Min(p => p.Y); + + var topLeft = new Vector2(minX, minY); + + Position = Parent.ToLocalSpace(topLeft); + Vertices = points.Select(p => Parent.ToLocalSpace(p) - Parent.ToLocalSpace(topLeft)).ToList(); } } } From 72f729cf3b99dc2c3ab1d680d200b35b104db753 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 18:14:57 +0900 Subject: [PATCH 1087/2854] Refactor beatmap set overlay test scene --- .../Online/TestSceneBeatmapSetOverlay.cs | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 38388218c2..fb2d4efc68 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -41,6 +41,9 @@ namespace osu.Game.Tests.Visual.Online typeof(SuccessRate), }; + private RulesetInfo maniaRuleset; + private RulesetInfo taikoRuleset; + public TestSceneBeatmapSetOverlay() { Add(overlay = new BeatmapSetOverlay()); @@ -49,13 +52,25 @@ namespace osu.Game.Tests.Visual.Online [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { - var mania = rulesets.GetRuleset(3); - var taiko = rulesets.GetRuleset(1); + maniaRuleset = rulesets.GetRuleset(3); + taikoRuleset = rulesets.GetRuleset(1); + } + [Test] + public void TestLoading() + { AddStep(@"show loading", () => overlay.ShowBeatmapSet(null)); + } + [Test] + public void TestOnline() + { AddStep(@"show online", () => overlay.FetchAndShowBeatmapSet(55)); + } + [Test] + public void TestLocalBeatmaps() + { AddStep(@"show first", () => { overlay.ShowBeatmapSet(new BeatmapSetInfo @@ -94,7 +109,7 @@ namespace osu.Game.Tests.Visual.Online { StarDifficulty = 1.36, Version = @"BASIC", - Ruleset = mania, + Ruleset = maniaRuleset, BaseDifficulty = new BeatmapDifficulty { CircleSize = 4, @@ -120,7 +135,7 @@ namespace osu.Game.Tests.Visual.Online { StarDifficulty = 2.22, Version = @"NOVICE", - Ruleset = mania, + Ruleset = maniaRuleset, BaseDifficulty = new BeatmapDifficulty { CircleSize = 4, @@ -146,7 +161,7 @@ namespace osu.Game.Tests.Visual.Online { StarDifficulty = 3.49, Version = @"ADVANCED", - Ruleset = mania, + Ruleset = maniaRuleset, BaseDifficulty = new BeatmapDifficulty { CircleSize = 4, @@ -172,7 +187,7 @@ namespace osu.Game.Tests.Visual.Online { StarDifficulty = 4.24, Version = @"EXHAUST", - Ruleset = mania, + Ruleset = maniaRuleset, BaseDifficulty = new BeatmapDifficulty { CircleSize = 4, @@ -198,7 +213,7 @@ namespace osu.Game.Tests.Visual.Online { StarDifficulty = 5.26, Version = @"GRAVITY", - Ruleset = mania, + Ruleset = maniaRuleset, BaseDifficulty = new BeatmapDifficulty { CircleSize = 4, @@ -261,7 +276,7 @@ namespace osu.Game.Tests.Visual.Online { StarDifficulty = 1.40, Version = @"yzrin's Kantan", - Ruleset = taiko, + Ruleset = taikoRuleset, BaseDifficulty = new BeatmapDifficulty { CircleSize = 2, @@ -287,7 +302,7 @@ namespace osu.Game.Tests.Visual.Online { StarDifficulty = 2.23, Version = @"Futsuu", - Ruleset = taiko, + Ruleset = taikoRuleset, BaseDifficulty = new BeatmapDifficulty { CircleSize = 2, @@ -313,7 +328,7 @@ namespace osu.Game.Tests.Visual.Online { StarDifficulty = 3.19, Version = @"Muzukashii", - Ruleset = taiko, + Ruleset = taikoRuleset, BaseDifficulty = new BeatmapDifficulty { CircleSize = 2, @@ -339,7 +354,7 @@ namespace osu.Game.Tests.Visual.Online { StarDifficulty = 3.97, Version = @"Charlotte's Oni", - Ruleset = taiko, + Ruleset = taikoRuleset, BaseDifficulty = new BeatmapDifficulty { CircleSize = 5, @@ -365,7 +380,7 @@ namespace osu.Game.Tests.Visual.Online { StarDifficulty = 5.08, Version = @"Labyrinth Oni", - Ruleset = taiko, + Ruleset = taikoRuleset, BaseDifficulty = new BeatmapDifficulty { CircleSize = 5, @@ -390,8 +405,17 @@ namespace osu.Game.Tests.Visual.Online }, }); }); + } + [Test] + public void TestHide() + { AddStep(@"hide", overlay.Hide); + } + + [Test] + public void TestShowWithNoReload() + { AddStep(@"show without reload", overlay.Show); } } From 7af2d650cd4eb8d3533c645735dda07821e87d87 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 18:31:12 +0900 Subject: [PATCH 1088/2854] Fix potential nullref --- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index 8ed52dade5..6a583baf38 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.BeatmapSet private void updateDisplay() { - bpm.Value = BeatmapSet?.OnlineInfo.BPM.ToString(@"0.##") ?? "-"; + bpm.Value = BeatmapSet?.OnlineInfo?.BPM.ToString(@"0.##") ?? "-"; if (beatmap == null) { From f2b5f274cff2d88869be2f4720eb757914eb6010 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 18:31:39 +0900 Subject: [PATCH 1089/2854] Add details test scene + fix metrics not getting updated correctly --- .../TestSceneBeatmapSetOverlayDetails.cs | 68 +++++++++++++++++++ osu.Game/Overlays/BeatmapSet/Details.cs | 9 +-- 2 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs new file mode 100644 index 0000000000..f7009f9df3 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs @@ -0,0 +1,68 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Overlays.BeatmapSet; +using osu.Game.Screens.Select.Details; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneBeatmapSetOverlayDetails : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(Details) + }; + + private RatingsExposingDetails details; + + [SetUp] + public void Setup() => Schedule(() => + { + Child = details = new RatingsExposingDetails + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }; + }); + + [Test] + public void TestMetrics() + { + var firstSet = createSet(); + var secondSet = createSet(); + + AddStep("set first set", () => details.BeatmapSet = firstSet); + AddAssert("ratings set", () => details.Ratings.Metrics == firstSet.Metrics); + + AddStep("set second set", () => details.BeatmapSet = secondSet); + AddAssert("ratings set", () => details.Ratings.Metrics == secondSet.Metrics); + + BeatmapSetInfo createSet() => new BeatmapSetInfo + { + Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, + Beatmaps = new List + { + new BeatmapInfo + { + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), + } + } + } + }; + } + + private class RatingsExposingDetails : Details + { + public new UserRatings Ratings => base.Ratings; + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs index 55e9500859..d76f6a43db 100644 --- a/osu.Game/Overlays/BeatmapSet/Details.cs +++ b/osu.Game/Overlays/BeatmapSet/Details.cs @@ -16,10 +16,11 @@ namespace osu.Game.Overlays.BeatmapSet { public class Details : FillFlowContainer { + protected readonly UserRatings Ratings; + private readonly PreviewButton preview; private readonly BasicStats basic; private readonly AdvancedStats advanced; - private readonly UserRatings ratings; private BeatmapSetInfo beatmapSet; @@ -33,6 +34,7 @@ namespace osu.Game.Overlays.BeatmapSet beatmapSet = value; basic.BeatmapSet = preview.BeatmapSet = BeatmapSet; + updateDisplay(); } } @@ -46,13 +48,12 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmap) return; basic.Beatmap = advanced.Beatmap = beatmap = value; - updateDisplay(); } } private void updateDisplay() { - ratings.Metrics = BeatmapSet?.Metrics; + Ratings.Metrics = BeatmapSet?.Metrics; } public Details() @@ -87,7 +88,7 @@ namespace osu.Game.Overlays.BeatmapSet }, new DetailBox { - Child = ratings = new UserRatings + Child = Ratings = new UserRatings { RelativeSizeAxes = Axes.X, Height = 95, From f9f32311b7cc0d9162c7e0690a65d77b6395f469 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 18:43:44 +0900 Subject: [PATCH 1090/2854] Add some randomness --- .../Visual/Online/TestSceneBeatmapSetOverlayDetails.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs index f7009f9df3..2a45e68c0a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Overlays.BeatmapSet; using osu.Game.Screens.Select.Details; @@ -45,15 +46,15 @@ namespace osu.Game.Tests.Visual.Online BeatmapSetInfo createSet() => new BeatmapSetInfo { - Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, + Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray() }, Beatmaps = new List { new BeatmapInfo { Metrics = new BeatmapMetrics { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), + Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(), } } } From 6b615d763aa5c6cc285b159e3f8e110ce3e34902 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 18:43:51 +0900 Subject: [PATCH 1091/2854] Fix potential nullref --- osu.Game/Overlays/BeatmapSet/SuccessRate.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index c89bddca63..c0e749b117 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -37,8 +37,8 @@ namespace osu.Game.Overlays.BeatmapSet private void updateDisplay() { - int passCount = beatmap?.OnlineInfo.PassCount ?? 0; - int playCount = beatmap?.OnlineInfo.PlayCount ?? 0; + int passCount = beatmap?.OnlineInfo?.PassCount ?? 0; + int playCount = beatmap?.OnlineInfo?.PlayCount ?? 0; var rate = playCount != 0 ? (float)passCount / playCount : 0; successPercent.Text = rate.ToString("P0"); From 39f9deea9640a8ee58d8cbf5a461d89963b41a73 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 18:44:00 +0900 Subject: [PATCH 1092/2854] Add success rate test scene --- .../TestSceneBeatmapSetOverlaySuccessRate.cs | 82 +++++++++++++++++++ osu.Game/Overlays/BeatmapSet/SuccessRate.cs | 9 +- 2 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs new file mode 100644 index 0000000000..05f5c117e4 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs @@ -0,0 +1,82 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Overlays.BeatmapSet; +using osu.Game.Screens.Select.Details; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneBeatmapSetOverlaySuccessRate : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(Details) + }; + + private GraphExposingSuccessRate successRate; + + [SetUp] + public void Setup() => Schedule(() => + { + Child = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(275, 220), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Gray, + }, + successRate = new GraphExposingSuccessRate + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(275, 220), + Padding = new MarginPadding(20) + } + } + }; + }); + + [Test] + public void TestMetrics() + { + var firstBeatmap = createBeatmap(); + var secondBeatmap = createBeatmap(); + + AddStep("set first set", () => successRate.Beatmap = firstBeatmap); + AddAssert("ratings set", () => successRate.Graph.Metrics == firstBeatmap.Metrics); + + AddStep("set second set", () => successRate.Beatmap = secondBeatmap); + AddAssert("ratings set", () => successRate.Graph.Metrics == secondBeatmap.Metrics); + + BeatmapInfo createBeatmap() => new BeatmapInfo + { + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(), + } + }; + } + + private class GraphExposingSuccessRate : SuccessRate + { + public new FailRetryGraph Graph => base.Graph; + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index c0e749b117..0258a0301a 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -14,11 +14,12 @@ namespace osu.Game.Overlays.BeatmapSet { public class SuccessRate : Container { + protected readonly FailRetryGraph Graph; + private readonly FillFlowContainer header; private readonly OsuSpriteText successRateLabel, successPercent, graphLabel; private readonly Bar successRate; private readonly Container percentContainer; - private readonly FailRetryGraph graph; private BeatmapInfo beatmap; @@ -45,7 +46,7 @@ namespace osu.Game.Overlays.BeatmapSet successRate.Length = rate; percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); - graph.Metrics = beatmap?.Metrics; + Graph.Metrics = beatmap?.Metrics; } public SuccessRate() @@ -94,7 +95,7 @@ namespace osu.Game.Overlays.BeatmapSet }, }, }, - graph = new FailRetryGraph + Graph = new FailRetryGraph { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, @@ -117,7 +118,7 @@ namespace osu.Game.Overlays.BeatmapSet { base.UpdateAfterChildren(); - graph.Padding = new MarginPadding { Top = header.DrawHeight }; + Graph.Padding = new MarginPadding { Top = header.DrawHeight }; } } } From 2ad4045b2eb7eadcc28bc1a1bb5507916147182e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 18:51:41 +0900 Subject: [PATCH 1093/2854] Refactor beatmap details testcase --- .../SongSelect/TestSceneBeatmapDetails.cs | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs index f4f3c2b8d1..64bad66919 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs @@ -1,26 +1,33 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.ComponentModel; +using System.Collections.Generic; using System.Linq; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Screens.Select; namespace osu.Game.Tests.Visual.SongSelect { - [Description("PlaySongSelect beatmap details")] + [System.ComponentModel.Description("PlaySongSelect beatmap details")] public class TestSceneBeatmapDetails : OsuTestScene { - public TestSceneBeatmapDetails() + private BeatmapDetails details; + + [SetUp] + public void Setup() => Schedule(() => { - BeatmapDetails details; - Add(details = new BeatmapDetails + Child = details = new BeatmapDetails { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(150), - }); + }; + }); + [Test] + public void TestAllMetrics() + { AddStep("all metrics", () => details.Beatmap = new BeatmapInfo { BeatmapSet = new BeatmapSetInfo @@ -47,7 +54,11 @@ namespace osu.Game.Tests.Visual.SongSelect Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }); + } + [Test] + public void TestAllMetricsExceptSource() + { AddStep("all except source", () => details.Beatmap = new BeatmapInfo { BeatmapSet = new BeatmapSetInfo @@ -73,7 +84,11 @@ namespace osu.Game.Tests.Visual.SongSelect Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }); + } + [Test] + public void TestOnlyRatings() + { AddStep("ratings", () => details.Beatmap = new BeatmapInfo { BeatmapSet = new BeatmapSetInfo @@ -95,7 +110,11 @@ namespace osu.Game.Tests.Visual.SongSelect }, StarDifficulty = 4.8f, }); + } + [Test] + public void TestOnlyFailsAndRetries() + { AddStep("fails retries", () => details.Beatmap = new BeatmapInfo { Version = "Only Retries and Fails", @@ -118,7 +137,11 @@ namespace osu.Game.Tests.Visual.SongSelect Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }); + } + [Test] + public void TestNoMetrics() + { AddStep("no metrics", () => details.Beatmap = new BeatmapInfo { Version = "No Metrics", @@ -136,26 +159,20 @@ namespace osu.Game.Tests.Visual.SongSelect }, StarDifficulty = 1.97f, }); + } + [Test] + public void TestNullBeatmap() + { AddStep("null beatmap", () => details.Beatmap = null); + } + [Test] + public void TestOnlineMetrics() + { AddStep("online ratings/retries/fails", () => details.Beatmap = new BeatmapInfo { OnlineBeatmapID = 162, - Version = "online ratings/retries/fails", - Metadata = new BeatmapMetadata - { - Source = "osu!lazer", - Tags = "this beatmap has online ratings/retries/fails", - }, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 7, - DrainRate = 1, - OverallDifficulty = 5.7f, - ApproachRate = 3.5f, - }, - StarDifficulty = 5.3f }); } } From d744c900c240f0eb174b3aa604548109a06ba84e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Jun 2019 19:04:59 +0900 Subject: [PATCH 1094/2854] Fix incorrect unbind logic --- osu.Game/Overlays/Settings/SettingsItem.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 4b992d6179..ae840c8c00 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -63,7 +63,9 @@ namespace osu.Game.Overlays.Settings set { - controlWithCurrent?.Current.UnbindBindings(); + if (bindable != null) + controlWithCurrent?.Current.UnbindFrom(bindable); + bindable = value; controlWithCurrent?.Current.BindTo(bindable); From ee9d82f0fef6573d8ba29bc96e0e27047151bd5c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Jun 2019 19:10:57 +0900 Subject: [PATCH 1095/2854] Revert right click handling for now --- osu.Game.Tournament/TournamentGameBase.cs | 30 +++++++++++++++++++++++ osu.Game/OsuGameBase.cs | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 739fabca9d..fd7a20ecb9 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -12,6 +12,7 @@ using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; +using osu.Framework.Input; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Game.Beatmaps; @@ -20,6 +21,7 @@ using osu.Game.Online.API.Requests; using osu.Game.Rulesets; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; +using osuTK.Input; namespace osu.Game.Tournament { @@ -250,5 +252,33 @@ namespace osu.Game.Tournament })); } } + + protected override UserInputManager CreateUserInputManager() => new TournamentInputManager(); + + private class TournamentInputManager : UserInputManager + { + protected override MouseButtonEventManager CreateButtonManagerFor(MouseButton button) + { + switch (button) + { + case MouseButton.Right: + return new RightMouseManager(button); + } + + return base.CreateButtonManagerFor(button); + } + + private class RightMouseManager : MouseButtonEventManager + { + public RightMouseManager(MouseButton button) + : base(button) + { + } + + public override bool EnableDrag => true; // allow right-mouse dragging for absolute scroll in scroll containers. + public override bool EnableClick => true; + public override bool ChangeFocusOnClick => false; + } + } } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 4c1266c3a6..637708a0e5 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -311,7 +311,7 @@ namespace osu.Game } public override bool EnableDrag => true; // allow right-mouse dragging for absolute scroll in scroll containers. - public override bool EnableClick => true; + public override bool EnableClick => false; public override bool ChangeFocusOnClick => false; } } From 389997dbc452ddebfe04930bb1294c89bf060a8f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 19:14:58 +0900 Subject: [PATCH 1096/2854] Fix metrics being populated with null ratings --- osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 05e647d107..00e08633dd 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -73,7 +73,7 @@ namespace osu.Game.Online.API.Requests.Responses OnlineBeatmapSetID = OnlineBeatmapSetID, Metadata = this, Status = Status, - Metrics = new BeatmapSetMetrics { Ratings = ratings }, + Metrics = ratings == null ? null : new BeatmapSetMetrics { Ratings = ratings }, OnlineInfo = new BeatmapSetOnlineInfo { Covers = covers, From d8ef18c56a8ffe04eee42e3fde14394f8a267316 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 19:16:19 +0900 Subject: [PATCH 1097/2854] Remove unused using --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs index 64bad66919..acf037198f 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; From 9e4f2c7eb9ce3f600f910bf544edfdaa591b4dad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Jun 2019 19:41:01 +0900 Subject: [PATCH 1098/2854] Move font local --- .../Gameplay/Components/MatchHeader.cs | 4 +- .../Gameplay/Components/MatchScoreDisplay.cs | 4 +- .../Screens/TeamIntro/TeamIntroScreen.cs | 4 +- .../Screens/TeamWin/TeamWinScreen.cs | 8 +-- osu.Game.Tournament/TournamentFont.cs | 70 +++++++++++++++++++ osu.Game.Tournament/TournamentGame.cs | 28 ++++++++ osu.Game/Graphics/OsuFont.cs | 6 +- 7 files changed, 109 insertions(+), 15 deletions(-) create mode 100644 osu.Game.Tournament/TournamentFont.cs diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index 22aa6995cf..f9ec16c357 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -176,7 +176,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components X = (flip ? -1 : 1) * 90, Y = -10, Colour = colour, - Font = OsuFont.GetFont(typeface: Typeface.Aquatico, weight: FontWeight.Regular, size: 20), + Font = TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Regular, size: 20), Origin = anchor, Anchor = anchor, }, @@ -219,7 +219,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components Origin = Anchor.Centre, Colour = Color4.White, Text = match.NewValue.Grouping.Value?.Name.Value ?? "Unknown Grouping", - Font = OsuFont.GetFont(typeface: Typeface.Aquatico, weight: FontWeight.Regular, size: 18), + Font = TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Regular, size: 18), }, }; } diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs index 62a785398f..fc28ddccfd 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs @@ -122,8 +122,8 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components public bool Winning { set => DisplayedCountSpriteText.Font = value - ? OsuFont.GetFont(typeface: Typeface.Aquatico, weight: FontWeight.Regular, size: 60) - : OsuFont.GetFont(typeface: Typeface.Aquatico, weight: FontWeight.Light, size: 40); + ? TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Regular, size: 60) + : TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Light, size: 40); } } } diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index 7000d776f0..1efe667eaa 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -200,7 +200,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro new OsuSpriteText { Text = team?.FullName.ToUpper() ?? "???", - Font = OsuFont.GetFont(Typeface.Aquatico, 40, FontWeight.Light), + Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 40, FontWeight.Light), Colour = Color4.Black, Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, @@ -208,7 +208,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro new OsuSpriteText { Text = teamName.ToUpper(), - Font = OsuFont.GetFont(Typeface.Aquatico, 20, FontWeight.Regular), + Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 20, FontWeight.Regular), Colour = colour, Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, diff --git a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs index 69c088efbc..6d5f7e7ad5 100644 --- a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs +++ b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs @@ -127,7 +127,7 @@ namespace osu.Game.Tournament.Screens.TeamWin Origin = Anchor.TopCentre, Colour = col, Text = "WINNER", - Font = OsuFont.GetFont(Typeface.Aquatico, 15, FontWeight.Regular), + Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 15, FontWeight.Regular), }, new OsuSpriteText { @@ -135,7 +135,7 @@ namespace osu.Game.Tournament.Screens.TeamWin Origin = Anchor.TopCentre, Colour = col, Text = pairing.Grouping.Value?.Name.Value ?? "Unknown Grouping", - Font = OsuFont.GetFont(Typeface.Aquatico, 50, FontWeight.Light), + Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 50, FontWeight.Light), Spacing = new Vector2(10, 0), }, new OsuSpriteText @@ -144,7 +144,7 @@ namespace osu.Game.Tournament.Screens.TeamWin Origin = Anchor.TopCentre, Colour = col, Text = pairing.Date.Value.ToUniversalTime().ToString("dd MMMM HH:mm UTC"), - Font = OsuFont.GetFont(Typeface.Aquatico, 20, FontWeight.Light), + Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 20, FontWeight.Light), }, } } @@ -204,7 +204,7 @@ namespace osu.Game.Tournament.Screens.TeamWin new OsuSpriteText { Text = team?.FullName.ToUpper() ?? "???", - Font = OsuFont.GetFont(Typeface.Aquatico, 40, FontWeight.Light), + Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 40, FontWeight.Light), Colour = Color4.Black, Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, diff --git a/osu.Game.Tournament/TournamentFont.cs b/osu.Game.Tournament/TournamentFont.cs new file mode 100644 index 0000000000..d2925d7632 --- /dev/null +++ b/osu.Game.Tournament/TournamentFont.cs @@ -0,0 +1,70 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; + +namespace osu.Game.Tournament +{ + public static class TournamentFont + { + /// + /// The default font size. + /// + public const float DEFAULT_FONT_SIZE = 16; + + /// + /// Retrieves a . + /// + /// The font typeface. + /// The size of the text in local space. For a value of 16, a single line will have a height of 16px. + /// The font weight. + /// Whether the font is italic. + /// Whether all characters should be spaced the same distance apart. + /// The . + public static FontUsage GetFont(TournamentTypeface typeface = TournamentTypeface.Aquatico, float size = DEFAULT_FONT_SIZE, FontWeight weight = FontWeight.Medium, bool italics = false, bool fixedWidth = false) + => new FontUsage(GetFamilyString(typeface), size, GetWeightString(typeface, weight), italics, fixedWidth); + + /// + /// Retrieves the string representation of a . + /// + /// The . + /// The string representation. + public static string GetFamilyString(TournamentTypeface typeface) + { + switch (typeface) + { + case TournamentTypeface.Aquatico: + return "Aquatico"; + } + + return null; + } + + /// + /// Retrieves the string representation of a . + /// + /// The . + /// The . + /// The string representation of in the specified . + public static string GetWeightString(TournamentTypeface typeface, FontWeight weight) + => GetWeightString(GetFamilyString(typeface), weight); + + /// + /// Retrieves the string representation of a . + /// + /// The family string. + /// The . + /// The string representation of in the specified . + public static string GetWeightString(string family, FontWeight weight) + { + string weightString = weight.ToString(); + + // Only exo has an explicit "regular" weight, other fonts do not + if (weight == FontWeight.Regular && family != GetFamilyString(TournamentTypeface.Aquatico) && family != GetFamilyString(TournamentTypeface.Aquatico)) + weightString = string.Empty; + + return weightString; + } + } +} diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index bb5682bb42..0f142e4d74 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; +using osu.Game.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Tournament.Screens; @@ -23,4 +25,30 @@ namespace osu.Game.Tournament MenuCursorContainer.Cursor.Alpha = 0; } } + + public static class TournamentFontExtensions + { + /// + /// Creates a new by applying adjustments to this . + /// + /// The base . + /// The font typeface. If null, the value is copied from this . + /// The text size. If null, the value is copied from this . + /// The font weight. If null, the value is copied from this . + /// Whether the font is italic. If null, the value is copied from this . + /// Whether all characters should be spaced apart the same distance. If null, the value is copied from this . + /// The resulting . + public static FontUsage With(this FontUsage usage, TournamentTypeface? typeface = null, float? size = null, FontWeight? weight = null, bool? italics = null, bool? fixedWidth = null) + { + string familyString = typeface != null ? TournamentFont.GetFamilyString(typeface.Value) : usage.Family; + string weightString = weight != null ? TournamentFont.GetWeightString(familyString, weight.Value) : usage.Weight; + + return usage.With(familyString, size, weightString, italics, fixedWidth); + } + } + + public enum TournamentTypeface + { + Aquatico + } } diff --git a/osu.Game/Graphics/OsuFont.cs b/osu.Game/Graphics/OsuFont.cs index 6c4b46c3ad..2c2f075563 100644 --- a/osu.Game/Graphics/OsuFont.cs +++ b/osu.Game/Graphics/OsuFont.cs @@ -45,9 +45,6 @@ namespace osu.Game.Graphics case Typeface.Venera: return "Venera"; - - case Typeface.Aquatico: - return "Aquatico"; } return null; @@ -73,7 +70,7 @@ namespace osu.Game.Graphics string weightString = weight.ToString(); // Only exo has an explicit "regular" weight, other fonts do not - if (weight == FontWeight.Regular && family != GetFamilyString(Typeface.Exo) && family != GetFamilyString(Typeface.Aquatico)) + if (weight == FontWeight.Regular && family != GetFamilyString(Typeface.Exo)) weightString = string.Empty; return weightString; @@ -105,7 +102,6 @@ namespace osu.Game.Graphics { Exo, Venera, - Aquatico // tournament use only } public enum FontWeight From 52c7ed99607028bef24a0f13073638866e09931f Mon Sep 17 00:00:00 2001 From: naoey Date: Thu, 13 Jun 2019 16:16:48 +0530 Subject: [PATCH 1099/2854] Add ability to change the flie extension of API download requests --- osu.Game/Online/API/APIDownloadRequest.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIDownloadRequest.cs b/osu.Game/Online/API/APIDownloadRequest.cs index efc832a71e..a8f768553b 100644 --- a/osu.Game/Online/API/APIDownloadRequest.cs +++ b/osu.Game/Online/API/APIDownloadRequest.cs @@ -10,9 +10,18 @@ namespace osu.Game.Online.API { private string filename; + /// + /// Sets the extension of the file outputted by this request. + /// + protected virtual string FileExtension { get; } = @".tmp"; + protected override WebRequest CreateWebRequest() { - var request = new FileWebRequest(filename = Path.GetTempFileName(), Uri); + var file = Path.GetTempFileName(); + + File.Move(file, filename = Path.ChangeExtension(file, FileExtension)); + + var request = new FileWebRequest(filename, Uri); request.DownloadProgress += request_Progress; return request; } From aa7cae0879be9a0ca5ffb51f9f2af1c9fbab227e Mon Sep 17 00:00:00 2001 From: naoey Date: Thu, 13 Jun 2019 16:55:41 +0530 Subject: [PATCH 1100/2854] Rephrase xmldoc --- osu.Game/Online/API/APIDownloadRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIDownloadRequest.cs b/osu.Game/Online/API/APIDownloadRequest.cs index a8f768553b..940b9b4803 100644 --- a/osu.Game/Online/API/APIDownloadRequest.cs +++ b/osu.Game/Online/API/APIDownloadRequest.cs @@ -11,7 +11,7 @@ namespace osu.Game.Online.API private string filename; /// - /// Sets the extension of the file outputted by this request. + /// Used to set the extension of the file returned by this request. /// protected virtual string FileExtension { get; } = @".tmp"; From 155f7c7e03541a3e8f825a6c6ce229f015c0d78d Mon Sep 17 00:00:00 2001 From: KingLuigi4932 Date: Thu, 13 Jun 2019 17:32:27 +0300 Subject: [PATCH 1101/2854] Use OsuTextFlowContainer for multi-lines --- .../BeatmapSet/BeatmapNotAvailable.cs | 26 +++++++------------ osu.Game/Overlays/BeatmapSet/Header.cs | 2 +- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs b/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs index 31ed0d9396..59ac8ce6e2 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapNotAvailable.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapSet @@ -29,47 +28,42 @@ namespace osu.Game.Overlays.BeatmapSet removeLinks(); if (beatmapSet?.OnlineInfo.Availability != null) - { - Header?.ResizeHeightTo(450, 500); Show(); - } else - { - Header?.ResizeHeightTo(400, 500); Hide(); - } } } - public Header Header; - - private readonly OsuSpriteText text; + private readonly OsuTextFlowContainer text; private readonly LinkFlowContainer link; public BeatmapNotAvailable() { - AutoSizeAxes = Axes.Both; - Margin = new MarginPadding { Top = 10 }; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Padding = new MarginPadding { Top = 10, Right = 20 }; Children = new Drawable[] { new Box { - Colour = Color4.Black.Opacity(0.6f), RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.6f), }, new FillFlowContainer { - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, Margin = new MarginPadding { Top = 10, Left = 5, Right = 20 }, Children = new Drawable[] { - text = new OsuSpriteText + text = new OsuTextFlowContainer(t => t.Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium)) { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Margin = new MarginPadding { Bottom = 10, Horizontal = 5 }, - Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium), Colour = Color4.Orange, }, link = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 14)) diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index a53b4a2e68..0f3ce008cf 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -135,7 +135,7 @@ namespace osu.Game.Overlays.BeatmapSet Margin = new MarginPadding { Top = 20 }, Child = author = new AuthorInfo(), }, - beatmapNotAvailable = new BeatmapNotAvailable { Header = this }, + beatmapNotAvailable = new BeatmapNotAvailable(), new Container { RelativeSizeAxes = Axes.X, From 3c2a2b23901baad45667c3fddbe044a1c0da7339 Mon Sep 17 00:00:00 2001 From: naoey Date: Thu, 13 Jun 2019 21:28:32 +0530 Subject: [PATCH 1102/2854] Move doc to interface --- osu.Game/Database/DownloadableArchiveModelManager.cs | 5 ----- osu.Game/Database/IModelDownloader.cs | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Database/DownloadableArchiveModelManager.cs b/osu.Game/Database/DownloadableArchiveModelManager.cs index 0735452ce3..fc50a4720a 100644 --- a/osu.Game/Database/DownloadableArchiveModelManager.cs +++ b/osu.Game/Database/DownloadableArchiveModelManager.cs @@ -72,11 +72,6 @@ namespace osu.Game.Database return true; } - /// - /// Checks whether a given is available in the local store already. - /// - /// The whose existence needs to be checked. - /// Whether the exists locally. public virtual bool IsAvailableLocally(TModel model) => modelStore.ConsumableItems.Any(m => m.Equals(model) && !m.DeletePending); public ArchiveDownloadRequest GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Model.Equals(model)); diff --git a/osu.Game/Database/IModelDownloader.cs b/osu.Game/Database/IModelDownloader.cs index 42c64ba67b..b4f8c1e24a 100644 --- a/osu.Game/Database/IModelDownloader.cs +++ b/osu.Game/Database/IModelDownloader.cs @@ -23,6 +23,12 @@ namespace osu.Game.Database /// event Action> DownloadFailed; + + /// + /// Checks whether a given is already available in the local store. + /// + /// The whose existence needs to be checked. + /// Whether the exists. bool IsAvailableLocally(TModel model); /// From 4f0aff3d9c2ad06df4c0197644f51cf6ec63df0c Mon Sep 17 00:00:00 2001 From: andy840119 Date: Fri, 14 Jun 2019 01:12:56 +0900 Subject: [PATCH 1103/2854] hide label when mod is empty --- osu.Game/Overlays/Mods/ModSection.cs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 50400e254f..9d9bb2ba6e 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -50,6 +50,34 @@ namespace osu.Game.Overlays.Mods ButtonsContainer.Children = modContainers; buttons = modContainers.OfType().ToArray(); + + Expanded = value.Any(); + } + } + + private bool expanded = true; + + public bool Expanded + { + set + { + if (expanded == value) return; + + expanded = value; + + this.ClearTransforms(); + + if (expanded) + { + this.AutoSizeAxes = Axes.Y; + this.headerLabel.FadeIn(200); + } + else + { + this.AutoSizeAxes = Axes.None; + this.headerLabel.FadeOut(200); + this.ResizeHeightTo(0, 200, Easing.OutQuint); + } } } From 3a14794c431a1fb13a09b5e3ec5eb77e586006ef Mon Sep 17 00:00:00 2001 From: andy840119 Date: Fri, 14 Jun 2019 01:43:20 +0900 Subject: [PATCH 1104/2854] use show/hide instead because FillFlowContainer's spacing --- osu.Game/Overlays/Mods/ModSection.cs | 19 +++---------------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 ++ 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 9d9bb2ba6e..7b032f1fcf 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -51,32 +51,19 @@ namespace osu.Game.Overlays.Mods ButtonsContainer.Children = modContainers; buttons = modContainers.OfType().ToArray(); - Expanded = value.Any(); - } - } - - private bool expanded = true; - - public bool Expanded - { - set - { - if (expanded == value) return; - - expanded = value; - - this.ClearTransforms(); + var expanded = value.Any(); if (expanded) { this.AutoSizeAxes = Axes.Y; this.headerLabel.FadeIn(200); + Show(); } else { this.AutoSizeAxes = Axes.None; this.headerLabel.FadeOut(200); - this.ResizeHeightTo(0, 200, Easing.OutQuint); + this.ResizeHeightTo(0, 200, Easing.OutQuint).OnComplete((c) => Hide()); } } } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 8e5c9588ce..9ff320841a 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -172,6 +172,8 @@ namespace osu.Game.Overlays.Mods AutoSizeAxes = Axes.Y, Spacing = new Vector2(0f, 10f), Width = content_width, + LayoutDuration = 200, + LayoutEasing = Easing.OutQuint, Children = new ModSection[] { new DifficultyReductionSection { Action = modButtonPressed }, From c30e4677179f426e066175f62b84cc6b49fd86c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=82=BA=E4=BB=80=E9=BA=BC?= Date: Fri, 14 Jun 2019 11:12:30 +0800 Subject: [PATCH 1105/2854] oops --- osu.Game/Overlays/Mods/ModSection.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 7b032f1fcf..07b1c53b3b 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -55,15 +55,15 @@ namespace osu.Game.Overlays.Mods if (expanded) { - this.AutoSizeAxes = Axes.Y; - this.headerLabel.FadeIn(200); + AutoSizeAxes = Axes.Y; + headerLabel.FadeIn(200); Show(); } else { - this.AutoSizeAxes = Axes.None; - this.headerLabel.FadeOut(200); - this.ResizeHeightTo(0, 200, Easing.OutQuint).OnComplete((c) => Hide()); + AutoSizeAxes = Axes.None; + headerLabel.FadeOut(200); + this.ResizeHeightTo(0, 200, Easing.OutQuint).OnComplete(c => Hide()); } } } From 0db9816321c893c5cdb073e763819f83c1ed0a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=82=BA=E4=BB=80=E9=BA=BC?= Date: Fri, 14 Jun 2019 11:23:41 +0800 Subject: [PATCH 1106/2854] expanded -> expand --- osu.Game/Overlays/Mods/ModSection.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 07b1c53b3b..34dede62de 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -51,9 +51,9 @@ namespace osu.Game.Overlays.Mods ButtonsContainer.Children = modContainers; buttons = modContainers.OfType().ToArray(); - var expanded = value.Any(); + var expand = value.Any(); - if (expanded) + if (expand) { AutoSizeAxes = Axes.Y; headerLabel.FadeIn(200); From 9114c8dee7ee96f1a6bb81fcc3312350f8c54e14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=82=BA=E4=BB=80=E9=BA=BC?= Date: Fri, 14 Jun 2019 11:44:03 +0800 Subject: [PATCH 1107/2854] remve unnecessary effect. --- osu.Game/Overlays/Mods/ModSection.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 34dede62de..c110f58b7a 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -55,15 +55,13 @@ namespace osu.Game.Overlays.Mods if (expand) { - AutoSizeAxes = Axes.Y; headerLabel.FadeIn(200); Show(); } else { - AutoSizeAxes = Axes.None; headerLabel.FadeOut(200); - this.ResizeHeightTo(0, 200, Easing.OutQuint).OnComplete(c => Hide()); + Hide(); } } } From 15b9b53d35b0c7c7e4a46b5efc5f89022bf224f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 13:40:32 +0900 Subject: [PATCH 1108/2854] Fix IconButtons not being scaled correctly --- .../UserInterface/TestSceneIconButton.cs | 13 +++---------- .../UserInterface/TestSceneMusicController.cs | 3 +-- osu.Game/Graphics/UserInterface/IconButton.cs | 19 ++----------------- osu.Game/Overlays/MusicController.cs | 14 ++++++++++++++ .../Compose/Components/BeatDivisorControl.cs | 2 +- .../Components/Timeline/TimelineButton.cs | 4 ++-- 6 files changed, 23 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneIconButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneIconButton.cs index 0c9ce50288..c80b3e6297 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneIconButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneIconButton.cs @@ -26,8 +26,7 @@ namespace osu.Game.Tests.Visual.UserInterface { new NamedIconButton("No change", new IconButton()), new NamedIconButton("Background colours", new ColouredIconButton()), - new NamedIconButton("Full-width", new IconButton { ButtonSize = new Vector2(200, 30) }), - new NamedIconButton("Unchanging size", new IconButton(), false), + new NamedIconButton("Full-width", new IconButton { Size = new Vector2(200, 30) }), new NamedIconButton("Icon colours", new IconButton { IconColour = Color4.Green, @@ -48,7 +47,7 @@ namespace osu.Game.Tests.Visual.UserInterface private class NamedIconButton : Container { - public NamedIconButton(string name, IconButton button, bool allowSizeChange = true) + public NamedIconButton(string name, IconButton button) { AutoSizeAxes = Axes.Y; Width = 200; @@ -101,13 +100,7 @@ namespace osu.Game.Tests.Visual.UserInterface } }; - if (allowSizeChange) - iconContainer.AutoSizeAxes = Axes.Both; - else - { - iconContainer.RelativeSizeAxes = Axes.X; - iconContainer.Height = 30; - } + iconContainer.AutoSizeAxes = Axes.Both; button.Anchor = Anchor.Centre; button.Origin = Anchor.Centre; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs index 2f2a40925f..ab2ca47100 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs @@ -3,7 +3,6 @@ using NUnit.Framework; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Timing; using osu.Game.Overlays; @@ -23,9 +22,9 @@ namespace osu.Game.Tests.Visual.UserInterface }; Add(mc); - AddToggleStep(@"toggle visibility", state => mc.State.Value = state ? Visibility.Visible : Visibility.Hidden); AddStep(@"show", () => mc.Show()); AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state); + AddStep(@"show", () => mc.Hide()); } } } diff --git a/osu.Game/Graphics/UserInterface/IconButton.cs b/osu.Game/Graphics/UserInterface/IconButton.cs index 052e9194fa..27427581fd 100644 --- a/osu.Game/Graphics/UserInterface/IconButton.cs +++ b/osu.Game/Graphics/UserInterface/IconButton.cs @@ -11,7 +11,7 @@ namespace osu.Game.Graphics.UserInterface { public class IconButton : OsuAnimatedButton { - public const float BUTTON_SIZE = 30; + public const float DEFAULT_BUTTON_SIZE = 30; private Color4? iconColour; @@ -57,26 +57,11 @@ namespace osu.Game.Graphics.UserInterface set => icon.Scale = value; } - /// - /// The size of the while it is not being pressed. - /// - public Vector2 ButtonSize - { - get => Content.Size; - set - { - Content.RelativeSizeAxes = Axes.None; - Content.AutoSizeAxes = Axes.None; - Content.Size = value; - } - } - private readonly SpriteIcon icon; public IconButton() { - AutoSizeAxes = Axes.Both; - ButtonSize = new Vector2(BUTTON_SIZE); + Size = new Vector2(DEFAULT_BUTTON_SIZE); Add(icon = new SpriteIcon { diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 85524e992c..8b9bac877b 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -464,12 +464,26 @@ namespace osu.Game.Overlays private class MusicIconButton : IconButton { + public MusicIconButton() + { + AutoSizeAxes = Axes.Both; + } + [BackgroundDependencyLoader] private void load(OsuColour colours) { HoverColour = colours.YellowDark.Opacity(0.6f); FlashColour = colours.Yellow; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // works with AutoSizeAxes above to make buttons autosize with the scale animation. + Content.AutoSizeAxes = Axes.None; + Content.Size = new Vector2(DEFAULT_BUTTON_SIZE); + } } private class Background : BufferedContainer diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index ebf8c9c309..c615656d60 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -171,7 +171,7 @@ namespace osu.Game.Screens.Edit.Compose.Components // Small offset to look a bit better centered along with the divisor text Y = 1; - ButtonSize = new Vector2(20); + Size = new Vector2(20); IconScale = new Vector2(0.6f); } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs index 49e97e698b..8865bf31ea 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs @@ -31,14 +31,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline InternalChild = button = new TimelineIconButton { Action = () => Action?.Invoke() }; button.Enabled.BindTo(Enabled); - Width = button.ButtonSize.X; + Width = button.Width; } protected override void Update() { base.Update(); - button.ButtonSize = new Vector2(button.ButtonSize.X, DrawHeight); + button.Size = new Vector2(button.Width, DrawHeight); } private class TimelineIconButton : IconButton From bc574520bf3c0911fa942bdb954da2172e6915ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 15:55:32 +0900 Subject: [PATCH 1109/2854] Update ScrollContainer usages in line with framework changes --- .../Online/TestSceneHistoricalSection.cs | 4 +- .../TestSceneUserProfileRecentSection.cs | 3 +- .../Visual/Online/TestSceneUserRanks.cs | 3 +- .../Visual/UserInterface/TestSceneOsuIcon.cs | 3 +- ...tSceneUpdateableBeatmapBackgroundSprite.cs | 5 +- .../Graphics/Containers/OsuScrollContainer.cs | 94 ++++++++++++++++++- .../Graphics/Containers/SectionsContainer.cs | 4 +- osu.Game/Online/Leaderboards/Leaderboard.cs | 2 +- osu.Game/Overlays/BeatmapSetOverlay.cs | 4 +- osu.Game/Overlays/ChangelogOverlay.cs | 2 +- osu.Game/Overlays/Chat/DrawableChannel.cs | 2 +- osu.Game/Overlays/Settings/Sidebar.cs | 3 +- .../Timeline/ZoomableScrollContainer.cs | 3 +- .../Multi/Lounge/Components/RoomInspector.cs | 3 +- .../Screens/Multi/Lounge/LoungeSubScreen.cs | 3 +- .../Match/Components/MatchSettingsOverlay.cs | 3 +- .../Multi/Match/Components/Participants.cs | 3 +- osu.Game/Screens/Select/BeatmapDetails.cs | 4 +- 18 files changed, 125 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs index 455807649a..883f0c5e3f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs @@ -5,9 +5,9 @@ using System; using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Overlays.Profile.Sections; using osu.Game.Overlays.Profile.Sections.Historical; using osu.Game.Users; @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Online Colour = OsuColour.Gray(0.2f) }); - Add(new ScrollContainer + Add(new OsuScrollContainer { RelativeSizeAxes = Axes.Both, Child = section = new HistoricalSection(), diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.cs index d60e723102..f022425bf6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Profile.Sections; @@ -36,7 +37,7 @@ namespace osu.Game.Tests.Visual.Online RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.2f) }, - new ScrollContainer + new OsuScrollContainer { RelativeSizeAxes = Axes.Both, Child = new FillFlowContainer diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs index 70118b5ebd..9f0a8c769a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Overlays.Profile.Sections; using osu.Game.Overlays.Profile.Sections.Ranks; using osu.Game.Users; @@ -33,7 +34,7 @@ namespace osu.Game.Tests.Visual.Online RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.2f) }, - new ScrollContainer + new OsuScrollContainer { RelativeSizeAxes = Axes.Both, Child = ranks = new RanksSection(), diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs index 2c2a28394c..061039b297 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osuTK; using osuTK.Graphics; @@ -29,7 +30,7 @@ namespace osu.Game.Tests.Visual.UserInterface Colour = Color4.Teal, RelativeSizeAxes = Axes.Both, }, - new ScrollContainer + new OsuScrollContainer { RelativeSizeAxes = Axes.Both, Child = flow = new FillFlowContainer diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs index c361598354..9cdfcb6cc4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics.Containers; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; @@ -92,13 +93,13 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestUnloadAndReload() { var backgrounds = new List(); - ScrollContainer scrollContainer = null; + OsuScrollContainer scrollContainer = null; AddStep("create backgrounds hierarchy", () => { FillFlowContainer backgroundFlow; - Child = scrollContainer = new ScrollContainer + Child = scrollContainer = new OsuScrollContainer { Size = new Vector2(500), Child = backgroundFlow = new FillFlowContainer diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs index f7d406a419..53092ddc9e 100644 --- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs +++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs @@ -1,13 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osuTK; +using osuTK.Graphics; using osuTK.Input; namespace osu.Game.Graphics.Containers { - public class OsuScrollContainer : ScrollContainer + public class OsuScrollContainer : ScrollContainer { /// /// Allows controlling the scroll bar from any position in the container using the right mouse button. @@ -28,6 +33,11 @@ namespace osu.Game.Graphics.Containers protected override bool IsDragging => base.IsDragging || mouseScrollBarDragging; + public OsuScrollContainer(Direction scrollDirection = Direction.Vertical) + : base(scrollDirection) + { + } + protected override bool OnMouseDown(MouseDownEvent e) { if (shouldPerformRightMouseScroll(e)) @@ -71,5 +81,87 @@ namespace osu.Game.Graphics.Containers return base.OnDragEnd(e); } + + protected override ScrollbarContainer CreateScrollbar(Direction direction) => new OsuScrollbar(direction); + + protected class OsuScrollbar : ScrollbarContainer + { + private const float dim_size = 10; + + private Color4 hoverColour; + private Color4 defaultColour; + private Color4 highlightColour; + + private readonly Box box; + + public OsuScrollbar(Direction scrollDir) + : base(scrollDir) + { + Blending = BlendingMode.Additive; + + CornerRadius = 5; + + const float margin = 3; + + Margin = new MarginPadding + { + Left = scrollDir == Direction.Vertical ? margin : 0, + Right = scrollDir == Direction.Vertical ? margin : 0, + Top = scrollDir == Direction.Horizontal ? margin : 0, + Bottom = scrollDir == Direction.Horizontal ? margin : 0, + }; + + Masking = true; + Child = box = new Box { RelativeSizeAxes = Axes.Both }; + + ResizeTo(1); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = defaultColour = colours.Gray8; + hoverColour = colours.GrayF; + highlightColour = colours.Green; + } + + public override void ResizeTo(float val, int duration = 0, Easing easing = Easing.None) + { + Vector2 size = new Vector2(dim_size) + { + [(int)ScrollDirection] = val + }; + this.ResizeTo(size, duration, easing); + } + + protected override bool OnHover(HoverEvent e) + { + this.FadeColour(hoverColour, 100); + return true; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + this.FadeColour(defaultColour, 100); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + if (!base.OnMouseDown(e)) return false; + + //note that we are changing the colour of the box here as to not interfere with the hover effect. + box.FadeColour(highlightColour, 100); + return true; + } + + protected override bool OnMouseUp(MouseUpEvent e) + { + if (e.Button != MouseButton.Left) return false; + + box.FadeColour(Color4.White, 100); + + return base.OnMouseUp(e); + } + } } } diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index a8d0e03c01..9d886c457f 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -16,7 +16,7 @@ namespace osu.Game.Graphics.Containers where T : Drawable { private Drawable expandableHeader, fixedHeader, footer, headerBackground; - private readonly ScrollContainer scrollContainer; + private readonly OsuScrollContainer scrollContainer; private readonly Container headerBackgroundContainer; private readonly FlowContainer scrollContentContainer; @@ -124,7 +124,7 @@ namespace osu.Game.Graphics.Containers public SectionsContainer() { - AddInternal(scrollContainer = new ScrollContainer + AddInternal(scrollContainer = new OsuScrollContainer { RelativeSizeAxes = Axes.Both, Masking = true, diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 3ce71cccba..b91de93a4a 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -23,7 +23,7 @@ namespace osu.Game.Online.Leaderboards { private const double fade_duration = 300; - private readonly ScrollContainer scrollContainer; + private readonly OsuScrollContainer scrollContainer; private readonly Container placeholderContainer; private FillFlowContainer scrollFlow; diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index e0852a890c..1e687267a3 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays private RulesetStore rulesets; - private readonly ScrollContainer scroll; + private readonly OsuScrollContainer scroll; private readonly Bindable beatmapSet = new Bindable(); @@ -49,7 +49,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.2f) }, - scroll = new ScrollContainer + scroll = new OsuScrollContainer { RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 67f195580e..7755c8a6a6 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -51,7 +51,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = colour.PurpleDarkAlternative, }, - new ScrollContainer + new OsuScrollContainer { RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index aec78b962f..7d28df3210 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Chat { public readonly Channel Channel; protected readonly ChatLineContainer ChatLineFlow; - private readonly ScrollContainer scroll; + private readonly OsuScrollContainer scroll; public DrawableChannel(Channel channel) { diff --git a/osu.Game/Overlays/Settings/Sidebar.cs b/osu.Game/Overlays/Settings/Sidebar.cs index 3c18627f23..a80b7c1108 100644 --- a/osu.Game/Overlays/Settings/Sidebar.cs +++ b/osu.Game/Overlays/Settings/Sidebar.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Threading; +using osu.Game.Graphics.Containers; using osu.Game.Overlays.Toolbar; namespace osu.Game.Overlays.Settings @@ -76,7 +77,7 @@ namespace osu.Game.Overlays.Settings return base.OnMouseMove(e); } - private class SidebarScrollContainer : ScrollContainer + private class SidebarScrollContainer : OsuScrollContainer { public SidebarScrollContainer() { diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs index 829437d599..cffb6bedf3 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs @@ -7,11 +7,12 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Transforms; using osu.Framework.Input.Events; using osu.Framework.MathUtils; +using osu.Game.Graphics.Containers; using osuTK; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - public class ZoomableScrollContainer : ScrollContainer + public class ZoomableScrollContainer : OsuScrollContainer { /// /// The time to zoom into/out of a point. diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs index d597e5bb0f..1a18f742a9 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online.API; using osu.Game.Online.API.Requests; @@ -226,7 +227,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components { Padding = new MarginPadding { Horizontal = 10 }; - InternalChild = new ScrollContainer + InternalChild = new OsuScrollContainer { RelativeSizeAxes = Axes.Both, Child = fill = new FillFlowContainer diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index 7cbae611ea..7f8e690516 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Screens; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Overlays.SearchableList; @@ -43,7 +44,7 @@ namespace osu.Game.Screens.Multi.Lounge Width = 0.55f, Children = new Drawable[] { - new ScrollContainer + new OsuScrollContainer { RelativeSizeAxes = Axes.Both, ScrollbarOverlapsContent = false, diff --git a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs index 359b5824c0..410aaed788 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; @@ -91,7 +92,7 @@ namespace osu.Game.Screens.Multi.Match.Components { new Drawable[] { - new ScrollContainer + new OsuScrollContainer { Padding = new MarginPadding { diff --git a/osu.Game/Screens/Multi/Match/Components/Participants.cs b/osu.Game/Screens/Multi/Match/Components/Participants.cs index 09d25572ec..ad38ec6a99 100644 --- a/osu.Game/Screens/Multi/Match/Components/Participants.cs +++ b/osu.Game/Screens/Multi/Match/Components/Participants.cs @@ -5,6 +5,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Containers; using osu.Game.Overlays.SearchableList; using osu.Game.Screens.Multi.Components; using osu.Game.Users; @@ -25,7 +26,7 @@ namespace osu.Game.Screens.Multi.Match.Components Padding = new MarginPadding { Horizontal = SearchableListOverlay.WIDTH_PADDING }, Children = new Drawable[] { - new ScrollContainer + new OsuScrollContainer { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Top = 10 }, diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 378b1b1dc6..90989d7ebb 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -30,7 +30,7 @@ namespace osu.Game.Screens.Select private readonly AdvancedStats advanced; private readonly DetailBox ratingsContainer; private readonly UserRatings ratings; - private readonly ScrollContainer metadataScroll; + private readonly OsuScrollContainer metadataScroll; private readonly MetadataSection description, source, tags; private readonly Container failRetryContainer; private readonly FailRetryGraph failRetryGraph; @@ -107,7 +107,7 @@ namespace osu.Game.Screens.Select }, }, }, - metadataScroll = new ScrollContainer + metadataScroll = new OsuScrollContainer { RelativeSizeAxes = Axes.X, Width = 0.5f, From 4972f862e64ca302c7294e698822dd2727b01d7d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 14 Jun 2019 19:35:31 +0900 Subject: [PATCH 1110/2854] Seek track instead --- .../Visual/Gameplay/TestSceneGameplayRewinding.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index 0176301c03..787b8ddfed 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Audio.Track; using osu.Framework.MathUtils; using osu.Framework.Timing; using osu.Game.Beatmaps; @@ -31,8 +32,14 @@ namespace osu.Game.Tests.Visual.Gameplay { } + private Track track; + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) - => new ClockBackedTestWorkingBeatmap(beatmap, new FramedClock(new ManualClock { Rate = 1 }), audioManager); + { + var working = new ClockBackedTestWorkingBeatmap(beatmap, new FramedClock(new ManualClock { Rate = 1 }), audioManager); + track = working.Track; + return working; + } [Test] public void TestNotJudgementsOnRewind() @@ -47,7 +54,7 @@ namespace osu.Game.Tests.Visual.Gameplay private void addSeekStep(double time) { - AddStep($"seek to {time}", () => player.GameplayClockContainer.Seek(time)); + AddStep($"seek to {time}", () => track.Seek(time)); // Allow a few frames of lenience AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, player.DrawableRuleset.FrameStableClock.CurrentTime, 100)); From 95082d2fccb0f709ac4f3978f15a1cf8796f4daa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 14 Jun 2019 19:35:47 +0900 Subject: [PATCH 1111/2854] Rename testcase --- osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index 787b8ddfed..a1746084a3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Gameplay } [Test] - public void TestNotJudgementsOnRewind() + public void TestNoJudgementsOnRewind() { addSeekStep(3000); AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged)); From 0c293be89cccbd1d12f0e22cf4997aee49520cf3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 14 Jun 2019 19:51:31 +0900 Subject: [PATCH 1112/2854] Wait for track to start running before seeking --- osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index a1746084a3..237fee1594 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -44,6 +44,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestNoJudgementsOnRewind() { + AddUntilStep("wait for track to start running", () => track.IsRunning); addSeekStep(3000); AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged)); AddStep("clear results", () => player.AppliedResults.Clear()); From 512b9dfd820d0e6a1c6852979f1a825b6d2e8fb5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 14 Jun 2019 20:18:22 +0900 Subject: [PATCH 1113/2854] Fix potential songselect testcase failures --- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index f5115c50a9..738b7f14f3 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -18,6 +18,7 @@ using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Taiko; using osu.Game.Screens.Select; @@ -100,8 +101,11 @@ namespace osu.Game.Tests.Visual.SongSelect } [SetUp] - public virtual void SetUp() => - Schedule(() => { manager?.Delete(manager.GetAllUsableBeatmapSets()); }); + public virtual void SetUp() => Schedule(() => + { + Ruleset.Value = new OsuRuleset().RulesetInfo; + manager?.Delete(manager.GetAllUsableBeatmapSets()); + }); [Test] public void TestDummy() @@ -185,7 +189,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("empty mods", () => !Mods.Value.Any()); void onModChange(ValueChangedEvent> e) => modChangeIndex = actionIndex++; - void onRulesetChange(ValueChangedEvent e) => rulesetChangeIndex = actionIndex--; + void onRulesetChange(ValueChangedEvent e) => rulesetChangeIndex = actionIndex++; } [Test] From 831686d20b5288fe7c5ef8f968d0e7d4f5bfee23 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 16:23:38 +0900 Subject: [PATCH 1114/2854] Fix crash when starting with an empty bracket --- osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 0e3a9b0dfd..fb7a7a8983 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -104,9 +104,10 @@ namespace osu.Game.Tournament.Screens.MapPool private void beatmapChanged(ValueChangedEvent beatmap) { - if (currentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) < 2) + if (currentMatch.Value == null || currentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) < 2) return; + // if bans have already been placed, beatmap changes result in a selection being made autoamtically if (beatmap.NewValue.OnlineBeatmapID != null) addForBeatmap(beatmap.NewValue.OnlineBeatmapID.Value); } From 2b4384bb30d44aa482b4ea13cb765189a6001bdb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 16:23:53 +0900 Subject: [PATCH 1115/2854] Fix crash when setting a match as current which doesn't have a grouping --- osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index 093a1ba2d9..0e17936079 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -48,9 +48,9 @@ namespace osu.Game.Tournament.Screens.Schedule currentMatch.BindTo(ladder.CurrentMatch); } - private void matchChanged(ValueChangedEvent pairing) + private void matchChanged(ValueChangedEvent match) { - if (pairing.NewValue == null) + if (match.NewValue == null) { mainContainer.Clear(); return; @@ -110,14 +110,14 @@ namespace osu.Game.Tournament.Screens.Schedule { Margin = new MarginPadding { Left = -10, Bottom = 10, Top = -5 }, Spacing = new Vector2(10, 0), - Text = currentMatch.Value.Grouping.Value.Name.Value, + Text = match.NewValue.Grouping.Value?.Name.Value, Colour = Color4.Black, Font = OsuFont.GetFont(size: 20) }, - new SchedulePairing(currentMatch.Value, false), + new SchedulePairing(match.NewValue, false), new OsuSpriteText { - Text = "Start Time " + pairing.NewValue.Date.Value.ToUniversalTime().ToString("HH:mm UTC"), + Text = "Start Time " + match.NewValue.Date.Value.ToUniversalTime().ToString("HH:mm UTC"), Colour = Color4.Black, Font = OsuFont.GetFont(size: 20) }, From d4808ded0b824768a6ff9a52b2b90200bbba49f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 16:28:59 +0900 Subject: [PATCH 1116/2854] Fix tooltips not showing at correct location --- osu.Game.Tournament/TournamentGameBase.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index fd7a20ecb9..c773844cec 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -223,7 +223,9 @@ namespace osu.Game.Tournament protected override void LoadComplete() { + MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display MenuCursorContainer.Cursor.Alpha = 0; + base.LoadComplete(); } From 1eb15c39f96098681eeceea20cc2acfd6b10a464 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 16:38:29 +0900 Subject: [PATCH 1117/2854] Fix tests not working --- osu.Game.Tournament.Tests/TournamentTestBrowser.cs | 4 ++-- osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs index 6d4063ec4f..f7ad757926 100644 --- a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs +++ b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs @@ -3,7 +3,7 @@ using osu.Framework.Testing; using osu.Game.Graphics; -using osu.Game.Screens.Backgrounds; +using osu.Game.Graphics.Backgrounds; namespace osu.Game.Tournament.Tests { @@ -13,7 +13,7 @@ namespace osu.Game.Tournament.Tests { base.LoadComplete(); - LoadComponentAsync(new BackgroundScreenDefault + LoadComponentAsync(new Background("Menu/menu-background-0") { Colour = OsuColour.Gray(0.5f), Depth = 10 diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index b76fc261f0..cf0752f764 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -14,6 +14,10 @@ netcoreapp2.1 + + + + \ No newline at end of file From 4cd6955a96c46da40e77c8d2f0820556c293dd0b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 16:57:29 +0900 Subject: [PATCH 1118/2854] Fix GameplayScreen test not working --- ...tCaseGameplay.cs => TestCaseGameplayScreen.cs} | 12 +++++------- .../Components/MatchChatDisplay.cs | 12 ++++++++++++ .../Screens/Gameplay/GameplayScreen.cs | 15 ++++++++------- .../Screens/TournamentSceneManager.cs | 11 +---------- 4 files changed, 26 insertions(+), 24 deletions(-) rename osu.Game.Tournament.Tests/{TestCaseGameplay.cs => TestCaseGameplayScreen.cs} (66%) diff --git a/osu.Game.Tournament.Tests/TestCaseGameplay.cs b/osu.Game.Tournament.Tests/TestCaseGameplayScreen.cs similarity index 66% rename from osu.Game.Tournament.Tests/TestCaseGameplay.cs rename to osu.Game.Tournament.Tests/TestCaseGameplayScreen.cs index c881f09cb9..b44cb11ec4 100644 --- a/osu.Game.Tournament.Tests/TestCaseGameplay.cs +++ b/osu.Game.Tournament.Tests/TestCaseGameplayScreen.cs @@ -1,25 +1,23 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Game.Tests.Visual; +using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Gameplay; namespace osu.Game.Tournament.Tests { - public class TestCaseGameplay : OsuTestScene + public class TestCaseGameplayScreen : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(GameplayScreen) - }; + [Cached] + private MatchChatDisplay chat = new MatchChatDisplay(); [BackgroundDependencyLoader] private void load() { Add(new GameplayScreen()); + Add(chat); } } } diff --git a/osu.Game.Tournament/Components/MatchChatDisplay.cs b/osu.Game.Tournament/Components/MatchChatDisplay.cs index 174b215732..fbf6949540 100644 --- a/osu.Game.Tournament/Components/MatchChatDisplay.cs +++ b/osu.Game.Tournament/Components/MatchChatDisplay.cs @@ -4,9 +4,11 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Game.Online.Chat; using osu.Game.Overlays.Chat; using osu.Game.Tournament.IPC; +using osuTK; using osuTK.Graphics; namespace osu.Game.Tournament.Components @@ -17,6 +19,16 @@ namespace osu.Game.Tournament.Components private ChannelManager manager; + public MatchChatDisplay() + { + RelativeSizeAxes = Axes.X; + Y = 100; + Size = new Vector2(0.45f, 112); + Margin = new MarginPadding(10); + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; + } + [BackgroundDependencyLoader(true)] private void load(MatchIPCInfo ipc) { diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 9883f03581..ca7d017bf3 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -32,13 +32,15 @@ namespace osu.Game.Tournament.Screens.Gameplay private readonly Color4 red = new Color4(186, 0, 18, 255); private readonly Color4 blue = new Color4(17, 136, 170, 255); - [Resolved] + [Resolved(canBeNull: true)] private TournamentSceneManager sceneManager { get; set; } + [Resolved] + private MatchChatDisplay chat { get; set; } + [BackgroundDependencyLoader] - private void load(LadderInfo ladder, MatchIPCInfo ipc, MatchChatDisplay chat) + private void load(LadderInfo ladder, MatchIPCInfo ipc) { - this.chat = chat; this.ipc = ipc; AddRangeInternal(new Drawable[] @@ -132,7 +134,6 @@ namespace osu.Game.Tournament.Screens.Gameplay } private ScheduledDelegate scheduledOperation; - private MatchChatDisplay chat; private MatchScoreDisplay scoreDisplay; private TourneyState lastState; @@ -155,7 +156,7 @@ namespace osu.Game.Tournament.Screens.Gameplay void expand() { - chat.Expand(); + chat?.Expand(); using (BeginDelayedSequence(300, true)) { @@ -168,8 +169,8 @@ namespace osu.Game.Tournament.Screens.Gameplay { SongBar.Expanded = false; scoreDisplay.FadeOut(100); - using (chat.BeginDelayedSequence(500)) - chat.Contract(); + using (chat?.BeginDelayedSequence(500)) + chat?.Contract(); } switch (state.NewValue) diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index 6bc624dca0..cec4f52751 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -32,17 +32,8 @@ namespace osu.Game.Tournament.Screens private Container screens; private VideoSprite video; - //todo: make less temporary [Cached] - private MatchChatDisplay chat = new MatchChatDisplay - { - RelativeSizeAxes = Axes.X, - Y = 100, - Size = new Vector2(0.45f, 112), - Margin = new MarginPadding(10), - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - }; + private MatchChatDisplay chat = new MatchChatDisplay(); private Container chatContainer; From 0db013b7825c50366ce45bcfed54631eca243b34 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 17:08:26 +0900 Subject: [PATCH 1119/2854] Fix save button not being topmost --- osu.Game.Tournament/TournamentGameBase.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index c773844cec..2d7e4cd83a 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -71,6 +71,7 @@ namespace osu.Game.Tournament Text = "Save Changes", Width = 140, Height = 50, + Depth = float.MinValue, Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, Padding = new MarginPadding(10), From 8da448fca793b2b4b4336f179a95ea2a9f7ac1a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 17:08:44 +0900 Subject: [PATCH 1120/2854] Improve usability of grouping editor screen --- .../Groupings/GroupingsEditorScreen.cs | 90 ++++++++++++++----- 1 file changed, 66 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs b/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs index 9c35ff21d4..1641b4205a 100644 --- a/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs @@ -8,10 +8,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; +using osuTK; namespace osu.Game.Tournament.Screens.Groupings { @@ -28,26 +30,27 @@ namespace osu.Game.Tournament.Screens.Groupings RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.2f), }, - new FillFlowContainer + new OsuScrollContainer { - Direction = FillDirection.Vertical, RelativeSizeAxes = Axes.Both, Width = 0.9f, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, + Child = items = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + }, + new ControlPanel + { Children = new Drawable[] { - items = new FillFlowContainer - { - Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, new TriangleButton { - Margin = new MarginPadding(20), - Width = 100, - Text = "Add", + RelativeSizeAxes = Axes.X, + Text = "Add new", Action = addNew }, } @@ -70,7 +73,13 @@ namespace osu.Game.Tournament.Screens.Groupings private void addNew() { - items.Add(new GroupingRow(new TournamentGrouping { StartDate = { Value = DateTimeOffset.UtcNow } }, updateGroupings)); + items.Add(new GroupingRow(new TournamentGrouping + { + StartDate = + { + Value = DateTimeOffset.UtcNow + } + }, updateGroupings)); updateGroupings(); } @@ -85,31 +94,64 @@ namespace osu.Game.Tournament.Screens.Groupings public GroupingRow(TournamentGrouping grouping, Action onDelete) { + Margin = new MarginPadding(10); + Grouping = grouping; InternalChildren = new Drawable[] { + new Box + { + Colour = OsuColour.Gray(0.1f), + RelativeSizeAxes = Axes.Both, + }, new FillFlowContainer { + Margin = new MarginPadding(5), + Padding = new MarginPadding { Right = 160 }, + Spacing = new Vector2(5), Direction = FillDirection.Full, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Children = new Drawable[] { - new SettingsTextBox { Width = 0.3f, Bindable = Grouping.Name }, - new SettingsTextBox { Width = 0.3f, Bindable = Grouping.Description }, - new SettingsSlider { LabelText = "Best of", Width = 0.3f, Bindable = Grouping.BestOf }, - new DangerousSettingsButton + new SettingsTextBox { - Width = 0.1f, - Text = "Delete", - Action = () => - { - Expire(); - onDelete(); - } + LabelText = "Name", + Width = 0.33f, + Bindable = Grouping.Name + }, + new SettingsTextBox + { + LabelText = "Description", + Width = 0.33f, + Bindable = Grouping.Description + }, + new DateTextBox + { + LabelText = "Start Time", + Width = 0.33f, + Bindable = Grouping.StartDate + }, + new SettingsSlider + { + LabelText = "Best of", + Width = 0.33f, + Bindable = Grouping.BestOf }, - new DateTextBox { Width = 0.3f, Bindable = Grouping.StartDate }, } + }, + new DangerousSettingsButton + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.None, + Width = 150, + Text = "Delete", + Action = () => + { + Expire(); + onDelete(); + }, } }; From a7fcec8d9775faa43b80b336b085aca3f04aeac9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 17:17:47 +0900 Subject: [PATCH 1121/2854] Fix TournamentSceneManager test --- osu.Game.Tournament/Screens/TournamentSceneManager.cs | 8 ++++++-- osu.Game.Tournament/TournamentGame.cs | 3 +-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index cec4f52751..083f26240b 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Video; using osu.Framework.Platform; using osu.Game.Graphics.UserInterface; -using osu.Game.Screens; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Drawings; using osu.Game.Tournament.Screens.Gameplay; @@ -27,7 +26,7 @@ using osuTK.Graphics; namespace osu.Game.Tournament.Screens { [Cached] - public class TournamentSceneManager : OsuScreen + public class TournamentSceneManager : CompositeDrawable { private Container screens; private VideoSprite video; @@ -37,6 +36,11 @@ namespace osu.Game.Tournament.Screens private Container chatContainer; + public TournamentSceneManager() + { + RelativeSizeAxes = Axes.Both; + } + [BackgroundDependencyLoader] private void load(LadderInfo ladder, Storage storage) { diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 0f142e4d74..c8c462a458 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -3,7 +3,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Tournament.Screens; @@ -19,7 +18,7 @@ namespace osu.Game.Tournament Add(new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, - Child = new ScreenStack(new TournamentSceneManager()) { RelativeSizeAxes = Axes.Both } + Child = new TournamentSceneManager() }); MenuCursorContainer.Cursor.Alpha = 0; From 9818637b8de5255cc44aa46734baf04d45cb2bed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 17:20:15 +0900 Subject: [PATCH 1122/2854] Fix MapPoolScreen test --- osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index fb7a7a8983..47d5a36a4b 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -28,6 +28,9 @@ namespace osu.Game.Tournament.Screens.MapPool private readonly Bindable currentMatch = new Bindable(); + [Resolved(canBeNull: true)] + private TournamentSceneManager sceneManager { get; set; } + private TeamColour pickColour; private ChoiceType pickType; @@ -169,9 +172,6 @@ namespace osu.Game.Tournament.Screens.MapPool setNextMode(); } - [Resolved] - private TournamentSceneManager sceneManager { get; set; } - private ScheduledDelegate scheduledChange; private void addForBeatmap(int beatmapId) From d4ff9dc833dcbeed8060f7d4e253f4ca8f42412c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 17:25:07 +0900 Subject: [PATCH 1123/2854] TestCase -> TestScene --- .../{LadderTestCase.cs => LadderTestScene.cs} | 2 +- .../{TestCaseBeatmapPanel.cs => TestSceneBeatmapPanel.cs} | 2 +- .../{TestCaseGameplayScreen.cs => TestSceneGameplayScreen.cs} | 2 +- ...pingsEditorScreen.cs => TestSceneGroupingsEditorScreen.cs} | 4 ++-- .../{TestCaseLadderManager.cs => TestSceneLadderManager.cs} | 2 +- .../{TestCaseMapPool.cs => TestSceneMapPool.cs} | 2 +- ...stCaseMatchChatDisplay.cs => TestSceneMatchChatDisplay.cs} | 4 ++-- .../{TestCaseMatchPairings.cs => TestSceneMatchPairings.cs} | 4 ++-- ...CaseMatchScoreDisplay.cs => TestSceneMatchScoreDisplay.cs} | 4 ++-- .../{TestCaseSceneManager.cs => TestSceneSceneManager.cs} | 2 +- .../{TestCaseSchedule.cs => TestSceneSchedule.cs} | 2 +- .../{TestCaseShowcase.cs => TestSceneShowcase.cs} | 2 +- .../{TestCaseTeamIntro.cs => TestSceneTeamIntro.cs} | 2 +- .../{TestCaseTeamWin.cs => TestSceneTeamWin.cs} | 2 +- 14 files changed, 18 insertions(+), 18 deletions(-) rename osu.Game.Tournament.Tests/{LadderTestCase.cs => LadderTestScene.cs} (87%) rename osu.Game.Tournament.Tests/{TestCaseBeatmapPanel.cs => TestSceneBeatmapPanel.cs} (96%) rename osu.Game.Tournament.Tests/{TestCaseGameplayScreen.cs => TestSceneGameplayScreen.cs} (91%) rename osu.Game.Tournament.Tests/{TestCaseGroupingsEditorScreen.cs => TestSceneGroupingsEditorScreen.cs} (73%) rename osu.Game.Tournament.Tests/{TestCaseLadderManager.cs => TestSceneLadderManager.cs} (91%) rename osu.Game.Tournament.Tests/{TestCaseMapPool.cs => TestSceneMapPool.cs} (91%) rename osu.Game.Tournament.Tests/{TestCaseMatchChatDisplay.cs => TestSceneMatchChatDisplay.cs} (96%) rename osu.Game.Tournament.Tests/{TestCaseMatchPairings.cs => TestSceneMatchPairings.cs} (97%) rename osu.Game.Tournament.Tests/{TestCaseMatchScoreDisplay.cs => TestSceneMatchScoreDisplay.cs} (90%) rename osu.Game.Tournament.Tests/{TestCaseSceneManager.cs => TestSceneSceneManager.cs} (89%) rename osu.Game.Tournament.Tests/{TestCaseSchedule.cs => TestSceneSchedule.cs} (92%) rename osu.Game.Tournament.Tests/{TestCaseShowcase.cs => TestSceneShowcase.cs} (92%) rename osu.Game.Tournament.Tests/{TestCaseTeamIntro.cs => TestSceneTeamIntro.cs} (95%) rename osu.Game.Tournament.Tests/{TestCaseTeamWin.cs => TestSceneTeamWin.cs} (95%) diff --git a/osu.Game.Tournament.Tests/LadderTestCase.cs b/osu.Game.Tournament.Tests/LadderTestScene.cs similarity index 87% rename from osu.Game.Tournament.Tests/LadderTestCase.cs rename to osu.Game.Tournament.Tests/LadderTestScene.cs index 450626fd43..cb55f543f6 100644 --- a/osu.Game.Tournament.Tests/LadderTestCase.cs +++ b/osu.Game.Tournament.Tests/LadderTestScene.cs @@ -6,7 +6,7 @@ using osu.Game.Tests.Visual; namespace osu.Game.Tournament.Tests { - public class LadderTestCase : OsuTestScene + public class LadderTestScene : OsuTestScene { [Resolved] protected LadderInfo Ladder { get; private set; } diff --git a/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs b/osu.Game.Tournament.Tests/TestSceneBeatmapPanel.cs similarity index 96% rename from osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs rename to osu.Game.Tournament.Tests/TestSceneBeatmapPanel.cs index 6ebdcc511b..50169adad3 100644 --- a/osu.Game.Tournament.Tests/TestCaseBeatmapPanel.cs +++ b/osu.Game.Tournament.Tests/TestSceneBeatmapPanel.cs @@ -15,7 +15,7 @@ using osu.Game.Tournament.Components; namespace osu.Game.Tournament.Tests { - public class TestCaseBeatmapPanel : OsuTestScene + public class TestSceneBeatmapPanel : OsuTestScene { [Resolved] private IAPIProvider api { get; set; } diff --git a/osu.Game.Tournament.Tests/TestCaseGameplayScreen.cs b/osu.Game.Tournament.Tests/TestSceneGameplayScreen.cs similarity index 91% rename from osu.Game.Tournament.Tests/TestCaseGameplayScreen.cs rename to osu.Game.Tournament.Tests/TestSceneGameplayScreen.cs index b44cb11ec4..1b73c798fc 100644 --- a/osu.Game.Tournament.Tests/TestCaseGameplayScreen.cs +++ b/osu.Game.Tournament.Tests/TestSceneGameplayScreen.cs @@ -8,7 +8,7 @@ using osu.Game.Tournament.Screens.Gameplay; namespace osu.Game.Tournament.Tests { - public class TestCaseGameplayScreen : OsuTestScene + public class TestSceneGameplayScreen : OsuTestScene { [Cached] private MatchChatDisplay chat = new MatchChatDisplay(); diff --git a/osu.Game.Tournament.Tests/TestCaseGroupingsEditorScreen.cs b/osu.Game.Tournament.Tests/TestSceneGroupingsEditorScreen.cs similarity index 73% rename from osu.Game.Tournament.Tests/TestCaseGroupingsEditorScreen.cs rename to osu.Game.Tournament.Tests/TestSceneGroupingsEditorScreen.cs index d3ef011535..993a233960 100644 --- a/osu.Game.Tournament.Tests/TestCaseGroupingsEditorScreen.cs +++ b/osu.Game.Tournament.Tests/TestSceneGroupingsEditorScreen.cs @@ -5,9 +5,9 @@ using osu.Game.Tournament.Screens.Groupings; namespace osu.Game.Tournament.Tests { - public class TestCaseGroupingsEditorScreen : LadderTestCase + public class TestSceneGroupingsEditorScreen : LadderTestScene { - public TestCaseGroupingsEditorScreen() + public TestSceneGroupingsEditorScreen() { Add(new GroupingsEditorScreen()); } diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestSceneLadderManager.cs similarity index 91% rename from osu.Game.Tournament.Tests/TestCaseLadderManager.cs rename to osu.Game.Tournament.Tests/TestSceneLadderManager.cs index 8704c3aec5..c9ea740f92 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestSceneLadderManager.cs @@ -8,7 +8,7 @@ using osu.Game.Tournament.Screens.Ladder; namespace osu.Game.Tournament.Tests { - public class TestCaseLadderManager : LadderTestCase + public class TestSceneLadderManager : LadderTestScene { [BackgroundDependencyLoader] private void load() diff --git a/osu.Game.Tournament.Tests/TestCaseMapPool.cs b/osu.Game.Tournament.Tests/TestSceneMapPool.cs similarity index 91% rename from osu.Game.Tournament.Tests/TestCaseMapPool.cs rename to osu.Game.Tournament.Tests/TestSceneMapPool.cs index 6d2f64e7a7..43f4831a2a 100644 --- a/osu.Game.Tournament.Tests/TestCaseMapPool.cs +++ b/osu.Game.Tournament.Tests/TestSceneMapPool.cs @@ -8,7 +8,7 @@ using osu.Game.Tournament.Screens.MapPool; namespace osu.Game.Tournament.Tests { - public class TestCaseMapPool : LadderTestCase + public class TestSceneMapPool : LadderTestScene { public override IReadOnlyList RequiredTypes => new[] { diff --git a/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs b/osu.Game.Tournament.Tests/TestSceneMatchChatDisplay.cs similarity index 96% rename from osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs rename to osu.Game.Tournament.Tests/TestSceneMatchChatDisplay.cs index cec6095598..fb31cd0f7c 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchChatDisplay.cs +++ b/osu.Game.Tournament.Tests/TestSceneMatchChatDisplay.cs @@ -14,7 +14,7 @@ using osuTK; namespace osu.Game.Tournament.Tests { - public class TestCaseMatchChatDisplay : OsuTestScene + public class TestSceneMatchChatDisplay : OsuTestScene { private readonly Channel testChannel = new Channel(); @@ -43,7 +43,7 @@ namespace osu.Game.Tournament.Tests [Cached] private MatchIPCInfo matchInfo = new MatchIPCInfo(); // hide parent - public TestCaseMatchChatDisplay() + public TestSceneMatchChatDisplay() { MatchChatDisplay chatDisplay; diff --git a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs b/osu.Game.Tournament.Tests/TestSceneMatchPairings.cs similarity index 97% rename from osu.Game.Tournament.Tests/TestCaseMatchPairings.cs rename to osu.Game.Tournament.Tests/TestSceneMatchPairings.cs index e7a329d35f..4d92e209be 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestSceneMatchPairings.cs @@ -11,7 +11,7 @@ using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament.Tests { - public class TestCaseMatchPairings : OsuTestScene + public class TestSceneMatchPairings : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { @@ -21,7 +21,7 @@ namespace osu.Game.Tournament.Tests typeof(DrawableTournamentTeam), }; - public TestCaseMatchPairings() + public TestSceneMatchPairings() { Container level1; Container level2; diff --git a/osu.Game.Tournament.Tests/TestCaseMatchScoreDisplay.cs b/osu.Game.Tournament.Tests/TestSceneMatchScoreDisplay.cs similarity index 90% rename from osu.Game.Tournament.Tests/TestCaseMatchScoreDisplay.cs rename to osu.Game.Tournament.Tests/TestSceneMatchScoreDisplay.cs index c7de5cf8ce..7e9b83a61b 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchScoreDisplay.cs +++ b/osu.Game.Tournament.Tests/TestSceneMatchScoreDisplay.cs @@ -9,12 +9,12 @@ using osu.Game.Tournament.Screens.Gameplay.Components; namespace osu.Game.Tournament.Tests { - public class TestCaseMatchScoreDisplay : LadderTestCase + public class TestSceneMatchScoreDisplay : LadderTestScene { [Cached(Type = typeof(MatchIPCInfo))] private MatchIPCInfo matchInfo = new MatchIPCInfo(); - public TestCaseMatchScoreDisplay() + public TestSceneMatchScoreDisplay() { Add(new MatchScoreDisplay { diff --git a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs b/osu.Game.Tournament.Tests/TestSceneSceneManager.cs similarity index 89% rename from osu.Game.Tournament.Tests/TestCaseSceneManager.cs rename to osu.Game.Tournament.Tests/TestSceneSceneManager.cs index 7c1b794e16..385dc09d58 100644 --- a/osu.Game.Tournament.Tests/TestCaseSceneManager.cs +++ b/osu.Game.Tournament.Tests/TestSceneSceneManager.cs @@ -8,7 +8,7 @@ using osu.Game.Tournament.Screens; namespace osu.Game.Tournament.Tests { - public class TestCaseSceneManager : OsuTestScene + public class TestSceneSceneManager : OsuTestScene { [BackgroundDependencyLoader] private void load(Storage storage) diff --git a/osu.Game.Tournament.Tests/TestCaseSchedule.cs b/osu.Game.Tournament.Tests/TestSceneSchedule.cs similarity index 92% rename from osu.Game.Tournament.Tests/TestCaseSchedule.cs rename to osu.Game.Tournament.Tests/TestSceneSchedule.cs index b5a80d7bee..00eb4c4e41 100644 --- a/osu.Game.Tournament.Tests/TestCaseSchedule.cs +++ b/osu.Game.Tournament.Tests/TestSceneSchedule.cs @@ -9,7 +9,7 @@ using osu.Game.Tournament.Screens.Schedule; namespace osu.Game.Tournament.Tests { - public class TestCaseSchedule : OsuTestScene + public class TestSceneSchedule : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { diff --git a/osu.Game.Tournament.Tests/TestCaseShowcase.cs b/osu.Game.Tournament.Tests/TestSceneShowcase.cs similarity index 92% rename from osu.Game.Tournament.Tests/TestCaseShowcase.cs rename to osu.Game.Tournament.Tests/TestSceneShowcase.cs index c0816e3594..a4d518eedd 100644 --- a/osu.Game.Tournament.Tests/TestCaseShowcase.cs +++ b/osu.Game.Tournament.Tests/TestSceneShowcase.cs @@ -9,7 +9,7 @@ using osu.Game.Tournament.Screens.Showcase; namespace osu.Game.Tournament.Tests { - public class TestCaseShowcase : OsuTestScene + public class TestSceneShowcase : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { diff --git a/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs b/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs similarity index 95% rename from osu.Game.Tournament.Tests/TestCaseTeamIntro.cs rename to osu.Game.Tournament.Tests/TestSceneTeamIntro.cs index 78614518ce..b9245c0c25 100644 --- a/osu.Game.Tournament.Tests/TestCaseTeamIntro.cs +++ b/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs @@ -10,7 +10,7 @@ using osu.Game.Tournament.Screens.TeamIntro; namespace osu.Game.Tournament.Tests { - public class TestCaseTeamIntro : LadderTestCase + public class TestSceneTeamIntro : LadderTestScene { [Cached] private readonly Bindable currentMatch = new Bindable(); diff --git a/osu.Game.Tournament.Tests/TestCaseTeamWin.cs b/osu.Game.Tournament.Tests/TestSceneTeamWin.cs similarity index 95% rename from osu.Game.Tournament.Tests/TestCaseTeamWin.cs rename to osu.Game.Tournament.Tests/TestSceneTeamWin.cs index df24877f09..3d6813b8d6 100644 --- a/osu.Game.Tournament.Tests/TestCaseTeamWin.cs +++ b/osu.Game.Tournament.Tests/TestSceneTeamWin.cs @@ -10,7 +10,7 @@ using osu.Game.Tournament.Screens.TeamWin; namespace osu.Game.Tournament.Tests { - public class TestCaseTeamWin : LadderTestCase + public class TestSceneTeamWin : LadderTestScene { [Cached] private readonly Bindable currentMatch = new Bindable(); From eb86d43d19ac790d9903e7cb24762bc479626204 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 17:56:47 +0900 Subject: [PATCH 1124/2854] Update grouping dropdown after new groupings are added --- osu.Game.Tournament/LadderInfo.cs | 2 +- .../Screens/Groupings/GroupingsEditorScreen.cs | 6 ++++-- .../Ladder/Components/LadderEditorSettings.cs | 16 ++++++++++++---- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tournament/LadderInfo.cs b/osu.Game.Tournament/LadderInfo.cs index 9ddca460e2..fae4dcdede 100644 --- a/osu.Game.Tournament/LadderInfo.cs +++ b/osu.Game.Tournament/LadderInfo.cs @@ -13,7 +13,7 @@ namespace osu.Game.Tournament { public List Pairings = new List(); public List Progressions = new List(); - public List Groupings = new List(); + public BindableList Groupings = new BindableList(); public List Teams = new List(); [JsonIgnore] diff --git a/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs b/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs index 1641b4205a..c90a166581 100644 --- a/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs @@ -68,7 +68,7 @@ namespace osu.Game.Tournament.Screens.Groupings protected override void LoadComplete() { base.LoadComplete(); - Scheduler.AddDelayed(() => LadderInfo.Groupings = items.Children.Select(c => c.Grouping).ToList(), 500, true); + Scheduler.AddDelayed(updateGroupings, 500, true); } private void addNew() @@ -80,12 +80,14 @@ namespace osu.Game.Tournament.Screens.Groupings Value = DateTimeOffset.UtcNow } }, updateGroupings)); + updateGroupings(); } private void updateGroupings() { - LadderInfo.Groupings = items.Children.Select(c => c.Grouping).ToList(); + LadderInfo.Groupings.Clear(); + LadderInfo.Groupings.AddRange(items.Children.Select(c => c.Grouping)); } public class GroupingRow : CompositeDrawable diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index be95b404f8..f701432ea0 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -39,8 +39,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { var teamEntries = ladderInfo.Teams; - var groupingOptions = ladderInfo.Groupings.Prepend(new TournamentGrouping()); - Children = new Drawable[] { new Container @@ -77,8 +75,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components textboxTeam2 = new OsuTextBox { RelativeSizeAxes = Axes.X, Height = 20 }, groupingDropdown = new SettingsDropdown { - Bindable = new Bindable { Default = groupingOptions.First() }, - Items = groupingOptions + Bindable = new Bindable(), }, losersCheckbox = new PlayerCheckbox { @@ -91,6 +88,17 @@ namespace osu.Game.Tournament.Screens.Ladder.Components } }; + var groupingOptions = ladderInfo.Groupings.Prepend(new TournamentGrouping()); + + void updateDropdownItems() + { + groupingOptions = ladderInfo.Groupings.Prepend(new TournamentGrouping()); + groupingDropdown.Items = groupingOptions; + } + + ladderInfo.Groupings.ItemsRemoved += _ => updateDropdownItems(); + ladderInfo.Groupings.ItemsAdded += _ => updateDropdownItems(); + editorInfo.Selected.ValueChanged += selection => { textboxTeam1.Text = selection.NewValue?.Team1.Value?.Acronym; From ef21a9e1d2e6e1e115e9f12780db56e1e893c237 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 19:16:20 +0900 Subject: [PATCH 1125/2854] Use bindable flow to avoid scheduled updates --- osu.Game.Tournament/LadderInfo.cs | 8 ++- .../Ladder/Components/DrawableMatchPairing.cs | 64 ++++++++++++++----- .../Ladder/Components/LadderEditorSettings.cs | 5 +- .../Ladder/Components/TournamentGrouping.cs | 1 + .../Screens/Ladder/LadderEditorScreen.cs | 29 ++------- .../Screens/Ladder/LadderScreen.cs | 47 ++++++++++++-- osu.Game.Tournament/TournamentGameBase.cs | 8 +++ 7 files changed, 111 insertions(+), 51 deletions(-) diff --git a/osu.Game.Tournament/LadderInfo.cs b/osu.Game.Tournament/LadderInfo.cs index fae4dcdede..08ef5d9062 100644 --- a/osu.Game.Tournament/LadderInfo.cs +++ b/osu.Game.Tournament/LadderInfo.cs @@ -11,10 +11,12 @@ namespace osu.Game.Tournament { public class LadderInfo { - public List Pairings = new List(); - public List Progressions = new List(); + public BindableList Pairings = new BindableList(); public BindableList Groupings = new BindableList(); - public List Teams = new List(); + public BindableList Teams = new BindableList(); + + // only used for serialisation + public List Progressions = new List(); [JsonIgnore] public Bindable CurrentMatch = new Bindable(); diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index cf9fe3f2be..fcd7ed241a 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -73,26 +75,56 @@ namespace osu.Game.Tournament.Screens.Ladder.Components } }; - pairing.Team1.BindValueChanged(_ => updateTeams()); - pairing.Team2.BindValueChanged(_ => updateTeams()); - pairing.Team1Score.BindValueChanged(_ => updateWinConditions()); - pairing.Team2Score.BindValueChanged(_ => updateWinConditions()); - pairing.Grouping.BindValueChanged(_ => updateWinConditions()); - pairing.Completed.BindValueChanged(_ => updateProgression()); - pairing.Progression.BindValueChanged(_ => updateProgression()); - pairing.LosersProgression.BindValueChanged(_ => updateProgression()); - pairing.Losers.BindValueChanged(_ => updateTeams()); - pairing.Current.BindValueChanged(_ => updateCurrentMatch(), true); - pairing.Position.BindValueChanged(pos => + boundReference(pairing.Team1).BindValueChanged(_ => updateTeams()); + boundReference(pairing.Team2).BindValueChanged(_ => updateTeams()); + boundReference(pairing.Team1Score).BindValueChanged(_ => updateWinConditions()); + boundReference(pairing.Team2Score).BindValueChanged(_ => updateWinConditions()); + boundReference(pairing.Grouping).BindValueChanged(_ => { - if (IsDragged) return; - - Position = new Vector2(pos.NewValue.X, pos.NewValue.Y); + updateWinConditions(); + Changed?.Invoke(); + }); + boundReference(pairing.Completed).BindValueChanged(_ => updateProgression()); + boundReference(pairing.Progression).BindValueChanged(_ => updateProgression()); + boundReference(pairing.LosersProgression).BindValueChanged(_ => updateProgression()); + boundReference(pairing.Losers).BindValueChanged(_ => + { + updateTeams(); + Changed?.Invoke(); + }); + boundReference(pairing.Current).BindValueChanged(_ => updateCurrentMatch(), true); + boundReference(pairing.Position).BindValueChanged(pos => + { + if (!IsDragged) + Position = new Vector2(pos.NewValue.X, pos.NewValue.Y); + Changed?.Invoke(); }, true); updateTeams(); } + /// + /// Fired when somethign changed that requires a ladder redraw. + /// + public Action Changed; + + private readonly List refBindables = new List(); + + private T boundReference(T obj) + where T : IBindable + { + obj = (T)obj.GetBoundCopy(); + refBindables.Add(obj); + return obj; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + foreach (var b in refBindables) + b.UnbindAll(); + } + private void updateCurrentMatch() { if (Pairing.Current.Value) @@ -149,6 +181,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components transferProgression(Pairing.Progression?.Value, Pairing.Winner); transferProgression(Pairing.LosersProgression?.Value, Pairing.Loser); } + + Changed?.Invoke(); } private void transferProgression(MatchPairing destination, TournamentTeam team) @@ -273,7 +307,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Pairing.Progression.Value = null; Pairing.LosersProgression.Value = null; - Expire(); + ladderInfo.Pairings.Remove(Pairing); } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index f701432ea0..21d20e1175 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -88,7 +89,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components } }; - var groupingOptions = ladderInfo.Groupings.Prepend(new TournamentGrouping()); + IEnumerable groupingOptions = null; void updateDropdownItems() { @@ -99,6 +100,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components ladderInfo.Groupings.ItemsRemoved += _ => updateDropdownItems(); ladderInfo.Groupings.ItemsAdded += _ => updateDropdownItems(); + updateDropdownItems(); + editorInfo.Selected.ValueChanged += selection => { textboxTeam1.Text = selection.NewValue?.Team1.Value?.Acronym; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs index e38b684c28..d6e6b11f28 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs @@ -21,6 +21,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly Bindable StartDate = new Bindable(); + // only used for serialisation public List Pairings = new List(); public override string ToString() => Name.Value ?? "None"; diff --git a/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs index 4a35f1ad30..fc98753e0a 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs @@ -24,6 +24,8 @@ namespace osu.Game.Tournament.Screens.Ladder [Cached] private LadderEditorInfo editorInfo = new LadderEditorInfo(); + protected override bool DrawLoserPaths => true; + [BackgroundDependencyLoader] private void load() { @@ -35,32 +37,9 @@ namespace osu.Game.Tournament.Screens.Ladder }); } - private void updateInfo() - { - LadderInfo.Pairings = PairingsContainer.Select(p => p.Pairing).ToList(); - foreach (var g in LadderInfo.Groupings) - g.Pairings = LadderInfo.Pairings.Where(p => p.Grouping.Value == g).Select(p => p.ID).ToList(); - - LadderInfo.Progressions = LadderInfo.Pairings.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat( - LadderInfo.Pairings.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) - .ToList(); - } - - protected override void AddPairing(MatchPairing pairing) - { - base.AddPairing(pairing); - updateInfo(); - } - - protected override void UpdateLayout() - { - base.UpdateLayout(); - updateInfo(); - } - public void BeginJoin(MatchPairing pairing, bool losers) { - ScrollContent.Add(new JoinVisualiser(PairingsContainer, pairing, losers, updateInfo)); + ScrollContent.Add(new JoinVisualiser(PairingsContainer, pairing, losers, UpdateLayout)); } public MenuItem[] ContextMenuItems @@ -75,7 +54,7 @@ namespace osu.Game.Tournament.Screens.Ladder new OsuMenuItem("Create new match", MenuItemType.Highlighted, () => { var pos = PairingsContainer.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); - AddPairing(new MatchPairing { Position = { Value = new Point((int)pos.X, (int)pos.Y) } }); + LadderInfo.Pairings.Add(new MatchPairing { Position = { Value = new Point((int)pos.X, (int)pos.Y) } }); }), new OsuMenuItem("Reset teams", MenuItemType.Destructive, () => { diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs index f2d4ebbb71..1457992003 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -57,16 +57,33 @@ namespace osu.Game.Tournament.Screens.Ladder } }; + void addPairing(MatchPairing pairing) => + PairingsContainer.Add(new DrawableMatchPairing(pairing, this is LadderEditorScreen) + { + Changed = () => layout.Invalidate() + }); + foreach (var pairing in LadderInfo.Pairings) - AddPairing(pairing); + addPairing(pairing); - // todo: fix this - Scheduler.AddDelayed(() => layout.Invalidate(), 1000, true); - } + LadderInfo.Groupings.ItemsAdded += _ => layout.Invalidate(); + LadderInfo.Groupings.ItemsRemoved += _ => layout.Invalidate(); - protected virtual void AddPairing(MatchPairing pairing) - { - PairingsContainer.Add(new DrawableMatchPairing(pairing, this is LadderEditorScreen)); + LadderInfo.Pairings.ItemsAdded += pairings => + { + foreach (var p in pairings) + addPairing(p); + layout.Invalidate(); + }; + + LadderInfo.Pairings.ItemsRemoved += pairings => + { + foreach (var p in pairings) + foreach (var d in PairingsContainer.Where(d => d.Pairing == p)) + d.Expire(); + + layout.Invalidate(); + }; } private Cached layout = new Cached(); @@ -82,6 +99,8 @@ namespace osu.Game.Tournament.Screens.Ladder private Color4 normalPathColour; private Color4 losersPathColour; + protected virtual bool DrawLoserPaths => false; + protected virtual void UpdateLayout() { paths.Clear(); @@ -103,6 +122,20 @@ namespace osu.Game.Tournament.Screens.Ladder else paths.Add(new ProgressionPath(pairing, dest) { Colour = pairing.Pairing.Losers.Value ? losersPathColour : normalPathColour }); } + + if (DrawLoserPaths) + { + if (pairing.Pairing.LosersProgression.Value != null) + { + var dest = PairingsContainer.FirstOrDefault(p => p.Pairing == pairing.Pairing.LosersProgression.Value); + + if (dest == null) + // clean up outdated progressions. + pairing.Pairing.LosersProgression.Value = null; + else + paths.Add(new ProgressionPath(pairing, dest) { Colour = losersPathColour.Opacity(0.1f) }); + } + } } foreach (var group in LadderInfo.Groupings) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 2d7e4cd83a..54d47625fa 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -21,6 +21,7 @@ using osu.Game.Online.API.Requests; using osu.Game.Rulesets; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; +using osu.Game.Tournament.Screens.Ladder.Components; using osuTK.Input; namespace osu.Game.Tournament @@ -244,6 +245,13 @@ namespace osu.Game.Tournament protected virtual void SaveChanges() { + foreach (var g in ladder.Groupings) + g.Pairings = ladder.Pairings.Where(p => p.Grouping.Value == g).Select(p => p.ID).ToList(); + + ladder.Progressions = ladder.Pairings.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat( + ladder.Pairings.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) + .ToList(); + using (var stream = storage.GetStream(bracket_filename, FileAccess.Write, FileMode.Create)) using (var sw = new StreamWriter(stream)) { From c9bd62e8157b9779157f75062cf40e1ee21a8565 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 20:30:09 +0900 Subject: [PATCH 1126/2854] Use bindable logic for grouping name/description updates --- .../Groupings/GroupingsEditorScreen.cs | 29 ++++------ .../Components/DrawableTournamentGrouping.cs | 23 ++++++-- .../Ladder/Components/LadderEditorSettings.cs | 55 +++++++++++++------ 3 files changed, 66 insertions(+), 41 deletions(-) diff --git a/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs b/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs index c90a166581..87a975aadc 100644 --- a/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -62,39 +61,31 @@ namespace osu.Game.Tournament.Screens.Groupings private void load() { foreach (var g in LadderInfo.Groupings) - items.Add(new GroupingRow(g, updateGroupings)); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - Scheduler.AddDelayed(updateGroupings, 500, true); + items.Add(new GroupingRow(g)); } private void addNew() { - items.Add(new GroupingRow(new TournamentGrouping + var grouping = new TournamentGrouping { StartDate = { Value = DateTimeOffset.UtcNow } - }, updateGroupings)); + }; - updateGroupings(); - } - - private void updateGroupings() - { - LadderInfo.Groupings.Clear(); - LadderInfo.Groupings.AddRange(items.Children.Select(c => c.Grouping)); + items.Add(new GroupingRow(grouping)); + LadderInfo.Groupings.Add(grouping); } public class GroupingRow : CompositeDrawable { public readonly TournamentGrouping Grouping; - public GroupingRow(TournamentGrouping grouping, Action onDelete) + [Resolved] + private LadderInfo ladderInfo { get; set; } + + public GroupingRow(TournamentGrouping grouping) { Margin = new MarginPadding(10); @@ -152,7 +143,7 @@ namespace osu.Game.Tournament.Screens.Groupings Action = () => { Expire(); - onDelete(); + ladderInfo.Groupings.Remove(Grouping); }, } }; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs index 40adc24dc4..a3a7bf4f64 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using JetBrains.Annotations; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; @@ -11,8 +13,17 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { public class DrawableTournamentGrouping : CompositeDrawable { + [UsedImplicitly] + private readonly Bindable name; + + [UsedImplicitly] + private readonly Bindable description; + public DrawableTournamentGrouping(TournamentGrouping grouping, bool losers = false) { + OsuSpriteText textName; + OsuSpriteText textDescription; + AutoSizeAxes = Axes.Both; InternalChild = new FillFlowContainer { @@ -20,16 +31,14 @@ namespace osu.Game.Tournament.Screens.Ladder.Components AutoSizeAxes = Axes.Both, Children = new Drawable[] { - new OsuSpriteText + textDescription = new OsuSpriteText { - Text = grouping.Description.Value.ToUpper(), Colour = Color4.Black, Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre }, - new OsuSpriteText + textName = new OsuSpriteText { - Text = ((losers ? "Losers " : "") + grouping.Name).ToUpper(), Font = OsuFont.GetFont(weight: FontWeight.Bold), Colour = Color4.Black, Origin = Anchor.TopCentre, @@ -37,6 +46,12 @@ namespace osu.Game.Tournament.Screens.Ladder.Components }, } }; + + name = grouping.Name.GetBoundCopy(); + name.BindValueChanged(n => textName.Text = ((losers ? "Losers " : "") + grouping.Name).ToUpper(), true); + + description = grouping.Name.GetBoundCopy(); + description.BindValueChanged(n => textDescription.Text = grouping.Description.Value.ToUpper(), true); } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index 21d20e1175..7d6b7ae57f 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -74,10 +75,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components }, }, textboxTeam2 = new OsuTextBox { RelativeSizeAxes = Axes.X, Height = 20 }, - groupingDropdown = new SettingsDropdown - { - Bindable = new Bindable(), - }, + groupingDropdown = new SettingsGroupingDropdown(ladderInfo.Groupings), losersCheckbox = new PlayerCheckbox { LabelText = "Losers Bracket", @@ -89,24 +87,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components } }; - IEnumerable groupingOptions = null; - - void updateDropdownItems() - { - groupingOptions = ladderInfo.Groupings.Prepend(new TournamentGrouping()); - groupingDropdown.Items = groupingOptions; - } - - ladderInfo.Groupings.ItemsRemoved += _ => updateDropdownItems(); - ladderInfo.Groupings.ItemsAdded += _ => updateDropdownItems(); - - updateDropdownItems(); - editorInfo.Selected.ValueChanged += selection => { textboxTeam1.Text = selection.NewValue?.Team1.Value?.Acronym; textboxTeam2.Text = selection.NewValue?.Team2.Value?.Acronym; - groupingDropdown.Bindable.Value = selection.NewValue?.Grouping.Value ?? groupingOptions.First(); + groupingDropdown.Bindable.Value = selection.NewValue?.Grouping.Value; losersCheckbox.Current.Value = selection.NewValue?.Losers.Value ?? false; dateTimeBox.Bindable.Value = selection.NewValue?.Date.Value ?? DateTimeOffset.UtcNow; }; @@ -164,5 +149,39 @@ namespace osu.Game.Tournament.Screens.Ladder.Components protected override void OnHoverLost(HoverLostEvent e) { } + + private class SettingsGroupingDropdown : SettingsDropdown + { + public SettingsGroupingDropdown(BindableList groupings) + { + Bindable = new Bindable(); + + foreach (var g in groupings.Prepend(new TournamentGrouping())) + add(g); + + groupings.ItemsRemoved += items => items.ForEach(i => Control.RemoveDropdownItem(i)); + groupings.ItemsAdded += items => items.ForEach(add); + } + + private readonly List refBindables = new List(); + + private T boundReference(T obj) + where T : IBindable + { + obj = (T)obj.GetBoundCopy(); + refBindables.Add(obj); + return obj; + } + + private void add(TournamentGrouping grouping) + { + Control.AddDropdownItem(grouping); + boundReference(grouping.Name).BindValueChanged(_ => + { + Control.RemoveDropdownItem(grouping); + Control.AddDropdownItem(grouping); + }); + } + } } } From 0ca33e16dd9d4c54fea216daef7c586633fb9d75 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 23:25:29 +0900 Subject: [PATCH 1127/2854] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 75a464d0b8..89c89faa5e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 5e151f916b..46e508c910 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 1a731782603d6eebd7918cabf2b2d4d56e183226 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sat, 15 Jun 2019 13:28:03 +0800 Subject: [PATCH 1128/2854] using FadeTo() instead of FadeIn()/FadeOut() --- osu.Game/Overlays/Mods/ModSection.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index c110f58b7a..1eaa3deea4 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -52,17 +52,12 @@ namespace osu.Game.Overlays.Mods buttons = modContainers.OfType().ToArray(); var expand = value.Any(); - if (expand) - { - headerLabel.FadeIn(200); Show(); - } else - { - headerLabel.FadeOut(200); Hide(); - } + + headerLabel.FadeTo(expand ? 1 : 0, 200); } } From 2b48f5d3e08d7781a96aefe26943e507a7392b85 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 15 Jun 2019 14:45:51 +0900 Subject: [PATCH 1129/2854] Tidy up updateMetrics flow --- osu.Game/Screens/Select/BeatmapDetails.cs | 52 ++++++++++++++--------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 8ddb3e1cc1..23dd87d8ea 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -187,17 +187,24 @@ namespace osu.Game.Screens.Select // metrics may have been previously fetched if (Beatmap?.BeatmapSet?.Metrics != null && Beatmap?.Metrics != null) { - updateMetrics(Beatmap.BeatmapSet.Metrics, Beatmap.Metrics); + updateMetrics(); return; } - // metrics may not be fetched but can be - if (Beatmap?.OnlineBeatmapID != null) + // for now, let's early abort if an OnlineBeatmapID is not present (should have been populated at import time). + if (Beatmap?.OnlineBeatmapID == null) { - var requestedBeatmap = Beatmap; + updateMetrics(); + return; + } - var lookup = new GetBeatmapRequest(requestedBeatmap); - lookup.Success += res => + var requestedBeatmap = Beatmap; + + var lookup = new GetBeatmapRequest(requestedBeatmap); + + lookup.Success += res => + { + Schedule(() => { if (beatmap != requestedBeatmap) //the beatmap has been changed since we started the lookup. @@ -212,27 +219,34 @@ namespace osu.Game.Screens.Select requestedBeatmap.Metrics = b.Metrics; - Schedule(() => updateMetrics(b.BeatmapSet.Metrics, b.Metrics)); - }; + updateMetrics(); + }); + }; - lookup.Failure += e => Schedule(() => updateMetrics(Beatmap?.BeatmapSet?.Metrics, Beatmap?.Metrics)); + lookup.Failure += e => + { + Schedule(() => + { + if (beatmap != requestedBeatmap) + //the beatmap has been changed since we started the lookup. + return; - api.Queue(lookup); - loading.Show(); - return; - } + updateMetrics(); + }); + }; - updateMetrics(Beatmap?.BeatmapSet?.Metrics, Beatmap?.Metrics); + api.Queue(lookup); + loading.Show(); } - private void updateMetrics(BeatmapSetMetrics setMetrics, BeatmapMetrics beatmapMetrics) + private void updateMetrics() { - var hasRatings = setMetrics?.Ratings?.Any() ?? false; - var hasRetriesFails = (beatmapMetrics?.Retries?.Any() ?? false) && (beatmapMetrics.Fails?.Any() ?? false); + var hasRatings = beatmap?.BeatmapSet?.Metrics?.Ratings?.Any() ?? false; + var hasRetriesFails = (beatmap?.Metrics?.Retries?.Any() ?? false) && (beatmap?.Metrics.Fails?.Any() ?? false); if (hasRatings) { - ratings.Metrics = setMetrics; + ratings.Metrics = beatmap.BeatmapSet.Metrics; ratingsContainer.FadeIn(transition_duration); } else @@ -243,7 +257,7 @@ namespace osu.Game.Screens.Select if (hasRetriesFails) { - failRetryGraph.Metrics = beatmapMetrics; + failRetryGraph.Metrics = beatmap.Metrics; failRetryContainer.FadeIn(transition_duration); } else From 4a16ac53bab407adeceadc992c5ec6c8eb1dd10e Mon Sep 17 00:00:00 2001 From: naoey Date: Sat, 15 Jun 2019 12:28:23 +0530 Subject: [PATCH 1130/2854] Remove extra newline --- osu.Game/Database/IModelDownloader.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Database/IModelDownloader.cs b/osu.Game/Database/IModelDownloader.cs index b4f8c1e24a..b4df7cc0e3 100644 --- a/osu.Game/Database/IModelDownloader.cs +++ b/osu.Game/Database/IModelDownloader.cs @@ -23,7 +23,6 @@ namespace osu.Game.Database /// event Action> DownloadFailed; - /// /// Checks whether a given is already available in the local store. /// From d7bea3aa1837958244d48f028f5ca924174b803b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 15 Jun 2019 19:38:05 +0900 Subject: [PATCH 1131/2854] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 89c89faa5e..957d365724 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 46e508c910..9b146fa490 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 2e7922c3f9a98a985dbc3f9e2cb453a534b3f9e9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 15 Jun 2019 23:53:28 +0900 Subject: [PATCH 1132/2854] Fix 0-length sliders not getting correct lengths --- .../Objects/Legacy/Catch/ConvertHitObjectParser.cs | 3 ++- .../Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 9 +++++++-- .../Objects/Legacy/Mania/ConvertHitObjectParser.cs | 3 ++- .../Objects/Legacy/Osu/ConvertHitObjectParser.cs | 6 +++--- .../Objects/Legacy/Taiko/ConvertHitObjectParser.cs | 3 ++- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index 48f637dfe8..c968fe469a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -37,7 +37,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch }; } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount, + List> nodeSamples) { newCombo |= forceNewCombo; comboOffset += extraComboOffset; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index c14f3b6a42..36ae15efcc 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Objects.Legacy else if (type.HasFlag(ConvertHitObjectType.Slider)) { PathType pathType = PathType.Catmull; - double length = 0; + double? length = null; string[] pointSplit = split[5].Split('|'); @@ -130,7 +130,11 @@ namespace osu.Game.Rulesets.Objects.Legacy repeatCount = Math.Max(0, repeatCount - 1); if (split.Length > 7) + { length = Math.Max(0, Parsing.ParseDouble(split[7])); + if (length == 0) + length = null; + } if (split.Length > 10) readCustomSampleBanks(split[10], bankInfo); @@ -291,7 +295,8 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The slider repeat count. /// The samples to be played when the slider nodes are hit. This includes the head and tail of the slider. /// The hit object. - protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples); + protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount, + List> nodeSamples); /// /// Creates a legacy Spinner-type hit object. diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs index 8a3e232e60..5acc085ba1 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs @@ -26,7 +26,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania }; } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount, + List> nodeSamples) { return new ConvertSlider { diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index b98de32bd0..d46e1fa86a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osuTK; using osu.Game.Rulesets.Objects.Types; using System.Collections.Generic; @@ -38,7 +37,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu }; } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount, + List> nodeSamples) { newCombo |= forceNewCombo; comboOffset += extraComboOffset; @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu Position = position, NewCombo = FirstObject || newCombo, ComboOffset = comboOffset, - Path = new SliderPath(pathType, controlPoints, Math.Max(0, length)), + Path = new SliderPath(pathType, controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount }; diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs index bab21b31ad..39fb9e3b3a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs @@ -23,7 +23,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko return new ConvertHit(); } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount, + List> nodeSamples) { return new ConvertSlider { From d693b2a329b4b6af487c995e4c1ca47c5dd4667e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 16 Jun 2019 00:31:14 +0900 Subject: [PATCH 1133/2854] Fix multiplayer score submission failing silently --- osu.Game/Online/API/APIAccess.cs | 3 ++- osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 12b38fab1e..d722c7a98a 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -262,8 +262,9 @@ namespace osu.Game.Online.API handleWebException(we); return false; } - catch + catch (Exception ex) { + Logger.Error(ex, "Error occurred while handling an API request."); return false; } } diff --git a/osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs b/osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs index 1a2503f339..50b62cd6ed 100644 --- a/osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs +++ b/osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs @@ -30,7 +30,10 @@ namespace osu.Game.Online.API.Requests req.ContentType = "application/json"; req.Method = HttpMethod.Put; - req.AddRaw(JsonConvert.SerializeObject(scoreInfo)); + req.AddRaw(JsonConvert.SerializeObject(scoreInfo, new JsonSerializerSettings + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore + })); return req; } From 515534cb34c4433d09224988e54848105e343f99 Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Sun, 16 Jun 2019 00:02:26 +0700 Subject: [PATCH 1134/2854] Adding custom tooltip to DifficultyIcon --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 94 ++++++++++++++++++- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 0a0ad28fdf..22a3fd0c8a 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -6,17 +6,20 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; namespace osu.Game.Beatmaps.Drawables { - public class DifficultyIcon : DifficultyColouredContainer + public class DifficultyIcon : DifficultyColouredContainer, IHasCustomTooltip { private readonly RulesetInfo ruleset; @@ -27,10 +30,97 @@ namespace osu.Game.Beatmaps.Drawables throw new ArgumentNullException(nameof(beatmap)); this.ruleset = ruleset ?? beatmap.Ruleset; + TooltipText = $"{beatmap.Version}${beatmap.StarDifficulty.ToString("0.##")}"; Size = new Vector2(20); } + public string TooltipText { get; set; } + + public ITooltip GetCustomTooltip() => new DifficultyIconTooltip(); + + public class DifficultyIconTooltip : VisibilityContainer, ITooltip + { + private readonly OsuSpriteText difficultyName, starRating; + private readonly Box background; + + public string TooltipText { get; set; } + + public DifficultyIconTooltip() + { + AutoSizeAxes = Axes.Both; + Masking = true; + CornerRadius = 5; + + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Padding = new MarginPadding(10), + Children = new Drawable[] + { + difficultyName = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold), + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + starRating = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular), + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Margin = new MarginPadding { Left = 4 }, + Icon = FontAwesome.Solid.Star, + Size = new Vector2(12), + Colour = Color4.White, + }, + } + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.GreyCarmineDark; + } + + public void Refresh() + { + var info = TooltipText.Split('$'); + difficultyName.Text = info[0]; + starRating.Text = info[1]; + } + + public void Move(Vector2 pos) => this.Position = pos; + + protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); + + protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); + } + [BackgroundDependencyLoader] private void load() { From d735d4cd7f0ad19a8818bf1b60eb864ab05a5863 Mon Sep 17 00:00:00 2001 From: HoLLy Date: Sat, 15 Jun 2019 19:11:29 +0200 Subject: [PATCH 1135/2854] Fix crash on 2B hitobjects --- osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index daa3f61de3..8f10f51461 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -47,6 +47,7 @@ namespace osu.Game.Rulesets.Catch.Replays double speedRequired = positionChange / timeAvailable; bool dashRequired = speedRequired > movement_speed && h.StartTime != 0; + bool impossibleJump = speedRequired > movement_speed * 2 && h.StartTime != 0; // todo: get correct catcher size, based on difficulty CS. const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f; @@ -59,7 +60,7 @@ namespace osu.Game.Rulesets.Catch.Replays return; } - if (h is Banana) + if (h is Banana || impossibleJump) { // auto bananas unrealistically warp to catch 100% combo. Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); From f97cab58156cf8abda751dd8e8689f6e7da6028f Mon Sep 17 00:00:00 2001 From: HoLLy Date: Sat, 15 Jun 2019 19:14:24 +0200 Subject: [PATCH 1136/2854] Prevent speedRequired from being NaN --- osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index 8f10f51461..c85518717a 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Catch.Replays double timeAvailable = h.StartTime - lastTime; //So we can either make it there without a dash or not. - double speedRequired = positionChange / timeAvailable; + double speedRequired = positionChange == 0 ? 0 : positionChange / timeAvailable; bool dashRequired = speedRequired > movement_speed && h.StartTime != 0; bool impossibleJump = speedRequired > movement_speed * 2 && h.StartTime != 0; From 4d690a2b14b25c0e70bf345e14e252f19e659d54 Mon Sep 17 00:00:00 2001 From: HoLLy Date: Sat, 15 Jun 2019 19:21:00 +0200 Subject: [PATCH 1137/2854] Use normal movement for bananas when possible --- osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index c85518717a..1a7d837683 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -60,9 +60,8 @@ namespace osu.Game.Rulesets.Catch.Replays return; } - if (h is Banana || impossibleJump) + if (impossibleJump) { - // auto bananas unrealistically warp to catch 100% combo. Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); } else if (h.HyperDash) From 1f2f26a5038be22a0b92fecb728ce2d862d191b8 Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Sun, 16 Jun 2019 00:40:44 +0700 Subject: [PATCH 1138/2854] Add color to star rating and star sprite icon --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 22a3fd0c8a..350e450877 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -37,7 +37,7 @@ namespace osu.Game.Beatmaps.Drawables public string TooltipText { get; set; } - public ITooltip GetCustomTooltip() => new DifficultyIconTooltip(); + public ITooltip GetCustomTooltip() => new DifficultyIconTooltip(AccentColour); public class DifficultyIconTooltip : VisibilityContainer, ITooltip { @@ -46,7 +46,7 @@ namespace osu.Game.Beatmaps.Drawables public string TooltipText { get; set; } - public DifficultyIconTooltip() + public DifficultyIconTooltip(Color4 accentColour) { AutoSizeAxes = Axes.Both; Masking = true; @@ -84,6 +84,7 @@ namespace osu.Game.Beatmaps.Drawables Anchor = Anchor.Centre, Origin = Anchor.Centre, Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular), + Colour = accentColour }, new SpriteIcon { @@ -92,7 +93,7 @@ namespace osu.Game.Beatmaps.Drawables Margin = new MarginPadding { Left = 4 }, Icon = FontAwesome.Solid.Star, Size = new Vector2(12), - Colour = Color4.White, + Colour = accentColour, }, } } From c723ec5a9db37fff40f04670081d9731e34c0a5a Mon Sep 17 00:00:00 2001 From: HoLLy Date: Sat, 15 Jun 2019 19:40:56 +0200 Subject: [PATCH 1139/2854] Remove unneeded checks against h.StartTime --- osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index 1a7d837683..e7e9c5b003 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -46,8 +46,8 @@ namespace osu.Game.Rulesets.Catch.Replays //So we can either make it there without a dash or not. double speedRequired = positionChange == 0 ? 0 : positionChange / timeAvailable; - bool dashRequired = speedRequired > movement_speed && h.StartTime != 0; - bool impossibleJump = speedRequired > movement_speed * 2 && h.StartTime != 0; + bool dashRequired = speedRequired > movement_speed; + bool impossibleJump = speedRequired > movement_speed * 2; // todo: get correct catcher size, based on difficulty CS. const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f; From 1a9efb3d7a6a1bc96ab9e31511868927b7046b58 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sat, 15 Jun 2019 11:05:58 -0700 Subject: [PATCH 1140/2854] Fix editor play button --- osu.Game/Screens/Edit/Components/PlaybackControl.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/PlaybackControl.cs b/osu.Game/Screens/Edit/Components/PlaybackControl.cs index f5c9f74f62..8d4ad0efa9 100644 --- a/osu.Game/Screens/Edit/Components/PlaybackControl.cs +++ b/osu.Game/Screens/Edit/Components/PlaybackControl.cs @@ -36,12 +36,11 @@ namespace osu.Game.Screens.Edit.Components playButton = new IconButton { Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, + Origin = Anchor.CentreLeft, Scale = new Vector2(1.4f), IconScale = new Vector2(1.4f), Icon = FontAwesome.Regular.PlayCircle, Action = togglePause, - Padding = new MarginPadding { Left = 20 } }, new OsuSpriteText { From 53a7d919c769b729ad856972c844366e440248e1 Mon Sep 17 00:00:00 2001 From: HoLLy Date: Sat, 15 Jun 2019 22:41:10 +0200 Subject: [PATCH 1141/2854] Clarify speedRequired --- osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index e7e9c5b003..d418e7278a 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -43,7 +43,8 @@ namespace osu.Game.Rulesets.Catch.Replays float positionChange = Math.Abs(lastPosition - h.X); double timeAvailable = h.StartTime - lastTime; - //So we can either make it there without a dash or not. + // So we can either make it there without a dash or not. + // If positionChange is 0, we don't need to move so speedRequired should also be 0 (could be NaN if timeAvailable is 0 too) double speedRequired = positionChange == 0 ? 0 : positionChange / timeAvailable; bool dashRequired = speedRequired > movement_speed; From 1916fdd247b687f08fb5eefacf1e3a7608cc88af Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 16 Jun 2019 05:57:52 +0900 Subject: [PATCH 1142/2854] Add comment specifically about infinity being ok --- osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index d418e7278a..8dd00756f2 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -44,7 +44,8 @@ namespace osu.Game.Rulesets.Catch.Replays double timeAvailable = h.StartTime - lastTime; // So we can either make it there without a dash or not. - // If positionChange is 0, we don't need to move so speedRequired should also be 0 (could be NaN if timeAvailable is 0 too) + // If positionChange is 0, we don't need to move, so speedRequired should also be 0 (could be NaN if timeAvailable is 0 too) + // The case where positionChange > 0 and timeAvailable == 0 results in PositiveInfinity which provides expected beheaviour. double speedRequired = positionChange == 0 ? 0 : positionChange / timeAvailable; bool dashRequired = speedRequired > movement_speed; From 84b4e877f8fa443d79969c7c25bf89847781b50b Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sun, 16 Jun 2019 13:27:01 +0900 Subject: [PATCH 1143/2854] using FadeTo instead of show/hide headerLabel.FadeTo() is still remain because effect can be visible when expand== true --- osu.Game/Overlays/Mods/ModSection.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 1eaa3deea4..155e8ebb75 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -52,12 +52,8 @@ namespace osu.Game.Overlays.Mods buttons = modContainers.OfType().ToArray(); var expand = value.Any(); - if (expand) - Show(); - else - Hide(); - headerLabel.FadeTo(expand ? 1 : 0, 200); + this.FadeTo(expand ? 1 : 0); } } From bc35a30a257e3509fd360c43c55684dfc622b5f2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 17 Jun 2019 12:27:53 +0900 Subject: [PATCH 1144/2854] Fix audio being dimmed during multiplayer --- osu.Game/Screens/Multi/Multiplayer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 9e5c11e098..9939915e16 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -255,7 +255,6 @@ namespace osu.Game.Screens.Multi if (!track.IsRunning) { - game.Audio.AddItem(track); track.Seek(Beatmap.Value.Metadata.PreviewTime); track.Start(); } From c1d2fff651b090da3337f763324f77d516c9d47e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 17 Jun 2019 12:44:19 +0900 Subject: [PATCH 1145/2854] Use RestartPoint --- osu.Game/Screens/Multi/Multiplayer.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 9939915e16..c76f132395 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -251,13 +251,11 @@ namespace osu.Game.Screens.Multi if (track != null) { + track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; track.Looping = true; if (!track.IsRunning) - { - track.Seek(Beatmap.Value.Metadata.PreviewTime); - track.Start(); - } + track.Restart(); } createButton.Hide(); From 98f8b1d59a7b49813ebac070eda65421bddba92c Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 17 Jun 2019 06:44:24 +0300 Subject: [PATCH 1146/2854] Use ModelBackedDrawable in DrawableFlag --- osu.Game/Users/Country.cs | 44 +++++++++++++-------------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/osu.Game/Users/Country.cs b/osu.Game/Users/Country.cs index b513b460bc..a84641d1fd 100644 --- a/osu.Game/Users/Country.cs +++ b/osu.Game/Users/Country.cs @@ -27,53 +27,39 @@ namespace osu.Game.Users public string FlagName; } - public class DrawableFlag : Container, IHasTooltip + public class DrawableFlag : ModelBackedDrawable, IHasTooltip { - private readonly Sprite sprite; private TextureStore textures; - private Country country; + private readonly Country country; public Country Country { - get => country; - set - { - if (value == country) - return; - - country = value; - - if (LoadState >= LoadState.Ready) - sprite.Texture = getFlagTexture(); - } + get => Model; + set => Model = value; } - public string TooltipText => country?.FullName; + public string TooltipText => Country?.FullName; public DrawableFlag(Country country = null) { this.country = country; - - Children = new Drawable[] - { - sprite = new Sprite - { - RelativeSizeAxes = Axes.Both, - }, - }; } [BackgroundDependencyLoader] private void load(TextureStore ts) { - if (ts == null) - throw new ArgumentNullException(nameof(ts)); - - textures = ts; - sprite.Texture = getFlagTexture(); + textures = ts ?? throw new ArgumentNullException(nameof(ts)); + Country = country; } - private Texture getFlagTexture() => textures.Get($@"Flags/{country?.FlagName ?? @"__"}"); + protected override Drawable CreateDrawable(Country country) + { + return new Sprite + { + RelativeSizeAxes = Axes.Both, + Texture = textures.Get($@"Flags/{country?.FlagName ?? @"__"}"), + }; + } } } From 4cb9563af20b43a6a9e64c1631c1ac2bcdaebfc5 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 17 Jun 2019 07:16:38 +0300 Subject: [PATCH 1147/2854] Use ModelBackedDrawable in UpdateableAvatar --- osu.Game/Users/UpdateableAvatar.cs | 34 ++++++------------------------ 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/osu.Game/Users/UpdateableAvatar.cs b/osu.Game/Users/UpdateableAvatar.cs index 7259468674..8eb08f963d 100644 --- a/osu.Game/Users/UpdateableAvatar.cs +++ b/osu.Game/Users/UpdateableAvatar.cs @@ -10,12 +10,8 @@ namespace osu.Game.Users /// /// An avatar which can update to a new user when needed. /// - public class UpdateableAvatar : Container + public class UpdateableAvatar : ModelBackedDrawable { - private Drawable displayedAvatar; - - private User user; - /// /// Whether to show a default guest representation on null user (as opposed to nothing). /// @@ -23,17 +19,8 @@ namespace osu.Game.Users public User User { - get => user; - set - { - if (user?.Id == value?.Id) - return; - - user = value; - - if (IsLoaded) - updateAvatar(); - } + get => Model; + set => Model = value; } /// @@ -41,17 +28,8 @@ namespace osu.Game.Users /// public readonly BindableBool OpenOnClick = new BindableBool(true); - protected override void LoadComplete() + protected override Drawable CreateDrawable(User user) { - base.LoadComplete(); - updateAvatar(); - } - - private void updateAvatar() - { - displayedAvatar?.FadeOut(300); - displayedAvatar?.Expire(); - if (user != null || ShowGuestOnNull) { var avatar = new Avatar(user) @@ -62,8 +40,10 @@ namespace osu.Game.Users avatar.OnLoadComplete += d => d.FadeInFromZero(300, Easing.OutQuint); avatar.OpenOnClick.BindTo(OpenOnClick); - Add(displayedAvatar = new DelayedLoadWrapper(avatar)); + return avatar; } + + return null; } } } From 3087099b32b6d79bee36f126ebbbad09279d8e35 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 17 Jun 2019 07:34:35 +0300 Subject: [PATCH 1148/2854] Use ModelBackedDrawable in DrawableRank --- osu.Game/Online/Leaderboards/DrawableRank.cs | 64 +++++++++----------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/osu.Game/Online/Leaderboards/DrawableRank.cs b/osu.Game/Online/Leaderboards/DrawableRank.cs index ce64395dde..b7beb4ffb0 100644 --- a/osu.Game/Online/Leaderboards/DrawableRank.cs +++ b/osu.Game/Online/Leaderboards/DrawableRank.cs @@ -8,67 +8,59 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Scoring; +using System; namespace osu.Game.Online.Leaderboards { - public class DrawableRank : Container + public class DrawableRank : ModelBackedDrawable { - private readonly Sprite rankSprite; private TextureStore textures; - public ScoreRank Rank { get; private set; } + public ScoreRank Rank + { + get => Model; + set => Model = value; + } + + private ScoreRank rank; public DrawableRank(ScoreRank rank) { - Rank = rank; - - Children = new Drawable[] - { - rankSprite = new Sprite - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - FillMode = FillMode.Fit - }, - }; + this.rank = rank; } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(TextureStore ts) { - this.textures = textures; - updateTexture(); + textures = ts ?? throw new ArgumentNullException(nameof(ts)); + Rank = rank; } - private void updateTexture() + protected override Drawable CreateDrawable(ScoreRank rank) { - string textureName; + return new Sprite + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fit, + Texture = textures.Get($"Grades/{getTextureName()}"), + }; + } + private string getTextureName() + { switch (Rank) { default: - textureName = Rank.GetDescription(); - break; + return Rank.GetDescription(); case ScoreRank.SH: - textureName = "SPlus"; - break; + return "SPlus"; case ScoreRank.XH: - textureName = "SSPlus"; - break; + return "SSPlus"; } - - rankSprite.Texture = textures.Get($@"Grades/{textureName}"); - } - - public void UpdateRank(ScoreRank newRank) - { - Rank = newRank; - - if (LoadState >= LoadState.Ready) - updateTexture(); } } } From 84a0b948e134092237e2cd5c7668c34f3da75a5c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 Jun 2019 16:32:38 +0900 Subject: [PATCH 1149/2854] Fix typo in VersionNavigation class name --- osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs b/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs index 40f1b791f9..36407c7b0e 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs @@ -31,9 +31,9 @@ namespace osu.Game.Online.API.Requests.Responses public List ChangelogEntries { get; set; } [JsonProperty("versions")] - public VersionNatigation Versions { get; set; } + public VersionNavigation Versions { get; set; } - public class VersionNatigation + public class VersionNavigation { [JsonProperty("next")] public APIChangelogBuild Next { get; set; } From e58d2594986c0381afa0fe3773afd3569681f549 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 20:32:02 +0900 Subject: [PATCH 1150/2854] Create wireframe for team editor --- .../TestSceneTeamsEditorScreen.cs | 15 ++ .../Screens/Teams/TeamsEditorScreen.cs | 156 ++++++++++++++++++ .../Screens/TournamentSceneManager.cs | 5 +- 3 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tournament.Tests/TestSceneTeamsEditorScreen.cs create mode 100644 osu.Game.Tournament/Screens/Teams/TeamsEditorScreen.cs diff --git a/osu.Game.Tournament.Tests/TestSceneTeamsEditorScreen.cs b/osu.Game.Tournament.Tests/TestSceneTeamsEditorScreen.cs new file mode 100644 index 0000000000..d3268219b3 --- /dev/null +++ b/osu.Game.Tournament.Tests/TestSceneTeamsEditorScreen.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Tournament.Screens.Teams; + +namespace osu.Game.Tournament.Tests +{ + public class TestSceneTeamsEditorScreen : LadderTestScene + { + public TestSceneTeamsEditorScreen() + { + Add(new TeamsEditorScreen()); + } + } +} diff --git a/osu.Game.Tournament/Screens/Teams/TeamsEditorScreen.cs b/osu.Game.Tournament/Screens/Teams/TeamsEditorScreen.cs new file mode 100644 index 0000000000..b2392e5dc0 --- /dev/null +++ b/osu.Game.Tournament/Screens/Teams/TeamsEditorScreen.cs @@ -0,0 +1,156 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; +using osu.Game.Tournament.Components; +using osu.Game.Tournament.Screens.Ladder.Components; +using osuTK; + +namespace osu.Game.Tournament.Screens.Teams +{ + public class TeamsEditorScreen : TournamentScreen, IProvideVideo + { + private readonly FillFlowContainer items; + + public TeamsEditorScreen() + { + AddRangeInternal(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.2f), + }, + new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Width = 0.9f, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Child = items = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + }, + new ControlPanel + { + Children = new Drawable[] + { + new TriangleButton + { + RelativeSizeAxes = Axes.X, + Text = "Add new", + Action = addNew + }, + } + } + }); + } + + [BackgroundDependencyLoader] + private void load() + { + foreach (var g in LadderInfo.Teams) + items.Add(new TeamRow(g)); + } + + private void addNew() + { + var team = new TournamentTeam + { + StartDate = + { + Value = DateTimeOffset.UtcNow + } + }; + + items.Add(new TeamRow(team)); + LadderInfo.Teams.Add(team); + } + + public class TeamRow : CompositeDrawable + { + public readonly TournamentTeam Team; + + [Resolved] + private LadderInfo ladderInfo { get; set; } + + public TeamRow(TournamentTeam team) + { + Margin = new MarginPadding(10); + + Team = team; + InternalChildren = new Drawable[] + { + new Box + { + Colour = OsuColour.Gray(0.1f), + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + Margin = new MarginPadding(5), + Padding = new MarginPadding { Right = 160 }, + Spacing = new Vector2(5), + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new SettingsTextBox + { + LabelText = "Name", + Width = 0.33f, + Bindable = Team.Acronym + }, + new SettingsTextBox + { + LabelText = "Description", + Width = 0.33f, + Bindable = Team.Description + }, + new DateTextBox + { + LabelText = "Start Time", + Width = 0.33f, + Bindable = Team.StartDate + }, + new SettingsSlider + { + LabelText = "Best of", + Width = 0.33f, + Bindable = Team.BestOf + }, + } + }, + new DangerousSettingsButton + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.None, + Width = 150, + Text = "Delete", + Action = () => + { + Expire(); + ladderInfo.Teams.Remove(Team); + }, + } + }; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + } + } + } +} diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index 083f26240b..8393cd52a2 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -19,6 +19,7 @@ using osu.Game.Tournament.Screens.MapPool; using osu.Game.Tournament.Screens.Schedule; using osu.Game.Tournament.Screens.Showcase; using osu.Game.Tournament.Screens.TeamIntro; +using osu.Game.Tournament.Screens.Teams; using osu.Game.Tournament.Screens.TeamWin; using osuTK; using osuTK.Graphics; @@ -72,6 +73,7 @@ namespace osu.Game.Tournament.Screens new ScheduleScreen(), new LadderScreen(), new LadderEditorScreen(), + new TeamsEditorScreen(), new GroupingsEditorScreen(), new ShowcaseScreen(), new MapPoolScreen(), @@ -105,8 +107,9 @@ namespace osu.Game.Tournament.Screens Direction = FillDirection.Vertical, Children = new Drawable[] { - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket Editor", Action = () => SetScreen(typeof(LadderEditorScreen)) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Team Editor", Action = () => SetScreen(typeof(TeamsEditorScreen)) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Groupings Editor", Action = () => SetScreen(typeof(GroupingsEditorScreen)) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket Editor", Action = () => SetScreen(typeof(LadderEditorScreen)) }, new Container { RelativeSizeAxes = Axes.X, Height = 50 }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Drawings", Action = () => SetScreen(typeof(DrawingsScreen)) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Showcase", Action = () => SetScreen(typeof(ShowcaseScreen)) }, From 93fc14426bb4c3000164321c188c321c922b41a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 Jun 2019 16:28:58 +0900 Subject: [PATCH 1151/2854] Convert TournamentTeam props to use binadbles --- .../.idea/runConfigurations/VisualTests.xml | 7 ++- .../TestSceneMatchChatDisplay.cs | 6 +- .../TestSceneMatchPairings.cs | 10 +-- .../TestSceneTeamIntro.cs | 4 +- osu.Game.Tournament.Tests/TestSceneTeamWin.cs | 4 +- .../Components/DrawableTournamentTeam.cs | 2 +- .../Components/TournamentTeam.cs | 41 ++++++------ .../Screens/Drawings/Components/Group.cs | 6 +- .../Components/StorageBackedTeamList.cs | 6 +- .../Screens/Drawings/DrawingsScreen.cs | 6 +- .../Gameplay/Components/MatchHeader.cs | 2 +- .../Ladder/Components/LadderEditorSettings.cs | 8 +-- .../Screens/Ladder/Components/MatchPairing.cs | 4 +- .../Screens/TeamIntro/TeamIntroScreen.cs | 2 +- .../Screens/TeamWin/TeamWinScreen.cs | 2 +- .../Screens/Teams/TeamsEditorScreen.cs | 62 ++++++++++++------- osu.Game.Tournament/TournamentGameBase.cs | 10 +-- 17 files changed, 103 insertions(+), 79 deletions(-) diff --git a/.idea/.idea.osu/.idea/runConfigurations/VisualTests.xml b/.idea/.idea.osu/.idea/runConfigurations/VisualTests.xml index 95cb4c0e62..34a83af3a1 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/VisualTests.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/VisualTests.xml @@ -6,12 +6,15 @@ public readonly BindableBool OpenOnClick = new BindableBool(true); + public UpdateableAvatar(User user = null) + { + User = user; + } + protected override Drawable CreateDrawable(User user) { if (user == null && !ShowGuestOnNull) diff --git a/osu.Game/Users/Drawables/DrawableFlag.cs b/osu.Game/Users/Drawables/DrawableFlag.cs index 5a2d1185fe..6f259d3253 100644 --- a/osu.Game/Users/Drawables/DrawableFlag.cs +++ b/osu.Game/Users/Drawables/DrawableFlag.cs @@ -19,7 +19,10 @@ namespace osu.Game.Users.Drawables set => Model = value; } - public UpdateableFlag(Country country = null) => Country = country; + public UpdateableFlag(Country country = null) + { + Country = country; + } protected override Drawable CreateDrawable(Country country) => new DrawableFlag(country) { @@ -33,7 +36,10 @@ namespace osu.Game.Users.Drawables public string TooltipText => country?.FullName; - public DrawableFlag(Country country) => this.country = country; + public DrawableFlag(Country country) + { + this.country = country; + } [BackgroundDependencyLoader] private void load(TextureStore ts) From c95e3da3ec6b3f577ae5116f491701bd6275bfd2 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 18 Jun 2019 00:23:00 +0300 Subject: [PATCH 1169/2854] Implement a BindableRulesetSelector --- .../Toolbar/ToolbarRulesetSelector.cs | 2 +- osu.Game/Rulesets/BindableRulesetSelector.cs | 37 ++++++++++++++++++ osu.Game/Rulesets/RulesetSelector.cs | 39 +------------------ 3 files changed, 39 insertions(+), 39 deletions(-) create mode 100644 osu.Game/Rulesets/BindableRulesetSelector.cs diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index bd7ac13c9e..62abca06c6 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -17,7 +17,7 @@ using System.Linq; namespace osu.Game.Overlays.Toolbar { - public class ToolbarRulesetSelector : RulesetSelector + public class ToolbarRulesetSelector : BindableRulesetSelector { private const float padding = 10; private readonly Drawable modeButtonLine; diff --git a/osu.Game/Rulesets/BindableRulesetSelector.cs b/osu.Game/Rulesets/BindableRulesetSelector.cs new file mode 100644 index 0000000000..0da79af263 --- /dev/null +++ b/osu.Game/Rulesets/BindableRulesetSelector.cs @@ -0,0 +1,37 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; + +namespace osu.Game.Rulesets +{ + public abstract class BindableRulesetSelector : RulesetSelector + { + protected readonly Bindable GlobalRuleset = new Bindable(); + + [BackgroundDependencyLoader] + private void load(Bindable parentRuleset) + { + GlobalRuleset.BindTo(parentRuleset); + + GlobalRuleset.BindValueChanged(globalRulesetChanged); + Current.BindValueChanged(OnLocalRulesetChanged); + } + + private void globalRulesetChanged(ValueChangedEvent e) => OnGlobalRulesetChanged(e); + + protected virtual void OnGlobalRulesetChanged(ValueChangedEvent e) + { + Current.Value = e.NewValue; + } + + protected virtual void OnLocalRulesetChanged(ValueChangedEvent e) + { + if (!GlobalRuleset.Disabled) + { + GlobalRuleset.Value = e.NewValue; + } + } + } +} diff --git a/osu.Game/Rulesets/RulesetSelector.cs b/osu.Game/Rulesets/RulesetSelector.cs index e646c2676b..d7ffb866ab 100644 --- a/osu.Game/Rulesets/RulesetSelector.cs +++ b/osu.Game/Rulesets/RulesetSelector.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics.UserInterface; -using osu.Framework.Bindables; using osu.Framework.Allocation; namespace osu.Game.Rulesets @@ -10,54 +9,18 @@ namespace osu.Game.Rulesets public abstract class RulesetSelector : TabControl { protected RulesetStore AvaliableRulesets; - protected readonly Bindable GlobalRuleset = new Bindable(); protected override Dropdown CreateDropdown() => null; - /// - /// Whether we want to change a global ruleset when local one is changed. - /// - protected virtual bool AllowGlobalRulesetChange => true; - - /// - /// Whether we want to change a local ruleset when global one is changed. - /// /// - protected virtual bool AllowLocalRulesetChange => true; - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets, Bindable parentRuleset) + private void load(RulesetStore rulesets) { AvaliableRulesets = rulesets; - GlobalRuleset.BindTo(parentRuleset); foreach (var r in rulesets.AvailableRulesets) { AddItem(r); } - - GlobalRuleset.BindValueChanged(globalRulesetChanged); - Current.BindValueChanged(OnLocalRulesetChanged); - } - - private void globalRulesetChanged(ValueChangedEvent e) - { - if (AllowLocalRulesetChange) - { - OnGlobalRulesetChanged(e); - } - } - - protected virtual void OnGlobalRulesetChanged(ValueChangedEvent e) - { - Current.Value = e.NewValue; - } - - protected virtual void OnLocalRulesetChanged(ValueChangedEvent e) - { - if (!GlobalRuleset.Disabled && AllowGlobalRulesetChange) - { - GlobalRuleset.Value = e.NewValue; - } } } } From 413c2158e25a56728d9116aab22078453d364004 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 18 Jun 2019 01:11:05 +0300 Subject: [PATCH 1170/2854] Simplify bindables usage --- .../Toolbar/ToolbarRulesetSelector.cs | 12 +++++------ osu.Game/Rulesets/BindableRulesetSelector.cs | 21 +++++-------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index 62abca06c6..d89418b50c 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -22,10 +22,10 @@ namespace osu.Game.Overlays.Toolbar private const float padding = 10; private readonly Drawable modeButtonLine; - public override bool HandleNonPositionalInput => !GlobalRuleset.Disabled && base.HandleNonPositionalInput; - public override bool HandlePositionalInput => !GlobalRuleset.Disabled && base.HandlePositionalInput; + public override bool HandleNonPositionalInput => !Current.Disabled && base.HandleNonPositionalInput; + public override bool HandlePositionalInput => !Current.Disabled && base.HandlePositionalInput; - public override bool PropagatePositionalInputSubTree => !GlobalRuleset.Disabled && base.PropagatePositionalInputSubTree; + public override bool PropagatePositionalInputSubTree => !Current.Disabled && base.PropagatePositionalInputSubTree; private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300); @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.Toolbar } }); - GlobalRuleset.DisabledChanged += disabledChanged; + Current.DisabledChanged += disabledChanged; } protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer @@ -92,9 +92,9 @@ namespace osu.Game.Overlays.Toolbar private readonly Cached activeMode = new Cached(); - protected override void OnLocalRulesetChanged(ValueChangedEvent e) + protected override void OnRulesetChanged(ValueChangedEvent e) { - base.OnLocalRulesetChanged(e); + base.OnRulesetChanged(e); activeMode.Invalidate(); } diff --git a/osu.Game/Rulesets/BindableRulesetSelector.cs b/osu.Game/Rulesets/BindableRulesetSelector.cs index 0da79af263..8c9bbcea0d 100644 --- a/osu.Game/Rulesets/BindableRulesetSelector.cs +++ b/osu.Game/Rulesets/BindableRulesetSelector.cs @@ -8,29 +8,18 @@ namespace osu.Game.Rulesets { public abstract class BindableRulesetSelector : RulesetSelector { - protected readonly Bindable GlobalRuleset = new Bindable(); - [BackgroundDependencyLoader] private void load(Bindable parentRuleset) { - GlobalRuleset.BindTo(parentRuleset); - - GlobalRuleset.BindValueChanged(globalRulesetChanged); - Current.BindValueChanged(OnLocalRulesetChanged); + Current.BindTo(parentRuleset); + Current.BindValueChanged(OnRulesetChanged); } - private void globalRulesetChanged(ValueChangedEvent e) => OnGlobalRulesetChanged(e); - - protected virtual void OnGlobalRulesetChanged(ValueChangedEvent e) + protected virtual void OnRulesetChanged(ValueChangedEvent e) { - Current.Value = e.NewValue; - } - - protected virtual void OnLocalRulesetChanged(ValueChangedEvent e) - { - if (!GlobalRuleset.Disabled) + if (Current.Disabled) { - GlobalRuleset.Value = e.NewValue; + return; } } } From 796afc0bf963b487a9da2749a96927900fb60832 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 13:44:38 +0900 Subject: [PATCH 1171/2854] Rename and expand chat tests --- .../TestSceneGameplayScreen.cs | 2 +- ...=> TestSceneTournamentMatchChatDisplay.cs} | 47 ++++++++++++++----- ...splay.cs => TournamentMatchChatDisplay.cs} | 4 +- .../Screens/Gameplay/GameplayScreen.cs | 2 +- .../Screens/TournamentSceneManager.cs | 2 +- 5 files changed, 40 insertions(+), 17 deletions(-) rename osu.Game.Tournament.Tests/{TestSceneMatchChatDisplay.cs => TestSceneTournamentMatchChatDisplay.cs} (64%) rename osu.Game.Tournament/Components/{MatchChatDisplay.cs => TournamentMatchChatDisplay.cs} (96%) diff --git a/osu.Game.Tournament.Tests/TestSceneGameplayScreen.cs b/osu.Game.Tournament.Tests/TestSceneGameplayScreen.cs index 1b73c798fc..74d8615db0 100644 --- a/osu.Game.Tournament.Tests/TestSceneGameplayScreen.cs +++ b/osu.Game.Tournament.Tests/TestSceneGameplayScreen.cs @@ -11,7 +11,7 @@ namespace osu.Game.Tournament.Tests public class TestSceneGameplayScreen : OsuTestScene { [Cached] - private MatchChatDisplay chat = new MatchChatDisplay(); + private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay(); [BackgroundDependencyLoader] private void load() diff --git a/osu.Game.Tournament.Tests/TestSceneMatchChatDisplay.cs b/osu.Game.Tournament.Tests/TestSceneTournamentMatchChatDisplay.cs similarity index 64% rename from osu.Game.Tournament.Tests/TestSceneMatchChatDisplay.cs rename to osu.Game.Tournament.Tests/TestSceneTournamentMatchChatDisplay.cs index 4fccb2996b..8a3950bac3 100644 --- a/osu.Game.Tournament.Tests/TestSceneMatchChatDisplay.cs +++ b/osu.Game.Tournament.Tests/TestSceneTournamentMatchChatDisplay.cs @@ -10,13 +10,13 @@ using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Screens.Ladder.Components; using osu.Game.Users; -using osuTK; namespace osu.Game.Tournament.Tests { - public class TestSceneMatchChatDisplay : OsuTestScene + public class TestSceneTournamentMatchChatDisplay : OsuTestScene { private readonly Channel testChannel = new Channel(); + private readonly Channel testChannel2 = new Channel(); private readonly User admin = new User { @@ -43,15 +43,14 @@ namespace osu.Game.Tournament.Tests [Cached] private MatchIPCInfo matchInfo = new MatchIPCInfo(); // hide parent - public TestSceneMatchChatDisplay() - { - MatchChatDisplay chatDisplay; + private readonly TournamentMatchChatDisplay chatDisplay; - Add(chatDisplay = new MatchChatDisplay + public TestSceneTournamentMatchChatDisplay() + { + Add(chatDisplay = new TournamentMatchChatDisplay { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(400, 80) }); ladderInfo.CurrentMatch.Value = new MatchPairing @@ -73,35 +72,59 @@ namespace osu.Game.Tournament.Tests { base.LoadComplete(); - AddStep("message from admin", () => testChannel.AddLocalEcho(new LocalEchoMessage + AddStep("message from admin", () => testChannel.AddNewMessages(new Message(nextMessageId()) { Sender = admin, Content = "I am a wang!" })); - AddStep("message from team red", () => testChannel.AddLocalEcho(new LocalEchoMessage + AddStep("message from team red", () => testChannel.AddNewMessages(new Message(nextMessageId()) { Sender = redUser, Content = "I am team red." })); - AddStep("message from team red", () => testChannel.AddLocalEcho(new LocalEchoMessage + AddStep("message from team red", () => testChannel.AddNewMessages(new Message(nextMessageId()) { Sender = redUser, Content = "I plan to win!" })); - AddStep("message from team blue", () => testChannel.AddLocalEcho(new LocalEchoMessage + AddStep("message from team blue", () => testChannel.AddNewMessages(new Message(nextMessageId()) { Sender = blueUser, Content = "Not on my watch. Prepare to eat saaaaaaaaaand. Lots and lots of saaaaaaand." })); - AddStep("message from admin", () => testChannel.AddLocalEcho(new LocalEchoMessage + AddStep("message from admin", () => testChannel.AddNewMessages(new Message(nextMessageId()) { Sender = admin, Content = "Okay okay, calm down guys. Let's do this!" })); + + AddStep("multiple messages", () => testChannel.AddNewMessages(new Message(nextMessageId()) + { + Sender = admin, + Content = "I spam you!" + }, + new Message(nextMessageId()) + { + Sender = admin, + Content = "I spam you!!!1" + }, + new Message(nextMessageId()) + { + Sender = admin, + Content = "I spam you!1!1" + })); + + AddStep("change channel to 2", () => chatDisplay.Channel.Value = testChannel2); + + AddStep("change channel to 1", () => chatDisplay.Channel.Value = testChannel); } + + private int messageId; + + private long? nextMessageId() => messageId++; } } diff --git a/osu.Game.Tournament/Components/MatchChatDisplay.cs b/osu.Game.Tournament/Components/TournamentMatchChatDisplay.cs similarity index 96% rename from osu.Game.Tournament/Components/MatchChatDisplay.cs rename to osu.Game.Tournament/Components/TournamentMatchChatDisplay.cs index fbf6949540..2afbb0f5ff 100644 --- a/osu.Game.Tournament/Components/MatchChatDisplay.cs +++ b/osu.Game.Tournament/Components/TournamentMatchChatDisplay.cs @@ -13,13 +13,13 @@ using osuTK.Graphics; namespace osu.Game.Tournament.Components { - public class MatchChatDisplay : StandAloneChatDisplay + public class TournamentMatchChatDisplay : StandAloneChatDisplay { private readonly Bindable chatChannel = new Bindable(); private ChannelManager manager; - public MatchChatDisplay() + public TournamentMatchChatDisplay() { RelativeSizeAxes = Axes.X; Y = 100; diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index ca7d017bf3..fad1919510 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tournament.Screens.Gameplay private TournamentSceneManager sceneManager { get; set; } [Resolved] - private MatchChatDisplay chat { get; set; } + private TournamentMatchChatDisplay chat { get; set; } [BackgroundDependencyLoader] private void load(LadderInfo ladder, MatchIPCInfo ipc) diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index f1a2f2219b..2c620f4e56 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tournament.Screens private TourneyVideo video; [Cached] - private MatchChatDisplay chat = new MatchChatDisplay(); + private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay(); private Container chatContainer; From 40eb6f49860887d7aebb4828b4a81fbbb1ef07a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 14:14:36 +0900 Subject: [PATCH 1172/2854] Undo ordering change to OsuFont --- osu.Game/Graphics/OsuFont.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/OsuFont.cs b/osu.Game/Graphics/OsuFont.cs index 2c2f075563..22250d4a56 100644 --- a/osu.Game/Graphics/OsuFont.cs +++ b/osu.Game/Graphics/OsuFont.cs @@ -70,7 +70,7 @@ namespace osu.Game.Graphics string weightString = weight.ToString(); // Only exo has an explicit "regular" weight, other fonts do not - if (weight == FontWeight.Regular && family != GetFamilyString(Typeface.Exo)) + if (family != GetFamilyString(Typeface.Exo) && weight == FontWeight.Regular) weightString = string.Empty; return weightString; From 07ea0f9755268e163c282847f4f01b5106936c4f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 14:16:54 +0900 Subject: [PATCH 1173/2854] Make OsuButton non-abstract again --- osu.Game/Graphics/UserInterface/OsuButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index 494d4e4262..7a27f825f6 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -17,11 +17,11 @@ namespace osu.Game.Graphics.UserInterface /// /// A button with added default sound effects. /// - public abstract class OsuButton : Button + public class OsuButton : Button { private Box hover; - protected OsuButton() + public OsuButton() { Height = 40; From 5bb8649f3b3502707db16c9a5963d74b7f98039a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 14:22:59 +0900 Subject: [PATCH 1174/2854] Remove unused property from chat message --- osu.Game/Online/Chat/Message.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Online/Chat/Message.cs b/osu.Game/Online/Chat/Message.cs index 62f20daddf..2e41038a59 100644 --- a/osu.Game/Online/Chat/Message.cs +++ b/osu.Game/Online/Chat/Message.cs @@ -13,10 +13,6 @@ namespace osu.Game.Online.Chat [JsonProperty(@"message_id")] public readonly long? Id; - //todo: this should be inside sender. - [JsonProperty(@"sender_id")] - public long UserId; - [JsonProperty(@"channel_id")] public long ChannelId; From 6823ba1ab08ce335f08b7b5a82c8c715cce2e315 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 14:24:44 +0900 Subject: [PATCH 1175/2854] Unbind from previous bindable when rebinding a SettingsItem --- osu.Game/Overlays/Settings/SettingsItem.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 4776cd6442..ae840c8c00 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -63,6 +63,9 @@ namespace osu.Game.Overlays.Settings set { + if (bindable != null) + controlWithCurrent?.Current.UnbindFrom(bindable); + bindable = value; controlWithCurrent?.Current.BindTo(bindable); From 4f5abeb79f3093503db833c084f40e8016974219 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 14:44:15 +0900 Subject: [PATCH 1176/2854] Grouping -> Round --- .../TestSceneGroupingsEditorScreen.cs | 4 +- .../TestSceneTeamIntro.cs | 2 +- osu.Game.Tournament.Tests/TestSceneTeamWin.cs | 2 +- osu.Game.Tournament/IPC/FileBasedIPC.cs | 2 +- osu.Game.Tournament/LadderInfo.cs | 2 +- .../Gameplay/Components/MatchHeader.cs | 2 +- .../Ladder/Components/DrawableMatchPairing.cs | 10 ++--- ...Grouping.cs => DrawableTournamentRound.cs} | 12 +++--- .../Ladder/Components/LadderEditorSettings.cs | 40 +++++++++---------- .../Screens/Ladder/Components/MatchPairing.cs | 4 +- .../{GroupingBeatmap.cs => RoundBeatmap.cs} | 2 +- ...urnamentGrouping.cs => TournamentRound.cs} | 4 +- .../Screens/Ladder/LadderScreen.cs | 16 ++++---- .../Screens/MapPool/MapPoolScreen.cs | 6 +-- .../RoundEditorScreen.cs} | 38 +++++++++--------- .../Screens/Schedule/ScheduleScreen.cs | 2 +- .../Screens/TeamIntro/TeamIntroScreen.cs | 2 +- .../Screens/TeamWin/TeamWinScreen.cs | 2 +- .../Screens/Teams/TeamsEditorScreen.cs | 4 +- .../Screens/TournamentSceneManager.cs | 6 +-- osu.Game.Tournament/TournamentGameBase.cs | 22 +++++----- 21 files changed, 92 insertions(+), 92 deletions(-) rename osu.Game.Tournament/Screens/Ladder/Components/{DrawableTournamentGrouping.cs => DrawableTournamentRound.cs} (82%) rename osu.Game.Tournament/Screens/Ladder/Components/{GroupingBeatmap.cs => RoundBeatmap.cs} (91%) rename osu.Game.Tournament/Screens/Ladder/Components/{TournamentGrouping.cs => TournamentRound.cs} (87%) rename osu.Game.Tournament/Screens/{Groupings/GroupingsEditorScreen.cs => Rounds/RoundEditorScreen.cs} (80%) diff --git a/osu.Game.Tournament.Tests/TestSceneGroupingsEditorScreen.cs b/osu.Game.Tournament.Tests/TestSceneGroupingsEditorScreen.cs index 993a233960..0ef19c2948 100644 --- a/osu.Game.Tournament.Tests/TestSceneGroupingsEditorScreen.cs +++ b/osu.Game.Tournament.Tests/TestSceneGroupingsEditorScreen.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Tournament.Screens.Groupings; +using osu.Game.Tournament.Screens.Rounds; namespace osu.Game.Tournament.Tests { @@ -9,7 +9,7 @@ namespace osu.Game.Tournament.Tests { public TestSceneGroupingsEditorScreen() { - Add(new GroupingsEditorScreen()); + Add(new RoundEditorScreen()); } } } diff --git a/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs b/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs index 7d10923949..560a8f9521 100644 --- a/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs +++ b/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tournament.Tests var pairing = new MatchPairing(); pairing.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA"); pairing.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN"); - pairing.Grouping.Value = Ladder.Groupings.FirstOrDefault(g => g.Name.Value == "Finals"); + pairing.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals"); currentMatch.Value = pairing; Add(new TeamIntroScreen diff --git a/osu.Game.Tournament.Tests/TestSceneTeamWin.cs b/osu.Game.Tournament.Tests/TestSceneTeamWin.cs index 1ef81b7550..9f642103de 100644 --- a/osu.Game.Tournament.Tests/TestSceneTeamWin.cs +++ b/osu.Game.Tournament.Tests/TestSceneTeamWin.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tournament.Tests var pairing = new MatchPairing(); pairing.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA"); pairing.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN"); - pairing.Grouping.Value = Ladder.Groupings.FirstOrDefault(g => g.Name.Value == "Finals"); + pairing.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals"); currentMatch.Value = pairing; Add(new TeamWinScreen diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 8be10e2089..438e32c20f 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -61,7 +61,7 @@ namespace osu.Game.Tournament.IPC { lastBeatmapId = beatmapId; - var existing = ladder.CurrentMatch.Value?.Grouping.Value?.Beatmaps.FirstOrDefault(b => b.ID == beatmapId && b.BeatmapInfo != null); + var existing = ladder.CurrentMatch.Value?.Round.Value?.Beatmaps.FirstOrDefault(b => b.ID == beatmapId && b.BeatmapInfo != null); if (existing != null) Beatmap.Value = existing.BeatmapInfo; diff --git a/osu.Game.Tournament/LadderInfo.cs b/osu.Game.Tournament/LadderInfo.cs index 08ef5d9062..fc825d1a9c 100644 --- a/osu.Game.Tournament/LadderInfo.cs +++ b/osu.Game.Tournament/LadderInfo.cs @@ -12,7 +12,7 @@ namespace osu.Game.Tournament public class LadderInfo { public BindableList Pairings = new BindableList(); - public BindableList Groupings = new BindableList(); + public BindableList Rounds = new BindableList(); public BindableList Teams = new BindableList(); // only used for serialisation diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index c921967836..71cfacdc32 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -218,7 +218,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components Anchor = Anchor.Centre, Origin = Anchor.Centre, Colour = Color4.White, - Text = match.NewValue.Grouping.Value?.Name.Value ?? "Unknown Grouping", + Text = match.NewValue.Round.Value?.Name.Value ?? "Unknown Round", Font = TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Regular, size: 18), }, }; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index fcd7ed241a..35741cbb55 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -79,7 +79,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components boundReference(pairing.Team2).BindValueChanged(_ => updateTeams()); boundReference(pairing.Team1Score).BindValueChanged(_ => updateWinConditions()); boundReference(pairing.Team2Score).BindValueChanged(_ => updateWinConditions()); - boundReference(pairing.Grouping).BindValueChanged(_ => + boundReference(pairing.Round).BindValueChanged(_ => { updateWinConditions(); Changed?.Invoke(); @@ -210,12 +210,12 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private void updateWinConditions() { - if (Pairing.Grouping.Value == null) return; + if (Pairing.Round.Value == null) return; - var instaWinAmount = Pairing.Grouping.Value.BestOf.Value / 2; + var instaWinAmount = Pairing.Round.Value.BestOf.Value / 2; - Pairing.Completed.Value = Pairing.Grouping.Value.BestOf.Value > 0 - && (Pairing.Team1Score.Value + Pairing.Team2Score.Value >= Pairing.Grouping.Value.BestOf.Value || Pairing.Team1Score.Value > instaWinAmount || Pairing.Team2Score.Value > instaWinAmount); + Pairing.Completed.Value = Pairing.Round.Value.BestOf.Value > 0 + && (Pairing.Team1Score.Value + Pairing.Team2Score.Value >= Pairing.Round.Value.BestOf.Value || Pairing.Team1Score.Value > instaWinAmount || Pairing.Team2Score.Value > instaWinAmount); } protected override void LoadComplete() diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentRound.cs similarity index 82% rename from osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs rename to osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentRound.cs index a3a7bf4f64..844c89a968 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentRound.cs @@ -11,7 +11,7 @@ using osuTK.Graphics; namespace osu.Game.Tournament.Screens.Ladder.Components { - public class DrawableTournamentGrouping : CompositeDrawable + public class DrawableTournamentRound : CompositeDrawable { [UsedImplicitly] private readonly Bindable name; @@ -19,7 +19,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [UsedImplicitly] private readonly Bindable description; - public DrawableTournamentGrouping(TournamentGrouping grouping, bool losers = false) + public DrawableTournamentRound(TournamentRound round, bool losers = false) { OsuSpriteText textName; OsuSpriteText textDescription; @@ -47,11 +47,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components } }; - name = grouping.Name.GetBoundCopy(); - name.BindValueChanged(n => textName.Text = ((losers ? "Losers " : "") + grouping.Name).ToUpper(), true); + name = round.Name.GetBoundCopy(); + name.BindValueChanged(n => textName.Text = ((losers ? "Losers " : "") + round.Name).ToUpper(), true); - description = grouping.Name.GetBoundCopy(); - description.BindValueChanged(n => textDescription.Text = grouping.Description.Value.ToUpper(), true); + description = round.Name.GetBoundCopy(); + description.BindValueChanged(n => textDescription.Text = round.Description.Value.ToUpper(), true); } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index 4c280519df..a4b74f00a2 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components protected override string Title => @"ladder"; - private SettingsDropdown groupingDropdown; + private SettingsDropdown roundDropdown; private PlayerCheckbox losersCheckbox; private DateTextBox dateTimeBox; private SettingsTeamDropdown team1Dropdown; @@ -39,14 +39,14 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { team1Dropdown = new SettingsTeamDropdown(ladderInfo.Teams) { LabelText = "Team 1" }, team2Dropdown = new SettingsTeamDropdown(ladderInfo.Teams) { LabelText = "Team 2" }, - groupingDropdown = new SettingsGroupingDropdown(ladderInfo.Groupings) { LabelText = "Grouping" }, + roundDropdown = new SettingsRoundDropdown(ladderInfo.Rounds) { LabelText = "Round" }, losersCheckbox = new PlayerCheckbox { LabelText = "Losers Bracket" }, dateTimeBox = new DateTextBox { LabelText = "Match Time" }, }; editorInfo.Selected.ValueChanged += selection => { - groupingDropdown.Bindable = selection.NewValue?.Grouping; + roundDropdown.Bindable = selection.NewValue?.Round; losersCheckbox.Current = selection.NewValue?.Losers; dateTimeBox.Bindable = selection.NewValue?.Date; @@ -54,11 +54,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components team2Dropdown.Bindable = selection.NewValue?.Team2; }; - groupingDropdown.Bindable.ValueChanged += grouping => + roundDropdown.Bindable.ValueChanged += round => { - if (editorInfo.Selected.Value?.Date.Value < grouping.NewValue?.StartDate.Value) + if (editorInfo.Selected.Value?.Date.Value < round.NewValue?.StartDate.Value) { - editorInfo.Selected.Value.Date.Value = grouping.NewValue.StartDate.Value; + editorInfo.Selected.Value.Date.Value = round.NewValue.StartDate.Value; editorInfo.Selected.TriggerChange(); } }; @@ -79,17 +79,17 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { } - private class SettingsGroupingDropdown : SettingsDropdown + private class SettingsRoundDropdown : SettingsDropdown { - public SettingsGroupingDropdown(BindableList groupings) + public SettingsRoundDropdown(BindableList rounds) { - Bindable = new Bindable(); + Bindable = new Bindable(); - foreach (var g in groupings.Prepend(new TournamentGrouping())) - add(g); + foreach (var r in rounds.Prepend(new TournamentRound())) + add(r); - groupings.ItemsRemoved += items => items.ForEach(i => Control.RemoveDropdownItem(i)); - groupings.ItemsAdded += items => items.ForEach(add); + rounds.ItemsRemoved += items => items.ForEach(i => Control.RemoveDropdownItem(i)); + rounds.ItemsAdded += items => items.ForEach(add); } private readonly List refBindables = new List(); @@ -102,13 +102,13 @@ namespace osu.Game.Tournament.Screens.Ladder.Components return obj; } - private void add(TournamentGrouping grouping) + private void add(TournamentRound round) { - Control.AddDropdownItem(grouping); - boundReference(grouping.Name).BindValueChanged(_ => + Control.AddDropdownItem(round); + boundReference(round.Name).BindValueChanged(_ => { - Control.RemoveDropdownItem(grouping); - Control.AddDropdownItem(grouping); + Control.RemoveDropdownItem(round); + Control.AddDropdownItem(round); }); } } @@ -117,8 +117,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { public SettingsTeamDropdown(BindableList teams) { - foreach (var g in teams.Prepend(new TournamentTeam())) - add(g); + foreach (var t in teams.Prepend(new TournamentTeam())) + add(t); teams.ItemsRemoved += items => items.ForEach(i => Control.RemoveDropdownItem(i)); teams.ItemsAdded += items => items.ForEach(add); diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index f80263e41a..4ff2df1388 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -51,7 +51,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly ObservableCollection PicksBans = new ObservableCollection(); [JsonIgnore] - public readonly Bindable Grouping = new Bindable(); + public readonly Bindable Round = new Bindable(); [JsonIgnore] public readonly Bindable Progression = new Bindable(); @@ -90,7 +90,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [JsonIgnore] public TournamentTeam Loser => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team2.Value : Team1.Value; - public int PointsToWin => Grouping.Value?.BestOf.Value / 2 + 1 ?? 0; + public int PointsToWin => Round.Value?.BestOf.Value / 2 + 1 ?? 0; /// /// Remove scores from the match, in case of a false click or false start. diff --git a/osu.Game.Tournament/Screens/Ladder/Components/GroupingBeatmap.cs b/osu.Game.Tournament/Screens/Ladder/Components/RoundBeatmap.cs similarity index 91% rename from osu.Game.Tournament/Screens/Ladder/Components/GroupingBeatmap.cs rename to osu.Game.Tournament/Screens/Ladder/Components/RoundBeatmap.cs index b16ba13f65..ef608c3f06 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/GroupingBeatmap.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/RoundBeatmap.cs @@ -5,7 +5,7 @@ using osu.Game.Beatmaps; namespace osu.Game.Tournament.Screens.Ladder.Components { - public class GroupingBeatmap + public class RoundBeatmap { public int ID; public string Mods; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentRound.cs similarity index 87% rename from osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs rename to osu.Game.Tournament/Screens/Ladder/Components/TournamentRound.cs index d6e6b11f28..79b94e06a2 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentRound.cs @@ -9,7 +9,7 @@ using osu.Framework.Bindables; namespace osu.Game.Tournament.Screens.Ladder.Components { [Serializable] - public class TournamentGrouping + public class TournamentRound { public readonly Bindable Name = new Bindable(); public readonly Bindable Description = new Bindable(); @@ -17,7 +17,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly BindableInt BestOf = new BindableInt(9) { Default = 9, MinValue = 3, MaxValue = 23 }; [JsonProperty] - public readonly List Beatmaps = new List(); + public readonly List Beatmaps = new List(); public readonly Bindable StartDate = new Bindable(); diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs index 4a50db9997..6a77c6c20e 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -66,8 +66,8 @@ namespace osu.Game.Tournament.Screens.Ladder foreach (var pairing in LadderInfo.Pairings) addPairing(pairing); - LadderInfo.Groupings.ItemsAdded += _ => layout.Invalidate(); - LadderInfo.Groupings.ItemsRemoved += _ => layout.Invalidate(); + LadderInfo.Rounds.ItemsAdded += _ => layout.Invalidate(); + LadderInfo.Rounds.ItemsRemoved += _ => layout.Invalidate(); LadderInfo.Pairings.ItemsAdded += pairings => { @@ -138,13 +138,13 @@ namespace osu.Game.Tournament.Screens.Ladder } } - foreach (var group in LadderInfo.Groupings) + foreach (var round in LadderInfo.Rounds) { - var topPairing = PairingsContainer.Where(p => !p.Pairing.Losers.Value && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); + var topPairing = PairingsContainer.Where(p => !p.Pairing.Losers.Value && p.Pairing.Round.Value == round).OrderBy(p => p.Y).FirstOrDefault(); if (topPairing == null) continue; - headings.Add(new DrawableTournamentGrouping(group) + headings.Add(new DrawableTournamentRound(round) { Position = headings.ToLocalSpace((topPairing.ScreenSpaceDrawQuad.TopLeft + topPairing.ScreenSpaceDrawQuad.TopRight) / 2), Margin = new MarginPadding { Bottom = 10 }, @@ -152,13 +152,13 @@ namespace osu.Game.Tournament.Screens.Ladder }); } - foreach (var group in LadderInfo.Groupings) + foreach (var round in LadderInfo.Rounds) { - var topPairing = PairingsContainer.Where(p => p.Pairing.Losers.Value && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); + var topPairing = PairingsContainer.Where(p => p.Pairing.Losers.Value && p.Pairing.Round.Value == round).OrderBy(p => p.Y).FirstOrDefault(); if (topPairing == null) continue; - headings.Add(new DrawableTournamentGrouping(group, true) + headings.Add(new DrawableTournamentRound(round, true) { Position = headings.ToLocalSpace((topPairing.ScreenSpaceDrawQuad.TopLeft + topPairing.ScreenSpaceDrawQuad.TopRight) / 2), Margin = new MarginPadding { Bottom = 10 }, diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 47d5a36a4b..2c14dad38b 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -179,7 +179,7 @@ namespace osu.Game.Tournament.Screens.MapPool if (currentMatch.Value == null) return; - if (currentMatch.Value.Grouping.Value.Beatmaps.All(b => b.BeatmapInfo.OnlineBeatmapID != beatmapId)) + if (currentMatch.Value.Round.Value.Beatmaps.All(b => b.BeatmapInfo.OnlineBeatmapID != beatmapId)) // don't attempt to add if the beatmap isn't in our pool return; @@ -207,12 +207,12 @@ namespace osu.Game.Tournament.Screens.MapPool { mapFlows.Clear(); - if (match.NewValue.Grouping.Value != null) + if (match.NewValue.Round.Value != null) { FillFlowContainer currentFlow = null; string currentMod = null; - foreach (var b in match.NewValue.Grouping.Value.Beatmaps) + foreach (var b in match.NewValue.Round.Value.Beatmaps) { if (currentFlow == null || currentMod != b.Mods) { diff --git a/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs b/osu.Game.Tournament/Screens/Rounds/RoundEditorScreen.cs similarity index 80% rename from osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs rename to osu.Game.Tournament/Screens/Rounds/RoundEditorScreen.cs index 45dc769c19..69808032cc 100644 --- a/osu.Game.Tournament/Screens/Groupings/GroupingsEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Rounds/RoundEditorScreen.cs @@ -14,13 +14,13 @@ using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Ladder.Components; using osuTK; -namespace osu.Game.Tournament.Screens.Groupings +namespace osu.Game.Tournament.Screens.Rounds { - public class GroupingsEditorScreen : TournamentScreen, IProvideVideo + public class RoundEditorScreen : TournamentScreen, IProvideVideo { - private readonly FillFlowContainer items; + private readonly FillFlowContainer items; - public GroupingsEditorScreen() + public RoundEditorScreen() { AddRangeInternal(new Drawable[] { @@ -35,7 +35,7 @@ namespace osu.Game.Tournament.Screens.Groupings Width = 0.9f, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Child = items = new FillFlowContainer + Child = items = new FillFlowContainer { Direction = FillDirection.Vertical, RelativeSizeAxes = Axes.X, @@ -62,13 +62,13 @@ namespace osu.Game.Tournament.Screens.Groupings [BackgroundDependencyLoader] private void load() { - foreach (var g in LadderInfo.Groupings) - items.Add(new GroupingRow(g)); + foreach (var r in LadderInfo.Rounds) + items.Add(new RoundRow(r)); } private void addNew() { - var grouping = new TournamentGrouping + var round = new TournamentRound { StartDate = { @@ -76,22 +76,22 @@ namespace osu.Game.Tournament.Screens.Groupings } }; - items.Add(new GroupingRow(grouping)); - LadderInfo.Groupings.Add(grouping); + items.Add(new RoundRow(round)); + LadderInfo.Rounds.Add(round); } - public class GroupingRow : CompositeDrawable + public class RoundRow : CompositeDrawable { - public readonly TournamentGrouping Grouping; + public readonly TournamentRound Round; [Resolved] private LadderInfo ladderInfo { get; set; } - public GroupingRow(TournamentGrouping grouping) + public RoundRow(TournamentRound round) { Margin = new MarginPadding(10); - Grouping = grouping; + Round = round; InternalChildren = new Drawable[] { new Box @@ -113,25 +113,25 @@ namespace osu.Game.Tournament.Screens.Groupings { LabelText = "Name", Width = 0.33f, - Bindable = Grouping.Name + Bindable = Round.Name }, new SettingsTextBox { LabelText = "Description", Width = 0.33f, - Bindable = Grouping.Description + Bindable = Round.Description }, new DateTextBox { LabelText = "Start Time", Width = 0.33f, - Bindable = Grouping.StartDate + Bindable = Round.StartDate }, new SettingsSlider { LabelText = "Best of", Width = 0.33f, - Bindable = Grouping.BestOf + Bindable = Round.BestOf }, } }, @@ -145,7 +145,7 @@ namespace osu.Game.Tournament.Screens.Groupings Action = () => { Expire(); - ladderInfo.Groupings.Remove(Grouping); + ladderInfo.Rounds.Remove(Round); }, } }; diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index be934afe8e..956dd836bb 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -110,7 +110,7 @@ namespace osu.Game.Tournament.Screens.Schedule { Margin = new MarginPadding { Left = -10, Bottom = 10, Top = -5 }, Spacing = new Vector2(10, 0), - Text = match.NewValue.Grouping.Value?.Name.Value, + Text = match.NewValue.Round.Value?.Name.Value, Colour = Color4.Black, Font = OsuFont.GetFont(size: 20) }, diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index 078d823d81..3bb26499bf 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -112,7 +112,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Colour = col, - Text = pairing.Grouping.Value?.Name.Value ?? "Unknown Grouping", + Text = pairing.Round.Value?.Name.Value ?? "Unknown Round", Spacing = new Vector2(10, 0), Font = OsuFont.GetFont(size: 50, weight: FontWeight.Light) }, diff --git a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs index c80f3b2dfb..8360b17c39 100644 --- a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs +++ b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs @@ -133,7 +133,7 @@ namespace osu.Game.Tournament.Screens.TeamWin Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Colour = col, - Text = pairing.Grouping.Value?.Name.Value ?? "Unknown Grouping", + Text = pairing.Round.Value?.Name.Value ?? "Unknown Round", Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 50, FontWeight.Light), Spacing = new Vector2(10, 0), }, diff --git a/osu.Game.Tournament/Screens/Teams/TeamsEditorScreen.cs b/osu.Game.Tournament/Screens/Teams/TeamsEditorScreen.cs index 503e7468b0..8f536361c3 100644 --- a/osu.Game.Tournament/Screens/Teams/TeamsEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Teams/TeamsEditorScreen.cs @@ -66,8 +66,8 @@ namespace osu.Game.Tournament.Screens.Teams [BackgroundDependencyLoader] private void load() { - foreach (var g in LadderInfo.Teams) - items.Add(new TeamRow(g)); + foreach (var t in LadderInfo.Teams) + items.Add(new TeamRow(t)); } private void addNew() diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/Screens/TournamentSceneManager.cs index 2c620f4e56..720e216e96 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/Screens/TournamentSceneManager.cs @@ -12,9 +12,9 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Drawings; using osu.Game.Tournament.Screens.Gameplay; -using osu.Game.Tournament.Screens.Groupings; using osu.Game.Tournament.Screens.Ladder; using osu.Game.Tournament.Screens.MapPool; +using osu.Game.Tournament.Screens.Rounds; using osu.Game.Tournament.Screens.Schedule; using osu.Game.Tournament.Screens.Showcase; using osu.Game.Tournament.Screens.TeamIntro; @@ -72,7 +72,7 @@ namespace osu.Game.Tournament.Screens new LadderScreen(), new LadderEditorScreen(), new TeamsEditorScreen(), - new GroupingsEditorScreen(), + new RoundEditorScreen(), new ShowcaseScreen(), new MapPoolScreen(), new TeamIntroScreen(), @@ -106,7 +106,7 @@ namespace osu.Game.Tournament.Screens Children = new Drawable[] { new OsuButton { RelativeSizeAxes = Axes.X, Text = "Team Editor", Action = () => SetScreen(typeof(TeamsEditorScreen)) }, - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Groupings Editor", Action = () => SetScreen(typeof(GroupingsEditorScreen)) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Rounds Editor", Action = () => SetScreen(typeof(RoundEditorScreen)) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket Editor", Action = () => SetScreen(typeof(LadderEditorScreen)) }, new Container { RelativeSizeAxes = Axes.X, Height = 50 }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Drawings", Action = () => SetScreen(typeof(DrawingsScreen)) }, diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index e9e2d0f054..49e626d057 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -107,7 +107,7 @@ namespace osu.Game.Tournament { conditional.Team1.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == conditional.Team1Acronym); conditional.Team2.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == conditional.Team2Acronym); - conditional.Grouping.Value = pairing.Grouping.Value; + conditional.Round.Value = pairing.Round.Value; } } @@ -128,17 +128,17 @@ namespace osu.Game.Tournament } } - // link pairings to groupings - foreach (var group in ladder.Groupings) - foreach (var id in group.Pairings) + // link pairings to rounds + foreach (var round in ladder.Rounds) + foreach (var id in round.Pairings) { var found = ladder.Pairings.FirstOrDefault(p => p.ID == id); if (found != null) { - found.Grouping.Value = group; - if (group.StartDate.Value > found.Date.Value) - found.Date.Value = group.StartDate.Value; + found.Round.Value = round; + if (round.StartDate.Value > found.Date.Value) + found.Date.Value = round.StartDate.Value; } } @@ -179,8 +179,8 @@ namespace osu.Game.Tournament { bool addedInfo = false; - foreach (var g in ladder.Groupings) - foreach (var b in g.Beatmaps) + foreach (var r in ladder.Rounds) + foreach (var b in r.Beatmaps) if (b.BeatmapInfo == null) { var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); @@ -245,8 +245,8 @@ namespace osu.Game.Tournament protected virtual void SaveChanges() { - foreach (var g in ladder.Groupings) - g.Pairings = ladder.Pairings.Where(p => p.Grouping.Value == g).Select(p => p.ID).ToList(); + foreach (var r in ladder.Rounds) + r.Pairings = ladder.Pairings.Where(p => p.Round.Value == r).Select(p => p.ID).ToList(); ladder.Progressions = ladder.Pairings.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat( ladder.Pairings.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) From 96e24ebd20eec8a005fc469da3d868ea576dce41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 14:51:48 +0900 Subject: [PATCH 1177/2854] General namespace tidy-up --- osu.Game.Tournament.Tests/LadderTestScene.cs | 1 + .../TestSceneGroupingsEditorScreen.cs | 2 +- osu.Game.Tournament.Tests/TestSceneMatchPairings.cs | 1 + osu.Game.Tournament.Tests/TestSceneSceneManager.cs | 1 - osu.Game.Tournament.Tests/TestSceneTeamIntro.cs | 2 +- osu.Game.Tournament.Tests/TestSceneTeamWin.cs | 2 +- osu.Game.Tournament.Tests/TestSceneTeamsEditorScreen.cs | 2 +- .../TestSceneTournamentMatchChatDisplay.cs | 2 +- osu.Game.Tournament/Components/DrawableTournamentTeam.cs | 1 + osu.Game.Tournament/Components/TournamentBeatmapPanel.cs | 2 +- .../Components/TournamentMatchChatDisplay.cs | 1 + osu.Game.Tournament/IPC/FileBasedIPC.cs | 1 + .../{Screens/Ladder/Components => Models}/BeatmapChoice.cs | 2 +- .../Ladder/Components => Models}/LadderEditorInfo.cs | 2 +- osu.Game.Tournament/{ => Models}/LadderInfo.cs | 4 +--- .../{Screens/Ladder/Components => Models}/MatchPairing.cs | 4 ++-- .../{Screens/Ladder/Components => Models}/RoundBeatmap.cs | 2 +- .../Ladder/Components => Models}/TournamentProgression.cs | 2 +- .../Ladder/Components => Models}/TournamentRound.cs | 2 +- .../{Components => Models}/TournamentTeam.cs | 2 +- osu.Game.Tournament/Screens/Drawings/Components/Group.cs | 1 + .../Screens/Drawings/Components/GroupContainer.cs | 2 +- .../Screens/Drawings/Components/ITeamList.cs | 2 +- .../Screens/Drawings/Components/ScrollingTeamContainer.cs | 1 + .../Screens/Drawings/Components/StorageBackedTeamList.cs | 2 +- osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs | 1 + .../Screens/{Ladder => Editors}/LadderEditorScreen.cs | 4 +++- .../Screens/{Rounds => Editors}/RoundEditorScreen.cs | 4 ++-- .../Screens/{Teams => Editors}/TeamsEditorScreen.cs | 3 ++- .../Screens/Gameplay/Components/MatchHeader.cs | 2 +- .../Screens/Gameplay/Components/MatchScoreDisplay.cs | 1 + osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs | 2 +- osu.Game.Tournament/Screens/IProvideVideo.cs | 3 +++ .../Screens/Ladder/Components/ConditionalMatchPairing.cs | 2 ++ .../Screens/Ladder/Components/DrawableMatchPairing.cs | 2 +- .../Screens/Ladder/Components/DrawableMatchTeam.cs | 2 ++ .../Screens/Ladder/Components/DrawableTournamentRound.cs | 1 + .../Screens/Ladder/Components/LadderEditorSettings.cs | 1 + osu.Game.Tournament/Screens/Ladder/LadderScreen.cs | 2 ++ osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs | 2 +- osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs | 1 + osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs | 2 +- osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs | 2 +- osu.Game.Tournament/Screens/TournamentScreen.cs | 5 +++-- osu.Game.Tournament/TournamentGame.cs | 1 - osu.Game.Tournament/TournamentGameBase.cs | 3 +-- .../{Screens => }/TournamentSceneManager.cs | 7 ++++--- osu.Game.Tournament/osu.Game.Tournament.csproj | 3 +++ 48 files changed, 63 insertions(+), 39 deletions(-) rename osu.Game.Tournament/{Screens/Ladder/Components => Models}/BeatmapChoice.cs (92%) rename osu.Game.Tournament/{Screens/Ladder/Components => Models}/LadderEditorInfo.cs (85%) rename osu.Game.Tournament/{ => Models}/LadderInfo.cs (86%) rename osu.Game.Tournament/{Screens/Ladder/Components => Models}/MatchPairing.cs (97%) rename osu.Game.Tournament/{Screens/Ladder/Components => Models}/RoundBeatmap.cs (84%) rename osu.Game.Tournament/{Screens/Ladder/Components => Models}/TournamentProgression.cs (92%) rename osu.Game.Tournament/{Screens/Ladder/Components => Models}/TournamentRound.cs (94%) rename osu.Game.Tournament/{Components => Models}/TournamentTeam.cs (97%) rename osu.Game.Tournament/Screens/{Ladder => Editors}/LadderEditorScreen.cs (97%) rename osu.Game.Tournament/Screens/{Rounds => Editors}/RoundEditorScreen.cs (98%) rename osu.Game.Tournament/Screens/{Teams => Editors}/TeamsEditorScreen.cs (99%) rename osu.Game.Tournament/{Screens => }/TournamentSceneManager.cs (97%) diff --git a/osu.Game.Tournament.Tests/LadderTestScene.cs b/osu.Game.Tournament.Tests/LadderTestScene.cs index cb55f543f6..5bb8112157 100644 --- a/osu.Game.Tournament.Tests/LadderTestScene.cs +++ b/osu.Game.Tournament.Tests/LadderTestScene.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Game.Tests.Visual; +using osu.Game.Tournament.Models; namespace osu.Game.Tournament.Tests { diff --git a/osu.Game.Tournament.Tests/TestSceneGroupingsEditorScreen.cs b/osu.Game.Tournament.Tests/TestSceneGroupingsEditorScreen.cs index 0ef19c2948..e0a6f8e8b9 100644 --- a/osu.Game.Tournament.Tests/TestSceneGroupingsEditorScreen.cs +++ b/osu.Game.Tournament.Tests/TestSceneGroupingsEditorScreen.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Tournament.Screens.Rounds; +using osu.Game.Tournament.Screens.Editors; namespace osu.Game.Tournament.Tests { diff --git a/osu.Game.Tournament.Tests/TestSceneMatchPairings.cs b/osu.Game.Tournament.Tests/TestSceneMatchPairings.cs index 611e87717a..42b68d654c 100644 --- a/osu.Game.Tournament.Tests/TestSceneMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestSceneMatchPairings.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Tests.Visual; using osu.Game.Tournament.Components; +using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament.Tests diff --git a/osu.Game.Tournament.Tests/TestSceneSceneManager.cs b/osu.Game.Tournament.Tests/TestSceneSceneManager.cs index 385dc09d58..aa333e39b1 100644 --- a/osu.Game.Tournament.Tests/TestSceneSceneManager.cs +++ b/osu.Game.Tournament.Tests/TestSceneSceneManager.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Platform; using osu.Game.Tests.Visual; -using osu.Game.Tournament.Screens; namespace osu.Game.Tournament.Tests { diff --git a/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs b/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs index 560a8f9521..9f0d59fcbc 100644 --- a/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs +++ b/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs @@ -5,7 +5,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.TeamIntro; namespace osu.Game.Tournament.Tests diff --git a/osu.Game.Tournament.Tests/TestSceneTeamWin.cs b/osu.Game.Tournament.Tests/TestSceneTeamWin.cs index 9f642103de..8beeb50513 100644 --- a/osu.Game.Tournament.Tests/TestSceneTeamWin.cs +++ b/osu.Game.Tournament.Tests/TestSceneTeamWin.cs @@ -5,7 +5,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.TeamWin; namespace osu.Game.Tournament.Tests diff --git a/osu.Game.Tournament.Tests/TestSceneTeamsEditorScreen.cs b/osu.Game.Tournament.Tests/TestSceneTeamsEditorScreen.cs index d3268219b3..60323e1d84 100644 --- a/osu.Game.Tournament.Tests/TestSceneTeamsEditorScreen.cs +++ b/osu.Game.Tournament.Tests/TestSceneTeamsEditorScreen.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Tournament.Screens.Teams; +using osu.Game.Tournament.Screens.Editors; namespace osu.Game.Tournament.Tests { diff --git a/osu.Game.Tournament.Tests/TestSceneTournamentMatchChatDisplay.cs b/osu.Game.Tournament.Tests/TestSceneTournamentMatchChatDisplay.cs index 8a3950bac3..125bf5679c 100644 --- a/osu.Game.Tournament.Tests/TestSceneTournamentMatchChatDisplay.cs +++ b/osu.Game.Tournament.Tests/TestSceneTournamentMatchChatDisplay.cs @@ -8,7 +8,7 @@ using osu.Game.Online.Chat; using osu.Game.Tests.Visual; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; -using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Tournament.Models; using osu.Game.Users; namespace osu.Game.Tournament.Tests diff --git a/osu.Game.Tournament/Components/DrawableTournamentTeam.cs b/osu.Game.Tournament/Components/DrawableTournamentTeam.cs index 704d5a47de..361bd92770 100644 --- a/osu.Game.Tournament/Components/DrawableTournamentTeam.cs +++ b/osu.Game.Tournament/Components/DrawableTournamentTeam.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Tournament.Models; namespace osu.Game.Tournament.Components { diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index cf826ee2c7..aee7f914e7 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -16,7 +16,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Tournament.Models; using osuTK; using osuTK.Graphics; diff --git a/osu.Game.Tournament/Components/TournamentMatchChatDisplay.cs b/osu.Game.Tournament/Components/TournamentMatchChatDisplay.cs index 2afbb0f5ff..48c5b9bd35 100644 --- a/osu.Game.Tournament/Components/TournamentMatchChatDisplay.cs +++ b/osu.Game.Tournament/Components/TournamentMatchChatDisplay.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Game.Online.Chat; using osu.Game.Overlays.Chat; using osu.Game.Tournament.IPC; +using osu.Game.Tournament.Models; using osuTK; using osuTK.Graphics; diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 438e32c20f..23ebe2f39c 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -13,6 +13,7 @@ using osu.Game.Beatmaps.Legacy; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; +using osu.Game.Tournament.Models; namespace osu.Game.Tournament.IPC { diff --git a/osu.Game.Tournament/Screens/Ladder/Components/BeatmapChoice.cs b/osu.Game.Tournament/Models/BeatmapChoice.cs similarity index 92% rename from osu.Game.Tournament/Screens/Ladder/Components/BeatmapChoice.cs rename to osu.Game.Tournament/Models/BeatmapChoice.cs index bb9ed39b82..c22077553b 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/BeatmapChoice.cs +++ b/osu.Game.Tournament/Models/BeatmapChoice.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace osu.Game.Tournament.Screens.Ladder.Components +namespace osu.Game.Tournament.Models { public class BeatmapChoice { diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs b/osu.Game.Tournament/Models/LadderEditorInfo.cs similarity index 85% rename from osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs rename to osu.Game.Tournament/Models/LadderEditorInfo.cs index d6b5d172de..9bf01e76f5 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorInfo.cs +++ b/osu.Game.Tournament/Models/LadderEditorInfo.cs @@ -3,7 +3,7 @@ using osu.Framework.Bindables; -namespace osu.Game.Tournament.Screens.Ladder.Components +namespace osu.Game.Tournament.Models { public class LadderEditorInfo { diff --git a/osu.Game.Tournament/LadderInfo.cs b/osu.Game.Tournament/Models/LadderInfo.cs similarity index 86% rename from osu.Game.Tournament/LadderInfo.cs rename to osu.Game.Tournament/Models/LadderInfo.cs index fc825d1a9c..2fdf1b06b1 100644 --- a/osu.Game.Tournament/LadderInfo.cs +++ b/osu.Game.Tournament/Models/LadderInfo.cs @@ -4,10 +4,8 @@ using System.Collections.Generic; using Newtonsoft.Json; using osu.Framework.Bindables; -using osu.Game.Tournament.Components; -using osu.Game.Tournament.Screens.Ladder.Components; -namespace osu.Game.Tournament +namespace osu.Game.Tournament.Models { public class LadderInfo { diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Models/MatchPairing.cs similarity index 97% rename from osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs rename to osu.Game.Tournament/Models/MatchPairing.cs index 4ff2df1388..caafe8af47 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Models/MatchPairing.cs @@ -6,10 +6,10 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using Newtonsoft.Json; using osu.Framework.Bindables; -using osu.Game.Tournament.Components; +using osu.Game.Tournament.Screens.Ladder.Components; using SixLabors.Primitives; -namespace osu.Game.Tournament.Screens.Ladder.Components +namespace osu.Game.Tournament.Models { /// /// A collection of two teams competing in a head-to-head match. diff --git a/osu.Game.Tournament/Screens/Ladder/Components/RoundBeatmap.cs b/osu.Game.Tournament/Models/RoundBeatmap.cs similarity index 84% rename from osu.Game.Tournament/Screens/Ladder/Components/RoundBeatmap.cs rename to osu.Game.Tournament/Models/RoundBeatmap.cs index ef608c3f06..5d43d0ca66 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/RoundBeatmap.cs +++ b/osu.Game.Tournament/Models/RoundBeatmap.cs @@ -3,7 +3,7 @@ using osu.Game.Beatmaps; -namespace osu.Game.Tournament.Screens.Ladder.Components +namespace osu.Game.Tournament.Models { public class RoundBeatmap { diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs b/osu.Game.Tournament/Models/TournamentProgression.cs similarity index 92% rename from osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs rename to osu.Game.Tournament/Models/TournamentProgression.cs index 0019dc8d79..4ef4be599d 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs +++ b/osu.Game.Tournament/Models/TournamentProgression.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -namespace osu.Game.Tournament.Screens.Ladder.Components +namespace osu.Game.Tournament.Models { public class TournamentProgression { diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentRound.cs b/osu.Game.Tournament/Models/TournamentRound.cs similarity index 94% rename from osu.Game.Tournament/Screens/Ladder/Components/TournamentRound.cs rename to osu.Game.Tournament/Models/TournamentRound.cs index 79b94e06a2..6fe4b20fa5 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentRound.cs +++ b/osu.Game.Tournament/Models/TournamentRound.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using Newtonsoft.Json; using osu.Framework.Bindables; -namespace osu.Game.Tournament.Screens.Ladder.Components +namespace osu.Game.Tournament.Models { [Serializable] public class TournamentRound diff --git a/osu.Game.Tournament/Components/TournamentTeam.cs b/osu.Game.Tournament/Models/TournamentTeam.cs similarity index 97% rename from osu.Game.Tournament/Components/TournamentTeam.cs rename to osu.Game.Tournament/Models/TournamentTeam.cs index 043dcc7084..eea1ef8104 100644 --- a/osu.Game.Tournament/Components/TournamentTeam.cs +++ b/osu.Game.Tournament/Models/TournamentTeam.cs @@ -6,7 +6,7 @@ using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Game.Users; -namespace osu.Game.Tournament.Components +namespace osu.Game.Tournament.Models { [Serializable] public class TournamentTeam diff --git a/osu.Game.Tournament/Screens/Drawings/Components/Group.cs b/osu.Game.Tournament/Screens/Drawings/Components/Group.cs index adeead277c..549ff26018 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/Group.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/Group.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Tournament.Components; +using osu.Game.Tournament.Models; using osuTK; using osuTK.Graphics; diff --git a/osu.Game.Tournament/Screens/Drawings/Components/GroupContainer.cs b/osu.Game.Tournament/Screens/Drawings/Components/GroupContainer.cs index 6e56dca2fb..8a66ca7bf6 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/GroupContainer.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/GroupContainer.cs @@ -7,7 +7,7 @@ using System.Linq; using System.Text; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Tournament.Components; +using osu.Game.Tournament.Models; using osuTK; namespace osu.Game.Tournament.Screens.Drawings.Components diff --git a/osu.Game.Tournament/Screens/Drawings/Components/ITeamList.cs b/osu.Game.Tournament/Screens/Drawings/Components/ITeamList.cs index a532f47176..09208818a9 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/ITeamList.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/ITeamList.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Tournament.Components; +using osu.Game.Tournament.Models; namespace osu.Game.Tournament.Screens.Drawings.Components { diff --git a/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs b/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs index 486ce7fe74..b147d680f0 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Tournament.Components; +using osu.Game.Tournament.Models; using osuTK; using osuTK.Graphics; diff --git a/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs b/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs index ca3536965f..f96ec01cbb 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.IO; using osu.Framework.Logging; using osu.Framework.Platform; -using osu.Game.Tournament.Components; +using osu.Game.Tournament.Models; namespace osu.Game.Tournament.Screens.Drawings.Components { diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index 2ef7f513b6..52ba73c27c 100644 --- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -17,6 +17,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; +using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.Drawings.Components; using osuTK; using osuTK.Graphics; diff --git a/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/LadderEditorScreen.cs similarity index 97% rename from osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs rename to osu.Game.Tournament/Screens/Editors/LadderEditorScreen.cs index fc98753e0a..9b298df91d 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/LadderEditorScreen.cs @@ -11,12 +11,14 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Framework.Input.States; using osu.Game.Graphics.UserInterface; +using osu.Game.Tournament.Models; +using osu.Game.Tournament.Screens.Ladder; using osu.Game.Tournament.Screens.Ladder.Components; using osuTK; using osuTK.Graphics; using SixLabors.Primitives; -namespace osu.Game.Tournament.Screens.Ladder +namespace osu.Game.Tournament.Screens.Editors { [Cached] public class LadderEditorScreen : LadderScreen, IHasContextMenu diff --git a/osu.Game.Tournament/Screens/Rounds/RoundEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs similarity index 98% rename from osu.Game.Tournament/Screens/Rounds/RoundEditorScreen.cs rename to osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs index 69808032cc..c8c2461b10 100644 --- a/osu.Game.Tournament/Screens/Rounds/RoundEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs @@ -11,10 +11,10 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; using osu.Game.Tournament.Components; -using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Tournament.Models; using osuTK; -namespace osu.Game.Tournament.Screens.Rounds +namespace osu.Game.Tournament.Screens.Editors { public class RoundEditorScreen : TournamentScreen, IProvideVideo { diff --git a/osu.Game.Tournament/Screens/Teams/TeamsEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TeamsEditorScreen.cs similarity index 99% rename from osu.Game.Tournament/Screens/Teams/TeamsEditorScreen.cs rename to osu.Game.Tournament/Screens/Editors/TeamsEditorScreen.cs index 8f536361c3..1bf3f1c6b9 100644 --- a/osu.Game.Tournament/Screens/Teams/TeamsEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/TeamsEditorScreen.cs @@ -14,10 +14,11 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Settings; using osu.Game.Tournament.Components; +using osu.Game.Tournament.Models; using osu.Game.Users; using osuTK; -namespace osu.Game.Tournament.Screens.Teams +namespace osu.Game.Tournament.Screens.Editors { public class TeamsEditorScreen : TournamentScreen, IProvideVideo { diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index 71cfacdc32..e75872cda4 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; -using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.Showcase; using osuTK; using osuTK.Graphics; diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs index fc28ddccfd..78455c8bb7 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.IPC; +using osu.Game.Tournament.Models; using osuTK.Graphics; namespace osu.Game.Tournament.Screens.Gameplay.Components diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index fad1919510..7b108731f3 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -10,8 +10,8 @@ using osu.Framework.Threading; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; +using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.Gameplay.Components; -using osu.Game.Tournament.Screens.Ladder.Components; using osu.Game.Tournament.Screens.MapPool; using osu.Game.Tournament.Screens.TeamWin; using osuTK; diff --git a/osu.Game.Tournament/Screens/IProvideVideo.cs b/osu.Game.Tournament/Screens/IProvideVideo.cs index c11c921412..61989d8448 100644 --- a/osu.Game.Tournament/Screens/IProvideVideo.cs +++ b/osu.Game.Tournament/Screens/IProvideVideo.cs @@ -3,6 +3,9 @@ namespace osu.Game.Tournament.Screens { + /// + /// Marker interface for a screen which provides its own local video background. + /// public interface IProvideVideo { } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ConditionalMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/ConditionalMatchPairing.cs index 7831cac84d..f3b5678c7c 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/ConditionalMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/ConditionalMatchPairing.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Tournament.Models; + namespace osu.Game.Tournament.Screens.Ladder.Components { /// diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 35741cbb55..e48155ab0e 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Game.Tournament.Components; +using osu.Game.Tournament.Models; using osuTK; using osuTK.Graphics; using osuTK.Input; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index 6d5ac74267..5514dfce3e 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -14,6 +14,8 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; +using osu.Game.Tournament.Models; +using osu.Game.Tournament.Screens.Editors; using osuTK; using osuTK.Graphics; using osuTK.Input; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentRound.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentRound.cs index 844c89a968..67d6bc4fa6 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentRound.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentRound.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Tournament.Models; using osuTK.Graphics; namespace osu.Game.Tournament.Screens.Ladder.Components diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index a4b74f00a2..d947215cfa 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -11,6 +11,7 @@ using osu.Framework.Input.Events; using osu.Game.Overlays.Settings; using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Tournament.Components; +using osu.Game.Tournament.Models; namespace osu.Game.Tournament.Screens.Ladder.Components { diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs index 6a77c6c20e..50675a6147 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -11,6 +11,8 @@ using osu.Framework.Graphics.Lines; using osu.Framework.Platform; using osu.Game.Graphics; using osu.Game.Tournament.Components; +using osu.Game.Tournament.Models; +using osu.Game.Tournament.Screens.Editors; using osu.Game.Tournament.Screens.Ladder.Components; using osuTK.Graphics; diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 2c14dad38b..ab03adbce9 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -13,9 +13,9 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; +using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.Gameplay; using osu.Game.Tournament.Screens.Gameplay.Components; -using osu.Game.Tournament.Screens.Ladder.Components; using osuTK; using osuTK.Graphics; using osuTK.Input; diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index 956dd836bb..457fb80141 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -12,6 +12,7 @@ using osu.Framework.Platform; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Tournament.Components; +using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.Ladder.Components; using osuTK; using osuTK.Graphics; diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index 3bb26499bf..4e0bb23a62 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -9,7 +9,7 @@ using osu.Framework.Platform; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Tournament.Components; -using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.Showcase; using osuTK; using osuTK.Graphics; diff --git a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs index 8360b17c39..0a37cf6c4a 100644 --- a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs +++ b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs @@ -9,7 +9,7 @@ using osu.Framework.Platform; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Tournament.Components; -using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.Showcase; using osuTK; using osuTK.Graphics; diff --git a/osu.Game.Tournament/Screens/TournamentScreen.cs b/osu.Game.Tournament/Screens/TournamentScreen.cs index 7f6c5f8e18..9d58ca2240 100644 --- a/osu.Game.Tournament/Screens/TournamentScreen.cs +++ b/osu.Game.Tournament/Screens/TournamentScreen.cs @@ -4,15 +4,16 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Tournament.Models; namespace osu.Game.Tournament.Screens { - public class TournamentScreen : CompositeDrawable + public abstract class TournamentScreen : CompositeDrawable { [Resolved] protected LadderInfo LadderInfo { get; private set; } - public TournamentScreen() + protected TournamentScreen() { RelativeSizeAxes = Axes.Both; } diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index c8c462a458..42e4ab3c13 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -5,7 +5,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Cursor; -using osu.Game.Tournament.Screens; namespace osu.Game.Tournament { diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 49e626d057..e35e0b0d30 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -19,9 +19,8 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; -using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; -using osu.Game.Tournament.Screens.Ladder.Components; +using osu.Game.Tournament.Models; using osuTK.Input; namespace osu.Game.Tournament diff --git a/osu.Game.Tournament/Screens/TournamentSceneManager.cs b/osu.Game.Tournament/TournamentSceneManager.cs similarity index 97% rename from osu.Game.Tournament/Screens/TournamentSceneManager.cs rename to osu.Game.Tournament/TournamentSceneManager.cs index 720e216e96..29f8eba579 100644 --- a/osu.Game.Tournament/Screens/TournamentSceneManager.cs +++ b/osu.Game.Tournament/TournamentSceneManager.cs @@ -10,20 +10,21 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Platform; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.Components; +using osu.Game.Tournament.Models; +using osu.Game.Tournament.Screens; using osu.Game.Tournament.Screens.Drawings; +using osu.Game.Tournament.Screens.Editors; using osu.Game.Tournament.Screens.Gameplay; using osu.Game.Tournament.Screens.Ladder; using osu.Game.Tournament.Screens.MapPool; -using osu.Game.Tournament.Screens.Rounds; using osu.Game.Tournament.Screens.Schedule; using osu.Game.Tournament.Screens.Showcase; using osu.Game.Tournament.Screens.TeamIntro; -using osu.Game.Tournament.Screens.Teams; using osu.Game.Tournament.Screens.TeamWin; using osuTK; using osuTK.Graphics; -namespace osu.Game.Tournament.Screens +namespace osu.Game.Tournament { [Cached] public class TournamentSceneManager : CompositeDrawable diff --git a/osu.Game.Tournament/osu.Game.Tournament.csproj b/osu.Game.Tournament/osu.Game.Tournament.csproj index 8adff80820..8412166250 100644 --- a/osu.Game.Tournament/osu.Game.Tournament.csproj +++ b/osu.Game.Tournament/osu.Game.Tournament.csproj @@ -10,4 +10,7 @@ + + + \ No newline at end of file From da20904a57bb48dc8b1bd3d20a6c0b857967fa36 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 14:57:05 +0900 Subject: [PATCH 1178/2854] MatchPairing -> TournamentMatch --- .../TestSceneMatchPairings.cs | 50 ++++----- .../TestSceneTeamIntro.cs | 12 +-- osu.Game.Tournament.Tests/TestSceneTeamWin.cs | 12 +-- .../TestSceneTournamentMatchChatDisplay.cs | 2 +- .../Components/TournamentBeatmapPanel.cs | 10 +- .../Models/LadderEditorInfo.cs | 2 +- osu.Game.Tournament/Models/LadderInfo.cs | 4 +- .../{MatchPairing.cs => TournamentMatch.cs} | 12 +-- osu.Game.Tournament/Models/TournamentRound.cs | 2 +- .../Screens/Editors/LadderEditorScreen.cs | 36 +++---- .../Gameplay/Components/MatchHeader.cs | 8 +- .../Screens/Gameplay/GameplayScreen.cs | 2 +- ...iring.cs => ConditionalTournamentMatch.cs} | 4 +- .../Ladder/Components/DrawableMatchPairing.cs | 102 +++++++++--------- .../Ladder/Components/DrawableMatchTeam.cs | 34 +++--- .../Ladder/Components/ProgressionPath.cs | 6 +- .../Screens/Ladder/LadderScreen.cs | 56 +++++----- .../Screens/MapPool/MapPoolScreen.cs | 4 +- .../Screens/Schedule/ScheduleScreen.cs | 30 +++--- .../Screens/TeamIntro/TeamIntroScreen.cs | 18 ++-- .../Screens/TeamWin/TeamWinScreen.cs | 22 ++-- osu.Game.Tournament/TournamentGameBase.cs | 28 ++--- 22 files changed, 228 insertions(+), 228 deletions(-) rename osu.Game.Tournament/Models/{MatchPairing.cs => TournamentMatch.cs} (88%) rename osu.Game.Tournament/Screens/Ladder/Components/{ConditionalMatchPairing.cs => ConditionalTournamentMatch.cs} (72%) diff --git a/osu.Game.Tournament.Tests/TestSceneMatchPairings.cs b/osu.Game.Tournament.Tests/TestSceneMatchPairings.cs index 42b68d654c..9c8ee2965e 100644 --- a/osu.Game.Tournament.Tests/TestSceneMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestSceneMatchPairings.cs @@ -12,22 +12,22 @@ using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament.Tests { - public class TestSceneMatchPairings : OsuTestScene + public class TestSceneMatches : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { - typeof(MatchPairing), - typeof(DrawableMatchPairing), + typeof(TournamentMatch), + typeof(DrawableTournamentMatch), typeof(DrawableMatchTeam), typeof(DrawableTournamentTeam), }; - public TestSceneMatchPairings() + public TestSceneMatches() { - Container level1; - Container level2; + Container level1; + Container level2; - var pairing1 = new MatchPairing( + var match1 = new TournamentMatch( new TournamentTeam { FlagName = { Value = "AU" }, FullName = { Value = "Australia" }, }, new TournamentTeam { FlagName = { Value = "JP" }, FullName = { Value = "Japan" }, Acronym = { Value = "JPN" } }) { @@ -35,7 +35,7 @@ namespace osu.Game.Tournament.Tests Team2Score = { Value = 1 }, }; - var pairing2 = new MatchPairing( + var match2 = new TournamentMatch( new TournamentTeam { FlagName = { Value = "RO" }, @@ -49,47 +49,47 @@ namespace osu.Game.Tournament.Tests Direction = FillDirection.Horizontal, Children = new Drawable[] { - level1 = new FillFlowContainer + level1 = new FillFlowContainer { AutoSizeAxes = Axes.X, Direction = FillDirection.Vertical, Children = new[] { - new DrawableMatchPairing(pairing1), - new DrawableMatchPairing(pairing2), - new DrawableMatchPairing(new MatchPairing()), + new DrawableTournamentMatch(match1), + new DrawableTournamentMatch(match2), + new DrawableTournamentMatch(new TournamentMatch()), } }, - level2 = new FillFlowContainer + level2 = new FillFlowContainer { AutoSizeAxes = Axes.X, Direction = FillDirection.Vertical, Margin = new MarginPadding(20), Children = new[] { - new DrawableMatchPairing(new MatchPairing()), - new DrawableMatchPairing(new MatchPairing()) + new DrawableTournamentMatch(new TournamentMatch()), + new DrawableTournamentMatch(new TournamentMatch()) } } } }; - level1.Children[0].Pairing.Progression.Value = level2.Children[0].Pairing; - level1.Children[1].Pairing.Progression.Value = level2.Children[0].Pairing; + level1.Children[0].Match.Progression.Value = level2.Children[0].Match; + level1.Children[1].Match.Progression.Value = level2.Children[0].Match; - AddRepeatStep("change scores", () => pairing1.Team2Score.Value++, 4); - AddStep("add new team", () => pairing2.Team2.Value = new TournamentTeam { FlagName = { Value = "PT" }, FullName = { Value = "Portugal" } }); - AddStep("Add progression", () => level1.Children[2].Pairing.Progression.Value = level2.Children[1].Pairing); + AddRepeatStep("change scores", () => match1.Team2Score.Value++, 4); + AddStep("add new team", () => match2.Team2.Value = new TournamentTeam { FlagName = { Value = "PT" }, FullName = { Value = "Portugal" } }); + AddStep("Add progression", () => level1.Children[2].Match.Progression.Value = level2.Children[1].Match); - AddStep("start match", () => pairing2.StartMatch()); + AddStep("start match", () => match2.StartMatch()); - AddRepeatStep("change scores", () => pairing2.Team1Score.Value++, 10); + AddRepeatStep("change scores", () => match2.Team1Score.Value++, 10); - AddStep("start submatch", () => level2.Children[0].Pairing.StartMatch()); + AddStep("start submatch", () => level2.Children[0].Match.StartMatch()); - AddRepeatStep("change scores", () => level2.Children[0].Pairing.Team1Score.Value++, 5); + AddRepeatStep("change scores", () => level2.Children[0].Match.Team1Score.Value++, 5); - AddRepeatStep("change scores", () => level2.Children[0].Pairing.Team2Score.Value++, 4); + AddRepeatStep("change scores", () => level2.Children[0].Match.Team2Score.Value++, 4); } } } diff --git a/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs b/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs index 9f0d59fcbc..6b31fd2742 100644 --- a/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs +++ b/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs @@ -13,16 +13,16 @@ namespace osu.Game.Tournament.Tests public class TestSceneTeamIntro : LadderTestScene { [Cached] - private readonly Bindable currentMatch = new Bindable(); + private readonly Bindable currentMatch = new Bindable(); [BackgroundDependencyLoader] private void load() { - var pairing = new MatchPairing(); - pairing.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA"); - pairing.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN"); - pairing.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals"); - currentMatch.Value = pairing; + var match = new TournamentMatch(); + match.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA"); + match.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN"); + match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals"); + currentMatch.Value = match; Add(new TeamIntroScreen { diff --git a/osu.Game.Tournament.Tests/TestSceneTeamWin.cs b/osu.Game.Tournament.Tests/TestSceneTeamWin.cs index 8beeb50513..d195ad42ca 100644 --- a/osu.Game.Tournament.Tests/TestSceneTeamWin.cs +++ b/osu.Game.Tournament.Tests/TestSceneTeamWin.cs @@ -13,16 +13,16 @@ namespace osu.Game.Tournament.Tests public class TestSceneTeamWin : LadderTestScene { [Cached] - private readonly Bindable currentMatch = new Bindable(); + private readonly Bindable currentMatch = new Bindable(); [BackgroundDependencyLoader] private void load() { - var pairing = new MatchPairing(); - pairing.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA"); - pairing.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN"); - pairing.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals"); - currentMatch.Value = pairing; + var match = new TournamentMatch(); + match.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA"); + match.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN"); + match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals"); + currentMatch.Value = match; Add(new TeamWinScreen { diff --git a/osu.Game.Tournament.Tests/TestSceneTournamentMatchChatDisplay.cs b/osu.Game.Tournament.Tests/TestSceneTournamentMatchChatDisplay.cs index 125bf5679c..829d8629e5 100644 --- a/osu.Game.Tournament.Tests/TestSceneTournamentMatchChatDisplay.cs +++ b/osu.Game.Tournament.Tests/TestSceneTournamentMatchChatDisplay.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tournament.Tests Origin = Anchor.Centre, }); - ladderInfo.CurrentMatch.Value = new MatchPairing + ladderInfo.CurrentMatch.Value = new TournamentMatch { Team1 = { diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index aee7f914e7..f7ca25adba 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tournament.Components public const float HEIGHT = 50; - private readonly Bindable currentMatch = new Bindable(); + private readonly Bindable currentMatch = new Bindable(); private Box flash; public TournamentBeatmapPanel(BeatmapInfo beatmap, string mods = null) @@ -141,11 +141,11 @@ namespace osu.Game.Tournament.Components }); } - private void matchChanged(ValueChangedEvent pairing) + private void matchChanged(ValueChangedEvent match) { - if (pairing.OldValue != null) - pairing.OldValue.PicksBans.CollectionChanged -= picksBansOnCollectionChanged; - pairing.NewValue.PicksBans.CollectionChanged += picksBansOnCollectionChanged; + if (match.OldValue != null) + match.OldValue.PicksBans.CollectionChanged -= picksBansOnCollectionChanged; + match.NewValue.PicksBans.CollectionChanged += picksBansOnCollectionChanged; updateState(); } diff --git a/osu.Game.Tournament/Models/LadderEditorInfo.cs b/osu.Game.Tournament/Models/LadderEditorInfo.cs index 9bf01e76f5..70fd115e25 100644 --- a/osu.Game.Tournament/Models/LadderEditorInfo.cs +++ b/osu.Game.Tournament/Models/LadderEditorInfo.cs @@ -7,6 +7,6 @@ namespace osu.Game.Tournament.Models { public class LadderEditorInfo { - public readonly Bindable Selected = new Bindable(); + public readonly Bindable Selected = new Bindable(); } } diff --git a/osu.Game.Tournament/Models/LadderInfo.cs b/osu.Game.Tournament/Models/LadderInfo.cs index 2fdf1b06b1..b6dc59c0d9 100644 --- a/osu.Game.Tournament/Models/LadderInfo.cs +++ b/osu.Game.Tournament/Models/LadderInfo.cs @@ -9,7 +9,7 @@ namespace osu.Game.Tournament.Models { public class LadderInfo { - public BindableList Pairings = new BindableList(); + public BindableList Matches = new BindableList(); public BindableList Rounds = new BindableList(); public BindableList Teams = new BindableList(); @@ -17,6 +17,6 @@ namespace osu.Game.Tournament.Models public List Progressions = new List(); [JsonIgnore] - public Bindable CurrentMatch = new Bindable(); + public Bindable CurrentMatch = new Bindable(); } } diff --git a/osu.Game.Tournament/Models/MatchPairing.cs b/osu.Game.Tournament/Models/TournamentMatch.cs similarity index 88% rename from osu.Game.Tournament/Models/MatchPairing.cs rename to osu.Game.Tournament/Models/TournamentMatch.cs index caafe8af47..db41e83038 100644 --- a/osu.Game.Tournament/Models/MatchPairing.cs +++ b/osu.Game.Tournament/Models/TournamentMatch.cs @@ -15,7 +15,7 @@ namespace osu.Game.Tournament.Models /// A collection of two teams competing in a head-to-head match. /// [Serializable] - public class MatchPairing + public class TournamentMatch { public int ID; @@ -54,10 +54,10 @@ namespace osu.Game.Tournament.Models public readonly Bindable Round = new Bindable(); [JsonIgnore] - public readonly Bindable Progression = new Bindable(); + public readonly Bindable Progression = new Bindable(); [JsonIgnore] - public readonly Bindable LosersProgression = new Bindable(); + public readonly Bindable LosersProgression = new Bindable(); /// /// Should not be set directly. Use LadderInfo.CurrentMatch.Value = this instead. @@ -67,17 +67,17 @@ namespace osu.Game.Tournament.Models public readonly Bindable Date = new Bindable(); [JsonProperty] - public readonly BindableList ConditionalPairings = new BindableList(); + public readonly BindableList ConditionalMatches = new BindableList(); public readonly Bindable Position = new Bindable(); - public MatchPairing() + public TournamentMatch() { Team1.BindValueChanged(t => Team1Acronym = t.NewValue?.Acronym.Value, true); Team2.BindValueChanged(t => Team2Acronym = t.NewValue?.Acronym.Value, true); } - public MatchPairing(TournamentTeam team1 = null, TournamentTeam team2 = null) + public TournamentMatch(TournamentTeam team1 = null, TournamentTeam team2 = null) : this() { Team1.Value = team1; diff --git a/osu.Game.Tournament/Models/TournamentRound.cs b/osu.Game.Tournament/Models/TournamentRound.cs index 6fe4b20fa5..e325ad4b96 100644 --- a/osu.Game.Tournament/Models/TournamentRound.cs +++ b/osu.Game.Tournament/Models/TournamentRound.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tournament.Models public readonly Bindable StartDate = new Bindable(); // only used for serialisation - public List Pairings = new List(); + public List Matches = new List(); public override string ToString() => Name.Value ?? "None"; } diff --git a/osu.Game.Tournament/Screens/Editors/LadderEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/LadderEditorScreen.cs index 9b298df91d..ba63013886 100644 --- a/osu.Game.Tournament/Screens/Editors/LadderEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/LadderEditorScreen.cs @@ -39,9 +39,9 @@ namespace osu.Game.Tournament.Screens.Editors }); } - public void BeginJoin(MatchPairing pairing, bool losers) + public void BeginJoin(TournamentMatch match, bool losers) { - ScrollContent.Add(new JoinVisualiser(PairingsContainer, pairing, losers, UpdateLayout)); + ScrollContent.Add(new JoinVisualiser(MatchesContainer, match, losers, UpdateLayout)); } public MenuItem[] ContextMenuItems @@ -55,35 +55,35 @@ namespace osu.Game.Tournament.Screens.Editors { new OsuMenuItem("Create new match", MenuItemType.Highlighted, () => { - var pos = PairingsContainer.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); - LadderInfo.Pairings.Add(new MatchPairing { Position = { Value = new Point((int)pos.X, (int)pos.Y) } }); + var pos = MatchesContainer.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); + LadderInfo.Matches.Add(new TournamentMatch { Position = { Value = new Point((int)pos.X, (int)pos.Y) } }); }), new OsuMenuItem("Reset teams", MenuItemType.Destructive, () => { - foreach (var p in PairingsContainer) - p.Pairing.Reset(); + foreach (var p in MatchesContainer) + p.Match.Reset(); }) }; } } - public void Remove(MatchPairing pairing) + public void Remove(TournamentMatch match) { - PairingsContainer.FirstOrDefault(p => p.Pairing == pairing)?.Remove(); + MatchesContainer.FirstOrDefault(p => p.Match == match)?.Remove(); } private class JoinVisualiser : CompositeDrawable { - private readonly Container pairingsContainer; - public readonly MatchPairing Source; + private readonly Container matchesContainer; + public readonly TournamentMatch Source; private readonly bool losers; private readonly Action complete; private ProgressionPath path; - public JoinVisualiser(Container pairingsContainer, MatchPairing source, bool losers, Action complete) + public JoinVisualiser(Container matchesContainer, TournamentMatch source, bool losers, Action complete) { - this.pairingsContainer = pairingsContainer; + this.matchesContainer = matchesContainer; RelativeSizeAxes = Axes.Both; Source = source; @@ -95,9 +95,9 @@ namespace osu.Game.Tournament.Screens.Editors Source.Progression.Value = null; } - private DrawableMatchPairing findTarget(InputState state) + private DrawableTournamentMatch findTarget(InputState state) { - return pairingsContainer.FirstOrDefault(d => d.ReceivePositionalInputAt(state.Mouse.Position)); + return matchesContainer.FirstOrDefault(d => d.ReceivePositionalInputAt(state.Mouse.Position)); } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) @@ -118,7 +118,7 @@ namespace osu.Game.Tournament.Screens.Editors if (found == null) return false; - AddInternal(path = new ProgressionPath(pairingsContainer.First(c => c.Pairing == Source), found) + AddInternal(path = new ProgressionPath(matchesContainer.First(c => c.Match == Source), found) { Colour = Color4.Yellow, }); @@ -132,12 +132,12 @@ namespace osu.Game.Tournament.Screens.Editors if (found != null) { - if (found.Pairing != Source) + if (found.Match != Source) { if (losers) - Source.LosersProgression.Value = found.Pairing; + Source.LosersProgression.Value = found.Match; else - Source.Progression.Value = found.Pairing; + Source.Progression.Value = found.Match; } complete?.Invoke(); diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index e75872cda4..cfa44537d6 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -55,7 +55,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components private readonly Color4 red = new Color4(129, 68, 65, 255); private readonly Color4 blue = new Color4(41, 91, 97, 255); - private readonly Bindable currentMatch = new Bindable(); + private readonly Bindable currentMatch = new Bindable(); private readonly Bindable currentTeam = new Bindable(); private readonly Bindable currentTeamScore = new Bindable(); @@ -74,7 +74,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components currentMatch.BindTo(ladder.CurrentMatch); } - private void matchChanged(ValueChangedEvent match) + private void matchChanged(ValueChangedEvent match) { currentTeamScore.UnbindBindings(); currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1Score : match.NewValue.Team2Score); @@ -187,7 +187,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components private class RoundDisplay : CompositeDrawable { - private readonly Bindable currentMatch = new Bindable(); + private readonly Bindable currentMatch = new Bindable(); public RoundDisplay() { @@ -204,7 +204,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components currentMatch.BindTo(ladder.CurrentMatch); } - private void matchChanged(ValueChangedEvent match) + private void matchChanged(ValueChangedEvent match) { InternalChildren = new Drawable[] { diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 7b108731f3..5bbd049cc0 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tournament.Screens.Gameplay { private readonly BindableBool warmup = new BindableBool(); - private readonly Bindable currentMatch = new Bindable(); + private readonly Bindable currentMatch = new Bindable(); public readonly Bindable State = new Bindable(); private TriangleButton warmupButton; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ConditionalMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/ConditionalTournamentMatch.cs similarity index 72% rename from osu.Game.Tournament/Screens/Ladder/Components/ConditionalMatchPairing.cs rename to osu.Game.Tournament/Screens/Ladder/Components/ConditionalTournamentMatch.cs index f3b5678c7c..16224a7fb4 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/ConditionalMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/ConditionalTournamentMatch.cs @@ -6,9 +6,9 @@ using osu.Game.Tournament.Models; namespace osu.Game.Tournament.Screens.Ladder.Components { /// - /// A pairing that may not necessarily occur. + /// A match that may not necessarily occur. /// - public class ConditionalMatchPairing : MatchPairing + public class ConditionalTournamentMatch : TournamentMatch { } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index e48155ab0e..dde280ccd8 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -17,14 +17,14 @@ using SixLabors.Primitives; namespace osu.Game.Tournament.Screens.Ladder.Components { - public class DrawableMatchPairing : CompositeDrawable + public class DrawableTournamentMatch : CompositeDrawable { - public readonly MatchPairing Pairing; + public readonly TournamentMatch Match; private readonly bool editor; protected readonly FillFlowContainer Flow; private readonly Drawable selectionBox; private readonly Drawable currentMatchSelectionBox; - private Bindable globalSelection; + private Bindable globalSelection; [Resolved(CanBeNull = true)] private LadderEditorInfo editorInfo { get; set; } @@ -32,9 +32,9 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [Resolved(CanBeNull = true)] private LadderInfo ladderInfo { get; set; } - public DrawableMatchPairing(MatchPairing pairing, bool editor = false) + public DrawableTournamentMatch(TournamentMatch match, bool editor = false) { - Pairing = pairing; + Match = match; this.editor = editor; AutoSizeAxes = Axes.Both; @@ -75,25 +75,25 @@ namespace osu.Game.Tournament.Screens.Ladder.Components } }; - boundReference(pairing.Team1).BindValueChanged(_ => updateTeams()); - boundReference(pairing.Team2).BindValueChanged(_ => updateTeams()); - boundReference(pairing.Team1Score).BindValueChanged(_ => updateWinConditions()); - boundReference(pairing.Team2Score).BindValueChanged(_ => updateWinConditions()); - boundReference(pairing.Round).BindValueChanged(_ => + boundReference(match.Team1).BindValueChanged(_ => updateTeams()); + boundReference(match.Team2).BindValueChanged(_ => updateTeams()); + boundReference(match.Team1Score).BindValueChanged(_ => updateWinConditions()); + boundReference(match.Team2Score).BindValueChanged(_ => updateWinConditions()); + boundReference(match.Round).BindValueChanged(_ => { updateWinConditions(); Changed?.Invoke(); }); - boundReference(pairing.Completed).BindValueChanged(_ => updateProgression()); - boundReference(pairing.Progression).BindValueChanged(_ => updateProgression()); - boundReference(pairing.LosersProgression).BindValueChanged(_ => updateProgression()); - boundReference(pairing.Losers).BindValueChanged(_ => + boundReference(match.Completed).BindValueChanged(_ => updateProgression()); + boundReference(match.Progression).BindValueChanged(_ => updateProgression()); + boundReference(match.LosersProgression).BindValueChanged(_ => updateProgression()); + boundReference(match.Losers).BindValueChanged(_ => { updateTeams(); Changed?.Invoke(); }); - boundReference(pairing.Current).BindValueChanged(_ => updateCurrentMatch(), true); - boundReference(pairing.Position).BindValueChanged(pos => + boundReference(match.Current).BindValueChanged(_ => updateCurrentMatch(), true); + boundReference(match.Position).BindValueChanged(pos => { if (!IsDragged) Position = new Vector2(pos.NewValue.X, pos.NewValue.Y); @@ -127,7 +127,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private void updateCurrentMatch() { - if (Pairing.Current.Value) + if (Match.Current.Value) currentMatchSelectionBox.Show(); else currentMatchSelectionBox.Hide(); @@ -149,9 +149,9 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { selectionBox.Show(); if (editor) - editorInfo.Selected.Value = Pairing; + editorInfo.Selected.Value = Match; else - ladderInfo.CurrentMatch.Value = Pairing; + ladderInfo.CurrentMatch.Value = Match; } else selectionBox.Hide(); @@ -160,36 +160,36 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private void updateProgression() { - if (!Pairing.Completed.Value) + if (!Match.Completed.Value) { // ensure we clear any of our teams from our progression. // this is not pretty logic but should suffice for now. - if (Pairing.Progression.Value != null && Pairing.Progression.Value.Team1.Value == Pairing.Team1.Value) - Pairing.Progression.Value.Team1.Value = null; + if (Match.Progression.Value != null && Match.Progression.Value.Team1.Value == Match.Team1.Value) + Match.Progression.Value.Team1.Value = null; - if (Pairing.Progression.Value != null && Pairing.Progression.Value.Team2.Value == Pairing.Team2.Value) - Pairing.Progression.Value.Team2.Value = null; + if (Match.Progression.Value != null && Match.Progression.Value.Team2.Value == Match.Team2.Value) + Match.Progression.Value.Team2.Value = null; - if (Pairing.LosersProgression.Value != null && Pairing.LosersProgression.Value.Team1.Value == Pairing.Team1.Value) - Pairing.LosersProgression.Value.Team1.Value = null; + if (Match.LosersProgression.Value != null && Match.LosersProgression.Value.Team1.Value == Match.Team1.Value) + Match.LosersProgression.Value.Team1.Value = null; - if (Pairing.LosersProgression.Value != null && Pairing.LosersProgression.Value.Team2.Value == Pairing.Team2.Value) - Pairing.LosersProgression.Value.Team2.Value = null; + if (Match.LosersProgression.Value != null && Match.LosersProgression.Value.Team2.Value == Match.Team2.Value) + Match.LosersProgression.Value.Team2.Value = null; } else { - transferProgression(Pairing.Progression?.Value, Pairing.Winner); - transferProgression(Pairing.LosersProgression?.Value, Pairing.Loser); + transferProgression(Match.Progression?.Value, Match.Winner); + transferProgression(Match.LosersProgression?.Value, Match.Loser); } Changed?.Invoke(); } - private void transferProgression(MatchPairing destination, TournamentTeam team) + private void transferProgression(TournamentMatch destination, TournamentTeam team) { if (destination == null) return; - bool progressionAbove = destination.ID < Pairing.ID; + bool progressionAbove = destination.ID < Match.ID; Bindable destinationTeam; @@ -210,12 +210,12 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private void updateWinConditions() { - if (Pairing.Round.Value == null) return; + if (Match.Round.Value == null) return; - var instaWinAmount = Pairing.Round.Value.BestOf.Value / 2; + var instaWinAmount = Match.Round.Value.BestOf.Value / 2; - Pairing.Completed.Value = Pairing.Round.Value.BestOf.Value > 0 - && (Pairing.Team1Score.Value + Pairing.Team2Score.Value >= Pairing.Round.Value.BestOf.Value || Pairing.Team1Score.Value > instaWinAmount || Pairing.Team2Score.Value > instaWinAmount); + Match.Completed.Value = Match.Round.Value.BestOf.Value > 0 + && (Match.Team1Score.Value + Match.Team2Score.Value >= Match.Round.Value.BestOf.Value || Match.Team1Score.Value > instaWinAmount || Match.Team2Score.Value > instaWinAmount); } protected override void LoadComplete() @@ -228,7 +228,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components globalSelection = editorInfo.Selected.GetBoundCopy(); globalSelection.BindValueChanged(s => { - if (s.NewValue != Pairing) Selected = false; + if (s.NewValue != Match) Selected = false; }); } } @@ -240,25 +240,25 @@ namespace osu.Game.Tournament.Screens.Ladder.Components // todo: teams may need to be bindable for transitions at a later point. - if (Pairing.Team1.Value == null || Pairing.Team2.Value == null) - Pairing.CancelMatchStart(); + if (Match.Team1.Value == null || Match.Team2.Value == null) + Match.CancelMatchStart(); - if (Pairing.ConditionalPairings.Count > 0) + if (Match.ConditionalMatches.Count > 0) { - foreach (var conditional in Pairing.ConditionalPairings) + foreach (var conditional in Match.ConditionalMatches) { - var team1Match = conditional.Acronyms.Contains(Pairing.Team1Acronym); - var team2Match = conditional.Acronyms.Contains(Pairing.Team2Acronym); + var team1Match = conditional.Acronyms.Contains(Match.Team1Acronym); + var team2Match = conditional.Acronyms.Contains(Match.Team2Acronym); if (team1Match && team2Match) - Pairing.Date.Value = conditional.Date.Value; + Match.Date.Value = conditional.Date.Value; } } Flow.Children = new[] { - new DrawableMatchTeam(Pairing.Team1.Value, Pairing, Pairing.Losers.Value), - new DrawableMatchTeam(Pairing.Team2.Value, Pairing, Pairing.Losers.Value) + new DrawableMatchTeam(Match.Team1.Value, Match, Match.Losers.Value), + new DrawableMatchTeam(Match.Team2.Value, Match, Match.Losers.Value) }; SchedulerAfterChildren.Add(() => Scheduler.Add(updateProgression)); @@ -282,7 +282,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components protected override bool OnClick(ClickEvent e) { - if (editorInfo == null || Pairing is ConditionalMatchPairing) + if (editorInfo == null || Match is ConditionalTournamentMatch) return false; Selected = true; @@ -297,17 +297,17 @@ namespace osu.Game.Tournament.Screens.Ladder.Components this.MoveToOffset(e.Delta); var pos = Position; - Pairing.Position.Value = new Point((int)pos.X, (int)pos.Y); + Match.Position.Value = new Point((int)pos.X, (int)pos.Y); return true; } public void Remove() { Selected = false; - Pairing.Progression.Value = null; - Pairing.LosersProgression.Value = null; + Match.Progression.Value = null; + Match.LosersProgression.Value = null; - ladderInfo.Pairings.Remove(Pairing); + ladderInfo.Matches.Remove(Match); } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index 5514dfce3e..ded21730f3 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { public class DrawableMatchTeam : DrawableTournamentTeam, IHasContextMenu { - private readonly MatchPairing pairing; + private readonly TournamentMatch match; private readonly bool losers; private OsuSpriteText scoreText; private Box background; @@ -47,17 +47,17 @@ namespace osu.Game.Tournament.Screens.Ladder.Components if (ladderInfo.CurrentMatch.Value != null) ladderInfo.CurrentMatch.Value.Current.Value = false; - ladderInfo.CurrentMatch.Value = pairing; + ladderInfo.CurrentMatch.Value = match; ladderInfo.CurrentMatch.Value.Current.Value = true; } [Resolved(CanBeNull = true)] private LadderEditorInfo editorInfo { get; set; } - public DrawableMatchTeam(TournamentTeam team, MatchPairing pairing, bool losers) + public DrawableMatchTeam(TournamentTeam team, TournamentMatch match, bool losers) : base(team) { - this.pairing = pairing; + this.match = match; this.losers = losers; Size = new Vector2(150, 40); @@ -71,13 +71,13 @@ namespace osu.Game.Tournament.Screens.Ladder.Components AcronymText.Padding = new MarginPadding { Left = 50 }; AcronymText.Font = OsuFont.GetFont(size: 24); - if (pairing != null) + if (match != null) { - isWinner = () => pairing.Winner == Team; + isWinner = () => match.Winner == Team; - completed.BindTo(pairing.Completed); + completed.BindTo(match.Completed); if (team != null) - score.BindTo(team == pairing.Team1.Value ? pairing.Team1Score : pairing.Team2Score); + score.BindTo(team == match.Team1.Value ? match.Team1Score : match.Team2Score); } } @@ -144,7 +144,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { if (Team == null || editorInfo != null) return false; - if (!pairing.Current.Value) + if (!match.Current.Value) { setCurrent(); return true; @@ -154,25 +154,25 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { if (score.Value == null) { - pairing.StartMatch(); + match.StartMatch(); } - else if (!pairing.Completed.Value) + else if (!match.Completed.Value) score.Value++; } else { - if (pairing.Progression.Value?.Completed.Value == true) + if (match.Progression.Value?.Completed.Value == true) // don't allow changing scores if the match has a progression. can cause large data loss return false; - if (pairing.Completed.Value && pairing.Winner != Team) + if (match.Completed.Value && match.Winner != Team) // don't allow changing scores from the non-winner return false; if (score.Value > 0) score.Value--; else - pairing.CancelMatchStart(); + match.CancelMatchStart(); } return false; @@ -197,9 +197,9 @@ namespace osu.Game.Tournament.Screens.Ladder.Components return new MenuItem[] { new OsuMenuItem("Set as current", MenuItemType.Standard, setCurrent), - new OsuMenuItem("Join with", MenuItemType.Standard, () => ladderEditor.BeginJoin(pairing, false)), - new OsuMenuItem("Join with (loser)", MenuItemType.Standard, () => ladderEditor.BeginJoin(pairing, true)), - new OsuMenuItem("Remove", MenuItemType.Destructive, () => ladderEditor.Remove(pairing)), + new OsuMenuItem("Join with", MenuItemType.Standard, () => ladderEditor.BeginJoin(match, false)), + new OsuMenuItem("Join with (loser)", MenuItemType.Standard, () => ladderEditor.BeginJoin(match, true)), + new OsuMenuItem("Remove", MenuItemType.Destructive, () => ladderEditor.Remove(match)), }; } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs index 5468844f66..34e0dc770f 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs @@ -10,10 +10,10 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { public class ProgressionPath : Path { - public DrawableMatchPairing Source { get; private set; } - public DrawableMatchPairing Destination { get; private set; } + public DrawableTournamentMatch Source { get; private set; } + public DrawableTournamentMatch Destination { get; private set; } - public ProgressionPath(DrawableMatchPairing source, DrawableMatchPairing destination) + public ProgressionPath(DrawableTournamentMatch source, DrawableTournamentMatch destination) { Source = source; Destination = destination; diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs index 50675a6147..67531ce5d3 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tournament.Screens.Ladder { public class LadderScreen : TournamentScreen, IProvideVideo { - protected Container PairingsContainer; + protected Container MatchesContainer; private Container paths; private Container headings; @@ -53,35 +53,35 @@ namespace osu.Game.Tournament.Screens.Ladder { paths = new Container { RelativeSizeAxes = Axes.Both }, headings = new Container { RelativeSizeAxes = Axes.Both }, - PairingsContainer = new Container { RelativeSizeAxes = Axes.Both }, + MatchesContainer = new Container { RelativeSizeAxes = Axes.Both }, } }, } }; - void addPairing(MatchPairing pairing) => - PairingsContainer.Add(new DrawableMatchPairing(pairing, this is LadderEditorScreen) + void addMatch(TournamentMatch match) => + MatchesContainer.Add(new DrawableTournamentMatch(match, this is LadderEditorScreen) { Changed = () => layout.Invalidate() }); - foreach (var pairing in LadderInfo.Pairings) - addPairing(pairing); + foreach (var match in LadderInfo.Matches) + addMatch(match); LadderInfo.Rounds.ItemsAdded += _ => layout.Invalidate(); LadderInfo.Rounds.ItemsRemoved += _ => layout.Invalidate(); - LadderInfo.Pairings.ItemsAdded += pairings => + LadderInfo.Matches.ItemsAdded += matches => { - foreach (var p in pairings) - addPairing(p); + foreach (var p in matches) + addMatch(p); layout.Invalidate(); }; - LadderInfo.Pairings.ItemsRemoved += pairings => + LadderInfo.Matches.ItemsRemoved += matches => { - foreach (var p in pairings) - foreach (var d in PairingsContainer.Where(d => d.Pairing == p)) + foreach (var p in matches) + foreach (var d in MatchesContainer.Where(d => d.Match == p)) d.Expire(); layout.Invalidate(); @@ -110,45 +110,45 @@ namespace osu.Game.Tournament.Screens.Ladder int id = 1; - foreach (var pairing in PairingsContainer.OrderBy(d => d.Y).ThenBy(d => d.X)) + foreach (var match in MatchesContainer.OrderBy(d => d.Y).ThenBy(d => d.X)) { - pairing.Pairing.ID = id++; + match.Match.ID = id++; - if (pairing.Pairing.Progression.Value != null) + if (match.Match.Progression.Value != null) { - var dest = PairingsContainer.FirstOrDefault(p => p.Pairing == pairing.Pairing.Progression.Value); + var dest = MatchesContainer.FirstOrDefault(p => p.Match == match.Match.Progression.Value); if (dest == null) // clean up outdated progressions. - pairing.Pairing.Progression.Value = null; + match.Match.Progression.Value = null; else - paths.Add(new ProgressionPath(pairing, dest) { Colour = pairing.Pairing.Losers.Value ? losersPathColour : normalPathColour }); + paths.Add(new ProgressionPath(match, dest) { Colour = match.Match.Losers.Value ? losersPathColour : normalPathColour }); } if (DrawLoserPaths) { - if (pairing.Pairing.LosersProgression.Value != null) + if (match.Match.LosersProgression.Value != null) { - var dest = PairingsContainer.FirstOrDefault(p => p.Pairing == pairing.Pairing.LosersProgression.Value); + var dest = MatchesContainer.FirstOrDefault(p => p.Match == match.Match.LosersProgression.Value); if (dest == null) // clean up outdated progressions. - pairing.Pairing.LosersProgression.Value = null; + match.Match.LosersProgression.Value = null; else - paths.Add(new ProgressionPath(pairing, dest) { Colour = losersPathColour.Opacity(0.1f) }); + paths.Add(new ProgressionPath(match, dest) { Colour = losersPathColour.Opacity(0.1f) }); } } } foreach (var round in LadderInfo.Rounds) { - var topPairing = PairingsContainer.Where(p => !p.Pairing.Losers.Value && p.Pairing.Round.Value == round).OrderBy(p => p.Y).FirstOrDefault(); + var topMatch = MatchesContainer.Where(p => !p.Match.Losers.Value && p.Match.Round.Value == round).OrderBy(p => p.Y).FirstOrDefault(); - if (topPairing == null) continue; + if (topMatch == null) continue; headings.Add(new DrawableTournamentRound(round) { - Position = headings.ToLocalSpace((topPairing.ScreenSpaceDrawQuad.TopLeft + topPairing.ScreenSpaceDrawQuad.TopRight) / 2), + Position = headings.ToLocalSpace((topMatch.ScreenSpaceDrawQuad.TopLeft + topMatch.ScreenSpaceDrawQuad.TopRight) / 2), Margin = new MarginPadding { Bottom = 10 }, Origin = Anchor.BottomCentre, }); @@ -156,13 +156,13 @@ namespace osu.Game.Tournament.Screens.Ladder foreach (var round in LadderInfo.Rounds) { - var topPairing = PairingsContainer.Where(p => p.Pairing.Losers.Value && p.Pairing.Round.Value == round).OrderBy(p => p.Y).FirstOrDefault(); + var topMatch = MatchesContainer.Where(p => p.Match.Losers.Value && p.Match.Round.Value == round).OrderBy(p => p.Y).FirstOrDefault(); - if (topPairing == null) continue; + if (topMatch == null) continue; headings.Add(new DrawableTournamentRound(round, true) { - Position = headings.ToLocalSpace((topPairing.ScreenSpaceDrawQuad.TopLeft + topPairing.ScreenSpaceDrawQuad.TopRight) / 2), + Position = headings.ToLocalSpace((topMatch.ScreenSpaceDrawQuad.TopLeft + topMatch.ScreenSpaceDrawQuad.TopRight) / 2), Margin = new MarginPadding { Bottom = 10 }, Origin = Anchor.BottomCentre, }); diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index ab03adbce9..1c5f07ce19 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -26,7 +26,7 @@ namespace osu.Game.Tournament.Screens.MapPool { private readonly FillFlowContainer> mapFlows; - private readonly Bindable currentMatch = new Bindable(); + private readonly Bindable currentMatch = new Bindable(); [Resolved(canBeNull: true)] private TournamentSceneManager sceneManager { get; set; } @@ -203,7 +203,7 @@ namespace osu.Game.Tournament.Screens.MapPool } } - private void matchChanged(ValueChangedEvent match) + private void matchChanged(ValueChangedEvent match) { mapFlows.Clear(); diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index 457fb80141..4b46264055 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tournament.Screens.Schedule { public class ScheduleScreen : TournamentScreen, IProvideVideo { - private readonly Bindable currentMatch = new Bindable(); + private readonly Bindable currentMatch = new Bindable(); private Container mainContainer; private LadderInfo ladder; @@ -49,7 +49,7 @@ namespace osu.Game.Tournament.Screens.Schedule currentMatch.BindTo(ladder.CurrentMatch); } - private void matchChanged(ValueChangedEvent match) + private void matchChanged(ValueChangedEvent match) { if (match.NewValue == null) { @@ -57,10 +57,10 @@ namespace osu.Game.Tournament.Screens.Schedule return; } - var upcoming = ladder.Pairings.Where(p => !p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4); + var upcoming = ladder.Matches.Where(p => !p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4); var conditionals = ladder - .Pairings.Where(p => !p.Completed.Value && (p.Team1.Value == null || p.Team2.Value == null) && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4) - .SelectMany(m => m.ConditionalPairings.Where(cp => m.Acronyms.TrueForAll(a => cp.Acronyms.Contains(a)))); + .Matches.Where(p => !p.Completed.Value && (p.Team1.Value == null || p.Team2.Value == null) && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4) + .SelectMany(m => m.ConditionalMatches.Where(cp => m.Acronyms.TrueForAll(a => cp.Acronyms.Contains(a)))); upcoming = upcoming.Concat(conditionals); upcoming = upcoming.OrderBy(p => p.Date.Value).Take(12); @@ -85,18 +85,18 @@ namespace osu.Game.Tournament.Screens.Schedule { RelativeSizeAxes = Axes.Both, Width = 0.4f, - ChildrenEnumerable = ladder.Pairings + ChildrenEnumerable = ladder.Matches .Where(p => p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4) .OrderByDescending(p => p.Date.Value) .Take(8) - .Select(p => new SchedulePairing(p)) + .Select(p => new ScheduleMatch(p)) }, new ScheduleContainer("match overview") { RelativeSizeAxes = Axes.Both, Width = 0.6f, - ChildrenEnumerable = upcoming.Select(p => new SchedulePairing(p)) + ChildrenEnumerable = upcoming.Select(p => new ScheduleMatch(p)) }, } } @@ -115,7 +115,7 @@ namespace osu.Game.Tournament.Screens.Schedule Colour = Color4.Black, Font = OsuFont.GetFont(size: 20) }, - new SchedulePairing(match.NewValue, false), + new ScheduleMatch(match.NewValue, false), new OsuSpriteText { Text = "Start Time " + match.NewValue.Date.Value.ToUniversalTime().ToString("HH:mm UTC"), @@ -128,21 +128,21 @@ namespace osu.Game.Tournament.Screens.Schedule }; } - public class SchedulePairing : DrawableMatchPairing + public class ScheduleMatch : DrawableTournamentMatch { - public SchedulePairing(MatchPairing pairing, bool showTimestamp = true) - : base(pairing) + public ScheduleMatch(TournamentMatch match, bool showTimestamp = true) + : base(match) { Flow.Direction = FillDirection.Horizontal; - bool conditional = pairing is ConditionalMatchPairing; + bool conditional = match is ConditionalTournamentMatch; if (conditional) Colour = OsuColour.Gray(0.5f); if (showTimestamp) { - AddInternal(new DrawableDate(Pairing.Date.Value) + AddInternal(new DrawableDate(Match.Date.Value) { Anchor = Anchor.TopRight, Origin = Anchor.TopLeft, @@ -157,7 +157,7 @@ namespace osu.Game.Tournament.Screens.Schedule Colour = Color4.Black, Alpha = conditional ? 0.6f : 1, Margin = new MarginPadding { Horizontal = 10, Vertical = 5 }, - Text = pairing.Date.Value.ToUniversalTime().ToString("HH:mm UTC") + (conditional ? " (conditional)" : "") + Text = match.Date.Value.ToUniversalTime().ToString("HH:mm UTC") + (conditional ? " (conditional)" : "") }); } } diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index 4e0bb23a62..2cb4ffe4e9 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro { private Container mainContainer; - private readonly Bindable currentMatch = new Bindable(); + private readonly Bindable currentMatch = new Bindable(); [BackgroundDependencyLoader] private void load(Storage storage) @@ -45,9 +45,9 @@ namespace osu.Game.Tournament.Screens.TeamIntro currentMatch.BindTo(LadderInfo.CurrentMatch); } - private void matchChanged(ValueChangedEvent pairing) + private void matchChanged(ValueChangedEvent match) { - if (pairing.NewValue == null) + if (match.NewValue == null) { mainContainer.Clear(); return; @@ -55,7 +55,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro mainContainer.Children = new Drawable[] { - new TeamWithPlayers(pairing.NewValue.Team1.Value, true) + new TeamWithPlayers(match.NewValue.Team1.Value, true) { RelativeSizeAxes = Axes.Both, Width = 0.5f, @@ -63,7 +63,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = Anchor.Centre, Origin = Anchor.CentreRight }, - new TeamWithPlayers(pairing.NewValue.Team2.Value) + new TeamWithPlayers(match.NewValue.Team2.Value) { RelativeSizeAxes = Axes.Both, Width = 0.5f, @@ -71,7 +71,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = Anchor.Centre, Origin = Anchor.CentreLeft }, - new RoundDisplay(pairing.NewValue) + new RoundDisplay(match.NewValue) { RelativeSizeAxes = Axes.Both, Height = 0.25f, @@ -83,7 +83,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro private class RoundDisplay : CompositeDrawable { - public RoundDisplay(MatchPairing pairing) + public RoundDisplay(TournamentMatch match) { var col = OsuColour.Gray(0.33f); @@ -112,7 +112,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Colour = col, - Text = pairing.Round.Value?.Name.Value ?? "Unknown Round", + Text = match.Round.Value?.Name.Value ?? "Unknown Round", Spacing = new Vector2(10, 0), Font = OsuFont.GetFont(size: 50, weight: FontWeight.Light) }, @@ -121,7 +121,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Colour = col, - Text = pairing.Date.Value.ToUniversalTime().ToString("dd MMMM HH:mm UTC"), + Text = match.Date.Value.ToUniversalTime().ToString("dd MMMM HH:mm UTC"), Font = OsuFont.GetFont(size: 20) }, } diff --git a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs index 0a37cf6c4a..efe4ee92fc 100644 --- a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs +++ b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tournament.Screens.TeamWin { private Container mainContainer; - private readonly Bindable currentMatch = new Bindable(); + private readonly Bindable currentMatch = new Bindable(); private readonly Bindable currentCompleted = new Bindable(); private TourneyVideo blueWinVideo; @@ -61,31 +61,31 @@ namespace osu.Game.Tournament.Screens.TeamWin currentCompleted.BindValueChanged(_ => update()); } - private void matchChanged(ValueChangedEvent pairing) + private void matchChanged(ValueChangedEvent match) { currentCompleted.UnbindBindings(); - currentCompleted.BindTo(pairing.NewValue.Completed); + currentCompleted.BindTo(match.NewValue.Completed); update(); } private void update() { - var pairing = currentMatch.Value; + var match = currentMatch.Value; - if (pairing.Winner == null) + if (match.Winner == null) { mainContainer.Clear(); return; } - bool redWin = pairing.Winner == pairing.Team1.Value; + bool redWin = match.Winner == match.Team1.Value; redWinVideo.Alpha = redWin ? 1 : 0; blueWinVideo.Alpha = redWin ? 0 : 1; mainContainer.Children = new Drawable[] { - new TeamWithPlayers(pairing.Winner, redWin) + new TeamWithPlayers(match.Winner, redWin) { RelativeSizeAxes = Axes.Both, Width = 0.5f, @@ -93,7 +93,7 @@ namespace osu.Game.Tournament.Screens.TeamWin Anchor = Anchor.Centre, Origin = Anchor.Centre }, - new RoundDisplay(pairing) + new RoundDisplay(match) { RelativeSizeAxes = Axes.Both, Height = 0.25f, @@ -105,7 +105,7 @@ namespace osu.Game.Tournament.Screens.TeamWin private class RoundDisplay : CompositeDrawable { - public RoundDisplay(MatchPairing pairing) + public RoundDisplay(TournamentMatch match) { var col = OsuColour.Gray(0.33f); @@ -133,7 +133,7 @@ namespace osu.Game.Tournament.Screens.TeamWin Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Colour = col, - Text = pairing.Round.Value?.Name.Value ?? "Unknown Round", + Text = match.Round.Value?.Name.Value ?? "Unknown Round", Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 50, FontWeight.Light), Spacing = new Vector2(10, 0), }, @@ -142,7 +142,7 @@ namespace osu.Game.Tournament.Screens.TeamWin Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Colour = col, - Text = pairing.Date.Value.ToUniversalTime().ToString("dd MMMM HH:mm UTC"), + Text = match.Date.Value.ToUniversalTime().ToString("dd MMMM HH:mm UTC"), Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 20, FontWeight.Light), }, } diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index e35e0b0d30..fb96641bcf 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -61,7 +61,7 @@ namespace osu.Game.Tournament readBracket(); - ladder.CurrentMatch.Value = ladder.Pairings.FirstOrDefault(p => p.Current.Value); + ladder.CurrentMatch.Value = ladder.Matches.FirstOrDefault(p => p.Current.Value); dependencies.CacheAs(ipc = new FileBasedIPC()); Add(ipc); @@ -97,24 +97,24 @@ namespace osu.Game.Tournament bool addedInfo = false; // assign teams - foreach (var pairing in ladder.Pairings) + foreach (var match in ladder.Matches) { - pairing.Team1.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == pairing.Team1Acronym); - pairing.Team2.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == pairing.Team2Acronym); + match.Team1.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == match.Team1Acronym); + match.Team2.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == match.Team2Acronym); - foreach (var conditional in pairing.ConditionalPairings) + foreach (var conditional in match.ConditionalMatches) { conditional.Team1.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == conditional.Team1Acronym); conditional.Team2.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == conditional.Team2Acronym); - conditional.Round.Value = pairing.Round.Value; + conditional.Round.Value = match.Round.Value; } } // assign progressions foreach (var pair in ladder.Progressions) { - var src = ladder.Pairings.FirstOrDefault(p => p.ID == pair.SourceID); - var dest = ladder.Pairings.FirstOrDefault(p => p.ID == pair.TargetID); + var src = ladder.Matches.FirstOrDefault(p => p.ID == pair.SourceID); + var dest = ladder.Matches.FirstOrDefault(p => p.ID == pair.TargetID); if (src == null) throw new InvalidOperationException(); @@ -127,11 +127,11 @@ namespace osu.Game.Tournament } } - // link pairings to rounds + // link matches to rounds foreach (var round in ladder.Rounds) - foreach (var id in round.Pairings) + foreach (var id in round.Matches) { - var found = ladder.Pairings.FirstOrDefault(p => p.ID == id); + var found = ladder.Matches.FirstOrDefault(p => p.ID == id); if (found != null) { @@ -245,10 +245,10 @@ namespace osu.Game.Tournament protected virtual void SaveChanges() { foreach (var r in ladder.Rounds) - r.Pairings = ladder.Pairings.Where(p => p.Round.Value == r).Select(p => p.ID).ToList(); + r.Matches = ladder.Matches.Where(p => p.Round.Value == r).Select(p => p.ID).ToList(); - ladder.Progressions = ladder.Pairings.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat( - ladder.Pairings.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) + ladder.Progressions = ladder.Matches.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat( + ladder.Matches.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) .ToList(); using (var stream = storage.GetStream(bracket_filename, FileAccess.Write, FileMode.Create)) From 6226889d1c9f04e96bfd08d9045afed614e123cc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 15:00:33 +0900 Subject: [PATCH 1179/2854] Add xmldoc and serialisable attributes --- osu.Game.Tournament/Models/BeatmapChoice.cs | 5 +++++ osu.Game.Tournament/Models/LadderInfo.cs | 5 +++++ osu.Game.Tournament/Models/TournamentProgression.cs | 7 +++++++ osu.Game.Tournament/Models/TournamentRound.cs | 3 +++ osu.Game.Tournament/Models/TournamentTeam.cs | 3 +++ 5 files changed, 23 insertions(+) diff --git a/osu.Game.Tournament/Models/BeatmapChoice.cs b/osu.Game.Tournament/Models/BeatmapChoice.cs index c22077553b..384b349b24 100644 --- a/osu.Game.Tournament/Models/BeatmapChoice.cs +++ b/osu.Game.Tournament/Models/BeatmapChoice.cs @@ -1,11 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using Newtonsoft.Json; using Newtonsoft.Json.Converters; namespace osu.Game.Tournament.Models { + /// + /// A beatmap choice by a team from a tournament's map pool. + /// + [Serializable] public class BeatmapChoice { [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] diff --git a/osu.Game.Tournament/Models/LadderInfo.cs b/osu.Game.Tournament/Models/LadderInfo.cs index b6dc59c0d9..547c4eab08 100644 --- a/osu.Game.Tournament/Models/LadderInfo.cs +++ b/osu.Game.Tournament/Models/LadderInfo.cs @@ -1,12 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using Newtonsoft.Json; using osu.Framework.Bindables; namespace osu.Game.Tournament.Models { + /// + /// Holds the complete data required to operate the tournament system. + /// + [Serializable] public class LadderInfo { public BindableList Matches = new BindableList(); diff --git a/osu.Game.Tournament/Models/TournamentProgression.cs b/osu.Game.Tournament/Models/TournamentProgression.cs index 4ef4be599d..3e9b2e05c5 100644 --- a/osu.Game.Tournament/Models/TournamentProgression.cs +++ b/osu.Game.Tournament/Models/TournamentProgression.cs @@ -1,8 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; + namespace osu.Game.Tournament.Models { + /// + /// A mapping between two es. + /// Used for serialisation exclusively. + /// + [Serializable] public class TournamentProgression { public int SourceID; diff --git a/osu.Game.Tournament/Models/TournamentRound.cs b/osu.Game.Tournament/Models/TournamentRound.cs index e325ad4b96..35215e90c5 100644 --- a/osu.Game.Tournament/Models/TournamentRound.cs +++ b/osu.Game.Tournament/Models/TournamentRound.cs @@ -8,6 +8,9 @@ using osu.Framework.Bindables; namespace osu.Game.Tournament.Models { + /// + /// A tournament round, containing many matches, generally executed in a short time period. + /// [Serializable] public class TournamentRound { diff --git a/osu.Game.Tournament/Models/TournamentTeam.cs b/osu.Game.Tournament/Models/TournamentTeam.cs index eea1ef8104..54b8a35180 100644 --- a/osu.Game.Tournament/Models/TournamentTeam.cs +++ b/osu.Game.Tournament/Models/TournamentTeam.cs @@ -8,6 +8,9 @@ using osu.Game.Users; namespace osu.Game.Tournament.Models { + /// + /// A team representation. For official tournaments this is generally a country. + /// [Serializable] public class TournamentTeam { From 21138e6e2caf7a4e6f5fef7bc5092cbf9915d7be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 15:05:55 +0900 Subject: [PATCH 1180/2854] Fix mismatching filenames --- ...eMatchPairings.cs => TestSceneDrawableTournamentMatch.cs} | 5 ++--- .../{DrawableMatchPairing.cs => DrawableTournamentMatch.cs} | 0 2 files changed, 2 insertions(+), 3 deletions(-) rename osu.Game.Tournament.Tests/{TestSceneMatchPairings.cs => TestSceneDrawableTournamentMatch.cs} (96%) rename osu.Game.Tournament/Screens/Ladder/Components/{DrawableMatchPairing.cs => DrawableTournamentMatch.cs} (100%) diff --git a/osu.Game.Tournament.Tests/TestSceneMatchPairings.cs b/osu.Game.Tournament.Tests/TestSceneDrawableTournamentMatch.cs similarity index 96% rename from osu.Game.Tournament.Tests/TestSceneMatchPairings.cs rename to osu.Game.Tournament.Tests/TestSceneDrawableTournamentMatch.cs index 9c8ee2965e..c3a4519597 100644 --- a/osu.Game.Tournament.Tests/TestSceneMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestSceneDrawableTournamentMatch.cs @@ -12,17 +12,16 @@ using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament.Tests { - public class TestSceneMatches : OsuTestScene + public class TestSceneDrawableTournamentMatch : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { typeof(TournamentMatch), typeof(DrawableTournamentMatch), - typeof(DrawableMatchTeam), typeof(DrawableTournamentTeam), }; - public TestSceneMatches() + public TestSceneDrawableTournamentMatch() { Container level1; Container level2; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs similarity index 100% rename from osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs rename to osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs From 7652339dc0ff985abf5b5dd1f619b134696f7f84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 15:07:33 +0900 Subject: [PATCH 1181/2854] Remove test file --- osu.Game.Tournament.Tests/teams.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 osu.Game.Tournament.Tests/teams.json diff --git a/osu.Game.Tournament.Tests/teams.json b/osu.Game.Tournament.Tests/teams.json deleted file mode 100644 index 7df0040469..0000000000 --- a/osu.Game.Tournament.Tests/teams.json +++ /dev/null @@ -1 +0,0 @@ -[{"Players":[{"id":3632846,"username":"lxLucasxl"},{"id":7110363,"username":"BubShish"},{"id":5748843,"username":"Fisk-"},{"id":4585260,"username":"A b y s s"},{"id":9513273,"username":"VorticalEx"},{"id":7341471,"username":"Bossplays_02"}],"Name":"Argentina","Acronym":"ARG"},{"Players":[{"id":2956184,"username":"Lusty Platypus"},{"id":2145124,"username":"Spartan-"},{"id":4018184,"username":"Rek"},{"id":4247722,"username":"PotassiumF"},{"id":9527845,"username":"AngeLItchysick"},{"id":8832989,"username":"[Crz]Yukikaze-"}],"Name":"Australia","Acronym":"AUS"},{"Players":[{"id":9530019,"username":"Lothus"},{"id":2288363,"username":"SillyFangirl"},{"id":4917435,"username":"FelipeLink"},{"id":5691061,"username":"andreymc"},{"id":4794096,"username":"Shedin"},{"id":3224958,"username":"Lazarento"}],"Name":"Brazil","Acronym":"BRA"},{"Players":[{"id":2747704,"username":"Dawt"},{"id":7025841,"username":"CommandoBlack"},{"id":5390121,"username":"Piggy"},{"id":2198070,"username":"beary605"},{"id":2777647,"username":"Freeflow"},{"id":9675053,"username":"Kiyora"}],"Name":"Canada","Acronym":"CAN"},{"Players":[{"id":5281416,"username":"WalterToro"},{"id":2225008,"username":"Skalim"},{"id":469808,"username":"Sophti"},{"id":4686036,"username":"sebaex"},{"id":4116072,"username":"Arkener"},{"id":4531184,"username":"Raizenn"}],"Name":"Chile","Acronym":"CHL"},{"Players":[{"id":89545,"username":"ZhangFan"},{"id":7215250,"username":"[Crz]Mix0130"},{"id":7961511,"username":"[Crz]Hina"},{"id":7082178,"username":"[Crz]Satori"},{"id":6659363,"username":"Wilben_Chan"},{"id":5270332,"username":"[Crz]Lucifer"}],"Name":"China","Acronym":"CHN"},{"Players":[{"id":2883132,"username":"Jole"},{"id":5001658,"username":"FreakyHands"},{"id":4402263,"username":"mart732c"},{"id":6751666,"username":"tailsdk"},{"id":5352616,"username":"Kainura"},{"id":8969233,"username":"zyglrox"}],"Name":"Denmark","Acronym":"DNK"},{"Players":[{"id":8132964,"username":"Camopoltergeist"},{"id":4789005,"username":"princesswell"},{"id":9663200,"username":"--Vanilla--"},{"id":1982941,"username":"matti644"},{"id":8370443,"username":"Your Daughter"},{"id":8105584,"username":"Twist-X"}],"Name":"Finland","Acronym":"FIN"},{"Players":[{"id":1594604,"username":"Azubeur"},{"id":2284328,"username":"Elementaires"},{"id":3897919,"username":"AntoAa"},{"id":4056690,"username":"Todestrieb"},{"id":7190228,"username":"Cunu"},{"id":3909293,"username":"DemonWaves"}],"Name":"France","Acronym":"FRA"},{"Players":[{"id":4516252,"username":"Malox"},{"id":3357640,"username":"ElectroYan"},{"id":5587671,"username":"-Dom-"},{"id":9764403,"username":"tyro901"},{"id":7009106,"username":"Nediz"},{"id":6232245,"username":"LastExceed"}],"Name":"Germany","Acronym":"GER"},{"Players":[{"id":5417362,"username":"Mooncha"},{"id":2121137,"username":"ng051106"},{"id":4544555,"username":"Opean"},{"id":643394,"username":"Snow Note"}],"Name":"['Hong Kong']","Acronym":"HKG"},{"Players":[{"id":5767941,"username":"RemFangirl"},{"id":4557440,"username":"reyss"},{"id":5492871,"username":"LovelySerenade"},{"id":6045757,"username":"Nixeria-sama"},{"id":5114499,"username":"lombit"},{"id":3497139,"username":"LordBoker-"}],"Name":"Indonesia","Acronym":"IDN"},{"Players":[{"id":3461860,"username":"Yomiel"},{"id":5245132,"username":"BadIsTheNewGod"},{"id":3244389,"username":"Mura7797"},{"id":8889323,"username":"extramen"},{"id":8485394,"username":"Cribob"},{"id":6380163,"username":"CribobFanBoy"}],"Name":"Italy","Acronym":"ITA"},{"Players":[{"id":1824775,"username":"inteliser"},{"id":7540718,"username":"tinpura"},{"id":1847698,"username":"PiraTom"},{"id":10011429,"username":"[ misa ]"},{"id":8679066,"username":"mach_jp"},{"id":10242062,"username":"AMDuskia1996"}],"Name":"Japan","Acronym":"JPN"},{"Players":[{"id":3946113,"username":"idqoos123"},{"id":10543278,"username":"hh27v5Fangirl"},{"id":8566617,"username":"capchon"},{"id":5315736,"username":"my2tic"}],"Name":"Macau","Acronym":"MAC"},{"Players":[{"id":7727987,"username":"Neokje"},{"id":8287005,"username":"[MY]xRay"},{"id":9627666,"username":"Minisora"},{"id":6237337,"username":"watarakisah"},{"id":6363947,"username":"Kiritolow"},{"id":4477497,"username":"cheewee10"}],"Name":"Malaysia","Acronym":"MYS"},{"Players":[{"id":1098581,"username":"mrdawn2"},{"id":9369363,"username":"TheSnooperPS"},{"id":6964358,"username":"Redenor"},{"id":9630674,"username":"Freek"},{"id":2827823,"username":"Boots"},{"id":5183940,"username":"2fast4you98"}],"Name":"Netherlands","Acronym":"NLD"},{"Players":[{"id":86188,"username":"Staiain"},{"id":7676585,"username":"Bizarrely_F4st"},{"id":3494742,"username":"KarlF"},{"id":3750387,"username":"Falniir"},{"id":9000473,"username":"Jesen"},{"id":2764122,"username":"Hjeg"}],"Name":"Norway","Acronym":"NOR"},{"Players":[{"id":914472,"username":"akuma123"},{"id":6114633,"username":"DaZeRo5"},{"id":11885200,"username":"DaKub"},{"id":10218427,"username":"Ovento17"}],"Name":"Peru","Acronym":"PER"},{"Players":[{"id":2039089,"username":"arcwinolivirus"},{"id":4469895,"username":"SurfChu85"},{"id":2471512,"username":"JztCallMeRon"},{"id":9770359,"username":"Toyohime-"},{"id":2722489,"username":"Cielo Day"},{"id":3770641,"username":"Ainyan"}],"Name":"Philippines","Acronym":"PHL"},{"Players":[{"id":743282,"username":"Tidek"},{"id":1654221,"username":"Hudonom"},{"id":6382502,"username":"Kroly-"},{"id":6905790,"username":"Arkitev"},{"id":2235750,"username":"_underjoy"},{"id":3353343,"username":"[-Agonys-]"}],"Name":"Poland","Acronym":"POL"},{"Players":[{"id":9074986,"username":"AngeloLagusa"},{"id":5145890,"username":"Jormungand"},{"id":9847747,"username":"MAZAFUKER1337"},{"id":8035172,"username":"fegasaren"},{"id":7767168,"username":"claer"}],"Name":"['Russian Federation']","Acronym":"RUS"},{"Players":[{"id":7199159,"username":"ByeForNow"},{"id":876528,"username":"Tamaneko"},{"id":8612061,"username":"Polytetral"},{"id":7462804,"username":"Lindyes"},{"id":4574597,"username":"OrienST8"},{"id":9362562,"username":"LuigiClaren"}],"Name":"Singapore","Acronym":"SGP"},{"Players":[{"id":6699923,"username":"SuddenDeath"},{"id":7014697,"username":"Estonians"},{"id":8474029,"username":"wonder5193"},{"id":8283444,"username":"[ Special ]"},{"id":903155,"username":"Nausicaa"},{"id":7945868,"username":"SnowScent"}],"Name":"['South Korea']","Acronym":"KOR"},{"Players":[{"id":3154852,"username":"aitor98"},{"id":8141215,"username":"David5_"},{"id":7935867,"username":"miguel-580"},{"id":6809566,"username":"itsdarious555"},{"id":8497100,"username":"GreenSoul"}],"Name":"Spain","Acronym":"ESP"},{"Players":[{"id":1612580,"username":"Vent"},{"id":6872025,"username":"Couil"},{"id":2229274,"username":"Xytox"},{"id":4899311,"username":"Stug"},{"id":5045509,"username":"YoShiZoRi"},{"id":3918056,"username":"Craty"}],"Name":"Sweden","Acronym":"SWE"},{"Players":[{"id":4952941,"username":"Gamer97"},{"id":8642966,"username":"Adyrem"},{"id":8372292,"username":"doere_"},{"id":9593126,"username":"Monogai"},{"id":3974114,"username":"Haprapra"},{"id":2573716,"username":"Akayro"}],"Name":"Switzerland","Acronym":"CHE"},{"Players":[{"id":766374,"username":"LostCool"},{"id":2838908,"username":"4ksrub"},{"id":6535376,"username":"SharpKunG1412"},{"id":2772110,"username":"BossMadWolf"},{"id":8521723,"username":"MyZterioN-"},{"id":6456531,"username":"-[DaNieL_TH]-"}],"Name":"Thailand","Acronym":"THA"},{"Players":[{"id":2656856,"username":"Sakaki"},{"id":6193819,"username":"SaKuRaLaN"},{"id":1990582,"username":"mspstommy"},{"id":8819232,"username":"Tamamo Desu"},{"id":11531528,"username":"Red MewFew"},{"id":1967808,"username":"luckygino"}],"Name":"Taiwan","Acronym":"TWN"},{"Players":[{"id":3359035,"username":"Amascite"},{"id":4168230,"username":"PikachuNick"},{"id":3617889,"username":"itsjakey"},{"id":3799946,"username":"xSnaggles"},{"id":6814203,"username":"Civilization"},{"id":6701945,"username":"Domblade"}],"Name":"['United Kingdom']","Acronym":"GBR"},{"Players":[{"id":7616811,"username":"TheToaphster"},{"id":2141612,"username":"stupud man"},{"id":7687954,"username":"Neuro-"},{"id":3251373,"username":"-Electro-"},{"id":5610085,"username":"EtienneXC"},{"id":2594280,"username":"Chrubble"}],"Name":"['United States']","Acronym":"USA"},{"Players":[{"id":2243452,"username":"Nakatoru"},{"id":8065567,"username":"Aezlack"},{"id":8301758,"username":"Edvo"},{"id":2140739,"username":"[_Chichinya_]"},{"id":8198818,"username":"[_Gearfrik_]"},{"id":1489811,"username":"_Yisus_"}],"Name":"Venezuela","Acronym":"VEN"}] \ No newline at end of file From 7c163ad911f4c29b927dbd391cd000b1e0503802 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 15:08:36 +0900 Subject: [PATCH 1182/2854] Move font-related code out of TournamentGame --- osu.Game.Tournament/TournamentFont.cs | 5 +++++ osu.Game.Tournament/TournamentGame.cs | 29 +-------------------------- 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tournament/TournamentFont.cs b/osu.Game.Tournament/TournamentFont.cs index d2925d7632..f9e60ff2bc 100644 --- a/osu.Game.Tournament/TournamentFont.cs +++ b/osu.Game.Tournament/TournamentFont.cs @@ -67,4 +67,9 @@ namespace osu.Game.Tournament return weightString; } } + + public enum TournamentTypeface + { + Aquatico + } } diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 42e4ab3c13..7dbcf37af6 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; using osu.Game.Graphics.Cursor; namespace osu.Game.Tournament @@ -20,33 +18,8 @@ namespace osu.Game.Tournament Child = new TournamentSceneManager() }); + // we don't want to show the menu cursor as it would appear on stream output. MenuCursorContainer.Cursor.Alpha = 0; } } - - public static class TournamentFontExtensions - { - /// - /// Creates a new by applying adjustments to this . - /// - /// The base . - /// The font typeface. If null, the value is copied from this . - /// The text size. If null, the value is copied from this . - /// The font weight. If null, the value is copied from this . - /// Whether the font is italic. If null, the value is copied from this . - /// Whether all characters should be spaced apart the same distance. If null, the value is copied from this . - /// The resulting . - public static FontUsage With(this FontUsage usage, TournamentTypeface? typeface = null, float? size = null, FontWeight? weight = null, bool? italics = null, bool? fixedWidth = null) - { - string familyString = typeface != null ? TournamentFont.GetFamilyString(typeface.Value) : usage.Family; - string weightString = weight != null ? TournamentFont.GetWeightString(familyString, weight.Value) : usage.Weight; - - return usage.With(familyString, size, weightString, italics, fixedWidth); - } - } - - public enum TournamentTypeface - { - Aquatico - } } From 3db6913a9c107d6b8b670833ffe419f2f72051e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 15:15:28 +0900 Subject: [PATCH 1183/2854] Rename editor screens removing plurals --- ...oupingsEditorScreen.cs => TestSceneRoundEditorScreen.cs} | 4 ++-- ...eneTeamsEditorScreen.cs => TestSceneTeamEditorScreen.cs} | 6 +++--- .../Editors/{TeamsEditorScreen.cs => TeamEditorScreen.cs} | 4 ++-- osu.Game.Tournament/TournamentSceneManager.cs | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) rename osu.Game.Tournament.Tests/{TestSceneGroupingsEditorScreen.cs => TestSceneRoundEditorScreen.cs} (72%) rename osu.Game.Tournament.Tests/{TestSceneTeamsEditorScreen.cs => TestSceneTeamEditorScreen.cs} (64%) rename osu.Game.Tournament/Screens/Editors/{TeamsEditorScreen.cs => TeamEditorScreen.cs} (99%) diff --git a/osu.Game.Tournament.Tests/TestSceneGroupingsEditorScreen.cs b/osu.Game.Tournament.Tests/TestSceneRoundEditorScreen.cs similarity index 72% rename from osu.Game.Tournament.Tests/TestSceneGroupingsEditorScreen.cs rename to osu.Game.Tournament.Tests/TestSceneRoundEditorScreen.cs index e0a6f8e8b9..9c1207a718 100644 --- a/osu.Game.Tournament.Tests/TestSceneGroupingsEditorScreen.cs +++ b/osu.Game.Tournament.Tests/TestSceneRoundEditorScreen.cs @@ -5,9 +5,9 @@ using osu.Game.Tournament.Screens.Editors; namespace osu.Game.Tournament.Tests { - public class TestSceneGroupingsEditorScreen : LadderTestScene + public class TestSceneRoundEditorScreen : LadderTestScene { - public TestSceneGroupingsEditorScreen() + public TestSceneRoundEditorScreen() { Add(new RoundEditorScreen()); } diff --git a/osu.Game.Tournament.Tests/TestSceneTeamsEditorScreen.cs b/osu.Game.Tournament.Tests/TestSceneTeamEditorScreen.cs similarity index 64% rename from osu.Game.Tournament.Tests/TestSceneTeamsEditorScreen.cs rename to osu.Game.Tournament.Tests/TestSceneTeamEditorScreen.cs index 60323e1d84..df0b79d8a9 100644 --- a/osu.Game.Tournament.Tests/TestSceneTeamsEditorScreen.cs +++ b/osu.Game.Tournament.Tests/TestSceneTeamEditorScreen.cs @@ -5,11 +5,11 @@ using osu.Game.Tournament.Screens.Editors; namespace osu.Game.Tournament.Tests { - public class TestSceneTeamsEditorScreen : LadderTestScene + public class TestSceneTeamEditorScreen : LadderTestScene { - public TestSceneTeamsEditorScreen() + public TestSceneTeamEditorScreen() { - Add(new TeamsEditorScreen()); + Add(new TeamEditorScreen()); } } } diff --git a/osu.Game.Tournament/Screens/Editors/TeamsEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs similarity index 99% rename from osu.Game.Tournament/Screens/Editors/TeamsEditorScreen.cs rename to osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs index 1bf3f1c6b9..ff272d5123 100644 --- a/osu.Game.Tournament/Screens/Editors/TeamsEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs @@ -20,11 +20,11 @@ using osuTK; namespace osu.Game.Tournament.Screens.Editors { - public class TeamsEditorScreen : TournamentScreen, IProvideVideo + public class TeamEditorScreen : TournamentScreen, IProvideVideo { private readonly FillFlowContainer items; - public TeamsEditorScreen() + public TeamEditorScreen() { AddRangeInternal(new Drawable[] { diff --git a/osu.Game.Tournament/TournamentSceneManager.cs b/osu.Game.Tournament/TournamentSceneManager.cs index 29f8eba579..4c255be463 100644 --- a/osu.Game.Tournament/TournamentSceneManager.cs +++ b/osu.Game.Tournament/TournamentSceneManager.cs @@ -72,7 +72,7 @@ namespace osu.Game.Tournament new ScheduleScreen(), new LadderScreen(), new LadderEditorScreen(), - new TeamsEditorScreen(), + new TeamEditorScreen(), new RoundEditorScreen(), new ShowcaseScreen(), new MapPoolScreen(), @@ -106,7 +106,7 @@ namespace osu.Game.Tournament Direction = FillDirection.Vertical, Children = new Drawable[] { - new OsuButton { RelativeSizeAxes = Axes.X, Text = "Team Editor", Action = () => SetScreen(typeof(TeamsEditorScreen)) }, + new OsuButton { RelativeSizeAxes = Axes.X, Text = "Team Editor", Action = () => SetScreen(typeof(TeamEditorScreen)) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Rounds Editor", Action = () => SetScreen(typeof(RoundEditorScreen)) }, new OsuButton { RelativeSizeAxes = Axes.X, Text = "Bracket Editor", Action = () => SetScreen(typeof(LadderEditorScreen)) }, new Container { RelativeSizeAxes = Axes.X, Height = 50 }, From 1a9226365284a02c676f49fce7ddc774d421ac58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 15:18:06 +0900 Subject: [PATCH 1184/2854] Ignore parse errors rather than dying --- osu.Game.Tournament/TournamentGameBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index fb96641bcf..9f63cc2302 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using System.Drawing; using System.IO; @@ -116,7 +115,8 @@ namespace osu.Game.Tournament var src = ladder.Matches.FirstOrDefault(p => p.ID == pair.SourceID); var dest = ladder.Matches.FirstOrDefault(p => p.ID == pair.TargetID); - if (src == null) throw new InvalidOperationException(); + if (src == null) + continue; if (dest != null) { From fa61b08a05211ddcda53dc68a238c68ef703ecdd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 15:20:28 +0900 Subject: [PATCH 1185/2854] Mark LadderTestScene abstract --- osu.Game.Tournament.Tests/LadderTestScene.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament.Tests/LadderTestScene.cs b/osu.Game.Tournament.Tests/LadderTestScene.cs index 5bb8112157..b49341d0d1 100644 --- a/osu.Game.Tournament.Tests/LadderTestScene.cs +++ b/osu.Game.Tournament.Tests/LadderTestScene.cs @@ -7,7 +7,7 @@ using osu.Game.Tournament.Models; namespace osu.Game.Tournament.Tests { - public class LadderTestScene : OsuTestScene + public abstract class LadderTestScene : OsuTestScene { [Resolved] protected LadderInfo Ladder { get; private set; } From 3fcb8081dd9bcab26656b7aa15d652d1ecc1c09f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 15:20:36 +0900 Subject: [PATCH 1186/2854] Remove unused ruleset bindable --- osu.Game.Tournament/TournamentGameBase.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 9f63cc2302..2f8d084848 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -17,7 +17,6 @@ using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests; -using osu.Game.Rulesets; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Models; using osuTK.Input; @@ -34,8 +33,6 @@ namespace osu.Game.Tournament private DependencyContainer dependencies; - private readonly Bindable ruleset = new Bindable(); - private Bindable windowSize; private FileBasedIPC ipc; From 926a11ab8ccd3f7728ddca773e997c8e73f005b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 15:28:36 +0900 Subject: [PATCH 1187/2854] Group, rename and standardise tests --- .../TestSceneDrawableTournamentMatch.cs | 3 +-- .../TestSceneMatchScoreDisplay.cs | 2 +- .../TestSceneTournamentBeatmapPanel.cs} | 11 ++------- .../TestSceneTournamentMatchChatDisplay.cs | 2 +- .../{ => Screens}/TestSceneGameplayScreen.cs | 2 +- .../Screens/TestSceneLadderEditorScreen.cs | 23 +++++++++++++++++++ .../TestSceneLadderScreen.cs} | 4 ++-- .../TestSceneMapPoolScreen.cs} | 4 ++-- .../TestSceneRoundEditorScreen.cs | 2 +- .../TestSceneScheduleScreen.cs} | 11 ++------- .../TestSceneShowcaseScreen.cs} | 11 ++------- .../TestSceneTeamEditorScreen.cs | 2 +- .../TestSceneTeamIntroScreen.cs} | 4 ++-- .../TestSceneTeamWinScreen.cs} | 4 ++-- ....cs => TestSceneTournamentSceneManager.cs} | 2 +- .../osu.Game.Tournament.csproj | 3 --- 16 files changed, 44 insertions(+), 46 deletions(-) rename osu.Game.Tournament.Tests/{ => Components}/TestSceneDrawableTournamentMatch.cs (97%) rename osu.Game.Tournament.Tests/{ => Components}/TestSceneMatchScoreDisplay.cs (96%) rename osu.Game.Tournament.Tests/{TestSceneBeatmapPanel.cs => Components/TestSceneTournamentBeatmapPanel.cs} (80%) rename osu.Game.Tournament.Tests/{ => Components}/TestSceneTournamentMatchChatDisplay.cs (98%) rename osu.Game.Tournament.Tests/{ => Screens}/TestSceneGameplayScreen.cs (93%) create mode 100644 osu.Game.Tournament.Tests/Screens/TestSceneLadderEditorScreen.cs rename osu.Game.Tournament.Tests/{TestSceneLadderManager.cs => Screens/TestSceneLadderScreen.cs} (84%) rename osu.Game.Tournament.Tests/{TestSceneMapPool.cs => Screens/TestSceneMapPoolScreen.cs} (84%) rename osu.Game.Tournament.Tests/{ => Screens}/TestSceneRoundEditorScreen.cs (89%) rename osu.Game.Tournament.Tests/{TestSceneSchedule.cs => Screens/TestSceneScheduleScreen.cs} (60%) rename osu.Game.Tournament.Tests/{TestSceneShowcase.cs => Screens/TestSceneShowcaseScreen.cs} (60%) rename osu.Game.Tournament.Tests/{ => Screens}/TestSceneTeamEditorScreen.cs (89%) rename osu.Game.Tournament.Tests/{TestSceneTeamIntro.cs => Screens/TestSceneTeamIntroScreen.cs} (91%) rename osu.Game.Tournament.Tests/{TestSceneTeamWin.cs => Screens/TestSceneTeamWinScreen.cs} (91%) rename osu.Game.Tournament.Tests/{TestSceneSceneManager.cs => TestSceneTournamentSceneManager.cs} (87%) diff --git a/osu.Game.Tournament.Tests/TestSceneDrawableTournamentMatch.cs b/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentMatch.cs similarity index 97% rename from osu.Game.Tournament.Tests/TestSceneDrawableTournamentMatch.cs rename to osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentMatch.cs index c3a4519597..f329623703 100644 --- a/osu.Game.Tournament.Tests/TestSceneDrawableTournamentMatch.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentMatch.cs @@ -10,14 +10,13 @@ using osu.Game.Tournament.Components; using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.Ladder.Components; -namespace osu.Game.Tournament.Tests +namespace osu.Game.Tournament.Tests.Components { public class TestSceneDrawableTournamentMatch : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { typeof(TournamentMatch), - typeof(DrawableTournamentMatch), typeof(DrawableTournamentTeam), }; diff --git a/osu.Game.Tournament.Tests/TestSceneMatchScoreDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneMatchScoreDisplay.cs similarity index 96% rename from osu.Game.Tournament.Tests/TestSceneMatchScoreDisplay.cs rename to osu.Game.Tournament.Tests/Components/TestSceneMatchScoreDisplay.cs index 7e9b83a61b..72d9eb0e07 100644 --- a/osu.Game.Tournament.Tests/TestSceneMatchScoreDisplay.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneMatchScoreDisplay.cs @@ -7,7 +7,7 @@ using osu.Framework.MathUtils; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Screens.Gameplay.Components; -namespace osu.Game.Tournament.Tests +namespace osu.Game.Tournament.Tests.Components { public class TestSceneMatchScoreDisplay : LadderTestScene { diff --git a/osu.Game.Tournament.Tests/TestSceneBeatmapPanel.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs similarity index 80% rename from osu.Game.Tournament.Tests/TestSceneBeatmapPanel.cs rename to osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs index 50169adad3..77fa411058 100644 --- a/osu.Game.Tournament.Tests/TestSceneBeatmapPanel.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; @@ -13,9 +11,9 @@ using osu.Game.Rulesets; using osu.Game.Tests.Visual; using osu.Game.Tournament.Components; -namespace osu.Game.Tournament.Tests +namespace osu.Game.Tournament.Tests.Components { - public class TestSceneBeatmapPanel : OsuTestScene + public class TestSceneTournamentBeatmapPanel : OsuTestScene { [Resolved] private IAPIProvider api { get; set; } @@ -23,11 +21,6 @@ namespace osu.Game.Tournament.Tests [Resolved] private RulesetStore rulesets { get; set; } - public override IReadOnlyList RequiredTypes => new[] - { - typeof(TournamentBeatmapPanel), - }; - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Tournament.Tests/TestSceneTournamentMatchChatDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentMatchChatDisplay.cs similarity index 98% rename from osu.Game.Tournament.Tests/TestSceneTournamentMatchChatDisplay.cs rename to osu.Game.Tournament.Tests/Components/TestSceneTournamentMatchChatDisplay.cs index 829d8629e5..41d32d9448 100644 --- a/osu.Game.Tournament.Tests/TestSceneTournamentMatchChatDisplay.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentMatchChatDisplay.cs @@ -11,7 +11,7 @@ using osu.Game.Tournament.IPC; using osu.Game.Tournament.Models; using osu.Game.Users; -namespace osu.Game.Tournament.Tests +namespace osu.Game.Tournament.Tests.Components { public class TestSceneTournamentMatchChatDisplay : OsuTestScene { diff --git a/osu.Game.Tournament.Tests/TestSceneGameplayScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs similarity index 93% rename from osu.Game.Tournament.Tests/TestSceneGameplayScreen.cs rename to osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs index 74d8615db0..201736f38a 100644 --- a/osu.Game.Tournament.Tests/TestSceneGameplayScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs @@ -6,7 +6,7 @@ using osu.Game.Tests.Visual; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Gameplay; -namespace osu.Game.Tournament.Tests +namespace osu.Game.Tournament.Tests.Screens { public class TestSceneGameplayScreen : OsuTestScene { diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneLadderEditorScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneLadderEditorScreen.cs new file mode 100644 index 0000000000..a45c5de2bd --- /dev/null +++ b/osu.Game.Tournament.Tests/Screens/TestSceneLadderEditorScreen.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics.Cursor; +using osu.Game.Tournament.Screens.Editors; + +namespace osu.Game.Tournament.Tests.Screens +{ + public class TestSceneLadderEditorScreen : LadderTestScene + { + [BackgroundDependencyLoader] + private void load() + { + Add(new OsuContextMenuContainer + { + RelativeSizeAxes = Axes.Both, + Child = new LadderEditorScreen() + }); + } + } +} diff --git a/osu.Game.Tournament.Tests/TestSceneLadderManager.cs b/osu.Game.Tournament.Tests/Screens/TestSceneLadderScreen.cs similarity index 84% rename from osu.Game.Tournament.Tests/TestSceneLadderManager.cs rename to osu.Game.Tournament.Tests/Screens/TestSceneLadderScreen.cs index c9ea740f92..2be0564c82 100644 --- a/osu.Game.Tournament.Tests/TestSceneLadderManager.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneLadderScreen.cs @@ -6,9 +6,9 @@ using osu.Framework.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Tournament.Screens.Ladder; -namespace osu.Game.Tournament.Tests +namespace osu.Game.Tournament.Tests.Screens { - public class TestSceneLadderManager : LadderTestScene + public class TestSceneLadderScreen : LadderTestScene { [BackgroundDependencyLoader] private void load() diff --git a/osu.Game.Tournament.Tests/TestSceneMapPool.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs similarity index 84% rename from osu.Game.Tournament.Tests/TestSceneMapPool.cs rename to osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs index 43f4831a2a..a7011c6d3c 100644 --- a/osu.Game.Tournament.Tests/TestSceneMapPool.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs @@ -6,9 +6,9 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Game.Tournament.Screens.MapPool; -namespace osu.Game.Tournament.Tests +namespace osu.Game.Tournament.Tests.Screens { - public class TestSceneMapPool : LadderTestScene + public class TestSceneMapPoolScreen : LadderTestScene { public override IReadOnlyList RequiredTypes => new[] { diff --git a/osu.Game.Tournament.Tests/TestSceneRoundEditorScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneRoundEditorScreen.cs similarity index 89% rename from osu.Game.Tournament.Tests/TestSceneRoundEditorScreen.cs rename to osu.Game.Tournament.Tests/Screens/TestSceneRoundEditorScreen.cs index 9c1207a718..6203d68e80 100644 --- a/osu.Game.Tournament.Tests/TestSceneRoundEditorScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneRoundEditorScreen.cs @@ -3,7 +3,7 @@ using osu.Game.Tournament.Screens.Editors; -namespace osu.Game.Tournament.Tests +namespace osu.Game.Tournament.Tests.Screens { public class TestSceneRoundEditorScreen : LadderTestScene { diff --git a/osu.Game.Tournament.Tests/TestSceneSchedule.cs b/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs similarity index 60% rename from osu.Game.Tournament.Tests/TestSceneSchedule.cs rename to osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs index 00eb4c4e41..f3e65919eb 100644 --- a/osu.Game.Tournament.Tests/TestSceneSchedule.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs @@ -1,21 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Game.Tests.Visual; using osu.Game.Tournament.Screens.Schedule; -namespace osu.Game.Tournament.Tests +namespace osu.Game.Tournament.Tests.Screens { - public class TestSceneSchedule : OsuTestScene + public class TestSceneScheduleScreen : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ScheduleScreen) - }; - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Tournament.Tests/TestSceneShowcase.cs b/osu.Game.Tournament.Tests/Screens/TestSceneShowcaseScreen.cs similarity index 60% rename from osu.Game.Tournament.Tests/TestSceneShowcase.cs rename to osu.Game.Tournament.Tests/Screens/TestSceneShowcaseScreen.cs index a4d518eedd..edf1477b06 100644 --- a/osu.Game.Tournament.Tests/TestSceneShowcase.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneShowcaseScreen.cs @@ -1,21 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Game.Tests.Visual; using osu.Game.Tournament.Screens.Showcase; -namespace osu.Game.Tournament.Tests +namespace osu.Game.Tournament.Tests.Screens { - public class TestSceneShowcase : OsuTestScene + public class TestSceneShowcaseScreen : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ShowcaseScreen) - }; - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Tournament.Tests/TestSceneTeamEditorScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneTeamEditorScreen.cs similarity index 89% rename from osu.Game.Tournament.Tests/TestSceneTeamEditorScreen.cs rename to osu.Game.Tournament.Tests/Screens/TestSceneTeamEditorScreen.cs index df0b79d8a9..126e0c2fda 100644 --- a/osu.Game.Tournament.Tests/TestSceneTeamEditorScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneTeamEditorScreen.cs @@ -3,7 +3,7 @@ using osu.Game.Tournament.Screens.Editors; -namespace osu.Game.Tournament.Tests +namespace osu.Game.Tournament.Tests.Screens { public class TestSceneTeamEditorScreen : LadderTestScene { diff --git a/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs b/osu.Game.Tournament.Tests/Screens/TestSceneTeamIntroScreen.cs similarity index 91% rename from osu.Game.Tournament.Tests/TestSceneTeamIntro.cs rename to osu.Game.Tournament.Tests/Screens/TestSceneTeamIntroScreen.cs index 6b31fd2742..3d340e393c 100644 --- a/osu.Game.Tournament.Tests/TestSceneTeamIntro.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneTeamIntroScreen.cs @@ -8,9 +8,9 @@ using osu.Framework.Graphics; using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.TeamIntro; -namespace osu.Game.Tournament.Tests +namespace osu.Game.Tournament.Tests.Screens { - public class TestSceneTeamIntro : LadderTestScene + public class TestSceneTeamIntroScreen : LadderTestScene { [Cached] private readonly Bindable currentMatch = new Bindable(); diff --git a/osu.Game.Tournament.Tests/TestSceneTeamWin.cs b/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs similarity index 91% rename from osu.Game.Tournament.Tests/TestSceneTeamWin.cs rename to osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs index d195ad42ca..6f5e17a36e 100644 --- a/osu.Game.Tournament.Tests/TestSceneTeamWin.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs @@ -8,9 +8,9 @@ using osu.Framework.Graphics; using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.TeamWin; -namespace osu.Game.Tournament.Tests +namespace osu.Game.Tournament.Tests.Screens { - public class TestSceneTeamWin : LadderTestScene + public class TestSceneTeamWinScreen : LadderTestScene { [Cached] private readonly Bindable currentMatch = new Bindable(); diff --git a/osu.Game.Tournament.Tests/TestSceneSceneManager.cs b/osu.Game.Tournament.Tests/TestSceneTournamentSceneManager.cs similarity index 87% rename from osu.Game.Tournament.Tests/TestSceneSceneManager.cs rename to osu.Game.Tournament.Tests/TestSceneTournamentSceneManager.cs index aa333e39b1..378614343a 100644 --- a/osu.Game.Tournament.Tests/TestSceneSceneManager.cs +++ b/osu.Game.Tournament.Tests/TestSceneTournamentSceneManager.cs @@ -7,7 +7,7 @@ using osu.Game.Tests.Visual; namespace osu.Game.Tournament.Tests { - public class TestSceneSceneManager : OsuTestScene + public class TestSceneTournamentSceneManager : OsuTestScene { [BackgroundDependencyLoader] private void load(Storage storage) diff --git a/osu.Game.Tournament/osu.Game.Tournament.csproj b/osu.Game.Tournament/osu.Game.Tournament.csproj index 8412166250..8adff80820 100644 --- a/osu.Game.Tournament/osu.Game.Tournament.csproj +++ b/osu.Game.Tournament/osu.Game.Tournament.csproj @@ -10,7 +10,4 @@ - - - \ No newline at end of file From e4eae3a6d5868952ae0d31492456cc0534d7f069 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Jun 2019 15:40:57 +0900 Subject: [PATCH 1188/2854] Move rider configurations into folders --- ...RulesetTests__catch_.xml => CatchRuleset__Tests_.xml} | 9 ++++++--- ...RulesetTests__mania_.xml => ManiaRuleset__Tests_.xml} | 9 ++++++--- .../{RulesetTests__osu__.xml => OsuRuleset__Tests_.xml} | 9 ++++++--- ...RulesetTests__taiko_.xml => TaikoRuleset__Tests_.xml} | 9 ++++++--- .../{osu___Tournament_.xml => Tournament.xml} | 6 +++--- .../{TournamentTests.xml => Tournament__Tests_.xml} | 7 +++++-- .idea/.idea.osu/.idea/runConfigurations/osu_.xml | 2 +- .../{VisualTests.xml => osu___Tests_.xml} | 2 +- 8 files changed, 34 insertions(+), 19 deletions(-) rename .idea/.idea.osu/.idea/runConfigurations/{RulesetTests__catch_.xml => CatchRuleset__Tests_.xml} (73%) rename .idea/.idea.osu/.idea/runConfigurations/{RulesetTests__mania_.xml => ManiaRuleset__Tests_.xml} (73%) rename .idea/.idea.osu/.idea/runConfigurations/{RulesetTests__osu__.xml => OsuRuleset__Tests_.xml} (72%) rename .idea/.idea.osu/.idea/runConfigurations/{RulesetTests__taiko_.xml => TaikoRuleset__Tests_.xml} (73%) rename .idea/.idea.osu/.idea/runConfigurations/{osu___Tournament_.xml => Tournament.xml} (78%) rename .idea/.idea.osu/.idea/runConfigurations/{TournamentTests.xml => Tournament__Tests_.xml} (77%) rename .idea/.idea.osu/.idea/runConfigurations/{VisualTests.xml => osu___Tests_.xml} (88%) diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml b/.idea/.idea.osu/.idea/runConfigurations/CatchRuleset__Tests_.xml similarity index 73% rename from .idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml rename to .idea/.idea.osu/.idea/runConfigurations/CatchRuleset__Tests_.xml index 2eff16cc91..6463dd6ea5 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/CatchRuleset__Tests_.xml @@ -1,18 +1,21 @@ - + \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__mania_.xml b/.idea/.idea.osu/.idea/runConfigurations/ManiaRuleset__Tests_.xml similarity index 73% rename from .idea/.idea.osu/.idea/runConfigurations/RulesetTests__mania_.xml rename to .idea/.idea.osu/.idea/runConfigurations/ManiaRuleset__Tests_.xml index cae9754560..0b63b2d966 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__mania_.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/ManiaRuleset__Tests_.xml @@ -1,18 +1,21 @@ - + \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__osu__.xml b/.idea/.idea.osu/.idea/runConfigurations/OsuRuleset__Tests_.xml similarity index 72% rename from .idea/.idea.osu/.idea/runConfigurations/RulesetTests__osu__.xml rename to .idea/.idea.osu/.idea/runConfigurations/OsuRuleset__Tests_.xml index 49ec93e1b3..750ece648b 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__osu__.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/OsuRuleset__Tests_.xml @@ -1,18 +1,21 @@ - + \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__taiko_.xml b/.idea/.idea.osu/.idea/runConfigurations/TaikoRuleset__Tests_.xml similarity index 73% rename from .idea/.idea.osu/.idea/runConfigurations/RulesetTests__taiko_.xml rename to .idea/.idea.osu/.idea/runConfigurations/TaikoRuleset__Tests_.xml index d0964c6f68..7b359a1ca0 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__taiko_.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/TaikoRuleset__Tests_.xml @@ -1,18 +1,21 @@ - + \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/osu___Tournament_.xml b/.idea/.idea.osu/.idea/runConfigurations/Tournament.xml similarity index 78% rename from .idea/.idea.osu/.idea/runConfigurations/osu___Tournament_.xml rename to .idea/.idea.osu/.idea/runConfigurations/Tournament.xml index a5f93489e8..3722f3dc04 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/osu___Tournament_.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/Tournament.xml @@ -1,6 +1,6 @@ - - /// The time to retrieve the sample info list from. /// - private List sampleInfoListAt(double time) + private List sampleInfoListAt(double time) { var curveData = HitObject as IHasCurve; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index 9e95be35fa..b3be08e1f7 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy switch (TotalColumns) { - case 8 when HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000: + case 8 when HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000: addToPattern(pattern, 0, generateHold); break; @@ -72,9 +72,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy }; if (hold.Head.Samples == null) - hold.Head.Samples = new List(); + hold.Head.Samples = new List(); - hold.Head.Samples.Add(new SampleInfo { Name = SampleInfo.HIT_NORMAL }); + hold.Head.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_NORMAL }); hold.Tail.Samples = HitObject.Samples; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index d13b21183b..decd159ee9 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -79,9 +79,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (!convertType.HasFlag(PatternType.KeepSingle)) { - if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && TotalColumns != 8) + if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH) && TotalColumns != 8) convertType |= PatternType.Mirror; - else if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)) + else if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP)) convertType |= PatternType.Gathered; } } @@ -263,7 +263,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// /// Whether this hit object can generate a note in the special column. /// - private bool hasSpecialColumn => HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH); + private bool hasSpecialColumn => HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH); /// /// Generates a random pattern. @@ -364,7 +364,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy break; } - if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)) + if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP)) p2 = 1; return GetRandomNoteCount(p2, p3, p4, p5); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index 1ba6d107be..c5a27205d6 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs @@ -248,9 +248,9 @@ namespace osu.Game.Rulesets.Osu.Tests private void createCatmull(int repeats = 0) { - var repeatSamples = new List>(); + var repeatSamples = new List>(); for (int i = 0; i < repeats; i++) - repeatSamples.Add(new List()); + repeatSamples.Add(new List()); var slider = new Slider { @@ -270,11 +270,11 @@ namespace osu.Game.Rulesets.Osu.Tests addSlider(slider, 3, 1); } - private List> createEmptySamples(int repeats) + private List> createEmptySamples(int repeats) { - var repeatSamples = new List>(); + var repeatSamples = new List>(); for (int i = 0; i < repeats; i++) - repeatSamples.Add(new List()); + repeatSamples.Add(new List()); return repeatSamples; } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index a8aec005d1..a4638c31f2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects /// internal float LazyTravelDistance; - public List> NodeSamples { get; set; } = new List>(); + public List> NodeSamples { get; set; } = new List>(); private int repeatCount; @@ -157,12 +157,12 @@ namespace osu.Game.Rulesets.Osu.Objects foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset)) { - var firstSample = Samples.Find(s => s.Name == SampleInfo.HIT_NORMAL) + var firstSample = Samples.Find(s => s.Name == HitSampleInfo.HIT_NORMAL) ?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933) - var sampleList = new List(); + var sampleList = new List(); if (firstSample != null) - sampleList.Add(new SampleInfo + sampleList.Add(new HitSampleInfo { Bank = firstSample.Bank, Volume = firstSample.Volume, @@ -225,7 +225,7 @@ namespace osu.Game.Rulesets.Osu.Objects } } - private List getNodeSamples(int nodeIndex) => + private List getNodeSamples(int nodeIndex) => nodeIndex < NodeSamples.Count ? NodeSamples[nodeIndex] : Samples; public override Judgement CreateJudgement() => new OsuJudgement(); diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs index 02300a5dde..8c1b0c4c62 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Tests { typeof(InputDrum), typeof(DrumSampleMapping), - typeof(SampleInfo), + typeof(HitSampleInfo), typeof(SampleControlPoint) }; diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs index d7fa661e8a..ad2596931d 100644 --- a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs +++ b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Taiko.Audio foreach (var s in samplePoints) { var centre = s.GetSampleInfo(); - var rim = s.GetSampleInfo(SampleInfo.HIT_CLAP); + var rim = s.GetSampleInfo(HitSampleInfo.HIT_CLAP); // todo: this is ugly centre.Namespace = "taiko"; @@ -43,9 +43,9 @@ namespace osu.Game.Rulesets.Taiko.Audio } } - private SkinnableSound addSound(SampleInfo sampleInfo) + private SkinnableSound addSound(HitSampleInfo hitSampleInfo) { - var drawable = new SkinnableSound(sampleInfo); + var drawable = new SkinnableSound(hitSampleInfo); Sounds.Add(drawable); return drawable; } diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index f8672037cd..f0cf8d9c7d 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -79,9 +79,9 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps var curveData = obj as IHasCurve; // Old osu! used hit sounding to determine various hit type information - List samples = obj.Samples; + List samples = obj.Samples; - bool strong = samples.Any(s => s.Name == SampleInfo.HIT_FINISH); + bool strong = samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH); if (distanceData != null) { @@ -117,15 +117,15 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength) { - List> allSamples = curveData != null ? curveData.NodeSamples : new List>(new[] { samples }); + List> allSamples = curveData != null ? curveData.NodeSamples : new List>(new[] { samples }); int i = 0; for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing) { - List currentSamples = allSamples[i]; - bool isRim = currentSamples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE); - strong = currentSamples.Any(s => s.Name == SampleInfo.HIT_FINISH); + List currentSamples = allSamples[i]; + bool isRim = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE); + strong = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_FINISH); if (isRim) { @@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps } else { - bool isRim = samples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE); + bool isRim = samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE); if (isRim) { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 119940536e..bd45b52d7b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } // Normal and clap samples are handled by the drum - protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != SampleInfo.HIT_NORMAL && s.Name != SampleInfo.HIT_CLAP); + protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP); protected override string SampleNamespace => "Taiko"; diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 5fd5fe342d..d087251e7e 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -354,14 +354,14 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.IsNotNull(curveData); Assert.AreEqual(new Vector2(192, 168), positionData.Position); Assert.AreEqual(956, hitObjects[0].StartTime); - Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL)); + Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == HitSampleInfo.HIT_NORMAL)); positionData = hitObjects[1] as IHasPosition; Assert.IsNotNull(positionData); Assert.AreEqual(new Vector2(304, 56), positionData.Position); Assert.AreEqual(1285, hitObjects[1].StartTime); - Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)); + Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP)); } } @@ -384,7 +384,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual("soft-hitnormal8", getTestableSampleInfo(hitObjects[4]).LookupNames.First()); } - SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]); + HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]); } [Test] @@ -402,7 +402,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual("normal-hitnormal3", getTestableSampleInfo(hitObjects[2]).LookupNames.First()); } - SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]); + HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]); } [Test] @@ -422,7 +422,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(70, getTestableSampleInfo(hitObjects[3]).Volume); } - SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]); + HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]); } [Test] @@ -438,34 +438,34 @@ namespace osu.Game.Tests.Beatmaps.Formats var slider1 = (ConvertSlider)hitObjects[0]; Assert.AreEqual(1, slider1.NodeSamples[0].Count); - Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[0][0].Name); + Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider1.NodeSamples[0][0].Name); Assert.AreEqual(1, slider1.NodeSamples[1].Count); - Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[1][0].Name); + Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider1.NodeSamples[1][0].Name); Assert.AreEqual(1, slider1.NodeSamples[2].Count); - Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[2][0].Name); + Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider1.NodeSamples[2][0].Name); var slider2 = (ConvertSlider)hitObjects[1]; Assert.AreEqual(2, slider2.NodeSamples[0].Count); - Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[0][0].Name); - Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[0][1].Name); + Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider2.NodeSamples[0][0].Name); + Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider2.NodeSamples[0][1].Name); Assert.AreEqual(2, slider2.NodeSamples[1].Count); - Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[1][0].Name); - Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[1][1].Name); + Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider2.NodeSamples[1][0].Name); + Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider2.NodeSamples[1][1].Name); Assert.AreEqual(2, slider2.NodeSamples[2].Count); - Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[2][0].Name); - Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[2][1].Name); + Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider2.NodeSamples[2][0].Name); + Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider2.NodeSamples[2][1].Name); var slider3 = (ConvertSlider)hitObjects[2]; Assert.AreEqual(2, slider3.NodeSamples[0].Count); - Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[0][0].Name); - Assert.AreEqual(SampleInfo.HIT_WHISTLE, slider3.NodeSamples[0][1].Name); + Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider3.NodeSamples[0][0].Name); + Assert.AreEqual(HitSampleInfo.HIT_WHISTLE, slider3.NodeSamples[0][1].Name); Assert.AreEqual(1, slider3.NodeSamples[1].Count); - Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[1][0].Name); + Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider3.NodeSamples[1][0].Name); Assert.AreEqual(2, slider3.NodeSamples[2].Count); - Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[2][0].Name); - Assert.AreEqual(SampleInfo.HIT_CLAP, slider3.NodeSamples[2][1].Name); + Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider3.NodeSamples[2][0].Name); + Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider3.NodeSamples[2][1].Name); } } diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index 39b7735a55..a725c58462 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -101,14 +101,14 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.IsNotNull(curveData); Assert.AreEqual(new Vector2(192, 168), positionData.Position); Assert.AreEqual(956, beatmap.HitObjects[0].StartTime); - Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL)); + Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == HitSampleInfo.HIT_NORMAL)); positionData = beatmap.HitObjects[1] as IHasPosition; Assert.IsNotNull(positionData); Assert.AreEqual(new Vector2(304, 56), positionData.Position); Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime); - Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)); + Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP)); } [TestCase(normal)] diff --git a/osu.Game/Audio/HitSampleInfo.cs b/osu.Game/Audio/HitSampleInfo.cs new file mode 100644 index 0000000000..23a74d3fa6 --- /dev/null +++ b/osu.Game/Audio/HitSampleInfo.cs @@ -0,0 +1,70 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; + +namespace osu.Game.Audio +{ + /// + /// Describes a gameplay hit sample. + /// + [Serializable] + public class HitSampleInfo : ISampleInfo + { + public const string HIT_WHISTLE = @"hitwhistle"; + public const string HIT_FINISH = @"hitfinish"; + public const string HIT_NORMAL = @"hitnormal"; + public const string HIT_CLAP = @"hitclap"; + + /// + /// An optional ruleset namespace. + /// + public string Namespace; + + /// + /// The bank to load the sample from. + /// + public string Bank; + + /// + /// The name of the sample to load. + /// + public string Name; + + /// + /// An optional suffix to provide priority lookup. Falls back to non-suffixed . + /// + public string Suffix; + + /// + /// The sample volume. + /// + public int Volume { get; set; } + + /// + /// Retrieve all possible filenames that can be used as a source, returned in order of preference (highest first). + /// + public virtual IEnumerable LookupNames + { + get + { + if (!string.IsNullOrEmpty(Namespace)) + { + if (!string.IsNullOrEmpty(Suffix)) + yield return $"{Namespace}/{Bank}-{Name}{Suffix}"; + + yield return $"{Namespace}/{Bank}-{Name}"; + } + + // check non-namespace as a fallback even when we have a namespace + if (!string.IsNullOrEmpty(Suffix)) + yield return $"{Bank}-{Name}{Suffix}"; + + yield return $"{Bank}-{Name}"; + } + } + + public HitSampleInfo Clone() => (HitSampleInfo)MemberwiseClone(); + } +} diff --git a/osu.Game/Audio/ISampleInfo.cs b/osu.Game/Audio/ISampleInfo.cs new file mode 100644 index 0000000000..4f81d37e78 --- /dev/null +++ b/osu.Game/Audio/ISampleInfo.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; + +namespace osu.Game.Audio +{ + public interface ISampleInfo + { + /// + /// Retrieve all possible filenames that can be used as a source, returned in order of preference (highest first). + /// + IEnumerable LookupNames { get; } + + int Volume { get; } + } +} diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs index 5bc6dce60b..66c07209f3 100644 --- a/osu.Game/Audio/SampleInfo.cs +++ b/osu.Game/Audio/SampleInfo.cs @@ -1,67 +1,24 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; namespace osu.Game.Audio { - [Serializable] - public class SampleInfo + /// + /// Describes a gameplay sample. + /// + public class SampleInfo : ISampleInfo { - public const string HIT_WHISTLE = @"hitwhistle"; - public const string HIT_FINISH = @"hitfinish"; - public const string HIT_NORMAL = @"hitnormal"; - public const string HIT_CLAP = @"hitclap"; + private readonly string sampleName; - /// - /// An optional ruleset namespace. - /// - public string Namespace; - - /// - /// The bank to load the sample from. - /// - public string Bank; - - /// - /// The name of the sample to load. - /// - public string Name; - - /// - /// An optional suffix to provide priority lookup. Falls back to non-suffixed . - /// - public string Suffix; - - /// - /// The sample volume. - /// - public int Volume; - - /// - /// Retrieve all possible filenames that can be used as a source, returned in order of preference (highest first). - /// - public virtual IEnumerable LookupNames + public SampleInfo(string sampleName) { - get - { - if (!string.IsNullOrEmpty(Namespace)) - { - if (!string.IsNullOrEmpty(Suffix)) - yield return $"{Namespace}/{Bank}-{Name}{Suffix}"; - - yield return $"{Namespace}/{Bank}-{Name}"; - } - - // check non-namespace as a fallback even when we have a namespace - if (!string.IsNullOrEmpty(Suffix)) - yield return $"{Bank}-{Name}{Suffix}"; - - yield return $"{Bank}-{Name}"; - } + this.sampleName = sampleName; } - public SampleInfo Clone() => (SampleInfo)MemberwiseClone(); + public IEnumerable LookupNames => new[] { sampleName }; + + public int Volume { get; set; } = 100; } } diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index 4c45bef862..7bc7a9056d 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -24,8 +24,8 @@ namespace osu.Game.Beatmaps.ControlPoints /// Create a SampleInfo based on the sample settings in this control point. /// /// The name of the same. - /// A populated . - public SampleInfo GetSampleInfo(string sampleName = SampleInfo.HIT_NORMAL) => new SampleInfo + /// A populated . + public HitSampleInfo GetSampleInfo(string sampleName = HitSampleInfo.HIT_NORMAL) => new HitSampleInfo { Bank = SampleBank, Name = sampleName, @@ -33,15 +33,15 @@ namespace osu.Game.Beatmaps.ControlPoints }; /// - /// Applies and to a if necessary, returning the modified . + /// Applies and to a if necessary, returning the modified . /// - /// The . This will not be modified. - /// The modified . This does not share a reference with . - public virtual SampleInfo ApplyTo(SampleInfo sampleInfo) + /// The . This will not be modified. + /// The modified . This does not share a reference with . + public virtual HitSampleInfo ApplyTo(HitSampleInfo hitSampleInfo) { - var newSampleInfo = sampleInfo.Clone(); - newSampleInfo.Bank = sampleInfo.Bank ?? SampleBank; - newSampleInfo.Volume = sampleInfo.Volume > 0 ? sampleInfo.Volume : SampleVolume; + var newSampleInfo = hitSampleInfo.Clone(); + newSampleInfo.Bank = hitSampleInfo.Bank ?? SampleBank; + newSampleInfo.Volume = hitSampleInfo.Volume > 0 ? hitSampleInfo.Volume : SampleVolume; return newSampleInfo; } diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 7b7e0e7101..7999c82761 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -193,9 +193,9 @@ namespace osu.Game.Beatmaps.Formats { public int CustomSampleBank; - public override SampleInfo ApplyTo(SampleInfo sampleInfo) + public override HitSampleInfo ApplyTo(HitSampleInfo hitSampleInfo) { - var baseInfo = base.ApplyTo(sampleInfo); + var baseInfo = base.ApplyTo(hitSampleInfo); if (string.IsNullOrEmpty(baseInfo.Suffix) && CustomSampleBank > 1) baseInfo.Suffix = CustomSampleBank.ToString(); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 35c3a0e5e4..1f6ca4dd73 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Objects.Drawables protected SkinnableSound Samples; - protected virtual IEnumerable GetSamples() => HitObject.Samples; + protected virtual IEnumerable GetSamples() => HitObject.Samples; private readonly Lazy> nestedHitObjects = new Lazy>(); public IEnumerable NestedHitObjects => nestedHitObjects.IsValueCreated ? nestedHitObjects.Value : Enumerable.Empty(); @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Objects.Drawables var samples = GetSamples().ToArray(); - if (samples.Any()) + if (samples.Length > 0) { if (HitObject.SampleControlPoint == null) throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}." diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index cede2e50d0..bf04963b76 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Objects /// public virtual double StartTime { get; set; } - private List samples; + private List samples; /// /// The samples to be played when this hit object is hit. @@ -38,9 +38,9 @@ namespace osu.Game.Rulesets.Objects /// and can be treated as the default samples for the hit object. /// /// - public List Samples + public List Samples { - get => samples ?? (samples = new List()); + get => samples ?? (samples = new List()); set => samples = value; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index 48f637dfe8..6e79d0b766 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch }; } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples) { newCombo |= forceNewCombo; comboOffset += extraComboOffset; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index c14f3b6a42..f5b1cbcebf 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -180,7 +180,7 @@ namespace osu.Game.Rulesets.Objects.Legacy } // Generate the final per-node samples - var nodeSamples = new List>(nodes); + var nodeSamples = new List>(nodes); for (int i = 0; i < nodes; i++) nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i])); @@ -291,7 +291,7 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The slider repeat count. /// The samples to be played when the slider nodes are hit. This includes the head and tail of the slider. /// The hit object. - protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples); + protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples); /// /// Creates a legacy Spinner-type hit object. @@ -312,14 +312,14 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The hold end time. protected abstract HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime); - private List convertSoundType(LegacySoundType type, SampleBankInfo bankInfo) + private List convertSoundType(LegacySoundType type, SampleBankInfo bankInfo) { // Todo: This should return the normal SampleInfos if the specified sample file isn't found, but that's a pretty edge-case scenario if (!string.IsNullOrEmpty(bankInfo.Filename)) { - return new List + return new List { - new FileSampleInfo + new FileHitSampleInfo { Filename = bankInfo.Filename, Volume = bankInfo.Volume @@ -327,12 +327,12 @@ namespace osu.Game.Rulesets.Objects.Legacy }; } - var soundTypes = new List + var soundTypes = new List { - new LegacySampleInfo + new LegacyHitSampleInfo { Bank = bankInfo.Normal, - Name = SampleInfo.HIT_NORMAL, + Name = HitSampleInfo.HIT_NORMAL, Volume = bankInfo.Volume, CustomSampleBank = bankInfo.CustomSampleBank } @@ -340,10 +340,10 @@ namespace osu.Game.Rulesets.Objects.Legacy if (type.HasFlag(LegacySoundType.Finish)) { - soundTypes.Add(new LegacySampleInfo + soundTypes.Add(new LegacyHitSampleInfo { Bank = bankInfo.Add, - Name = SampleInfo.HIT_FINISH, + Name = HitSampleInfo.HIT_FINISH, Volume = bankInfo.Volume, CustomSampleBank = bankInfo.CustomSampleBank }); @@ -351,10 +351,10 @@ namespace osu.Game.Rulesets.Objects.Legacy if (type.HasFlag(LegacySoundType.Whistle)) { - soundTypes.Add(new LegacySampleInfo + soundTypes.Add(new LegacyHitSampleInfo { Bank = bankInfo.Add, - Name = SampleInfo.HIT_WHISTLE, + Name = HitSampleInfo.HIT_WHISTLE, Volume = bankInfo.Volume, CustomSampleBank = bankInfo.CustomSampleBank }); @@ -362,10 +362,10 @@ namespace osu.Game.Rulesets.Objects.Legacy if (type.HasFlag(LegacySoundType.Clap)) { - soundTypes.Add(new LegacySampleInfo + soundTypes.Add(new LegacyHitSampleInfo { Bank = bankInfo.Add, - Name = SampleInfo.HIT_CLAP, + Name = HitSampleInfo.HIT_CLAP, Volume = bankInfo.Volume, CustomSampleBank = bankInfo.CustomSampleBank }); @@ -387,7 +387,7 @@ namespace osu.Game.Rulesets.Objects.Legacy public SampleBankInfo Clone() => (SampleBankInfo)MemberwiseClone(); } - private class LegacySampleInfo : SampleInfo + private class LegacyHitSampleInfo : HitSampleInfo { public int CustomSampleBank { @@ -399,7 +399,7 @@ namespace osu.Game.Rulesets.Objects.Legacy } } - private class FileSampleInfo : SampleInfo + private class FileHitSampleInfo : HitSampleInfo { public string Filename; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index bb1a5e200d..ff6b9be8b5 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Objects.Legacy public double Distance => Path.Distance; - public List> NodeSamples { get; set; } + public List> NodeSamples { get; set; } public int RepeatCount { get; set; } public double EndTime => StartTime + this.SpanCount() * Distance / Velocity; diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs index 8a3e232e60..b20a027e78 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania }; } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples) { return new ConvertSlider { diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index b98de32bd0..0a4e38df02 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu }; } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples) { newCombo |= forceNewCombo; comboOffset += extraComboOffset; diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs index bab21b31ad..7c1514c1eb 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko return new ConvertHit(); } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples) { return new ConvertSlider { diff --git a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs index 8be95c063d..697adeda98 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Objects.Types /// n-1: The last repeat.
/// n: The last node. ///
- List> NodeSamples { get; } + List> NodeSamples { get; } } public static class HasRepeatsExtensions diff --git a/osu.Game/Screens/Play/ComboEffects.cs b/osu.Game/Screens/Play/ComboEffects.cs index d752f4a556..1c4ac921f0 100644 --- a/osu.Game/Screens/Play/ComboEffects.cs +++ b/osu.Game/Screens/Play/ComboEffects.cs @@ -2,10 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; +using osu.Game.Audio; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; @@ -13,23 +12,31 @@ namespace osu.Game.Screens.Play { public class ComboEffects : CompositeDrawable { - private SampleChannel sampleComboBreak; + private readonly ScoreProcessor processor; + + private SkinnableSound comboBreakSample; public ComboEffects(ScoreProcessor processor) { - processor.Combo.BindValueChanged(onComboChange); + this.processor = processor; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = comboBreakSample = new SkinnableSound(new SampleInfo("combobreak")); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + processor.Combo.BindValueChanged(onComboChange, true); } private void onComboChange(ValueChangedEvent combo) { if (combo.NewValue == 0 && combo.OldValue > 20) - sampleComboBreak?.Play(); - } - - [BackgroundDependencyLoader] - private void load(ISkinSource skin, AudioManager audio) - { - sampleComboBreak = skin.GetSample(@"Gameplay/combobreak") ?? audio.Samples.Get(@"Gameplay/combobreak"); + comboBreakSample?.Play(); } } -} \ No newline at end of file +} diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index e88e088f5e..8e2b5cec98 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -13,14 +14,19 @@ namespace osu.Game.Skinning { public class SkinnableSound : SkinReloadableDrawable { - private readonly SampleInfo[] samples; + private readonly ISampleInfo[] hitSamples; private SampleChannel[] channels; private AudioManager audio; - public SkinnableSound(params SampleInfo[] samples) + public SkinnableSound(IEnumerable hitSamples) { - this.samples = samples; + this.hitSamples = hitSamples.ToArray(); + } + + public SkinnableSound(ISampleInfo hitSamples) + { + this.hitSamples = new[] { hitSamples }; } [BackgroundDependencyLoader] @@ -35,7 +41,7 @@ namespace osu.Game.Skinning protected override void SkinChanged(ISkinSource skin, bool allowFallback) { - channels = samples.Select(s => + channels = hitSamples.Select(s => { var ch = loadChannel(s, skin.GetSample); if (ch == null && allowFallback) @@ -44,7 +50,7 @@ namespace osu.Game.Skinning }).Where(c => c != null).ToArray(); } - private SampleChannel loadChannel(SampleInfo info, Func getSampleFunction) + private SampleChannel loadChannel(ISampleInfo info, Func getSampleFunction) { foreach (var lookup in info.LookupNames) { From 8703f0ad40ec532c98e1fbdfbd7d03bbf51c0829 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 30 Jun 2019 22:23:48 +0900 Subject: [PATCH 1462/2854] Change song select initialisation to promote db context sharing --- osu.Game/Screens/Select/BeatmapCarousel.cs | 43 ++++++++++------------ osu.Game/Screens/Select/SongSelect.cs | 2 - 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index a6fbb1ff94..16354534f4 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -11,7 +11,6 @@ using osu.Game.Configuration; using osuTK.Input; using osu.Framework.MathUtils; using System.Diagnostics; -using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Caching; @@ -64,35 +63,29 @@ namespace osu.Game.Screens.Select public IEnumerable BeatmapSets { get => beatmapSets.Select(g => g.BeatmapSet); - set => loadBeatmapSets(() => value); + set => loadBeatmapSets(value); } - public void LoadBeatmapSetsFromManager(BeatmapManager manager) => loadBeatmapSets(manager.GetAllUsableBeatmapSetsEnumerable); - - private void loadBeatmapSets(Func> beatmapSets) + private void loadBeatmapSets(IEnumerable beatmapSets) { CarouselRoot newRoot = new CarouselRoot(this); - Task.Run(() => - { - beatmapSets().Select(createCarouselSet).Where(g => g != null).ForEach(newRoot.AddChild); - newRoot.Filter(activeCriteria); + beatmapSets.Select(createCarouselSet).Where(g => g != null).ForEach(newRoot.AddChild); + newRoot.Filter(activeCriteria); - // preload drawables as the ctor overhead is quite high currently. - var _ = newRoot.Drawables; - }).ContinueWith(_ => Schedule(() => - { - root = newRoot; - scrollableContent.Clear(false); - itemsCache.Invalidate(); - scrollPositionCache.Invalidate(); + // preload drawables as the ctor overhead is quite high currently. + var _ = newRoot.Drawables; - Schedule(() => - { - BeatmapSetsChanged?.Invoke(); - BeatmapSetsLoaded = true; - }); - })); + root = newRoot; + scrollableContent.Clear(false); + itemsCache.Invalidate(); + scrollPositionCache.Invalidate(); + + Schedule(() => + { + BeatmapSetsChanged?.Invoke(); + BeatmapSetsLoaded = true; + }); } private readonly List yPositions = new List(); @@ -125,13 +118,15 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuConfigManager config) + private void load(OsuConfigManager config, BeatmapManager beatmaps) { config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm); config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled); RightClickScrollingEnabled.ValueChanged += enabled => RightMouseScrollbar = enabled.NewValue; RightClickScrollingEnabled.TriggerChange(); + + loadBeatmapSets(beatmaps.GetAllUsableBeatmapSetsEnumerable()); } public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 7c62a49a97..3581ed5534 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -244,8 +244,6 @@ namespace osu.Game.Screens.Select sampleChangeBeatmap = audio.Samples.Get(@"SongSelect/select-expand"); SampleConfirm = audio.Samples.Get(@"SongSelect/confirm-selection"); - Carousel.LoadBeatmapSetsFromManager(this.beatmaps); - if (dialogOverlay != null) { Schedule(() => From 489ca7b45786c762b1fea01cdfa60fb5b3e3b40b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 30 Jun 2019 22:26:49 +0900 Subject: [PATCH 1463/2854] Update resources in ios props --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index ce8b62cc3f..a8013914af 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -104,7 +104,7 @@ - + From 79fc143422815a9b1335f33c87cfc857b1ad14b8 Mon Sep 17 00:00:00 2001 From: HoLLy Date: Sun, 30 Jun 2019 16:52:39 +0200 Subject: [PATCH 1464/2854] Only remove .osk files when importing skin archives --- osu.Game/Skinning/SkinManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index cf1e9368bc..00e6fddc6a 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Linq.Expressions; using System.Threading; @@ -56,7 +57,7 @@ namespace osu.Game.Skinning protected override IEnumerable GetStableImportPaths() => GetStableStorage().GetDirectories(ImportFromStablePath); - protected override bool ShouldRemoveArchive(string path) => true; + protected override bool ShouldRemoveArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osk"; /// /// Returns a list of all usable s. Includes the special default skin plus all skins from . From f42ded343779ab6d168538892d0dc77d6a3ee00b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 30 Jun 2019 18:27:47 +0300 Subject: [PATCH 1465/2854] Move to DrawableOsuHitObject --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs | 2 ++ osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 10b37af957..eb5b6aab36 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { private readonly ShakeContainer shakeContainer; + public override bool HandlePositionalInput => true; + protected DrawableOsuHitObject(OsuHitObject hitObject) : base(hitObject) { diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index cc1a1f370f..1f6ca4dd73 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -81,8 +81,6 @@ namespace osu.Game.Rulesets.Objects.Drawables public override bool RemoveCompletedTransforms => false; protected override bool RequiresChildrenUpdate => true; - public override bool HandlePositionalInput => true; - public override bool IsPresent => base.IsPresent || (State.Value == ArmedState.Idle && Clock?.CurrentTime >= LifetimeStart); public readonly Bindable State = new Bindable(); From d11b799571df4d06973b4955994b94ad0e520da9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 30 Jun 2019 18:28:20 +0300 Subject: [PATCH 1466/2854] Add explaining comment --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index eb5b6aab36..f372cb65ce 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -18,6 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { private readonly ShakeContainer shakeContainer; + // Must be set to update IsHovered as it's used in relax mdo to detect osu hit objects. public override bool HandlePositionalInput => true; protected DrawableOsuHitObject(OsuHitObject hitObject) From 9de4bb342311e0573163dba38c41be8878667567 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Jul 2019 16:12:20 +0900 Subject: [PATCH 1467/2854] Remove all non-transform LogoVisualisation per-frame allocations --- osu.Game/Screens/Menu/LogoVisualisation.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index c6de5857c2..2ba82b5d9b 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -96,13 +96,13 @@ namespace osu.Game.Screens.Menu var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null; var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null; - float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes ?? new float[256]; + float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes; for (int i = 0; i < bars_per_visualiser; i++) { if (track?.IsRunning ?? false) { - float targetAmplitude = temporalAmplitudes[(i + indexOffset) % bars_per_visualiser] * (effect?.KiaiMode == true ? 1 : 0.5f); + float targetAmplitude = (temporalAmplitudes?[(i + indexOffset) % bars_per_visualiser] ?? 0) * (effect?.KiaiMode == true ? 1 : 0.5f); if (targetAmplitude > frequencyAmplitudes[i]) frequencyAmplitudes[i] = targetAmplitude; } @@ -115,7 +115,6 @@ namespace osu.Game.Screens.Menu } indexOffset = (indexOffset + index_change) % bars_per_visualiser; - Scheduler.AddDelayed(updateAmplitudes, time_between_updates); } private void updateColour() @@ -131,7 +130,8 @@ namespace osu.Game.Screens.Menu protected override void LoadComplete() { base.LoadComplete(); - updateAmplitudes(); + + Scheduler.AddDelayed(updateAmplitudes, time_between_updates, true); } protected override void Update() From 665da09ed76c5a74bd688ddf2a41c5dd325b3ba7 Mon Sep 17 00:00:00 2001 From: Unknown Date: Mon, 1 Jul 2019 09:45:14 +0200 Subject: [PATCH 1468/2854] disable HD for taiko --- osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index 502dd54e9e..a6f902208c 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -9,5 +9,6 @@ namespace osu.Game.Rulesets.Taiko.Mods { public override string Description => @"Beats fade out before you hit them!"; public override double ScoreMultiplier => 1.06; + public override bool HasImplementation => false; } } From 0636df5660c50ae08adb2732527fbf11ffb5e412 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Jul 2019 18:13:14 +0900 Subject: [PATCH 1469/2854] Add support for legacy skins which use animation frames to hide elements --- osu.Game/Skinning/LegacySkin.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 7b658f86d0..d6424df9fe 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -81,7 +81,10 @@ namespace osu.Game.Skinning }; } - var texture = GetTexture(componentName); + // temporary allowance is given for skins the fact that stable handles non-animatable items such as hitcircles (incorrectly) + // by (incorrectly) displaying the first frame of animation rather than the non-animated version. + // users have userd this to "hide" certain elements like hit300. + var texture = GetTexture($"{componentName}-0") ?? GetTexture(componentName); if (texture == null) return null; From b875ab2f5809623f2b146042d987e8bbeacd57f5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 1 Jul 2019 12:15:53 +0300 Subject: [PATCH 1470/2854] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b4af007447..5a5f93046c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,7 +14,7 @@ - + From e6c1b059bc7a4899de9a418731d386d5b37d3ca1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Jul 2019 18:49:36 +0900 Subject: [PATCH 1471/2854] Disable dimming main content --- osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs index 71e9e4bdf3..e0ded11ec9 100644 --- a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs +++ b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs @@ -32,6 +32,8 @@ namespace osu.Game.Overlays.Chat.Selection private readonly SearchTextBox search; private readonly SearchContainer sectionsFlow; + protected override bool DimMainContent => false; + public Action OnRequestJoin; public Action OnRequestLeave; From e25158f434e6a44bc396e81dd5955c6044c60551 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Jul 2019 19:35:04 +0900 Subject: [PATCH 1472/2854] Rename move and fix tests --- .../Visual/Online/TestSceneChatOverlay.cs | 135 +++++++++++++++-- .../TestSceneChatOverlayScenarios.cs | 137 ------------------ 2 files changed, 124 insertions(+), 148 deletions(-) delete mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneChatOverlayScenarios.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index c75348112f..4d3992ce13 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -3,19 +3,23 @@ using System; using System.Collections.Generic; -using System.ComponentModel; +using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; using osu.Game.Online.Chat; using osu.Game.Overlays; using osu.Game.Overlays.Chat; +using osu.Game.Overlays.Chat.Selection; using osu.Game.Overlays.Chat.Tabs; +using osu.Game.Users; +using osuTK.Input; namespace osu.Game.Tests.Visual.Online { - [Description("Testing chat api and overlay")] - public class TestSceneChatOverlay : OsuTestScene + public class TestSceneChatOverlay : ManualInputManagerTestScene { public override IReadOnlyList RequiredTypes => new[] { @@ -28,17 +32,126 @@ namespace osu.Game.Tests.Visual.Online typeof(TabCloseButton) }; - [Cached] - private readonly ChannelManager channelManager = new ChannelManager(); + private TestChatOverlay chatOverlay; + private ChannelManager channelManager; - [BackgroundDependencyLoader] - private void load() + private readonly Channel channel1 = new Channel(new User()) { Name = "test1" }; + private readonly Channel channel2 = new Channel(new User()) { Name = "test2" }; + + [SetUp] + public void Setup() { - Children = new Drawable[] + Schedule(() => { - channelManager, - new ChatOverlay { State = { Value = Visibility.Visible } } - }; + ChannelManagerContainer container; + + Child = container = new ChannelManagerContainer(new List { channel1, channel2 }) + { + RelativeSizeAxes = Axes.Both, + }; + + chatOverlay = container.ChatOverlay; + channelManager = container.ChannelManager; + }); + } + + [Test] + public void TestHideOverlay() + { + AddAssert("Chat overlay is visible", () => chatOverlay.State.Value == Visibility.Visible); + AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); + + AddStep("Close chat overlay", () => chatOverlay.Hide()); + + AddAssert("Chat overlay was hidden", () => chatOverlay.State.Value == Visibility.Hidden); + AddAssert("Channel selection overlay was hidden", () => chatOverlay.SelectionOverlayState == Visibility.Hidden); + } + + [Test] + public void TestSelectingChannelClosesSelector() + { + AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); + + AddStep("Join channel 1", () => channelManager.JoinChannel(channel1)); + AddStep("Switch to channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); + + AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1); + AddAssert("Channel selector was closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden); + } + + [Test] + public void TestCloseChannelWhileSelectorClosed() + { + AddStep("Join channel 1", () => channelManager.JoinChannel(channel1)); + AddStep("Join channel 2", () => channelManager.JoinChannel(channel2)); + + AddStep("Switch to channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); + AddStep("Close channel 2", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); + + AddAssert("Selector remained closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden); + AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1); + + AddStep("Close channel 1", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); + + AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); + } + + private void clickDrawable(Drawable d) + { + InputManager.MoveMouseTo(d); + InputManager.Click(MouseButton.Left); + } + + private class ChannelManagerContainer : Container + { + public TestChatOverlay ChatOverlay { get; private set; } + + [Cached] + public ChannelManager ChannelManager { get; } = new ChannelManager(); + + private readonly List channels; + + public ChannelManagerContainer(List channels) + { + this.channels = channels; + } + + [BackgroundDependencyLoader] + private void load() + { + ((BindableList)ChannelManager.AvailableChannels).AddRange(channels); + + Child = ChatOverlay = new TestChatOverlay { RelativeSizeAxes = Axes.Both, }; + ChatOverlay.Show(); + } + } + + private class TestChatOverlay : ChatOverlay + { + public Visibility SelectionOverlayState => ChannelSelectionOverlay.State.Value; + + public new ChannelSelectionOverlay ChannelSelectionOverlay => base.ChannelSelectionOverlay; + + protected override ChannelTabControl CreateChannelTabControl() => new TestTabControl(); + + public IReadOnlyDictionary> TabMap => ((TestTabControl)ChannelTabControl).TabMap; + } + + private class TestTabControl : ChannelTabControl + { + protected override TabItem CreateTabItem(Channel value) => new TestChannelTabItem(value); + + public new IReadOnlyDictionary> TabMap => base.TabMap; + } + + private class TestChannelTabItem : PrivateChannelTabItem + { + public TestChannelTabItem(Channel channel) + : base(channel) + { + } + + public new ClickableContainer CloseButton => base.CloseButton; } } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneChatOverlayScenarios.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneChatOverlayScenarios.cs deleted file mode 100644 index 2886bcfe56..0000000000 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneChatOverlayScenarios.cs +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Online.Chat; -using osu.Game.Overlays; -using osu.Game.Overlays.Chat.Selection; -using osu.Game.Overlays.Chat.Tabs; -using osu.Game.Users; -using osuTK.Input; - -namespace osu.Game.Tests.Visual.UserInterface -{ - public class TestSceneChatOverlayScenarios : ManualInputManagerTestScene - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ChannelTabControl), - typeof(ChannelTabItem), - typeof(ChatOverlay), - }; - - private TestChatOverlay chatOverlay; - private ChannelManager channelManager; - - private readonly Channel channel1 = new Channel(new User()) { Name = "test1" }; - private readonly Channel channel2 = new Channel(new User()) { Name = "test2" }; - - [SetUp] - public void Setup() - { - Schedule(() => - { - ChannelManagerContainer container; - Child = container = new ChannelManagerContainer(new List { channel1, channel2 }) { RelativeSizeAxes = Axes.Both, }; - chatOverlay = container.ChatOverlay; - channelManager = container.ChannelManager; - }); - } - - [Test] - public void TestHideOverlay() - { - AddStep("Close chat overlay", () => chatOverlay.Hide()); - AddAssert("Channel selection overlay was hidden", () => chatOverlay.SelectionOverlayState == Visibility.Hidden); - AddAssert("Chat overlay was hidden", () => chatOverlay.State.Value == Visibility.Hidden); - } - - [Test] - public void TestTabbingAwayClosesSelector() - { - AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); - AddStep("Join channel 1", () => channelManager.JoinChannel(channel1)); - AddStep("Switch to channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); - AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1); - AddAssert("Channel selector was closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden); - } - - [Test] - public void TestCloseChannelWhileSelectorClosed() - { - AddStep("Join channel 1", () => channelManager.JoinChannel(channel1)); - AddStep("Join channel 2", () => channelManager.JoinChannel(channel2)); - AddStep("Switch to channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); - AddStep("Close channel 2", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); - AddAssert("Selector remained closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden); - AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1); - AddStep("Close channel 1", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); - AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); - } - - private void clickDrawable(Drawable d) - { - InputManager.MoveMouseTo(d); - InputManager.Click(MouseButton.Left); - } - - private class ChannelManagerContainer : Container - { - public TestChatOverlay ChatOverlay { get; private set; } - - [Cached] - public ChannelManager ChannelManager { get; } = new ChannelManager(); - - private readonly List channels; - - public ChannelManagerContainer(List channels) - { - this.channels = channels; - } - - [BackgroundDependencyLoader] - private void load() - { - ((BindableList)ChannelManager.AvailableChannels).AddRange(channels); - - Child = ChatOverlay = new TestChatOverlay { RelativeSizeAxes = Axes.Both, }; - ChatOverlay.Show(); - } - } - - private class TestChatOverlay : ChatOverlay - { - public Visibility SelectionOverlayState => ChannelSelectionOverlay.State.Value; - - public new ChannelSelectionOverlay ChannelSelectionOverlay => base.ChannelSelectionOverlay; - - protected override ChannelTabControl CreateChannelTabControl() => new TestTabControl(); - - public IReadOnlyDictionary> TabMap => ((TestTabControl)ChannelTabControl).TabMap; - } - - private class TestTabControl : ChannelTabControl - { - protected override TabItem CreateTabItem(Channel value) => new TestChannelTabItem(value); - - public new IReadOnlyDictionary> TabMap => base.TabMap; - } - - private class TestChannelTabItem : PrivateChannelTabItem - { - public TestChannelTabItem(Channel channel) - : base(channel) - { - } - - public new ClickableContainer CloseButton => base.CloseButton; - } - } -} From cd5e1bc4b14297e4f4fd694240ab97f5b0f15df8 Mon Sep 17 00:00:00 2001 From: MaxOhnh Date: Mon, 1 Jul 2019 13:55:09 +0200 Subject: [PATCH 1473/2854] Replace deflate progress with modified copy-paste of grow mod --- osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs | 106 ++++++++++---------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs index 2638d5bf78..56f64d14b8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -1,84 +1,84 @@ // Copyright (c) 2007-2018 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.Bindables; using osu.Framework.Graphics; -using osu.Game.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; -using System.Collections.Generic; -using OpenTK; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModDeflate : Mod, IApplicableToDrawableHitObjects + public class OsuModDeflate : Mod, IReadFromConfig, IApplicableToDrawableHitObjects { public override string Name => "Deflate"; - public override string ShortenedName => "DF"; - public override FontAwesome Icon => FontAwesome.fa_compress; + + public override string Acronym => "DF"; + + public override IconUsage Icon => FontAwesome.Solid.CompressArrowsAlt; + public override ModType Type => ModType.Fun; + public override string Description => "Become one with the approach circle..."; + public override double ScoreMultiplier => 1; + private Bindable increaseFirstObjectVisibility = new Bindable(); + + public void ReadFromConfig(OsuConfigManager config) + { + increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); + } + public void ApplyToDrawableHitObjects(IEnumerable drawables) { - foreach (var drawable in drawables) - drawable.ApplyCustomUpdateState += drawableOnApplyCustomUpdateState; + foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0)) + { + switch (drawable) + { + case DrawableSpinner _: + continue; + + default: + drawable.ApplyCustomUpdateState += ApplyCustomState; + break; + } + } } - private void drawableOnApplyCustomUpdateState(DrawableHitObject drawable, ArmedState state) + protected virtual void ApplyCustomState(DrawableHitObject drawable, ArmedState state) { - if (!(drawable is DrawableOsuHitObject d)) - return; - - var h = d.HitObject; - - const float rescale = 2; + var h = (OsuHitObject)drawable.HitObject; + // apply grow effect switch (drawable) { - case DrawableHitCircle c: - c.ApproachCircle.Hide(); - using (d.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) - { - var origScale = d.Scale; - d.ScaleTo(1.1f, 1) - .Then() - .ScaleTo(origScale, h.TimePreempt); - } - switch (state) - { - case ArmedState.Miss: - d.FadeOut(100); - break; - case ArmedState.Hit: - d.FadeOut(800) - .ScaleTo(d.Scale * 1.5f, 400, Easing.OutQuad); - break; - } + case DrawableSliderHead _: + case DrawableSliderTail _: + // special cases we should *not* be scaling. break; - case DrawableSlider s: - using (d.BeginAbsoluteSequence(h.StartTime - h.TimePreempt + 1, true)) + case DrawableSlider _: + case DrawableHitCircle _: { - float origPathWidth = s.Body.PathWidth; - var origBodySize = s.Body.Size; - var origBodyDrawPos = s.Body.DrawPosition; - - // Fits nicely for CS=4, too big on lower CS, too small on higher CS - s.Body.Animate( - b => b.MoveTo(origBodyDrawPos - new Vector2(origPathWidth)).MoveTo(origBodyDrawPos, h.TimePreempt), - b => b.ResizeTo(origBodySize * rescale).ResizeTo(origBodySize, h.TimePreempt), - b => b.TransformTo("PathWidth", origPathWidth * rescale).TransformTo("PathWidth", origPathWidth, h.TimePreempt) - ); - } - break; - case DrawableRepeatPoint rp: - if (!rp.IsFirstRepeat) + using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + drawable.ScaleTo(2f).Then().ScaleTo(1f, h.TimePreempt); // sole difference to grow mod break; - var origSizeRp = rp.Size; - using (d.BeginAbsoluteSequence(h.StartTime - h.TimePreempt + 1, true)) - rp.ResizeTo(origSizeRp * rescale).ResizeTo(origSizeRp, h.TimePreempt); + } + } + + // remove approach circles + switch (drawable) + { + case DrawableHitCircle circle: + // we don't want to see the approach circle + using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + circle.ApproachCircle.Hide(); break; } } From 02b9e89f431b3164f5850587ae6eca5df3d67b44 Mon Sep 17 00:00:00 2001 From: MaxOhnh Date: Mon, 1 Jul 2019 14:46:17 +0200 Subject: [PATCH 1474/2854] Removed old unnecessary change, updated license header and apply deflating onto first object regardless --- osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs | 15 ++++----------- .../Objects/Drawables/DrawableRepeatPoint.cs | 2 -- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs index 56f64d14b8..56bb55c13e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; using System.Linq; @@ -14,7 +14,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModDeflate : Mod, IReadFromConfig, IApplicableToDrawableHitObjects + public class OsuModDeflate : Mod, IApplicableToDrawableHitObjects { public override string Name => "Deflate"; @@ -28,16 +28,9 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; - private Bindable increaseFirstObjectVisibility = new Bindable(); - - public void ReadFromConfig(OsuConfigManager config) - { - increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); - } - public void ApplyToDrawableHitObjects(IEnumerable drawables) { - foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0)) + foreach (var drawable in drawables) { switch (drawable) { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 29a84b53a9..cce6dfe106 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -41,8 +41,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }; } - public bool IsFirstRepeat => repeatPoint.RepeatIndex == 0; - protected override void CheckForResult(bool userTriggered, double timeOffset) { if (repeatPoint.StartTime <= Time.Current) From 6e739411144163839767ed11f1e09b835e1c047b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Jul 2019 22:06:54 +0900 Subject: [PATCH 1475/2854] Update iOS resources --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index a8013914af..48d2e6846a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -104,7 +104,7 @@ - + From fc8dee612388d453437dce5f19985ade52ead788 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Jul 2019 23:26:53 +0930 Subject: [PATCH 1476/2854] Fix typo --- osu.Game/Skinning/LegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index d6424df9fe..513a024a36 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -83,7 +83,7 @@ namespace osu.Game.Skinning // temporary allowance is given for skins the fact that stable handles non-animatable items such as hitcircles (incorrectly) // by (incorrectly) displaying the first frame of animation rather than the non-animated version. - // users have userd this to "hide" certain elements like hit300. + // users have used this to "hide" certain elements like hit300. var texture = GetTexture($"{componentName}-0") ?? GetTexture(componentName); if (texture == null) From 313648b8694863772dcd7e8750be2f76d6e67fe0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 00:34:43 +0900 Subject: [PATCH 1477/2854] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b430c36264..d6a998bf55 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 48d2e6846a..de4a14f01f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 0c95dff3d690bd73dccf74f7ccdcdf45d1b275ea Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Mon, 1 Jul 2019 18:41:08 +0300 Subject: [PATCH 1478/2854] Make FlowContainer insertion cleaner --- .../Changelog/ChangelogSingleBuild.cs | 24 +++++++------------ osu.Game/Overlays/Music/PlaylistList.cs | 5 +--- .../Profile/Header/MedalHeaderContainer.cs | 4 +--- .../Sections/Ranks/DrawableProfileScore.cs | 3 +-- osu.Game/Overlays/Settings/SettingsItem.cs | 3 +-- 5 files changed, 12 insertions(+), 27 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs index 36ae5a756c..fdd3d6d555 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs @@ -88,24 +88,16 @@ namespace osu.Game.Overlays.Changelog }); } - NavigationIconButton left, right; - - fill.AddRange(new[] + fill.Insert(-1, new NavigationIconButton(Build.Versions?.Previous) { - left = new NavigationIconButton(Build.Versions?.Previous) - { - Icon = FontAwesome.Solid.ChevronLeft, - SelectBuild = b => SelectBuild(b) - }, - right = new NavigationIconButton(Build.Versions?.Next) - { - Icon = FontAwesome.Solid.ChevronRight, - SelectBuild = b => SelectBuild(b) - }, + Icon = FontAwesome.Solid.ChevronLeft, + SelectBuild = b => SelectBuild(b) + }); + fill.Insert(1, new NavigationIconButton(Build.Versions?.Next) + { + Icon = FontAwesome.Solid.ChevronRight, + SelectBuild = b => SelectBuild(b) }); - - fill.SetLayoutPosition(left, -1); - fill.SetLayoutPosition(right, 1); return fill; } diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index 07040f166d..539601c359 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -85,10 +85,7 @@ namespace osu.Game.Overlays.Music private void addBeatmapSet(BeatmapSetInfo obj) => Schedule(() => { - var newItem = new PlaylistItem(obj) { OnSelect = set => Selected?.Invoke(set) }; - - items.Add(newItem); - items.SetLayoutPosition(newItem, items.Count - 1); + items.Insert(items.Count - 1, new PlaylistItem(obj) { OnSelect = set => Selected?.Invoke(set) }); }); private void removeBeatmapSet(BeatmapSetInfo obj) => Schedule(() => diff --git a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs index 67229a80c0..45bc60f794 100644 --- a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs @@ -78,10 +78,8 @@ namespace osu.Game.Overlays.Profile.Header int displayIndex = index; LoadComponentAsync(new DrawableBadge(badges[index]), asyncBadge => { - badgeFlowContainer.Add(asyncBadge); - // load in stable order regardless of async load order. - badgeFlowContainer.SetLayoutPosition(asyncBadge, displayIndex); + badgeFlowContainer.Insert(displayIndex, asyncBadge); }); } } diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index 0a90c9b135..b77357edd8 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs @@ -49,8 +49,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Font = OsuFont.GetFont(size: 11, weight: FontWeight.Regular, italics: true) }; - RightFlowContainer.Add(text); - RightFlowContainer.SetLayoutPosition(text, 1); + RightFlowContainer.Insert(1, text); LeftFlowContainer.Add(new BeatmapMetadataContainer(Score.Beatmap)); LeftFlowContainer.Add(new DrawableDate(Score.Date)); diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index ae840c8c00..d48c0b6b66 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -46,8 +46,7 @@ namespace osu.Game.Overlays.Settings if (text == null) { // construct lazily for cases where the label is not needed (may be provided by the Control). - Add(text = new OsuSpriteText()); - FlowContent.SetLayoutPosition(text, -1); + FlowContent.Insert(-1, text = new OsuSpriteText()); } text.Text = value; From 7bdf73795669a69582126c13211acea559f1f261 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Mon, 1 Jul 2019 18:41:40 +0300 Subject: [PATCH 1479/2854] Make notification insertion cleaner --- osu.Game/Overlays/Notifications/NotificationSection.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index f9278bbbd2..17a2d4cf9f 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -26,8 +26,7 @@ namespace osu.Game.Overlays.Notifications public void Add(Notification notification, float position) { - notifications.Add(notification); - notifications.SetLayoutPosition(notification, position); + notifications.Insert((int)position, notification); } public IEnumerable AcceptTypes; From 9037fb59de95a3cdcd6dd4de68b0461244b33a35 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Mon, 1 Jul 2019 18:42:18 +0300 Subject: [PATCH 1480/2854] Make BeatmapOptionsButton insertion cleaner --- osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs index 669264cef0..ede526f9da 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs @@ -110,8 +110,7 @@ namespace osu.Game.Screens.Select.Options HotKey = hotkey }; - buttonsContainer.Add(button); - buttonsContainer.SetLayoutPosition(button, depth); + buttonsContainer.Insert((int)depth, button); } } } From 48a828b7466a6ce369d4d977af8670ca1c38f889 Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Mon, 1 Jul 2019 17:49:37 +0200 Subject: [PATCH 1481/2854] Removed redundant imports and fixed indentation --- osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs index 56bb55c13e..cbefc42c3b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -2,11 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Linq; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; @@ -58,11 +55,11 @@ namespace osu.Game.Rulesets.Osu.Mods case DrawableSlider _: case DrawableHitCircle _: - { - using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) - drawable.ScaleTo(2f).Then().ScaleTo(1f, h.TimePreempt); // sole difference to grow mod - break; - } + { + using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + drawable.ScaleTo(2f).Then().ScaleTo(1f, h.TimePreempt); // sole difference to grow mod + break; + } } // remove approach circles From 5496b8bc58fae51b4dc242bb5eb0051a7d2039a1 Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Mon, 1 Jul 2019 20:11:50 +0200 Subject: [PATCH 1482/2854] Rewrote traceable mod to inherit from hidden --- osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs | 3 + osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 2 + osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 90 ++++++++----------- .../Objects/Drawables/DrawableHitCircle.cs | 23 ++--- osu.Game/Overlays/Mods/ModSection.cs | 4 +- 5 files changed, 48 insertions(+), 74 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs index 8072dc09c1..9b1f43b209 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Bindables; @@ -28,6 +29,8 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; + public override Type[] IncompatibleMods => new[] { typeof(OsuModTraceable) }; + private Bindable increaseFirstObjectVisibility = new Bindable(); public void ReadFromConfig(OsuConfigManager config) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index ddf708d0f1..5887cb2865 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Description => @"Play with no approach circles and fading circles/sliders."; public override double ScoreMultiplier => 1.06; + public override Type[] IncompatibleMods => new[] { typeof(OsuModTraceable) }; + private const double fade_in_duration_multiplier = 0.4; private const double fade_out_duration_multiplier = 0.3; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index a6d8d35125..858b7e15c8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -2,83 +2,63 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Linq; using System.Collections.Generic; -using osu.Game.Graphics; +using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Osu.Mods { - internal class OsuModTraceable : Mod, IApplicableToDrawableHitObjects + internal class OsuModTraceable : OsuModHidden, IReadFromConfig, IApplicableToDrawableHitObjects { public override string Name => "Traceable"; - public override string ShortenedName => "TC"; - public override FontAwesome Icon => FontAwesome.fa_snapchat_ghost; + public override string Acronym => "TC"; + public override IconUsage Icon => FontAwesome.Brands.SnapchatGhost; public override ModType Type => ModType.Fun; public override string Description => "Put your faith in the approach circles..."; public override double ScoreMultiplier => 1; + public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModGrow) }; - public void ApplyToDrawableHitObjects(IEnumerable drawables) + public override void ApplyToDrawableHitObjects(IEnumerable drawables) { - foreach (var drawable in drawables) - drawable.ApplyCustomUpdateState += ApplyTraceableState; + foreach (var drawable in drawables.Skip(IncreaseFirstObjectVisibility.Value ? 1 : 0)) + { + switch (drawable) + { + case DrawableHitCircle _: + drawable.ApplyCustomUpdateState += ApplyTraceableState; + break; + case DrawableSlider slider: + slider.ApplyCustomUpdateState += ApplyHiddenState; + slider.HeadCircle.ApplyCustomUpdateState += ApplyTraceableState; + break; + default: + drawable.ApplyCustomUpdateState += ApplyHiddenState; + break; + } + } } - /* Similar to ApplyHiddenState, only different if drawable is DrawableHitCircle. - * If we'd use ApplyHiddenState instead but only on non-DrawableHitCircle's, then - * the nested object HeadCircle of DrawableSlider would still use ApplyHiddenState, - * thus treating the DrawableHitCircle with the hidden mod instead of the traceable mod. - */ protected void ApplyTraceableState(DrawableHitObject drawable, ArmedState state) { - if (!(drawable is DrawableOsuHitObject d)) + if (!(drawable is DrawableHitCircle circle)) return; - var h = d.HitObject; + var h = circle.HitObject; - var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadeIn; - - // new duration from completed fade in to end (before fading out) - var longFadeDuration = ((h as IHasEndTime)?.EndTime ?? h.StartTime) - fadeOutStartTime; - - switch (drawable) + // we only want to see the approach circle + using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) { - case DrawableHitCircle circle: - // we only want to see the approach circle - using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) - circle.HideButApproachCircle(); - - // approach circle fades out quickly at StartTime - using (drawable.BeginAbsoluteSequence(h.StartTime, true)) - circle.ApproachCircle.FadeOut(50); - - break; - case DrawableSlider slider: - using (slider.BeginAbsoluteSequence(fadeOutStartTime, true)) - slider.Body.FadeOut(longFadeDuration, Easing.Out); - - break; - case DrawableSliderTick sliderTick: - // slider ticks fade out over up to one second - var tickFadeOutDuration = Math.Min(sliderTick.HitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000); - - using (sliderTick.BeginAbsoluteSequence(sliderTick.HitObject.StartTime - tickFadeOutDuration, true)) - sliderTick.FadeOut(tickFadeOutDuration); - - break; - case DrawableSpinner spinner: - // hide elements we don't care about. - spinner.Disc.Hide(); - spinner.Ticks.Hide(); - spinner.Background.Hide(); - - using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true)) - spinner.FadeOut(h.TimePreempt); - - break; + circle.circle.Hide(); // CirclePiece + circle.circle.AlwaysPresent = true; + circle.ring.Hide(); + circle.flash.Hide(); + circle.explode.Hide(); + circle.number.Hide(); + circle.glow.Hide(); + circle.ApproachCircle.Show(); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 67c8371340..31c86474cd 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -17,12 +17,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach { public ApproachCircle ApproachCircle; - private readonly CirclePiece circle; - private readonly RingPiece ring; - private readonly FlashPiece flash; - private readonly ExplodePiece explode; - private readonly NumberPiece number; - private readonly GlowPiece glow; + public readonly CirclePiece circle; + public readonly RingPiece ring; + public readonly FlashPiece flash; + public readonly ExplodePiece explode; + public readonly NumberPiece number; + public readonly GlowPiece glow; private readonly IBindable positionBindable = new Bindable(); private readonly IBindable stackHeightBindable = new Bindable(); @@ -113,17 +113,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - public void HideButApproachCircle() - { - circle.Hide(); - circle.AlwaysPresent = true; - ring.Hide(); - flash.Hide(); - explode.Hide(); - number.Hide(); - glow.Hide(); - } - protected override void CheckForResult(bool userTriggered, double timeOffset) { if (!userTriggered) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index dedd397fa5..0eca1bcbec 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -112,7 +112,7 @@ namespace osu.Game.Overlays.Mods if (selected == null) continue; foreach (var type in modTypes) - if (type.IsInstanceOfType(selected)) + if (type.IsInstanceOfType(selected) && !selected.GetType().IsSubclassOf(type)) { if (immediate) button.Deselect(); @@ -130,7 +130,7 @@ namespace osu.Game.Overlays.Mods { foreach (var button in buttons) { - int i = Array.FindIndex(button.Mods, m => modTypes.Any(t => t.IsInstanceOfType(m))); + int i = Array.FindIndex(button.Mods, m => modTypes.Any(t => t.IsInstanceOfType(m) && !m.GetType().IsSubclassOf(t))); if (i >= 0) button.SelectAt(i); From d753f446e4fb995ea8ad403c851e22ff9bf46d13 Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Mon, 1 Jul 2019 20:20:25 +0200 Subject: [PATCH 1483/2854] Updated license header --- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index 858b7e15c8..fe1b20c0cc 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Linq; From 72e5cbb07f6ec411c5774c8dbc19229ad2625507 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 2 Jul 2019 01:45:09 +0300 Subject: [PATCH 1484/2854] Add checkbox for hiding health bar --- osu.Game/Configuration/OsuConfigManager.cs | 2 ++ .../Overlays/Settings/Sections/Gameplay/GeneralSettings.cs | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 795f0b43f7..b5097e95c8 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -77,6 +77,7 @@ namespace osu.Game.Configuration Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01); Set(OsuSetting.ShowInterface, true); + Set(OsuSetting.HideHealthBar, true); Set(OsuSetting.KeyOverlay, false); Set(OsuSetting.FloatingComments, false); @@ -131,6 +132,7 @@ namespace osu.Game.Configuration KeyOverlay, FloatingComments, ShowInterface, + HideHealthBar, MouseDisableButtons, MouseDisableWheel, AudioOffset, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 997d1354b3..2d208c5f71 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -35,6 +35,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay Bindable = config.GetBindable(OsuSetting.ShowInterface) }, new SettingsCheckbox + { + LabelText = "Hide health bar if you can't fail", + Bindable = config.GetBindable(OsuSetting.HideHealthBar), + }, + new SettingsCheckbox { LabelText = "Always show key overlay", Bindable = config.GetBindable(OsuSetting.KeyOverlay) From a8e8650dddb79298df8e4a99fddae7500de2cf10 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 2 Jul 2019 01:47:39 +0300 Subject: [PATCH 1485/2854] Move blocking fail logic into a base class --- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 7 +----- osu.Game/Rulesets/Mods/ModBlockFail.cs | 30 +++++++++++++++++++++++ osu.Game/Rulesets/Mods/ModNoFail.cs | 10 +------- osu.Game/Rulesets/Mods/ModRelax.cs | 2 +- osu.sln | 12 +++++++++ 5 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 osu.Game/Rulesets/Mods/ModBlockFail.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index a6a81fa989..5625028707 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -9,18 +9,15 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.UI; -using osu.Game.Screens.Play; using static osu.Game.Input.Handlers.ReplayInputHandler; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModRelax : ModRelax, IApplicableFailOverride, IUpdatableByPlayfield, IApplicableToDrawableRuleset, IApplicableToHUD + public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset { public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things."; public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); - public bool AllowFail => false; - public void Update(Playfield playfield) { bool requiresHold = false; @@ -86,7 +83,5 @@ namespace osu.Game.Rulesets.Osu.Mods osuInputManager = (OsuInputManager)drawableRuleset.KeyBindingInputManager; osuInputManager.AllowUserPresses = false; } - - public void ApplyToHUD(HUDOverlay overlay) => overlay.HealthDisplay.Hide(); } } diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs new file mode 100644 index 0000000000..42ec82f8aa --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Game.Configuration; +using osu.Game.Screens.Play; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModBlockFail : Mod, IApplicableFailOverride, IApplicableToHUD, IReadFromConfig + { + private Bindable hideHealthBar = new Bindable(); + + /// + /// We never fail, 'yo. + /// + public bool AllowFail => false; + + public void ReadFromConfig(OsuConfigManager config) + { + hideHealthBar = config.GetBindable(OsuSetting.HideHealthBar); + } + + public void ApplyToHUD(HUDOverlay overlay) + { + if (hideHealthBar.Value) + overlay.HealthDisplay.Hide(); + } + } +} diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs index 171ebb78bc..49ee3354c3 100644 --- a/osu.Game/Rulesets/Mods/ModNoFail.cs +++ b/osu.Game/Rulesets/Mods/ModNoFail.cs @@ -4,11 +4,10 @@ using System; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; -using osu.Game.Screens.Play; namespace osu.Game.Rulesets.Mods { - public abstract class ModNoFail : Mod, IApplicableFailOverride, IApplicableToHUD + public abstract class ModNoFail : ModBlockFail { public override string Name => "No Fail"; public override string Acronym => "NF"; @@ -18,12 +17,5 @@ namespace osu.Game.Rulesets.Mods public override double ScoreMultiplier => 0.5; public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModAutoplay) }; - - /// - /// We never fail, 'yo. - /// - public bool AllowFail => false; - - public void ApplyToHUD(HUDOverlay overlay) => overlay.HealthDisplay.Hide(); } } diff --git a/osu.Game/Rulesets/Mods/ModRelax.cs b/osu.Game/Rulesets/Mods/ModRelax.cs index 4feb89186c..7c355577d4 100644 --- a/osu.Game/Rulesets/Mods/ModRelax.cs +++ b/osu.Game/Rulesets/Mods/ModRelax.cs @@ -7,7 +7,7 @@ using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods { - public abstract class ModRelax : Mod + public abstract class ModRelax : ModBlockFail { public override string Name => "Relax"; public override string Acronym => "RX"; diff --git a/osu.sln b/osu.sln index 688339fab5..cee2be106e 100644 --- a/osu.sln +++ b/osu.sln @@ -29,6 +29,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tournament", "osu. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tournament.Tests", "osu.Game.Tournament.Tests\osu.Game.Tournament.Tests.csproj", "{5789E78D-38F9-4072-AB7B-978F34B2C17F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework", "..\..\..\Desktop\osu-framework-master\osu.Framework\osu.Framework.csproj", "{05FD51FA-88EA-4895-85A0-FDE2D7DCF6F2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework.NativeLibs", "..\..\..\Desktop\osu-framework-master\osu.Framework.NativeLibs\osu.Framework.NativeLibs.csproj", "{2E9365F4-25A1-41D1-90ED-F91B5A946DBD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -87,6 +91,14 @@ Global {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Debug|Any CPU.Build.0 = Debug|Any CPU {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Release|Any CPU.ActiveCfg = Release|Any CPU {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Release|Any CPU.Build.0 = Release|Any CPU + {05FD51FA-88EA-4895-85A0-FDE2D7DCF6F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {05FD51FA-88EA-4895-85A0-FDE2D7DCF6F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {05FD51FA-88EA-4895-85A0-FDE2D7DCF6F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {05FD51FA-88EA-4895-85A0-FDE2D7DCF6F2}.Release|Any CPU.Build.0 = Release|Any CPU + {2E9365F4-25A1-41D1-90ED-F91B5A946DBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2E9365F4-25A1-41D1-90ED-F91B5A946DBD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E9365F4-25A1-41D1-90ED-F91B5A946DBD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2E9365F4-25A1-41D1-90ED-F91B5A946DBD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 6a79349f4aefde437804ca8db4e6025c61e85e02 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 2 Jul 2019 02:19:59 +0300 Subject: [PATCH 1486/2854] Move health display out of the visibility container --- osu.Game/Screens/Play/HUDOverlay.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 017bf70133..6488bf9452 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -40,6 +41,7 @@ namespace osu.Game.Screens.Play private readonly IReadOnlyList mods; private Bindable showHud; + private Bindable hideHealthbar; private readonly Container visibilityContainer; private readonly BindableBool replayLoaded = new BindableBool(); @@ -77,11 +79,11 @@ namespace osu.Game.Screens.Play ComboCounter = CreateComboCounter(), }, }, - HealthDisplay = CreateHealthDisplay(), Progress = CreateProgress(), ModDisplay = CreateModsContainer(), } }, + HealthDisplay = CreateHealthDisplay(), PlayerSettingsOverlay = CreatePlayerSettingsOverlay(), new FillFlowContainer { @@ -112,8 +114,14 @@ namespace osu.Game.Screens.Play ModDisplay.Current.Value = mods; + hideHealthbar = config.GetBindable(OsuSetting.HideHealthBar); showHud = config.GetBindable(OsuSetting.ShowInterface); - showHud.ValueChanged += visible => visibilityContainer.FadeTo(visible.NewValue ? 1 : 0, duration); + showHud.ValueChanged += visible => + { + visibilityContainer.FadeTo(visible.NewValue ? 1 : 0, duration); + if (!(hideHealthbar.Value && mods.OfType().Any())) + HealthDisplay.FadeTo(visible.NewValue ? 1 : 0, duration); + }; showHud.TriggerChange(); if (!showHud.Value && !hasShownNotificationOnce) From 7c1fc075e0ca6243eb9aa6028c583e580070db2c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 2 Jul 2019 02:24:04 +0300 Subject: [PATCH 1487/2854] Remove mistaken change --- osu.sln | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/osu.sln b/osu.sln index cee2be106e..688339fab5 100644 --- a/osu.sln +++ b/osu.sln @@ -29,10 +29,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tournament", "osu. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tournament.Tests", "osu.Game.Tournament.Tests\osu.Game.Tournament.Tests.csproj", "{5789E78D-38F9-4072-AB7B-978F34B2C17F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework", "..\..\..\Desktop\osu-framework-master\osu.Framework\osu.Framework.csproj", "{05FD51FA-88EA-4895-85A0-FDE2D7DCF6F2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework.NativeLibs", "..\..\..\Desktop\osu-framework-master\osu.Framework.NativeLibs\osu.Framework.NativeLibs.csproj", "{2E9365F4-25A1-41D1-90ED-F91B5A946DBD}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -91,14 +87,6 @@ Global {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Debug|Any CPU.Build.0 = Debug|Any CPU {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Release|Any CPU.ActiveCfg = Release|Any CPU {5789E78D-38F9-4072-AB7B-978F34B2C17F}.Release|Any CPU.Build.0 = Release|Any CPU - {05FD51FA-88EA-4895-85A0-FDE2D7DCF6F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {05FD51FA-88EA-4895-85A0-FDE2D7DCF6F2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {05FD51FA-88EA-4895-85A0-FDE2D7DCF6F2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {05FD51FA-88EA-4895-85A0-FDE2D7DCF6F2}.Release|Any CPU.Build.0 = Release|Any CPU - {2E9365F4-25A1-41D1-90ED-F91B5A946DBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2E9365F4-25A1-41D1-90ED-F91B5A946DBD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2E9365F4-25A1-41D1-90ED-F91B5A946DBD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2E9365F4-25A1-41D1-90ED-F91B5A946DBD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 4145173ac905e0b3eef36e77ae542695dc68712c Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Tue, 2 Jul 2019 04:04:07 +0200 Subject: [PATCH 1488/2854] Combined hidden with traceable as multi mod --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 1 - osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 22 +++++---- .../Objects/Drawables/DrawableHitCircle.cs | 48 +++++++++---------- osu.Game.Rulesets.Osu/OsuRuleset.cs | 4 +- .../TestSceneModSelectOverlay.cs | 5 +- osu.Game/Overlays/Mods/ModSection.cs | 6 ++- 6 files changed, 44 insertions(+), 42 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 5887cb2865..2723be9df8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -17,7 +17,6 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Description => @"Play with no approach circles and fading circles/sliders."; public override double ScoreMultiplier => 1.06; - public override Type[] IncompatibleMods => new[] { typeof(OsuModTraceable) }; private const double fade_in_duration_multiplier = 0.4; private const double fade_out_duration_multiplier = 0.3; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index fe1b20c0cc..64a331213f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -11,15 +11,15 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Mods { - internal class OsuModTraceable : OsuModHidden, IReadFromConfig, IApplicableToDrawableHitObjects + internal class OsuModTraceable : OsuModHidden { public override string Name => "Traceable"; public override string Acronym => "TC"; public override IconUsage Icon => FontAwesome.Brands.SnapchatGhost; - public override ModType Type => ModType.Fun; + public override ModType Type => ModType.DifficultyIncrease; public override string Description => "Put your faith in the approach circles..."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModGrow) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModGrow) }; public override void ApplyToDrawableHitObjects(IEnumerable drawables) { @@ -30,10 +30,12 @@ namespace osu.Game.Rulesets.Osu.Mods case DrawableHitCircle _: drawable.ApplyCustomUpdateState += ApplyTraceableState; break; + case DrawableSlider slider: slider.ApplyCustomUpdateState += ApplyHiddenState; slider.HeadCircle.ApplyCustomUpdateState += ApplyTraceableState; break; + default: drawable.ApplyCustomUpdateState += ApplyHiddenState; break; @@ -51,13 +53,13 @@ namespace osu.Game.Rulesets.Osu.Mods // we only want to see the approach circle using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) { - circle.circle.Hide(); // CirclePiece - circle.circle.AlwaysPresent = true; - circle.ring.Hide(); - circle.flash.Hide(); - circle.explode.Hide(); - circle.number.Hide(); - circle.glow.Hide(); + circle.Circle.Hide(); // CirclePiece + circle.Circle.AlwaysPresent = true; + circle.Ring.Hide(); + circle.Flash.Hide(); + circle.Explode.Hide(); + circle.Number.Hide(); + circle.Glow.Hide(); circle.ApproachCircle.Show(); } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 31c86474cd..e8a2f94c14 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -17,18 +17,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach { public ApproachCircle ApproachCircle; - public readonly CirclePiece circle; - public readonly RingPiece ring; - public readonly FlashPiece flash; - public readonly ExplodePiece explode; - public readonly NumberPiece number; - public readonly GlowPiece glow; + public readonly CirclePiece Circle; + public readonly RingPiece Ring; + public readonly FlashPiece Flash; + public readonly ExplodePiece Explode; + public readonly NumberPiece Number; + public readonly GlowPiece Glow; private readonly IBindable positionBindable = new Bindable(); private readonly IBindable stackHeightBindable = new Bindable(); private readonly IBindable scaleBindable = new Bindable(); - public OsuAction? HitAction => circle.HitAction; + public OsuAction? HitAction => Circle.HitAction; private readonly Container explodeContainer; @@ -55,8 +55,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Anchor = Anchor.Centre, Children = new Drawable[] { - glow = new GlowPiece(), - circle = new CirclePiece + Glow = new GlowPiece(), + Circle = new CirclePiece { Hit = () => { @@ -67,13 +67,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables return true; }, }, - number = new NumberPiece + Number = new NumberPiece { Text = (HitObject.IndexInCurrentCombo + 1).ToString(), }, - ring = new RingPiece(), - flash = new FlashPiece(), - explode = new ExplodePiece(), + Ring = new RingPiece(), + Flash = new FlashPiece(), + Explode = new ExplodePiece(), ApproachCircle = new ApproachCircle { Alpha = 0, @@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }; //may not be so correct - Size = circle.DrawSize; + Size = Circle.DrawSize; } [BackgroundDependencyLoader] @@ -106,9 +106,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables set { base.AccentColour = value; - explode.Colour = AccentColour; - glow.Colour = AccentColour; - circle.Colour = AccentColour; + Explode.Colour = AccentColour; + Glow.Colour = AccentColour; + Circle.Colour = AccentColour; ApproachCircle.Colour = AccentColour; } } @@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void UpdateCurrentState(ArmedState state) { - glow.FadeOut(400); + Glow.FadeOut(400); switch (state) { @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Expire(true); - circle.HitAction = null; + Circle.HitAction = null; // override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early. LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.HalfWindowFor(HitResult.Miss); @@ -170,18 +170,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables ApproachCircle.FadeOut(50); const double flash_in = 40; - flash.FadeTo(0.8f, flash_in) + Flash.FadeTo(0.8f, flash_in) .Then() .FadeOut(100); - explode.FadeIn(flash_in); + Explode.FadeIn(flash_in); using (BeginDelayedSequence(flash_in, true)) { //after the flash, we can hide some elements that were behind it - ring.FadeOut(); - circle.FadeOut(); - number.FadeOut(); + Ring.FadeOut(); + Circle.FadeOut(); + Number.FadeOut(); this.FadeOut(800); explodeContainer.ScaleTo(1.5f, 400, Easing.OutQuad); diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 169d54d64a..9c48169a24 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Osu new OsuModHardRock(), new MultiMod(new OsuModSuddenDeath(), new OsuModPerfect()), new MultiMod(new OsuModDoubleTime(), new OsuModNightcore()), - new OsuModHidden(), + new MultiMod(new OsuModHidden(), new OsuModTraceable()), new MultiMod(new OsuModFlashlight(), new OsuModBlinds()), }; @@ -136,8 +136,6 @@ namespace osu.Game.Rulesets.Osu new OsuModWiggle(), new OsuModGrow(), new MultiMod(new ModWindUp(), new ModWindDown()), - - new OsuModTraceable(), }; default: diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 80408ab43b..9074b64eb8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -85,7 +85,7 @@ namespace osu.Game.Tests.Visual.UserInterface var assistMods = instance.GetModsFor(ModType.Automation); var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail); - var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden); + var hiddenMod = harderMods.OfType().FirstOrDefault(m => m.Mods.Any(a => a is OsuModHidden)); var doubleTimeMod = harderMods.OfType().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime)); @@ -96,10 +96,11 @@ namespace osu.Game.Tests.Visual.UserInterface testSingleMod(noFailMod); testMultiMod(doubleTimeMod); + testMultiMod(hiddenMod); testIncompatibleMods(easy, hardRock); testDeselectAll(easierMods.Where(m => !(m is MultiMod))); testMultiplierTextColour(noFailMod, modSelect.LowMultiplierColour); - testMultiplierTextColour(hiddenMod, modSelect.HighMultiplierColour); + testMultiplierTextColour(hardRock, modSelect.HighMultiplierColour); testUnimplementedMod(autoPilotMod); } diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 0eca1bcbec..5c2ab8e18c 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -112,13 +112,15 @@ namespace osu.Game.Overlays.Mods if (selected == null) continue; foreach (var type in modTypes) - if (type.IsInstanceOfType(selected) && !selected.GetType().IsSubclassOf(type)) + { + if (type.IsInstanceOfType(selected)) { if (immediate) button.Deselect(); else Scheduler.AddDelayed(button.Deselect, delay += 50); } + } } } @@ -130,7 +132,7 @@ namespace osu.Game.Overlays.Mods { foreach (var button in buttons) { - int i = Array.FindIndex(button.Mods, m => modTypes.Any(t => t.IsInstanceOfType(m) && !m.GetType().IsSubclassOf(t))); + int i = Array.FindIndex(button.Mods, m => modTypes.Any(t => t.IsInstanceOfType(m))); if (i >= 0) button.SelectAt(i); From 8e54990f62a72dbd3789549fce975ff02e62a884 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 13:40:40 +0900 Subject: [PATCH 1489/2854] Add database statistics to GlobalStatistics --- osu.Game/Database/DatabaseContextFactory.cs | 19 ++++++++++++++++++- osu.Game/Database/OsuDbContext.cs | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs index 554337c477..bb6bef1c50 100644 --- a/osu.Game/Database/DatabaseContextFactory.cs +++ b/osu.Game/Database/DatabaseContextFactory.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using Microsoft.EntityFrameworkCore.Storage; using osu.Framework.Platform; +using osu.Framework.Statistics; namespace osu.Game.Database { @@ -31,11 +32,20 @@ namespace osu.Game.Database recycleThreadContexts(); } + private static readonly GlobalStatistic reads = GlobalStatistics.Get("Database", "Get (Read)"); + private static readonly GlobalStatistic writes = GlobalStatistics.Get("Database", "Get (Write)"); + private static readonly GlobalStatistic commits = GlobalStatistics.Get("Database", "Commits"); + private static readonly GlobalStatistic rollbacks = GlobalStatistics.Get("Database", "Rollbacks"); + /// /// Get a context for the current thread for read-only usage. /// If a is in progress, the existing write-safe context will be returned. /// - public OsuDbContext Get() => threadContexts.Value; + public OsuDbContext Get() + { + reads.Value++; + return threadContexts.Value; + } /// /// Request a context for write usage. Can be consumed in a nested fashion (and will return the same underlying context). @@ -45,6 +55,7 @@ namespace osu.Game.Database /// A usage containing a usable context. public DatabaseWriteUsage GetForWrite(bool withTransaction = true) { + writes.Value++; Monitor.Enter(writeLock); OsuDbContext context; @@ -90,9 +101,15 @@ namespace osu.Game.Database if (usages == 0) { if (currentWriteDidError) + { + rollbacks.Value++; currentWriteTransaction?.Rollback(); + } else + { + commits.Value++; currentWriteTransaction?.Commit(); + } if (currentWriteDidWrite || currentWriteDidError) { diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index d31d7cbff7..538ec41b3d 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Logging; using osu.Framework.Logging; +using osu.Framework.Statistics; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.IO; @@ -34,6 +35,8 @@ namespace osu.Game.Database private static readonly Lazy logger = new Lazy(() => new OsuDbLoggerFactory()); + private static readonly GlobalStatistic contexts = GlobalStatistics.Get("Database", "Contexts"); + static OsuDbContext() { // required to initialise native SQLite libraries on some platforms. @@ -76,6 +79,8 @@ namespace osu.Game.Database connection.Close(); throw; } + + contexts.Value++; } ~OsuDbContext() @@ -85,6 +90,20 @@ namespace osu.Game.Database Dispose(); } + private bool isDisposed; + + public override void Dispose() + { + if (isDisposed) return; + + isDisposed = true; + + base.Dispose(); + + contexts.Value--; + GC.SuppressFinalize(this); + } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); From 2645967dc4c3aa26ae1c53a8daf5a9ed53b68ce9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 15:17:35 +0900 Subject: [PATCH 1490/2854] Fix wave-based overlays always being present before initial display --- .../Graphics/Containers/OsuFocusedOverlayContainer.cs | 2 +- osu.Game/Graphics/Containers/WaveContainer.cs | 9 ++------- osu.Game/Overlays/WaveOverlayContainer.cs | 4 ++++ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index f6db3102f2..5606328575 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -15,7 +15,7 @@ using osu.Game.Overlays; namespace osu.Game.Graphics.Containers { - public class OsuFocusedOverlayContainer : FocusedOverlayContainer, IPreviewTrackOwner, IKeyBindingHandler + public abstract class OsuFocusedOverlayContainer : FocusedOverlayContainer, IPreviewTrackOwner, IKeyBindingHandler { private SampleChannel samplePopIn; private SampleChannel samplePopOut; diff --git a/osu.Game/Graphics/Containers/WaveContainer.cs b/osu.Game/Graphics/Containers/WaveContainer.cs index f87909ab17..64a897088b 100644 --- a/osu.Game/Graphics/Containers/WaveContainer.cs +++ b/osu.Game/Graphics/Containers/WaveContainer.cs @@ -95,6 +95,7 @@ namespace osu.Game.Graphics.Containers AddInternal(contentContainer = new Container { RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Both, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, }); @@ -105,21 +106,15 @@ namespace osu.Game.Graphics.Containers foreach (var w in wavesContainer.Children) w.Show(); - this.FadeIn(100, Easing.OutQuint); contentContainer.MoveToY(0, APPEAR_DURATION, Easing.OutQuint); - - this.FadeIn(100, Easing.OutQuint); } protected override void PopOut() { - this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint); - contentContainer.MoveToY(DrawHeight * 2f, DISAPPEAR_DURATION, Easing.In); - foreach (var w in wavesContainer.Children) w.Hide(); - this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint); + contentContainer.MoveToY(2, DISAPPEAR_DURATION, Easing.In); } protected override void UpdateAfterChildren() diff --git a/osu.Game/Overlays/WaveOverlayContainer.cs b/osu.Game/Overlays/WaveOverlayContainer.cs index 05d3e7df0a..5059b136ee 100644 --- a/osu.Game/Overlays/WaveOverlayContainer.cs +++ b/osu.Game/Overlays/WaveOverlayContainer.cs @@ -25,13 +25,17 @@ namespace osu.Game.Overlays protected override void PopIn() { base.PopIn(); + Waves.Show(); + this.FadeIn(100, Easing.OutQuint); } protected override void PopOut() { base.PopOut(); + Waves.Hide(); + this.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InQuint); } } } From d27a0db45c2bfe2df8e7788c1c9b8cda5682ac2f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 15:21:57 +0900 Subject: [PATCH 1491/2854] Enforce StartHidden on relevant overlays --- osu.Game/Graphics/Containers/WaveContainer.cs | 2 ++ osu.Game/Overlays/WaveOverlayContainer.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/osu.Game/Graphics/Containers/WaveContainer.cs b/osu.Game/Graphics/Containers/WaveContainer.cs index 64a897088b..9050e775f7 100644 --- a/osu.Game/Graphics/Containers/WaveContainer.cs +++ b/osu.Game/Graphics/Containers/WaveContainer.cs @@ -29,6 +29,8 @@ namespace osu.Game.Graphics.Containers protected override Container Content => contentContainer; + protected override bool StartHidden => true; + public Color4 FirstWaveColour { get => firstWave.Colour; diff --git a/osu.Game/Overlays/WaveOverlayContainer.cs b/osu.Game/Overlays/WaveOverlayContainer.cs index 5059b136ee..5c87096dd4 100644 --- a/osu.Game/Overlays/WaveOverlayContainer.cs +++ b/osu.Game/Overlays/WaveOverlayContainer.cs @@ -14,6 +14,8 @@ namespace osu.Game.Overlays protected override bool BlockNonPositionalInput => true; protected override Container Content => Waves; + protected override bool StartHidden => true; + protected WaveOverlayContainer() { AddInternal(Waves = new WaveContainer From 451765784a9f5324c1f011e6c9593e714ffe749b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 15:17:15 +0900 Subject: [PATCH 1492/2854] Centralise SocialOverlay's scheduling logic Just some clean-ups to make it easier to confirm correct logic --- osu.Game/Overlays/SocialOverlay.cs | 91 +++++++++++++++--------------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/osu.Game/Overlays/SocialOverlay.cs b/osu.Game/Overlays/SocialOverlay.cs index 780a80b4fc..4def249200 100644 --- a/osu.Game/Overlays/SocialOverlay.cs +++ b/osu.Game/Overlays/SocialOverlay.cs @@ -66,24 +66,64 @@ namespace osu.Game.Overlays } }; - Header.Tabs.Current.ValueChanged += _ => Scheduler.AddOnce(updateSearch); + Header.Tabs.Current.ValueChanged += _ => queueUpdate(); - Filter.Tabs.Current.ValueChanged += _ => Scheduler.AddOnce(updateSearch); + Filter.Tabs.Current.ValueChanged += _ => queueUpdate(); Filter.DisplayStyleControl.DisplayStyle.ValueChanged += style => recreatePanels(style.NewValue); - Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += _ => Scheduler.AddOnce(updateSearch); + Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += _ => queueUpdate(); + currentQuery.BindTo(Filter.Search.Current); currentQuery.ValueChanged += query => { queryChangedDebounce?.Cancel(); if (string.IsNullOrEmpty(query.NewValue)) - Scheduler.AddOnce(updateSearch); + queueUpdate(); else queryChangedDebounce = Scheduler.AddDelayed(updateSearch, 500); }; + } - currentQuery.BindTo(Filter.Search.Current); + private APIRequest getUsersRequest; + + private readonly Bindable currentQuery = new Bindable(); + + private ScheduledDelegate queryChangedDebounce; + + private void queueUpdate() => Scheduler.AddOnce(updateSearch); + + private void updateSearch() + { + queryChangedDebounce?.Cancel(); + + if (!IsLoaded) + return; + + Users = null; + clearPanels(); + loading.Hide(); + getUsersRequest?.Cancel(); + + if (API?.IsLoggedIn != true) + return; + + switch (Header.Tabs.Current.Value) + { + case SocialTab.Friends: + var friendRequest = new GetFriendsRequest(); // TODO filter arguments? + friendRequest.Success += updateUsers; + API.Queue(getUsersRequest = friendRequest); + break; + + default: + var userRequest = new GetUsersRequest(); // TODO filter arguments! + userRequest.Success += response => updateUsers(response.Select(r => r.User)); + API.Queue(getUsersRequest = userRequest); + break; + } + + loading.Show(); } private void recreatePanels(PanelDisplayStyle displayStyle) @@ -133,45 +173,6 @@ namespace osu.Game.Overlays }); } - private APIRequest getUsersRequest; - - private readonly Bindable currentQuery = new Bindable(); - - private ScheduledDelegate queryChangedDebounce; - - private void updateSearch() - { - queryChangedDebounce?.Cancel(); - - if (!IsLoaded) - return; - - Users = null; - clearPanels(); - loading.Hide(); - getUsersRequest?.Cancel(); - - if (API?.IsLoggedIn != true) - return; - - switch (Header.Tabs.Current.Value) - { - case SocialTab.Friends: - var friendRequest = new GetFriendsRequest(); // TODO filter arguments? - friendRequest.Success += updateUsers; - API.Queue(getUsersRequest = friendRequest); - break; - - default: - var userRequest = new GetUsersRequest(); // TODO filter arguments! - userRequest.Success += response => updateUsers(response.Select(r => r.User)); - API.Queue(getUsersRequest = userRequest); - break; - } - - loading.Show(); - } - private void updateUsers(IEnumerable newUsers) { Users = newUsers; @@ -193,7 +194,7 @@ namespace osu.Game.Overlays switch (state) { case APIState.Online: - Scheduler.AddOnce(updateSearch); + queueUpdate(); break; default: From 29bb227de28fc264a77ded4cc1180e0a28244b55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 16:28:06 +0900 Subject: [PATCH 1493/2854] Avoid Intro screen holding references to the intro beatmap --- osu.Game/Screens/Menu/Intro.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index c52e8541c5..dab5066c52 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -86,6 +86,7 @@ namespace osu.Game.Screens.Menu if (!resuming) { Beatmap.Value = introBeatmap; + introBeatmap = null; if (menuVoice.Value) welcome.Play(); @@ -94,7 +95,10 @@ namespace osu.Game.Screens.Menu { // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu. if (menuMusic.Value) + { track.Start(); + track = null; + } LoadComponentAsync(mainMenu = new MainMenu()); From 6c7b97931e2642090c7d28c8779498d4a46959f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 17:45:46 +0900 Subject: [PATCH 1494/2854] Avoid using a BufferedContainer for backgrounds unless required --- osu.Game/Graphics/Backgrounds/Background.cs | 36 ++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index db055d15e5..8fdb8ebcdd 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -6,23 +6,28 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Transforms; +using osuTK; namespace osu.Game.Graphics.Backgrounds { - public class Background : BufferedContainer + /// + /// A background which offer blurring on demand. + /// + public class Background : CompositeDrawable { public Sprite Sprite; private readonly string textureName; + private BufferedContainer bufferedContainer; + public Background(string textureName = @"") { - CacheDrawnFrameBuffer = true; - this.textureName = textureName; RelativeSizeAxes = Axes.Both; - Add(Sprite = new Sprite + AddInternal(Sprite = new Sprite { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, @@ -37,5 +42,28 @@ namespace osu.Game.Graphics.Backgrounds if (!string.IsNullOrEmpty(textureName)) Sprite.Texture = textures.Get(textureName); } + + public Vector2 BlurSigma => bufferedContainer?.BlurSigma ?? Vector2.Zero; + + /// + /// Smoothly adjusts over time. + /// + /// A to which further transforms can be added. + public void BlurTo(Vector2 newBlurSigma, double duration = 0, Easing easing = Easing.None) + { + if (bufferedContainer == null) + { + RemoveInternal(Sprite); + + AddInternal(bufferedContainer = new BufferedContainer + { + CacheDrawnFrameBuffer = true, + RelativeSizeAxes = Axes.Both, + Child = Sprite + }); + } + + bufferedContainer.BlurTo(newBlurSigma, duration, easing); + } } } From 7b2227c5053f78fb3bda55854259fa7a6422e768 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 17:47:19 +0900 Subject: [PATCH 1495/2854] Fix xmldoc --- osu.Game/Graphics/Backgrounds/Background.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index 8fdb8ebcdd..526b3da8a6 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -12,7 +12,7 @@ using osuTK; namespace osu.Game.Graphics.Backgrounds { /// - /// A background which offer blurring on demand. + /// A background which offers blurring via a on demand. /// public class Background : CompositeDrawable { From 587be955c3e20d667067660be5b8aa9d1f72aa31 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 17:57:23 +0900 Subject: [PATCH 1496/2854] Increase number of backgrounds in line with resources --- osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 7092ac0c4a..55338ea01a 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.Backgrounds private Background background; private int currentDisplay; - private const int background_count = 5; + private const int background_count = 7; private string backgroundName => $@"Menu/menu-background-{currentDisplay % background_count + 1}"; From 79b0deb353c0d13bcf0a86ad46059ef2b82dc05a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 17:59:25 +0900 Subject: [PATCH 1497/2854] Update resources reference --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d6a998bf55..8c4f5dcb7d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,7 +14,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index de4a14f01f..113874f6f6 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -104,7 +104,7 @@ - + From 08dfe413c11d3112735e020dd146a9f0d63923da Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 2 Jul 2019 13:07:36 +0300 Subject: [PATCH 1498/2854] Refactor Ruleset Selector in Direct --- .../Overlays/Direct/DirectRulesetSelector.cs | 94 +++++++++++++++++++ osu.Game/Overlays/Direct/FilterControl.cs | 85 +---------------- 2 files changed, 99 insertions(+), 80 deletions(-) create mode 100644 osu.Game/Overlays/Direct/DirectRulesetSelector.cs diff --git a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs new file mode 100644 index 0000000000..b42633eed3 --- /dev/null +++ b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs @@ -0,0 +1,94 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Direct +{ + public class DirectRulesetSelector : RulesetSelector + { + public override bool HandleNonPositionalInput => !Current.Disabled && base.HandleNonPositionalInput; + + public override bool HandlePositionalInput => !Current.Disabled && base.HandlePositionalInput; + + public override bool PropagatePositionalInputSubTree => !Current.Disabled && base.PropagatePositionalInputSubTree; + + public DirectRulesetSelector() + { + TabContainer.Masking = false; + TabContainer.Spacing = new Vector2(10, 0); + AutoSizeAxes = Axes.Both; + + Current.DisabledChanged += value => SelectedTab.FadeColour(value ? Color4.DarkGray : Color4.White, 200, Easing.OutQuint); + } + + protected override TabItem CreateTabItem(RulesetInfo value) => new DirectRulesetTabItem(value); + + protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer + { + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + }; + + private class DirectRulesetTabItem : TabItem + { + private readonly SpriteIcon icon; + + public DirectRulesetTabItem(RulesetInfo value) + : base(value) + { + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + icon = (SpriteIcon)value.CreateInstance().CreateIcon(), + new HoverClickSounds() + }; + + icon.Size = new Vector2(30); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + updateState(); + } + + protected override bool OnHover(HoverEvent e) + { + base.OnHover(e); + updateState(); + return true; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + updateState(); + } + + protected override void OnActivated() => updateState(); + + protected override void OnDeactivated() => updateState(); + + private void updateState() + { + if (IsHovered || Active.Value) + { + icon.FadeColour(Color4.White, 120, Easing.InQuad); + } + else + icon.FadeColour(Color4.Gray, 120, Easing.InQuad); + } + } + } +} diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index 268e011350..4b43542b43 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -4,105 +4,30 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests; using osu.Game.Overlays.SearchableList; using osu.Game.Rulesets; -using osuTK; using osuTK.Graphics; namespace osu.Game.Overlays.Direct { public class FilterControl : SearchableListFilterControl { - public readonly Bindable Ruleset = new Bindable(); - private FillFlowContainer modeButtons; + private DirectRulesetSelector rulesetSelector; protected override Color4 BackgroundColour => OsuColour.FromHex(@"384552"); protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked; - protected override Drawable CreateSupplementaryControls() - { - modeButtons = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(10f, 0f), - }; + protected override Drawable CreateSupplementaryControls() => rulesetSelector = new DirectRulesetSelector(); - return modeButtons; - } + public Bindable Ruleset => rulesetSelector.Current; [BackgroundDependencyLoader(true)] - private void load(RulesetStore rulesets, OsuColour colours, Bindable ruleset) + private void load(OsuColour colours, Bindable ruleset) { DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark; - - Ruleset.Value = ruleset.Value ?? rulesets.GetRuleset(0); - foreach (var r in rulesets.AvailableRulesets) - modeButtons.Add(new RulesetToggleButton(Ruleset, r)); - } - - private class RulesetToggleButton : OsuClickableContainer - { - private Drawable icon - { - get => iconContainer.Icon; - set => iconContainer.Icon = value; - } - - private RulesetInfo ruleset; - - public RulesetInfo Ruleset - { - get => ruleset; - set - { - ruleset = value; - icon = Ruleset.CreateInstance().CreateIcon(); - } - } - - private readonly Bindable bindable; - - private readonly ConstrainedIconContainer iconContainer; - - private void Bindable_ValueChanged(ValueChangedEvent e) - { - iconContainer.FadeTo(Ruleset.ID == e.NewValue?.ID ? 1f : 0.5f, 100); - } - - public override bool HandleNonPositionalInput => !bindable.Disabled && base.HandleNonPositionalInput; - public override bool HandlePositionalInput => !bindable.Disabled && base.HandlePositionalInput; - - public RulesetToggleButton(Bindable bindable, RulesetInfo ruleset) - { - this.bindable = bindable; - AutoSizeAxes = Axes.Both; - - Children = new[] - { - iconContainer = new ConstrainedIconContainer - { - Origin = Anchor.TopLeft, - Anchor = Anchor.TopLeft, - Size = new Vector2(32), - } - }; - - Ruleset = ruleset; - bindable.ValueChanged += Bindable_ValueChanged; - Bindable_ValueChanged(new ValueChangedEvent(bindable.Value, bindable.Value)); - Action = () => bindable.Value = Ruleset; - } - - protected override void Dispose(bool isDisposing) - { - if (bindable != null) - bindable.ValueChanged -= Bindable_ValueChanged; - base.Dispose(isDisposing); - } + rulesetSelector.Current.BindTo(ruleset); } } From 2971bd8cbcf3ed62c98d4e703963ac0bdcde35fb Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 2 Jul 2019 13:22:38 +0300 Subject: [PATCH 1499/2854] Add disable trigger to a testcase --- .../Visual/Online/TestSceneDirectOverlay.cs | 40 +++++++------------ .../Overlays/Direct/DirectRulesetSelector.cs | 2 +- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs index efc12c5fdd..75c2a2a6a1 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs @@ -3,10 +3,8 @@ using System.Collections.Generic; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Overlays; -using osu.Game.Rulesets; namespace osu.Game.Tests.Visual.Online { @@ -14,7 +12,6 @@ namespace osu.Game.Tests.Visual.Online public class TestSceneDirectOverlay : OsuTestScene { private DirectOverlay direct; - private RulesetStore rulesets; protected override void LoadComplete() { @@ -25,18 +22,11 @@ namespace osu.Game.Tests.Visual.Online AddStep(@"toggle", direct.ToggleVisibility); AddStep(@"result counts", () => direct.ResultAmounts = new DirectOverlay.ResultCounts(1, 4, 13)); - } - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - this.rulesets = rulesets; + AddStep(@"trigger disabled", () => Ruleset.Disabled = !Ruleset.Disabled); } private void newBeatmaps() { - var ruleset = rulesets.GetRuleset(0); - direct.BeatmapSets = new[] { new BeatmapSetInfo @@ -65,7 +55,7 @@ namespace osu.Game.Tests.Visual.Online { new BeatmapInfo { - Ruleset = ruleset, + Ruleset = Ruleset.Value, StarDifficulty = 5.35f, Metadata = new BeatmapMetadata(), }, @@ -97,7 +87,7 @@ namespace osu.Game.Tests.Visual.Online { new BeatmapInfo { - Ruleset = ruleset, + Ruleset = Ruleset.Value, StarDifficulty = 5.81f, Metadata = new BeatmapMetadata(), }, @@ -129,23 +119,23 @@ namespace osu.Game.Tests.Visual.Online { new BeatmapInfo { - Ruleset = ruleset, + Ruleset = Ruleset.Value, StarDifficulty = 0.9f, Metadata = new BeatmapMetadata(), }, new BeatmapInfo { - Ruleset = ruleset, + Ruleset = Ruleset.Value, StarDifficulty = 1.1f, }, new BeatmapInfo { - Ruleset = ruleset, + Ruleset = Ruleset.Value, StarDifficulty = 2.02f, }, new BeatmapInfo { - Ruleset = ruleset, + Ruleset = Ruleset.Value, StarDifficulty = 3.49f, }, }, @@ -176,43 +166,43 @@ namespace osu.Game.Tests.Visual.Online { new BeatmapInfo { - Ruleset = ruleset, + Ruleset = Ruleset.Value, StarDifficulty = 1.26f, Metadata = new BeatmapMetadata(), }, new BeatmapInfo { - Ruleset = ruleset, + Ruleset = Ruleset.Value, StarDifficulty = 2.01f, }, new BeatmapInfo { - Ruleset = ruleset, + Ruleset = Ruleset.Value, StarDifficulty = 2.87f, }, new BeatmapInfo { - Ruleset = ruleset, + Ruleset = Ruleset.Value, StarDifficulty = 3.76f, }, new BeatmapInfo { - Ruleset = ruleset, + Ruleset = Ruleset.Value, StarDifficulty = 3.93f, }, new BeatmapInfo { - Ruleset = ruleset, + Ruleset = Ruleset.Value, StarDifficulty = 4.37f, }, new BeatmapInfo { - Ruleset = ruleset, + Ruleset = Ruleset.Value, StarDifficulty = 5.13f, }, new BeatmapInfo { - Ruleset = ruleset, + Ruleset = Ruleset.Value, StarDifficulty = 5.42f, }, }, diff --git a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs index b42633eed3..289c44a822 100644 --- a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs +++ b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.Direct private readonly SpriteIcon icon; public DirectRulesetTabItem(RulesetInfo value) - : base(value) + : base(value) { AutoSizeAxes = Axes.Both; From ee516d2515c47a3b7728805b6b7f3c8332afb528 Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 2 Jul 2019 15:55:30 +0530 Subject: [PATCH 1500/2854] Make direct panel download and replay buttons share UI --- .../Gameplay/TestSceneReplayDownloadButton.cs | 2 + .../UserInterface/OsuDownloadButton.cs | 87 ++++++++++++++ osu.Game/Overlays/Direct/DownloadButton.cs | 61 ++-------- osu.Game/Screens/Play/ReplayDownloadButton.cs | 108 ++++-------------- .../Screens/Ranking/Pages/ScoreResultsPage.cs | 3 +- 5 files changed, 119 insertions(+), 142 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/OsuDownloadButton.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index e71b2d596e..0dfcda122f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -9,6 +9,7 @@ using osu.Game.Rulesets.Osu; using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Users; +using osuTK; using System; using System.Collections.Generic; @@ -41,6 +42,7 @@ namespace osu.Game.Tests.Visual.Gameplay { Anchor = Anchor.Centre, Origin = Anchor.Centre, + Size = new Vector2(80, 40), }; }); } diff --git a/osu.Game/Graphics/UserInterface/OsuDownloadButton.cs b/osu.Game/Graphics/UserInterface/OsuDownloadButton.cs new file mode 100644 index 0000000000..6e95c7e291 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/OsuDownloadButton.cs @@ -0,0 +1,87 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Online; +using osuTK; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuDownloadButton : OsuAnimatedButton + { + public readonly Bindable State = new Bindable(); + + private readonly SpriteIcon icon; + private readonly SpriteIcon checkmark; + private readonly Box background; + + private OsuColour colours; + + public OsuDownloadButton() + { + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Depth = float.MaxValue + }, + icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(13), + Icon = FontAwesome.Solid.Download, + }, + checkmark = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + X = 8, + Size = Vector2.Zero, + Icon = FontAwesome.Solid.Check, + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + this.colours = colours; + + State.BindValueChanged(updateState, true); + } + + private void updateState(ValueChangedEvent state) + { + switch (state.NewValue) + { + case DownloadState.NotDownloaded: + background.FadeColour(colours.Gray4, 500, Easing.InOutExpo); + icon.MoveToX(0, 500, Easing.InOutExpo); + checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo); + break; + + case DownloadState.Downloading: + background.FadeColour(colours.Blue, 500, Easing.InOutExpo); + icon.MoveToX(0, 500, Easing.InOutExpo); + checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo); + break; + + case DownloadState.Downloaded: + background.FadeColour(colours.Yellow, 500, Easing.InOutExpo); + break; + + case DownloadState.LocallyAvailable: + background.FadeColour(colours.Green, 500, Easing.InOutExpo); + icon.MoveToX(-8, 500, Easing.InOutExpo); + checkmark.ScaleTo(new Vector2(13), 500, Easing.InOutExpo); + break; + } + } + } +} diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs index 81709187e7..9aec7bcd0c 100644 --- a/osu.Game/Overlays/Direct/DownloadButton.cs +++ b/osu.Game/Overlays/Direct/DownloadButton.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -10,7 +10,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; -using osuTK; namespace osu.Game.Overlays.Direct { @@ -25,7 +24,7 @@ namespace osu.Game.Overlays.Direct private OsuColour colours; private readonly ShakeContainer shakeContainer; - private readonly OsuAnimatedButton button; + private readonly OsuDownloadButton button; public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) : base(beatmapSet) @@ -35,33 +34,10 @@ namespace osu.Game.Overlays.Direct InternalChild = shakeContainer = new ShakeContainer { RelativeSizeAxes = Axes.Both, - Child = button = new OsuAnimatedButton + Child = button = new OsuDownloadButton { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - Depth = float.MaxValue - }, - icon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(13), - Icon = FontAwesome.Solid.Download, - }, - checkmark = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - X = 8, - Size = Vector2.Zero, - Icon = FontAwesome.Solid.Check, - } - } - } + }, }; } @@ -69,7 +45,7 @@ namespace osu.Game.Overlays.Direct { base.LoadComplete(); - State.BindValueChanged(state => updateState(state.NewValue), true); + button.State.BindTo(State); FinishTransforms(true); } @@ -105,32 +81,11 @@ namespace osu.Game.Overlays.Direct }; } - private void updateState(DownloadState state) + protected override void Dispose(bool isDisposing) { - switch (state) - { - case DownloadState.NotDownloaded: - background.FadeColour(colours.Gray4, 500, Easing.InOutExpo); - icon.MoveToX(0, 500, Easing.InOutExpo); - checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo); - break; + base.Dispose(isDisposing); - case DownloadState.Downloading: - background.FadeColour(colours.Blue, 500, Easing.InOutExpo); - icon.MoveToX(0, 500, Easing.InOutExpo); - checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo); - break; - - case DownloadState.Downloaded: - background.FadeColour(colours.Yellow, 500, Easing.InOutExpo); - break; - - case DownloadState.LocallyAvailable: - background.FadeColour(colours.Green, 500, Easing.InOutExpo); - icon.MoveToX(-8, 500, Easing.InOutExpo); - checkmark.ScaleTo(new Vector2(13), 500, Easing.InOutExpo); - break; - } + button?.State.UnbindAll(); } } } diff --git a/osu.Game/Screens/Play/ReplayDownloadButton.cs b/osu.Game/Screens/Play/ReplayDownloadButton.cs index d715f17109..9655bde36a 100644 --- a/osu.Game/Screens/Play/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Play/ReplayDownloadButton.cs @@ -2,57 +2,28 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics; -using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online; using osu.Game.Scoring; -using osuTK; -using osu.Framework.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; -using osu.Framework.Graphics.Effects; -using osuTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Screens.Play { - public class ReplayDownloadButton : DownloadTrackingComposite, IHasTooltip + public class ReplayDownloadButton : DownloadTrackingComposite { [Resolved] private ScoreManager scores { get; set; } - private OsuClickableContainer button; - private SpriteIcon downloadIcon; - private SpriteIcon playIcon; + private OsuDownloadButton button; private ShakeContainer shakeContainer; - private CircularContainer circle; - - public string TooltipText - { - get - { - switch (replayAvailability) - { - case ReplayAvailability.Local: - return @"Watch replay"; - - case ReplayAvailability.Online: - return @"Download replay"; - - default: - return @"Replay unavailable"; - } - } - } private ReplayAvailability replayAvailability { get { - if (scores.IsAvailableLocally(Model.Value)) + if (State.Value == DownloadState.LocallyAvailable) return ReplayAvailability.Local; if (Model.Value is APILegacyScoreInfo apiScore && apiScore.Replay) @@ -65,54 +36,18 @@ namespace osu.Game.Screens.Play public ReplayDownloadButton(ScoreInfo score) : base(score) { - AutoSizeAxes = Axes.Both; } [BackgroundDependencyLoader(true)] - private void load(OsuGame game, OsuColour colours) + private void load(OsuGame game) { InternalChild = shakeContainer = new ShakeContainer { - AutoSizeAxes = Axes.Both, - Child = circle = new CircularContainer + RelativeSizeAxes = Axes.Both, + Child = button = new OsuDownloadButton { - Masking = true, - Size = new Vector2(40), - EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.Black.Opacity(0.4f), - Type = EdgeEffectType.Shadow, - Radius = 5, - }, - Child = button = new OsuClickableContainer - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.GrayF, - }, - playIcon = new SpriteIcon - { - Icon = FontAwesome.Solid.Play, - Size = Vector2.Zero, - Colour = colours.Gray3, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - downloadIcon = new SpriteIcon - { - Icon = FontAwesome.Solid.FileDownload, - Size = Vector2.Zero, - Colour = colours.Gray3, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - }, - } - }, + RelativeSizeAxes = Axes.Both, + } }; button.Action = () => @@ -127,32 +62,29 @@ namespace osu.Game.Screens.Play scores.Download(Model.Value); break; + case DownloadState.Downloaded: case DownloadState.Downloading: shakeContainer.Shake(); break; } }; - State.BindValueChanged(state => + State.BindValueChanged((state) => { - switch (state.NewValue) + button.State.Value = state.NewValue; + + switch (replayAvailability) { - case DownloadState.Downloading: - playIcon.ResizeTo(Vector2.Zero, 400, Easing.OutQuint); - downloadIcon.ResizeTo(13, 400, Easing.OutQuint); - circle.FadeEdgeEffectTo(colours.Yellow, 400, Easing.OutQuint); + case ReplayAvailability.Local: + button.TooltipText = @"Watch replay"; break; - case DownloadState.LocallyAvailable: - playIcon.ResizeTo(13, 400, Easing.OutQuint); - downloadIcon.ResizeTo(Vector2.Zero, 400, Easing.OutQuint); - circle.FadeEdgeEffectTo(Color4.Black.Opacity(0.4f), 400, Easing.OutQuint); + case ReplayAvailability.Online: + button.TooltipText = @"Download replay"; break; - case DownloadState.NotDownloaded: - playIcon.ResizeTo(Vector2.Zero, 400, Easing.OutQuint); - downloadIcon.ResizeTo(13, 400, Easing.OutQuint); - circle.FadeEdgeEffectTo(Color4.Black.Opacity(0.4f), 400, Easing.OutQuint); + default: + button.TooltipText = @"Replay unavailable"; break; } }, true); diff --git a/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs b/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs index 676c1e3adf..7c35742ff6 100644 --- a/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs +++ b/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs @@ -173,7 +173,8 @@ namespace osu.Game.Screens.Ranking.Pages { Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, - Margin = new MarginPadding { Bottom = 5 }, + Margin = new MarginPadding { Bottom = 10 }, + Size = new Vector2(50, 30), }, }; From 082fa0d808c153c105f269ee9a017dfdb0ec83ef Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 2 Jul 2019 13:35:02 +0300 Subject: [PATCH 1501/2854] simplify updateState logic --- osu.Game/Overlays/Direct/DirectRulesetSelector.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs index 289c44a822..f2abca9ce6 100644 --- a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs +++ b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs @@ -80,15 +80,7 @@ namespace osu.Game.Overlays.Direct protected override void OnDeactivated() => updateState(); - private void updateState() - { - if (IsHovered || Active.Value) - { - icon.FadeColour(Color4.White, 120, Easing.InQuad); - } - else - icon.FadeColour(Color4.Gray, 120, Easing.InQuad); - } + private void updateState() => icon.FadeColour(IsHovered || Active.Value ? Color4.White : Color4.Gray, 120, Easing.InQuad); } } } From bc821fcc5d0e16ee9b2891c9e7ce3ff04da4121c Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 1 Jul 2019 15:28:34 +0200 Subject: [PATCH 1502/2854] Add native bass libs for x86 Android --- osu.Android/lib/x86/libbass.so | Bin 0 -> 316716 bytes osu.Android/lib/x86/libbass_fx.so | Bin 0 -> 153532 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 osu.Android/lib/x86/libbass.so create mode 100644 osu.Android/lib/x86/libbass_fx.so diff --git a/osu.Android/lib/x86/libbass.so b/osu.Android/lib/x86/libbass.so new file mode 100644 index 0000000000000000000000000000000000000000..b0f758a42b7397b3289557f51de6b55df7b3b4ea GIT binary patch literal 316716 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 z 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 kkiFcJ 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 E0opfv^#A|> literal 0 HcmV?d00001 From 1a3cc25d6c375784f1a285c56d9367ba89cb551a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 19:40:33 +0900 Subject: [PATCH 1503/2854] Update in accordance with visual studio changes Adds x86 emulator deployment target --- osu.Android/Properties/AndroidManifest.xml | 14 +++++++------- osu.Android/osu.Android.csproj | 1 + .../osu.Game.Rulesets.Catch.Tests.Android.csproj | 1 + .../osu.Game.Rulesets.Mania.Tests.Android.csproj | 1 + .../osu.Game.Rulesets.Osu.Tests.Android.csproj | 1 + .../osu.Game.Rulesets.Taiko.Tests.Android.csproj | 1 + .../osu.Game.Tests.Android.csproj | 1 + 7 files changed, 13 insertions(+), 7 deletions(-) diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml index 6f77560e01..edbf651d09 100644 --- a/osu.Android/Properties/AndroidManifest.xml +++ b/osu.Android/Properties/AndroidManifest.xml @@ -1,10 +1,10 @@  - - - - - - - + + + + + + + \ No newline at end of file diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 6a859742ee..f56d38ac5c 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -12,6 +12,7 @@ osu.Android osu.Android Properties\AndroidManifest.xml + armeabi-v7a;x86;arm64-v8a diff --git a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj index ed746921be..88b420ffad 100644 --- a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj +++ b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj @@ -12,6 +12,7 @@ osu.Game.Rulesets.Catch.Tests osu.Game.Rulesets.Catch.Tests.Android Properties\AndroidManifest.xml + armeabi-v7a;x86;arm64-v8a diff --git a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj index b366958342..0e557cb260 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj +++ b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj @@ -12,6 +12,7 @@ osu.Game.Rulesets.Mania.Tests osu.Game.Rulesets.Mania.Tests.Android Properties\AndroidManifest.xml + armeabi-v7a;x86;arm64-v8a diff --git a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj index a40f7ca588..dcf1573522 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj +++ b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj @@ -12,6 +12,7 @@ osu.Game.Rulesets.Osu.Tests osu.Game.Rulesets.Osu.Tests.Android Properties\AndroidManifest.xml + armeabi-v7a;x86;arm64-v8a diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj index bc7b00ffc8..392442b713 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj +++ b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj @@ -12,6 +12,7 @@ osu.Game.Rulesets.Taiko.Tests osu.Game.Rulesets.Taiko.Tests.Android Properties\AndroidManifest.xml + armeabi-v7a;x86;arm64-v8a diff --git a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj index 4e83234e7d..c2dd194e09 100644 --- a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj +++ b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj @@ -12,6 +12,7 @@ osu.Game.Tests osu.Game.Tests.Android Properties\AndroidManifest.xml + armeabi-v7a;x86;arm64-v8a From eaa19d5a49faf44645e824ca48c62651249621d2 Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 2 Jul 2019 16:13:47 +0530 Subject: [PATCH 1504/2854] Remove unused/unnecessary fields --- osu.Game/Overlays/Direct/DownloadButton.cs | 10 +--------- osu.Game/Screens/Play/ReplayDownloadButton.cs | 7 ++----- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs index 9aec7bcd0c..6bac07fc88 100644 --- a/osu.Game/Overlays/Direct/DownloadButton.cs +++ b/osu.Game/Overlays/Direct/DownloadButton.cs @@ -3,8 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -18,11 +16,7 @@ namespace osu.Game.Overlays.Direct protected bool DownloadEnabled => button.Enabled.Value; private readonly bool noVideo; - private readonly SpriteIcon icon; - private readonly SpriteIcon checkmark; - private readonly Box background; - private OsuColour colours; private readonly ShakeContainer shakeContainer; private readonly OsuDownloadButton button; @@ -50,10 +44,8 @@ namespace osu.Game.Overlays.Direct } [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, OsuGame game, BeatmapManager beatmaps) + private void load(OsuGame game, BeatmapManager beatmaps) { - this.colours = colours; - if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) { button.Enabled.Value = false; diff --git a/osu.Game/Screens/Play/ReplayDownloadButton.cs b/osu.Game/Screens/Play/ReplayDownloadButton.cs index 9655bde36a..5acf4e83d9 100644 --- a/osu.Game/Screens/Play/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Play/ReplayDownloadButton.cs @@ -13,9 +13,6 @@ namespace osu.Game.Screens.Play { public class ReplayDownloadButton : DownloadTrackingComposite { - [Resolved] - private ScoreManager scores { get; set; } - private OsuDownloadButton button; private ShakeContainer shakeContainer; @@ -39,7 +36,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader(true)] - private void load(OsuGame game) + private void load(OsuGame game, ScoreManager scores) { InternalChild = shakeContainer = new ShakeContainer { @@ -69,7 +66,7 @@ namespace osu.Game.Screens.Play } }; - State.BindValueChanged((state) => + State.BindValueChanged(state => { button.State.Value = state.NewValue; From 486e7e4e1e4e156824423852cea22e37230b4c81 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 19:44:04 +0900 Subject: [PATCH 1505/2854] Remove unnecessary file --- osu.Android/monogc.txt | 1 - osu.Android/osu.Android.csproj | 3 --- 2 files changed, 4 deletions(-) delete mode 100644 osu.Android/monogc.txt diff --git a/osu.Android/monogc.txt b/osu.Android/monogc.txt deleted file mode 100644 index d15a2bd2ae..0000000000 --- a/osu.Android/monogc.txt +++ /dev/null @@ -1 +0,0 @@ -MONO_GC_PARAMS=nursery-size=8m \ No newline at end of file diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index f56d38ac5c..42a3185cd1 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -46,8 +46,5 @@ - - - \ No newline at end of file From 7dbba87e4c83d98defef5d4f01bb3b37607d9e06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 19:55:16 +0900 Subject: [PATCH 1506/2854] Update dependencies oops --- osu.Android.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 74fe16ebf6..ead059cdf2 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,7 +63,7 @@ - - + + From f45b42021ff01782b0594a2c97ee89f5dfc2e7b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 19:45:55 +0900 Subject: [PATCH 1507/2854] Change app label to just "osu!" --- osu.Android/Properties/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml index edbf651d09..326d32f7ab 100644 --- a/osu.Android/Properties/AndroidManifest.xml +++ b/osu.Android/Properties/AndroidManifest.xml @@ -6,5 +6,5 @@ - + \ No newline at end of file From 1ff6a9d085da89471a8d608cd4dd72251cef4a52 Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 2 Jul 2019 16:25:40 +0530 Subject: [PATCH 1508/2854] Remove unused using --- osu.Game/Overlays/Direct/DownloadButton.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs index 6bac07fc88..dac1521bf3 100644 --- a/osu.Game/Overlays/Direct/DownloadButton.cs +++ b/osu.Game/Overlays/Direct/DownloadButton.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; -using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; From a26b14a4f89a44ee8d9adb96774b75fa4989e46e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 22:21:56 +0900 Subject: [PATCH 1509/2854] Move finaliser inside disposal region --- osu.Game/Beatmaps/WorkingBeatmap.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 61390fe51b..40b3d70262 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -46,11 +46,6 @@ namespace osu.Game.Beatmaps skin = new RecyclableLazy(GetSkin); } - ~WorkingBeatmap() - { - Dispose(false); - } - protected virtual Track GetVirtualTrack() { const double excess_length = 1000; @@ -229,6 +224,11 @@ namespace osu.Game.Beatmaps beatmapCancellation.Cancel(); } + ~WorkingBeatmap() + { + Dispose(false); + } + #endregion public class RecyclableLazy From 0b66f139020b9a13e3816814c76078db25c97a72 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 22:22:33 +0900 Subject: [PATCH 1510/2854] Add todo about beatmap load cancellation --- osu.Game/Beatmaps/WorkingBeatmap.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 40b3d70262..2f611b8409 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -145,6 +145,7 @@ namespace osu.Game.Beatmaps public Task LoadBeatmapAsync() => (beatmapLoadTask ?? (beatmapLoadTask = Task.Factory.StartNew(() => { + // Todo: Handle cancellation during beatmap parsing var b = GetBeatmap() ?? new Beatmap(); // The original beatmap version needs to be preserved as the database doesn't contain it From a6acc1f99f25e9dfbd344679ba9cc147e5eb9e3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 22:25:51 +0900 Subject: [PATCH 1511/2854] Catch exception and return null for safety . --- osu.Game/Beatmaps/WorkingBeatmap.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 2f611b8409..a4324ecb0c 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -157,7 +157,20 @@ namespace osu.Game.Beatmaps return b; }, beatmapCancellation.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default))); - public IBeatmap Beatmap => LoadBeatmapAsync().Result; + public IBeatmap Beatmap + { + get + { + try + { + return LoadBeatmapAsync().Result; + } + catch (TaskCanceledException) + { + return null; + } + } + } private readonly CancellationTokenSource beatmapCancellation = new CancellationTokenSource(); protected abstract IBeatmap GetBeatmap(); From 9e33fb35e9241dda9fa34c33804bae5f7a9e9670 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 22:24:08 +0900 Subject: [PATCH 1512/2854] Fix typo --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index a4324ecb0c..00ba8963cb 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -231,7 +231,7 @@ namespace osu.Game.Beatmaps { // recycling logic is not here for the time being, as components which use // retrieved objects from WorkingBeatmap may not hold a reference to the WorkingBeatmap itself. - // this should be fine as each retrieved comopnent do have their own finalizers. + // this should be fine as each retrieved component do have their own finalizers. // cancelling the beatmap load is safe for now since the retrieval is a synchronous // operation. if we add an async retrieval method this may need to be reconsidered. From f31d840c13eecb92af95e69aceb26fad15a69a0f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 22:25:03 +0900 Subject: [PATCH 1513/2854] Dispose previous WorkingBeatmap on change --- osu.Game/OsuGame.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index bfa4aeadef..49c543537a 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -296,6 +296,8 @@ namespace osu.Game if (nextBeatmap?.Track != null) nextBeatmap.Track.Completed += currentTrackCompleted; + beatmap.OldValue?.Dispose(); + nextBeatmap?.LoadBeatmapAsync(); } From 93511266e5d771b4c2099ddd1b5b45b39b6c81b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 22:33:33 +0900 Subject: [PATCH 1514/2854] Fix waves not displaying at all --- osu.Game/Graphics/Containers/WaveContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/WaveContainer.cs b/osu.Game/Graphics/Containers/WaveContainer.cs index 9050e775f7..17e4f0afd5 100644 --- a/osu.Game/Graphics/Containers/WaveContainer.cs +++ b/osu.Game/Graphics/Containers/WaveContainer.cs @@ -125,7 +125,7 @@ namespace osu.Game.Graphics.Containers // This is done as an optimization, such that invisible parts of the waves // are masked away, and thus do not consume fill rate. - wavesContainer.Height = Math.Max(0, DrawHeight - (contentContainer.DrawHeight - contentContainer.Y)); + wavesContainer.Height = Math.Max(0, DrawHeight - (contentContainer.DrawHeight - contentContainer.Y * DrawHeight)); } private class Wave : VisibilityContainer From e7a7f2f660c0eed21b1d7696f3236038f59c2edf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 22:39:42 +0900 Subject: [PATCH 1515/2854] Add statistic for count of alive WorkingBeatmaps --- osu.Game/Beatmaps/WorkingBeatmap.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 00ba8963cb..138d911556 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -13,6 +13,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework.Audio; +using osu.Framework.Statistics; using osu.Game.IO.Serialization; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; @@ -32,6 +33,8 @@ namespace osu.Game.Beatmaps protected AudioManager AudioManager { get; } + private static readonly GlobalStatistic total_count = GlobalStatistics.Get(nameof(Beatmaps), $"Total {nameof(WorkingBeatmap)}s"); + protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager) { AudioManager = audioManager; @@ -44,6 +47,8 @@ namespace osu.Game.Beatmaps waveform = new RecyclableLazy(GetWaveform); storyboard = new RecyclableLazy(GetStoryboard); skin = new RecyclableLazy(GetSkin); + + total_count.Value++; } protected virtual Track GetVirtualTrack() @@ -227,8 +232,15 @@ namespace osu.Game.Beatmaps GC.SuppressFinalize(this); } + private bool isDisposed; + protected virtual void Dispose(bool isDisposing) { + if (isDisposed) + return; + + isDisposed = true; + // recycling logic is not here for the time being, as components which use // retrieved objects from WorkingBeatmap may not hold a reference to the WorkingBeatmap itself. // this should be fine as each retrieved component do have their own finalizers. @@ -236,6 +248,8 @@ namespace osu.Game.Beatmaps // cancelling the beatmap load is safe for now since the retrieval is a synchronous // operation. if we add an async retrieval method this may need to be reconsidered. beatmapCancellation.Cancel(); + + total_count.Value--; } ~WorkingBeatmap() From 8e0b5f16225f00266c52c61818636b798502770d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 23:21:13 +0900 Subject: [PATCH 1516/2854] Fix weird merge conflict --- osu.Game/Beatmaps/WorkingBeatmap.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index c6f26423dd..37aa0024da 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -51,11 +51,6 @@ namespace osu.Game.Beatmaps total_count.Value++; } - ~WorkingBeatmap() - { - Dispose(false); - } - protected virtual Track GetVirtualTrack() { const double excess_length = 1000; From 778c36c7d7a243696380f14fb0942ad08153cc9d Mon Sep 17 00:00:00 2001 From: miterosan Date: Tue, 2 Jul 2019 17:05:04 +0200 Subject: [PATCH 1517/2854] Allow discover of rulesets that are already loaded. --- osu.Game/Rulesets/RulesetStore.cs | 42 +++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 0ebadd73d2..919b5dde22 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -22,8 +22,20 @@ namespace osu.Game.Rulesets { AppDomain.CurrentDomain.AssemblyResolve += currentDomain_AssemblyResolve; - foreach (string file in Directory.GetFiles(Environment.CurrentDirectory, $"{ruleset_library_prefix}.*.dll") - .Where(f => !Path.GetFileName(f).Contains("Tests"))) + addLoadedRulesets(); + + IEnumerable files = new string[0]; + + try + { + files = Directory.GetFiles(Environment.CurrentDirectory, $"{ruleset_library_prefix}.*.dll"); + } + catch (Exception e) + { + Logger.Error(e, $"Could not load rulesets from directory {Environment.CurrentDirectory}"); + } + + foreach (string file in files.Where(f => !Path.GetFileName(f).Contains("Tests"))) loadRulesetFromFile(file); } @@ -111,6 +123,17 @@ namespace osu.Game.Rulesets } } + private static void addLoadedRulesets() + { + // on android the rulesets are already loaded + var loadedRulesets = AppDomain.CurrentDomain.GetAssemblies() + .Where(assembly => assembly.GetName().Name.StartsWith(ruleset_library_prefix, StringComparison.InvariantCultureIgnoreCase)) + .Where(assembly => !assembly.GetName().Name.Contains("Tests")); + + foreach (var ruleset in loadedRulesets) + addRuleset(ruleset); + } + private static void loadRulesetFromFile(string file) { var filename = Path.GetFileNameWithoutExtension(file); @@ -128,5 +151,20 @@ namespace osu.Game.Rulesets Logger.Error(e, $"Failed to load ruleset {filename}"); } } + + private static void addRuleset(Assembly assembly) + { + if (loaded_assemblies.ContainsKey(assembly)) + return; + + try + { + loaded_assemblies[assembly] = assembly.GetTypes().First(t => t.IsPublic && t.IsSubclassOf(typeof(Ruleset))); + } + catch (Exception e) + { + Logger.Error(e, $"Failed to add ruleset {assembly}"); + } + } } } From e1d0d2666997580a47f510c088cc3d0af4046f18 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 00:21:16 +0900 Subject: [PATCH 1518/2854] Add a note about local optimisation that may not be required in the future --- osu.Game/Graphics/Containers/WaveContainer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Graphics/Containers/WaveContainer.cs b/osu.Game/Graphics/Containers/WaveContainer.cs index 17e4f0afd5..c01674f5b4 100644 --- a/osu.Game/Graphics/Containers/WaveContainer.cs +++ b/osu.Game/Graphics/Containers/WaveContainer.cs @@ -125,6 +125,7 @@ namespace osu.Game.Graphics.Containers // This is done as an optimization, such that invisible parts of the waves // are masked away, and thus do not consume fill rate. + // todo: revert https://github.com/ppy/osu/commit/aff9e3617da0c8fe252169fae287e39b44575b5e after FTB is fixed on iOS. wavesContainer.Height = Math.Max(0, DrawHeight - (contentContainer.DrawHeight - contentContainer.Y * DrawHeight)); } From f1ceea8361e18ce23e9e5f4dc031f5bd40a393c5 Mon Sep 17 00:00:00 2001 From: miterosan Date: Tue, 2 Jul 2019 17:25:12 +0200 Subject: [PATCH 1519/2854] style fixes --- osu.Game/Rulesets/RulesetStore.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 919b5dde22..17834f8545 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -24,19 +24,17 @@ namespace osu.Game.Rulesets addLoadedRulesets(); - IEnumerable files = new string[0]; - try { - files = Directory.GetFiles(Environment.CurrentDirectory, $"{ruleset_library_prefix}.*.dll"); + string[] files = Directory.GetFiles(Environment.CurrentDirectory, $"{ruleset_library_prefix}.*.dll"); + + foreach (string file in files.Where(f => !Path.GetFileName(f).Contains("Tests"))) + loadRulesetFromFile(file); } catch (Exception e) { Logger.Error(e, $"Could not load rulesets from directory {Environment.CurrentDirectory}"); } - - foreach (string file in files.Where(f => !Path.GetFileName(f).Contains("Tests"))) - loadRulesetFromFile(file); } public RulesetStore(IDatabaseContextFactory factory) @@ -125,13 +123,15 @@ namespace osu.Game.Rulesets private static void addLoadedRulesets() { - // on android the rulesets are already loaded - var loadedRulesets = AppDomain.CurrentDomain.GetAssemblies() - .Where(assembly => assembly.GetName().Name.StartsWith(ruleset_library_prefix, StringComparison.InvariantCultureIgnoreCase)) - .Where(assembly => !assembly.GetName().Name.Contains("Tests")); + foreach (var ruleset in AppDomain.CurrentDomain.GetAssemblies()) + { + string rulesetName = ruleset.GetName().Name; + + if (!rulesetName.StartsWith(ruleset_library_prefix, StringComparison.InvariantCultureIgnoreCase) || ruleset.GetName().Name.Contains("Tests")) + continue; - foreach (var ruleset in loadedRulesets) addRuleset(ruleset); + } } private static void loadRulesetFromFile(string file) From f9c24f2281dc82ffd14ec3f47b7fc155112d9a66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 00:35:55 +0900 Subject: [PATCH 1520/2854] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8c4f5dcb7d..b59828a52e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 113874f6f6..ddbdaf3d18 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From d387629e5348aaa8ac3322a169401415e5ba6747 Mon Sep 17 00:00:00 2001 From: Joehu Date: Tue, 2 Jul 2019 10:52:24 -0700 Subject: [PATCH 1521/2854] Fix layout of profile top header to match web --- .../Profile/Header/TopHeaderContainer.cs | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index dc6c3f56f8..bdf047478d 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -72,18 +72,30 @@ namespace osu.Game.Overlays.Profile.Header new FillFlowContainer { AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, + Direction = FillDirection.Vertical, Children = new Drawable[] { - usernameText = new OsuSpriteText + new FillFlowContainer { - Font = OsuFont.GetFont(size: 24, weight: FontWeight.Regular) + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + usernameText = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 24, weight: FontWeight.Regular) + }, + openUserExternally = new ExternalLinkButton + { + Margin = new MarginPadding { Left = 5 }, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + } }, - openUserExternally = new ExternalLinkButton + titleText = new OsuSpriteText { - Margin = new MarginPadding { Left = 5 }, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(size: 18, weight: FontWeight.Regular) }, } }, @@ -95,10 +107,6 @@ namespace osu.Game.Overlays.Profile.Header AutoSizeAxes = Axes.Both, Children = new Drawable[] { - titleText = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 18, weight: FontWeight.Regular) - }, supporterTag = new SupporterIcon { Height = 20, From a1fb41ea66c7489d1036f7e17ca0f9eb5d9f9ba5 Mon Sep 17 00:00:00 2001 From: Joehu Date: Tue, 2 Jul 2019 10:53:52 -0700 Subject: [PATCH 1522/2854] Use FillFlowContainer on flag and country --- osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index bdf047478d..b0d7070994 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -119,10 +119,11 @@ namespace osu.Game.Overlays.Profile.Header Margin = new MarginPadding { Top = 10 }, Colour = colours.GreySeafoamLighter, }, - new Container + new FillFlowContainer { AutoSizeAxes = Axes.Both, Margin = new MarginPadding { Top = 5 }, + Direction = FillDirection.Horizontal, Children = new Drawable[] { userFlag = new UpdateableFlag @@ -133,7 +134,7 @@ namespace osu.Game.Overlays.Profile.Header userCountryText = new OsuSpriteText { Font = OsuFont.GetFont(size: 17.5f, weight: FontWeight.Regular), - Margin = new MarginPadding { Left = 40 }, + Margin = new MarginPadding { Left = 10 }, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, Colour = colours.GreySeafoamLighter, From 8b4ef52c13b2ce84c7ea387c9e1f08450b9a19bb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 3 Jul 2019 07:27:24 +0300 Subject: [PATCH 1523/2854] Revert unnecessary changes --- osu.Game/Screens/Play/HUDOverlay.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 6488bf9452..017bf70133 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -41,7 +40,6 @@ namespace osu.Game.Screens.Play private readonly IReadOnlyList mods; private Bindable showHud; - private Bindable hideHealthbar; private readonly Container visibilityContainer; private readonly BindableBool replayLoaded = new BindableBool(); @@ -79,11 +77,11 @@ namespace osu.Game.Screens.Play ComboCounter = CreateComboCounter(), }, }, + HealthDisplay = CreateHealthDisplay(), Progress = CreateProgress(), ModDisplay = CreateModsContainer(), } }, - HealthDisplay = CreateHealthDisplay(), PlayerSettingsOverlay = CreatePlayerSettingsOverlay(), new FillFlowContainer { @@ -114,14 +112,8 @@ namespace osu.Game.Screens.Play ModDisplay.Current.Value = mods; - hideHealthbar = config.GetBindable(OsuSetting.HideHealthBar); showHud = config.GetBindable(OsuSetting.ShowInterface); - showHud.ValueChanged += visible => - { - visibilityContainer.FadeTo(visible.NewValue ? 1 : 0, duration); - if (!(hideHealthbar.Value && mods.OfType().Any())) - HealthDisplay.FadeTo(visible.NewValue ? 1 : 0, duration); - }; + showHud.ValueChanged += visible => visibilityContainer.FadeTo(visible.NewValue ? 1 : 0, duration); showHud.TriggerChange(); if (!showHud.Value && !hasShownNotificationOnce) From 3f39541cc2daa11b9e319a96ae5b2739db25af51 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 3 Jul 2019 05:11:11 +0300 Subject: [PATCH 1524/2854] Fade health bar on value change --- osu.Game/Rulesets/Mods/ModBlockFail.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs index 42ec82f8aa..f98521da69 100644 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -2,14 +2,17 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; namespace osu.Game.Rulesets.Mods { public abstract class ModBlockFail : Mod, IApplicableFailOverride, IApplicableToHUD, IReadFromConfig { - private Bindable hideHealthBar = new Bindable(); + private Bindable hideHealthBar; + private HealthDisplay healthDisplay; /// /// We never fail, 'yo. @@ -19,12 +22,13 @@ namespace osu.Game.Rulesets.Mods public void ReadFromConfig(OsuConfigManager config) { hideHealthBar = config.GetBindable(OsuSetting.HideHealthBar); + hideHealthBar.ValueChanged += v => healthDisplay?.FadeTo(v.NewValue ? 0 : 1, 250, Easing.OutQuint); } public void ApplyToHUD(HUDOverlay overlay) { - if (hideHealthBar.Value) - overlay.HealthDisplay.Hide(); + healthDisplay = overlay.HealthDisplay; + hideHealthBar?.TriggerChange(); } } } From 23acddcb562fcdfd6d66e2182177901d1db55595 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 12:02:35 +0900 Subject: [PATCH 1525/2854] Rename download buttons to avoid ambiguity --- .../Visual/Online/TestSceneBeatmapSetOverlay.cs | 2 +- .../Visual/Online/TestSceneDirectDownloadButton.cs | 4 ++-- .../{OsuDownloadButton.cs => DownloadButton.cs} | 4 ++-- .../{DownloadButton.cs => HeaderDownloadButton.cs} | 4 ++-- osu.Game/Overlays/BeatmapSet/Header.cs | 9 ++++----- osu.Game/Overlays/Direct/DirectGridPanel.cs | 2 +- osu.Game/Overlays/Direct/DirectListPanel.cs | 4 ++-- .../{DownloadButton.cs => PanelDownloadButton.cs} | 10 +++++----- osu.Game/Screens/Play/ReplayDownloadButton.cs | 4 ++-- 9 files changed, 21 insertions(+), 22 deletions(-) rename osu.Game/Graphics/UserInterface/{OsuDownloadButton.cs => DownloadButton.cs} (96%) rename osu.Game/Overlays/BeatmapSet/Buttons/{DownloadButton.cs => HeaderDownloadButton.cs} (97%) rename osu.Game/Overlays/Direct/{DownloadButton.cs => PanelDownloadButton.cs} (86%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index c494f5ef33..a9c44c9020 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Online typeof(BasicStats), typeof(BeatmapPicker), typeof(Details), - typeof(DownloadButton), + typeof(HeaderDownloadButton), typeof(FavouriteButton), typeof(Header), typeof(HeaderButton), diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs index 5a5833feb6..5b0c2d3c67 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.Online { public override IReadOnlyList RequiredTypes => new[] { - typeof(DownloadButton) + typeof(PanelDownloadButton) }; private TestDownloadButton downloadButton; @@ -143,7 +143,7 @@ namespace osu.Game.Tests.Visual.Online return beatmap; } - private class TestDownloadButton : DownloadButton + private class TestDownloadButton : PanelDownloadButton { public new bool DownloadEnabled => base.DownloadEnabled; diff --git a/osu.Game/Graphics/UserInterface/OsuDownloadButton.cs b/osu.Game/Graphics/UserInterface/DownloadButton.cs similarity index 96% rename from osu.Game/Graphics/UserInterface/OsuDownloadButton.cs rename to osu.Game/Graphics/UserInterface/DownloadButton.cs index 6e95c7e291..41b90d3802 100644 --- a/osu.Game/Graphics/UserInterface/OsuDownloadButton.cs +++ b/osu.Game/Graphics/UserInterface/DownloadButton.cs @@ -11,7 +11,7 @@ using osuTK; namespace osu.Game.Graphics.UserInterface { - public class OsuDownloadButton : OsuAnimatedButton + public class DownloadButton : OsuAnimatedButton { public readonly Bindable State = new Bindable(); @@ -21,7 +21,7 @@ namespace osu.Game.Graphics.UserInterface private OsuColour colours; - public OsuDownloadButton() + public DownloadButton() { Children = new Drawable[] { diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs similarity index 97% rename from osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs rename to osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index 3e8a5a8324..fe10287491 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -20,7 +20,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapSet.Buttons { - public class DownloadButton : BeatmapDownloadTrackingComposite, IHasTooltip + public class HeaderDownloadButton : BeatmapDownloadTrackingComposite, IHasTooltip { private readonly bool noVideo; @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons private ShakeContainer shakeContainer; private HeaderButton button; - public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) + public HeaderDownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) : base(beatmapSet) { this.noVideo = noVideo; diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 1c1167d08e..b50eac2c1a 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -18,7 +18,6 @@ using osu.Game.Overlays.BeatmapSet.Buttons; using osu.Game.Overlays.Direct; using osuTK; using osuTK.Graphics; -using DownloadButton = osu.Game.Overlays.BeatmapSet.Buttons.DownloadButton; namespace osu.Game.Overlays.BeatmapSet { @@ -268,7 +267,7 @@ namespace osu.Game.Overlays.BeatmapSet { case DownloadState.LocallyAvailable: // temporary for UX until new design is implemented. - downloadButtonsContainer.Child = new Direct.DownloadButton(BeatmapSet.Value) + downloadButtonsContainer.Child = new PanelDownloadButton(BeatmapSet.Value) { Width = 50, RelativeSizeAxes = Axes.Y @@ -278,13 +277,13 @@ namespace osu.Game.Overlays.BeatmapSet case DownloadState.Downloading: case DownloadState.Downloaded: // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design. - downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); + downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value); break; default: - downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value); + downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value); if (BeatmapSet.Value.OnlineInfo.HasVideo) - downloadButtonsContainer.Add(new DownloadButton(BeatmapSet.Value, true)); + downloadButtonsContainer.Add(new HeaderDownloadButton(BeatmapSet.Value, true)); break; } } diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index 5756a4593d..243e79eb9b 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -155,7 +155,7 @@ namespace osu.Game.Overlays.Direct }, }, }, - new DownloadButton(SetInfo) + new PanelDownloadButton(SetInfo) { Size = new Vector2(50, 30), Margin = new MarginPadding(horizontal_padding), diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index 6f3b5bc5f1..5757e1445b 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Direct private const float height = 70; private FillFlowContainer statusContainer; - protected DownloadButton DownloadButton; + protected PanelDownloadButton DownloadButton; private PlayButton playButton; private Box progressBar; @@ -150,7 +150,7 @@ namespace osu.Game.Overlays.Direct Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, AutoSizeAxes = Axes.Both, - Child = DownloadButton = new DownloadButton(SetInfo) + Child = DownloadButton = new PanelDownloadButton(SetInfo) { Size = new Vector2(height - vertical_padding * 3), Margin = new MarginPadding { Left = vertical_padding * 2, Right = vertical_padding }, diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/PanelDownloadButton.cs similarity index 86% rename from osu.Game/Overlays/Direct/DownloadButton.cs rename to osu.Game/Overlays/Direct/PanelDownloadButton.cs index dac1521bf3..017f92abaa 100644 --- a/osu.Game/Overlays/Direct/DownloadButton.cs +++ b/osu.Game/Overlays/Direct/PanelDownloadButton.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -10,16 +10,16 @@ using osu.Game.Online; namespace osu.Game.Overlays.Direct { - public class DownloadButton : BeatmapDownloadTrackingComposite + public class PanelDownloadButton : BeatmapDownloadTrackingComposite { protected bool DownloadEnabled => button.Enabled.Value; private readonly bool noVideo; private readonly ShakeContainer shakeContainer; - private readonly OsuDownloadButton button; + private readonly DownloadButton button; - public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) + public PanelDownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) : base(beatmapSet) { this.noVideo = noVideo; @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Direct InternalChild = shakeContainer = new ShakeContainer { RelativeSizeAxes = Axes.Both, - Child = button = new OsuDownloadButton + Child = button = new DownloadButton { RelativeSizeAxes = Axes.Both, }, diff --git a/osu.Game/Screens/Play/ReplayDownloadButton.cs b/osu.Game/Screens/Play/ReplayDownloadButton.cs index 5acf4e83d9..748fe8cc90 100644 --- a/osu.Game/Screens/Play/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Play/ReplayDownloadButton.cs @@ -13,7 +13,7 @@ namespace osu.Game.Screens.Play { public class ReplayDownloadButton : DownloadTrackingComposite { - private OsuDownloadButton button; + private DownloadButton button; private ShakeContainer shakeContainer; private ReplayAvailability replayAvailability @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Play InternalChild = shakeContainer = new ShakeContainer { RelativeSizeAxes = Axes.Both, - Child = button = new OsuDownloadButton + Child = button = new DownloadButton { RelativeSizeAxes = Axes.Both, } From d22a1229cbba13668b9f2abd5b33a220041d9658 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 12:06:20 +0900 Subject: [PATCH 1526/2854] Remove unnecessary disposal --- osu.Game/Overlays/Direct/PanelDownloadButton.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game/Overlays/Direct/PanelDownloadButton.cs b/osu.Game/Overlays/Direct/PanelDownloadButton.cs index 017f92abaa..4037cd46f3 100644 --- a/osu.Game/Overlays/Direct/PanelDownloadButton.cs +++ b/osu.Game/Overlays/Direct/PanelDownloadButton.cs @@ -71,12 +71,5 @@ namespace osu.Game.Overlays.Direct } }; } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - button?.State.UnbindAll(); - } } } From 98daaf634ae4851fec97326efe1e9a0eea65137f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 3 Jul 2019 06:44:17 +0300 Subject: [PATCH 1527/2854] Simplify changes --- osu.Game/Rulesets/Mods/ModBlockFail.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs index f98521da69..e37b1b0698 100644 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -22,13 +22,12 @@ namespace osu.Game.Rulesets.Mods public void ReadFromConfig(OsuConfigManager config) { hideHealthBar = config.GetBindable(OsuSetting.HideHealthBar); - hideHealthBar.ValueChanged += v => healthDisplay?.FadeTo(v.NewValue ? 0 : 1, 250, Easing.OutQuint); } public void ApplyToHUD(HUDOverlay overlay) { healthDisplay = overlay.HealthDisplay; - hideHealthBar?.TriggerChange(); + hideHealthBar.BindValueChanged(v => healthDisplay.FadeTo(v.NewValue ? 0 : 1, 250, Easing.OutQuint), true); } } } From 1189092e2033daa69f9fc224604de88fdbce546a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 12:49:16 +0900 Subject: [PATCH 1528/2854] Remove redundant scale specification --- osu.Game/Online/Leaderboards/DrawableRank.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Online/Leaderboards/DrawableRank.cs b/osu.Game/Online/Leaderboards/DrawableRank.cs index 4d81ead494..50cb58c6ab 100644 --- a/osu.Game/Online/Leaderboards/DrawableRank.cs +++ b/osu.Game/Online/Leaderboards/DrawableRank.cs @@ -49,7 +49,6 @@ namespace osu.Game.Online.Leaderboards RelativeSizeAxes = Axes.Both, ColourDark = rankColour.Darken(0.1f), ColourLight = rankColour.Lighten(0.1f), - TriangleScale = 1, Velocity = 0.25f, }, new OsuSpriteText From 9eef8243c0906efb59013c66da45a7044a18fc1b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 14:22:14 +0900 Subject: [PATCH 1529/2854] Update bounty/contribution information --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 55f2eebec9..6ef56149ab 100644 --- a/README.md +++ b/README.md @@ -92,11 +92,13 @@ Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is cu We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention on having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time, to ensure no effort is wasted. -Please make sure you are familiar with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. New component development, and where possible, bug fixing and debugging existing components **should always be done under VisualTests**. +If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues) (especially those with the ["good first issue"](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) label). -Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://www.bountysource.com/teams/ppy). If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues). +Before starting, please make sure you are familiar with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. New component development, and where possible, bug fixing and debugging existing components **should always be done under VisualTests**. -Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. I welcome all feedback so we can make contributing to this project as pain-free as possible. +Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. We welcome all feedback so we can make contributing to this project as pain-free as possible. + +For those interested, we love to reward quality contributions via [awarding bounties](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform). Don't hesitate to request a bounty for your work on this project. ## Licence From 6c46643c26c6d5ad5539e2bbb812d4b30377cc2e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 14:26:19 +0900 Subject: [PATCH 1530/2854] Update icon --- README.md | 6 ++++-- assets/lazer.png | Bin 0 -> 39498 bytes 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 assets/lazer.png diff --git a/README.md b/README.md index 6ef56149ab..18663353b4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +

+ +

+ # osu! [![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu) [![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu) [![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](https://discord.gg/ppy) @@ -22,8 +26,6 @@ Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh ### Releases -![](https://puu.sh/DCmvA/f6a74f5fbb.png) - If you are not interested in developing the game, you can still consume our [binary releases](https://github.com/ppy/osu/releases). **Latest build:** diff --git a/assets/lazer.png b/assets/lazer.png new file mode 100644 index 0000000000000000000000000000000000000000..075a8e7184ad508cedd8fe9ae549d8cea696b51a GIT binary patch literal 39498 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 X{r?ZpP_kcw2Ot$CjR&O)ra}J$c_$^Y literal 0 HcmV?d00001 From 299e9c9c33c7d1c1423813c616eac6085c67b714 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 14:34:51 +0900 Subject: [PATCH 1531/2854] Update bounty line --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 18663353b4..2c330e403c 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ Before starting, please make sure you are familiar with the [development and tes Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. We welcome all feedback so we can make contributing to this project as pain-free as possible. -For those interested, we love to reward quality contributions via [awarding bounties](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform). Don't hesitate to request a bounty for your work on this project. +For those interested, we love to reward quality contributions via bounties, paid out via paypal or osu! supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project. ## Licence From 6e308945b184e3ae8ae4bcc24a724be60de82491 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 15:22:17 +0900 Subject: [PATCH 1532/2854] Fix logo visualisation trying to catch up after being off-screen --- osu.Game/Screens/Menu/LogoVisualisation.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 2ba82b5d9b..39bda799b5 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -131,7 +131,8 @@ namespace osu.Game.Screens.Menu { base.LoadComplete(); - Scheduler.AddDelayed(updateAmplitudes, time_between_updates, true); + var delayed = Scheduler.AddDelayed(updateAmplitudes, time_between_updates, true); + delayed.PerformRepeatCatchUpExecutions = false; } protected override void Update() From 6391d21af1b5c671fc99d6cfc9d483971840b6f6 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 3 Jul 2019 15:54:21 +0900 Subject: [PATCH 1533/2854] Remove test used for visualization --- .../UserInterface/TestSceneButtonSystem.cs | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs index 6c4d9b20f7..c8cc864089 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Shapes; using osu.Game.Screens.Menu; -using osuTK; using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface @@ -43,25 +42,7 @@ namespace osu.Game.Tests.Visual.UserInterface buttons.SetOsuLogo(logo); foreach (var s in Enum.GetValues(typeof(ButtonSystemState)).OfType().Skip(1)) - AddStep($"State to {s}", () => - { - buttons.State = s; - - if (buttons.State == ButtonSystemState.EnteringMode) - { - buttons.FadeOut(400, Easing.InSine); - buttons.MoveTo(new Vector2(-800, 0), 400, Easing.InSine); - logo.FadeOut(300, Easing.InSine) - .ScaleTo(0.2f, 300, Easing.InSine); - } - else - { - buttons.FadeIn(400, Easing.OutQuint); - buttons.MoveTo(new Vector2(0), 400, Easing.OutQuint); - logo.FadeColour(Color4.White, 100, Easing.OutQuint); - logo.FadeIn(100, Easing.OutQuint); - } - }); + AddStep($"State to {s}", () => buttons.State = s); } } } From d9e646d9ef75290af7cf6a79ef59c006e121928e Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 3 Jul 2019 09:51:09 +0200 Subject: [PATCH 1534/2854] Use addRuleset inside loadRulesetFromFile --- osu.Game/Rulesets/RulesetStore.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 17834f8545..15adc258a1 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -143,8 +143,7 @@ namespace osu.Game.Rulesets try { - var assembly = Assembly.LoadFrom(file); - loaded_assemblies[assembly] = assembly.GetTypes().First(t => t.IsPublic && t.IsSubclassOf(typeof(Ruleset))); + addRuleset(Assembly.LoadFrom(file)); } catch (Exception e) { From ab244d1b7f7cb1ab4abf7c65aa043571cc8165b9 Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 3 Jul 2019 10:21:18 +0200 Subject: [PATCH 1535/2854] Only log that the rulesets could not be loaded from a directory. --- osu.Game/Rulesets/RulesetStore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 15adc258a1..31c8d03df4 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -31,9 +31,9 @@ namespace osu.Game.Rulesets foreach (string file in files.Where(f => !Path.GetFileName(f).Contains("Tests"))) loadRulesetFromFile(file); } - catch (Exception e) + catch { - Logger.Error(e, $"Could not load rulesets from directory {Environment.CurrentDirectory}"); + Logger.Log($"Could not load rulesets from directory {Environment.CurrentDirectory}"); } } From 43d7f66c5bb095bd63ba02de3d34eaefe4c34d79 Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 3 Jul 2019 10:54:10 +0200 Subject: [PATCH 1536/2854] Add comment about the android ruleset situation. --- osu.Game/Rulesets/RulesetStore.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 31c8d03df4..d0f6971e1c 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets { AppDomain.CurrentDomain.AssemblyResolve += currentDomain_AssemblyResolve; + // On android in release configuration the assemblies are loaded from the apk directly into memory. + // In this case there is no way to access the ruleset assemblies from the filesystem. addLoadedRulesets(); try From 9805adc61d527063082a0740f5ebbeda9c64bc8d Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 3 Jul 2019 14:25:59 +0530 Subject: [PATCH 1537/2854] Fix online ScoreInfo having the wrong ruleset --- osu.Game/Online/API/Requests/GetScoresRequest.cs | 3 +++ .../API/Requests/Responses/APILegacyScoreInfo.cs | 11 ----------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 0b6f65a0e0..e56df05570 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -34,7 +34,10 @@ namespace osu.Game.Online.API.Requests private void onSuccess(APILegacyScores r) { foreach (APILegacyScoreInfo score in r.Scores) + { score.Beatmap = beatmap; + score.Ruleset = ruleset; + } } protected override WebRequest CreateWebRequest() diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs index 5a18cf63f9..17da255873 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs @@ -116,17 +116,6 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"mods")] private string[] modStrings { get; set; } - public override BeatmapInfo Beatmap - { - get => base.Beatmap; - set - { - base.Beatmap = value; - if (Beatmap.Ruleset != null) - Ruleset = value.Ruleset; - } - } - public override RulesetInfo Ruleset { get => base.Ruleset; From 5ecb764cdcf65ddd0f9a0bae68f30cda46072542 Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 3 Jul 2019 10:57:09 +0200 Subject: [PATCH 1538/2854] Remove whitespaces and lowercase the comments --- osu.Game/Rulesets/RulesetStore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index d0f6971e1c..a7d62665db 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -22,8 +22,8 @@ namespace osu.Game.Rulesets { AppDomain.CurrentDomain.AssemblyResolve += currentDomain_AssemblyResolve; - // On android in release configuration the assemblies are loaded from the apk directly into memory. - // In this case there is no way to access the ruleset assemblies from the filesystem. + //on android in release configuration the assemblies are loaded from the apk directly into memory. + //in this case there is no way to access the rulesets assemblies from the filesystem. addLoadedRulesets(); try From ccae4ce95ef77a5475ea122653bbc5f393a886e2 Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 3 Jul 2019 14:39:11 +0530 Subject: [PATCH 1539/2854] Fix local beatmap leaderboard displaying scores from all game modes --- osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index aafa6bb0eb..62f93afbbb 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -55,7 +55,9 @@ namespace osu.Game.Screens.Select.Leaderboards { if (Scope == BeatmapLeaderboardScope.Local) { - Scores = scoreManager.QueryScores(s => !s.DeletePending && s.Beatmap.ID == Beatmap.ID).OrderByDescending(s => s.TotalScore).ToArray(); + Scores = scoreManager + .QueryScores(s => !s.DeletePending && s.Beatmap.ID == Beatmap.ID && s.Ruleset.ID == ruleset.Value.ID) + .OrderByDescending(s => s.TotalScore).ToArray(); PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; return null; } From 62dd89197c7fce70d38744a01beea5ad74056872 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 18:36:04 +0900 Subject: [PATCH 1540/2854] Refactor comment slightly --- osu.Game/Rulesets/RulesetStore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index a7d62665db..4476f16e32 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -22,8 +22,8 @@ namespace osu.Game.Rulesets { AppDomain.CurrentDomain.AssemblyResolve += currentDomain_AssemblyResolve; - //on android in release configuration the assemblies are loaded from the apk directly into memory. - //in this case there is no way to access the rulesets assemblies from the filesystem. + // On android in release configuration assemblies are loaded from the apk directly into memory. + // We cannot read assemblies from cwd, so should check loaded assemblies isntead. addLoadedRulesets(); try From 06ef8f71e96e2af7242c558c2cd279afe3c72a6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 18:41:01 +0900 Subject: [PATCH 1541/2854] Fix typo --- osu.Game/Rulesets/RulesetStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 4476f16e32..69ce5f97b6 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets AppDomain.CurrentDomain.AssemblyResolve += currentDomain_AssemblyResolve; // On android in release configuration assemblies are loaded from the apk directly into memory. - // We cannot read assemblies from cwd, so should check loaded assemblies isntead. + // We cannot read assemblies from cwd, so should check loaded assemblies instead. addLoadedRulesets(); try From 8120bb36bc598acadb0d30952bd370fdeb639fae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 18:42:10 +0900 Subject: [PATCH 1542/2854] More methods --- osu.Game/Rulesets/RulesetStore.cs | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 69ce5f97b6..fd42f96c92 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -24,19 +24,9 @@ namespace osu.Game.Rulesets // On android in release configuration assemblies are loaded from the apk directly into memory. // We cannot read assemblies from cwd, so should check loaded assemblies instead. - addLoadedRulesets(); + loadFromAppDomain(); - try - { - string[] files = Directory.GetFiles(Environment.CurrentDirectory, $"{ruleset_library_prefix}.*.dll"); - - foreach (string file in files.Where(f => !Path.GetFileName(f).Contains("Tests"))) - loadRulesetFromFile(file); - } - catch - { - Logger.Log($"Could not load rulesets from directory {Environment.CurrentDirectory}"); - } + loadFromDisk(); } public RulesetStore(IDatabaseContextFactory factory) @@ -123,7 +113,7 @@ namespace osu.Game.Rulesets } } - private static void addLoadedRulesets() + private static void loadFromAppDomain() { foreach (var ruleset in AppDomain.CurrentDomain.GetAssemblies()) { @@ -136,6 +126,21 @@ namespace osu.Game.Rulesets } } + private static void loadFromDisk() + { + try + { + string[] files = Directory.GetFiles(Environment.CurrentDirectory, $"{ruleset_library_prefix}.*.dll"); + + foreach (string file in files.Where(f => !Path.GetFileName(f).Contains("Tests"))) + loadRulesetFromFile(file); + } + catch + { + Logger.Log($"Could not load rulesets from directory {Environment.CurrentDirectory}"); + } + } + private static void loadRulesetFromFile(string file) { var filename = Path.GetFileNameWithoutExtension(file); From cc9a28afa81042a16284b7ce11d9164d548261a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 19:42:16 +0900 Subject: [PATCH 1543/2854] Add shared base class for both mod imlpementations --- osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs | 62 +------------- osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs | 70 +--------------- .../Mods/OsuModeObjectScaleTween.cs | 84 +++++++++++++++++++ 3 files changed, 89 insertions(+), 127 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs index cbefc42c3b..adca95cf8a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -1,17 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; -using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModDeflate : Mod, IApplicableToDrawableHitObjects + public class OsuModDeflate : OsuModeObjectScaleTween { public override string Name => "Deflate"; @@ -19,58 +13,8 @@ namespace osu.Game.Rulesets.Osu.Mods public override IconUsage Icon => FontAwesome.Solid.CompressArrowsAlt; - public override ModType Type => ModType.Fun; + public override string Description => "Hit them at the right size!"; - public override string Description => "Become one with the approach circle..."; - - public override double ScoreMultiplier => 1; - - public void ApplyToDrawableHitObjects(IEnumerable drawables) - { - foreach (var drawable in drawables) - { - switch (drawable) - { - case DrawableSpinner _: - continue; - - default: - drawable.ApplyCustomUpdateState += ApplyCustomState; - break; - } - } - } - - protected virtual void ApplyCustomState(DrawableHitObject drawable, ArmedState state) - { - var h = (OsuHitObject)drawable.HitObject; - - // apply grow effect - switch (drawable) - { - case DrawableSliderHead _: - case DrawableSliderTail _: - // special cases we should *not* be scaling. - break; - - case DrawableSlider _: - case DrawableHitCircle _: - { - using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) - drawable.ScaleTo(2f).Then().ScaleTo(1f, h.TimePreempt); // sole difference to grow mod - break; - } - } - - // remove approach circles - switch (drawable) - { - case DrawableHitCircle circle: - // we don't want to see the approach circle - using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) - circle.ApproachCircle.Hide(); - break; - } - } + protected override float StartScale => 2f; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs index 8072dc09c1..3c81203ad7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs @@ -1,20 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Game.Configuration; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Mods { - internal class OsuModGrow : Mod, IReadFromConfig, IApplicableToDrawableHitObjects + internal class OsuModGrow : OsuModeObjectScaleTween { public override string Name => "Grow"; @@ -22,65 +13,8 @@ namespace osu.Game.Rulesets.Osu.Mods public override IconUsage Icon => FontAwesome.Solid.ArrowsAltV; - public override ModType Type => ModType.Fun; - public override string Description => "Hit them at the right size!"; - public override double ScoreMultiplier => 1; - - private Bindable increaseFirstObjectVisibility = new Bindable(); - - public void ReadFromConfig(OsuConfigManager config) - { - increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); - } - - public void ApplyToDrawableHitObjects(IEnumerable drawables) - { - foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0)) - { - switch (drawable) - { - case DrawableSpinner _: - continue; - - default: - drawable.ApplyCustomUpdateState += ApplyCustomState; - break; - } - } - } - - protected virtual void ApplyCustomState(DrawableHitObject drawable, ArmedState state) - { - var h = (OsuHitObject)drawable.HitObject; - - // apply grow effect - switch (drawable) - { - case DrawableSliderHead _: - case DrawableSliderTail _: - // special cases we should *not* be scaling. - break; - - case DrawableSlider _: - case DrawableHitCircle _: - { - using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) - drawable.ScaleTo(0.5f).Then().ScaleTo(1, h.TimePreempt, Easing.OutSine); - break; - } - } - - // remove approach circles - switch (drawable) - { - case DrawableHitCircle circle: - // we don't want to see the approach circle - using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) - circle.ApproachCircle.Hide(); - break; - } - } + protected override float StartScale => 0.5f; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs b/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs new file mode 100644 index 0000000000..ad6a15718a --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs @@ -0,0 +1,84 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Configuration; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; + +namespace osu.Game.Rulesets.Osu.Mods +{ + /// + /// Adjusts the size of hit objects during their fade in animation. + /// + public abstract class OsuModeObjectScaleTween : Mod, IReadFromConfig, IApplicableToDrawableHitObjects + { + public override ModType Type => ModType.Fun; + + public override double ScoreMultiplier => 1; + + protected virtual float StartScale => 1; + + protected virtual float EndScale => 1; + + private Bindable increaseFirstObjectVisibility = new Bindable(); + + public void ReadFromConfig(OsuConfigManager config) + { + increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); + } + + public void ApplyToDrawableHitObjects(IEnumerable drawables) + { + foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0)) + { + switch (drawable) + { + case DrawableSpinner _: + continue; + + default: + drawable.ApplyCustomUpdateState += ApplyCustomState; + break; + } + } + } + + protected virtual void ApplyCustomState(DrawableHitObject drawable, ArmedState state) + { + var h = (OsuHitObject)drawable.HitObject; + + // apply grow effect + switch (drawable) + { + case DrawableSliderHead _: + case DrawableSliderTail _: + // special cases we should *not* be scaling. + break; + + case DrawableSlider _: + case DrawableHitCircle _: + { + using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + drawable.ScaleTo(StartScale).Then().ScaleTo(EndScale, h.TimePreempt, Easing.OutSine); + break; + } + } + + // remove approach circles + switch (drawable) + { + case DrawableHitCircle circle: + // we don't want to see the approach circle + using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + circle.ApproachCircle.Hide(); + break; + } + } + } +} From 0740fff0c9d70d3c6a92dc4f6f4249cb278aaf63 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 19:53:25 +0900 Subject: [PATCH 1544/2854] Update android package dependencies --- osu.Android.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index ead059cdf2..8ef635fe75 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,7 +63,7 @@ - - + + From 4ba60ed089636efeb6e276c2a80d7f7aee485d98 Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 3 Jul 2019 17:04:20 +0530 Subject: [PATCH 1545/2854] Apply currently selected mods to filter leaderboard scores Modifies GetScoresRequest to build query string locally instead of using WebRequest.AddParameter since it doesn't support array parameters --- .../Online/API/Requests/GetScoresRequest.cs | 26 ++++++++------ osu.Game/Screens/Select/BeatmapDetailArea.cs | 2 ++ .../Select/Leaderboards/BeatmapLeaderboard.cs | 35 ++++++++++++++++++- 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 0b6f65a0e0..2de0fcef9c 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -5,8 +5,10 @@ using System; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Screens.Select.Leaderboards; -using osu.Framework.IO.Network; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets.Mods; +using System.Text; +using System.Collections.Generic; namespace osu.Game.Online.API.Requests { @@ -15,8 +17,9 @@ namespace osu.Game.Online.API.Requests private readonly BeatmapInfo beatmap; private readonly BeatmapLeaderboardScope scope; private readonly RulesetInfo ruleset; + private readonly IEnumerable mods; - public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global) + public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global, IEnumerable mods = null) { if (!beatmap.OnlineBeatmapID.HasValue) throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); @@ -27,6 +30,7 @@ namespace osu.Game.Online.API.Requests this.beatmap = beatmap; this.scope = scope; this.ruleset = ruleset ?? throw new ArgumentNullException(nameof(ruleset)); + this.mods = mods ?? Array.Empty(); Success += onSuccess; } @@ -37,17 +41,19 @@ namespace osu.Game.Online.API.Requests score.Beatmap = beatmap; } - protected override WebRequest CreateWebRequest() + protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores{createQueryParameters()}"; + + private string createQueryParameters() { - var req = base.CreateWebRequest(); + StringBuilder query = new StringBuilder(@"?"); - req.Timeout = 30000; - req.AddParameter(@"type", scope.ToString().ToLowerInvariant()); - req.AddParameter(@"mode", ruleset.ShortName); + query.Append($@"type={scope.ToString().ToLowerInvariant()}&"); + query.Append($@"mode={ruleset.ShortName}&"); - return req; + foreach (var mod in mods) + query.Append($@"mods[]={mod.Acronym}&"); + + return query.ToString().TrimEnd('&'); } - - protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores"; } } diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs index 477037355c..b66a2ffe0f 100644 --- a/osu.Game/Screens/Select/BeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs @@ -41,6 +41,8 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.X, OnFilter = (tab, mods) => { + Leaderboard.FilterMods = mods; + switch (tab) { case BeatmapDetailTab.Details: diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index aafa6bb0eb..890cc7e9a3 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -11,6 +11,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Leaderboards; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Scoring; namespace osu.Game.Screens.Select.Leaderboards @@ -36,12 +37,31 @@ namespace osu.Game.Screens.Select.Leaderboards } } + private bool filterMods; + + public bool FilterMods + { + get => filterMods; + set + { + if (value == filterMods) + return; + + filterMods = value; + + UpdateScores(); + } + } + [Resolved] private ScoreManager scoreManager { get; set; } [Resolved] private IBindable ruleset { get; set; } + [Resolved] + private IBindable> mods { get; set; } + [Resolved] private IAPIProvider api { get; set; } @@ -49,6 +69,11 @@ namespace osu.Game.Screens.Select.Leaderboards private void load() { ruleset.ValueChanged += _ => UpdateScores(); + mods.ValueChanged += _ => + { + if (filterMods) + UpdateScores(); + }; } protected override APIRequest FetchScores(Action> scoresCallback) @@ -72,7 +97,15 @@ namespace osu.Game.Screens.Select.Leaderboards return null; } - var req = new GetScoresRequest(Beatmap, ruleset.Value ?? Beatmap.Ruleset, Scope); + IReadOnlyList requestMods = null; + + if (filterMods && mods.Value.Count == 0) + // add nomod for the request + requestMods = new Mod[] { new ModNoMod() }; + else if (filterMods) + requestMods = mods.Value; + + var req = new GetScoresRequest(Beatmap, ruleset.Value ?? Beatmap.Ruleset, Scope, requestMods); req.Success += r => scoresCallback?.Invoke(r.Scores); From cfac90b228a382854242d6ce99fb33ccb8b0d255 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 3 Jul 2019 14:34:24 +0300 Subject: [PATCH 1546/2854] Use ConstrainedIconContainer instead of SpriteIcon --- osu.Game/Overlays/Direct/DirectRulesetSelector.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs index f2abca9ce6..fdab9f1b90 100644 --- a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs +++ b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs @@ -3,9 +3,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osuTK; @@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Direct private class DirectRulesetTabItem : TabItem { - private readonly SpriteIcon icon; + private readonly ConstrainedIconContainer iconContainer; public DirectRulesetTabItem(RulesetInfo value) : base(value) @@ -49,11 +49,13 @@ namespace osu.Game.Overlays.Direct Children = new Drawable[] { - icon = (SpriteIcon)value.CreateInstance().CreateIcon(), + iconContainer = new ConstrainedIconContainer + { + Icon = value.CreateInstance().CreateIcon(), + Size = new Vector2(32), + }, new HoverClickSounds() }; - - icon.Size = new Vector2(30); } protected override void LoadComplete() @@ -80,7 +82,7 @@ namespace osu.Game.Overlays.Direct protected override void OnDeactivated() => updateState(); - private void updateState() => icon.FadeColour(IsHovered || Active.Value ? Color4.White : Color4.Gray, 120, Easing.InQuad); + private void updateState() => iconContainer.FadeColour(IsHovered || Active.Value ? Color4.White : Color4.Gray, 120, Easing.InQuad); } } } From 66eb979fff5f92698d750bb7d807c6b6a07b0938 Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 3 Jul 2019 15:51:20 +0200 Subject: [PATCH 1547/2854] Massivly deduplicate properties. Removed bass.dll. is it unused (and not included in the apk) --- osu.Android.props | 44 +++++++++--------- osu.Android/bass.dll | Bin 210944 -> 0 bytes osu.Android/osu.Android.csproj | 8 ---- ...u.Game.Rulesets.Catch.Tests.Android.csproj | 8 ---- ...u.Game.Rulesets.Mania.Tests.Android.csproj | 8 ---- ...osu.Game.Rulesets.Osu.Tests.Android.csproj | 8 ---- ...u.Game.Rulesets.Taiko.Tests.Android.csproj | 8 ---- .../osu.Game.Tests.Android.csproj | 8 ---- 8 files changed, 21 insertions(+), 71 deletions(-) delete mode 100644 osu.Android/bass.dll diff --git a/osu.Android.props b/osu.Android.props index 8ef635fe75..217b7ebdab 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -13,37 +13,36 @@ Xamarin.Android.Net.AndroidClientHandler v8.1 false + true + armeabi-v7a;x86;arm64-v8a + true + cjk,mideast,other,rare,west + SdkOnly + False + prompt + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {122416d6-6b49-4ee2-a1e8-b825f31c79fe} + Debug + AnyCPU + 8.0.30703 + 2.0 - + True portable False DEBUG;TRACE - prompt - false - false - SdkOnly - true - false - cjk,mideast,other,rare,west - true - armeabi-v7a;x86;arm64-v8a - true + False + True + False - - false + + False None True - prompt - true - false - SdkOnly + true False - true - cjk,mideast,other,rare,west - true - armeabi-v7a;x86;arm64-v8a - true + True @@ -52,7 +51,6 @@ Always - diff --git a/osu.Android/bass.dll b/osu.Android/bass.dll deleted file mode 100644 index 3cd403c3acbd9e89ad071c5069392ea208baaa38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 210944 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 S(A~_h>u%w{`TPGJ1^#c6n$Mj8 diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 42a3185cd1..3654e0301e 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -2,17 +2,9 @@ - Debug - AnyCPU - 8.0.30703 - 2.0 - {D1D5F9A8-B40B-40E6-B02F-482D03346D3D} - {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - {122416d6-6b49-4ee2-a1e8-b825f31c79fe} osu.Android osu.Android Properties\AndroidManifest.xml - armeabi-v7a;x86;arm64-v8a diff --git a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj index 88b420ffad..c886ddf92e 100644 --- a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj +++ b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj @@ -2,17 +2,9 @@ - Debug - AnyCPU - 8.0.30703 - 2.0 - {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D} - {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - {122416d6-6b49-4ee2-a1e8-b825f31c79fe} osu.Game.Rulesets.Catch.Tests osu.Game.Rulesets.Catch.Tests.Android Properties\AndroidManifest.xml - armeabi-v7a;x86;arm64-v8a diff --git a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj index 0e557cb260..27a5ebefcd 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj +++ b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj @@ -2,17 +2,9 @@ - Debug - AnyCPU - 8.0.30703 - 2.0 - {531F1092-DB27-445D-AA33-2A77C7187C99} - {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - {122416d6-6b49-4ee2-a1e8-b825f31c79fe} osu.Game.Rulesets.Mania.Tests osu.Game.Rulesets.Mania.Tests.Android Properties\AndroidManifest.xml - armeabi-v7a;x86;arm64-v8a diff --git a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj index dcf1573522..7d4389557b 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj +++ b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj @@ -2,17 +2,9 @@ - Debug - AnyCPU - 8.0.30703 - 2.0 - {90CAB706-39CB-4B93-9629-3218A6FF8E9B} - {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - {122416d6-6b49-4ee2-a1e8-b825f31c79fe} osu.Game.Rulesets.Osu.Tests osu.Game.Rulesets.Osu.Tests.Android Properties\AndroidManifest.xml - armeabi-v7a;x86;arm64-v8a diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj index 392442b713..6ed16c0b6a 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj +++ b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj @@ -2,17 +2,9 @@ - Debug - AnyCPU - 8.0.30703 - 2.0 - {3701A0A1-8476-42C6-B5C4-D24129B4A484} - {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - {122416d6-6b49-4ee2-a1e8-b825f31c79fe} osu.Game.Rulesets.Taiko.Tests osu.Game.Rulesets.Taiko.Tests.Android Properties\AndroidManifest.xml - armeabi-v7a;x86;arm64-v8a diff --git a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj index c2dd194e09..394157f832 100644 --- a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj +++ b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj @@ -2,17 +2,9 @@ - Debug - AnyCPU - 8.0.30703 - 2.0 - {5CC222DC-5716-4499-B897-DCBDDA4A5CF9} - {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - {122416d6-6b49-4ee2-a1e8-b825f31c79fe} osu.Game.Tests osu.Game.Tests.Android Properties\AndroidManifest.xml - armeabi-v7a;x86;arm64-v8a From b901aab19da0572328ddc6851cd18a78e91f1f0a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 23:06:16 +0900 Subject: [PATCH 1548/2854] Fix update notification not correctly restarting the game --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index 69064a40cb..78a1e680ec 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -129,7 +129,7 @@ namespace osu.Desktop.Updater Activated = () => { updateManager.PrepareUpdateAsync() - .ContinueWith(_ => Schedule(() => game.GracefullyExit())); + .ContinueWith(_ => updateManager.Schedule(() => game.GracefullyExit())); return true; } }; From 0acaf53c9b1cb73079d36231a3792d395335698e Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 3 Jul 2019 17:25:42 +0200 Subject: [PATCH 1549/2854] Revert everything except removall of the bass.dll --- osu.Android.props | 43 ++++++++++--------- osu.Android/osu.Android.csproj | 8 ++++ ...u.Game.Rulesets.Catch.Tests.Android.csproj | 8 ++++ ...u.Game.Rulesets.Mania.Tests.Android.csproj | 8 ++++ ...osu.Game.Rulesets.Osu.Tests.Android.csproj | 8 ++++ ...u.Game.Rulesets.Taiko.Tests.Android.csproj | 8 ++++ .../osu.Game.Tests.Android.csproj | 8 ++++ 7 files changed, 70 insertions(+), 21 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 217b7ebdab..d47615d4b3 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -13,36 +13,37 @@ Xamarin.Android.Net.AndroidClientHandler v8.1 false - true - armeabi-v7a;x86;arm64-v8a - true - cjk,mideast,other,rare,west - SdkOnly - False - prompt - {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - {122416d6-6b49-4ee2-a1e8-b825f31c79fe} - Debug - AnyCPU - 8.0.30703 - 2.0 - + True portable False DEBUG;TRACE - False - True - False + prompt + false + false + SdkOnly + true + false + cjk,mideast,other,rare,west + true + armeabi-v7a;x86;arm64-v8a + true - - False + + false None True - true + prompt + true + false + SdkOnly False - True + true + cjk,mideast,other,rare,west + true + armeabi-v7a;x86;arm64-v8a + true diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 3654e0301e..42a3185cd1 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -2,9 +2,17 @@ + Debug + AnyCPU + 8.0.30703 + 2.0 + {D1D5F9A8-B40B-40E6-B02F-482D03346D3D} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {122416d6-6b49-4ee2-a1e8-b825f31c79fe} osu.Android osu.Android Properties\AndroidManifest.xml + armeabi-v7a;x86;arm64-v8a diff --git a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj index c886ddf92e..88b420ffad 100644 --- a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj +++ b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj @@ -2,9 +2,17 @@ + Debug + AnyCPU + 8.0.30703 + 2.0 + {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {122416d6-6b49-4ee2-a1e8-b825f31c79fe} osu.Game.Rulesets.Catch.Tests osu.Game.Rulesets.Catch.Tests.Android Properties\AndroidManifest.xml + armeabi-v7a;x86;arm64-v8a diff --git a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj index 27a5ebefcd..0e557cb260 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj +++ b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj @@ -2,9 +2,17 @@ + Debug + AnyCPU + 8.0.30703 + 2.0 + {531F1092-DB27-445D-AA33-2A77C7187C99} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {122416d6-6b49-4ee2-a1e8-b825f31c79fe} osu.Game.Rulesets.Mania.Tests osu.Game.Rulesets.Mania.Tests.Android Properties\AndroidManifest.xml + armeabi-v7a;x86;arm64-v8a diff --git a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj index 7d4389557b..dcf1573522 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj +++ b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj @@ -2,9 +2,17 @@ + Debug + AnyCPU + 8.0.30703 + 2.0 + {90CAB706-39CB-4B93-9629-3218A6FF8E9B} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {122416d6-6b49-4ee2-a1e8-b825f31c79fe} osu.Game.Rulesets.Osu.Tests osu.Game.Rulesets.Osu.Tests.Android Properties\AndroidManifest.xml + armeabi-v7a;x86;arm64-v8a diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj index 6ed16c0b6a..392442b713 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj +++ b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj @@ -2,9 +2,17 @@ + Debug + AnyCPU + 8.0.30703 + 2.0 + {3701A0A1-8476-42C6-B5C4-D24129B4A484} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {122416d6-6b49-4ee2-a1e8-b825f31c79fe} osu.Game.Rulesets.Taiko.Tests osu.Game.Rulesets.Taiko.Tests.Android Properties\AndroidManifest.xml + armeabi-v7a;x86;arm64-v8a diff --git a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj index 394157f832..c2dd194e09 100644 --- a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj +++ b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj @@ -2,9 +2,17 @@ + Debug + AnyCPU + 8.0.30703 + 2.0 + {5CC222DC-5716-4499-B897-DCBDDA4A5CF9} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {122416d6-6b49-4ee2-a1e8-b825f31c79fe} osu.Game.Tests osu.Game.Tests.Android Properties\AndroidManifest.xml + armeabi-v7a;x86;arm64-v8a From 1824d2999650969f54ff50c45e4dcc150b41a5a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jul 2019 00:34:39 +0900 Subject: [PATCH 1550/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8ef635fe75..400b43dbe6 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -64,6 +64,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b59828a52e..e872cd1387 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index ddbdaf3d18..a319094cb1 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 664257fbbe31f7b1617fc55527e59a088dc5c259 Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Wed, 3 Jul 2019 18:42:02 +0200 Subject: [PATCH 1551/2854] Sliders no longer modified by HD but have transparent body now --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 1 - osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 60 ++++++++++--------- osu.Game/Overlays/Mods/ModSection.cs | 2 - 3 files changed, 31 insertions(+), 32 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 2723be9df8..ddf708d0f1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -17,7 +17,6 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Description => @"Play with no approach circles and fading circles/sliders."; public override double ScoreMultiplier => 1.06; - private const double fade_in_duration_multiplier = 0.4; private const double fade_out_duration_multiplier = 0.3; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index 64a331213f..8ccff6b258 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { @@ -24,43 +25,44 @@ namespace osu.Game.Rulesets.Osu.Mods public override void ApplyToDrawableHitObjects(IEnumerable drawables) { foreach (var drawable in drawables.Skip(IncreaseFirstObjectVisibility.Value ? 1 : 0)) - { - switch (drawable) - { - case DrawableHitCircle _: - drawable.ApplyCustomUpdateState += ApplyTraceableState; - break; - - case DrawableSlider slider: - slider.ApplyCustomUpdateState += ApplyHiddenState; - slider.HeadCircle.ApplyCustomUpdateState += ApplyTraceableState; - break; - - default: - drawable.ApplyCustomUpdateState += ApplyHiddenState; - break; - } - } + drawable.ApplyCustomUpdateState += ApplyTraceableState; } protected void ApplyTraceableState(DrawableHitObject drawable, ArmedState state) { - if (!(drawable is DrawableHitCircle circle)) + if (!(drawable is DrawableOsuHitObject d)) return; - var h = circle.HitObject; + var h = d.HitObject; - // we only want to see the approach circle - using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + switch (drawable) { - circle.Circle.Hide(); // CirclePiece - circle.Circle.AlwaysPresent = true; - circle.Ring.Hide(); - circle.Flash.Hide(); - circle.Explode.Hide(); - circle.Number.Hide(); - circle.Glow.Hide(); - circle.ApproachCircle.Show(); + case DrawableHitCircle circle: + // we only want to see the approach circle + using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + { + circle.Circle.Hide(); // CirclePiece + circle.Circle.AlwaysPresent = true; + circle.Ring.Hide(); + circle.Flash.Hide(); + circle.Explode.Hide(); + circle.Number.Hide(); + circle.Glow.Hide(); + circle.ApproachCircle.Show(); + } + + break; + + case DrawableSlider slider: + ApplyTraceableState(slider.HeadCircle, state); + slider.Body.AccentColour = Color4.Transparent; + + break; + + default: + ApplyHiddenState(drawable, state); + + break; } } } diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 5c2ab8e18c..dedd397fa5 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -112,7 +112,6 @@ namespace osu.Game.Overlays.Mods if (selected == null) continue; foreach (var type in modTypes) - { if (type.IsInstanceOfType(selected)) { if (immediate) @@ -120,7 +119,6 @@ namespace osu.Game.Overlays.Mods else Scheduler.AddDelayed(button.Deselect, delay += 50); } - } } } From 5b4640d3ead5a89f31cb30b95c2234e79beb03c4 Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Wed, 3 Jul 2019 21:40:14 +0200 Subject: [PATCH 1552/2854] Traceable no longer inherits from OsuModHidden and is no longer multi mod --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 1 + osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 24 +++++++++++++------ .../Mods/OsuModeObjectScaleTween.cs | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 3 ++- .../TestSceneModSelectOverlay.cs | 5 ++-- 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index ddf708d0f1..74f9398f18 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Description => @"Play with no approach circles and fading circles/sliders."; public override double ScoreMultiplier => 1.06; + public override Type[] IncompatibleMods => new[] { typeof(OsuModTraceable) }; private const double fade_in_duration_multiplier = 0.4; private const double fade_out_duration_multiplier = 0.3; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index c5b5a064a6..1d68793c50 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -3,8 +3,10 @@ using System; using System.Linq; +using osu.Framework.Bindables; using System.Collections.Generic; using osu.Framework.Graphics.Sprites; +using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; @@ -12,19 +14,25 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { - internal class OsuModTraceable : OsuModHidden + internal class OsuModTraceable : Mod, IReadFromConfig, IApplicableToDrawableHitObjects { public override string Name => "Traceable"; public override string Acronym => "TC"; public override IconUsage Icon => FontAwesome.Brands.SnapchatGhost; - public override ModType Type => ModType.DifficultyIncrease; + public override ModType Type => ModType.Fun; public override string Description => "Put your faith in the approach circles..."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModeObjectScaleTween) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModeObjectScaleTween) }; + private Bindable increaseFirstObjectVisibility = new Bindable(); - public override void ApplyToDrawableHitObjects(IEnumerable drawables) + public void ReadFromConfig(OsuConfigManager config) { - foreach (var drawable in drawables.Skip(IncreaseFirstObjectVisibility.Value ? 1 : 0)) + increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); + } + + public void ApplyToDrawableHitObjects(IEnumerable drawables) + { + foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0)) drawable.ApplyCustomUpdateState += ApplyTraceableState; } @@ -59,8 +67,10 @@ namespace osu.Game.Rulesets.Osu.Mods break; - default: - ApplyHiddenState(drawable, state); + case DrawableSpinner spinner: + spinner.Disc.Hide(); + //spinner.Ticks.Hide(); // do they contribute to the theme? debatable + spinner.Background.Hide(); break; } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs b/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs index de277100b5..db61c15846 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModeObjectScaleTween) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModTraceable) }; protected virtual float StartScale => 1; diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 949b473ca6..c0e2809b38 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Osu new OsuModHardRock(), new MultiMod(new OsuModSuddenDeath(), new OsuModPerfect()), new MultiMod(new OsuModDoubleTime(), new OsuModNightcore()), - new MultiMod(new OsuModHidden(), new OsuModTraceable()), + new OsuModHidden(), new MultiMod(new OsuModFlashlight(), new OsuModBlinds()), }; @@ -136,6 +136,7 @@ namespace osu.Game.Rulesets.Osu new OsuModWiggle(), new MultiMod(new OsuModGrow(), new OsuModDeflate()), new MultiMod(new ModWindUp(), new ModWindDown()), + new OsuModTraceable(), }; default: diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 9074b64eb8..80408ab43b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -85,7 +85,7 @@ namespace osu.Game.Tests.Visual.UserInterface var assistMods = instance.GetModsFor(ModType.Automation); var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail); - var hiddenMod = harderMods.OfType().FirstOrDefault(m => m.Mods.Any(a => a is OsuModHidden)); + var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden); var doubleTimeMod = harderMods.OfType().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime)); @@ -96,11 +96,10 @@ namespace osu.Game.Tests.Visual.UserInterface testSingleMod(noFailMod); testMultiMod(doubleTimeMod); - testMultiMod(hiddenMod); testIncompatibleMods(easy, hardRock); testDeselectAll(easierMods.Where(m => !(m is MultiMod))); testMultiplierTextColour(noFailMod, modSelect.LowMultiplierColour); - testMultiplierTextColour(hardRock, modSelect.HighMultiplierColour); + testMultiplierTextColour(hiddenMod, modSelect.HighMultiplierColour); testUnimplementedMod(autoPilotMod); } From 99603ca0b67f29edc9fad584cc1e6a4e1fa5526b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 4 Jul 2019 04:50:49 +0300 Subject: [PATCH 1553/2854] Fade out game volume on exiting Invokes 'this.Exit()' on completion (simplify lines) --- osu.Game/Screens/Menu/Intro.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index dab5066c52..2883559bbd 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -33,6 +33,8 @@ namespace osu.Game.Screens.Menu protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack(); + private readonly BindableDouble exitingVolumeFade = new BindableDouble(1); + private Bindable menuVoice; private Bindable menuMusic; private Track track; @@ -72,6 +74,8 @@ namespace osu.Game.Screens.Menu welcome = audio.Samples.Get(@"welcome"); seeya = audio.Samples.Get(@"seeya"); + + audio.AddAdjustment(AdjustableProperty.Volume, exitingVolumeFade); } private const double delay_step_one = 2300; @@ -161,7 +165,7 @@ namespace osu.Game.Screens.Menu else fadeOutTime = 500; - Scheduler.AddDelayed(this.Exit, fadeOutTime); + this.TransformBindableTo(exitingVolumeFade, 0, fadeOutTime).OnComplete(_ => this.Exit()); //don't want to fade out completely else we will stop running updates. Game.FadeTo(0.01f, fadeOutTime); From b53aeec90de21feca625932e2163d45ab379d757 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 4 Jul 2019 05:18:29 +0300 Subject: [PATCH 1554/2854] Move audio adjustment inside OnResuming --- osu.Game/Screens/Menu/Intro.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index 2883559bbd..f6fbcf6498 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -35,13 +35,16 @@ namespace osu.Game.Screens.Menu private readonly BindableDouble exitingVolumeFade = new BindableDouble(1); + [Resolved] + private AudioManager audio { get; set; } + private Bindable menuVoice; private Bindable menuMusic; private Track track; private WorkingBeatmap introBeatmap; [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game) + private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game) { menuVoice = config.GetBindable(OsuSetting.MenuVoice); menuMusic = config.GetBindable(OsuSetting.MenuMusic); @@ -74,8 +77,6 @@ namespace osu.Game.Screens.Menu welcome = audio.Samples.Get(@"welcome"); seeya = audio.Samples.Get(@"seeya"); - - audio.AddAdjustment(AdjustableProperty.Volume, exitingVolumeFade); } private const double delay_step_one = 2300; @@ -165,6 +166,7 @@ namespace osu.Game.Screens.Menu else fadeOutTime = 500; + audio.AddAdjustment(AdjustableProperty.Volume, exitingVolumeFade); this.TransformBindableTo(exitingVolumeFade, 0, fadeOutTime).OnComplete(_ => this.Exit()); //don't want to fade out completely else we will stop running updates. From 7795a16c36d3809768a72240c89ba6c64d681085 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jul 2019 11:38:25 +0900 Subject: [PATCH 1555/2854] Update android metadata in line with standards --- osu.Android/Properties/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml index 326d32f7ab..0cf2b786b1 100644 --- a/osu.Android/Properties/AndroidManifest.xml +++ b/osu.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + From 70e2b83f294a943dca2369198d9d8a0f73b434a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jul 2019 11:40:05 +0900 Subject: [PATCH 1556/2854] Target newer api version --- osu.Android/Properties/AndroidManifest.xml | 2 +- .../Properties/AndroidManifest.xml | 2 +- .../Properties/AndroidManifest.xml | 2 +- .../Properties/AndroidManifest.xml | 2 +- .../Properties/AndroidManifest.xml | 2 +- osu.Game.Tests.Android/Properties/AndroidManifest.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml index 0cf2b786b1..acd21f9587 100644 --- a/osu.Android/Properties/AndroidManifest.xml +++ b/osu.Android/Properties/AndroidManifest.xml @@ -1,6 +1,6 @@  - + diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml index b04b0718f5..db95e18f13 100644 --- a/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml index c315581606..e6728c801d 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml index dac9c19477..aad907b241 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml index f731042a4c..cd4b74aa16 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Tests.Android/Properties/AndroidManifest.xml index 146f96c2a3..bb996dc5ca 100644 --- a/osu.Game.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Tests.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file From d2ebf296d99858c715a4e09a14150c6680589197 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jul 2019 13:07:59 +0900 Subject: [PATCH 1557/2854] Release with better compilation options --- osu.Android/osu.Android.csproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 42a3185cd1..ac3905a372 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -14,6 +14,11 @@ Properties\AndroidManifest.xml armeabi-v7a;x86;arm64-v8a + + cjk;mideast;other;rare;west + d8 + r8 + From 32bb9633933cd3398c63ca2faa3a771ea03becc6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jul 2019 14:33:00 +0900 Subject: [PATCH 1558/2854] Lock WorkingBeatmap cache to avoid threading issues --- osu.Game/Beatmaps/BeatmapManager.cs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 860c7fc0fa..ab08d35f52 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -176,20 +176,23 @@ namespace osu.Game.Beatmaps if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) return DefaultBeatmap; - var cached = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID); + lock (workingCache) + { + var cached = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID); - if (cached != null) - return cached; + if (cached != null) + return cached; - if (beatmapInfo.Metadata == null) - beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; + if (beatmapInfo.Metadata == null) + beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; - WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager); + WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager); - previous?.TransferTo(working); - workingCache.Add(working); + previous?.TransferTo(working); + workingCache.Add(working); - return working; + return working; + } } /// From 4885f0f0c72c05a1174a521c77c2392dafbbfe8b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jul 2019 15:47:06 +0900 Subject: [PATCH 1559/2854] Add messaging telling users how to leave changelog comments --- .../Online/TestSceneChangelogOverlay.cs | 1 + .../Requests/Responses/APIChangelogBuild.cs | 2 + .../Changelog/ChangelogSingleBuild.cs | 6 +- osu.Game/Overlays/Changelog/Comments.cs | 79 +++++++++++++++++++ 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Overlays/Changelog/Comments.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index 0655611230..cf8bac7642 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -24,6 +24,7 @@ namespace osu.Game.Tests.Visual.Online typeof(ChangelogListing), typeof(ChangelogSingleBuild), typeof(ChangelogBuild), + typeof(Comments), }; protected override void LoadComplete() diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs b/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs index 36407c7b0e..d11e2d454b 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs @@ -33,6 +33,8 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty("versions")] public VersionNavigation Versions { get; set; } + public object Url => $"https://osu.ppy.sh/home/changelog/{UpdateStream.Name}/{Version}"; + public class VersionNavigation { [JsonProperty("next")] diff --git a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs index 36ae5a756c..44552b214f 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs @@ -58,7 +58,11 @@ namespace osu.Game.Overlays.Changelog } if (build != null) - Child = new ChangelogBuildWithNavigation(build) { SelectBuild = SelectBuild }; + Children = new Drawable[] + { + new ChangelogBuildWithNavigation(build) { SelectBuild = SelectBuild }, + new Comments(build) + }; } public class ChangelogBuildWithNavigation : ChangelogBuild diff --git a/osu.Game/Overlays/Changelog/Comments.cs b/osu.Game/Overlays/Changelog/Comments.cs new file mode 100644 index 0000000000..4cf39e7b44 --- /dev/null +++ b/osu.Game/Overlays/Changelog/Comments.cs @@ -0,0 +1,79 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Online.API.Requests.Responses; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Changelog +{ + public class Comments : CompositeDrawable + { + private readonly APIChangelogBuild build; + + public Comments(APIChangelogBuild build) + { + this.build = build; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Padding = new MarginPadding + { + Horizontal = 50, + Vertical = 20, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + LinkFlowContainer text; + + InternalChildren = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 10, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.GreyVioletDarker + }, + }, + text = new LinkFlowContainer(t => + { + t.Colour = colours.PinkLighter; + t.Font = OsuFont.Default.With(size: 14); + }) + { + Padding = new MarginPadding(20), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } + }; + + text.AddParagraph("Got feedback?", t => + { + t.Colour = Color4.White; + t.Font = OsuFont.Default.With(italics: true, size: 20); + t.Padding = new MarginPadding { Bottom = 20 }; + }); + + text.AddParagraph("We would love to hear what you think of this update! "); + text.AddIcon(FontAwesome.Regular.GrinHearts); + + text.AddParagraph("Please visit the "); + text.AddLink("web version", $"{build.Url}#comments"); + text.AddText(" of this changelog to leave any comments."); + } + } +} From 2b9f7d551f7214fba14b38f8629ba6b539700c4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jul 2019 16:16:17 +0900 Subject: [PATCH 1560/2854] Fix incorrect type specification --- osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs b/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs index d11e2d454b..56005e15f8 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelogBuild.cs @@ -33,7 +33,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty("versions")] public VersionNavigation Versions { get; set; } - public object Url => $"https://osu.ppy.sh/home/changelog/{UpdateStream.Name}/{Version}"; + public string Url => $"https://osu.ppy.sh/home/changelog/{UpdateStream.Name}/{Version}"; public class VersionNavigation { From a20d5baa57c3b84557f2a584cef3796091d400ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jul 2019 16:21:01 +0900 Subject: [PATCH 1561/2854] Fix skip button not being clickable after fade out --- osu.Game/Screens/Play/SkipOverlay.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index 38dd179f25..d0912db63c 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -201,14 +201,15 @@ namespace osu.Game.Screens.Play protected override bool OnMouseDown(MouseDownEvent e) { + Show(); scheduledHide?.Cancel(); - return base.OnMouseDown(e); + return true; } protected override bool OnMouseUp(MouseUpEvent e) { Show(); - return base.OnMouseUp(e); + return true; } public override void Hide() => State = Visibility.Hidden; From db24ac28ec21ebac80e7525a51b787b5b920274c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jul 2019 16:53:08 +0900 Subject: [PATCH 1562/2854] Add tests --- .../Visual/Gameplay/TestSceneSkipOverlay.cs | 63 +++++++++++++++++-- osu.Game/Screens/Play/SkipOverlay.cs | 2 + 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs index 0519660477..8d2f71d9b4 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs @@ -1,19 +1,72 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Game.Screens.Play; +using osuTK; +using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay { [TestFixture] - public class TestSceneSkipOverlay : OsuTestScene + public class TestSceneSkipOverlay : ManualInputManagerTestScene { - protected override void LoadComplete() - { - base.LoadComplete(); + private SkipOverlay skip; + private int requestCount; - Add(new SkipOverlay(Clock.CurrentTime + 5000)); + [SetUp] + public void SetUp() => Schedule(() => + { + requestCount = 0; + Child = skip = new SkipOverlay(Clock.CurrentTime + 5000) + { + RequestSeek = _ => requestCount++ + }; + }); + + [Test] + public void TestFadeOnIdle() + { + AddStep("move mouse", () => InputManager.MoveMouseTo(Vector2.Zero)); + AddUntilStep("wait for fade", () => skip.Children.First().Alpha == 0); + AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre)); + AddUntilStep("visible again", () => skip.Children.First().Alpha > 0); + AddUntilStep("wait for fade", () => skip.Children.First().Alpha == 0); } + + [Test] + public void TestClickableAfterFade() + { + AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre)); + AddUntilStep("wait for fade", () => skip.Children.First().Alpha == 0); + AddStep("click", () => InputManager.Click(MouseButton.Left)); + checkRequestCount(1); + } + + [Test] + public void TestClickOnlyActuatesOnce() + { + AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre)); + AddStep("click", () => InputManager.Click(MouseButton.Left)); + AddStep("click", () => InputManager.Click(MouseButton.Left)); + AddStep("click", () => InputManager.Click(MouseButton.Left)); + AddStep("click", () => InputManager.Click(MouseButton.Left)); + checkRequestCount(1); + } + + [Test] + public void TestDoesntFadeOnMouseDown() + { + AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre)); + AddStep("button down", () => InputManager.PressButton(MouseButton.Left)); + AddUntilStep("wait for overlay disapper", () => !skip.IsAlive); + AddAssert("ensure button didn't disappear", () => skip.Children.First().Alpha > 0); + AddStep("button up", () => InputManager.ReleaseButton(MouseButton.Left)); + checkRequestCount(0); + } + + private void checkRequestCount(int expected) => + AddAssert($"request count is {expected}", () => requestCount == expected); } } diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index d0912db63c..d6c2b59d98 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -161,6 +161,8 @@ namespace osu.Game.Screens.Play private Visibility state; private ScheduledDelegate scheduledHide; + public override bool IsPresent => true; + public Visibility State { get => state; From 530675f3642a5da7729e9720276c4a4fe34c159e Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 4 Jul 2019 17:05:29 +0900 Subject: [PATCH 1563/2854] Add tests as separate steps --- .../UserInterface/TestSceneButtonSystem.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs index 6c4d9b20f7..88129308db 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs @@ -43,25 +43,25 @@ namespace osu.Game.Tests.Visual.UserInterface buttons.SetOsuLogo(logo); foreach (var s in Enum.GetValues(typeof(ButtonSystemState)).OfType().Skip(1)) - AddStep($"State to {s}", () => - { - buttons.State = s; + AddStep($"State to {s}", () => buttons.State = s); - if (buttons.State == ButtonSystemState.EnteringMode) - { - buttons.FadeOut(400, Easing.InSine); - buttons.MoveTo(new Vector2(-800, 0), 400, Easing.InSine); - logo.FadeOut(300, Easing.InSine) - .ScaleTo(0.2f, 300, Easing.InSine); - } - else - { - buttons.FadeIn(400, Easing.OutQuint); - buttons.MoveTo(new Vector2(0), 400, Easing.OutQuint); - logo.FadeColour(Color4.White, 100, Easing.OutQuint); - logo.FadeIn(100, Easing.OutQuint); - } - }); + AddStep("Exiting menu", () => + { + buttons.State = ButtonSystemState.EnteringMode; + buttons.FadeOut(400, Easing.InSine); + buttons.MoveTo(new Vector2(-800, 0), 400, Easing.InSine); + logo.FadeOut(300, Easing.InSine) + .ScaleTo(0.2f, 300, Easing.InSine); + }); + + AddStep("Entering menu", () => + { + buttons.State = ButtonSystemState.Play; + buttons.FadeIn(400, Easing.OutQuint); + buttons.MoveTo(new Vector2(0), 400, Easing.OutQuint); + logo.FadeColour(Color4.White, 100, Easing.OutQuint); + logo.FadeIn(100, Easing.OutQuint); + }); } } } From 07078b735c7909fd192570d531bfd3a883312afb Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 4 Jul 2019 17:06:28 +0900 Subject: [PATCH 1564/2854] use new vector2 --- osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs index e5a27dac81..88129308db 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Shapes; using osu.Game.Screens.Menu; +using osuTK; using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface From be4e7d0f5059ea1d5d6d00eea71c368207c6bc36 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 4 Jul 2019 17:08:21 +0900 Subject: [PATCH 1565/2854] remove comment --- osu.Game/Screens/Menu/ButtonSystem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 040a9f8843..87c009c437 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -332,7 +332,6 @@ namespace osu.Game.Screens.Menu break; case ButtonSystemState.EnteringMode: - // When coming from the Initial (untracked) state, interpolate to the tracking position over a brief duration instead of tracking immediately. logoTrackingContainer.StartTracking(logo, lastState == ButtonSystemState.Initial ? 400 : 0, Easing.InSine); break; } From 73bc71f8b2b891a33d6e87fb33e0929d90c53758 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jul 2019 17:32:58 +0900 Subject: [PATCH 1566/2854] Use local clock to add consistency to skip times --- .../Visual/Gameplay/TestSceneSkipOverlay.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs index 8d2f71d9b4..a0e2e10c00 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs @@ -3,6 +3,9 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Timing; using osu.Game.Screens.Play; using osuTK; using osuTK.Input; @@ -19,9 +22,20 @@ namespace osu.Game.Tests.Visual.Gameplay public void SetUp() => Schedule(() => { requestCount = 0; - Child = skip = new SkipOverlay(Clock.CurrentTime + 5000) + Child = new Container { - RequestSeek = _ => requestCount++ + RelativeSizeAxes = Axes.Both, + Clock = new FramedOffsetClock(Clock) + { + Offset = -Clock.CurrentTime, + }, + Children = new Drawable[] + { + skip = new SkipOverlay(5000) + { + RequestSeek = _ => requestCount++ + } + }, }; }); From 86e9d97b6a3006f89fc4b9b67adda55e31b51386 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jul 2019 18:38:29 +0900 Subject: [PATCH 1567/2854] Adjust variables and test length to avoid random failures --- .../Visual/Gameplay/TestSceneSkipOverlay.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs index a0e2e10c00..94ae243bb9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Gameplay }, Children = new Drawable[] { - skip = new SkipOverlay(5000) + skip = new SkipOverlay(6000) { RequestSeek = _ => requestCount++ } @@ -42,11 +42,12 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestFadeOnIdle() { - AddStep("move mouse", () => InputManager.MoveMouseTo(Vector2.Zero)); - AddUntilStep("wait for fade", () => skip.Children.First().Alpha == 0); + AddUntilStep("fully visible", () => skip.Children.First().Alpha == 1); + AddUntilStep("wait for fade", () => skip.Children.First().Alpha < 1); + AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre)); - AddUntilStep("visible again", () => skip.Children.First().Alpha > 0); - AddUntilStep("wait for fade", () => skip.Children.First().Alpha == 0); + AddUntilStep("fully visible", () => skip.Children.First().Alpha == 1); + AddUntilStep("wait for fade", () => skip.Children.First().Alpha < 1); } [Test] From 2dc356a24e794eb9c3e31ff216c3b688660dc730 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jul 2019 18:47:43 +0900 Subject: [PATCH 1568/2854] Remove using --- osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs index 94ae243bb9..2a08d2ad19 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Timing; using osu.Game.Screens.Play; -using osuTK; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay From 1b368601695164d2789097156e02788e678add66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jul 2019 19:26:12 +0900 Subject: [PATCH 1569/2854] Fix test cross-talk --- osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs index 2a08d2ad19..b152c21454 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Timing; using osu.Game.Screens.Play; +using osuTK; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay @@ -41,6 +42,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestFadeOnIdle() { + AddStep("move mouse", () => InputManager.MoveMouseTo(Vector2.Zero)); AddUntilStep("fully visible", () => skip.Children.First().Alpha == 1); AddUntilStep("wait for fade", () => skip.Children.First().Alpha < 1); From 4758914f7fced0da59361bfb18d8556f5f42eaeb Mon Sep 17 00:00:00 2001 From: Morilli <35152647+Morilli@users.noreply.github.com> Date: Thu, 4 Jul 2019 15:14:15 +0200 Subject: [PATCH 1570/2854] Bump android TargetFrameworkVersion --- osu.Android.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android.props b/osu.Android.props index 7b22b76e0e..5ee0573c58 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ Off True Xamarin.Android.Net.AndroidClientHandler - v8.1 + v9.0 false From 9b2ebed669aacbbfc81b46379f017c111a897c1e Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 4 Jul 2019 08:36:34 -0700 Subject: [PATCH 1571/2854] Remove unused EditSongSelect file --- osu.Game/Screens/Select/EditSongSelect.cs | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 osu.Game/Screens/Select/EditSongSelect.cs diff --git a/osu.Game/Screens/Select/EditSongSelect.cs b/osu.Game/Screens/Select/EditSongSelect.cs deleted file mode 100644 index bdf5f905fe..0000000000 --- a/osu.Game/Screens/Select/EditSongSelect.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Screens; - -namespace osu.Game.Screens.Select -{ - public class EditSongSelect : SongSelect - { - protected override bool ShowFooter => false; - - protected override bool OnStart() - { - this.Exit(); - return true; - } - } -} From f04adb719252aaf723c89250aaf30e53c63e043d Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 3 Jul 2019 22:28:13 +0530 Subject: [PATCH 1572/2854] Apply mods filter to local scores too --- .../Select/Leaderboards/BeatmapLeaderboard.cs | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 08f9632462..c1cbc40680 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -39,6 +39,9 @@ namespace osu.Game.Screens.Select.Leaderboards private bool filterMods; + /// + /// Whether to apply the game's currently selected mods as a filter when retrieving scores. + /// public bool FilterMods { get => filterMods; @@ -80,10 +83,25 @@ namespace osu.Game.Screens.Select.Leaderboards { if (Scope == BeatmapLeaderboardScope.Local) { - Scores = scoreManager - .QueryScores(s => !s.DeletePending && s.Beatmap.ID == Beatmap.ID && s.Ruleset.ID == ruleset.Value.ID) - .OrderByDescending(s => s.TotalScore).ToArray(); + var scores = scoreManager + .QueryScores(s => !s.DeletePending && s.Beatmap.ID == Beatmap.ID && s.Ruleset.ID == ruleset.Value.ID); + + if (filterMods && !mods.Value.Any()) + { + // we need to filter out all scores that have any mods to get all local nomod scores + scores = scores.Where(s => !s.Mods.Any()); + } + else if (filterMods) + { + // otherwise find all the scores that have *any* of the currently selected mods (similar to how web applies mod filters) + // we're creating and using a string list representation of selected mods so that it can be translated into the DB query itself + var selectedMods = mods.Value.Select(m => m.Acronym); + scores = scores.Where(s => s.Mods.Any(m => selectedMods.Contains(m.Acronym))); + } + + Scores = scores.OrderByDescending(s => s.TotalScore).ToArray(); PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; + return null; } @@ -101,7 +119,7 @@ namespace osu.Game.Screens.Select.Leaderboards IReadOnlyList requestMods = null; - if (filterMods && mods.Value.Count == 0) + if (filterMods && !mods.Value.Any()) // add nomod for the request requestMods = new Mod[] { new ModNoMod() }; else if (filterMods) From 5f6544eebfc913cde2c505825a37eb429311951f Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 4 Jul 2019 12:05:07 -0700 Subject: [PATCH 1573/2854] Fix beatmap leaderboards requiring supporter when signed out --- osu.Game/Online/Leaderboards/Leaderboard.cs | 6 ------ osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index b91de93a4a..dea2ff1a21 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -231,12 +231,6 @@ namespace osu.Game.Online.Leaderboards if (getScoresRequest == null) return; - if (api?.IsLoggedIn != true) - { - PlaceholderState = PlaceholderState.NotLoggedIn; - return; - } - getScoresRequest.Failure += e => Schedule(() => { if (e is OperationCanceledException) diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 62f93afbbb..68f153a97d 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -62,6 +62,12 @@ namespace osu.Game.Screens.Select.Leaderboards return null; } + if (api?.IsLoggedIn != true) + { + PlaceholderState = PlaceholderState.NotLoggedIn; + return null; + } + if (Beatmap?.OnlineBeatmapID == null) { PlaceholderState = PlaceholderState.Unavailable; From ae7da2557e15e634d140340d7e0a150a0ad31eb3 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 4 Jul 2019 13:24:13 -0700 Subject: [PATCH 1574/2854] Fix initial colour of leaderboard mod filter --- .../UserInterface/OsuTabControlCheckbox.cs | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs index 869005d05c..908fbb67bb 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs @@ -37,6 +37,8 @@ namespace osu.Game.Graphics.UserInterface text.Colour = AccentColour; icon.Colour = AccentColour; } + + updateFade(); } } @@ -48,28 +50,22 @@ namespace osu.Game.Graphics.UserInterface private const float transition_length = 500; - private void fadeIn() + private void updateFade() { - box.FadeIn(transition_length, Easing.OutQuint); - text.FadeColour(Color4.White, transition_length, Easing.OutQuint); - } - - private void fadeOut() - { - box.FadeOut(transition_length, Easing.OutQuint); - text.FadeColour(AccentColour, transition_length, Easing.OutQuint); + box.FadeTo(IsHovered ? 1 : 0, transition_length, Easing.OutQuint); + text.FadeColour(IsHovered ? Color4.White : AccentColour, transition_length, Easing.OutQuint); } protected override bool OnHover(HoverEvent e) { - fadeIn(); + updateFade(); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { if (!Current.Value) - fadeOut(); + updateFade(); base.OnHoverLost(e); } @@ -117,16 +113,7 @@ namespace osu.Game.Graphics.UserInterface Current.ValueChanged += selected => { - if (selected.NewValue) - { - fadeIn(); - icon.Icon = FontAwesome.Regular.CheckCircle; - } - else - { - fadeOut(); - icon.Icon = FontAwesome.Regular.Circle; - } + icon.Icon = selected.NewValue ? FontAwesome.Regular.CheckCircle : FontAwesome.Regular.Circle; }; } } From 38dceddc2710567b683144a908e1cc19374726fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jul 2019 10:07:45 +0900 Subject: [PATCH 1575/2854] Fix file ordering --- .../UserInterface/OsuTabControlCheckbox.cs | 59 +++++++++---------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs index 908fbb67bb..8134cfb42d 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs @@ -50,33 +50,6 @@ namespace osu.Game.Graphics.UserInterface private const float transition_length = 500; - private void updateFade() - { - box.FadeTo(IsHovered ? 1 : 0, transition_length, Easing.OutQuint); - text.FadeColour(IsHovered ? Color4.White : AccentColour, transition_length, Easing.OutQuint); - } - - protected override bool OnHover(HoverEvent e) - { - updateFade(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - if (!Current.Value) - updateFade(); - - base.OnHoverLost(e); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - if (accentColour == null) - AccentColour = colours.Blue; - } - public OsuTabControlCheckbox() { AutoSizeAxes = Axes.Both; @@ -111,10 +84,34 @@ namespace osu.Game.Graphics.UserInterface } }; - Current.ValueChanged += selected => - { - icon.Icon = selected.NewValue ? FontAwesome.Regular.CheckCircle : FontAwesome.Regular.Circle; - }; + Current.ValueChanged += selected => { icon.Icon = selected.NewValue ? FontAwesome.Regular.CheckCircle : FontAwesome.Regular.Circle; }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + if (accentColour == null) + AccentColour = colours.Blue; + } + + protected override bool OnHover(HoverEvent e) + { + updateFade(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + if (!Current.Value) + updateFade(); + + base.OnHoverLost(e); + } + + private void updateFade() + { + box.FadeTo(IsHovered ? 1 : 0, transition_length, Easing.OutQuint); + text.FadeColour(IsHovered ? Color4.White : AccentColour, transition_length, Easing.OutQuint); } } } From bff5ad22f4fedf8c3a1c1ae9bb1e6cf791ed676e Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 5 Jul 2019 05:16:40 +0300 Subject: [PATCH 1576/2854] Check if the locally available score has files --- osu.Game/Scoring/ScoreManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 2d82987da0..f0d897701c 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -65,6 +65,6 @@ namespace osu.Game.Scoring protected override ArchiveDownloadRequest CreateDownloadRequest(ScoreInfo score, bool minimiseDownload) => new DownloadReplayRequest(score); - protected override bool CheckLocalAvailability(ScoreInfo model, IQueryable items) => items.Any(s => s.OnlineScoreID == model.OnlineScoreID); + protected override bool CheckLocalAvailability(ScoreInfo model, IQueryable items) => items.Any(s => s.OnlineScoreID == model.OnlineScoreID && s.Files.Any()); } } From d624157c45afc11187b58f523c8ff0e61d005c81 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 5 Jul 2019 05:17:36 +0300 Subject: [PATCH 1577/2854] Remove unnecessary fading --- osu.Game/Screens/Play/ReplayDownloadButton.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/ReplayDownloadButton.cs b/osu.Game/Screens/Play/ReplayDownloadButton.cs index 748fe8cc90..290e00f287 100644 --- a/osu.Game/Screens/Play/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Play/ReplayDownloadButton.cs @@ -86,11 +86,7 @@ namespace osu.Game.Screens.Play } }, true); - if (replayAvailability == ReplayAvailability.NotAvailable) - { - button.Enabled.Value = false; - button.Alpha = 0.6f; - } + button.Enabled.Value = replayAvailability != ReplayAvailability.NotAvailable; } private enum ReplayAvailability From 29bc2a27514a8573f17ebc1a9acf1e2e7813f003 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jul 2019 11:29:47 +0900 Subject: [PATCH 1578/2854] Split out tests --- .../UserInterface/TestSceneButtonSystem.cs | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs index 88129308db..3b790d33b6 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs @@ -24,11 +24,12 @@ namespace osu.Game.Tests.Visual.UserInterface typeof(Button) }; - public TestSceneButtonSystem() - { - OsuLogo logo; - ButtonSystem buttons; + private OsuLogo logo; + private ButtonSystem buttons; + [SetUp] + public void SetUp() => Schedule(() => + { Children = new Drawable[] { new Box @@ -37,15 +38,23 @@ namespace osu.Game.Tests.Visual.UserInterface RelativeSizeAxes = Axes.Both, }, buttons = new ButtonSystem(), - logo = new OsuLogo { RelativePositionAxes = Axes.Both } + logo = new OsuLogo + { + RelativePositionAxes = Axes.Both, + Position = new Vector2(0.5f) + } }; buttons.SetOsuLogo(logo); + }); + [Test] + public void TestAllStates() + { foreach (var s in Enum.GetValues(typeof(ButtonSystemState)).OfType().Skip(1)) AddStep($"State to {s}", () => buttons.State = s); - AddStep("Exiting menu", () => + AddStep("Enter mode", () => { buttons.State = ButtonSystemState.EnteringMode; buttons.FadeOut(400, Easing.InSine); @@ -54,7 +63,7 @@ namespace osu.Game.Tests.Visual.UserInterface .ScaleTo(0.2f, 300, Easing.InSine); }); - AddStep("Entering menu", () => + AddStep("Return to menu", () => { buttons.State = ButtonSystemState.Play; buttons.FadeIn(400, Easing.OutQuint); @@ -63,5 +72,18 @@ namespace osu.Game.Tests.Visual.UserInterface logo.FadeIn(100, Easing.OutQuint); }); } + + [Test] + public void TestSmoothExit() + { + AddStep("Enter mode", () => + { + buttons.State = ButtonSystemState.EnteringMode; + buttons.FadeOut(400, Easing.InSine); + buttons.MoveTo(new Vector2(-800, 0), 400, Easing.InSine); + logo.FadeOut(300, Easing.InSine) + .ScaleTo(0.2f, 300, Easing.InSine); + }); + } } } From a30f0c03bb071dbb4d959dc7da0c09ed4314ce51 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 5 Jul 2019 05:32:30 +0300 Subject: [PATCH 1579/2854] Fix incorrect xmldoc --- osu.Game/Online/DownloadTrackingComposite.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index 786afdf450..62d6efcb6f 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -11,7 +11,7 @@ using osu.Game.Online.API; namespace osu.Game.Online { /// - /// A component which tracks a beatmap through potential download/import/deletion. + /// A component which tracks a through potential download/import/deletion. /// public abstract class DownloadTrackingComposite : CompositeDrawable where TModel : class, IEquatable @@ -22,7 +22,7 @@ namespace osu.Game.Online private TModelManager manager; /// - /// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded. + /// Holds the current download state of the , whether is has already been downloaded, is in progress, or is not downloaded. /// protected readonly Bindable State = new Bindable(); From 1dab363be309abd7e94e57b161bd63ff74fb8af5 Mon Sep 17 00:00:00 2001 From: naoey Date: Fri, 5 Jul 2019 08:42:44 +0530 Subject: [PATCH 1580/2854] Require supporter for filtering mods on online leaderboards --- osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 88ce5dc117..76bfd96305 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -117,7 +117,7 @@ namespace osu.Game.Screens.Select.Leaderboards return null; } - if (Scope != BeatmapLeaderboardScope.Global && !api.LocalUser.Value.IsSupporter) + if (!api.LocalUser.Value.IsSupporter && (Scope != BeatmapLeaderboardScope.Global || filterMods)) { PlaceholderState = PlaceholderState.NotSupporter; return null; From f1dab946fff325ae420f87364a2636af4293316f Mon Sep 17 00:00:00 2001 From: naoey Date: Fri, 5 Jul 2019 08:46:17 +0530 Subject: [PATCH 1581/2854] Remove need to trim query string --- osu.Game/Online/API/Requests/GetScoresRequest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 53349e15d5..6b0e680eb5 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -50,13 +50,13 @@ namespace osu.Game.Online.API.Requests { StringBuilder query = new StringBuilder(@"?"); - query.Append($@"type={scope.ToString().ToLowerInvariant()}&"); - query.Append($@"mode={ruleset.ShortName}&"); + query.Append($@"type={scope.ToString().ToLowerInvariant()}"); + query.Append($@"&mode={ruleset.ShortName}"); foreach (var mod in mods) - query.Append($@"mods[]={mod.Acronym}&"); + query.Append($@"&mods[]={mod.Acronym}"); - return query.ToString().TrimEnd('&'); + return query.ToString(); } } } From 79d6670dc581ed27edaef31a0b57466fce707aeb Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 5 Jul 2019 13:08:45 +0900 Subject: [PATCH 1582/2854] Expose durations from MainMenu and reorder --- .../UserInterface/TestSceneButtonSystem.cs | 8 ++++---- osu.Game/Screens/Menu/MainMenu.cs | 20 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs index 88129308db..461db4a30c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs @@ -48,8 +48,8 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Exiting menu", () => { buttons.State = ButtonSystemState.EnteringMode; - buttons.FadeOut(400, Easing.InSine); - buttons.MoveTo(new Vector2(-800, 0), 400, Easing.InSine); + buttons.FadeOut(MainMenu.FADE_OUT_DURATION, Easing.InSine); + buttons.MoveTo(new Vector2(-800, 0), MainMenu.FADE_OUT_DURATION, Easing.InSine); logo.FadeOut(300, Easing.InSine) .ScaleTo(0.2f, 300, Easing.InSine); }); @@ -57,8 +57,8 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Entering menu", () => { buttons.State = ButtonSystemState.Play; - buttons.FadeIn(400, Easing.OutQuint); - buttons.MoveTo(new Vector2(0), 400, Easing.OutQuint); + buttons.FadeIn(MainMenu.FADE_IN_DURATION, Easing.OutQuint); + buttons.MoveTo(new Vector2(0), MainMenu.FADE_IN_DURATION, Easing.OutQuint); logo.FadeColour(Color4.White, 100, Easing.OutQuint); logo.FadeIn(100, Easing.OutQuint); }); diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 7e6de54d1b..c64bea840f 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -23,7 +23,9 @@ namespace osu.Game.Screens.Menu { public class MainMenu : OsuScreen { - private ButtonSystem buttons; + public const float FADE_IN_DURATION = 300; + + public const float FADE_OUT_DURATION = 400; public override bool HideOverlaysOnEnter => buttons == null || buttons.State == ButtonSystemState.Initial; @@ -35,6 +37,8 @@ namespace osu.Game.Screens.Menu private MenuSideFlashes sideFlashes; + private ButtonSystem buttons; + [Resolved] private GameHost host { get; set; } @@ -141,12 +145,10 @@ namespace osu.Game.Screens.Menu { buttons.State = ButtonSystemState.TopLevel; - const float length = 300; + this.FadeIn(FADE_IN_DURATION, Easing.OutQuint); + this.MoveTo(new Vector2(0, 0), FADE_IN_DURATION, Easing.OutQuint); - this.FadeIn(length, Easing.OutQuint); - this.MoveTo(new Vector2(0, 0), length, Easing.OutQuint); - - sideFlashes.Delay(length).FadeIn(64, Easing.InQuint); + sideFlashes.Delay(FADE_IN_DURATION).FadeIn(64, Easing.InQuint); } } @@ -171,12 +173,10 @@ namespace osu.Game.Screens.Menu { base.OnSuspending(next); - const float length = 400; - buttons.State = ButtonSystemState.EnteringMode; - this.FadeOut(length, Easing.InSine); - this.MoveTo(new Vector2(-800, 0), length, Easing.InSine); + this.FadeOut(FADE_OUT_DURATION, Easing.InSine); + this.MoveTo(new Vector2(-800, 0), FADE_OUT_DURATION, Easing.InSine); sideFlashes.FadeOut(64, Easing.OutQuint); } From 4eb01b12be4f0cba9ec6a8aa8d17cabafb88284d Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 5 Jul 2019 13:10:59 +0900 Subject: [PATCH 1583/2854] Convert to method --- .../UserInterface/TestSceneButtonSystem.cs | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs index 9bc8f332f4..f0e1c38525 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs @@ -54,14 +54,7 @@ namespace osu.Game.Tests.Visual.UserInterface foreach (var s in Enum.GetValues(typeof(ButtonSystemState)).OfType().Skip(1)) AddStep($"State to {s}", () => buttons.State = s); - AddStep("Enter mode", () => - { - buttons.State = ButtonSystemState.EnteringMode; - buttons.FadeOut(MainMenu.FADE_OUT_DURATION, Easing.InSine); - buttons.MoveTo(new Vector2(-800, 0), MainMenu.FADE_OUT_DURATION, Easing.InSine); - logo.FadeOut(300, Easing.InSine) - .ScaleTo(0.2f, 300, Easing.InSine); - }); + AddStep("Enter mode", performEnterMode); AddStep("Return to menu", () => { @@ -76,14 +69,16 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestSmoothExit() { - AddStep("Enter mode", () => - { - buttons.State = ButtonSystemState.EnteringMode; - buttons.FadeOut(400, Easing.InSine); - buttons.MoveTo(new Vector2(-800, 0), 400, Easing.InSine); - logo.FadeOut(300, Easing.InSine) - .ScaleTo(0.2f, 300, Easing.InSine); - }); + AddStep("Enter mode", performEnterMode); + } + + private void performEnterMode() + { + buttons.State = ButtonSystemState.EnteringMode; + buttons.FadeOut(MainMenu.FADE_OUT_DURATION, Easing.InSine); + buttons.MoveTo(new Vector2(-800, 0), MainMenu.FADE_OUT_DURATION, Easing.InSine); + logo.FadeOut(300, Easing.InSine) + .ScaleTo(0.2f, 300, Easing.InSine); } } } From e8168037f915a506b1026723a7ad687495a31ef1 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 4 Jul 2019 21:19:51 -0700 Subject: [PATCH 1584/2854] Fix unranked beatmap leaderboards showing "no records" placeholder --- .../Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 68f153a97d..34f46608da 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -68,10 +68,14 @@ namespace osu.Game.Screens.Select.Leaderboards return null; } - if (Beatmap?.OnlineBeatmapID == null) + switch (Beatmap?.Status) { - PlaceholderState = PlaceholderState.Unavailable; - return null; + case BeatmapSetOnlineStatus.Graveyard: + case BeatmapSetOnlineStatus.None: + case BeatmapSetOnlineStatus.Pending: + case BeatmapSetOnlineStatus.WIP: + PlaceholderState = PlaceholderState.Unavailable; + return null; } if (Scope != BeatmapLeaderboardScope.Global && !api.LocalUser.Value.IsSupporter) From 04ef6c4d45741bae700e266370c10d9ea574079c Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 4 Jul 2019 21:25:10 -0700 Subject: [PATCH 1585/2854] Add beatmap status tests --- .../Visual/SongSelect/TestSceneLeaderboard.cs | 98 ++++++++++++++----- 1 file changed, 73 insertions(+), 25 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs index 157e572606..fc42048984 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs @@ -47,7 +47,14 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter)); AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn)); AddStep(@"Unavailable", () => leaderboard.SetRetrievalState(PlaceholderState.Unavailable)); - AddStep(@"Real beatmap", realBeatmap); + AddStep(@"Ranked beatmap", rankedBeatmap); + AddStep(@"Approved beatmap", approvedBeatmap); + AddStep(@"Qualified beatmap", qualifiedBeatmap); + AddStep(@"Loved beatmap", lovedBeatmap); + AddStep(@"Pending beatmap", pendingBeatmap); + AddStep(@"WIP beatmap", wipBeatmap); + AddStep(@"Graveyard beatmap", graveyardBeatmap); + AddStep(@"Unpublished beatmap", unpublishedBeatmap); } [BackgroundDependencyLoader] @@ -245,34 +252,75 @@ namespace osu.Game.Tests.Visual.SongSelect leaderboard.Scores = scores; } - private void realBeatmap() + private void rankedBeatmap() { leaderboard.Beatmap = new BeatmapInfo { - StarDifficulty = 1.36, - Version = @"BASIC", OnlineBeatmapID = 1113057, - Ruleset = rulesets.GetRuleset(0), - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 4, - DrainRate = 6.5f, - OverallDifficulty = 6.5f, - ApproachRate = 5, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 115000, - CircleCount = 265, - SliderCount = 71, - PlayCount = 47906, - PassCount = 19899, - }, - Metrics = new BeatmapMetrics - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, + Status = BeatmapSetOnlineStatus.Ranked, + }; + } + + private void approvedBeatmap() + { + leaderboard.Beatmap = new BeatmapInfo + { + OnlineBeatmapID = 1113057, + Status = BeatmapSetOnlineStatus.Approved, + }; + } + + private void qualifiedBeatmap() + { + leaderboard.Beatmap = new BeatmapInfo + { + OnlineBeatmapID = 1113057, + Status = BeatmapSetOnlineStatus.Qualified, + }; + } + + private void lovedBeatmap() + { + leaderboard.Beatmap = new BeatmapInfo + { + OnlineBeatmapID = 1113057, + Status = BeatmapSetOnlineStatus.Loved, + }; + } + + private void pendingBeatmap() + { + leaderboard.Beatmap = new BeatmapInfo + { + OnlineBeatmapID = 1113057, + Status = BeatmapSetOnlineStatus.Pending, + }; + } + + private void wipBeatmap() + { + leaderboard.Beatmap = new BeatmapInfo + { + OnlineBeatmapID = 1113057, + Status = BeatmapSetOnlineStatus.WIP, + }; + } + + private void graveyardBeatmap() + { + leaderboard.Beatmap = new BeatmapInfo + { + OnlineBeatmapID = 1113057, + Status = BeatmapSetOnlineStatus.Graveyard, + }; + } + + private void unpublishedBeatmap() + { + leaderboard.Beatmap = new BeatmapInfo + { + OnlineBeatmapID = null, + Status = BeatmapSetOnlineStatus.None, }; } From 1534b75d27658a3bc671a3a483f276bc49e67766 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 4 Jul 2019 21:39:21 -0700 Subject: [PATCH 1586/2854] Fix ci errors --- .../Visual/SongSelect/TestSceneLeaderboard.cs | 11 ----------- .../Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 6 ++++++ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs index fc42048984..f6164c8a73 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs @@ -4,12 +4,9 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Linq; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Online.Leaderboards; -using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; @@ -27,8 +24,6 @@ namespace osu.Game.Tests.Visual.SongSelect typeof(RetrievalFailurePlaceholder), }; - private RulesetStore rulesets; - private readonly FailableLeaderboard leaderboard; public TestSceneLeaderboard() @@ -57,12 +52,6 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep(@"Unpublished beatmap", unpublishedBeatmap); } - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - this.rulesets = rulesets; - } - private void newScores() { var scores = new[] diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 34f46608da..550e87bcdc 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -68,6 +68,12 @@ namespace osu.Game.Screens.Select.Leaderboards return null; } + if (Beatmap?.OnlineBeatmapID == null) + { + PlaceholderState = PlaceholderState.Unavailable; + return null; + } + switch (Beatmap?.Status) { case BeatmapSetOnlineStatus.Graveyard: From 8346c50ce113cdecea9378cf2a723795251c197b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jul 2019 13:49:54 +0900 Subject: [PATCH 1587/2854] Rename delete method and improve xmldoc --- osu.Game/Beatmaps/BeatmapManager.cs | 4 +--- osu.Game/Database/ArchiveModelManager.cs | 8 +++++--- osu.Game/Skinning/SkinManager.cs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 643fef5904..df9c8973ea 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -82,9 +82,7 @@ namespace osu.Game.Beatmaps protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => new DownloadBeatmapSetRequest(set, minimiseDownloadSize); - protected override IEnumerable GetStableImportPaths() => GetStableStorage().GetDirectories(ImportFromStablePath); - - protected override bool ShouldRemoveArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osz"; + protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osz"; protected override Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default) { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 939333c6f4..dabd8fa2ef 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -176,7 +176,7 @@ namespace osu.Game.Database // TODO: Add a check to prevent files from storage to be deleted. try { - if (import != null && File.Exists(path) && ShouldRemoveArchive(path)) + if (import != null && File.Exists(path) && ShouldDeleteArchive(path)) File.Delete(path); } catch (Exception e) @@ -504,9 +504,11 @@ namespace osu.Game.Database protected abstract IEnumerable GetStableImportPaths(); /// - /// Should this archive be removed after importing? + /// Whether this specified path should be removed after successful import. /// - protected virtual bool ShouldRemoveArchive(string path) => false; + /// The path for consideration. May be a file or a directory. + /// Whether to perform deletion. + protected virtual bool ShouldDeleteArchive(string path) => false; /// /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 00e6fddc6a..3509263e6f 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -57,7 +57,7 @@ namespace osu.Game.Skinning protected override IEnumerable GetStableImportPaths() => GetStableStorage().GetDirectories(ImportFromStablePath); - protected override bool ShouldRemoveArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osk"; + protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osk"; /// /// Returns a list of all usable s. Includes the special default skin plus all skins from . From ba8df3ba9244034db85fa92828e19c7c9ce4c689 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jul 2019 13:59:31 +0900 Subject: [PATCH 1588/2854] Clean up stable lookup and mutate logic --- osu.Game/Database/ArchiveModelManager.cs | 6 +++--- osu.Game/Scoring/ScoreManager.cs | 5 ++--- osu.Game/Skinning/SkinManager.cs | 2 -- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index dabd8fa2ef..445c8feb3b 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -486,7 +486,7 @@ namespace osu.Game.Database /// /// Set a storage with access to an osu-stable install for import purposes. /// - public Func GetStableStorage { protected get; set; } + public Func GetStableStorage { private get; set; } /// /// Denotes whether an osu-stable installation is present to perform automated imports from. @@ -501,7 +501,7 @@ namespace osu.Game.Database /// /// Selects paths to import from. /// - protected abstract IEnumerable GetStableImportPaths(); + protected virtual IEnumerable GetStableImportPaths(Storage stableStoage) => stableStoage.GetDirectories(ImportFromStablePath); /// /// Whether this specified path should be removed after successful import. @@ -530,7 +530,7 @@ namespace osu.Game.Database return Task.CompletedTask; } - return Task.Run(async () => await Import(GetStableImportPaths().Select(f => stable.GetFullPath(f)).ToArray())); + return Task.Run(async () => await Import(GetStableImportPaths(GetStableStorage()).Select(f => stable.GetFullPath(f)).ToArray())); } #endregion diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 4c7de90d6f..770b56e1fe 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -56,9 +56,8 @@ namespace osu.Game.Scoring } } - protected override IEnumerable GetStableImportPaths() - => GetStableStorage().GetFiles(ImportFromStablePath) - .Where(p => HandledExtensions.Any(ext => Path.GetExtension(p)?.Equals(ext, StringComparison.InvariantCultureIgnoreCase) ?? false)); + protected override IEnumerable GetStableImportPaths(Storage stableStorage) + => stableStorage.GetFiles(ImportFromStablePath).Where(p => HandledExtensions.Any(ext => Path.GetExtension(p)?.Equals(ext, StringComparison.InvariantCultureIgnoreCase) ?? false)); public Score GetScore(ScoreInfo score) => new LegacyDatabasedScore(score, rulesets, beatmaps(), Files.Store); diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 3509263e6f..70abfac501 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -55,8 +55,6 @@ namespace osu.Game.Skinning }; } - protected override IEnumerable GetStableImportPaths() => GetStableStorage().GetDirectories(ImportFromStablePath); - protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osk"; /// From 99da04527d5ca2b2e130acce60d1f5a1f831413d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jul 2019 14:07:14 +0900 Subject: [PATCH 1589/2854] Replays -> scores --- .../Overlays/Settings/Sections/Maintenance/GeneralSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index e3f5acc8ac..832673703b 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { Add(importScoresButton = new SettingsButton { - Text = "Import replays from stable", + Text = "Import scores from stable", Action = () => { importScoresButton.Enabled.Value = false; From 87c8fd0035ec6fa0b7cf37dbe10f674f695127ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jul 2019 14:15:29 +0900 Subject: [PATCH 1590/2854] Fix path specification not being cross-platform compliant --- osu.Game/Scoring/ScoreManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 770b56e1fe..d78e9e073e 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -25,7 +25,7 @@ namespace osu.Game.Scoring protected override string[] HashableFileTypes => new[] { ".osr" }; - protected override string ImportFromStablePath => @"Data\r"; + protected override string ImportFromStablePath => Path.Combine("Data", "r"); private readonly RulesetStore rulesets; private readonly Func beatmaps; From 80d8ce83928a09042fd52c5d985fc051d836445f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jul 2019 14:21:56 +0900 Subject: [PATCH 1591/2854] Fix GetStableImportPaths xmldoc --- osu.Game/Database/ArchiveModelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 445c8feb3b..355411063d 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -499,7 +499,7 @@ namespace osu.Game.Database protected virtual string ImportFromStablePath => null; /// - /// Selects paths to import from. + /// Select paths to import from stable. Default implementation iterates all directories in . /// protected virtual IEnumerable GetStableImportPaths(Storage stableStoage) => stableStoage.GetDirectories(ImportFromStablePath); From adf6d6c942b2c32c4522458c0023fb89da32915c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jul 2019 14:25:52 +0900 Subject: [PATCH 1592/2854] Update initial run import process to include scores --- osu.Game/Screens/Select/ImportFromStablePopup.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/ImportFromStablePopup.cs b/osu.Game/Screens/Select/ImportFromStablePopup.cs index 54e4c096f6..20494829ae 100644 --- a/osu.Game/Screens/Select/ImportFromStablePopup.cs +++ b/osu.Game/Screens/Select/ImportFromStablePopup.cs @@ -12,7 +12,7 @@ namespace osu.Game.Screens.Select public ImportFromStablePopup(Action importFromStable) { HeaderText = @"You have no beatmaps!"; - BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps (and skins)?"; + BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps, skins and scores?"; Icon = FontAwesome.Solid.Plane; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 3581ed5534..bf5857f725 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -35,6 +35,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; +using osu.Game.Scoring; namespace osu.Game.Screens.Select { @@ -215,7 +216,7 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader(true)] - private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins) + private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, ScoreManager scores) { mods.BindTo(Mods); @@ -252,7 +253,7 @@ namespace osu.Game.Screens.Select if (!beatmaps.GetAllUsableBeatmapSets().Any() && beatmaps.StableInstallationAvailable) dialogOverlay.Push(new ImportFromStablePopup(() => { - Task.Run(beatmaps.ImportFromStableAsync); + Task.Run(beatmaps.ImportFromStableAsync).ContinueWith(_ => scores.ImportFromStableAsync(), TaskContinuationOptions.OnlyOnRanToCompletion); Task.Run(skins.ImportFromStableAsync); })); }); From df7d31350c8fe8e22478d88f5a57da0b83d88a30 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jul 2019 14:47:55 +0900 Subject: [PATCH 1593/2854] Stop import failures from being added to the imported model list --- osu.Game/Database/ArchiveModelManager.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 01455e7d50..7d81b95203 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -114,7 +114,8 @@ namespace osu.Game.Database lock (imported) { - imported.Add(model); + if (model != null) + imported.Add(model); current++; notification.Text = $"Imported {current} of {paths.Length} {HumanisedModelName}s"; @@ -140,7 +141,7 @@ namespace osu.Game.Database { notification.CompletionText = imported.Count == 1 ? $"Imported {imported.First()}!" - : $"Imported {current} {HumanisedModelName}s!"; + : $"Imported {imported.Count} {HumanisedModelName}s!"; if (imported.Count > 0 && PresentImport != null) { From 3d12c709a53bf0d8343797c327daf8296644f15d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 5 Jul 2019 15:40:47 +0930 Subject: [PATCH 1594/2854] Add test case --- .../OsuDifficultyCalculatorTest.cs | 1 + .../Testing/Beatmaps/zero-length-sliders.osu | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/zero-length-sliders.osu diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index e55dc1f902..693faee3b7 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Osu.Tests protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; [TestCase(6.931145117263422, "diffcalc-test")] + [TestCase(1.0736587013228804d, "zero-length-sliders")] public void Test(double expected, string name) => base.Test(expected, name); diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/zero-length-sliders.osu b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/zero-length-sliders.osu new file mode 100644 index 0000000000..18736043b5 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/zero-length-sliders.osu @@ -0,0 +1,28 @@ +osu file format v14 + +[Difficulty] +HPDrainRate:3 +CircleSize:3 +OverallDifficulty:3 +ApproachRate:4.5 +SliderMultiplier:0.799999999999999 +SliderTickRate:1 + +[TimingPoints] +800,260.869565217391,3,2,10,60,1,0 + +[HitObjects] +// Linear +78,193,2365,2,0,L|330:193,1,0 +78,193,3669,2,0,L|330:193,1,0 +78,193,4973,2,0,L|330:193,1,0 + +// Perfect-curve +151,206,6278,2,0,P|293:75|345:204,1,0 +151,206,8104,2,0,P|293:75|345:204,1,0 +151,206,9930,2,0,P|293:75|345:204,1,0 + +// Bezier +76,191,11756,2,0,B|176:59|358:340|438:190,1,0 +76,191,13582,2,0,B|176:59|358:340|438:190,1,0 +76,191,15408,2,0,B|176:59|358:340|438:190,1,0 From b902457f8dbe8609b679cdbdfce50cfba3d113d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jul 2019 15:32:07 +0900 Subject: [PATCH 1595/2854] Allow PlayerLoader to proceed even if the mouse is hovering an overlay panel --- osu.Game/Screens/Play/PlayerLoader.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 681ce701d0..5396321160 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -18,6 +18,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Input; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Menu; using osu.Game.Screens.Play.HUD; @@ -53,6 +54,8 @@ namespace osu.Game.Screens.Play private InputManager inputManager; + private IdleTracker idleTracker; + public PlayerLoader(Func createPlayer) { this.createPlayer = createPlayer; @@ -93,7 +96,8 @@ namespace osu.Game.Screens.Play VisualSettings = new VisualSettings(), new InputSettings() } - } + }, + idleTracker = new IdleTracker(750) }); loadNewPlayer(); @@ -193,7 +197,7 @@ namespace osu.Game.Screens.Play // Here because IsHovered will not update unless we do so. public override bool HandlePositionalInput => true; - private bool readyForPush => player.LoadState == LoadState.Ready && IsHovered && GetContainingInputManager()?.DraggedDrawable == null; + private bool readyForPush => player.LoadState == LoadState.Ready && (IsHovered || idleTracker.IsIdle.Value) && inputManager?.DraggedDrawable == null; private void pushWhenLoaded() { From 39bd5e6478daefc5369c34c89ba30bceab73716c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jul 2019 15:50:31 +0900 Subject: [PATCH 1596/2854] Add test --- .../Visual/Gameplay/TestScenePlayerLoader.cs | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index daee3a520c..18edaf1b92 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -9,6 +9,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.MathUtils; using osu.Framework.Screens; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; @@ -16,12 +17,13 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens; using osu.Game.Screens.Play; +using osu.Game.Screens.Play.PlayerSettings; namespace osu.Game.Tests.Visual.Gameplay { public class TestScenePlayerLoader : ManualInputManagerTestScene { - private PlayerLoader loader; + private TestPlayerLoader loader; private OsuScreenStack stack; [SetUp] @@ -31,19 +33,28 @@ namespace osu.Game.Tests.Visual.Gameplay Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); }); + [Test] + public void TestBlockLoadViaMouseMovement() + { + AddStep("load dummy beatmap", () => stack.Push(loader = new TestPlayerLoader(() => new TestPlayer(false, false)))); + AddRepeatStep("move mouse", () => InputManager.MoveMouseTo(loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft + (loader.VisualSettings.ScreenSpaceDrawQuad.BottomRight - loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft) * RNG.NextSingle()), 20); + AddAssert("loader still active", () => loader.IsCurrentScreen()); + AddUntilStep("loads after idle", () => !loader.IsCurrentScreen()); + } + [Test] public void TestLoadContinuation() { Player player = null; SlowLoadPlayer slowPlayer = null; - AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => player = new TestPlayer(false, false)))); + AddStep("load dummy beatmap", () => stack.Push(loader = new TestPlayerLoader(() => player = new TestPlayer(false, false)))); AddUntilStep("wait for current", () => loader.IsCurrentScreen()); AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre)); AddUntilStep("wait for player to be current", () => player.IsCurrentScreen()); AddStep("load slow dummy beatmap", () => { - stack.Push(loader = new PlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false))); + stack.Push(loader = new TestPlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false))); Scheduler.AddDelayed(() => slowPlayer.AllowLoad.Set(), 5000); }); @@ -61,7 +72,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("load player", () => { Mods.Value = new[] { gameMod = new TestMod() }; - stack.Push(loader = new PlayerLoader(() => player = new TestPlayer())); + stack.Push(loader = new TestPlayerLoader(() => player = new TestPlayer())); }); AddUntilStep("wait for loader to become current", () => loader.IsCurrentScreen()); @@ -85,6 +96,16 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("player mods applied", () => playerMod2.Applied); } + private class TestPlayerLoader : PlayerLoader + { + public new VisualSettings VisualSettings => base.VisualSettings; + + public TestPlayerLoader(Func createPlayer) + : base(createPlayer) + { + } + } + private class TestMod : Mod, IApplicableToScoreProcessor { public override string Name => string.Empty; From a259247a98fa40d4beb3db268032225533df2e34 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 5 Jul 2019 16:07:17 +0900 Subject: [PATCH 1597/2854] use const --- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 87c009c437..1a3e1213b4 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -332,7 +332,7 @@ namespace osu.Game.Screens.Menu break; case ButtonSystemState.EnteringMode: - logoTrackingContainer.StartTracking(logo, lastState == ButtonSystemState.Initial ? 400 : 0, Easing.InSine); + logoTrackingContainer.StartTracking(logo, lastState == ButtonSystemState.Initial ? MainMenu.FADE_OUT_DURATION : 0, Easing.InSine); break; } } From 6288cc6e5c32fcc8790a9a31bf1b22f64292b11a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 5 Jul 2019 16:03:22 +0200 Subject: [PATCH 1598/2854] Refactor OnScreenDisplay to allow displaying of custom messages on OSD --- osu.Game/Overlays/OSD/OsdToast.cs | 40 ++++++ osu.Game/Overlays/OnScreenDisplay.cs | 203 ++------------------------- osu.Game/osu.Game.csproj | 3 + 3 files changed, 55 insertions(+), 191 deletions(-) create mode 100644 osu.Game/Overlays/OSD/OsdToast.cs diff --git a/osu.Game/Overlays/OSD/OsdToast.cs b/osu.Game/Overlays/OSD/OsdToast.cs new file mode 100644 index 0000000000..f700577ba1 --- /dev/null +++ b/osu.Game/Overlays/OSD/OsdToast.cs @@ -0,0 +1,40 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osuTK.Graphics; + +namespace osu.Game.Overlays.OSD +{ + public class OsdToast : Container + { + private readonly Container content; + protected override Container Content => content; + + public OsdToast() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + Width = 240; + RelativeSizeAxes = Axes.Y; + + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.7f + }, + content = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + } + }; + } + } +} diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs index 88a1edddc5..0577257080 100644 --- a/osu.Game/Overlays/OnScreenDisplay.cs +++ b/osu.Game/Overlays/OnScreenDisplay.cs @@ -8,17 +8,11 @@ using osu.Framework.Configuration; using osu.Framework.Configuration.Tracking; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; using osuTK; -using osuTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Transforms; using osu.Framework.Threading; using osu.Game.Configuration; -using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.OSD; namespace osu.Game.Overlays { @@ -26,16 +20,9 @@ namespace osu.Game.Overlays { private readonly Container box; - private readonly SpriteText textLine1; - private readonly SpriteText textLine2; - private readonly SpriteText textLine3; - private const float height = 110; - private const float height_notext = 98; private const float height_contracted = height * 0.9f; - private readonly FillFlowContainer optionLights; - public OnScreenDisplay() { RelativeSizeAxes = Axes.Both; @@ -52,64 +39,6 @@ namespace osu.Game.Overlays Height = height_contracted, Alpha = 0, CornerRadius = 20, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.7f, - }, - new Container // purely to add a minimum width - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 240, - RelativeSizeAxes = Axes.Y, - }, - textLine1 = new OsuSpriteText - { - Padding = new MarginPadding(10), - Font = OsuFont.GetFont(size: 14, weight: FontWeight.Black), - Spacing = new Vector2(1, 0), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }, - textLine2 = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 24, weight: FontWeight.Light), - Padding = new MarginPadding { Left = 10, Right = 10 }, - Anchor = Anchor.Centre, - Origin = Anchor.BottomCentre, - }, - new FillFlowContainer - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - optionLights = new FillFlowContainer - { - Padding = new MarginPadding { Top = 20, Bottom = 5 }, - Spacing = new Vector2(5, 0), - Direction = FillDirection.Horizontal, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both - }, - textLine3 = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Margin = new MarginPadding { Bottom = 15 }, - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), - Alpha = 0.3f, - }, - } - } - } }, }; } @@ -142,7 +71,7 @@ namespace osu.Game.Overlays return; configManager.LoadInto(trackedSettings); - trackedSettings.SettingChanged += display; + trackedSettings.SettingChanged += displayTrackedSettingChange; trackedConfigManagers.Add((source, configManager), trackedSettings); } @@ -162,56 +91,23 @@ namespace osu.Game.Overlays return; existing.Unload(); - existing.SettingChanged -= display; + existing.SettingChanged -= displayTrackedSettingChange; trackedConfigManagers.Remove((source, configManager)); } - private void display(SettingDescription description) + /// + /// Displays the given as parameter on the OSD + /// + /// + public void Display(OsdToast toast) { - Schedule(() => - { - textLine1.Text = description.Name.ToUpperInvariant(); - textLine2.Text = description.Value; - textLine3.Text = description.Shortcut.ToUpperInvariant(); - - if (string.IsNullOrEmpty(textLine3.Text)) - textLine3.Text = "NO KEY BOUND"; - - DisplayTemporarily(box); - - int optionCount = 0; - int selectedOption = -1; - - switch (description.RawValue) - { - case bool val: - optionCount = 1; - if (val) selectedOption = 0; - break; - - case Enum _: - var values = Enum.GetValues(description.RawValue.GetType()); - optionCount = values.Length; - selectedOption = Convert.ToInt32(description.RawValue); - break; - } - - textLine2.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre; - textLine2.Y = optionCount > 0 ? 0 : 5; - - if (optionLights.Children.Count != optionCount) - { - optionLights.Clear(); - for (int i = 0; i < optionCount; i++) - optionLights.Add(new OptionLight()); - } - - for (int i = 0; i < optionCount; i++) - optionLights.Children[i].Glowing = i == selectedOption; - }); + box.Child = toast; + DisplayTemporarily(box); } + private void displayTrackedSettingChange(SettingDescription description) => Schedule(() => Display(new OsdTrackedSettingToast(description))); + private TransformSequence fadeIn; private ScheduledDelegate fadeOut; @@ -236,80 +132,5 @@ namespace osu.Game.Overlays b => b.ResizeHeightTo(height_contracted, 1500, Easing.InQuint)); }, 500); } - - private class OptionLight : Container - { - private Color4 glowingColour, idleColour; - - private const float transition_speed = 300; - - private const float glow_strength = 0.4f; - - private readonly Box fill; - - public OptionLight() - { - Children = new[] - { - fill = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 1, - }, - }; - } - - private bool glowing; - - public bool Glowing - { - set - { - glowing = value; - if (!IsLoaded) return; - - updateGlow(); - } - } - - private void updateGlow() - { - if (glowing) - { - fill.FadeColour(glowingColour, transition_speed, Easing.OutQuint); - FadeEdgeEffectTo(glow_strength, transition_speed, Easing.OutQuint); - } - else - { - FadeEdgeEffectTo(0, transition_speed, Easing.OutQuint); - fill.FadeColour(idleColour, transition_speed, Easing.OutQuint); - } - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - fill.Colour = idleColour = Color4.White.Opacity(0.4f); - glowingColour = Color4.White; - - Size = new Vector2(25, 5); - - Masking = true; - CornerRadius = 3; - - EdgeEffect = new EdgeEffectParameters - { - Colour = colours.BlueDark.Opacity(glow_strength), - Type = EdgeEffectType.Glow, - Radius = 8, - }; - } - - protected override void LoadComplete() - { - updateGlow(); - FinishTransforms(true); - } - } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e872cd1387..950a940fec 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -21,4 +21,7 @@ + + + From 4f0429d046da8d5ca21d43d26c40a1773febdd4b Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 5 Jul 2019 16:03:46 +0200 Subject: [PATCH 1599/2854] Add OsdTrackedSettingToast --- .../Overlays/OSD/OsdTrackedSettingToast.cs | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 osu.Game/Overlays/OSD/OsdTrackedSettingToast.cs diff --git a/osu.Game/Overlays/OSD/OsdTrackedSettingToast.cs b/osu.Game/Overlays/OSD/OsdTrackedSettingToast.cs new file mode 100644 index 0000000000..411a33b000 --- /dev/null +++ b/osu.Game/Overlays/OSD/OsdTrackedSettingToast.cs @@ -0,0 +1,180 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Configuration.Tracking; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; +using System; + +namespace osu.Game.Overlays.OSD +{ + public class OsdTrackedSettingToast : OsdToast + { + public OsdTrackedSettingToast(SettingDescription description) + { + SpriteText textLine2; + FillFlowContainer optionLights; + + Children = new Drawable[] + { + new OsuSpriteText + { + Padding = new MarginPadding(10), + Font = OsuFont.GetFont(size: 14, weight: FontWeight.Black), + Spacing = new Vector2(1, 0), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = description.Name.ToUpperInvariant() + }, + textLine2 = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 24, weight: FontWeight.Light), + Padding = new MarginPadding { Left = 10, Right = 10 }, + Anchor = Anchor.Centre, + Origin = Anchor.BottomCentre, + Text = description.Value + }, + new FillFlowContainer + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + optionLights = new FillFlowContainer + { + Padding = new MarginPadding { Top = 20, Bottom = 5 }, + Spacing = new Vector2(5, 0), + Direction = FillDirection.Horizontal, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding { Bottom = 15 }, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), + Alpha = 0.3f, + Text = string.IsNullOrEmpty(description.Shortcut) ? "NO KEY BOUND" : description.Shortcut.ToUpperInvariant() + }, + } + } + }; + + int optionCount = 0; + int selectedOption = -1; + + switch (description.RawValue) + { + case bool val: + optionCount = 1; + if (val) selectedOption = 0; + break; + + case Enum _: + var values = Enum.GetValues(description.RawValue.GetType()); + optionCount = values.Length; + selectedOption = Convert.ToInt32(description.RawValue); + break; + } + + textLine2.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre; + textLine2.Y = optionCount > 0 ? 0 : 5; + + for (int i = 0; i < optionCount; i++) + { + optionLights.Add(new OptionLight + { + Glowing = i == selectedOption + }); + } + } + + private class OptionLight : Container + { + private Color4 glowingColour, idleColour; + + private const float transition_speed = 300; + + private const float glow_strength = 0.4f; + + private readonly Box fill; + + public OptionLight() + { + Children = new[] + { + fill = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 1, + }, + }; + } + + private bool glowing; + + public bool Glowing + { + set + { + glowing = value; + if (!IsLoaded) return; + + updateGlow(); + } + } + + private void updateGlow() + { + if (glowing) + { + fill.FadeColour(glowingColour, transition_speed, Easing.OutQuint); + FadeEdgeEffectTo(glow_strength, transition_speed, Easing.OutQuint); + } + else + { + FadeEdgeEffectTo(0, transition_speed, Easing.OutQuint); + fill.FadeColour(idleColour, transition_speed, Easing.OutQuint); + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + fill.Colour = idleColour = Color4.White.Opacity(0.4f); + glowingColour = Color4.White; + + Size = new Vector2(25, 5); + + Masking = true; + CornerRadius = 3; + + EdgeEffect = new EdgeEffectParameters + { + Colour = colours.BlueDark.Opacity(glow_strength), + Type = EdgeEffectType.Glow, + Radius = 8, + }; + } + + protected override void LoadComplete() + { + updateGlow(); + FinishTransforms(true); + } + } + } +} From bc60b1dc13d2b069518d3b4b4d1698c43aa48ab9 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 5 Jul 2019 16:04:47 +0200 Subject: [PATCH 1600/2854] Add test for empty OsdToast --- osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs index d900526c07..b28f794a58 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs @@ -22,6 +22,7 @@ namespace osu.Game.Tests.Visual.UserInterface osd.BeginTracking(this, config); Add(osd); + AddStep("Display empty osd toast", () => osd.Display(new Overlays.OSD.OsdToast())); AddRepeatStep("Change toggle (no bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingNoKeybind), 2); AddRepeatStep("Change toggle (with bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingWithKeybind), 2); AddRepeatStep("Change enum (no bind)", () => config.IncrementEnumSetting(TestConfigSetting.EnumSettingNoKeybind), 3); From df75f9d3124f169e2d7950fd317af11c5b481b1a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 5 Jul 2019 16:08:02 +0200 Subject: [PATCH 1601/2854] Revert changes made by VS to csproj --- osu.Game/osu.Game.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 950a940fec..e872cd1387 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -21,7 +21,4 @@ - - - From 70372dd03d1e1735de241d431b629bdb684ae9c1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 5 Jul 2019 16:14:04 +0200 Subject: [PATCH 1602/2854] Add global actions for game-wide music jukebox manipulation --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 14d356f889..6d85a69804 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -38,6 +38,10 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.MouseWheelDown, GlobalAction.DecreaseVolume), new KeyBinding(InputKey.F4, GlobalAction.ToggleMute), + new KeyBinding(InputKey.F5, GlobalAction.MusicPrev), + new KeyBinding(InputKey.F6, GlobalAction.MusicNext), + new KeyBinding(InputKey.X, GlobalAction.MusicPlay), + new KeyBinding(InputKey.Escape, GlobalAction.Back), new KeyBinding(InputKey.MouseButton1, GlobalAction.Back), @@ -88,6 +92,16 @@ namespace osu.Game.Input.Bindings [Description("Toggle mute")] ToggleMute, + // Game-wide beatmap jukebox keybindings + [Description("Jukebox next track")] + MusicNext, + + [Description("Jukebox previous track")] + MusicPrev, + + [Description("Jukebox play / pause current track")] + MusicPlay, + // In-Game Keybindings [Description("Skip cutscene")] SkipCutscene, From b5bd863dd080d25f0c01f9cdd30c0d6cde4a9c5d Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 5 Jul 2019 16:21:50 +0200 Subject: [PATCH 1603/2854] Add methods to MusicController to play or pause, select next or previous track --- osu.Game/Overlays/MusicController.cs | 31 +++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 29ae5983be..91fa2f5a95 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -529,6 +529,35 @@ namespace osu.Game.Overlays /// /// Play the next random or playlist track. /// - public void NextTrack() => next(); + /// Returns whether the track could be changed or not + public bool NextTrack() + { + if (beatmap.Disabled) return false; + + next(); + return true; + } + + /// + /// Play the previous random or playlist track. + /// + public bool PreviousTrack() + { + if (beatmap.Disabled) return false; + + prev(); + return true; + } + + /// + /// Play or pause the current beatmap track. + /// Returns whether the current track could be played / paused or not + public bool PlayTrack() + { + if (beatmap.Disabled) return false; + + play(); + return true; + } } } From c9e44e5e34aa481ce0210898616829c1770a67c0 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 5 Jul 2019 16:25:09 +0200 Subject: [PATCH 1604/2854] Add OsdIconToast + test --- .../UserInterface/TestSceneOnScreenDisplay.cs | 2 + osu.Game/Overlays/OSD/OsdIconToast.cs | 42 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 osu.Game/Overlays/OSD/OsdIconToast.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs index b28f794a58..047c89ae54 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Configuration.Tracking; using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; using osu.Game.Overlays; namespace osu.Game.Tests.Visual.UserInterface @@ -23,6 +24,7 @@ namespace osu.Game.Tests.Visual.UserInterface Add(osd); AddStep("Display empty osd toast", () => osd.Display(new Overlays.OSD.OsdToast())); + AddStep("Display osd toast with icon and message", () => osd.Display(new Overlays.OSD.OsdIconToast("Hey there !", FontAwesome.Solid.HandSpock))); AddRepeatStep("Change toggle (no bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingNoKeybind), 2); AddRepeatStep("Change toggle (with bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingWithKeybind), 2); AddRepeatStep("Change enum (no bind)", () => config.IncrementEnumSetting(TestConfigSetting.EnumSettingNoKeybind), 3); diff --git a/osu.Game/Overlays/OSD/OsdIconToast.cs b/osu.Game/Overlays/OSD/OsdIconToast.cs new file mode 100644 index 0000000000..c00949d48b --- /dev/null +++ b/osu.Game/Overlays/OSD/OsdIconToast.cs @@ -0,0 +1,42 @@ +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Overlays.OSD +{ + public class OsdIconToast : OsdToast + { + public OsdIconToast(string message, IconUsage icon) + { + Children = new Drawable[] + { + new FillFlowContainer() + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Vertical, + Spacing = new osuTK.Vector2(10), + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.BottomCentre, + Font = OsuFont.GetFont(size: 24, weight: FontWeight.Light), + Text = message + }, + new SpriteIcon + { + Icon = icon, + Size = new osuTK.Vector2(45), + Anchor = Anchor.Centre, + Origin = Anchor.BottomCentre, + } + } + } + }; + } + } +} From 5cad2b21927199dd4baf026c5d27baf85c29c43b Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 5 Jul 2019 16:28:37 +0200 Subject: [PATCH 1605/2854] Add missing function to MusicController --- osu.Game/Overlays/MusicController.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 91fa2f5a95..f6d67e9981 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -559,5 +559,10 @@ namespace osu.Game.Overlays play(); return true; } + + /// + /// Returns whether the current beatmap track is playing. + /// + public bool IsPlaying => beatmap.Value.Track.IsRunning; } } From 89acd9da3e0c9e5aa55d1bdc2647c5349ec1a556 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 5 Jul 2019 16:33:13 +0200 Subject: [PATCH 1606/2854] Add game-wide jukebox keybindings handling to OsuGame --- osu.Game/OsuGame.cs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 0a472d4dc1..32539055a7 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -67,6 +67,8 @@ namespace osu.Game private BeatmapSetOverlay beatmapSetOverlay; + private OnScreenDisplay osd; + [Cached] private readonly ScreenshotManager screenshotManager = new ScreenshotManager(); @@ -456,7 +458,7 @@ namespace osu.Game }); loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add); - loadComponentSingleFile(new OnScreenDisplay(), Add, true); + loadComponentSingleFile(osd = new OnScreenDisplay(), Add, true); loadComponentSingleFile(notifications = new NotificationOverlay { @@ -719,6 +721,24 @@ namespace osu.Game case GlobalAction.ToggleGameplayMouseButtons: LocalConfig.Set(OsuSetting.MouseDisableButtons, !LocalConfig.Get(OsuSetting.MouseDisableButtons)); return true; + + case GlobalAction.MusicPlay: + if (!musicController.IsLoaded) return true; + if (musicController.PlayTrack()) + osd.Display(new Overlays.OSD.OsdIconToast(musicController.IsPlaying ? "Play track" : "Pause track", musicController.IsPlaying ? FontAwesome.Solid.PlayCircle : FontAwesome.Solid.PauseCircle)); + return true; + + case GlobalAction.MusicNext: + if (!musicController.IsLoaded) return true; + if (musicController.NextTrack()) + osd.Display(new Overlays.OSD.OsdIconToast("Next track", FontAwesome.Solid.FastForward)); + return true; + + case GlobalAction.MusicPrev: + if (!musicController.IsLoaded) return true; + if (musicController.PreviousTrack()) + osd.Display(new Overlays.OSD.OsdIconToast("Previous track", FontAwesome.Solid.FastForward)); + return true; } return false; From 5f8bd6eca7a7e0f43b890ef1726da86d41cb0c82 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 5 Jul 2019 16:37:37 +0200 Subject: [PATCH 1607/2854] Fix CI issues + minor issues --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 8 ++++---- osu.Game/OsuGame.cs | 5 ++++- osu.Game/Overlays/MusicController.cs | 1 + osu.Game/Overlays/OSD/OsdIconToast.cs | 7 +++++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 6d85a69804..e756694285 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -38,16 +38,16 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.MouseWheelDown, GlobalAction.DecreaseVolume), new KeyBinding(InputKey.F4, GlobalAction.ToggleMute), - new KeyBinding(InputKey.F5, GlobalAction.MusicPrev), - new KeyBinding(InputKey.F6, GlobalAction.MusicNext), - new KeyBinding(InputKey.X, GlobalAction.MusicPlay), - new KeyBinding(InputKey.Escape, GlobalAction.Back), new KeyBinding(InputKey.MouseButton1, GlobalAction.Back), new KeyBinding(InputKey.Space, GlobalAction.Select), new KeyBinding(InputKey.Enter, GlobalAction.Select), new KeyBinding(InputKey.KeypadEnter, GlobalAction.Select), + + new KeyBinding(InputKey.F5, GlobalAction.MusicPrev), + new KeyBinding(InputKey.F6, GlobalAction.MusicNext), + new KeyBinding(InputKey.X, GlobalAction.MusicPlay), }; public IEnumerable InGameKeyBindings => new[] diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 32539055a7..bae301a8a6 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -724,20 +724,23 @@ namespace osu.Game case GlobalAction.MusicPlay: if (!musicController.IsLoaded) return true; + if (musicController.PlayTrack()) osd.Display(new Overlays.OSD.OsdIconToast(musicController.IsPlaying ? "Play track" : "Pause track", musicController.IsPlaying ? FontAwesome.Solid.PlayCircle : FontAwesome.Solid.PauseCircle)); return true; case GlobalAction.MusicNext: if (!musicController.IsLoaded) return true; + if (musicController.NextTrack()) osd.Display(new Overlays.OSD.OsdIconToast("Next track", FontAwesome.Solid.FastForward)); return true; case GlobalAction.MusicPrev: if (!musicController.IsLoaded) return true; + if (musicController.PreviousTrack()) - osd.Display(new Overlays.OSD.OsdIconToast("Previous track", FontAwesome.Solid.FastForward)); + osd.Display(new Overlays.OSD.OsdIconToast("Previous track", FontAwesome.Solid.FastBackward)); return true; } diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index f6d67e9981..618187069d 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -551,6 +551,7 @@ namespace osu.Game.Overlays /// /// Play or pause the current beatmap track. + /// /// Returns whether the current track could be played / paused or not public bool PlayTrack() { diff --git a/osu.Game/Overlays/OSD/OsdIconToast.cs b/osu.Game/Overlays/OSD/OsdIconToast.cs index c00949d48b..0e2bcd377f 100644 --- a/osu.Game/Overlays/OSD/OsdIconToast.cs +++ b/osu.Game/Overlays/OSD/OsdIconToast.cs @@ -1,4 +1,7 @@ -using osu.Framework.Graphics; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; @@ -12,7 +15,7 @@ namespace osu.Game.Overlays.OSD { Children = new Drawable[] { - new FillFlowContainer() + new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 71e40b46843e69d9bba072cbf6eb7aeede9a3d46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 6 Jul 2019 12:32:16 +0900 Subject: [PATCH 1608/2854] Force SQLite to multithreading mode --- osu.Game/Database/OsuDbContext.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 538ec41b3d..ea3318598f 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -41,6 +41,9 @@ namespace osu.Game.Database { // required to initialise native SQLite libraries on some platforms. SQLitePCL.Batteries_V2.Init(); + + // https://github.com/aspnet/EntityFrameworkCore/issues/9994#issuecomment-508588678 + SQLitePCL.raw.sqlite3_config(2 /*SQLITE_CONFIG_MULTITHREAD*/); } /// From 530e07110f0425aed5d7c3c56eed920c03e80483 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 6 Jul 2019 06:32:25 +0300 Subject: [PATCH 1609/2854] Use a descriptive name for the setting --- osu.Game/Configuration/OsuConfigManager.cs | 4 ++-- .../Overlays/Settings/Sections/Gameplay/GeneralSettings.cs | 2 +- osu.Game/Rulesets/Mods/ModBlockFail.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index b5097e95c8..c56a5cc6e4 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -77,7 +77,7 @@ namespace osu.Game.Configuration Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01); Set(OsuSetting.ShowInterface, true); - Set(OsuSetting.HideHealthBar, true); + Set(OsuSetting.HideHealthBarWhenCantFail, true); Set(OsuSetting.KeyOverlay, false); Set(OsuSetting.FloatingComments, false); @@ -132,7 +132,7 @@ namespace osu.Game.Configuration KeyOverlay, FloatingComments, ShowInterface, - HideHealthBar, + HideHealthBarWhenCantFail, MouseDisableButtons, MouseDisableWheel, AudioOffset, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 2d208c5f71..7aeefe959f 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay new SettingsCheckbox { LabelText = "Hide health bar if you can't fail", - Bindable = config.GetBindable(OsuSetting.HideHealthBar), + Bindable = config.GetBindable(OsuSetting.HideHealthBarWhenCantFail), }, new SettingsCheckbox { diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs index e37b1b0698..de509a7ea7 100644 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mods public void ReadFromConfig(OsuConfigManager config) { - hideHealthBar = config.GetBindable(OsuSetting.HideHealthBar); + hideHealthBar = config.GetBindable(OsuSetting.HideHealthBarWhenCantFail); } public void ApplyToHUD(HUDOverlay overlay) From 64de3840b0f84f805077d7434b30e4295372e922 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 6 Jul 2019 15:25:53 +0900 Subject: [PATCH 1610/2854] Add missing wait step in TestScenePlayerLoader --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 18edaf1b92..ab519360ac 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -37,6 +37,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestBlockLoadViaMouseMovement() { AddStep("load dummy beatmap", () => stack.Push(loader = new TestPlayerLoader(() => new TestPlayer(false, false)))); + AddUntilStep("wait for current", () => loader.IsCurrentScreen()); AddRepeatStep("move mouse", () => InputManager.MoveMouseTo(loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft + (loader.VisualSettings.ScreenSpaceDrawQuad.BottomRight - loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft) * RNG.NextSingle()), 20); AddAssert("loader still active", () => loader.IsCurrentScreen()); AddUntilStep("loads after idle", () => !loader.IsCurrentScreen()); From 6fd3ad5c1dac275e6a8112f4ae8af31f64ee99f4 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 6 Jul 2019 12:10:30 +0300 Subject: [PATCH 1611/2854] Remove unnecessary fading --- .../Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs index 1fd3502799..30346a8a96 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs @@ -35,15 +35,15 @@ namespace osu.Game.Beatmaps.Drawables protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func createContentFunc, double timeBeforeLoad) => new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad, UnloadDelay); + protected override double TransformDuration => 400; + protected override Drawable CreateDrawable(BeatmapInfo model) { - Drawable drawable = getDrawableForModel(model); - + var drawable = getDrawableForModel(model); drawable.RelativeSizeAxes = Axes.Both; drawable.Anchor = Anchor.Centre; drawable.Origin = Anchor.Centre; drawable.FillMode = FillMode.Fill; - drawable.OnLoadComplete += d => d.FadeInFromZero(400); return drawable; } From 58183ad3d5b01a9bf49bfa11c43186ad19644424 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 6 Jul 2019 19:05:12 +0900 Subject: [PATCH 1612/2854] Change intro test to test full intro screen --- .../Visual/Menus/TestSceneIntroSequence.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneIntroSequence.cs b/osu.Game.Tests/Visual/Menus/TestSceneIntroSequence.cs index b59fb18428..9cb335c3a3 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneIntroSequence.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneIntroSequence.cs @@ -4,10 +4,12 @@ using System; using System.Collections.Generic; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Timing; +using osu.Game.Screens; using osu.Game.Screens.Menu; using osuTK.Graphics; @@ -19,12 +21,16 @@ namespace osu.Game.Tests.Visual.Menus public override IReadOnlyList RequiredTypes => new[] { typeof(OsuLogo), + typeof(Intro), + typeof(StartupScreen), + typeof(OsuScreen) }; + [Cached] + private OsuLogo logo; + public TestSceneIntroSequence() { - OsuLogo logo; - var rateAdjustClock = new StopwatchClock(true); var framedClock = new FramedClock(rateAdjustClock); framedClock.ProcessFrame(); @@ -40,14 +46,17 @@ namespace osu.Game.Tests.Visual.Menus RelativeSizeAxes = Axes.Both, Colour = Color4.Black, }, + new OsuScreenStack(new Intro()) + { + RelativeSizeAxes = Axes.Both, + }, logo = new OsuLogo { Anchor = Anchor.Centre, - } + }, } }); - AddStep(@"Restart", logo.PlayIntro); AddSliderStep("Playback speed", 0.0, 2.0, 1, v => rateAdjustClock.Rate = v); } } From 6bee26cfc9764bf0db15d168d3a3fa6d4691d507 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 6 Jul 2019 19:05:37 +0900 Subject: [PATCH 1613/2854] Rename to match --- .../Menus/{TestSceneIntroSequence.cs => TestSceneIntro.cs} | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) rename osu.Game.Tests/Visual/Menus/{TestSceneIntroSequence.cs => TestSceneIntro.cs} (93%) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneIntroSequence.cs b/osu.Game.Tests/Visual/Menus/TestSceneIntro.cs similarity index 93% rename from osu.Game.Tests/Visual/Menus/TestSceneIntroSequence.cs rename to osu.Game.Tests/Visual/Menus/TestSceneIntro.cs index 9cb335c3a3..15b5c2338e 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneIntroSequence.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneIntro.cs @@ -16,12 +16,11 @@ using osuTK.Graphics; namespace osu.Game.Tests.Visual.Menus { [TestFixture] - public class TestSceneIntroSequence : OsuTestScene + public class TestSceneIntro : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { typeof(OsuLogo), - typeof(Intro), typeof(StartupScreen), typeof(OsuScreen) }; @@ -29,7 +28,7 @@ namespace osu.Game.Tests.Visual.Menus [Cached] private OsuLogo logo; - public TestSceneIntroSequence() + public TestSceneIntro() { var rateAdjustClock = new StopwatchClock(true); var framedClock = new FramedClock(rateAdjustClock); From ea911b2fd222d8ab9673927abfdd00fd73c0b78a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 6 Jul 2019 19:05:42 +0900 Subject: [PATCH 1614/2854] Ensure intro restarts track --- osu.Game/Screens/Menu/Intro.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index f6fbcf6498..3fb16eaf90 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -101,7 +101,7 @@ namespace osu.Game.Screens.Menu // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu. if (menuMusic.Value) { - track.Start(); + track.Restart(); track = null; } From a42c79895d907351d3764f019f9e6ca475a41760 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 6 Jul 2019 18:38:41 +0300 Subject: [PATCH 1615/2854] Remove unnecessary private field --- osu.Game/Rulesets/Mods/ModBlockFail.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs index de509a7ea7..1eb997ec58 100644 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -5,14 +5,12 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Screens.Play; -using osu.Game.Screens.Play.HUD; namespace osu.Game.Rulesets.Mods { public abstract class ModBlockFail : Mod, IApplicableFailOverride, IApplicableToHUD, IReadFromConfig { private Bindable hideHealthBar; - private HealthDisplay healthDisplay; /// /// We never fail, 'yo. @@ -26,7 +24,6 @@ namespace osu.Game.Rulesets.Mods public void ApplyToHUD(HUDOverlay overlay) { - healthDisplay = overlay.HealthDisplay; hideHealthBar.BindValueChanged(v => healthDisplay.FadeTo(v.NewValue ? 0 : 1, 250, Easing.OutQuint), true); } } From 1f13b94c729302707b59e60cbd84073708e16a28 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 6 Jul 2019 18:44:55 +0300 Subject: [PATCH 1616/2854] Fix build error --- osu.Game/Rulesets/Mods/ModBlockFail.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs index 1eb997ec58..e90b79bb11 100644 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Mods public void ApplyToHUD(HUDOverlay overlay) { - hideHealthBar.BindValueChanged(v => healthDisplay.FadeTo(v.NewValue ? 0 : 1, 250, Easing.OutQuint), true); + hideHealthBar.BindValueChanged(v => overlay.HealthDisplay.FadeTo(v.NewValue ? 0 : 1, 250, Easing.OutQuint), true); } } } From 5767f4360e02cd91dcfdb1e42ce45bd7d3fb840c Mon Sep 17 00:00:00 2001 From: Joehu Date: Sat, 6 Jul 2019 12:07:13 -0700 Subject: [PATCH 1617/2854] Remove unnecessary test checks --- .../Visual/SongSelect/TestSceneLeaderboard.cs | 60 ------------------- 1 file changed, 60 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs index f6164c8a73..9472696295 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs @@ -43,13 +43,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn)); AddStep(@"Unavailable", () => leaderboard.SetRetrievalState(PlaceholderState.Unavailable)); AddStep(@"Ranked beatmap", rankedBeatmap); - AddStep(@"Approved beatmap", approvedBeatmap); - AddStep(@"Qualified beatmap", qualifiedBeatmap); - AddStep(@"Loved beatmap", lovedBeatmap); AddStep(@"Pending beatmap", pendingBeatmap); - AddStep(@"WIP beatmap", wipBeatmap); - AddStep(@"Graveyard beatmap", graveyardBeatmap); - AddStep(@"Unpublished beatmap", unpublishedBeatmap); } private void newScores() @@ -250,33 +244,6 @@ namespace osu.Game.Tests.Visual.SongSelect }; } - private void approvedBeatmap() - { - leaderboard.Beatmap = new BeatmapInfo - { - OnlineBeatmapID = 1113057, - Status = BeatmapSetOnlineStatus.Approved, - }; - } - - private void qualifiedBeatmap() - { - leaderboard.Beatmap = new BeatmapInfo - { - OnlineBeatmapID = 1113057, - Status = BeatmapSetOnlineStatus.Qualified, - }; - } - - private void lovedBeatmap() - { - leaderboard.Beatmap = new BeatmapInfo - { - OnlineBeatmapID = 1113057, - Status = BeatmapSetOnlineStatus.Loved, - }; - } - private void pendingBeatmap() { leaderboard.Beatmap = new BeatmapInfo @@ -286,33 +253,6 @@ namespace osu.Game.Tests.Visual.SongSelect }; } - private void wipBeatmap() - { - leaderboard.Beatmap = new BeatmapInfo - { - OnlineBeatmapID = 1113057, - Status = BeatmapSetOnlineStatus.WIP, - }; - } - - private void graveyardBeatmap() - { - leaderboard.Beatmap = new BeatmapInfo - { - OnlineBeatmapID = 1113057, - Status = BeatmapSetOnlineStatus.Graveyard, - }; - } - - private void unpublishedBeatmap() - { - leaderboard.Beatmap = new BeatmapInfo - { - OnlineBeatmapID = null, - Status = BeatmapSetOnlineStatus.None, - }; - } - private class FailableLeaderboard : BeatmapLeaderboard { public void SetRetrievalState(PlaceholderState state) From bf41fd5d9dd590e7a51301c484db0030293ef41f Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sat, 6 Jul 2019 23:29:35 +0300 Subject: [PATCH 1618/2854] Update package references --- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- .../osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 265ecb7688..9acf47a67c 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index dbade6ff8d..df5131dd8b 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index a99a93c3e9..bb3e5a66f3 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index 216cc0222f..5510c3a9d9 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 11d70ee7be..659f5415c3 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index 1c169184fb..dad2fe0877 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -5,9 +5,9 @@ - + - + WinExe From 84919d70bb37cfcb8cd859e01dac2bcdfc0c9da1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 7 Jul 2019 05:30:30 +0900 Subject: [PATCH 1619/2854] Health bar -> Health display Also inverts logic --- osu.Game/Configuration/OsuConfigManager.cs | 4 ++-- .../Overlays/Settings/Sections/Gameplay/GeneralSettings.cs | 4 ++-- osu.Game/Rulesets/Mods/ModBlockFail.cs | 7 +++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index c56a5cc6e4..1da7c7ec1d 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -77,7 +77,7 @@ namespace osu.Game.Configuration Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01); Set(OsuSetting.ShowInterface, true); - Set(OsuSetting.HideHealthBarWhenCantFail, true); + Set(OsuSetting.ShowHealthDisplayWhenCantFail, true); Set(OsuSetting.KeyOverlay, false); Set(OsuSetting.FloatingComments, false); @@ -132,7 +132,7 @@ namespace osu.Game.Configuration KeyOverlay, FloatingComments, ShowInterface, - HideHealthBarWhenCantFail, + ShowHealthDisplayWhenCantFail, MouseDisableButtons, MouseDisableWheel, AudioOffset, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 7aeefe959f..13a494f51f 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -36,8 +36,8 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay }, new SettingsCheckbox { - LabelText = "Hide health bar if you can't fail", - Bindable = config.GetBindable(OsuSetting.HideHealthBarWhenCantFail), + LabelText = "Show health display even when can't fail", + Bindable = config.GetBindable(OsuSetting.ShowHealthDisplayWhenCantFail), }, new SettingsCheckbox { diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs index e90b79bb11..26efc3932d 100644 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Screens.Play; @@ -10,7 +9,7 @@ namespace osu.Game.Rulesets.Mods { public abstract class ModBlockFail : Mod, IApplicableFailOverride, IApplicableToHUD, IReadFromConfig { - private Bindable hideHealthBar; + private Bindable showHealthBar; /// /// We never fail, 'yo. @@ -19,12 +18,12 @@ namespace osu.Game.Rulesets.Mods public void ReadFromConfig(OsuConfigManager config) { - hideHealthBar = config.GetBindable(OsuSetting.HideHealthBarWhenCantFail); + showHealthBar = config.GetBindable(OsuSetting.ShowHealthDisplayWhenCantFail); } public void ApplyToHUD(HUDOverlay overlay) { - hideHealthBar.BindValueChanged(v => overlay.HealthDisplay.FadeTo(v.NewValue ? 0 : 1, 250, Easing.OutQuint), true); + overlay.ShowHealthbar.BindTo(showHealthBar); } } } From 8f2ec736262e3c69ffd4ba35479eb0044539edca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 7 Jul 2019 05:30:53 +0900 Subject: [PATCH 1620/2854] Move logic inside of HUDOverlay Add vertical offset adjust. --- osu.Game/Screens/Play/HUDOverlay.cs | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 017bf70133..43b9491750 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -23,7 +23,8 @@ namespace osu.Game.Screens.Play { public class HUDOverlay : Container { - private const int duration = 100; + private const int duration = 250; + private const Easing easing = Easing.OutQuint; public readonly KeyCounterDisplay KeyCounter; public readonly RollingCounter ComboCounter; @@ -35,6 +36,8 @@ namespace osu.Game.Screens.Play public readonly HoldForMenuButton HoldToQuit; public readonly PlayerSettingsOverlay PlayerSettingsOverlay; + public Bindable ShowHealthbar = new Bindable(true); + private readonly ScoreProcessor scoreProcessor; private readonly DrawableRuleset drawableRuleset; private readonly IReadOnlyList mods; @@ -47,6 +50,8 @@ namespace osu.Game.Screens.Play public Action RequestSeek; + private readonly Container topScoreContainer; + public HUDOverlay(ScoreProcessor scoreProcessor, DrawableRuleset drawableRuleset, IReadOnlyList mods) { this.scoreProcessor = scoreProcessor; @@ -62,11 +67,10 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new Container + topScoreContainer = new Container { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Y = 30, AutoSizeAxes = Axes.Both, AutoSizeDuration = 200, AutoSizeEasing = Easing.Out, @@ -113,8 +117,21 @@ namespace osu.Game.Screens.Play ModDisplay.Current.Value = mods; showHud = config.GetBindable(OsuSetting.ShowInterface); - showHud.ValueChanged += visible => visibilityContainer.FadeTo(visible.NewValue ? 1 : 0, duration); - showHud.TriggerChange(); + showHud.BindValueChanged(visible => visibilityContainer.FadeTo(visible.NewValue ? 1 : 0, duration, easing), true); + + ShowHealthbar.BindValueChanged(healthBar => + { + if (healthBar.NewValue) + { + HealthDisplay.FadeIn(duration, easing); + topScoreContainer.MoveToY(30, duration, easing); + } + else + { + HealthDisplay.FadeOut(duration, easing); + topScoreContainer.MoveToY(0, duration, easing); + } + }, true); if (!showHud.Value && !hasShownNotificationOnce) { From b10c35b6ab5c74d10f6c100b0a9fa7e081687dc2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 7 Jul 2019 06:13:27 +0300 Subject: [PATCH 1621/2854] Update label text Co-Authored-By: Aergwyn --- osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 13a494f51f..9142492610 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay }, new SettingsCheckbox { - LabelText = "Show health display even when can't fail", + LabelText = "Show health display even when you can't fail", Bindable = config.GetBindable(OsuSetting.ShowHealthDisplayWhenCantFail), }, new SettingsCheckbox From cd31d2bc051e40528a8946c9f31a7e73f8b4091e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 7 Jul 2019 13:06:31 +0900 Subject: [PATCH 1622/2854] Simplify tests --- .../Visual/SongSelect/TestSceneLeaderboard.cs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs index 9472696295..8e358a77db 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs @@ -42,8 +42,8 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter)); AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn)); AddStep(@"Unavailable", () => leaderboard.SetRetrievalState(PlaceholderState.Unavailable)); - AddStep(@"Ranked beatmap", rankedBeatmap); - AddStep(@"Pending beatmap", pendingBeatmap); + foreach (BeatmapSetOnlineStatus status in Enum.GetValues(typeof(BeatmapSetOnlineStatus))) + AddStep($"{status} beatmap", () => showBeatmapWithStatus(status)); } private void newScores() @@ -235,21 +235,12 @@ namespace osu.Game.Tests.Visual.SongSelect leaderboard.Scores = scores; } - private void rankedBeatmap() + private void showBeatmapWithStatus(BeatmapSetOnlineStatus status) { leaderboard.Beatmap = new BeatmapInfo { OnlineBeatmapID = 1113057, - Status = BeatmapSetOnlineStatus.Ranked, - }; - } - - private void pendingBeatmap() - { - leaderboard.Beatmap = new BeatmapInfo - { - OnlineBeatmapID = 1113057, - Status = BeatmapSetOnlineStatus.Pending, + Status = status, }; } From ebc0e502958e157d21ec5cf7005e39349c57bb44 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 7 Jul 2019 13:21:41 +0900 Subject: [PATCH 1623/2854] Simplify conditions --- .../Select/Leaderboards/BeatmapLeaderboard.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 5c568c6983..0f6d4f3188 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -111,22 +111,12 @@ namespace osu.Game.Screens.Select.Leaderboards return null; } - if (Beatmap?.OnlineBeatmapID == null) + if (Beatmap?.OnlineBeatmapID == null || Beatmap?.Status <= BeatmapSetOnlineStatus.Pending) { PlaceholderState = PlaceholderState.Unavailable; return null; } - switch (Beatmap?.Status) - { - case BeatmapSetOnlineStatus.Graveyard: - case BeatmapSetOnlineStatus.None: - case BeatmapSetOnlineStatus.Pending: - case BeatmapSetOnlineStatus.WIP: - PlaceholderState = PlaceholderState.Unavailable; - return null; - } - if (!api.LocalUser.Value.IsSupporter && (Scope != BeatmapLeaderboardScope.Global || filterMods)) { PlaceholderState = PlaceholderState.NotSupporter; From 1a117d1511d48523a7103693e4340a8bd858141d Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Sun, 7 Jul 2019 17:05:53 +0200 Subject: [PATCH 1624/2854] Closes #5277 --- osu.Game/Screens/Edit/Editor.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 89da9ae063..69fbf7d387 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -168,6 +168,8 @@ namespace osu.Game.Screens.Edit menuBar.Mode.ValueChanged += onModeChanged; + host.Exiting += onQuittingGame; + bottomBackground.Colour = colours.Gray2; } @@ -235,16 +237,27 @@ namespace osu.Game.Screens.Edit Beatmap.Value.Track?.Stop(); } + private bool isExitingGame = false; + public override bool OnExiting(IScreen next) { Background.FadeColour(Color4.White, 500); if (Beatmap.Value.Track != null) { - Beatmap.Value.Track.Tempo.Value = 1; - Beatmap.Value.Track.Start(); + if (isExitingGame) + { + Beatmap.Value.Track.Stop(); + } + else + { + Beatmap.Value.Track.Tempo.Value = 1; + Beatmap.Value.Track.Start(); + } } + host.Exiting -= onQuittingGame; + return base.OnExiting(next); } @@ -281,5 +294,7 @@ namespace osu.Game.Screens.Edit else clock.SeekForward(!clock.IsRunning, amount); } + + private bool onQuittingGame() => isExitingGame = true; } } From 84cadc688a4f12df27485e7ec4c1b570acf065bf Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Sun, 7 Jul 2019 17:11:54 +0200 Subject: [PATCH 1625/2854] Change method name --- osu.Game/Screens/Edit/Editor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 69fbf7d387..0a9f7672d3 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -168,7 +168,7 @@ namespace osu.Game.Screens.Edit menuBar.Mode.ValueChanged += onModeChanged; - host.Exiting += onQuittingGame; + host.Exiting += onExitingGame; bottomBackground.Colour = colours.Gray2; } @@ -256,7 +256,7 @@ namespace osu.Game.Screens.Edit } } - host.Exiting -= onQuittingGame; + host.Exiting -= onExitingGame; return base.OnExiting(next); } @@ -295,6 +295,6 @@ namespace osu.Game.Screens.Edit clock.SeekForward(!clock.IsRunning, amount); } - private bool onQuittingGame() => isExitingGame = true; + private bool onExitingGame() => isExitingGame = true; } } From 188c80374e2401032d9522a0a1f596882228be1c Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 7 Jul 2019 18:14:23 +0300 Subject: [PATCH 1626/2854] Add sorting by BPM --- osu.Game/Beatmaps/BeatmapManager.cs | 6 +++++- osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 7ef50da7d3..c2d8e06d53 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -271,7 +271,11 @@ namespace osu.Game.Beatmaps OnlineBeatmapSetID = beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID, Beatmaps = new List(), Metadata = beatmap.Metadata, - DateAdded = DateTimeOffset.UtcNow + DateAdded = DateTimeOffset.UtcNow, + OnlineInfo = new BeatmapSetOnlineInfo + { + BPM = beatmap.ControlPointInfo.BPMMode, + } }; } diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index f1951e27ab..c51638277d 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -48,6 +48,9 @@ namespace osu.Game.Screens.Select.Carousel case SortMode.DateAdded: return otherSet.BeatmapSet.DateAdded.CompareTo(BeatmapSet.DateAdded); + case SortMode.BPM: + return BeatmapSet.OnlineInfo.BPM.CompareTo(otherSet.BeatmapSet.OnlineInfo.BPM); + case SortMode.Difficulty: return BeatmapSet.MaxStarDifficulty.CompareTo(otherSet.BeatmapSet.MaxStarDifficulty); } From 65c8249c94212dacfff06683b0156e2a2eea0fb7 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 7 Jul 2019 18:25:52 +0300 Subject: [PATCH 1627/2854] Add beatmap extension for calculating length --- osu.Game/Beatmaps/Beatmap.cs | 12 ++++++++++++ osu.Game/Screens/Select/BeatmapInfoWedge.cs | 5 +---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 4ebeee40bf..7fca13a1e2 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -8,6 +8,7 @@ using System.Linq; using osu.Game.Beatmaps.ControlPoints; using Newtonsoft.Json; using osu.Game.IO.Serialization.Converters; +using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Beatmaps { @@ -61,4 +62,15 @@ namespace osu.Game.Beatmaps { public new Beatmap Clone() => (Beatmap)base.Clone(); } + + public static class BeatmapExtensions + { + public static double CalculateLength(this IBeatmap b) + { + HitObject lastObject = b.HitObjects.LastOrDefault(); + var endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0; + + return endTime - b.HitObjects.FirstOrDefault()?.StartTime ?? 0; + } + } } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index fa9ffd0706..e0521307fc 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -289,14 +289,11 @@ namespace osu.Game.Screens.Select if (b?.HitObjects?.Any() == true) { - HitObject lastObject = b.HitObjects.LastOrDefault(); - double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0; - labels.Add(new InfoLabel(new BeatmapStatistic { Name = "Length", Icon = FontAwesome.Regular.Clock, - Content = TimeSpan.FromMilliseconds(endTime - b.HitObjects.First().StartTime).ToString(@"m\:ss"), + Content = TimeSpan.FromMilliseconds(b.CalculateLength()).ToString(@"m\:ss"), })); labels.Add(new InfoLabel(new BeatmapStatistic From b4ef64fa61a56cc02f09294ab8442fe5739da944 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 7 Jul 2019 18:26:56 +0300 Subject: [PATCH 1628/2854] Add sorting by Length --- osu.Game/Beatmaps/BeatmapManager.cs | 5 +++++ osu.Game/Beatmaps/BeatmapSetInfo.cs | 2 ++ osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 7 +++++++ osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs | 3 +++ 4 files changed, 17 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index c2d8e06d53..b4c211ad53 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -307,6 +307,11 @@ namespace osu.Game.Beatmaps // TODO: this should be done in a better place once we actually need to dynamically update it. beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0; + beatmap.BeatmapInfo.OnlineInfo = new BeatmapOnlineInfo + { + Length = beatmap.CalculateLength(), + }; + beatmapInfos.Add(beatmap.BeatmapInfo); } } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 524ed0ed56..cdf8ddb5a1 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -37,6 +37,8 @@ namespace osu.Game.Beatmaps public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0; + public double MaxLength => Beatmaps?.Max(b => b.OnlineInfo.Length) ?? 0; + [NotMapped] public bool DeletePending { get; set; } diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 712ab7b571..fa24a64d51 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -47,6 +47,13 @@ namespace osu.Game.Screens.Select.Carousel if (ruleset != 0) return ruleset; return Beatmap.StarDifficulty.CompareTo(otherBeatmap.Beatmap.StarDifficulty); + + case SortMode.Length: + // Length comparing must be in seconds + if (TimeSpan.FromMilliseconds(Beatmap.OnlineInfo.Length).Seconds != TimeSpan.FromMilliseconds(otherBeatmap.Beatmap.OnlineInfo.Length).Seconds) + return Beatmap.OnlineInfo.Length.CompareTo(otherBeatmap.Beatmap.OnlineInfo.Length); + + goto case SortMode.Difficulty; } } diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index c51638277d..7934cf388f 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -51,6 +51,9 @@ namespace osu.Game.Screens.Select.Carousel case SortMode.BPM: return BeatmapSet.OnlineInfo.BPM.CompareTo(otherSet.BeatmapSet.OnlineInfo.BPM); + case SortMode.Length: + return BeatmapSet.MaxLength.CompareTo(otherSet.BeatmapSet.MaxLength); + case SortMode.Difficulty: return BeatmapSet.MaxStarDifficulty.CompareTo(otherSet.BeatmapSet.MaxStarDifficulty); } From 31e1d204d4d1d63debe92159443e09db9ddb1cd1 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 7 Jul 2019 18:27:12 +0300 Subject: [PATCH 1629/2854] Add test for sorting by BPM and Length --- .../SongSelect/TestScenePlaySongSelect.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 962e0fb362..04c75d70e3 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -133,6 +133,9 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; }); AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; }); AddStep(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; }); + AddStep(@"Sort by DateAdded", delegate { songSelect.FilterControl.Sort = SortMode.DateAdded; }); + AddStep(@"Sort by BPM", delegate { songSelect.FilterControl.Sort = SortMode.BPM; }); + AddStep(@"Sort by Length", delegate { songSelect.FilterControl.Sort = SortMode.Length; }); AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; }); } @@ -264,20 +267,26 @@ namespace osu.Game.Tests.Visual.SongSelect for (int i = 0; i < 6; i++) { int beatmapId = setId * 10 + i; + int length = RNG.Next(30000, 200000); beatmaps.Add(new BeatmapInfo { Ruleset = getRuleset(), OnlineBeatmapID = beatmapId, Path = "normal.osu", - Version = $"{beatmapId}", + Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss})", BaseDifficulty = new BeatmapDifficulty { OverallDifficulty = 3.5f, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = length, } }); } + double bpm = RNG.NextSingle(80, 200); return new BeatmapSetInfo { OnlineBeatmapSetID = setId, @@ -286,10 +295,15 @@ namespace osu.Game.Tests.Visual.SongSelect { // Create random metadata, then we can check if sorting works based on these Artist = "Some Artist " + RNG.Next(0, 9), - Title = $"Some Song (set id {setId})", + Title = $"Some Song (set id {setId}, bpm {bpm:0.#})", AuthorString = "Some Guy " + RNG.Next(0, 9), }, - Beatmaps = beatmaps + Beatmaps = beatmaps, + DateAdded = DateTimeOffset.UtcNow, + OnlineInfo = new BeatmapSetOnlineInfo + { + BPM = bpm, + } }; } } From 28cc797fb61ee60fa82193982e84f2dc86c2d48f Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 7 Jul 2019 18:29:25 +0300 Subject: [PATCH 1630/2854] Add migrations --- ...7134040_AddBPMAndLengthSorting.Designer.cs | 500 ++++++++++++++++++ .../20190707134040_AddBPMAndLengthSorting.cs | 17 + 2 files changed, 517 insertions(+) create mode 100644 osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.Designer.cs create mode 100644 osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.cs diff --git a/osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.Designer.cs b/osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.Designer.cs new file mode 100644 index 0000000000..7038fcba6e --- /dev/null +++ b/osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.Designer.cs @@ -0,0 +1,500 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20190707134040_AddBPMAndLengthSorting")] + partial class AddBPMAndLengthSorting + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.4-servicing-10062"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("Status"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.Property("Status"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Key") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("SkinInfoID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("SkinInfoID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("ScoreInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("ScoreInfoID"); + + b.ToTable("ScoreFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Accuracy") + .HasColumnType("DECIMAL(1,4)"); + + b.Property("BeatmapInfoID"); + + b.Property("Combo"); + + b.Property("Date"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MaxCombo"); + + b.Property("ModsJson") + .HasColumnName("Mods"); + + b.Property("OnlineScoreID"); + + b.Property("PP"); + + b.Property("Rank"); + + b.Property("RulesetID"); + + b.Property("StatisticsJson") + .HasColumnName("Statistics"); + + b.Property("TotalScore"); + + b.Property("UserID") + .HasColumnName("UserID"); + + b.Property("UserString") + .HasColumnName("User"); + + b.HasKey("ID"); + + b.HasIndex("BeatmapInfoID"); + + b.HasIndex("OnlineScoreID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("ScoreInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Settings") + .HasForeignKey("SkinInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Scoring.ScoreInfo") + .WithMany("Files") + .HasForeignKey("ScoreInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") + .WithMany("Scores") + .HasForeignKey("BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.cs b/osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.cs new file mode 100644 index 0000000000..98a94b1c4e --- /dev/null +++ b/osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class AddBPMAndLengthSorting : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} From 6ee10640e31f5343fc3eeba33913ab43a1b96591 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 7 Jul 2019 19:26:41 +0300 Subject: [PATCH 1631/2854] Remove unnecessary migration + Fix CI issues --- ...7134040_AddBPMAndLengthSorting.Designer.cs | 500 ------------------ .../20190707134040_AddBPMAndLengthSorting.cs | 17 - osu.Game/Screens/Select/BeatmapInfoWedge.cs | 2 - 3 files changed, 519 deletions(-) delete mode 100644 osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.Designer.cs delete mode 100644 osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.cs diff --git a/osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.Designer.cs b/osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.Designer.cs deleted file mode 100644 index 7038fcba6e..0000000000 --- a/osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.Designer.cs +++ /dev/null @@ -1,500 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20190707134040_AddBPMAndLengthSorting")] - partial class AddBPMAndLengthSorting - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.4-servicing-10062"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DateAdded"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Key") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("SkinInfoID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("SkinInfoID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("ScoreInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("ScoreInfoID"); - - b.ToTable("ScoreFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Accuracy") - .HasColumnType("DECIMAL(1,4)"); - - b.Property("BeatmapInfoID"); - - b.Property("Combo"); - - b.Property("Date"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MaxCombo"); - - b.Property("ModsJson") - .HasColumnName("Mods"); - - b.Property("OnlineScoreID"); - - b.Property("PP"); - - b.Property("Rank"); - - b.Property("RulesetID"); - - b.Property("StatisticsJson") - .HasColumnName("Statistics"); - - b.Property("TotalScore"); - - b.Property("UserID") - .HasColumnName("UserID"); - - b.Property("UserString") - .HasColumnName("User"); - - b.HasKey("ID"); - - b.HasIndex("BeatmapInfoID"); - - b.HasIndex("OnlineScoreID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("ScoreInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Settings") - .HasForeignKey("SkinInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Scoring.ScoreInfo") - .WithMany("Files") - .HasForeignKey("ScoreInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") - .WithMany("Scores") - .HasForeignKey("BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.cs b/osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.cs deleted file mode 100644 index 98a94b1c4e..0000000000 --- a/osu.Game/Migrations/20190707134040_AddBPMAndLengthSorting.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddBPMAndLengthSorting : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - - } - } -} diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index e0521307fc..26a83f930c 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -18,8 +18,6 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; From 3ea9629daf6cdbd22658ec4ef5f457d8f86f4d3d Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 7 Jul 2019 20:11:44 +0300 Subject: [PATCH 1632/2854] Move BPM out of OnlineInfo --- osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs | 4 ++-- osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs | 2 +- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 5 +---- osu.Game.Tournament/Components/SongBar.cs | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 5 +---- osu.Game/Beatmaps/BeatmapSetInfo.cs | 5 +++++ osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs | 5 ----- osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs | 2 +- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 2 +- osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs | 2 +- 10 files changed, 14 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index a9c44c9020..75e8c88f9d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -96,11 +96,11 @@ namespace osu.Game.Tests.Visual.Online FavouriteCount = 456, Submitted = DateTime.Now, Ranked = DateTime.Now, - BPM = 111, HasVideo = true, HasStoryboard = true, Covers = new BeatmapSetOnlineCovers(), }, + BPM = 111, Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, Beatmaps = new List { @@ -169,11 +169,11 @@ namespace osu.Game.Tests.Visual.Online FavouriteCount = 456, Submitted = DateTime.Now, Ranked = DateTime.Now, - BPM = 111, HasVideo = true, HasStoryboard = true, Covers = new BeatmapSetOnlineCovers(), }, + BPM = 111, Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, Beatmaps = new List { diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs index 53dbaeddda..5f80223541 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs @@ -47,11 +47,11 @@ namespace osu.Game.Tests.Visual.Online Preview = @"https://b.ppy.sh/preview/12345.mp3", PlayCount = 123, FavouriteCount = 456, - BPM = 111, HasVideo = true, HasStoryboard = true, Covers = new BeatmapSetOnlineCovers(), }, + BPM = 111, Beatmaps = new List { new BeatmapInfo diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 04c75d70e3..5813e9e6d5 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -300,10 +300,7 @@ namespace osu.Game.Tests.Visual.SongSelect }, Beatmaps = beatmaps, DateAdded = DateTimeOffset.UtcNow, - OnlineInfo = new BeatmapSetOnlineInfo - { - BPM = bpm, - } + BPM = bpm, }; } } diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index c07882ddd0..06541bc264 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -158,7 +158,7 @@ namespace osu.Game.Tournament.Components return; } - var bpm = beatmap.BeatmapSet.OnlineInfo.BPM; + var bpm = beatmap.BeatmapSet.BPM; var length = beatmap.OnlineInfo.Length; string hardRockExtra = ""; string srExtra = ""; diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index b4c211ad53..f94c461c9a 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -272,10 +272,7 @@ namespace osu.Game.Beatmaps Beatmaps = new List(), Metadata = beatmap.Metadata, DateAdded = DateTimeOffset.UtcNow, - OnlineInfo = new BeatmapSetOnlineInfo - { - BPM = beatmap.ControlPointInfo.BPMMode, - } + BPM = beatmap.ControlPointInfo.BPMMode, }; } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index cdf8ddb5a1..2b4ef961ce 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -35,6 +35,11 @@ namespace osu.Game.Beatmaps [NotMapped] public BeatmapSetMetrics Metrics { get; set; } + /// + /// The beats per minute of this beatmap set's song. + /// + public double BPM { get; set; } + public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0; public double MaxLength => Beatmaps?.Max(b => b.OnlineInfo.Length) ?? 0; diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index ea3f0b61b9..903d07bea0 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -51,11 +51,6 @@ namespace osu.Game.Beatmaps /// public string Preview { get; set; } - /// - /// The beats per minute of this beatmap set's song. - /// - public double BPM { get; set; } - /// /// The amount of plays this beatmap set has. /// diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 200a705500..50128bde7a 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -77,13 +77,13 @@ namespace osu.Game.Online.API.Requests.Responses Metadata = this, Status = Status, Metrics = ratings == null ? null : new BeatmapSetMetrics { Ratings = ratings }, + BPM = bpm, OnlineInfo = new BeatmapSetOnlineInfo { Covers = covers, Preview = preview, PlayCount = playCount, FavouriteCount = favouriteCount, - BPM = bpm, Status = Status, HasVideo = hasVideo, HasStoryboard = hasStoryboard, diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index 6a583baf38..651ef30bdc 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.BeatmapSet private void updateDisplay() { - bpm.Value = BeatmapSet?.OnlineInfo?.BPM.ToString(@"0.##") ?? "-"; + bpm.Value = BeatmapSet?.BPM.ToString(@"0.##") ?? "-"; if (beatmap == null) { diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 7934cf388f..6457f78746 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Select.Carousel return otherSet.BeatmapSet.DateAdded.CompareTo(BeatmapSet.DateAdded); case SortMode.BPM: - return BeatmapSet.OnlineInfo.BPM.CompareTo(otherSet.BeatmapSet.OnlineInfo.BPM); + return BeatmapSet.BPM.CompareTo(otherSet.BeatmapSet.BPM); case SortMode.Length: return BeatmapSet.MaxLength.CompareTo(otherSet.BeatmapSet.MaxLength); From 729f0901f7bca3595c2834822a440f2f92433910 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 7 Jul 2019 20:25:36 +0300 Subject: [PATCH 1633/2854] Move Length out of OnlineInfo --- osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs | 4 ++-- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 5 +---- osu.Game.Tournament/Components/SongBar.cs | 2 +- osu.Game/Beatmaps/BeatmapInfo.cs | 5 +++++ osu.Game/Beatmaps/BeatmapManager.cs | 6 +----- osu.Game/Beatmaps/BeatmapOnlineInfo.cs | 5 ----- osu.Game/Beatmaps/BeatmapSetInfo.cs | 2 +- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 2 +- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 2 +- osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 4 ++-- 10 files changed, 15 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 75e8c88f9d..02b26acb7c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -108,6 +108,7 @@ namespace osu.Game.Tests.Visual.Online { StarDifficulty = 9.99, Version = @"TEST", + Length = 456000, Ruleset = maniaRuleset, BaseDifficulty = new BeatmapDifficulty { @@ -118,7 +119,6 @@ namespace osu.Game.Tests.Visual.Online }, OnlineInfo = new BeatmapOnlineInfo { - Length = 456000, CircleCount = 111, SliderCount = 12, PlayCount = 222, @@ -181,6 +181,7 @@ namespace osu.Game.Tests.Visual.Online { StarDifficulty = 5.67, Version = @"ANOTHER TEST", + Length = 123000, Ruleset = taikoRuleset, BaseDifficulty = new BeatmapDifficulty { @@ -191,7 +192,6 @@ namespace osu.Game.Tests.Visual.Online }, OnlineInfo = new BeatmapOnlineInfo { - Length = 123000, CircleCount = 123, SliderCount = 45, PlayCount = 567, diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 5813e9e6d5..0b0f65f572 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -275,14 +275,11 @@ namespace osu.Game.Tests.Visual.SongSelect OnlineBeatmapID = beatmapId, Path = "normal.osu", Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss})", + Length = length, BaseDifficulty = new BeatmapDifficulty { OverallDifficulty = 3.5f, }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = length, - } }); } diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 06541bc264..938fbba303 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -159,7 +159,7 @@ namespace osu.Game.Tournament.Components } var bpm = beatmap.BeatmapSet.BPM; - var length = beatmap.OnlineInfo.Length; + var length = beatmap.Length; string hardRockExtra = ""; string srExtra = ""; diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 3c082bb71e..1610e37620 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -51,6 +51,11 @@ namespace osu.Game.Beatmaps [NotMapped] public BeatmapOnlineInfo OnlineInfo { get; set; } + /// + /// The length in milliseconds of this beatmap's song. + /// + public double Length { get; set; } + public string Path { get; set; } [JsonProperty("file_sha2")] diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f94c461c9a..810db1589e 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -303,11 +303,7 @@ namespace osu.Game.Beatmaps beatmap.BeatmapInfo.Ruleset = ruleset; // TODO: this should be done in a better place once we actually need to dynamically update it. beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0; - - beatmap.BeatmapInfo.OnlineInfo = new BeatmapOnlineInfo - { - Length = beatmap.CalculateLength(), - }; + beatmap.BeatmapInfo.Length = beatmap.CalculateLength(); beatmapInfos.Add(beatmap.BeatmapInfo); } diff --git a/osu.Game/Beatmaps/BeatmapOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapOnlineInfo.cs index faae74db88..bfeacd9bfc 100644 --- a/osu.Game/Beatmaps/BeatmapOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapOnlineInfo.cs @@ -8,11 +8,6 @@ namespace osu.Game.Beatmaps /// public class BeatmapOnlineInfo { - /// - /// The length in milliseconds of this beatmap's song. - /// - public double Length { get; set; } - /// /// The amount of circles in this beatmap. /// diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 2b4ef961ce..62366cab4e 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -42,7 +42,7 @@ namespace osu.Game.Beatmaps public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0; - public double MaxLength => Beatmaps?.Max(b => b.OnlineInfo.Length) ?? 0; + public double MaxLength => Beatmaps?.Max(b => b.Length) ?? 0; [NotMapped] public bool DeletePending { get; set; } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index bcbe060f82..ff4d240bf0 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -71,6 +71,7 @@ namespace osu.Game.Online.API.Requests.Responses StarDifficulty = starDifficulty, OnlineBeatmapID = OnlineBeatmapID, Version = version, + Length = length, Status = Status, BeatmapSet = set, Metrics = metrics, @@ -85,7 +86,6 @@ namespace osu.Game.Online.API.Requests.Responses { PlayCount = playCount, PassCount = passCount, - Length = length, CircleCount = circleCount, SliderCount = sliderCount, }, diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index 651ef30bdc..f97212f180 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -60,7 +60,7 @@ namespace osu.Game.Overlays.BeatmapSet } else { - length.Value = TimeSpan.FromSeconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss"); + length.Value = TimeSpan.FromSeconds(beatmap.Length).ToString(@"m\:ss"); circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString(); sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString(); } diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index fa24a64d51..490012da61 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -50,8 +50,8 @@ namespace osu.Game.Screens.Select.Carousel case SortMode.Length: // Length comparing must be in seconds - if (TimeSpan.FromMilliseconds(Beatmap.OnlineInfo.Length).Seconds != TimeSpan.FromMilliseconds(otherBeatmap.Beatmap.OnlineInfo.Length).Seconds) - return Beatmap.OnlineInfo.Length.CompareTo(otherBeatmap.Beatmap.OnlineInfo.Length); + if (TimeSpan.FromMilliseconds(Beatmap.Length).Seconds != TimeSpan.FromMilliseconds(otherBeatmap.Beatmap.Length).Seconds) + return Beatmap.Length.CompareTo(otherBeatmap.Beatmap.Length); goto case SortMode.Difficulty; } From d8745746129c26f25d6ac40ac67a5d1c2b52effc Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 7 Jul 2019 20:25:59 +0300 Subject: [PATCH 1634/2854] Add migration --- ...7172237_AddBPMAndLengthColumns.Designer.cs | 504 ++++++++++++++++++ .../20190707172237_AddBPMAndLengthColumns.cs | 33 ++ .../Migrations/OsuDbContextModelSnapshot.cs | 4 + 3 files changed, 541 insertions(+) create mode 100644 osu.Game/Migrations/20190707172237_AddBPMAndLengthColumns.Designer.cs create mode 100644 osu.Game/Migrations/20190707172237_AddBPMAndLengthColumns.cs diff --git a/osu.Game/Migrations/20190707172237_AddBPMAndLengthColumns.Designer.cs b/osu.Game/Migrations/20190707172237_AddBPMAndLengthColumns.Designer.cs new file mode 100644 index 0000000000..c1e9f5071a --- /dev/null +++ b/osu.Game/Migrations/20190707172237_AddBPMAndLengthColumns.Designer.cs @@ -0,0 +1,504 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20190707172237_AddBPMAndLengthColumns")] + partial class AddBPMAndLengthColumns + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.4-servicing-10062"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("Length"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("Status"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BPM"); + + b.Property("DateAdded"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.Property("Status"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Key") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("SkinInfoID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("SkinInfoID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("ScoreInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("ScoreInfoID"); + + b.ToTable("ScoreFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Accuracy") + .HasColumnType("DECIMAL(1,4)"); + + b.Property("BeatmapInfoID"); + + b.Property("Combo"); + + b.Property("Date"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MaxCombo"); + + b.Property("ModsJson") + .HasColumnName("Mods"); + + b.Property("OnlineScoreID"); + + b.Property("PP"); + + b.Property("Rank"); + + b.Property("RulesetID"); + + b.Property("StatisticsJson") + .HasColumnName("Statistics"); + + b.Property("TotalScore"); + + b.Property("UserID") + .HasColumnName("UserID"); + + b.Property("UserString") + .HasColumnName("User"); + + b.HasKey("ID"); + + b.HasIndex("BeatmapInfoID"); + + b.HasIndex("OnlineScoreID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("ScoreInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Settings") + .HasForeignKey("SkinInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Scoring.ScoreInfo") + .WithMany("Files") + .HasForeignKey("ScoreInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") + .WithMany("Scores") + .HasForeignKey("BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20190707172237_AddBPMAndLengthColumns.cs b/osu.Game/Migrations/20190707172237_AddBPMAndLengthColumns.cs new file mode 100644 index 0000000000..b14722daf6 --- /dev/null +++ b/osu.Game/Migrations/20190707172237_AddBPMAndLengthColumns.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class AddBPMAndLengthColumns : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "BPM", + table: "BeatmapSetInfo", + nullable: false, + defaultValue: 0.0); + + migrationBuilder.AddColumn( + name: "Length", + table: "BeatmapInfo", + nullable: false, + defaultValue: 0.0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "BPM", + table: "BeatmapSetInfo"); + + migrationBuilder.DropColumn( + name: "Length", + table: "BeatmapInfo"); + } + } +} diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index 11b032a941..9e65dc7f0f 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -61,6 +61,8 @@ namespace osu.Game.Migrations b.Property("Hidden"); + b.Property("Length"); + b.Property("LetterboxInBreaks"); b.Property("MD5Hash"); @@ -166,6 +168,8 @@ namespace osu.Game.Migrations b.Property("ID") .ValueGeneratedOnAdd(); + b.Property("BPM"); + b.Property("DateAdded"); b.Property("DeletePending"); From 2747d7692b1db44559f305c3e4873e78048a6f48 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 8 Jul 2019 14:55:05 +0900 Subject: [PATCH 1635/2854] Create ReplayPlayerLoader for local mod caching --- osu.Game/OsuGame.cs | 3 +- osu.Game/Screens/Play/ReplayPlayerLoader.cs | 32 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Screens/Play/ReplayPlayerLoader.cs diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 0a472d4dc1..ab7efc6e38 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -284,9 +284,8 @@ namespace osu.Game { Ruleset.Value = databasedScoreInfo.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap); - Mods.Value = databasedScoreInfo.Mods; - menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore))); + menuScreen.Push(new ReplayPlayerLoader(() => new ReplayPlayer(databasedScore), databasedScoreInfo.Mods)); }, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true); } diff --git a/osu.Game/Screens/Play/ReplayPlayerLoader.cs b/osu.Game/Screens/Play/ReplayPlayerLoader.cs new file mode 100644 index 0000000000..1c24709e41 --- /dev/null +++ b/osu.Game/Screens/Play/ReplayPlayerLoader.cs @@ -0,0 +1,32 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Screens.Play +{ + public class ReplayPlayerLoader : PlayerLoader + { + private readonly IReadOnlyList mods; + + public ReplayPlayerLoader(Func player, IReadOnlyList mods) + : base(player) + { + this.mods = mods; + } + + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + dependencies = new DependencyContainer(parent); + dependencies.Cache(new Bindable>(mods)); + + return base.CreateChildDependencies(dependencies); + } + } +} From 90d5484818b9e0372694a1d99a3801d340699bd3 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 8 Jul 2019 09:10:41 +0300 Subject: [PATCH 1636/2854] Return BPM back to OnlineInfo Revert commit of "Move BPM out of OnlineInfo" --- osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs | 4 ++-- osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs | 2 +- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 5 ++++- osu.Game/Beatmaps/BeatmapManager.cs | 5 ++++- osu.Game/Beatmaps/BeatmapSetInfo.cs | 5 ----- osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs | 5 +++++ osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs | 2 +- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 2 +- osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs | 2 +- 9 files changed, 19 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 02b26acb7c..daee419b52 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -96,11 +96,11 @@ namespace osu.Game.Tests.Visual.Online FavouriteCount = 456, Submitted = DateTime.Now, Ranked = DateTime.Now, + BPM = 111, HasVideo = true, HasStoryboard = true, Covers = new BeatmapSetOnlineCovers(), }, - BPM = 111, Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, Beatmaps = new List { @@ -169,11 +169,11 @@ namespace osu.Game.Tests.Visual.Online FavouriteCount = 456, Submitted = DateTime.Now, Ranked = DateTime.Now, + BPM = 111, HasVideo = true, HasStoryboard = true, Covers = new BeatmapSetOnlineCovers(), }, - BPM = 111, Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, Beatmaps = new List { diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs index 5f80223541..53dbaeddda 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs @@ -47,11 +47,11 @@ namespace osu.Game.Tests.Visual.Online Preview = @"https://b.ppy.sh/preview/12345.mp3", PlayCount = 123, FavouriteCount = 456, + BPM = 111, HasVideo = true, HasStoryboard = true, Covers = new BeatmapSetOnlineCovers(), }, - BPM = 111, Beatmaps = new List { new BeatmapInfo diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 0b0f65f572..27a341ffb7 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -297,7 +297,10 @@ namespace osu.Game.Tests.Visual.SongSelect }, Beatmaps = beatmaps, DateAdded = DateTimeOffset.UtcNow, - BPM = bpm, + OnlineInfo = new BeatmapSetOnlineInfo + { + BPM = bpm, + } }; } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 810db1589e..34a8ddf5c9 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -272,7 +272,10 @@ namespace osu.Game.Beatmaps Beatmaps = new List(), Metadata = beatmap.Metadata, DateAdded = DateTimeOffset.UtcNow, - BPM = beatmap.ControlPointInfo.BPMMode, + OnlineInfo = new BeatmapSetOnlineInfo + { + BPM = beatmap.ControlPointInfo.BPMMode, + } }; } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 62366cab4e..a77dfa2148 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -35,11 +35,6 @@ namespace osu.Game.Beatmaps [NotMapped] public BeatmapSetMetrics Metrics { get; set; } - /// - /// The beats per minute of this beatmap set's song. - /// - public double BPM { get; set; } - public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0; public double MaxLength => Beatmaps?.Max(b => b.Length) ?? 0; diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index 903d07bea0..ea3f0b61b9 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -51,6 +51,11 @@ namespace osu.Game.Beatmaps /// public string Preview { get; set; } + /// + /// The beats per minute of this beatmap set's song. + /// + public double BPM { get; set; } + /// /// The amount of plays this beatmap set has. /// diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 50128bde7a..200a705500 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -77,13 +77,13 @@ namespace osu.Game.Online.API.Requests.Responses Metadata = this, Status = Status, Metrics = ratings == null ? null : new BeatmapSetMetrics { Ratings = ratings }, - BPM = bpm, OnlineInfo = new BeatmapSetOnlineInfo { Covers = covers, Preview = preview, PlayCount = playCount, FavouriteCount = favouriteCount, + BPM = bpm, Status = Status, HasVideo = hasVideo, HasStoryboard = hasStoryboard, diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index f97212f180..2926c82631 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.BeatmapSet private void updateDisplay() { - bpm.Value = BeatmapSet?.BPM.ToString(@"0.##") ?? "-"; + bpm.Value = BeatmapSet?.OnlineInfo?.BPM.ToString(@"0.##") ?? "-"; if (beatmap == null) { diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 6457f78746..7934cf388f 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Select.Carousel return otherSet.BeatmapSet.DateAdded.CompareTo(BeatmapSet.DateAdded); case SortMode.BPM: - return BeatmapSet.BPM.CompareTo(otherSet.BeatmapSet.BPM); + return BeatmapSet.OnlineInfo.BPM.CompareTo(otherSet.BeatmapSet.OnlineInfo.BPM); case SortMode.Length: return BeatmapSet.MaxLength.CompareTo(otherSet.BeatmapSet.MaxLength); From 79ddb8d5d36f76131d5c75da5f6650d91ffa22c9 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 8 Jul 2019 09:23:01 +0300 Subject: [PATCH 1637/2854] Change to a more convenient xmldoc --- osu.Game/Beatmaps/BeatmapInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 1610e37620..609f75461c 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -52,7 +52,7 @@ namespace osu.Game.Beatmaps public BeatmapOnlineInfo OnlineInfo { get; set; } /// - /// The length in milliseconds of this beatmap's song. + /// The playable length of this beatmap. /// public double Length { get; set; } From 2d0c924bdf579bd935f1eaf9fa19ce50aba39d5f Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 8 Jul 2019 09:35:12 +0300 Subject: [PATCH 1638/2854] Add xmldoc for MaxStarDifficulty and MaxLength --- osu.Game/Beatmaps/BeatmapSetInfo.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index a77dfa2148..3e6385bdd5 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -35,8 +35,14 @@ namespace osu.Game.Beatmaps [NotMapped] public BeatmapSetMetrics Metrics { get; set; } + /// + /// The maximum star difficulty of all beatmaps in this set. + /// public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0; + /// + /// The maximum playable length of all beatmaps in this set. + /// public double MaxLength => Beatmaps?.Max(b => b.Length) ?? 0; [NotMapped] From 5853a877c257a14316cf03e5a39dd2a70e912e90 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 8 Jul 2019 15:40:10 +0900 Subject: [PATCH 1639/2854] create base dependencies before caching, create player in playerloader --- osu.Game/OsuGame.cs | 2 +- osu.Game/Screens/Play/ReplayPlayerLoader.cs | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index ab7efc6e38..1c426d928f 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -285,7 +285,7 @@ namespace osu.Game Ruleset.Value = databasedScoreInfo.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap); - menuScreen.Push(new ReplayPlayerLoader(() => new ReplayPlayer(databasedScore), databasedScoreInfo.Mods)); + menuScreen.Push(new ReplayPlayerLoader(databasedScore, databasedScoreInfo.Mods)); }, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true); } diff --git a/osu.Game/Screens/Play/ReplayPlayerLoader.cs b/osu.Game/Screens/Play/ReplayPlayerLoader.cs index 1c24709e41..41f02b5cb1 100644 --- a/osu.Game/Screens/Play/ReplayPlayerLoader.cs +++ b/osu.Game/Screens/Play/ReplayPlayerLoader.cs @@ -1,32 +1,33 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Rulesets.Mods; +using osu.Game.Scoring; namespace osu.Game.Screens.Play { public class ReplayPlayerLoader : PlayerLoader { - private readonly IReadOnlyList mods; + private readonly Bindable> mods; - public ReplayPlayerLoader(Func player, IReadOnlyList mods) - : base(player) + public ReplayPlayerLoader(Score score, IReadOnlyList mods) + : base(() => new ReplayPlayer(score)) { - this.mods = mods; + this.mods = new Bindable>(mods); } - private DependencyContainer dependencies; - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - dependencies = new DependencyContainer(parent); - dependencies.Cache(new Bindable>(mods)); + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + dependencies.Cache(mods); - return base.CreateChildDependencies(dependencies); + // Overwrite the global mods here for use in the mod hud. + Mods.Value = mods.Value; + + return dependencies; } } } From 6a86f62d17b05a899059337ed9f82d25a92f2d5d Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 8 Jul 2019 16:13:03 +0900 Subject: [PATCH 1640/2854] Get mods from score info --- osu.Game/OsuGame.cs | 2 +- osu.Game/Screens/Play/ReplayPlayerLoader.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 1c426d928f..2260c5bb57 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -285,7 +285,7 @@ namespace osu.Game Ruleset.Value = databasedScoreInfo.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap); - menuScreen.Push(new ReplayPlayerLoader(databasedScore, databasedScoreInfo.Mods)); + menuScreen.Push(new ReplayPlayerLoader(databasedScore)); }, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true); } diff --git a/osu.Game/Screens/Play/ReplayPlayerLoader.cs b/osu.Game/Screens/Play/ReplayPlayerLoader.cs index 41f02b5cb1..da30ed80b6 100644 --- a/osu.Game/Screens/Play/ReplayPlayerLoader.cs +++ b/osu.Game/Screens/Play/ReplayPlayerLoader.cs @@ -13,10 +13,10 @@ namespace osu.Game.Screens.Play { private readonly Bindable> mods; - public ReplayPlayerLoader(Score score, IReadOnlyList mods) + public ReplayPlayerLoader(Score score) : base(() => new ReplayPlayer(score)) { - this.mods = new Bindable>(mods); + mods = new Bindable>(score.ScoreInfo.Mods); } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) From ef22ab9340d152cb9713b4aded8957321f6b5412 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 8 Jul 2019 16:32:11 +0900 Subject: [PATCH 1641/2854] remove bindable --- osu.Game/Screens/Play/ReplayPlayerLoader.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/ReplayPlayerLoader.cs b/osu.Game/Screens/Play/ReplayPlayerLoader.cs index da30ed80b6..b877e7e2ca 100644 --- a/osu.Game/Screens/Play/ReplayPlayerLoader.cs +++ b/osu.Game/Screens/Play/ReplayPlayerLoader.cs @@ -11,21 +11,20 @@ namespace osu.Game.Screens.Play { public class ReplayPlayerLoader : PlayerLoader { - private readonly Bindable> mods; + private readonly IReadOnlyList mods; public ReplayPlayerLoader(Score score) : base(() => new ReplayPlayer(score)) { - mods = new Bindable>(score.ScoreInfo.Mods); + mods = score.ScoreInfo.Mods; } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - dependencies.Cache(mods); // Overwrite the global mods here for use in the mod hud. - Mods.Value = mods.Value; + Mods.Value = mods; return dependencies; } From 72362d92d47e388af4bb9457ed0b963bc1bd77ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 Jul 2019 16:34:11 +0900 Subject: [PATCH 1642/2854] Fix a few inspections from EAP r# --- osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs | 2 +- osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs | 7 ++++--- .../UserInterface/TestSceneScreenBreadcrumbControl.cs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs index 364c986723..16e47c5df9 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs @@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.Online }); channelTabControl.OnRequestLeave += channel => channelTabControl.RemoveChannel(channel); - channelTabControl.Current.ValueChanged += channel => currentText.Text = "Currently selected channel: " + channel.NewValue.ToString(); + channelTabControl.Current.ValueChanged += channel => currentText.Text = "Currently selected channel: " + channel.NewValue; AddStep("Add random private channel", addRandomPrivateChannel); AddAssert("There is only one channels", () => channelTabControl.Items.Count() == 2); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs index 867b3130c9..38a9af05d8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs @@ -14,8 +14,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneBackButton : OsuTestScene { - private readonly BackButton button; - public override IReadOnlyList RequiredTypes => new[] { typeof(TwoLayerButton) @@ -23,6 +21,8 @@ namespace osu.Game.Tests.Visual.UserInterface public TestSceneBackButton() { + BackButton button; + Child = new Container { Anchor = Anchor.Centre, @@ -40,11 +40,12 @@ namespace osu.Game.Tests.Visual.UserInterface { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Action = () => button.Hide(), } } }; + button.Action = () => button.Hide(); + AddStep("show button", () => button.Show()); AddStep("hide button", () => button.Hide()); } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenBreadcrumbControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenBreadcrumbControl.cs index 9c83fdf96c..0cb8683d72 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenBreadcrumbControl.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenBreadcrumbControl.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.UserInterface }, }; - breadcrumbs.Current.ValueChanged += screen => titleText.Text = $"Changed to {screen.NewValue.ToString()}"; + breadcrumbs.Current.ValueChanged += screen => titleText.Text = $"Changed to {screen.NewValue}"; breadcrumbs.Current.TriggerChange(); waitForCurrent(); From 129899f41950da4d0f11557000e5af1a617777d7 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 8 Jul 2019 10:43:35 +0300 Subject: [PATCH 1643/2854] Add a BPM property in BeatmapInfo --- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 12 +++++------- osu.Game.Tournament/Components/SongBar.cs | 2 +- osu.Game/Beatmaps/BeatmapInfo.cs | 5 +++++ osu.Game/Beatmaps/BeatmapManager.cs | 7 ++----- osu.Game/Beatmaps/BeatmapSetInfo.cs | 5 +++++ .../Screens/Select/Carousel/CarouselBeatmapSet.cs | 2 +- 6 files changed, 19 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 27a341ffb7..f3255814f2 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -267,15 +267,18 @@ namespace osu.Game.Tests.Visual.SongSelect for (int i = 0; i < 6; i++) { int beatmapId = setId * 10 + i; + int length = RNG.Next(30000, 200000); + double bpm = RNG.NextSingle(80, 200); beatmaps.Add(new BeatmapInfo { Ruleset = getRuleset(), OnlineBeatmapID = beatmapId, Path = "normal.osu", - Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss})", + Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})", Length = length, + BPM = bpm, BaseDifficulty = new BeatmapDifficulty { OverallDifficulty = 3.5f, @@ -283,7 +286,6 @@ namespace osu.Game.Tests.Visual.SongSelect }); } - double bpm = RNG.NextSingle(80, 200); return new BeatmapSetInfo { OnlineBeatmapSetID = setId, @@ -292,15 +294,11 @@ namespace osu.Game.Tests.Visual.SongSelect { // Create random metadata, then we can check if sorting works based on these Artist = "Some Artist " + RNG.Next(0, 9), - Title = $"Some Song (set id {setId}, bpm {bpm:0.#})", + Title = $"Some Song (set id {setId}, max bpm {beatmaps.Max(b => b.BPM):0.#})", AuthorString = "Some Guy " + RNG.Next(0, 9), }, Beatmaps = beatmaps, DateAdded = DateTimeOffset.UtcNow, - OnlineInfo = new BeatmapSetOnlineInfo - { - BPM = bpm, - } }; } } diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 938fbba303..ec021a8d1f 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -158,7 +158,7 @@ namespace osu.Game.Tournament.Components return; } - var bpm = beatmap.BeatmapSet.BPM; + var bpm = beatmap.BeatmapSet.OnlineInfo.BPM; var length = beatmap.Length; string hardRockExtra = ""; string srExtra = ""; diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 609f75461c..fa1282647e 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -56,6 +56,11 @@ namespace osu.Game.Beatmaps /// public double Length { get; set; } + /// + /// The most common BPM of this beatmap. + /// + public double BPM { get; set; } + public string Path { get; set; } [JsonProperty("file_sha2")] diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 34a8ddf5c9..56816607ee 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -271,11 +271,7 @@ namespace osu.Game.Beatmaps OnlineBeatmapSetID = beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID, Beatmaps = new List(), Metadata = beatmap.Metadata, - DateAdded = DateTimeOffset.UtcNow, - OnlineInfo = new BeatmapSetOnlineInfo - { - BPM = beatmap.ControlPointInfo.BPMMode, - } + DateAdded = DateTimeOffset.UtcNow }; } @@ -307,6 +303,7 @@ namespace osu.Game.Beatmaps // TODO: this should be done in a better place once we actually need to dynamically update it. beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0; beatmap.BeatmapInfo.Length = beatmap.CalculateLength(); + beatmap.BeatmapInfo.BPM = beatmap.ControlPointInfo.BPMMode; beatmapInfos.Add(beatmap.BeatmapInfo); } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 3e6385bdd5..4075263e12 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -45,6 +45,11 @@ namespace osu.Game.Beatmaps /// public double MaxLength => Beatmaps?.Max(b => b.Length) ?? 0; + /// + /// The maximum BPM of all beatmaps in this set. + /// + public double MaxBPM => Beatmaps?.Max(b => b.BPM) ?? 0; + [NotMapped] public bool DeletePending { get; set; } diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 7934cf388f..5a3996bb49 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Select.Carousel return otherSet.BeatmapSet.DateAdded.CompareTo(BeatmapSet.DateAdded); case SortMode.BPM: - return BeatmapSet.OnlineInfo.BPM.CompareTo(otherSet.BeatmapSet.OnlineInfo.BPM); + return BeatmapSet.MaxBPM.CompareTo(otherSet.BeatmapSet.MaxBPM); case SortMode.Length: return BeatmapSet.MaxLength.CompareTo(otherSet.BeatmapSet.MaxLength); From 574d9a51b393a1baae947eab18d08ff1fa74da85 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 8 Jul 2019 10:44:23 +0300 Subject: [PATCH 1644/2854] Update migrations --- ...cs => 20190708070844_AddBPMAndLengthColumns.Designer.cs} | 6 +++--- ...hColumns.cs => 20190708070844_AddBPMAndLengthColumns.cs} | 4 ++-- osu.Game/Migrations/OsuDbContextModelSnapshot.cs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) rename osu.Game/Migrations/{20190707172237_AddBPMAndLengthColumns.Designer.cs => 20190708070844_AddBPMAndLengthColumns.Designer.cs} (99%) rename osu.Game/Migrations/{20190707172237_AddBPMAndLengthColumns.cs => 20190708070844_AddBPMAndLengthColumns.cs} (91%) diff --git a/osu.Game/Migrations/20190707172237_AddBPMAndLengthColumns.Designer.cs b/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.Designer.cs similarity index 99% rename from osu.Game/Migrations/20190707172237_AddBPMAndLengthColumns.Designer.cs rename to osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.Designer.cs index c1e9f5071a..c5fcc16f84 100644 --- a/osu.Game/Migrations/20190707172237_AddBPMAndLengthColumns.Designer.cs +++ b/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.Designer.cs @@ -9,7 +9,7 @@ using osu.Game.Database; namespace osu.Game.Migrations { [DbContext(typeof(OsuDbContext))] - [Migration("20190707172237_AddBPMAndLengthColumns")] + [Migration("20190708070844_AddBPMAndLengthColumns")] partial class AddBPMAndLengthColumns { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -47,6 +47,8 @@ namespace osu.Game.Migrations b.Property("AudioLeadIn"); + b.Property("BPM"); + b.Property("BaseDifficultyID"); b.Property("BeatDivisor"); @@ -170,8 +172,6 @@ namespace osu.Game.Migrations b.Property("ID") .ValueGeneratedOnAdd(); - b.Property("BPM"); - b.Property("DateAdded"); b.Property("DeletePending"); diff --git a/osu.Game/Migrations/20190707172237_AddBPMAndLengthColumns.cs b/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.cs similarity index 91% rename from osu.Game/Migrations/20190707172237_AddBPMAndLengthColumns.cs rename to osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.cs index b14722daf6..f5963ebf5e 100644 --- a/osu.Game/Migrations/20190707172237_AddBPMAndLengthColumns.cs +++ b/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.cs @@ -8,7 +8,7 @@ namespace osu.Game.Migrations { migrationBuilder.AddColumn( name: "BPM", - table: "BeatmapSetInfo", + table: "BeatmapInfo", nullable: false, defaultValue: 0.0); @@ -23,7 +23,7 @@ namespace osu.Game.Migrations { migrationBuilder.DropColumn( name: "BPM", - table: "BeatmapSetInfo"); + table: "BeatmapInfo"); migrationBuilder.DropColumn( name: "Length", diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index 9e65dc7f0f..761dca2801 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -45,6 +45,8 @@ namespace osu.Game.Migrations b.Property("AudioLeadIn"); + b.Property("BPM"); + b.Property("BaseDifficultyID"); b.Property("BeatDivisor"); @@ -168,8 +170,6 @@ namespace osu.Game.Migrations b.Property("ID") .ValueGeneratedOnAdd(); - b.Property("BPM"); - b.Property("DateAdded"); b.Property("DeletePending"); From e78e326f34d84ce8cc3abbcc581adf35f8194bad Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 8 Jul 2019 17:02:42 +0900 Subject: [PATCH 1645/2854] remove unused using --- osu.Game/Screens/Play/ReplayPlayerLoader.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/ReplayPlayerLoader.cs b/osu.Game/Screens/Play/ReplayPlayerLoader.cs index b877e7e2ca..62edb661b9 100644 --- a/osu.Game/Screens/Play/ReplayPlayerLoader.cs +++ b/osu.Game/Screens/Play/ReplayPlayerLoader.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; From 16c993579bf943cb8ca71e89654e4ffd1115302c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 Jul 2019 17:10:12 +0900 Subject: [PATCH 1646/2854] Fix track transfer not running when beatmap is retrieved from cache --- osu.Game/Beatmaps/BeatmapManager.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 7ef50da7d3..5031176790 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -180,19 +180,18 @@ namespace osu.Game.Beatmaps lock (workingCache) { - var cached = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID); + var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID); - if (cached != null) - return cached; + if (working == null) + { + if (beatmapInfo.Metadata == null) + beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; - if (beatmapInfo.Metadata == null) - beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; - - WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager); + workingCache.Add(working = new BeatmapManagerWorkingBeatmap(Files.Store, + new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager)); + } previous?.TransferTo(working); - workingCache.Add(working); - return working; } } From a0efd50f629476bda2f1bfa86e7d801cd2662a48 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 8 Jul 2019 11:25:25 +0300 Subject: [PATCH 1647/2854] Extend APILegacyScores request --- osu.Game/Online/API/Requests/GetScoresRequest.cs | 8 ++++++++ .../Online/API/Requests/Responses/APILegacyScores.cs | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 6b0e680eb5..50844fa256 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -42,6 +42,14 @@ namespace osu.Game.Online.API.Requests score.Beatmap = beatmap; score.Ruleset = ruleset; } + + var userScore = r.UserScore; + + if (userScore != null) + { + userScore.Score.Beatmap = beatmap; + userScore.Score.Ruleset = ruleset; + } } protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores{createQueryParameters()}"; diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScores.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScores.cs index c629caaa6f..318fcb00de 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScores.cs +++ b/osu.Game/Online/API/Requests/Responses/APILegacyScores.cs @@ -10,5 +10,17 @@ namespace osu.Game.Online.API.Requests.Responses { [JsonProperty(@"scores")] public List Scores; + + [JsonProperty(@"userScore")] + public APILegacyUserTopScoreInfo UserScore; + } + + public class APILegacyUserTopScoreInfo + { + [JsonProperty(@"position")] + public int Position; + + [JsonProperty(@"score")] + public APILegacyScoreInfo Score; } } From fbd300e6643a582b9fab5299265633e907e16d6b Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 8 Jul 2019 17:37:20 +0900 Subject: [PATCH 1648/2854] Move ruleset into ReplayPlayerLoader as well --- osu.Game/OsuGame.cs | 1 - osu.Game/Screens/Play/ReplayPlayerLoader.cs | 9 ++++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2260c5bb57..361ff62155 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -282,7 +282,6 @@ namespace osu.Game performFromMainMenu(() => { - Ruleset.Value = databasedScoreInfo.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap); menuScreen.Push(new ReplayPlayerLoader(databasedScore)); diff --git a/osu.Game/Screens/Play/ReplayPlayerLoader.cs b/osu.Game/Screens/Play/ReplayPlayerLoader.cs index 62edb661b9..329e799f7c 100644 --- a/osu.Game/Screens/Play/ReplayPlayerLoader.cs +++ b/osu.Game/Screens/Play/ReplayPlayerLoader.cs @@ -1,21 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Game.Rulesets.Mods; using osu.Game.Scoring; namespace osu.Game.Screens.Play { public class ReplayPlayerLoader : PlayerLoader { - private readonly IReadOnlyList mods; + private readonly ScoreInfo scoreInfo; public ReplayPlayerLoader(Score score) : base(() => new ReplayPlayer(score)) { - mods = score.ScoreInfo.Mods; + scoreInfo = score.ScoreInfo; } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -23,7 +21,8 @@ namespace osu.Game.Screens.Play var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); // Overwrite the global mods here for use in the mod hud. - Mods.Value = mods; + Mods.Value = scoreInfo.Mods; + Ruleset.Value = scoreInfo.Ruleset; return dependencies; } From 67a6abb96c7e08c0afa5e4f5eb49c0e6bbeb369c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 8 Jul 2019 11:49:33 +0300 Subject: [PATCH 1649/2854] Add user top score on selected beatmap --- .../BeatmapSet/Scores/DrawableTopScore.cs | 22 +++------- .../BeatmapSet/Scores/ScoresContainer.cs | 41 +++++++++++++++---- .../BeatmapSet/Scores/TopScoreUserSection.cs | 4 +- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs index 8e806c6747..bdae730f7e 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs @@ -23,10 +23,8 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private Color4 backgroundHoveredColour; private readonly Box background; - private readonly TopScoreUserSection userSection; - private readonly TopScoreStatisticsSection statisticsSection; - public DrawableTopScore() + public DrawableTopScore(ScoreInfo score, int position = 1) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -61,16 +59,18 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { new Drawable[] { - userSection = new TopScoreUserSection + new TopScoreUserSection(position) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, + Score = score, }, null, - statisticsSection = new TopScoreStatisticsSection + new TopScoreStatisticsSection { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, + Score = score, } }, }, @@ -91,18 +91,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores background.Colour = backgroundIdleColour; } - /// - /// Sets the score to be displayed. - /// - public ScoreInfo Score - { - set - { - userSection.Score = value; - statisticsSection.Score = value; - } - } - protected override bool OnHover(HoverEvent e) { background.FadeColour(backgroundHoveredColour, fade_duration, Easing.OutQuint); diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 3e6c938802..30685fb826 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -14,6 +14,7 @@ using osuTK; using System.Collections.Generic; using System.Linq; using osu.Game.Scoring; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.BeatmapSet.Scores { @@ -25,7 +26,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly Box background; private readonly ScoreTable scoreTable; - private readonly DrawableTopScore topScore; + private readonly FillFlowContainer topScoresContainer; private readonly LoadingAnimation loadingAnimation; [Resolved] @@ -54,7 +55,13 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Margin = new MarginPadding { Vertical = spacing }, Children = new Drawable[] { - topScore = new DrawableTopScore(), + topScoresContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + }, scoreTable = new ScoreTable { Anchor = Anchor.TopCentre, @@ -97,6 +104,20 @@ namespace osu.Game.Overlays.BeatmapSet.Scores } } + private APILegacyUserTopScoreInfo userScore; + + public APILegacyUserTopScoreInfo UserScore + { + get => userScore; + set + { + getScoresRequest?.Cancel(); + userScore = value; + + updateDisplay(); + } + } + private BeatmapInfo beatmap; public BeatmapInfo Beatmap @@ -114,7 +135,12 @@ namespace osu.Game.Overlays.BeatmapSet.Scores loading = true; getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); - getScoresRequest.Success += r => Schedule(() => Scores = r.Scores); + getScoresRequest.Success += r => Schedule(() => + { + scores = r.Scores; + userScore = r.UserScore; + updateDisplay(); + }); api.Queue(getScoresRequest); } } @@ -122,17 +148,18 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private void updateDisplay() { loading = false; + topScoresContainer.Clear(); scoreTable.Scores = scores?.Count > 1 ? scores : new List(); scoreTable.FadeTo(scores?.Count > 1 ? 1 : 0); if (scores?.Any() == true) { - topScore.Score = scores.FirstOrDefault(); - topScore.Show(); + topScoresContainer.Add(new DrawableTopScore(scores.FirstOrDefault())); + + if (userScore != null && userScore.Position != 1) + topScoresContainer.Add(new DrawableTopScore(userScore.Score, userScore.Position)); } - else - topScore.Hide(); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 1d9c4e7fc8..1314573cb4 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly SpriteText date; private readonly UpdateableFlag flag; - public TopScoreUserSection() + public TopScoreUserSection(int position) { AutoSizeAxes = Axes.Both; @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Text = "#1", + Text = position.ToString(), Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, italics: true) }, rank = new UpdateableRank(ScoreRank.D) From 5f3f59629ec04a69132f3419d8e2667795500ffb Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 8 Jul 2019 11:55:07 +0300 Subject: [PATCH 1650/2854] Use the length field instead of recalculating --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 26a83f930c..2551ffe2fc 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -291,7 +291,7 @@ namespace osu.Game.Screens.Select { Name = "Length", Icon = FontAwesome.Regular.Clock, - Content = TimeSpan.FromMilliseconds(b.CalculateLength()).ToString(@"m\:ss"), + Content = TimeSpan.FromMilliseconds(b.BeatmapInfo.Length).ToString(@"m\:ss"), })); labels.Add(new InfoLabel(new BeatmapStatistic From b62e69d170ebd0ed24c704a2a1ebf6397af32a82 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 8 Jul 2019 11:56:48 +0300 Subject: [PATCH 1651/2854] Calculate length inside BeatmapManager --- osu.Game/Beatmaps/BeatmapManager.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 56816607ee..6fe70b6ec6 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -23,6 +23,7 @@ using osu.Game.IO.Archives; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; +using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Beatmaps { @@ -302,7 +303,7 @@ namespace osu.Game.Beatmaps beatmap.BeatmapInfo.Ruleset = ruleset; // TODO: this should be done in a better place once we actually need to dynamically update it. beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0; - beatmap.BeatmapInfo.Length = beatmap.CalculateLength(); + beatmap.BeatmapInfo.Length = calculateLength(beatmap); beatmap.BeatmapInfo.BPM = beatmap.ControlPointInfo.BPMMode; beatmapInfos.Add(beatmap.BeatmapInfo); @@ -312,6 +313,14 @@ namespace osu.Game.Beatmaps return beatmapInfos; } + private double calculateLength(IBeatmap b) + { + var lastObject = b.HitObjects.LastOrDefault(); + var endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0; + + return endTime - b.HitObjects.FirstOrDefault()?.StartTime ?? 0; + } + /// /// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation. /// From 11ef65e3e2d72cd750f4dc1626c3373f0df92864 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 8 Jul 2019 11:57:02 +0300 Subject: [PATCH 1652/2854] Remove unnecessary extension --- osu.Game/Beatmaps/Beatmap.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 7fca13a1e2..4ebeee40bf 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -8,7 +8,6 @@ using System.Linq; using osu.Game.Beatmaps.ControlPoints; using Newtonsoft.Json; using osu.Game.IO.Serialization.Converters; -using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Beatmaps { @@ -62,15 +61,4 @@ namespace osu.Game.Beatmaps { public new Beatmap Clone() => (Beatmap)base.Clone(); } - - public static class BeatmapExtensions - { - public static double CalculateLength(this IBeatmap b) - { - HitObject lastObject = b.HitObjects.LastOrDefault(); - var endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0; - - return endTime - b.HitObjects.FirstOrDefault()?.StartTime ?? 0; - } - } } From d489a77fe18c3a3f06b1cccb733fdb8a410bcb39 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 8 Jul 2019 17:57:29 +0900 Subject: [PATCH 1653/2854] remove new container and comment --- osu.Game/Screens/Play/ReplayPlayerLoader.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/ReplayPlayerLoader.cs b/osu.Game/Screens/Play/ReplayPlayerLoader.cs index 329e799f7c..8681ae6887 100644 --- a/osu.Game/Screens/Play/ReplayPlayerLoader.cs +++ b/osu.Game/Screens/Play/ReplayPlayerLoader.cs @@ -18,9 +18,8 @@ namespace osu.Game.Screens.Play protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + var dependencies = base.CreateChildDependencies(parent); - // Overwrite the global mods here for use in the mod hud. Mods.Value = scoreInfo.Mods; Ruleset.Value = scoreInfo.Ruleset; From 59cfd39670c62df39ba8ce95f71e23c86395e7f1 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 8 Jul 2019 12:02:10 +0300 Subject: [PATCH 1654/2854] Add testcase --- .../Visual/Online/TestSceneScoresContainer.cs | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index 06414af865..e4e6acec06 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.MathUtils; using osu.Game.Graphics; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapSet.Scores; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; @@ -165,6 +166,29 @@ namespace osu.Game.Tests.Visual.Online }, }; + var myBestScore = new APILegacyUserTopScoreInfo + { + Score = new APILegacyScoreInfo + { + User = new User + { + Id = 7151382, + Username = @"Mayuri Hana", + Country = new Country + { + FullName = @"Thailand", + FlagName = @"TH", + }, + }, + Rank = ScoreRank.D, + PP = 160, + MaxCombo = 1234, + TotalScore = 123456, + Accuracy = 0.6543, + }, + Position = 1337, + }; + foreach (var s in scores) { s.Statistics.Add(HitResult.Great, RNG.Next(2000)); @@ -173,9 +197,18 @@ namespace osu.Game.Tests.Visual.Online s.Statistics.Add(HitResult.Miss, RNG.Next(2000)); } - AddStep("Load all scores", () => scoresContainer.Scores = scores); - AddStep("Load null scores", () => scoresContainer.Scores = null); + AddStep("Load all scores", () => + { + scoresContainer.Scores = scores; + scoresContainer.UserScore = myBestScore; + }); + AddStep("Load null scores", () => + { + scoresContainer.Scores = null; + scoresContainer.UserScore = null; + }); AddStep("Load only one score", () => scoresContainer.Scores = new[] { scores.First() }); + AddStep("Add my best score", () => scoresContainer.UserScore = myBestScore); } [BackgroundDependencyLoader] From 1e5639acee4858571dfe4c832354a04fef38432d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 8 Jul 2019 12:10:08 +0300 Subject: [PATCH 1655/2854] Add forgotten symbol --- osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 1314573cb4..385d8ff38c 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Text = position.ToString(), + Text = $"#{position.ToString()}", Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, italics: true) }, rank = new UpdateableRank(ScoreRank.D) From 39f04e497d5734ec57393b7a974cb255540f4ae6 Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Mon, 8 Jul 2019 11:24:06 +0200 Subject: [PATCH 1656/2854] Add UserRequestedPause --- osu.Game/Overlays/MusicController.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 29ae5983be..3db71d39ee 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -66,6 +66,8 @@ namespace osu.Game.Overlays /// public Func GetToolbarHeight; + public bool UserRequestedPause { get; private set; } + public MusicController() { Width = 400; @@ -287,6 +289,8 @@ namespace osu.Game.Overlays return; } + UserRequestedPause = track.IsRunning; + if (track.IsRunning) track.Stop(); else From 0cf4bf2352048fd6feca7495a69b6d9e09b992f9 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 8 Jul 2019 18:46:12 +0900 Subject: [PATCH 1657/2854] Manually set clock for storyboard if loading before being given a parent --- osu.Game/Screens/Play/Player.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0da9c77f25..ea614e7658 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -362,7 +362,12 @@ namespace osu.Game.Screens.Play storyboard.Masking = true; if (asyncLoad) - LoadComponentAsync(storyboard, StoryboardContainer.Add); + LoadComponentAsync(storyboard, c => + { + // Since the storyboard was loaded before it can be added to the draw hierarchy, manually set the clock for it here. + c.Clock = GameplayClockContainer.GameplayClock; + StoryboardContainer.Add(c); + }); else StoryboardContainer.Add(storyboard); } From 581ffb7fb09d50fc0b64bb422841f830c61d1ac7 Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Mon, 8 Jul 2019 13:52:15 +0200 Subject: [PATCH 1658/2854] Adjusted slider border colour --- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index 1d68793c50..4918ea60b2 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -64,14 +64,13 @@ namespace osu.Game.Rulesets.Osu.Mods case DrawableSlider slider: ApplyTraceableState(slider.HeadCircle, state); slider.Body.AccentColour = Color4.Transparent; - + slider.Body.BorderColour = slider.HeadCircle.AccentColour; break; case DrawableSpinner spinner: spinner.Disc.Hide(); //spinner.Ticks.Hide(); // do they contribute to the theme? debatable spinner.Background.Hide(); - break; } } From 54f5e6aedf4ffcce98f62a830020f1252eb42d93 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 Jul 2019 22:37:39 +0900 Subject: [PATCH 1659/2854] Add assertion and comment about lease logic --- osu.Game/Screens/Play/ReplayPlayerLoader.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/Play/ReplayPlayerLoader.cs b/osu.Game/Screens/Play/ReplayPlayerLoader.cs index 8681ae6887..86179ef067 100644 --- a/osu.Game/Screens/Play/ReplayPlayerLoader.cs +++ b/osu.Game/Screens/Play/ReplayPlayerLoader.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Game.Scoring; @@ -13,6 +14,9 @@ namespace osu.Game.Screens.Play public ReplayPlayerLoader(Score score) : base(() => new ReplayPlayer(score)) { + if (score.Replay == null) + throw new ArgumentNullException(nameof(score.Replay), $"{nameof(score)} must have a non-null {nameof(score.Replay)}."); + scoreInfo = score.ScoreInfo; } @@ -20,6 +24,7 @@ namespace osu.Game.Screens.Play { var dependencies = base.CreateChildDependencies(parent); + // these will be reverted thanks to PlayerLoader's lease. Mods.Value = scoreInfo.Mods; Ruleset.Value = scoreInfo.Ruleset; From 338371c3fcec0e9c45ac7ec440e732e083eae972 Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Tue, 9 Jul 2019 00:08:18 +0200 Subject: [PATCH 1660/2854] Fix music playing while exiting from editor --- osu.Game/OsuGame.cs | 6 +++--- osu.Game/Screens/Edit/Editor.cs | 6 ------ osu.Game/Screens/Menu/MainMenu.cs | 4 +++- osu.Game/Screens/OsuScreen.cs | 11 ++++++++++- osu.Game/Screens/Select/SongSelect.cs | 2 ++ 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 0a472d4dc1..325d2cbd85 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -306,7 +306,7 @@ namespace osu.Game private void currentTrackCompleted() { if (!Beatmap.Value.Track.Looping && !Beatmap.Disabled) - musicController.NextTrack(); + MusicController.NextTrack(); } #endregion @@ -484,7 +484,7 @@ namespace osu.Game Origin = Anchor.TopRight, }, rightFloatingOverlayContent.Add, true); - loadComponentSingleFile(musicController = new MusicController + loadComponentSingleFile(MusicController = new MusicController { GetToolbarHeight = () => ToolbarOffset, Anchor = Anchor.TopRight, @@ -752,7 +752,7 @@ namespace osu.Game private ScalingContainer screenContainer; - private MusicController musicController; + public MusicController MusicController { get; private set; } protected override bool OnExiting() { diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 89da9ae063..676e060433 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -239,12 +239,6 @@ namespace osu.Game.Screens.Edit { Background.FadeColour(Color4.White, 500); - if (Beatmap.Value.Track != null) - { - Beatmap.Value.Track.Tempo.Value = 1; - Beatmap.Value.Track.Start(); - } - return base.OnExiting(next); } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index c64bea840f..dcce49179d 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -120,7 +120,7 @@ namespace osu.Game.Screens.Menu var track = Beatmap.Value.Track; var metadata = Beatmap.Value.Metadata; - if (last is Intro && track != null) + if (last is Intro && track != null && !Game.MusicController.UserRequestedPause) { if (!track.IsRunning) { @@ -189,6 +189,8 @@ namespace osu.Game.Screens.Menu //we may have consumed our preloaded instance, so let's make another. preloadSongSelect(); + + ResumeIfNoUserPauseRequested(); } public override bool OnExiting(IScreen next) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 328631ff9c..0682710133 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -50,7 +50,7 @@ namespace osu.Game.Screens public virtual bool CursorVisible => true; - protected new OsuGameBase Game => base.Game as OsuGameBase; + protected new OsuGame Game => base.Game as OsuGame; /// /// The to set the user's activity automatically to when this screen is entered @@ -179,6 +179,15 @@ namespace osu.Game.Screens api.Activity.Value = activity; } + protected void ResumeIfNoUserPauseRequested() + { + if (Beatmap.Value.Track != null && !Game.MusicController.UserRequestedPause) + { + Beatmap.Value.Track.Tempo.Value = 1; + Beatmap.Value.Track.Start(); + } + } + /// /// Fired when this screen was entered or resumed and the logo state is required to be adjusted. /// diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index bf5857f725..17b2ae376f 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -426,6 +426,8 @@ namespace osu.Game.Screens.Select { base.OnEntering(last); + ResumeIfNoUserPauseRequested(); + this.FadeInFromZero(250); FilterControl.Activate(); } From 8f7476e9ccf2f8dd3744315cb56a852d5ff4197a Mon Sep 17 00:00:00 2001 From: Oskar Solecki <31374466+Desconocidosmh@users.noreply.github.com> Date: Tue, 9 Jul 2019 00:26:57 +0200 Subject: [PATCH 1661/2854] Remove unused stuff --- osu.Game/Screens/Edit/Editor.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index cffe756548..676e060433 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -168,8 +168,6 @@ namespace osu.Game.Screens.Edit menuBar.Mode.ValueChanged += onModeChanged; - host.Exiting += onExitingGame; - bottomBackground.Colour = colours.Gray2; } @@ -237,8 +235,6 @@ namespace osu.Game.Screens.Edit Beatmap.Value.Track?.Stop(); } - private bool isExitingGame = false; - public override bool OnExiting(IScreen next) { Background.FadeColour(Color4.White, 500); @@ -279,7 +275,5 @@ namespace osu.Game.Screens.Edit else clock.SeekForward(!clock.IsRunning, amount); } - - private bool onExitingGame() => isExitingGame = true; } } From 5d81445454bd03c1018bce7c660e65fc931af915 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 9 Jul 2019 08:05:34 +0300 Subject: [PATCH 1662/2854] Move api request outside the scores container --- .../Visual/Online/TestSceneScoresContainer.cs | 243 ++++++++++-------- .../BeatmapSet/Scores/ScoresContainer.cs | 82 ++---- osu.Game/Overlays/BeatmapSetOverlay.cs | 30 ++- 3 files changed, 184 insertions(+), 171 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index e4e6acec06..730bf0d4e2 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs @@ -50,120 +50,123 @@ namespace osu.Game.Tests.Visual.Online } }; - var scores = new List + var allScores = new APILegacyScores { - new ScoreInfo + Scores = new List { - User = new User + new APILegacyScoreInfo { - Id = 6602580, - Username = @"waaiiru", - Country = new Country + User = new User { - FullName = @"Spain", - FlagName = @"ES", + Id = 6602580, + Username = @"waaiiru", + Country = new Country + { + FullName = @"Spain", + FlagName = @"ES", + }, }, - }, - Mods = new Mod[] - { - new OsuModDoubleTime(), - new OsuModHidden(), - new OsuModFlashlight(), - new OsuModHardRock(), - }, - Rank = ScoreRank.XH, - PP = 200, - MaxCombo = 1234, - TotalScore = 1234567890, - Accuracy = 1, - }, - new ScoreInfo - { - User = new User - { - Id = 4608074, - Username = @"Skycries", - Country = new Country + Mods = new Mod[] { - FullName = @"Brazil", - FlagName = @"BR", + new OsuModDoubleTime(), + new OsuModHidden(), + new OsuModFlashlight(), + new OsuModHardRock(), }, + Rank = ScoreRank.XH, + PP = 200, + MaxCombo = 1234, + TotalScore = 1234567890, + Accuracy = 1, }, - Mods = new Mod[] + new APILegacyScoreInfo { - new OsuModDoubleTime(), - new OsuModHidden(), - new OsuModFlashlight(), - }, - Rank = ScoreRank.S, - PP = 190, - MaxCombo = 1234, - TotalScore = 1234789, - Accuracy = 0.9997, - }, - new ScoreInfo - { - User = new User - { - Id = 1014222, - Username = @"eLy", - Country = new Country + User = new User { - FullName = @"Japan", - FlagName = @"JP", + Id = 4608074, + Username = @"Skycries", + Country = new Country + { + FullName = @"Brazil", + FlagName = @"BR", + }, }, - }, - Mods = new Mod[] - { - new OsuModDoubleTime(), - new OsuModHidden(), - }, - Rank = ScoreRank.B, - PP = 180, - MaxCombo = 1234, - TotalScore = 12345678, - Accuracy = 0.9854, - }, - new ScoreInfo - { - User = new User - { - Id = 1541390, - Username = @"Toukai", - Country = new Country + Mods = new Mod[] { - FullName = @"Canada", - FlagName = @"CA", + new OsuModDoubleTime(), + new OsuModHidden(), + new OsuModFlashlight(), }, + Rank = ScoreRank.S, + PP = 190, + MaxCombo = 1234, + TotalScore = 1234789, + Accuracy = 0.9997, }, - Mods = new Mod[] + new APILegacyScoreInfo { - new OsuModDoubleTime(), - }, - Rank = ScoreRank.C, - PP = 170, - MaxCombo = 1234, - TotalScore = 1234567, - Accuracy = 0.8765, - }, - new ScoreInfo - { - User = new User - { - Id = 7151382, - Username = @"Mayuri Hana", - Country = new Country + User = new User { - FullName = @"Thailand", - FlagName = @"TH", + Id = 1014222, + Username = @"eLy", + Country = new Country + { + FullName = @"Japan", + FlagName = @"JP", + }, }, + Mods = new Mod[] + { + new OsuModDoubleTime(), + new OsuModHidden(), + }, + Rank = ScoreRank.B, + PP = 180, + MaxCombo = 1234, + TotalScore = 12345678, + Accuracy = 0.9854, }, - Rank = ScoreRank.D, - PP = 160, - MaxCombo = 1234, - TotalScore = 123456, - Accuracy = 0.6543, - }, + new APILegacyScoreInfo + { + User = new User + { + Id = 1541390, + Username = @"Toukai", + Country = new Country + { + FullName = @"Canada", + FlagName = @"CA", + }, + }, + Mods = new Mod[] + { + new OsuModDoubleTime(), + }, + Rank = ScoreRank.C, + PP = 170, + MaxCombo = 1234, + TotalScore = 1234567, + Accuracy = 0.8765, + }, + new APILegacyScoreInfo + { + User = new User + { + Id = 7151382, + Username = @"Mayuri Hana", + Country = new Country + { + FullName = @"Thailand", + FlagName = @"TH", + }, + }, + Rank = ScoreRank.D, + PP = 160, + MaxCombo = 1234, + TotalScore = 123456, + Accuracy = 0.6543, + }, + } }; var myBestScore = new APILegacyUserTopScoreInfo @@ -189,7 +192,39 @@ namespace osu.Game.Tests.Visual.Online Position = 1337, }; - foreach (var s in scores) + var oneScore = new APILegacyScores + { + Scores = new List + { + new APILegacyScoreInfo + { + User = new User + { + Id = 6602580, + Username = @"waaiiru", + Country = new Country + { + FullName = @"Spain", + FlagName = @"ES", + }, + }, + Mods = new Mod[] + { + new OsuModDoubleTime(), + new OsuModHidden(), + new OsuModFlashlight(), + new OsuModHardRock(), + }, + Rank = ScoreRank.XH, + PP = 200, + MaxCombo = 1234, + TotalScore = 1234567890, + Accuracy = 1, + } + } + }; + + foreach (var s in allScores.Scores) { s.Statistics.Add(HitResult.Great, RNG.Next(2000)); s.Statistics.Add(HitResult.Good, RNG.Next(2000)); @@ -199,16 +234,16 @@ namespace osu.Game.Tests.Visual.Online AddStep("Load all scores", () => { - scoresContainer.Scores = scores; - scoresContainer.UserScore = myBestScore; + allScores.UserScore = null; + scoresContainer.Scores = allScores; }); - AddStep("Load null scores", () => + AddStep("Load null scores", () => scoresContainer.Scores = null); + AddStep("Load only one score", () => scoresContainer.Scores = oneScore); + AddStep("Load scores with my best", () => { - scoresContainer.Scores = null; - scoresContainer.UserScore = null; + allScores.UserScore = myBestScore; + scoresContainer.Scores = allScores; }); - AddStep("Load only one score", () => scoresContainer.Scores = new[] { scores.First() }); - AddStep("Add my best score", () => scoresContainer.UserScore = myBestScore); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 30685fb826..7e0f3d0b1c 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -5,15 +5,11 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osuTK; using System.Collections.Generic; using System.Linq; -using osu.Game.Scoring; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.BeatmapSet.Scores @@ -29,9 +25,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly FillFlowContainer topScoresContainer; private readonly LoadingAnimation loadingAnimation; - [Resolved] - private IAPIProvider api { get; set; } - public ScoresContainer() { RelativeSizeAxes = Axes.X; @@ -72,7 +65,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores loadingAnimation = new LoadingAnimation { Alpha = 0, - Margin = new MarginPadding(20) + Margin = new MarginPadding(20), }, }; } @@ -84,87 +77,46 @@ namespace osu.Game.Overlays.BeatmapSet.Scores updateDisplay(); } - private bool loading + public bool Loading { - set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration); + set + { + loadingAnimation.FadeTo(value ? 1 : 0, fade_duration); + + if (value) + Scores = null; + } } - private GetScoresRequest getScoresRequest; - private IReadOnlyList scores; + private APILegacyScores scores; - public IReadOnlyList Scores + public APILegacyScores Scores { get => scores; set { - getScoresRequest?.Cancel(); scores = value; updateDisplay(); } } - private APILegacyUserTopScoreInfo userScore; - - public APILegacyUserTopScoreInfo UserScore - { - get => userScore; - set - { - getScoresRequest?.Cancel(); - userScore = value; - - updateDisplay(); - } - } - - private BeatmapInfo beatmap; - - public BeatmapInfo Beatmap - { - get => beatmap; - set - { - beatmap = value; - - Scores = null; - - if (beatmap?.OnlineBeatmapID.HasValue != true) - return; - - loading = true; - - getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); - getScoresRequest.Success += r => Schedule(() => - { - scores = r.Scores; - userScore = r.UserScore; - updateDisplay(); - }); - api.Queue(getScoresRequest); - } - } - private void updateDisplay() { - loading = false; topScoresContainer.Clear(); - scoreTable.Scores = scores?.Count > 1 ? scores : new List(); - scoreTable.FadeTo(scores?.Count > 1 ? 1 : 0); + scoreTable.Scores = scores?.Scores.Count > 1 ? scores.Scores : new List(); + scoreTable.FadeTo(scores?.Scores.Count > 1 ? 1 : 0); - if (scores?.Any() == true) + if (scores?.Scores.Any() == true) { - topScoresContainer.Add(new DrawableTopScore(scores.FirstOrDefault())); + topScoresContainer.Add(new DrawableTopScore(scores.Scores.FirstOrDefault())); + + var userScore = scores.UserScore; if (userScore != null && userScore.Position != 1) topScoresContainer.Add(new DrawableTopScore(userScore.Score, userScore.Position)); } } - - protected override void Dispose(bool isDisposing) - { - getScoresRequest?.Cancel(); - } } } diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 19f6a3f692..df132f9d47 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -11,6 +11,7 @@ using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.BeatmapSet; using osu.Game.Overlays.BeatmapSet.Scores; @@ -30,9 +31,14 @@ namespace osu.Game.Overlays protected readonly Header Header; private RulesetStore rulesets; + private ScoresContainer scores; + private GetScoresRequest getScoresRequest; private readonly Bindable beatmapSet = new Bindable(); + [Resolved] + private IAPIProvider api { get; set; } + // receive input outside our bounds so we can trigger a close event on ourselves. public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; @@ -40,7 +46,6 @@ namespace osu.Game.Overlays { OsuScrollContainer scroll; Info info; - ScoresContainer scores; Children = new Drawable[] { @@ -74,12 +79,33 @@ namespace osu.Game.Overlays Header.Picker.Beatmap.ValueChanged += b => { info.Beatmap = b.NewValue; - scores.Beatmap = b.NewValue; + getScores(b.NewValue); scroll.ScrollToStart(); }; } + private void getScores(BeatmapInfo b) + { + getScoresRequest?.Cancel(); + + if (b?.OnlineBeatmapID.HasValue != true) + { + scores.Scores = null; + return; + } + + scores.Loading = true; + + getScoresRequest = new GetScoresRequest(b, b.Ruleset); + getScoresRequest.Success += r => Schedule(() => + { + scores.Scores = r; + scores.Loading = false; + }); + api.Queue(getScoresRequest); + } + [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { From 8d46d4a28e6fc0a6ba3c72df86faac97c4439dfd Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 9 Jul 2019 08:09:31 +0300 Subject: [PATCH 1663/2854] Fix grade layout --- .../BeatmapSet/Scores/TopScoreUserSection.cs | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 385d8ff38c..056fe71a39 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -39,19 +39,30 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Spacing = new Vector2(10, 0), Children = new Drawable[] { - rankText = new OsuSpriteText + new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Text = $"#{position.ToString()}", - Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, italics: true) - }, - rank = new UpdateableRank(ScoreRank.D) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(40), - FillMode = FillMode.Fit, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 3), + Children = new Drawable[] + { + rankText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = $"#{position.ToString()}", + Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, italics: true) + }, + rank = new UpdateableRank(ScoreRank.D) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(40), + FillMode = FillMode.Fit, + }, + } }, avatar = new UpdateableAvatar(hideImmediately: true) { From eb4ef8f6ac8a2c32cb53f4f1cc006e687e7252dc Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 9 Jul 2019 08:25:10 +0300 Subject: [PATCH 1664/2854] CI fixes --- osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs | 1 - osu.Game/Overlays/BeatmapSetOverlay.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index 730bf0d4e2..827a300a5e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index df132f9d47..44475dc53c 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays protected readonly Header Header; private RulesetStore rulesets; - private ScoresContainer scores; + private readonly ScoresContainer scores; private GetScoresRequest getScoresRequest; private readonly Bindable beatmapSet = new Bindable(); From 89cb8a0cacb4ac50a1b83cbf32923d40ba5d13d7 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 9 Jul 2019 16:23:59 +0900 Subject: [PATCH 1665/2854] Move storyboard initialization to new StoryboardContainer --- .../TestSceneBackgroundScreenBeatmap.cs | 26 ++++---- .../Containers/StoryboardContainer.cs | 59 +++++++++++++++++++ .../Graphics/Containers/UserDimContainer.cs | 52 ++++++++-------- osu.Game/Screens/Play/Player.cs | 42 ++----------- 4 files changed, 103 insertions(+), 76 deletions(-) create mode 100644 osu.Game/Graphics/Containers/StoryboardContainer.cs diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs index 8b941e4633..0d62bf9bdc 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs @@ -28,6 +28,7 @@ using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Play; using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Screens.Select; +using osu.Game.Storyboards; using osu.Game.Tests.Resources; using osu.Game.Users; using osuTK; @@ -333,9 +334,9 @@ namespace osu.Game.Tests.Visual.Background { protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); - protected override UserDimContainer CreateStoryboardContainer() + protected override StoryboardContainer CreateStoryboardContainer(Storyboard storyboard) { - return new TestUserDimContainer(true) + return new TestStoryboardContainer { RelativeSizeAxes = Axes.Both, Alpha = 1, @@ -343,7 +344,7 @@ namespace osu.Game.Tests.Visual.Background }; } - public UserDimContainer CurrentStoryboardContainer => StoryboardContainer; + public TestStoryboardContainer CurrentStoryboardContainer => (TestStoryboardContainer)StoryboardContainer; // Whether or not the player should be allowed to load. public bool BlockLoad; @@ -357,9 +358,9 @@ namespace osu.Game.Tests.Visual.Background { } - public bool IsStoryboardVisible() => ((TestUserDimContainer)CurrentStoryboardContainer).CurrentAlpha == 1; + public bool IsStoryboardVisible() => CurrentStoryboardContainer.CurrentAlpha == 1; - public bool IsStoryboardInvisible() => ((TestUserDimContainer)CurrentStoryboardContainer).CurrentAlpha <= 1; + public bool IsStoryboardInvisible() => CurrentStoryboardContainer.CurrentAlpha <= 1; [BackgroundDependencyLoader] private void load(OsuConfigManager config, CancellationToken token) @@ -408,15 +409,20 @@ namespace osu.Game.Tests.Visual.Background } } + private class TestStoryboardContainer : StoryboardContainer + { + public float CurrentAlpha => DimContainer.Alpha; + + public TestStoryboardContainer() + : base(new Storyboard()) + { + } + } + private class TestUserDimContainer : UserDimContainer { public Color4 CurrentColour => DimContainer.Colour; public float CurrentAlpha => DimContainer.Alpha; - - public TestUserDimContainer(bool isStoryboard = false) - : base(isStoryboard) - { - } } } } diff --git a/osu.Game/Graphics/Containers/StoryboardContainer.cs b/osu.Game/Graphics/Containers/StoryboardContainer.cs new file mode 100644 index 0000000000..c425d38d48 --- /dev/null +++ b/osu.Game/Graphics/Containers/StoryboardContainer.cs @@ -0,0 +1,59 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Storyboards; +using osu.Game.Storyboards.Drawables; + +namespace osu.Game.Graphics.Containers +{ + /// + /// A container that handles loading, as well as applies user-specified visual settings to it. + /// + public class StoryboardContainer : UserDimContainer + { + private readonly Storyboard storyboard; + private DrawableStoryboard drawableStoryboard; + + public StoryboardContainer(Storyboard storyboard) + { + this.storyboard = storyboard; + } + + [BackgroundDependencyLoader] + private void load() + { + initializeStoryboard(false); + } + + protected override void LoadComplete() + { + ShowStoryboard.ValueChanged += _ => initializeStoryboard(true); + base.LoadComplete(); + } + + protected override void ApplyFade() + { + // Storyboards cannot be blurred, so we should just hide the storyboard if it gets toggled. + DimContainer.FadeTo(!ShowStoryboard.Value || UserDimLevel.Value == 1 ? 0 : 1, BACKGROUND_FADE_DURATION, Easing.OutQuint); + } + + private void initializeStoryboard(bool async) + { + if (drawableStoryboard != null) + return; + + if (!ShowStoryboard.Value) + return; + + drawableStoryboard = storyboard.CreateDrawable(); + drawableStoryboard.Masking = true; + + if (async) + LoadComponentAsync(drawableStoryboard, Add); + else + Add(drawableStoryboard); + } + } +} diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index fe9eb7baf4..b14051f432 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -16,11 +16,10 @@ namespace osu.Game.Graphics.Containers { /// /// A container that applies user-configured visual settings to its contents. - /// This container specifies behavior that applies to both Storyboards and Backgrounds. /// public class UserDimContainer : Container { - private const float background_fade_duration = 800; + protected const float BACKGROUND_FADE_DURATION = 800; /// /// Whether or not user-configured dim levels should be applied to the container. @@ -40,17 +39,15 @@ namespace osu.Game.Graphics.Containers /// public readonly Bindable BlurAmount = new Bindable(); - private Bindable userDimLevel { get; set; } + protected Bindable UserDimLevel { get; private set; } - private Bindable userBlurLevel { get; set; } - - private Bindable showStoryboard { get; set; } + protected Bindable ShowStoryboard { get; private set; } protected Container DimContainer { get; } protected override Container Content => DimContainer; - private readonly bool isStoryboard; + private Bindable userBlurLevel { get; set; } /// /// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs. @@ -62,15 +59,12 @@ namespace osu.Game.Graphics.Containers /// /// Creates a new . /// - /// Whether or not this instance contains a storyboard. /// - /// While both backgrounds and storyboards allow user dim levels to be applied, storyboards can be toggled via + /// While both backgrounds and storyboards allow user dim levels to be applied, storyboards can be toggled via /// and can cause backgrounds to become hidden via . Storyboards are also currently unable to be blurred. /// - /// - public UserDimContainer(bool isStoryboard = false) + public UserDimContainer() { - this.isStoryboard = isStoryboard; AddInternal(DimContainer = new Container { RelativeSizeAxes = Axes.Both }); } @@ -97,16 +91,16 @@ namespace osu.Game.Graphics.Containers [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - userDimLevel = config.GetBindable(OsuSetting.DimLevel); + UserDimLevel = config.GetBindable(OsuSetting.DimLevel); userBlurLevel = config.GetBindable(OsuSetting.BlurLevel); - showStoryboard = config.GetBindable(OsuSetting.ShowStoryboard); + ShowStoryboard = config.GetBindable(OsuSetting.ShowStoryboard); EnableUserDim.ValueChanged += _ => updateVisuals(); - userDimLevel.ValueChanged += _ => updateVisuals(); - userBlurLevel.ValueChanged += _ => updateVisuals(); - showStoryboard.ValueChanged += _ => updateVisuals(); + UserDimLevel.ValueChanged += _ => updateVisuals(); + ShowStoryboard.ValueChanged += _ => updateVisuals(); StoryboardReplacesBackground.ValueChanged += _ => updateVisuals(); BlurAmount.ValueChanged += _ => updateVisuals(); + userBlurLevel.ValueChanged += _ => updateVisuals(); } protected override void LoadComplete() @@ -115,21 +109,21 @@ namespace osu.Game.Graphics.Containers updateVisuals(); } + /// + /// Apply non-dim related settings to the background, such as hiding and blurring. + /// + protected virtual void ApplyFade() + { + // The background needs to be hidden in the case of it being replaced by the storyboard + DimContainer.FadeTo(ShowStoryboard.Value && StoryboardReplacesBackground.Value ? 0 : 1, BACKGROUND_FADE_DURATION, Easing.OutQuint); + Background?.BlurTo(blurTarget, BACKGROUND_FADE_DURATION, Easing.OutQuint); + } + private void updateVisuals() { - if (isStoryboard) - { - DimContainer.FadeTo(!showStoryboard.Value || userDimLevel.Value == 1 ? 0 : 1, background_fade_duration, Easing.OutQuint); - } - else - { - // The background needs to be hidden in the case of it being replaced by the storyboard - DimContainer.FadeTo(showStoryboard.Value && StoryboardReplacesBackground.Value ? 0 : 1, background_fade_duration, Easing.OutQuint); + ApplyFade(); - Background?.BlurTo(blurTarget, background_fade_duration, Easing.OutQuint); - } - - DimContainer.FadeColour(EnableUserDim.Value ? OsuColour.Gray(1 - (float)userDimLevel.Value) : Color4.White, background_fade_duration, Easing.OutQuint); + DimContainer.FadeColour(EnableUserDim.Value ? OsuColour.Gray(1 - (float)UserDimLevel.Value) : Color4.White, BACKGROUND_FADE_DURATION, Easing.OutQuint); } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ea614e7658..621df0b562 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -26,7 +26,7 @@ using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Skinning; -using osu.Game.Storyboards.Drawables; +using osu.Game.Storyboards; using osu.Game.Users; namespace osu.Game.Screens.Play @@ -109,7 +109,7 @@ namespace osu.Game.Screens.Play sampleRestart = audio.Samples.Get(@"Gameplay/restart"); mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); - showStoryboard = config.GetBindable(OsuSetting.ShowStoryboard); + config.GetBindable(OsuSetting.ShowStoryboard); ScoreProcessor = DrawableRuleset.CreateScoreProcessor(); ScoreProcessor.Mods.BindTo(Mods); @@ -121,7 +121,7 @@ namespace osu.Game.Screens.Play GameplayClockContainer.Children = new[] { - StoryboardContainer = CreateStoryboardContainer(), + StoryboardContainer = CreateStoryboardContainer(Beatmap.Value.Storyboard), new ScalingContainer(ScalingMode.Gameplay) { Child = new LocalSkinOverrideContainer(working.Skin) @@ -199,9 +199,6 @@ namespace osu.Game.Screens.Play // bind clock into components that require it DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused); - // load storyboard as part of player's load if we can - initializeStoryboard(false); - // Bind ScoreProcessor to ourselves ScoreProcessor.AllJudged += onCompletion; ScoreProcessor.Failed += onFail; @@ -336,42 +333,15 @@ namespace osu.Game.Screens.Play #region Storyboard - private DrawableStoryboard storyboard; - protected UserDimContainer StoryboardContainer { get; private set; } + protected StoryboardContainer StoryboardContainer { get; private set; } - protected virtual UserDimContainer CreateStoryboardContainer() => new UserDimContainer(true) + protected virtual StoryboardContainer CreateStoryboardContainer(Storyboard storyboard) => new StoryboardContainer(storyboard) { RelativeSizeAxes = Axes.Both, Alpha = 1, EnableUserDim = { Value = true } }; - private Bindable showStoryboard; - - private void initializeStoryboard(bool asyncLoad) - { - if (StoryboardContainer == null || storyboard != null) - return; - - if (!showStoryboard.Value) - return; - - var beatmap = Beatmap.Value; - - storyboard = beatmap.Storyboard.CreateDrawable(); - storyboard.Masking = true; - - if (asyncLoad) - LoadComponentAsync(storyboard, c => - { - // Since the storyboard was loaded before it can be added to the draw hierarchy, manually set the clock for it here. - c.Clock = GameplayClockContainer.GameplayClock; - StoryboardContainer.Add(c); - }); - else - StoryboardContainer.Add(storyboard); - } - #endregion #region Fail Logic @@ -491,8 +461,6 @@ namespace osu.Game.Screens.Play .Delay(250) .FadeIn(250); - showStoryboard.ValueChanged += _ => initializeStoryboard(true); - Background.EnableUserDim.Value = true; Background.BlurAmount.Value = 0; From 1b5fadf93f468ea0c6894cba50e80f03797d48f0 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 9 Jul 2019 16:38:12 +0900 Subject: [PATCH 1666/2854] move comment to more relevant location --- osu.Game/Graphics/Containers/UserDimContainer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index b14051f432..89c1821425 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -59,10 +59,6 @@ namespace osu.Game.Graphics.Containers /// /// Creates a new . /// - /// - /// While both backgrounds and storyboards allow user dim levels to be applied, storyboards can be toggled via - /// and can cause backgrounds to become hidden via . Storyboards are also currently unable to be blurred. - /// public UserDimContainer() { AddInternal(DimContainer = new Container { RelativeSizeAxes = Axes.Both }); @@ -112,6 +108,10 @@ namespace osu.Game.Graphics.Containers /// /// Apply non-dim related settings to the background, such as hiding and blurring. /// + /// + /// While both backgrounds and storyboards allow user dim levels to be applied, storyboards can be toggled via + /// and can cause backgrounds to become hidden via . Storyboards are also currently unable to be blurred. + /// protected virtual void ApplyFade() { // The background needs to be hidden in the case of it being replaced by the storyboard From 5bb21ecae0aa027c80ef2108ac3a1991495ed6f3 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 9 Jul 2019 16:50:37 +0900 Subject: [PATCH 1667/2854] remove storyboard region --- .../Containers/StoryboardContainer.cs | 2 +- .../Graphics/Containers/UserDimContainer.cs | 2 +- osu.Game/Screens/Play/Player.cs | 22 ++++++++----------- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/osu.Game/Graphics/Containers/StoryboardContainer.cs b/osu.Game/Graphics/Containers/StoryboardContainer.cs index c425d38d48..472e22e212 100644 --- a/osu.Game/Graphics/Containers/StoryboardContainer.cs +++ b/osu.Game/Graphics/Containers/StoryboardContainer.cs @@ -35,7 +35,7 @@ namespace osu.Game.Graphics.Containers protected override void ApplyFade() { - // Storyboards cannot be blurred, so we should just hide the storyboard if it gets toggled. + // Storyboards cannot be blurred, so just hide the storyboard if it gets toggled. DimContainer.FadeTo(!ShowStoryboard.Value || UserDimLevel.Value == 1 ? 0 : 1, BACKGROUND_FADE_DURATION, Easing.OutQuint); } diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index 89c1821425..ad6f73eff5 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -106,7 +106,7 @@ namespace osu.Game.Graphics.Containers } /// - /// Apply non-dim related settings to the background, such as hiding and blurring. + /// Apply non-dim related settings to the content, such as hiding and blurring. /// /// /// While both backgrounds and storyboards allow user dim levels to be applied, storyboards can be toggled via diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 621df0b562..d9e050a681 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -76,6 +76,15 @@ namespace osu.Game.Screens.Play protected GameplayClockContainer GameplayClockContainer { get; private set; } + protected StoryboardContainer StoryboardContainer { get; private set; } + + protected virtual StoryboardContainer CreateStoryboardContainer(Storyboard storyboard) => new StoryboardContainer(storyboard) + { + RelativeSizeAxes = Axes.Both, + Alpha = 1, + EnableUserDim = { Value = true } + }; + [Cached] [Cached(Type = typeof(IBindable>))] protected new readonly Bindable> Mods = new Bindable>(Array.Empty()); @@ -331,19 +340,6 @@ namespace osu.Game.Screens.Play protected virtual Results CreateResults(ScoreInfo score) => new SoloResults(score); - #region Storyboard - - protected StoryboardContainer StoryboardContainer { get; private set; } - - protected virtual StoryboardContainer CreateStoryboardContainer(Storyboard storyboard) => new StoryboardContainer(storyboard) - { - RelativeSizeAxes = Axes.Both, - Alpha = 1, - EnableUserDim = { Value = true } - }; - - #endregion - #region Fail Logic protected FailOverlay FailOverlay { get; private set; } From 8d6af1625abda2a84879c508419e116c57448e75 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 9 Jul 2019 11:40:51 +0300 Subject: [PATCH 1668/2854] Visibility improvements --- .../Visual/Online/TestSceneScoresContainer.cs | 1 + .../BeatmapSet/Scores/ScoresContainer.cs | 142 ++++++++++++++---- osu.Game/Overlays/BeatmapSetOverlay.cs | 6 +- 3 files changed, 118 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index 827a300a5e..c414b6b940 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs @@ -243,6 +243,7 @@ namespace osu.Game.Tests.Visual.Online allScores.UserScore = myBestScore; scoresContainer.Scores = allScores; }); + AddStep("Trigger loading", () => scoresContainer.Loading = !scoresContainer.Loading); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 7e0f3d0b1c..eacbe6200a 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -17,13 +17,14 @@ namespace osu.Game.Overlays.BeatmapSet.Scores public class ScoresContainer : CompositeDrawable { private const int spacing = 15; - private const int fade_duration = 200; + private const int padding = 20; private readonly Box background; private readonly ScoreTable scoreTable; private readonly FillFlowContainer topScoresContainer; - private readonly LoadingAnimation loadingAnimation; + private readonly ContentContainer resizableContainer; + private readonly LoadingContainer loadingContainer; public ScoresContainer() { @@ -38,53 +39,85 @@ namespace osu.Game.Overlays.BeatmapSet.Scores }, new FillFlowContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Width = 0.95f, Direction = FillDirection.Vertical, - Spacing = new Vector2(0, spacing), - Margin = new MarginPadding { Vertical = spacing }, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, Children = new Drawable[] { - topScoresContainer = new FillFlowContainer + loadingContainer = new LoadingContainer { RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), + Masking = true, }, - scoreTable = new ScoreTable + resizableContainer = new ContentContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - } + RelativeSizeAxes = Axes.X, + Masking = true, + Children = new Drawable[] + { + new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.95f, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, spacing), + Padding = new MarginPadding { Vertical = padding }, + Children = new Drawable[] + { + topScoresContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + }, + scoreTable = new ScoreTable + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + } + } + }, + } + }, } - }, - loadingAnimation = new LoadingAnimation - { - Alpha = 0, - Margin = new MarginPadding(20), - }, + } }; + + Loading = true; } [BackgroundDependencyLoader] private void load(OsuColour colours) { background.Colour = colours.Gray2; - updateDisplay(); } + private bool loading; + public bool Loading { + get => loading; set { - loadingAnimation.FadeTo(value ? 1 : 0, fade_duration); + if (loading == value) + return; + + loading = value; if (value) - Scores = null; + { + loadingContainer.Show(); + resizableContainer.Hide(); + } + else + { + loadingContainer.Hide(); + resizableContainer.Show(); + } } } @@ -117,6 +150,63 @@ namespace osu.Game.Overlays.BeatmapSet.Scores if (userScore != null && userScore.Position != 1) topScoresContainer.Add(new DrawableTopScore(userScore.Score, userScore.Position)); } + + Loading = false; + } + + private class ContentContainer : VisibilityContainer + { + private const int duration = 300; + + private float maxHeight; + + protected override void PopIn() => this.ResizeHeightTo(maxHeight, duration, Easing.OutQuint); + + protected override void PopOut() => this.ResizeHeightTo(0, duration, Easing.OutQuint); + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + if (State.Value == Visibility.Hidden) + return; + + float height = 0; + + foreach (var c in Children) + { + height += c.Height; + } + + maxHeight = height; + + this.ResizeHeightTo(maxHeight, duration, Easing.OutQuint); + } + } + + private class LoadingContainer : VisibilityContainer + { + private const int duration = 300; + private const int height = 50; + + private readonly LoadingAnimation loadingAnimation; + + public LoadingContainer() + { + Child = loadingAnimation = new LoadingAnimation(); + } + + protected override void PopIn() + { + this.ResizeHeightTo(height, duration, Easing.OutQuint); + loadingAnimation.Show(); + } + + protected override void PopOut() + { + this.ResizeHeightTo(0, duration, Easing.OutQuint); + loadingAnimation.Hide(); + } } } } diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 44475dc53c..1c408ead54 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -98,11 +98,7 @@ namespace osu.Game.Overlays scores.Loading = true; getScoresRequest = new GetScoresRequest(b, b.Ruleset); - getScoresRequest.Success += r => Schedule(() => - { - scores.Scores = r; - scores.Loading = false; - }); + getScoresRequest.Success += r => Schedule(() => scores.Scores = r); api.Queue(getScoresRequest); } From 0580c322639e62dc155ed969deb7c7f1a97fda94 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jul 2019 17:59:40 +0900 Subject: [PATCH 1669/2854] Abstract intro screen logic to base class --- osu.Desktop/OsuGameDesktop.cs | 2 +- osu.Game.Tests/Visual/Menus/TestSceneIntro.cs | 7 +- osu.Game/OsuGame.cs | 7 +- .../Menu/{Intro.cs => IntroCircles.cs} | 94 ++------------- osu.Game/Screens/Menu/IntroScreen.cs | 114 ++++++++++++++++++ osu.Game/Screens/Menu/MainMenu.cs | 2 +- 6 files changed, 136 insertions(+), 90 deletions(-) rename osu.Game/Screens/Menu/{Intro.cs => IntroCircles.cs} (52%) create mode 100644 osu.Game/Screens/Menu/IntroScreen.cs diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 975b7f9f5a..18f0cd1f80 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -71,7 +71,7 @@ namespace osu.Desktop switch (newScreen) { - case Intro _: + case IntroScreen _: case MainMenu _: versionManager?.Show(); break; diff --git a/osu.Game.Tests/Visual/Menus/TestSceneIntro.cs b/osu.Game.Tests/Visual/Menus/TestSceneIntro.cs index 15b5c2338e..8b993f618f 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneIntro.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneIntro.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Timing; using osu.Game.Screens; using osu.Game.Screens.Menu; +using osuTK; using osuTK.Graphics; namespace osu.Game.Tests.Visual.Menus @@ -45,13 +46,15 @@ namespace osu.Game.Tests.Visual.Menus RelativeSizeAxes = Axes.Both, Colour = Color4.Black, }, - new OsuScreenStack(new Intro()) + new OsuScreenStack(new IntroCircles()) { RelativeSizeAxes = Axes.Both, }, logo = new OsuLogo { - Anchor = Anchor.Centre, + Alpha = 0, + RelativePositionAxes = Axes.Both, + Position = new Vector2(0.5f), }, } }); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 361ff62155..1ca4527786 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -86,7 +86,8 @@ namespace osu.Game private BackButton backButton; private MainMenu menuScreen; - private Intro introScreen; + + private IntroScreen introScreen; private Bindable configRuleset; @@ -760,7 +761,7 @@ namespace osu.Game if (introScreen == null) return true; - if (!introScreen.DidLoadMenu || !(screenStack.CurrentScreen is Intro)) + if (!introScreen.DidLoadMenu || !(screenStack.CurrentScreen is IntroScreen)) { Scheduler.Add(introScreen.MakeCurrent); return true; @@ -795,7 +796,7 @@ namespace osu.Game { switch (newScreen) { - case Intro intro: + case IntroScreen intro: introScreen = intro; break; diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/IntroCircles.cs similarity index 52% rename from osu.Game/Screens/Menu/Intro.cs rename to osu.Game/Screens/Menu/IntroCircles.cs index 3fb16eaf90..4fa1a81123 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/IntroCircles.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; using osu.Framework.Bindables; @@ -12,41 +11,24 @@ using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.IO.Archives; -using osu.Game.Screens.Backgrounds; -using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.Menu { - public class Intro : StartupScreen + public class IntroCircles : IntroScreen { private const string menu_music_beatmap_hash = "3c8b1fcc9434dbb29e2fb613d3b9eada9d7bb6c125ceb32396c3b53437280c83"; - /// - /// Whether we have loaded the menu previously. - /// - public bool DidLoadMenu; - - private MainMenu mainMenu; private SampleChannel welcome; - private SampleChannel seeya; - protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack(); - - private readonly BindableDouble exitingVolumeFade = new BindableDouble(1); - - [Resolved] - private AudioManager audio { get; set; } - - private Bindable menuVoice; private Bindable menuMusic; + private Track track; + private WorkingBeatmap introBeatmap; [BackgroundDependencyLoader] - private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game) + private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game, ISampleStore samples) { - menuVoice = config.GetBindable(OsuSetting.MenuVoice); menuMusic = config.GetBindable(OsuSetting.MenuMusic); BeatmapSetInfo setInfo = null; @@ -75,15 +57,13 @@ namespace osu.Game.Screens.Menu introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); track = introBeatmap.Track; - welcome = audio.Samples.Get(@"welcome"); - seeya = audio.Samples.Get(@"seeya"); + if (config.Get(OsuSetting.MenuVoice)) + welcome = samples.Get(@"welcome"); } private const double delay_step_one = 2300; private const double delay_step_two = 600; - public const int EXIT_DELAY = 3000; - protected override void LogoArriving(OsuLogo logo, bool resuming) { base.LogoArriving(logo, resuming); @@ -93,8 +73,7 @@ namespace osu.Game.Screens.Menu Beatmap.Value = introBeatmap; introBeatmap = null; - if (menuVoice.Value) - welcome.Play(); + welcome?.Play(); Scheduler.AddDelayed(delegate { @@ -105,74 +84,23 @@ namespace osu.Game.Screens.Menu track = null; } - LoadComponentAsync(mainMenu = new MainMenu()); + PrepareMenuLoad(); - Scheduler.AddDelayed(delegate - { - DidLoadMenu = true; - this.Push(mainMenu); - }, delay_step_one); + Scheduler.AddDelayed(LoadMenu, delay_step_one); }, delay_step_two); - } - logo.Colour = Color4.White; - logo.Ripple = false; - - const int quick_appear = 350; - - int initialMovementTime = logo.Alpha > 0.2f ? quick_appear : 0; - - logo.MoveTo(new Vector2(0.5f), initialMovementTime, Easing.OutQuint); - - if (!resuming) - { logo.ScaleTo(1); logo.FadeIn(); logo.PlayIntro(); } - else - { - logo.Triangles = false; - - logo - .ScaleTo(1, initialMovementTime, Easing.OutQuint) - .FadeIn(quick_appear, Easing.OutQuint) - .Then() - .RotateTo(20, EXIT_DELAY * 1.5f) - .FadeOut(EXIT_DELAY); - } } public override void OnSuspending(IScreen next) { + track = null; + this.FadeOut(300); base.OnSuspending(next); } - - public override bool OnExiting(IScreen next) - { - //cancel exiting if we haven't loaded the menu yet. - return !DidLoadMenu; - } - - public override void OnResuming(IScreen last) - { - this.FadeIn(300); - - double fadeOutTime = EXIT_DELAY; - //we also handle the exit transition. - if (menuVoice.Value) - seeya.Play(); - else - fadeOutTime = 500; - - audio.AddAdjustment(AdjustableProperty.Volume, exitingVolumeFade); - this.TransformBindableTo(exitingVolumeFade, 0, fadeOutTime).OnComplete(_ => this.Exit()); - - //don't want to fade out completely else we will stop running updates. - Game.FadeTo(0.01f, fadeOutTime); - - base.OnResuming(last); - } } } diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs new file mode 100644 index 0000000000..27f3c9a45b --- /dev/null +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -0,0 +1,114 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Screens; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Screens.Backgrounds; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Menu +{ + public abstract class IntroScreen : StartupScreen + { + private readonly BindableDouble exitingVolumeFade = new BindableDouble(1); + + public const int EXIT_DELAY = 3000; + + [Resolved] + private AudioManager audio { get; set; } + + private SampleChannel seeya; + + private Bindable menuVoice; + + protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack(); + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game) + { + menuVoice = config.GetBindable(OsuSetting.MenuVoice); + seeya = audio.Samples.Get(@"seeya"); + } + + /// + /// Whether we have loaded the menu previously. + /// + public bool DidLoadMenu { get; private set; } + + public override bool OnExiting(IScreen next) + { + //cancel exiting if we haven't loaded the menu yet. + return !DidLoadMenu; + } + + public override void OnResuming(IScreen last) + { + this.FadeIn(300); + + double fadeOutTime = EXIT_DELAY; + //we also handle the exit transition. + if (menuVoice.Value) + seeya.Play(); + else + fadeOutTime = 500; + + audio.AddAdjustment(AdjustableProperty.Volume, exitingVolumeFade); + this.TransformBindableTo(exitingVolumeFade, 0, fadeOutTime).OnComplete(_ => this.Exit()); + + //don't want to fade out completely else we will stop running updates. + Game.FadeTo(0.01f, fadeOutTime); + + base.OnResuming(last); + } + + protected override void LogoArriving(OsuLogo logo, bool resuming) + { + base.LogoArriving(logo, resuming); + + logo.Colour = Color4.White; + logo.Triangles = false; + logo.Ripple = false; + + if (!resuming) + { + logo.MoveTo(new Vector2(0.5f)); + logo.ScaleTo(Vector2.One); + logo.Hide(); + } + else + { + const int quick_appear = 350; + int initialMovementTime = logo.Alpha > 0.2f ? quick_appear : 0; + + logo.MoveTo(new Vector2(0.5f), initialMovementTime, Easing.OutQuint); + + logo + .ScaleTo(1, initialMovementTime, Easing.OutQuint) + .FadeIn(quick_appear, Easing.OutQuint) + .Then() + .RotateTo(20, EXIT_DELAY * 1.5f) + .FadeOut(EXIT_DELAY); + } + } + + private MainMenu mainMenu; + + protected void PrepareMenuLoad() + { + LoadComponentAsync(mainMenu = new MainMenu()); + } + + protected void LoadMenu() + { + DidLoadMenu = true; + this.Push(mainMenu); + } + } +} diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index c64bea840f..f73de6f730 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -120,7 +120,7 @@ namespace osu.Game.Screens.Menu var track = Beatmap.Value.Track; var metadata = Beatmap.Value.Metadata; - if (last is Intro && track != null) + if (last is IntroScreen && track != null) { if (!track.IsRunning) { From e835cd0f6f498cd3db205c31d16f6a1ba0343924 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jul 2019 18:06:49 +0900 Subject: [PATCH 1670/2854] Improve information flow to Disclaimer --- osu.Game/Screens/Loader.cs | 10 +++++++++- osu.Game/Screens/Menu/Disclaimer.cs | 11 +++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index 8add730c4e..de00ba2e9f 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -45,7 +45,15 @@ namespace osu.Game.Screens private OsuScreen loadableScreen; private ShaderPrecompiler precompiler; - protected virtual OsuScreen CreateLoadableScreen() => showDisclaimer ? (OsuScreen)new Disclaimer() : new Intro(); + protected virtual OsuScreen CreateLoadableScreen() + { + if (showDisclaimer) + return new Disclaimer(getIntroSequence()); + + return getIntroSequence(); + } + + private IntroScreen getIntroSequence() => new IntroCircles(); protected virtual ShaderPrecompiler CreateShaderPrecompiler() => new ShaderPrecompiler(); diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 97231a1331..073ab639e3 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -21,7 +21,6 @@ namespace osu.Game.Screens.Menu { public class Disclaimer : StartupScreen { - private Intro intro; private SpriteIcon icon; private Color4 iconColour; private LinkFlowContainer textFlow; @@ -32,10 +31,13 @@ namespace osu.Game.Screens.Menu private const float icon_y = -85; private const float icon_size = 30; + private readonly OsuScreen nextScreen; + private readonly Bindable currentUser = new Bindable(); - public Disclaimer() + public Disclaimer(OsuScreen nextScreen = null) { + this.nextScreen = nextScreen; ValidForResume = false; } @@ -146,7 +148,8 @@ namespace osu.Game.Screens.Menu protected override void LoadComplete() { base.LoadComplete(); - LoadComponentAsync(intro = new Intro()); + if (nextScreen != null) + LoadComponentAsync(nextScreen); } public override void OnEntering(IScreen last) @@ -170,7 +173,7 @@ namespace osu.Game.Screens.Menu .Then(5500) .FadeOut(250) .ScaleTo(0.9f, 250, Easing.InQuint) - .Finally(d => this.Push(intro)); + .Finally(d => this.Push(nextScreen)); } } } From e8b9b1b0bfe52812948b27b6fbaf834699f27ef3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 9 Jul 2019 12:16:58 +0300 Subject: [PATCH 1671/2854] visibility logic adjustments --- .../Visual/Online/TestSceneScoresContainer.cs | 15 ++--- .../BeatmapSet/Scores/ScoresContainer.cs | 67 ++++++++----------- 2 files changed, 35 insertions(+), 47 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index c414b6b940..10207ea192 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs @@ -3,12 +3,10 @@ using System; using System.Collections.Generic; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.MathUtils; -using osu.Game.Graphics; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapSet.Scores; using osu.Game.Rulesets.Mods; @@ -16,6 +14,7 @@ using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Users; +using osuTK.Graphics; namespace osu.Game.Tests.Visual.Online { @@ -44,7 +43,11 @@ namespace osu.Game.Tests.Visual.Online Width = 0.8f, Children = new Drawable[] { - background = new Box { RelativeSizeAxes = Axes.Both }, + background = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, scoresContainer = new ScoresContainer(), } }; @@ -245,11 +248,5 @@ namespace osu.Game.Tests.Visual.Online }); AddStep("Trigger loading", () => scoresContainer.Loading = !scoresContainer.Loading); } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - background.Colour = colours.Gray2; - } } } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index eacbe6200a..63e18f3da7 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly ScoreTable scoreTable; private readonly FillFlowContainer topScoresContainer; - private readonly ContentContainer resizableContainer; + private readonly ContentContainer contentContainer; private readonly LoadingContainer loadingContainer; public ScoresContainer() @@ -49,36 +49,33 @@ namespace osu.Game.Overlays.BeatmapSet.Scores RelativeSizeAxes = Axes.X, Masking = true, }, - resizableContainer = new ContentContainer + contentContainer = new ContentContainer { RelativeSizeAxes = Axes.X, Masking = true, - Children = new Drawable[] + Child = new FillFlowContainer { - new FillFlowContainer + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.95f, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, spacing), + Padding = new MarginPadding { Vertical = padding }, + Children = new Drawable[] { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Width = 0.95f, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, spacing), - Padding = new MarginPadding { Vertical = padding }, - Children = new Drawable[] + topScoresContainer = new FillFlowContainer { - topScoresContainer = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), - }, - scoreTable = new ScoreTable - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - } + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + }, + scoreTable = new ScoreTable + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, } }, } @@ -103,20 +100,21 @@ namespace osu.Game.Overlays.BeatmapSet.Scores get => loading; set { - if (loading == value) - return; - loading = value; if (value) { loadingContainer.Show(); - resizableContainer.Hide(); + contentContainer.Hide(); } else { loadingContainer.Hide(); - resizableContainer.Show(); + + if (scores == null || scores?.Scores.Count < 1) + contentContainer.Hide(); + else + contentContainer.Show(); } } } @@ -171,14 +169,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores if (State.Value == Visibility.Hidden) return; - float height = 0; - - foreach (var c in Children) - { - height += c.Height; - } - - maxHeight = height; + maxHeight = Child.DrawHeight; this.ResizeHeightTo(maxHeight, duration, Easing.OutQuint); } From e3e72a627607751a84560077586ecdb80cfd1fee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jul 2019 18:15:31 +0900 Subject: [PATCH 1672/2854] Reorganise tests and add restart step --- osu.Game.Tests/Visual/Menus/IntroTestScene.cs | 66 +++++++++++++++++++ osu.Game.Tests/Visual/Menus/TestSceneIntro.cs | 65 ------------------ .../Visual/Menus/TestSceneIntroCircles.cs | 15 +++++ 3 files changed, 81 insertions(+), 65 deletions(-) create mode 100644 osu.Game.Tests/Visual/Menus/IntroTestScene.cs delete mode 100644 osu.Game.Tests/Visual/Menus/TestSceneIntro.cs create mode 100644 osu.Game.Tests/Visual/Menus/TestSceneIntroCircles.cs diff --git a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs new file mode 100644 index 0000000000..0d78a43cf6 --- /dev/null +++ b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs @@ -0,0 +1,66 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Screens; +using osu.Game.Screens; +using osu.Game.Screens.Menu; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.Menus +{ + [TestFixture] + public abstract class IntroTestScene : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(OsuLogo), + typeof(StartupScreen), + typeof(IntroScreen), + typeof(OsuScreen), + typeof(IntroTestScene), + }; + + [Cached] + private OsuLogo logo; + + protected IntroTestScene() + { + Drawable introStack = null; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Depth = float.MaxValue, + Colour = Color4.Black, + }, + logo = new OsuLogo + { + Alpha = 0, + RelativePositionAxes = Axes.Both, + Depth = float.MinValue, + Position = new Vector2(0.5f), + } + }; + + AddStep("restart sequence", () => + { + introStack?.Expire(); + Add(introStack = new OsuScreenStack(CreateScreen()) + { + RelativeSizeAxes = Axes.Both, + }); + }); + } + + protected abstract IScreen CreateScreen(); + } +} diff --git a/osu.Game.Tests/Visual/Menus/TestSceneIntro.cs b/osu.Game.Tests/Visual/Menus/TestSceneIntro.cs deleted file mode 100644 index 8b993f618f..0000000000 --- a/osu.Game.Tests/Visual/Menus/TestSceneIntro.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Timing; -using osu.Game.Screens; -using osu.Game.Screens.Menu; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Tests.Visual.Menus -{ - [TestFixture] - public class TestSceneIntro : OsuTestScene - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OsuLogo), - typeof(StartupScreen), - typeof(OsuScreen) - }; - - [Cached] - private OsuLogo logo; - - public TestSceneIntro() - { - var rateAdjustClock = new StopwatchClock(true); - var framedClock = new FramedClock(rateAdjustClock); - framedClock.ProcessFrame(); - - Add(new Container - { - RelativeSizeAxes = Axes.Both, - Clock = framedClock, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - new OsuScreenStack(new IntroCircles()) - { - RelativeSizeAxes = Axes.Both, - }, - logo = new OsuLogo - { - Alpha = 0, - RelativePositionAxes = Axes.Both, - Position = new Vector2(0.5f), - }, - } - }); - - AddSliderStep("Playback speed", 0.0, 2.0, 1, v => rateAdjustClock.Rate = v); - } - } -} diff --git a/osu.Game.Tests/Visual/Menus/TestSceneIntroCircles.cs b/osu.Game.Tests/Visual/Menus/TestSceneIntroCircles.cs new file mode 100644 index 0000000000..107734cc8d --- /dev/null +++ b/osu.Game.Tests/Visual/Menus/TestSceneIntroCircles.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Screens; +using osu.Game.Screens.Menu; + +namespace osu.Game.Tests.Visual.Menus +{ + [TestFixture] + public class TestSceneIntroCircles : IntroTestScene + { + protected override IScreen CreateScreen() => new IntroCircles(); + } +} From 276873ff8ada90e18a9fde2b8f6e2641828c2e17 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 9 Jul 2019 12:28:59 +0300 Subject: [PATCH 1673/2854] remove unused field --- osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index 10207ea192..4ce689ce6b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs @@ -29,8 +29,6 @@ namespace osu.Game.Tests.Visual.Online typeof(ScoreTableRowBackground), }; - private readonly Box background; - public TestSceneScoresContainer() { ScoresContainer scoresContainer; @@ -43,7 +41,7 @@ namespace osu.Game.Tests.Visual.Online Width = 0.8f, Children = new Drawable[] { - background = new Box + new Box { RelativeSizeAxes = Axes.Both, Colour = Color4.Black, From 2546f647be43d3e44477fa962a22f6dcd768e510 Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Tue, 9 Jul 2019 11:32:49 +0200 Subject: [PATCH 1674/2854] Completely change the way we fix the bug --- osu.Game/OsuGame.cs | 6 +++--- osu.Game/Overlays/MusicController.cs | 4 ++-- osu.Game/Screens/Menu/MainMenu.cs | 18 ++++++++---------- osu.Game/Screens/OsuScreen.cs | 11 +---------- osu.Game/Screens/Select/SongSelect.cs | 7 ++++--- 5 files changed, 18 insertions(+), 28 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 325d2cbd85..0a472d4dc1 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -306,7 +306,7 @@ namespace osu.Game private void currentTrackCompleted() { if (!Beatmap.Value.Track.Looping && !Beatmap.Disabled) - MusicController.NextTrack(); + musicController.NextTrack(); } #endregion @@ -484,7 +484,7 @@ namespace osu.Game Origin = Anchor.TopRight, }, rightFloatingOverlayContent.Add, true); - loadComponentSingleFile(MusicController = new MusicController + loadComponentSingleFile(musicController = new MusicController { GetToolbarHeight = () => ToolbarOffset, Anchor = Anchor.TopRight, @@ -752,7 +752,7 @@ namespace osu.Game private ScalingContainer screenContainer; - public MusicController MusicController { get; private set; } + private MusicController musicController; protected override bool OnExiting() { diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 3db71d39ee..ad0c0717ac 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -55,6 +55,8 @@ namespace osu.Game.Overlays private Container dragContainer; private Container playerContainer; + public bool UserRequestedPause { get; private set; } + [Resolved] private Bindable beatmap { get; set; } @@ -66,8 +68,6 @@ namespace osu.Game.Overlays /// public Func GetToolbarHeight; - public bool UserRequestedPause { get; private set; } - public MusicController() { Width = 400; diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index dcce49179d..078f9c5a15 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -42,6 +42,9 @@ namespace osu.Game.Screens.Menu [Resolved] private GameHost host { get; set; } + [Resolved] + private MusicController musicController { get; set; } + private BackgroundScreenDefault background; protected override BackgroundScreen CreateBackground() => background; @@ -120,15 +123,6 @@ namespace osu.Game.Screens.Menu var track = Beatmap.Value.Track; var metadata = Beatmap.Value.Metadata; - if (last is Intro && track != null && !Game.MusicController.UserRequestedPause) - { - if (!track.IsRunning) - { - track.Seek(metadata.PreviewTime != -1 ? metadata.PreviewTime : 0.4f * track.Length); - track.Start(); - } - } - Beatmap.ValueChanged += beatmap_ValueChanged; } @@ -190,7 +184,11 @@ namespace osu.Game.Screens.Menu //we may have consumed our preloaded instance, so let's make another. preloadSongSelect(); - ResumeIfNoUserPauseRequested(); + if (Beatmap.Value.Track != null && !musicController.UserRequestedPause) + { + Beatmap.Value.Track.Tempo.Value = 1; + Beatmap.Value.Track.Start(); + } } public override bool OnExiting(IScreen next) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 0682710133..328631ff9c 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -50,7 +50,7 @@ namespace osu.Game.Screens public virtual bool CursorVisible => true; - protected new OsuGame Game => base.Game as OsuGame; + protected new OsuGameBase Game => base.Game as OsuGameBase; /// /// The to set the user's activity automatically to when this screen is entered @@ -179,15 +179,6 @@ namespace osu.Game.Screens api.Activity.Value = activity; } - protected void ResumeIfNoUserPauseRequested() - { - if (Beatmap.Value.Track != null && !Game.MusicController.UserRequestedPause) - { - Beatmap.Value.Track.Tempo.Value = 1; - Beatmap.Value.Track.Start(); - } - } - /// /// Fired when this screen was entered or resumed and the logo state is required to be adjusted. /// diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 17b2ae376f..dd115e04d4 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -87,6 +87,9 @@ namespace osu.Game.Screens.Select private readonly Bindable decoupledRuleset = new Bindable(); + [Resolved] + private MusicController musicController { get; set; } + [Cached] [Cached(Type = typeof(IBindable>))] private readonly Bindable> mods = new Bindable>(Array.Empty()); // Bound to the game's mods, but is not reset on exiting @@ -426,8 +429,6 @@ namespace osu.Game.Screens.Select { base.OnEntering(last); - ResumeIfNoUserPauseRequested(); - this.FadeInFromZero(250); FilterControl.Activate(); } @@ -572,7 +573,7 @@ namespace osu.Game.Screens.Select { Track track = Beatmap.Value.Track; - if (!track.IsRunning || restart) + if ((!track.IsRunning || restart) && !musicController.UserRequestedPause) { track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; track.Restart(); From 8eeba069cc06fdced468da0e8184e834914f6cc3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jul 2019 18:51:10 +0900 Subject: [PATCH 1675/2854] Ensure logo isn't left in a bad state on re-run --- osu.Game.Tests/Visual/Menus/IntroTestScene.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs index 0d78a43cf6..e8addf59ed 100644 --- a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs +++ b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs @@ -53,7 +53,9 @@ namespace osu.Game.Tests.Visual.Menus AddStep("restart sequence", () => { + logo.FinishTransforms(); introStack?.Expire(); + Add(introStack = new OsuScreenStack(CreateScreen()) { RelativeSizeAxes = Axes.Both, From 2d0e6652f90e645538f9b1a87d20d8641e96fece Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jul 2019 18:59:56 +0900 Subject: [PATCH 1676/2854] Ensure logo doesn't get stuck tracking --- osu.Game.Tests/Visual/Menus/IntroTestScene.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs index e8addf59ed..aa27ed48df 100644 --- a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs +++ b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs @@ -54,6 +54,8 @@ namespace osu.Game.Tests.Visual.Menus AddStep("restart sequence", () => { logo.FinishTransforms(); + logo.IsTracking = false; + introStack?.Expire(); Add(introStack = new OsuScreenStack(CreateScreen()) From a0d048ad8ddca7cda23f6a8ab023c0a28c87952f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jul 2019 19:31:53 +0900 Subject: [PATCH 1677/2854] Don't test the logo Breaks main menu adding --- osu.Game.Tests/Visual/Menus/IntroTestScene.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs index aa27ed48df..d03d341ee4 100644 --- a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs +++ b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs @@ -20,7 +20,6 @@ namespace osu.Game.Tests.Visual.Menus { public override IReadOnlyList RequiredTypes => new[] { - typeof(OsuLogo), typeof(StartupScreen), typeof(IntroScreen), typeof(OsuScreen), From 3472979d0b4d9d2d652ded1a3b9f9c56e2642e1f Mon Sep 17 00:00:00 2001 From: Oskar Solecki <31374466+Desconocidosmh@users.noreply.github.com> Date: Tue, 9 Jul 2019 12:53:40 +0200 Subject: [PATCH 1678/2854] Make sure exiting editor doesn't unpause the music --- osu.Game/Screens/Edit/Editor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 676e060433..8b3cf1ec90 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -238,7 +238,7 @@ namespace osu.Game.Screens.Edit public override bool OnExiting(IScreen next) { Background.FadeColour(Color4.White, 500); - + Beatmap.Value.Track?.Stop(); return base.OnExiting(next); } From 2472c6b4b121d788c5ef835e5156eb179d582f8e Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Tue, 9 Jul 2019 21:07:29 +0930 Subject: [PATCH 1679/2854] Fix iOS visual tests not supporting raw keyboard handler --- osu.Game.Rulesets.Catch.Tests.iOS/Application.cs | 2 +- osu.Game.Rulesets.Mania.Tests.iOS/Application.cs | 2 +- osu.Game.Rulesets.Osu.Tests.iOS/Application.cs | 2 +- osu.Game.Rulesets.Taiko.Tests.iOS/Application.cs | 2 +- osu.Game.Tests.iOS/Application.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests.iOS/Application.cs b/osu.Game.Rulesets.Catch.Tests.iOS/Application.cs index 44817c1304..beca477943 100644 --- a/osu.Game.Rulesets.Catch.Tests.iOS/Application.cs +++ b/osu.Game.Rulesets.Catch.Tests.iOS/Application.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Catch.Tests.iOS { public static void Main(string[] args) { - UIApplication.Main(args, null, "AppDelegate"); + UIApplication.Main(args, "GameUIApplication", "AppDelegate"); } } } diff --git a/osu.Game.Rulesets.Mania.Tests.iOS/Application.cs b/osu.Game.Rulesets.Mania.Tests.iOS/Application.cs index d47ac4643f..0362402320 100644 --- a/osu.Game.Rulesets.Mania.Tests.iOS/Application.cs +++ b/osu.Game.Rulesets.Mania.Tests.iOS/Application.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Mania.Tests.iOS { public static void Main(string[] args) { - UIApplication.Main(args, null, "AppDelegate"); + UIApplication.Main(args, "GameUIApplication", "AppDelegate"); } } } diff --git a/osu.Game.Rulesets.Osu.Tests.iOS/Application.cs b/osu.Game.Rulesets.Osu.Tests.iOS/Application.cs index 7a0797a909..3718264a42 100644 --- a/osu.Game.Rulesets.Osu.Tests.iOS/Application.cs +++ b/osu.Game.Rulesets.Osu.Tests.iOS/Application.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Osu.Tests.iOS { public static void Main(string[] args) { - UIApplication.Main(args, null, "AppDelegate"); + UIApplication.Main(args, "GameUIApplication", "AppDelegate"); } } } diff --git a/osu.Game.Rulesets.Taiko.Tests.iOS/Application.cs b/osu.Game.Rulesets.Taiko.Tests.iOS/Application.cs index 6613e9e2b4..330cb42901 100644 --- a/osu.Game.Rulesets.Taiko.Tests.iOS/Application.cs +++ b/osu.Game.Rulesets.Taiko.Tests.iOS/Application.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.iOS { public static void Main(string[] args) { - UIApplication.Main(args, null, "AppDelegate"); + UIApplication.Main(args, "GameUIApplication", "AppDelegate"); } } } diff --git a/osu.Game.Tests.iOS/Application.cs b/osu.Game.Tests.iOS/Application.cs index a23fe4e129..d96a3e27a4 100644 --- a/osu.Game.Tests.iOS/Application.cs +++ b/osu.Game.Tests.iOS/Application.cs @@ -9,7 +9,7 @@ namespace osu.Game.Tests.iOS { public static void Main(string[] args) { - UIApplication.Main(args, null, "AppDelegate"); + UIApplication.Main(args, "GameUIApplication", "AppDelegate"); } } } From e0c1fb78181571b926f12e84e4f6a508861191ad Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 9 Jul 2019 14:47:54 +0300 Subject: [PATCH 1680/2854] Compare by milliseconds for length --- osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 490012da61..c38b13cfca 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -49,11 +49,7 @@ namespace osu.Game.Screens.Select.Carousel return Beatmap.StarDifficulty.CompareTo(otherBeatmap.Beatmap.StarDifficulty); case SortMode.Length: - // Length comparing must be in seconds - if (TimeSpan.FromMilliseconds(Beatmap.Length).Seconds != TimeSpan.FromMilliseconds(otherBeatmap.Beatmap.Length).Seconds) - return Beatmap.Length.CompareTo(otherBeatmap.Beatmap.Length); - - goto case SortMode.Difficulty; + return Beatmap.Length.CompareTo(otherBeatmap.Beatmap.Length); } } From 38bc652bf2b8b740fcf1008dcb67759f93fccc52 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 9 Jul 2019 17:02:51 +0300 Subject: [PATCH 1681/2854] Remove sorting by length for beatmaps --- osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index c38b13cfca..712ab7b571 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -47,9 +47,6 @@ namespace osu.Game.Screens.Select.Carousel if (ruleset != 0) return ruleset; return Beatmap.StarDifficulty.CompareTo(otherBeatmap.Beatmap.StarDifficulty); - - case SortMode.Length: - return Beatmap.Length.CompareTo(otherBeatmap.Beatmap.Length); } } From f3329f4d792dc97f064f5ab0f0e51384b9ae795c Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 9 Jul 2019 17:22:21 +0300 Subject: [PATCH 1682/2854] Use a more readable code for calculating length --- osu.Game/Beatmaps/BeatmapManager.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 6fe70b6ec6..4f67139706 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -315,10 +315,15 @@ namespace osu.Game.Beatmaps private double calculateLength(IBeatmap b) { - var lastObject = b.HitObjects.LastOrDefault(); - var endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0; + if (!b.HitObjects.Any()) + return 0; - return endTime - b.HitObjects.FirstOrDefault()?.StartTime ?? 0; + var lastObject = b.HitObjects.Last(); + + double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime; + double startTime = b.HitObjects.First().StartTime; + + return endTime - startTime; } /// From 1485c273ab1f3b36f7357823db220536e79c4a6b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 9 Jul 2019 17:31:15 +0300 Subject: [PATCH 1683/2854] Describe the xmldoc mo --- osu.Game/Beatmaps/BeatmapInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index fa1282647e..8042f6b4b9 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -52,7 +52,7 @@ namespace osu.Game.Beatmaps public BeatmapOnlineInfo OnlineInfo { get; set; } /// - /// The playable length of this beatmap. + /// The playable length in milliseconds of this beatmap. /// public double Length { get; set; } From 9907a58ec4aaf3427769b2044248d060f3a0563b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 9 Jul 2019 17:38:17 +0300 Subject: [PATCH 1684/2854] Revert animations and apply suggested changes --- .../Visual/Online/TestSceneScoresContainer.cs | 1 - .../BeatmapSet/Scores/DrawableTopScore.cs | 3 +- .../BeatmapSet/Scores/ScoresContainer.cs | 137 ++++-------------- .../BeatmapSet/Scores/TopScoreUserSection.cs | 9 +- osu.Game/Overlays/BeatmapSetOverlay.cs | 14 +- 5 files changed, 45 insertions(+), 119 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index 4ce689ce6b..824280fe68 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs @@ -244,7 +244,6 @@ namespace osu.Game.Tests.Visual.Online allScores.UserScore = myBestScore; scoresContainer.Scores = allScores; }); - AddStep("Trigger loading", () => scoresContainer.Loading = !scoresContainer.Loading); } } } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs index bdae730f7e..d263483046 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs @@ -59,11 +59,12 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { new Drawable[] { - new TopScoreUserSection(position) + new TopScoreUserSection { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Score = score, + ScorePosition = position, }, null, new TopScoreStatisticsSection diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 63e18f3da7..94bcfdee4f 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -17,20 +17,17 @@ namespace osu.Game.Overlays.BeatmapSet.Scores public class ScoresContainer : CompositeDrawable { private const int spacing = 15; - private const int padding = 20; + private const int fade_duration = 200; private readonly Box background; private readonly ScoreTable scoreTable; - private readonly FillFlowContainer topScoresContainer; - private readonly ContentContainer contentContainer; - private readonly LoadingContainer loadingContainer; + private readonly LoadingAnimation loadingAnimation; public ScoresContainer() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - InternalChildren = new Drawable[] { background = new Box @@ -39,83 +36,53 @@ namespace osu.Game.Overlays.BeatmapSet.Scores }, new FillFlowContainer { - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Y, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.95f, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, spacing), + Margin = new MarginPadding { Vertical = spacing }, Children = new Drawable[] { - loadingContainer = new LoadingContainer + topScoresContainer = new FillFlowContainer { RelativeSizeAxes = Axes.X, - Masking = true, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), }, - contentContainer = new ContentContainer + scoreTable = new ScoreTable { - RelativeSizeAxes = Axes.X, - Masking = true, - Child = new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Width = 0.95f, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, spacing), - Padding = new MarginPadding { Vertical = padding }, - Children = new Drawable[] - { - topScoresContainer = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), - }, - scoreTable = new ScoreTable - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - } - }, - } - }, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + } } - } + }, + loadingAnimation = new LoadingAnimation + { + Alpha = 0, + Margin = new MarginPadding(20), + }, }; - - Loading = true; } [BackgroundDependencyLoader] private void load(OsuColour colours) { background.Colour = colours.Gray2; + updateDisplay(); } - private bool loading; - public bool Loading { - get => loading; set { - loading = value; + loadingAnimation.FadeTo(value ? 1 : 0, fade_duration); if (value) - { - loadingContainer.Show(); - contentContainer.Hide(); - } - else - { - loadingContainer.Hide(); - - if (scores == null || scores?.Scores.Count < 1) - contentContainer.Hide(); - else - contentContainer.Show(); - } + Scores = null; } } @@ -139,7 +106,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores scoreTable.Scores = scores?.Scores.Count > 1 ? scores.Scores : new List(); scoreTable.FadeTo(scores?.Scores.Count > 1 ? 1 : 0); - if (scores?.Scores.Any() == true) + if (scores?.Scores.Any() ?? false) { topScoresContainer.Add(new DrawableTopScore(scores.Scores.FirstOrDefault())); @@ -148,56 +115,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores if (userScore != null && userScore.Position != 1) topScoresContainer.Add(new DrawableTopScore(userScore.Score, userScore.Position)); } - - Loading = false; - } - - private class ContentContainer : VisibilityContainer - { - private const int duration = 300; - - private float maxHeight; - - protected override void PopIn() => this.ResizeHeightTo(maxHeight, duration, Easing.OutQuint); - - protected override void PopOut() => this.ResizeHeightTo(0, duration, Easing.OutQuint); - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - if (State.Value == Visibility.Hidden) - return; - - maxHeight = Child.DrawHeight; - - this.ResizeHeightTo(maxHeight, duration, Easing.OutQuint); - } - } - - private class LoadingContainer : VisibilityContainer - { - private const int duration = 300; - private const int height = 50; - - private readonly LoadingAnimation loadingAnimation; - - public LoadingContainer() - { - Child = loadingAnimation = new LoadingAnimation(); - } - - protected override void PopIn() - { - this.ResizeHeightTo(height, duration, Easing.OutQuint); - loadingAnimation.Show(); - } - - protected override void PopOut() - { - this.ResizeHeightTo(0, duration, Easing.OutQuint); - loadingAnimation.Hide(); - } } } } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 056fe71a39..36e60b3fd9 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -28,7 +28,12 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly SpriteText date; private readonly UpdateableFlag flag; - public TopScoreUserSection(int position) + public int ScorePosition + { + set => rankText.Text = $"#{value}"; + } + + public TopScoreUserSection() { AutoSizeAxes = Axes.Both; @@ -52,7 +57,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Text = $"#{position.ToString()}", + Text = $"#1", Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, italics: true) }, rank = new UpdateableRank(ScoreRank.D) diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 1c408ead54..abd86df920 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -17,17 +17,14 @@ using osu.Game.Overlays.BeatmapSet; using osu.Game.Overlays.BeatmapSet.Scores; using osu.Game.Rulesets; using osuTK; - namespace osu.Game.Overlays { public class BeatmapSetOverlay : FullscreenOverlay { private const int fade_duration = 300; - public const float X_PADDING = 40; public const float TOP_PADDING = 25; public const float RIGHT_WIDTH = 275; - protected readonly Header Header; private RulesetStore rulesets; @@ -46,7 +43,6 @@ namespace osu.Game.Overlays { OsuScrollContainer scroll; Info info; - Children = new Drawable[] { new Box @@ -98,7 +94,11 @@ namespace osu.Game.Overlays scores.Loading = true; getScoresRequest = new GetScoresRequest(b, b.Ruleset); - getScoresRequest.Success += r => Schedule(() => scores.Scores = r); + getScoresRequest.Success += r => Schedule(() => + { + scores.Scores = r; + scores.Loading = false; + }); api.Queue(getScoresRequest); } @@ -123,6 +123,7 @@ namespace osu.Game.Overlays public void FetchAndShowBeatmap(int beatmapId) { beatmapSet.Value = null; + var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId); req.Success += res => { @@ -130,15 +131,18 @@ namespace osu.Game.Overlays Header.Picker.Beatmap.Value = Header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); }; API.Queue(req); + Show(); } public void FetchAndShowBeatmapSet(int beatmapSetId) { beatmapSet.Value = null; + var req = new GetBeatmapSetRequest(beatmapSetId); req.Success += res => beatmapSet.Value = res.ToBeatmapSet(rulesets); API.Queue(req); + Show(); } From e73f22eff8844b3ee429764801c8b4164a194eb6 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 9 Jul 2019 17:53:34 +0300 Subject: [PATCH 1685/2854] Convert length retrieved from online to milliseconds --- osu.Game.Tournament/Components/SongBar.cs | 2 +- osu.Game/Beatmaps/BeatmapSetInfo.cs | 2 +- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index ec021a8d1f..7005c068ae 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -180,7 +180,7 @@ namespace osu.Game.Tournament.Components panelContents.Children = new Drawable[] { - new DiffPiece(("Length", TimeSpan.FromSeconds(length).ToString(@"mm\:ss"))) + new DiffPiece(("Length", TimeSpan.FromMilliseconds(length).ToString(@"mm\:ss"))) { Anchor = Anchor.CentreLeft, Origin = Anchor.BottomLeft, diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 4075263e12..03bc7c7312 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -41,7 +41,7 @@ namespace osu.Game.Beatmaps public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0; /// - /// The maximum playable length of all beatmaps in this set. + /// The maximum playable length in milliseconds of all beatmaps in this set. /// public double MaxLength => Beatmaps?.Max(b => b.Length) ?? 0; diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index ff4d240bf0..f50e281dd0 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Rulesets; @@ -71,7 +72,7 @@ namespace osu.Game.Online.API.Requests.Responses StarDifficulty = starDifficulty, OnlineBeatmapID = OnlineBeatmapID, Version = version, - Length = length, + Length = TimeSpan.FromSeconds(length).Milliseconds, Status = Status, BeatmapSet = set, Metrics = metrics, From a0389c338ba2d89e6c07579b397e101d6c4e1c1d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 9 Jul 2019 17:56:08 +0300 Subject: [PATCH 1686/2854] CI fixes --- osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs | 1 - osu.Game/Overlays/BeatmapSetOverlay.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 36e60b3fd9..6d43ec6177 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -57,7 +57,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Text = $"#1", Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, italics: true) }, rank = new UpdateableRank(ScoreRank.D) diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index abd86df920..3a17b6eec1 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -17,11 +17,11 @@ using osu.Game.Overlays.BeatmapSet; using osu.Game.Overlays.BeatmapSet.Scores; using osu.Game.Rulesets; using osuTK; + namespace osu.Game.Overlays { public class BeatmapSetOverlay : FullscreenOverlay { - private const int fade_duration = 300; public const float X_PADDING = 40; public const float TOP_PADDING = 25; public const float RIGHT_WIDTH = 275; From b9be4080d315007715870c8f2381448961691f03 Mon Sep 17 00:00:00 2001 From: Joehu Date: Tue, 9 Jul 2019 07:59:38 -0700 Subject: [PATCH 1687/2854] Update beatmap leaderboard to placeholder when signing out --- osu.Game/Online/Leaderboards/Leaderboard.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index dea2ff1a21..35f7ba1c1b 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -203,8 +203,13 @@ namespace osu.Game.Online.Leaderboards public void APIStateChanged(IAPIProvider api, APIState state) { - if (state == APIState.Online) - UpdateScores(); + switch (state) + { + case APIState.Online: + case APIState.Offline: + UpdateScores(); + break; + } } protected void UpdateScores() From 41afe89c0b1ee2f194c48f45c119fcab5dde72f3 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 10 Jul 2019 00:46:34 +0900 Subject: [PATCH 1688/2854] delete no longer needed bindable --- osu.Game/Screens/Play/Player.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index d9e050a681..663113f747 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -118,7 +118,6 @@ namespace osu.Game.Screens.Play sampleRestart = audio.Samples.Get(@"Gameplay/restart"); mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); - config.GetBindable(OsuSetting.ShowStoryboard); ScoreProcessor = DrawableRuleset.CreateScoreProcessor(); ScoreProcessor.Mods.BindTo(Mods); From 80ddfc3b1e33f3c93c5174fef01fd4819bbccfcb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 Jul 2019 10:27:51 +0900 Subject: [PATCH 1689/2854] Disable frame accurate replay playback I want to prioritise better playback performance over accuracy for now. Also, in my testing this is still 100% accurate due to the addition of the FrameStabilityContainer, which is pretty cool. --- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 3830fa5cbe..4c011388fa 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Replays /// When set, we will ensure frames executed by nested drawables are frame-accurate to replay data. /// Disabling this can make replay playback smoother (useful for autoplay, currently). /// - public bool FrameAccuratePlayback = true; + public bool FrameAccuratePlayback = false; protected bool HasFrames => Frames.Count > 0; From 2a3601e43b26c70f93f7d4532e0313db92d72c03 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 Jul 2019 11:42:30 +0900 Subject: [PATCH 1690/2854] Fix test class filename case --- ...eplayinputHandlerTest.cs => FramedReplayInputHandlerTest.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Tests/NonVisual/{FramedReplayinputHandlerTest.cs => FramedReplayInputHandlerTest.cs} (99%) diff --git a/osu.Game.Tests/NonVisual/FramedReplayinputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs similarity index 99% rename from osu.Game.Tests/NonVisual/FramedReplayinputHandlerTest.cs rename to osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index 73387fa5ab..aa5bb02cdd 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayinputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -9,7 +9,7 @@ using osu.Game.Rulesets.Replays; namespace osu.Game.Tests.NonVisual { [TestFixture] - public class FramedReplayinputHandlerTest + public class FramedReplayInputHandlerTest { private Replay replay; private TestInputHandler handler; From bd53a96507ee406e28e38272fe904cb7607597eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 Jul 2019 11:47:50 +0900 Subject: [PATCH 1691/2854] Ensure tests cannot run forever --- .../NonVisual/FramedReplayInputHandlerTest.cs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index aa5bb02cdd..2f1c4831a4 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using NUnit.Framework; using osu.Game.Replays; @@ -160,10 +161,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestRewindInsideImportantSection() { - // fast forward to important section - while (handler.SetFrameFromTime(3000) != null) - { - } + fastForwardToPoint(3000); setTime(4000, 4000); confirmCurrentFrame(4); @@ -205,10 +203,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestRewindOutOfImportantSection() { - // fast forward to important section - while (handler.SetFrameFromTime(3500) != null) - { - } + fastForwardToPoint(3500); confirmCurrentFrame(3); confirmNextFrame(4); @@ -227,6 +222,15 @@ namespace osu.Game.Tests.NonVisual confirmNextFrame(2); } + private void fastForwardToPoint(double destination) + { + for (int i = 0; i < 1000; i++) + if (handler.SetFrameFromTime(destination) == null) + return; + + throw new TimeoutException("Seek was never fulfilled"); + } + private void setTime(double set, double? expect) { Assert.AreEqual(expect, handler.SetFrameFromTime(set)); From 5e2adf59beaff47a0dd0724067852df1dfd2dc8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 Jul 2019 11:53:34 +0900 Subject: [PATCH 1692/2854] Enforce frame accuracy for tests --- osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index 2f1c4831a4..18cbd4e7c5 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -278,6 +278,7 @@ namespace osu.Game.Tests.NonVisual public TestInputHandler(Replay replay) : base(replay) { + FrameAccuratePlayback = true; } protected override double AllowedImportantTimeSpan => 1000; From 7929104b8a8297c7333cc127b97d16370d453a71 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 10 Jul 2019 12:24:05 +0900 Subject: [PATCH 1693/2854] move default into StoryboardContainer, fix load bug, remove comment --- .../Background/TestSceneBackgroundScreenBeatmap.cs | 10 +--------- osu.Game/Graphics/Containers/StoryboardContainer.cs | 5 +++-- osu.Game/Screens/Play/Player.cs | 7 +------ 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs index 0d62bf9bdc..f0e50f8498 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs @@ -334,15 +334,7 @@ namespace osu.Game.Tests.Visual.Background { protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); - protected override StoryboardContainer CreateStoryboardContainer(Storyboard storyboard) - { - return new TestStoryboardContainer - { - RelativeSizeAxes = Axes.Both, - Alpha = 1, - EnableUserDim = { Value = true } - }; - } + protected override StoryboardContainer CreateStoryboardContainer(Storyboard storyboard) => new TestStoryboardContainer { RelativeSizeAxes = Axes.Both }; public TestStoryboardContainer CurrentStoryboardContainer => (TestStoryboardContainer)StoryboardContainer; diff --git a/osu.Game/Graphics/Containers/StoryboardContainer.cs b/osu.Game/Graphics/Containers/StoryboardContainer.cs index 472e22e212..899cbe1f0d 100644 --- a/osu.Game/Graphics/Containers/StoryboardContainer.cs +++ b/osu.Game/Graphics/Containers/StoryboardContainer.cs @@ -19,6 +19,8 @@ namespace osu.Game.Graphics.Containers public StoryboardContainer(Storyboard storyboard) { this.storyboard = storyboard; + EnableUserDim.Default = true; + EnableUserDim.Value = true; } [BackgroundDependencyLoader] @@ -29,13 +31,12 @@ namespace osu.Game.Graphics.Containers protected override void LoadComplete() { - ShowStoryboard.ValueChanged += _ => initializeStoryboard(true); + ShowStoryboard.BindValueChanged(_ => initializeStoryboard(true), true); base.LoadComplete(); } protected override void ApplyFade() { - // Storyboards cannot be blurred, so just hide the storyboard if it gets toggled. DimContainer.FadeTo(!ShowStoryboard.Value || UserDimLevel.Value == 1 ? 0 : 1, BACKGROUND_FADE_DURATION, Easing.OutQuint); } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 663113f747..f44cb069a9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -78,12 +78,7 @@ namespace osu.Game.Screens.Play protected StoryboardContainer StoryboardContainer { get; private set; } - protected virtual StoryboardContainer CreateStoryboardContainer(Storyboard storyboard) => new StoryboardContainer(storyboard) - { - RelativeSizeAxes = Axes.Both, - Alpha = 1, - EnableUserDim = { Value = true } - }; + protected virtual StoryboardContainer CreateStoryboardContainer(Storyboard storyboard) => new StoryboardContainer(storyboard) { RelativeSizeAxes = Axes.Both }; [Cached] [Cached(Type = typeof(IBindable>))] From 221ee58f550549a6f0e65c68a0baf4e68771cad6 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 10 Jul 2019 12:36:58 +0900 Subject: [PATCH 1694/2854] make storyboard text more visible --- .../Visual/Background/TestSceneBackgroundScreenBeatmap.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs index f0e50f8498..d9f4631ea8 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs @@ -10,6 +10,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Input.States; using osu.Framework.Platform; @@ -244,12 +245,13 @@ namespace osu.Game.Tests.Visual.Background player.ReplacesBackground.Value = false; player.CurrentStoryboardContainer.Add(new OsuSpriteText { - Size = new Vector2(250, 50), + Size = new Vector2(500, 50), Alpha = 1, - Colour = Color4.Tomato, + Colour = Color4.White, Anchor = Anchor.Centre, Origin = Anchor.Centre, Text = "THIS IS A STORYBOARD", + Font = new FontUsage(size: 50) }); }); From 8b8e67fd726d5b1a2ae2380a3afd62ecbeb38ddc Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Wed, 10 Jul 2019 10:41:52 +0200 Subject: [PATCH 1695/2854] Add accidentally deleted code --- osu.Game/Screens/Menu/MainMenu.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 078f9c5a15..b67801f9ba 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -123,6 +123,15 @@ namespace osu.Game.Screens.Menu var track = Beatmap.Value.Track; var metadata = Beatmap.Value.Metadata; + if (last is Intro && track != null) + { + if (!track.IsRunning) + { + track.Seek(metadata.PreviewTime != -1 ? metadata.PreviewTime : 0.4f * track.Length); + track.Start(); + } + } + Beatmap.ValueChanged += beatmap_ValueChanged; } From 100d15e651f3c8d27de36a689d6660c7a4e89083 Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Wed, 10 Jul 2019 10:43:02 +0200 Subject: [PATCH 1696/2854] Move reseting tempo to Editor --- osu.Game/Screens/Edit/Editor.cs | 8 +++++++- osu.Game/Screens/Menu/MainMenu.cs | 3 --- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 8b3cf1ec90..310ce27d45 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -238,7 +238,13 @@ namespace osu.Game.Screens.Edit public override bool OnExiting(IScreen next) { Background.FadeColour(Color4.White, 500); - Beatmap.Value.Track?.Stop(); + + if (Beatmap.Value.Track != null) + { + Beatmap.Value.Track.Tempo.Value = 1; + Beatmap.Value.Track.Stop(); + } + return base.OnExiting(next); } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index b67801f9ba..6e1c471c1a 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -194,10 +194,7 @@ namespace osu.Game.Screens.Menu preloadSongSelect(); if (Beatmap.Value.Track != null && !musicController.UserRequestedPause) - { - Beatmap.Value.Track.Tempo.Value = 1; Beatmap.Value.Track.Start(); - } } public override bool OnExiting(IScreen next) From fae3348a69127b10fcadff6437c1823ab8c9ade4 Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Wed, 10 Jul 2019 13:20:23 +0200 Subject: [PATCH 1697/2854] Add caching MusicController in tests --- .../Visual/Background/TestSceneBackgroundScreenBeatmap.cs | 3 ++- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs index 8b941e4633..a4bb5a38f0 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs @@ -20,6 +20,7 @@ using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Scoring; @@ -71,7 +72,7 @@ namespace osu.Game.Tests.Visual.Background Dependencies.Cache(rulesets = new RulesetStore(factory)); Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, Beatmap.Default)); Dependencies.Cache(new OsuConfigManager(LocalStorage)); - + Dependencies.Cache(new MusicController()); manager.Import(TestResources.GetTestBeatmapForImport()).Wait(); Beatmap.SetDefault(); diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 962e0fb362..446a632b31 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -16,6 +16,7 @@ using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; @@ -96,6 +97,7 @@ namespace osu.Game.Tests.Visual.SongSelect Dependencies.Cache(rulesets = new RulesetStore(factory)); Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default)); + Dependencies.Cache(new MusicController()); Beatmap.SetDefault(); } From c3315e805f92e2cb2e38d3ad42bdca7fa8283416 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 10 Jul 2019 16:49:32 +0300 Subject: [PATCH 1698/2854] Use milliseconds for BasicStats' beatmap length --- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index 2926c82631..5b10c4e0bb 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -60,7 +60,7 @@ namespace osu.Game.Overlays.BeatmapSet } else { - length.Value = TimeSpan.FromSeconds(beatmap.Length).ToString(@"m\:ss"); + length.Value = TimeSpan.FromMilliseconds(beatmap.Length).ToString(@"m\:ss"); circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString(); sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString(); } From 9986178bf41b051c2c288549ed8087f630e2331b Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Wed, 10 Jul 2019 13:20:23 +0200 Subject: [PATCH 1699/2854] Revert "Add caching MusicController in tests" This reverts commit fae3348a69127b10fcadff6437c1823ab8c9ade4. --- .../Visual/Background/TestSceneBackgroundScreenBeatmap.cs | 3 +-- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs index a4bb5a38f0..8b941e4633 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs @@ -20,7 +20,6 @@ using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Scoring; @@ -72,7 +71,7 @@ namespace osu.Game.Tests.Visual.Background Dependencies.Cache(rulesets = new RulesetStore(factory)); Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, Beatmap.Default)); Dependencies.Cache(new OsuConfigManager(LocalStorage)); - Dependencies.Cache(new MusicController()); + manager.Import(TestResources.GetTestBeatmapForImport()).Wait(); Beatmap.SetDefault(); diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 446a632b31..962e0fb362 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -16,7 +16,6 @@ using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Database; -using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; @@ -97,7 +96,6 @@ namespace osu.Game.Tests.Visual.SongSelect Dependencies.Cache(rulesets = new RulesetStore(factory)); Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default)); - Dependencies.Cache(new MusicController()); Beatmap.SetDefault(); } From b225b2eb3958cf7c3f1d4fd245a1703885d3d228 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Jul 2019 00:18:19 +0900 Subject: [PATCH 1700/2854] Rename to IsUserPaused --- osu.Game/Overlays/MusicController.cs | 14 +++++++++----- osu.Game/Screens/Menu/MainMenu.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index ad0c0717ac..abbcec5094 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -55,7 +55,7 @@ namespace osu.Game.Overlays private Container dragContainer; private Container playerContainer; - public bool UserRequestedPause { get; private set; } + public bool IsUserPaused { get; private set; } [Resolved] private Bindable beatmap { get; set; } @@ -159,7 +159,7 @@ namespace osu.Game.Overlays Origin = Anchor.Centre, Scale = new Vector2(1.4f), IconScale = new Vector2(1.4f), - Action = play, + Action = togglePause, Icon = FontAwesome.Regular.PlayCircle, }, nextButton = new MusicIconButton @@ -278,7 +278,7 @@ namespace osu.Game.Overlays } } - private void play() + private void togglePause() { var track = current?.Track; @@ -289,12 +289,16 @@ namespace osu.Game.Overlays return; } - UserRequestedPause = track.IsRunning; - if (track.IsRunning) + { + IsUserPaused = true; track.Stop(); + } else + { track.Start(); + IsUserPaused = false; + } } private void prev() diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 6e1c471c1a..93c413452d 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -193,7 +193,7 @@ namespace osu.Game.Screens.Menu //we may have consumed our preloaded instance, so let's make another. preloadSongSelect(); - if (Beatmap.Value.Track != null && !musicController.UserRequestedPause) + if (Beatmap.Value.Track != null && !musicController.IsUserPaused) Beatmap.Value.Track.Start(); } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index dd115e04d4..085232b27f 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -573,7 +573,7 @@ namespace osu.Game.Screens.Select { Track track = Beatmap.Value.Track; - if ((!track.IsRunning || restart) && !musicController.UserRequestedPause) + if ((!track.IsRunning || restart) && !musicController.IsUserPaused) { track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; track.Restart(); From 6819c528db75c4d88f75ab35c837f1f3e81e13bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Jul 2019 00:20:01 +0900 Subject: [PATCH 1701/2854] Use canBeNull instead of needlessly caching MusicController for tests --- osu.Game/Screens/Menu/MainMenu.cs | 6 +++--- osu.Game/Screens/Select/SongSelect.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 93c413452d..5999cbdfb5 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -42,8 +42,8 @@ namespace osu.Game.Screens.Menu [Resolved] private GameHost host { get; set; } - [Resolved] - private MusicController musicController { get; set; } + [Resolved(canBeNull: true)] + private MusicController music { get; set; } private BackgroundScreenDefault background; @@ -193,7 +193,7 @@ namespace osu.Game.Screens.Menu //we may have consumed our preloaded instance, so let's make another. preloadSongSelect(); - if (Beatmap.Value.Track != null && !musicController.IsUserPaused) + if (Beatmap.Value.Track != null && music?.IsUserPaused != true) Beatmap.Value.Track.Start(); } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 085232b27f..0eeffda5eb 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -87,8 +87,8 @@ namespace osu.Game.Screens.Select private readonly Bindable decoupledRuleset = new Bindable(); - [Resolved] - private MusicController musicController { get; set; } + [Resolved(canBeNull: true)] + private MusicController music { get; set; } [Cached] [Cached(Type = typeof(IBindable>))] @@ -573,7 +573,7 @@ namespace osu.Game.Screens.Select { Track track = Beatmap.Value.Track; - if ((!track.IsRunning || restart) && !musicController.IsUserPaused) + if ((!track.IsRunning || restart) && music?.IsUserPaused != true) { track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; track.Restart(); From ad873b542a09334a86956be8b34249bce849b2f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Jul 2019 00:22:40 +0900 Subject: [PATCH 1702/2854] Simplify editor logic --- osu.Game/Screens/Edit/Editor.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 310ce27d45..8cc227d9be 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -224,30 +224,32 @@ namespace osu.Game.Screens.Edit public override void OnResuming(IScreen last) { - Beatmap.Value.Track?.Stop(); base.OnResuming(last); + Beatmap.Value.Track?.Stop(); } public override void OnEntering(IScreen last) { base.OnEntering(last); + Background.FadeColour(Color4.DarkGray, 500); - Beatmap.Value.Track?.Stop(); + resetTrack(); } public override bool OnExiting(IScreen next) { Background.FadeColour(Color4.White, 500); - - if (Beatmap.Value.Track != null) - { - Beatmap.Value.Track.Tempo.Value = 1; - Beatmap.Value.Track.Stop(); - } + resetTrack(); return base.OnExiting(next); } + private void resetTrack() + { + Beatmap.Value.Track?.ResetSpeedAdjustments(); + Beatmap.Value.Track?.Stop(); + } + private void exportBeatmap() => host.OpenFileExternally(Beatmap.Value.Save()); private void onModeChanged(ValueChangedEvent e) From f21e700b7af378f7196732cf1a725137433de8fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Jul 2019 00:42:14 +0900 Subject: [PATCH 1703/2854] Code style cleanup --- osu.Game/Overlays/BeatmapSetOverlay.cs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 3a17b6eec1..154e6f20f6 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -28,7 +28,9 @@ namespace osu.Game.Overlays protected readonly Header Header; private RulesetStore rulesets; - private readonly ScoresContainer scores; + + private readonly ScoresContainer scoreContainer; + private GetScoresRequest getScoresRequest; private readonly Bindable beatmapSet = new Bindable(); @@ -63,7 +65,7 @@ namespace osu.Game.Overlays { Header = new Header(), info = new Info(), - scores = new ScoresContainer(), + scoreContainer = new ScoresContainer(), }, }, }, @@ -81,23 +83,24 @@ namespace osu.Game.Overlays }; } - private void getScores(BeatmapInfo b) + private void getScores(BeatmapInfo beatmap) { getScoresRequest?.Cancel(); + getScoresRequest = null; - if (b?.OnlineBeatmapID.HasValue != true) + if (beatmap?.OnlineBeatmapID.HasValue != true) { - scores.Scores = null; + scoreContainer.Scores = null; return; } - scores.Loading = true; + scoreContainer.Loading = true; - getScoresRequest = new GetScoresRequest(b, b.Ruleset); - getScoresRequest.Success += r => Schedule(() => + getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); + getScoresRequest.Success += scores => Schedule(() => { - scores.Scores = r; - scores.Loading = false; + scoreContainer.Scores = scores; + scoreContainer.Loading = false; }); api.Queue(getScoresRequest); } From 953d32366c8f067fe11f41825d64268e18889ed4 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 10 Jul 2019 19:40:29 +0300 Subject: [PATCH 1704/2854] Move request inside the ScoresContainer again --- .../Visual/Online/TestSceneScoresContainer.cs | 12 +++- .../BeatmapSet/Scores/ScoresContainer.cs | 64 ++++++++++++++++--- osu.Game/Overlays/BeatmapSetOverlay.cs | 34 +--------- 3 files changed, 68 insertions(+), 42 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index 824280fe68..b26de1984a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Online public TestSceneScoresContainer() { - ScoresContainer scoresContainer; + TestScoresContainer scoresContainer; Child = new Container { @@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Online RelativeSizeAxes = Axes.Both, Colour = Color4.Black, }, - scoresContainer = new ScoresContainer(), + scoresContainer = new TestScoresContainer(), } }; @@ -245,5 +245,13 @@ namespace osu.Game.Tests.Visual.Online scoresContainer.Scores = allScores; }); } + + private class TestScoresContainer : ScoresContainer + { + public new APILegacyScores Scores + { + set => base.Scores = value; + } + } } } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 94bcfdee4f..bcb9383d0b 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -11,6 +11,9 @@ using osuTK; using System.Collections.Generic; using System.Linq; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; namespace osu.Game.Overlays.BeatmapSet.Scores { @@ -24,6 +27,40 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly FillFlowContainer topScoresContainer; private readonly LoadingAnimation loadingAnimation; + [Resolved] + private IAPIProvider api { get; set; } + + private GetScoresRequest getScoresRequest; + + private APILegacyScores scores; + + protected APILegacyScores Scores + { + get => scores; + set + { + scores = value; + + updateDisplay(); + } + } + + private BeatmapInfo beatmap; + + public BeatmapInfo Beatmap + { + get => beatmap; + set + { + if (beatmap == value) + return; + + beatmap = value; + + getScores(beatmap); + } + } + public ScoresContainer() { RelativeSizeAxes = Axes.X; @@ -75,7 +112,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores updateDisplay(); } - public bool Loading + private bool loading { set { @@ -86,17 +123,26 @@ namespace osu.Game.Overlays.BeatmapSet.Scores } } - private APILegacyScores scores; - - public APILegacyScores Scores + private void getScores(BeatmapInfo beatmap) { - get => scores; - set - { - scores = value; + getScoresRequest?.Cancel(); + getScoresRequest = null; - updateDisplay(); + if (beatmap?.OnlineBeatmapID.HasValue != true) + { + Scores = null; + return; } + + loading = true; + + getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); + getScoresRequest.Success += scores => Schedule(() => + { + Scores = scores; + loading = false; + }); + api.Queue(getScoresRequest); } private void updateDisplay() diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 154e6f20f6..c20e6368d8 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -11,7 +11,6 @@ using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.BeatmapSet; using osu.Game.Overlays.BeatmapSet.Scores; @@ -29,15 +28,8 @@ namespace osu.Game.Overlays private RulesetStore rulesets; - private readonly ScoresContainer scoreContainer; - - private GetScoresRequest getScoresRequest; - private readonly Bindable beatmapSet = new Bindable(); - [Resolved] - private IAPIProvider api { get; set; } - // receive input outside our bounds so we can trigger a close event on ourselves. public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; @@ -45,6 +37,8 @@ namespace osu.Game.Overlays { OsuScrollContainer scroll; Info info; + ScoresContainer scoreContainer; + Children = new Drawable[] { new Box @@ -77,34 +71,12 @@ namespace osu.Game.Overlays Header.Picker.Beatmap.ValueChanged += b => { info.Beatmap = b.NewValue; - getScores(b.NewValue); + scoreContainer.Beatmap = b.NewValue; scroll.ScrollToStart(); }; } - private void getScores(BeatmapInfo beatmap) - { - getScoresRequest?.Cancel(); - getScoresRequest = null; - - if (beatmap?.OnlineBeatmapID.HasValue != true) - { - scoreContainer.Scores = null; - return; - } - - scoreContainer.Loading = true; - - getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); - getScoresRequest.Success += scores => Schedule(() => - { - scoreContainer.Scores = scores; - scoreContainer.Loading = false; - }); - api.Queue(getScoresRequest); - } - [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { From b32b078e48d38a88cfd222c085cb5e9d648e98e8 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 10 Jul 2019 21:55:43 +0200 Subject: [PATCH 1705/2854] Set default keybindings for jukebox to stable's ones. --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index e756694285..cdd821c173 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -45,9 +45,9 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.Enter, GlobalAction.Select), new KeyBinding(InputKey.KeypadEnter, GlobalAction.Select), - new KeyBinding(InputKey.F5, GlobalAction.MusicPrev), - new KeyBinding(InputKey.F6, GlobalAction.MusicNext), - new KeyBinding(InputKey.X, GlobalAction.MusicPlay), + new KeyBinding(InputKey.F1, GlobalAction.MusicPrev), + new KeyBinding(InputKey.F5, GlobalAction.MusicNext), + new KeyBinding(InputKey.F3, GlobalAction.MusicPlay), }; public IEnumerable InGameKeyBindings => new[] From b2f23a10c87437db39a20b98a1fd2ad6afcf738f Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 10 Jul 2019 23:12:18 +0300 Subject: [PATCH 1706/2854] Use the correct property to retrieve the milliseconds --- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index f50e281dd0..f4d67a56aa 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -72,7 +72,7 @@ namespace osu.Game.Online.API.Requests.Responses StarDifficulty = starDifficulty, OnlineBeatmapID = OnlineBeatmapID, Version = version, - Length = TimeSpan.FromSeconds(length).Milliseconds, + Length = TimeSpan.FromSeconds(length).TotalMilliseconds, Status = Status, BeatmapSet = set, Metrics = metrics, From a49bde7ed3369a734f27957b3a08e4456cbc8f90 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Jul 2019 10:38:32 +0900 Subject: [PATCH 1707/2854] Move protected below public --- .../BeatmapSet/Scores/ScoresContainer.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index bcb9383d0b..dfe6c45750 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -32,19 +32,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private GetScoresRequest getScoresRequest; - private APILegacyScores scores; - - protected APILegacyScores Scores - { - get => scores; - set - { - scores = value; - - updateDisplay(); - } - } - private BeatmapInfo beatmap; public BeatmapInfo Beatmap @@ -61,6 +48,19 @@ namespace osu.Game.Overlays.BeatmapSet.Scores } } + private APILegacyScores scores; + + protected APILegacyScores Scores + { + get => scores; + set + { + scores = value; + + updateDisplay(); + } + } + public ScoresContainer() { RelativeSizeAxes = Axes.X; From cc9ee472d6090584587c56098c92bee6f5e34d30 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Jul 2019 11:07:30 +0900 Subject: [PATCH 1708/2854] Move score nulling out of loading property --- .../Overlays/BeatmapSet/Scores/ScoresContainer.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index dfe6c45750..5f200d7343 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -114,13 +114,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private bool loading { - set - { - loadingAnimation.FadeTo(value ? 1 : 0, fade_duration); - - if (value) - Scores = null; - } + set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration); } private void getScores(BeatmapInfo beatmap) @@ -128,11 +122,10 @@ namespace osu.Game.Overlays.BeatmapSet.Scores getScoresRequest?.Cancel(); getScoresRequest = null; + Scores = null; + if (beatmap?.OnlineBeatmapID.HasValue != true) - { - Scores = null; return; - } loading = true; From 8f9b8ed5a19d121977a08c16b308dc734ef2d3a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Jul 2019 11:17:33 +0900 Subject: [PATCH 1709/2854] Simplify information propagation logic --- .../Overlays/BeatmapSet/Scores/ScoreTable.cs | 2 +- .../BeatmapSet/Scores/ScoresContainer.cs | 60 +++++++++---------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 15816be327..347522fb48 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Content = null; backgroundFlow.Clear(); - if (value == null || !value.Any()) + if (value?.Any() != true) return; for (int i = 0; i < value.Count; i++) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 5f200d7343..22d7ea9c97 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osuTK; -using System.Collections.Generic; using System.Linq; using osu.Game.Online.API.Requests.Responses; using osu.Game.Beatmaps; @@ -48,16 +47,34 @@ namespace osu.Game.Overlays.BeatmapSet.Scores } } - private APILegacyScores scores; - protected APILegacyScores Scores { - get => scores; set { - scores = value; + Schedule(() => + { + loading = false; - updateDisplay(); + topScoresContainer.Clear(); + + if (value?.Scores.Any() != true) + { + scoreTable.Scores = null; + scoreTable.Hide(); + return; + } + + scoreTable.Scores = value.Scores; + scoreTable.Show(); + + var topScore = value.Scores.First(); + var userScore = value.UserScore; + + topScoresContainer.Add(new DrawableTopScore(topScore)); + + if (userScore != null && userScore.Score.OnlineScoreID != topScore.OnlineScoreID) + topScoresContainer.Add(new DrawableTopScore(userScore.Score, userScore.Position)); + }); } } @@ -109,7 +126,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private void load(OsuColour colours) { background.Colour = colours.Gray2; - updateDisplay(); } private bool loading @@ -125,35 +141,15 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Scores = null; if (beatmap?.OnlineBeatmapID.HasValue != true) + { + loading = false; return; - - loading = true; + } getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); - getScoresRequest.Success += scores => Schedule(() => - { - Scores = scores; - loading = false; - }); + getScoresRequest.Success += scores => Scores = scores; api.Queue(getScoresRequest); - } - - private void updateDisplay() - { - topScoresContainer.Clear(); - - scoreTable.Scores = scores?.Scores.Count > 1 ? scores.Scores : new List(); - scoreTable.FadeTo(scores?.Scores.Count > 1 ? 1 : 0); - - if (scores?.Scores.Any() ?? false) - { - topScoresContainer.Add(new DrawableTopScore(scores.Scores.FirstOrDefault())); - - var userScore = scores.UserScore; - - if (userScore != null && userScore.Position != 1) - topScoresContainer.Add(new DrawableTopScore(userScore.Score, userScore.Position)); - } + loading = true; } } } From 85f2212ebcb8d3612e41304f49d4caf0289374ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Jul 2019 11:32:42 +0900 Subject: [PATCH 1710/2854] Reduce spacing and font for rank position --- .../BeatmapSet/Scores/TopScoreUserSection.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 6d43ec6177..a15d3c5fd1 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -28,11 +28,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly SpriteText date; private readonly UpdateableFlag flag; - public int ScorePosition - { - set => rankText.Text = $"#{value}"; - } - public TopScoreUserSection() { AutoSizeAxes = Axes.Both; @@ -50,14 +45,13 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Origin = Anchor.Centre, AutoSizeAxes = Axes.Both, Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 3), Children = new Drawable[] { rankText = new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, italics: true) + Font = OsuFont.GetFont(size: 24, weight: FontWeight.Bold, italics: true) }, rank = new UpdateableRank(ScoreRank.D) { @@ -124,6 +118,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores rankText.Colour = colours.Yellow; } + public int ScorePosition + { + set => rankText.Text = $"#{value}"; + } + /// /// Sets the score to be displayed. /// From 321266e96fc1de7735441dbb70ead7c856bff191 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 11 Jul 2019 13:17:28 +0900 Subject: [PATCH 1711/2854] Make UserDimContainer abstract --- .../TestSceneBackgroundScreenBeatmap.cs | 20 +++--- .../Containers/DimmableBackgroundContainer.cs | 68 +++++++++++++++++++ ...iner.cs => DimmableStoryboardContainer.cs} | 6 +- .../Graphics/Containers/UserDimContainer.cs | 67 +++--------------- .../Backgrounds/BackgroundScreenBeatmap.cs | 4 +- osu.Game/Screens/Play/Player.cs | 8 +-- 6 files changed, 95 insertions(+), 78 deletions(-) create mode 100644 osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs rename osu.Game/Graphics/Containers/{StoryboardContainer.cs => DimmableStoryboardContainer.cs} (89%) diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs index d9f4631ea8..09aaee4adc 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs @@ -243,7 +243,7 @@ namespace osu.Game.Tests.Visual.Background { player.StoryboardEnabled.Value = false; player.ReplacesBackground.Value = false; - player.CurrentStoryboardContainer.Add(new OsuSpriteText + player.CurrentDimmableStoryboardContainer.Add(new OsuSpriteText { Size = new Vector2(500, 50), Alpha = 1, @@ -336,9 +336,9 @@ namespace osu.Game.Tests.Visual.Background { protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); - protected override StoryboardContainer CreateStoryboardContainer(Storyboard storyboard) => new TestStoryboardContainer { RelativeSizeAxes = Axes.Both }; + protected override DimmableStoryboardContainer CreateStoryboardContainer(Storyboard storyboard) => new TestDimmableStoryboardContainer { RelativeSizeAxes = Axes.Both }; - public TestStoryboardContainer CurrentStoryboardContainer => (TestStoryboardContainer)StoryboardContainer; + public TestDimmableStoryboardContainer CurrentDimmableStoryboardContainer => (TestDimmableStoryboardContainer)DimmableStoryboardContainer; // Whether or not the player should be allowed to load. public bool BlockLoad; @@ -352,9 +352,9 @@ namespace osu.Game.Tests.Visual.Background { } - public bool IsStoryboardVisible() => CurrentStoryboardContainer.CurrentAlpha == 1; + public bool IsStoryboardVisible() => CurrentDimmableStoryboardContainer.CurrentAlpha == 1; - public bool IsStoryboardInvisible() => CurrentStoryboardContainer.CurrentAlpha <= 1; + public bool IsStoryboardInvisible() => CurrentDimmableStoryboardContainer.CurrentAlpha <= 1; [BackgroundDependencyLoader] private void load(OsuConfigManager config, CancellationToken token) @@ -387,7 +387,7 @@ namespace osu.Game.Tests.Visual.Background private class FadeAccessibleBackground : BackgroundScreenBeatmap { - protected override UserDimContainer CreateFadeContainer() => fadeContainer = new TestUserDimContainer { RelativeSizeAxes = Axes.Both }; + protected override DimmableBackgroundContainer CreateFadeContainer() => fadeContainer = new TestDimmableBackgroundContainer { RelativeSizeAxes = Axes.Both }; public Color4 CurrentColour => fadeContainer.CurrentColour; @@ -395,7 +395,7 @@ namespace osu.Game.Tests.Visual.Background public Vector2 CurrentBlur => Background.BlurSigma; - private TestUserDimContainer fadeContainer; + private TestDimmableBackgroundContainer fadeContainer; public FadeAccessibleBackground(WorkingBeatmap beatmap) : base(beatmap) @@ -403,17 +403,17 @@ namespace osu.Game.Tests.Visual.Background } } - private class TestStoryboardContainer : StoryboardContainer + private class TestDimmableStoryboardContainer : DimmableStoryboardContainer { public float CurrentAlpha => DimContainer.Alpha; - public TestStoryboardContainer() + public TestDimmableStoryboardContainer() : base(new Storyboard()) { } } - private class TestUserDimContainer : UserDimContainer + private class TestDimmableBackgroundContainer : DimmableBackgroundContainer { public Color4 CurrentColour => DimContainer.Colour; public float CurrentAlpha => DimContainer.Alpha; diff --git a/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs b/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs new file mode 100644 index 0000000000..2d010943bd --- /dev/null +++ b/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs @@ -0,0 +1,68 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Configuration; +using osu.Game.Graphics.Backgrounds; +using osuTK; + +namespace osu.Game.Graphics.Containers +{ + public class DimmableBackgroundContainer : UserDimContainer + { + /// + /// The amount of blur to be applied to the background in addition to user-specified blur. + /// + /// + /// Used in contexts where there can potentially be both user and screen-specified blurring occuring at the same time, such as in + /// + public readonly Bindable BlurAmount = new Bindable(); + + private Bindable userBlurLevel { get; set; } + + private Background background; + + public Background Background + { + get => background; + set + { + base.Add(background = value); + background.BlurTo(blurTarget, 0, Easing.OutQuint); + } + } + + public override void Add(Drawable drawable) + { + if (drawable is Background) + throw new InvalidOperationException($"Use {nameof(Background)} to set a background."); + + base.Add(drawable); + } + + /// + /// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs. + /// + private Vector2 blurTarget => EnableUserDim.Value + ? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * 25) + : new Vector2(BlurAmount.Value); + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + userBlurLevel = config.GetBindable(OsuSetting.BlurLevel); + BlurAmount.ValueChanged += _ => UpdateVisuals(); + userBlurLevel.ValueChanged += _ => UpdateVisuals(); + } + + protected override void ApplyFade() + { + // The background needs to be hidden in the case of it being replaced by the storyboard + DimContainer.FadeTo(ShowStoryboard.Value && StoryboardReplacesBackground.Value ? 0 : 1, BACKGROUND_FADE_DURATION, Easing.OutQuint); + Background?.BlurTo(blurTarget, BACKGROUND_FADE_DURATION, Easing.OutQuint); + } + } +} diff --git a/osu.Game/Graphics/Containers/StoryboardContainer.cs b/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs similarity index 89% rename from osu.Game/Graphics/Containers/StoryboardContainer.cs rename to osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs index 899cbe1f0d..a8a7b67e01 100644 --- a/osu.Game/Graphics/Containers/StoryboardContainer.cs +++ b/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs @@ -11,16 +11,14 @@ namespace osu.Game.Graphics.Containers /// /// A container that handles loading, as well as applies user-specified visual settings to it. /// - public class StoryboardContainer : UserDimContainer + public class DimmableStoryboardContainer : UserDimContainer { private readonly Storyboard storyboard; private DrawableStoryboard drawableStoryboard; - public StoryboardContainer(Storyboard storyboard) + public DimmableStoryboardContainer(Storyboard storyboard) { this.storyboard = storyboard; - EnableUserDim.Default = true; - EnableUserDim.Value = true; } [BackgroundDependencyLoader] diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index ad6f73eff5..e6a040fcd8 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -1,15 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Configuration; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Screens.Play; -using osuTK; using osuTK.Graphics; namespace osu.Game.Graphics.Containers @@ -17,7 +13,7 @@ namespace osu.Game.Graphics.Containers /// /// A container that applies user-configured visual settings to its contents. /// - public class UserDimContainer : Container + public abstract class UserDimContainer : Container { protected const float BACKGROUND_FADE_DURATION = 800; @@ -31,14 +27,6 @@ namespace osu.Game.Graphics.Containers /// public readonly Bindable StoryboardReplacesBackground = new Bindable(); - /// - /// The amount of blur to be applied to the background in addition to user-specified blur. - /// - /// - /// Used in contexts where there can potentially be both user and screen-specified blurring occuring at the same time, such as in - /// - public readonly Bindable BlurAmount = new Bindable(); - protected Bindable UserDimLevel { get; private set; } protected Bindable ShowStoryboard { get; private set; } @@ -47,62 +35,30 @@ namespace osu.Game.Graphics.Containers protected override Container Content => DimContainer; - private Bindable userBlurLevel { get; set; } - - /// - /// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs. - /// - private Vector2 blurTarget => EnableUserDim.Value - ? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * 25) - : new Vector2(BlurAmount.Value); - /// /// Creates a new . /// - public UserDimContainer() + protected UserDimContainer() { AddInternal(DimContainer = new Container { RelativeSizeAxes = Axes.Both }); } - private Background background; - - public Background Background - { - get => background; - set - { - base.Add(background = value); - background.BlurTo(blurTarget, 0, Easing.OutQuint); - } - } - - public override void Add(Drawable drawable) - { - if (drawable is Background) - throw new InvalidOperationException($"Use {nameof(Background)} to set a background."); - - base.Add(drawable); - } - [BackgroundDependencyLoader] private void load(OsuConfigManager config) { UserDimLevel = config.GetBindable(OsuSetting.DimLevel); - userBlurLevel = config.GetBindable(OsuSetting.BlurLevel); ShowStoryboard = config.GetBindable(OsuSetting.ShowStoryboard); - EnableUserDim.ValueChanged += _ => updateVisuals(); - UserDimLevel.ValueChanged += _ => updateVisuals(); - ShowStoryboard.ValueChanged += _ => updateVisuals(); - StoryboardReplacesBackground.ValueChanged += _ => updateVisuals(); - BlurAmount.ValueChanged += _ => updateVisuals(); - userBlurLevel.ValueChanged += _ => updateVisuals(); + EnableUserDim.ValueChanged += _ => UpdateVisuals(); + UserDimLevel.ValueChanged += _ => UpdateVisuals(); + ShowStoryboard.ValueChanged += _ => UpdateVisuals(); + StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals(); } protected override void LoadComplete() { base.LoadComplete(); - updateVisuals(); + UpdateVisuals(); } /// @@ -112,14 +68,9 @@ namespace osu.Game.Graphics.Containers /// While both backgrounds and storyboards allow user dim levels to be applied, storyboards can be toggled via /// and can cause backgrounds to become hidden via . Storyboards are also currently unable to be blurred. /// - protected virtual void ApplyFade() - { - // The background needs to be hidden in the case of it being replaced by the storyboard - DimContainer.FadeTo(ShowStoryboard.Value && StoryboardReplacesBackground.Value ? 0 : 1, BACKGROUND_FADE_DURATION, Easing.OutQuint); - Background?.BlurTo(blurTarget, BACKGROUND_FADE_DURATION, Easing.OutQuint); - } + protected abstract void ApplyFade(); - private void updateVisuals() + protected void UpdateVisuals() { ApplyFade(); diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index b6c2d016d2..1bb613755b 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -30,9 +30,9 @@ namespace osu.Game.Screens.Backgrounds /// public readonly Bindable BlurAmount = new Bindable(); - private readonly UserDimContainer fadeContainer; + private readonly DimmableBackgroundContainer fadeContainer; - protected virtual UserDimContainer CreateFadeContainer() => new UserDimContainer { RelativeSizeAxes = Axes.Both }; + protected virtual DimmableBackgroundContainer CreateFadeContainer() => new DimmableBackgroundContainer() { RelativeSizeAxes = Axes.Both }; public BackgroundScreenBeatmap(WorkingBeatmap beatmap = null) { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index f44cb069a9..55e759d215 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -76,9 +76,9 @@ namespace osu.Game.Screens.Play protected GameplayClockContainer GameplayClockContainer { get; private set; } - protected StoryboardContainer StoryboardContainer { get; private set; } + protected DimmableStoryboardContainer DimmableStoryboardContainer { get; private set; } - protected virtual StoryboardContainer CreateStoryboardContainer(Storyboard storyboard) => new StoryboardContainer(storyboard) { RelativeSizeAxes = Axes.Both }; + protected virtual DimmableStoryboardContainer CreateStoryboardContainer(Storyboard storyboard) => new DimmableStoryboardContainer(storyboard) { RelativeSizeAxes = Axes.Both }; [Cached] [Cached(Type = typeof(IBindable>))] @@ -124,7 +124,7 @@ namespace osu.Game.Screens.Play GameplayClockContainer.Children = new[] { - StoryboardContainer = CreateStoryboardContainer(Beatmap.Value.Storyboard), + DimmableStoryboardContainer = CreateStoryboardContainer(Beatmap.Value.Storyboard), new ScalingContainer(ScalingMode.Gameplay) { Child = new LocalSkinOverrideContainer(working.Skin) @@ -455,7 +455,7 @@ namespace osu.Game.Screens.Play Background.BlurAmount.Value = 0; Background.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); - StoryboardContainer.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); + DimmableStoryboardContainer.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); storyboardReplacesBackground.Value = Beatmap.Value.Storyboard.ReplacesBackground && Beatmap.Value.Storyboard.HasDrawable; From ac170a695749c8b1e9b9662d193de78348a93e7c Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 11 Jul 2019 14:00:25 +0900 Subject: [PATCH 1712/2854] add comment and cleanup --- osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs | 1 + osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs | 4 ++++ osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs b/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs index 2d010943bd..f415792993 100644 --- a/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs +++ b/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Graphics.Backgrounds; +using osu.Game.Screens.Play; using osuTK; namespace osu.Game.Graphics.Containers diff --git a/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs b/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs index a8a7b67e01..16379baeab 100644 --- a/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs +++ b/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs @@ -19,6 +19,10 @@ namespace osu.Game.Graphics.Containers public DimmableStoryboardContainer(Storyboard storyboard) { this.storyboard = storyboard; + + // Storyboards current do not get used in scenarios without user dim, so default to enabled here. + EnableUserDim.Default = true; + EnableUserDim.Value = true; } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 1bb613755b..97f77f789a 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Backgrounds private readonly DimmableBackgroundContainer fadeContainer; - protected virtual DimmableBackgroundContainer CreateFadeContainer() => new DimmableBackgroundContainer() { RelativeSizeAxes = Axes.Both }; + protected virtual DimmableBackgroundContainer CreateFadeContainer() => new DimmableBackgroundContainer { RelativeSizeAxes = Axes.Both }; public BackgroundScreenBeatmap(WorkingBeatmap beatmap = null) { From 932243cfd4c7908d548d15df939d4eb2df6e8eb1 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 11 Jul 2019 14:14:00 +0900 Subject: [PATCH 1713/2854] public before private --- .../Graphics/Containers/DimmableBackgroundContainer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs b/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs index f415792993..7339548069 100644 --- a/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs +++ b/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs @@ -22,10 +22,6 @@ namespace osu.Game.Graphics.Containers /// public readonly Bindable BlurAmount = new Bindable(); - private Bindable userBlurLevel { get; set; } - - private Background background; - public Background Background { get => background; @@ -36,6 +32,10 @@ namespace osu.Game.Graphics.Containers } } + private Bindable userBlurLevel { get; set; } + + private Background background; + public override void Add(Drawable drawable) { if (drawable is Background) From 3bc789fca892d8120e9916c6b285d56f64490086 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Jul 2019 15:09:06 +0900 Subject: [PATCH 1714/2854] Remove osd prefix and prefer upper-case OSD --- .../Visual/UserInterface/TestSceneOnScreenDisplay.cs | 3 ++- osu.Game/Overlays/OSD/{OsdToast.cs => Toast.cs} | 6 +++--- .../{OsdTrackedSettingToast.cs => TrackedSettingToast.cs} | 6 +++--- osu.Game/Overlays/OnScreenDisplay.cs | 6 +++--- 4 files changed, 11 insertions(+), 10 deletions(-) rename osu.Game/Overlays/OSD/{OsdToast.cs => Toast.cs} (94%) rename osu.Game/Overlays/OSD/{OsdTrackedSettingToast.cs => TrackedSettingToast.cs} (98%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs index b28f794a58..59a35591bd 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs @@ -7,6 +7,7 @@ using osu.Framework.Configuration; using osu.Framework.Configuration.Tracking; using osu.Framework.Graphics; using osu.Game.Overlays; +using osu.Game.Overlays.OSD; namespace osu.Game.Tests.Visual.UserInterface { @@ -22,7 +23,7 @@ namespace osu.Game.Tests.Visual.UserInterface osd.BeginTracking(this, config); Add(osd); - AddStep("Display empty osd toast", () => osd.Display(new Overlays.OSD.OsdToast())); + AddStep("Display empty osd toast", () => osd.Display(new Toast())); AddRepeatStep("Change toggle (no bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingNoKeybind), 2); AddRepeatStep("Change toggle (with bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingWithKeybind), 2); AddRepeatStep("Change enum (no bind)", () => config.IncrementEnumSetting(TestConfigSetting.EnumSettingNoKeybind), 3); diff --git a/osu.Game/Overlays/OSD/OsdToast.cs b/osu.Game/Overlays/OSD/Toast.cs similarity index 94% rename from osu.Game/Overlays/OSD/OsdToast.cs rename to osu.Game/Overlays/OSD/Toast.cs index f700577ba1..6634959bca 100644 --- a/osu.Game/Overlays/OSD/OsdToast.cs +++ b/osu.Game/Overlays/OSD/Toast.cs @@ -1,19 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osuTK.Graphics; namespace osu.Game.Overlays.OSD { - public class OsdToast : Container + public class Toast : Container { private readonly Container content; protected override Container Content => content; - public OsdToast() + public Toast() { Anchor = Anchor.Centre; Origin = Anchor.Centre; diff --git a/osu.Game/Overlays/OSD/OsdTrackedSettingToast.cs b/osu.Game/Overlays/OSD/TrackedSettingToast.cs similarity index 98% rename from osu.Game/Overlays/OSD/OsdTrackedSettingToast.cs rename to osu.Game/Overlays/OSD/TrackedSettingToast.cs index 411a33b000..8def8476f0 100644 --- a/osu.Game/Overlays/OSD/OsdTrackedSettingToast.cs +++ b/osu.Game/Overlays/OSD/TrackedSettingToast.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Configuration.Tracking; using osu.Framework.Extensions.Color4Extensions; @@ -13,13 +14,12 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; -using System; namespace osu.Game.Overlays.OSD { - public class OsdTrackedSettingToast : OsdToast + public class TrackedSettingToast : Toast { - public OsdTrackedSettingToast(SettingDescription description) + public TrackedSettingToast(SettingDescription description) { SpriteText textLine2; FillFlowContainer optionLights; diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs index 0577257080..a30ce5c56f 100644 --- a/osu.Game/Overlays/OnScreenDisplay.cs +++ b/osu.Game/Overlays/OnScreenDisplay.cs @@ -97,16 +97,16 @@ namespace osu.Game.Overlays } /// - /// Displays the given as parameter on the OSD + /// Displays the given as parameter on the OSD /// /// - public void Display(OsdToast toast) + public void Display(Toast toast) { box.Child = toast; DisplayTemporarily(box); } - private void displayTrackedSettingChange(SettingDescription description) => Schedule(() => Display(new OsdTrackedSettingToast(description))); + private void displayTrackedSettingChange(SettingDescription description) => Schedule(() => Display(new TrackedSettingToast(description))); private TransformSequence fadeIn; private ScheduledDelegate fadeOut; From dd13e2508ae854c483b60afcfa8aadd994393bc5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Jul 2019 15:14:57 +0900 Subject: [PATCH 1715/2854] Add note about toast height --- osu.Game/Overlays/OSD/Toast.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/OSD/Toast.cs b/osu.Game/Overlays/OSD/Toast.cs index 6634959bca..3ca010c5f4 100644 --- a/osu.Game/Overlays/OSD/Toast.cs +++ b/osu.Game/Overlays/OSD/Toast.cs @@ -18,6 +18,8 @@ namespace osu.Game.Overlays.OSD Anchor = Anchor.Centre; Origin = Anchor.Centre; Width = 240; + + // A toast's height is decided (and transformed) by the containing OnScreenDisplay. RelativeSizeAxes = Axes.Y; InternalChildren = new Drawable[] From 2c62891c4836ceb5b24957331dd6aa615ca8f85e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Jul 2019 15:15:34 +0900 Subject: [PATCH 1716/2854] Make Toast base class abstract --- .../Visual/UserInterface/TestSceneOnScreenDisplay.cs | 6 +++++- osu.Game/Overlays/OSD/Toast.cs | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs index 59a35591bd..ca14822205 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.UserInterface osd.BeginTracking(this, config); Add(osd); - AddStep("Display empty osd toast", () => osd.Display(new Toast())); + AddStep("Display empty osd toast", () => osd.Display(new EmptyToast())); AddRepeatStep("Change toggle (no bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingNoKeybind), 2); AddRepeatStep("Change toggle (with bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingWithKeybind), 2); AddRepeatStep("Change enum (no bind)", () => config.IncrementEnumSetting(TestConfigSetting.EnumSettingNoKeybind), 3); @@ -88,6 +88,10 @@ namespace osu.Game.Tests.Visual.UserInterface Setting4 } + private class EmptyToast : Toast + { + } + private class TestOnScreenDisplay : OnScreenDisplay { protected override void DisplayTemporarily(Drawable toDisplay) => toDisplay.FadeIn().ResizeHeightTo(110); diff --git a/osu.Game/Overlays/OSD/Toast.cs b/osu.Game/Overlays/OSD/Toast.cs index 3ca010c5f4..c6e3227cd1 100644 --- a/osu.Game/Overlays/OSD/Toast.cs +++ b/osu.Game/Overlays/OSD/Toast.cs @@ -8,12 +8,12 @@ using osuTK.Graphics; namespace osu.Game.Overlays.OSD { - public class Toast : Container + public abstract class Toast : Container { private readonly Container content; protected override Container Content => content; - public Toast() + protected Toast() { Anchor = Anchor.Centre; Origin = Anchor.Centre; From b6e15fb79186fc94379f2993694b8474af35c076 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Jul 2019 22:21:37 +0900 Subject: [PATCH 1717/2854] Update framework --- osu.Android.props | 2 +- osu.Game/Input/Bindings/GlobalActionContainer.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 5ee0573c58..98f9bf1a42 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 14d356f889..669fd62e45 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -39,7 +39,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.F4, GlobalAction.ToggleMute), new KeyBinding(InputKey.Escape, GlobalAction.Back), - new KeyBinding(InputKey.MouseButton1, GlobalAction.Back), + new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back), new KeyBinding(InputKey.Space, GlobalAction.Select), new KeyBinding(InputKey.Enter, GlobalAction.Select), diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e872cd1387..436ba90a88 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index a319094cb1..c24349bcb5 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From cd7c03c13a0519c069b531b4c7c127539b85871f Mon Sep 17 00:00:00 2001 From: StanR Date: Thu, 11 Jul 2019 16:44:48 +0300 Subject: [PATCH 1718/2854] Add genre and language sections to beatmapset overlay --- .../Online/TestSceneBeatmapSetOverlay.cs | 2 ++ osu.Game/Beatmaps/BeatmapSetOnlineGenre.cs | 20 ++++++++++++++++++ osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs | 10 +++++++++ osu.Game/Beatmaps/BeatmapSetOnlineLanguage.cs | 21 +++++++++++++++++++ .../API/Requests/Responses/APIBeatmapSet.cs | 8 +++++++ osu.Game/Overlays/BeatmapSet/Info.cs | 21 ++++++++++++++++++- 6 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Beatmaps/BeatmapSetOnlineGenre.cs create mode 100644 osu.Game/Beatmaps/BeatmapSetOnlineLanguage.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index daee419b52..d87d9910c3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -173,6 +173,8 @@ namespace osu.Game.Tests.Visual.Online HasVideo = true, HasStoryboard = true, Covers = new BeatmapSetOnlineCovers(), + Language = BeatmapSetOnlineLanguage.English, + Genre = BeatmapSetOnlineGenre.Rock, }, Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, Beatmaps = new List diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineGenre.cs b/osu.Game/Beatmaps/BeatmapSetOnlineGenre.cs new file mode 100644 index 0000000000..cea9d94987 --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapSetOnlineGenre.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Beatmaps +{ + public enum BeatmapSetOnlineGenre + { + Any = 0, + Unspecified = 1, + VideoGame = 2, + Anime = 3, + Rock = 4, + Pop = 5, + Other = 6, + Novelty = 7, + // genre_id 8 doesnt exist + HipHop = 9, + Electronic = 10 + } +} diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index ea3f0b61b9..2d2f06a57c 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -70,6 +70,16 @@ namespace osu.Game.Beatmaps /// The availability of this beatmap set. /// public BeatmapSetOnlineAvailability Availability { get; set; } + + /// + /// Beatmap set's song genre. + /// + public BeatmapSetOnlineGenre Genre { get; set; } + + /// + /// Beatmap set's song language. + /// + public BeatmapSetOnlineLanguage Language { get; set; } } public class BeatmapSetOnlineCovers diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineLanguage.cs b/osu.Game/Beatmaps/BeatmapSetOnlineLanguage.cs new file mode 100644 index 0000000000..0fb0dec904 --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapSetOnlineLanguage.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Beatmaps +{ + public enum BeatmapSetOnlineLanguage + { + Any = 0, + Other = 1, + English = 2, + Japanese = 3, + Chinese = 4, + Instrumental = 5, + Korean = 6, + French = 7, + German = 8, + Swedish = 9, + Spanish = 10, + Italian = 11 + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 200a705500..9ca2cb0b81 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -66,6 +66,12 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"availability")] private BeatmapSetOnlineAvailability availability { get; set; } + [JsonProperty(@"genre_id")] + private BeatmapSetOnlineGenre genre { get; set; } + + [JsonProperty(@"language_id")] + private BeatmapSetOnlineLanguage language { get; set; } + [JsonProperty(@"beatmaps")] private IEnumerable beatmaps { get; set; } @@ -91,6 +97,8 @@ namespace osu.Game.Online.API.Requests.Responses Ranked = ranked, LastUpdated = lastUpdated, Availability = availability, + Genre = genre, + Language = language }, Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(), }; diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index 44827f0a0c..27ca58fbd3 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.BeatmapSet public Info() { - MetadataSection source, tags; + MetadataSection source, tags, genre, language; RelativeSizeAxes = Axes.X; Height = 220; Masking = true; @@ -88,6 +88,19 @@ namespace osu.Game.Overlays.BeatmapSet Children = new[] { source = new MetadataSection("Source"), + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Width = 0.5f, + FillMode = FillMode.Fit, + Children = new Drawable[] + { + genre = new MetadataSection("Genre"), + language = new MetadataSection("Language"), + } + }, tags = new MetadataSection("Tags"), }, }, @@ -119,6 +132,12 @@ namespace osu.Game.Overlays.BeatmapSet { source.Text = b.NewValue?.Metadata.Source ?? string.Empty; tags.Text = b.NewValue?.Metadata.Tags ?? string.Empty; + + var genreId = b.NewValue?.OnlineInfo.Genre ?? BeatmapSetOnlineGenre.Unspecified; + genre.Text = genreId.ToString(); + + var languageId = b.NewValue?.OnlineInfo.Language ?? BeatmapSetOnlineLanguage.Other; + language.Text = languageId.ToString(); }; } From 1e04fcc6b54d8dab5d24478794ad27478cf38b80 Mon Sep 17 00:00:00 2001 From: StanR Date: Thu, 11 Jul 2019 17:47:09 +0300 Subject: [PATCH 1719/2854] Apply fixes --- osu.Game/Beatmaps/BeatmapSetOnlineGenre.cs | 20 ------- osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs | 56 ++++++++++++++++++- osu.Game/Beatmaps/BeatmapSetOnlineLanguage.cs | 21 ------- osu.Game/Overlays/BeatmapSet/Info.cs | 9 +-- 4 files changed, 57 insertions(+), 49 deletions(-) delete mode 100644 osu.Game/Beatmaps/BeatmapSetOnlineGenre.cs delete mode 100644 osu.Game/Beatmaps/BeatmapSetOnlineLanguage.cs diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineGenre.cs b/osu.Game/Beatmaps/BeatmapSetOnlineGenre.cs deleted file mode 100644 index cea9d94987..0000000000 --- a/osu.Game/Beatmaps/BeatmapSetOnlineGenre.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Beatmaps -{ - public enum BeatmapSetOnlineGenre - { - Any = 0, - Unspecified = 1, - VideoGame = 2, - Anime = 3, - Rock = 4, - Pop = 5, - Other = 6, - Novelty = 7, - // genre_id 8 doesnt exist - HipHop = 9, - Electronic = 10 - } -} diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index 2d2f06a57c..4cb50b59fb 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.ComponentModel; using Newtonsoft.Json; namespace osu.Game.Beatmaps @@ -72,16 +73,67 @@ namespace osu.Game.Beatmaps public BeatmapSetOnlineAvailability Availability { get; set; } /// - /// Beatmap set's song genre. + /// The song genre of this beatmap set. /// public BeatmapSetOnlineGenre Genre { get; set; } /// - /// Beatmap set's song language. + /// The song language of this beatmap set. /// public BeatmapSetOnlineLanguage Language { get; set; } } + public enum BeatmapSetOnlineGenre + { + [Description("Any")] + Any = 0, + + [Description("Unspecified")] + Unspecified = 1, + + [Description("Video Game")] + VideoGame = 2, + + [Description("Anime")] + Anime = 3, + + [Description("Rock")] + Rock = 4, + + [Description("Pop")] + Pop = 5, + + [Description("Other")] + Other = 6, + + [Description("Novelty")] + Novelty = 7, + + // genre_id 8 doesn't exist + + [Description("Hip-Hop")] + HipHop = 9, + + [Description("Electronic")] + Electronic = 10 + } + + public enum BeatmapSetOnlineLanguage + { + Any, + Other, + English, + Japanese, + Chinese, + Instrumental, + Korean, + French, + German, + Swedish, + Spanish, + Italian + } + public class BeatmapSetOnlineCovers { public string CoverLowRes { get; set; } diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineLanguage.cs b/osu.Game/Beatmaps/BeatmapSetOnlineLanguage.cs deleted file mode 100644 index 0fb0dec904..0000000000 --- a/osu.Game/Beatmaps/BeatmapSetOnlineLanguage.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Beatmaps -{ - public enum BeatmapSetOnlineLanguage - { - Any = 0, - Other = 1, - English = 2, - Japanese = 3, - Chinese = 4, - Instrumental = 5, - Korean = 6, - French = 7, - German = 8, - Swedish = 9, - Spanish = 10, - Italian = 11 - } -} diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index 27ca58fbd3..0e4e9db948 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -132,12 +133,8 @@ namespace osu.Game.Overlays.BeatmapSet { source.Text = b.NewValue?.Metadata.Source ?? string.Empty; tags.Text = b.NewValue?.Metadata.Tags ?? string.Empty; - - var genreId = b.NewValue?.OnlineInfo.Genre ?? BeatmapSetOnlineGenre.Unspecified; - genre.Text = genreId.ToString(); - - var languageId = b.NewValue?.OnlineInfo.Language ?? BeatmapSetOnlineLanguage.Other; - language.Text = languageId.ToString(); + genre.Text = (b.NewValue?.OnlineInfo.Genre ?? BeatmapSetOnlineGenre.Unspecified).GetDescription(); + language.Text = (b.NewValue?.OnlineInfo.Language ?? BeatmapSetOnlineLanguage.Other).ToString(); }; } From 0d9f978857a869f433bd6733d3640a0fb387cdcd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Jul 2019 11:38:15 +0900 Subject: [PATCH 1720/2854] Don't expose DimContainer --- .../TestSceneBackgroundScreenBeatmap.cs | 6 ++--- .../Containers/DimmableBackgroundContainer.cs | 10 ++++---- .../Containers/DimmableStoryboardContainer.cs | 6 +---- .../Graphics/Containers/UserDimContainer.cs | 24 +++++++++---------- 4 files changed, 21 insertions(+), 25 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs index 09aaee4adc..6a4145b24f 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs @@ -405,7 +405,7 @@ namespace osu.Game.Tests.Visual.Background private class TestDimmableStoryboardContainer : DimmableStoryboardContainer { - public float CurrentAlpha => DimContainer.Alpha; + public float CurrentAlpha => Content.Alpha; public TestDimmableStoryboardContainer() : base(new Storyboard()) @@ -415,8 +415,8 @@ namespace osu.Game.Tests.Visual.Background private class TestDimmableBackgroundContainer : DimmableBackgroundContainer { - public Color4 CurrentColour => DimContainer.Colour; - public float CurrentAlpha => DimContainer.Alpha; + public Color4 CurrentColour => Content.Colour; + public float CurrentAlpha => Content.Alpha; } } } diff --git a/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs b/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs index 7339548069..32b333528f 100644 --- a/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs +++ b/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs @@ -55,14 +55,16 @@ namespace osu.Game.Graphics.Containers private void load(OsuConfigManager config) { userBlurLevel = config.GetBindable(OsuSetting.BlurLevel); - BlurAmount.ValueChanged += _ => UpdateVisuals(); userBlurLevel.ValueChanged += _ => UpdateVisuals(); + BlurAmount.ValueChanged += _ => UpdateVisuals(); } - protected override void ApplyFade() + protected override bool ShowDimContent => !ShowStoryboard.Value || !StoryboardReplacesBackground.Value; // The background needs to be hidden in the case of it being replaced by the storyboard + + protected override void UpdateVisuals() { - // The background needs to be hidden in the case of it being replaced by the storyboard - DimContainer.FadeTo(ShowStoryboard.Value && StoryboardReplacesBackground.Value ? 0 : 1, BACKGROUND_FADE_DURATION, Easing.OutQuint); + base.UpdateVisuals(); + Background?.BlurTo(blurTarget, BACKGROUND_FADE_DURATION, Easing.OutQuint); } } diff --git a/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs b/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs index 16379baeab..0d87e64447 100644 --- a/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs +++ b/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Game.Storyboards; using osu.Game.Storyboards.Drawables; @@ -37,10 +36,7 @@ namespace osu.Game.Graphics.Containers base.LoadComplete(); } - protected override void ApplyFade() - { - DimContainer.FadeTo(!ShowStoryboard.Value || UserDimLevel.Value == 1 ? 0 : 1, BACKGROUND_FADE_DURATION, Easing.OutQuint); - } + protected override bool ShowDimContent => ShowStoryboard.Value && UserDimLevel.Value < 1; private void initializeStoryboard(bool async) { diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index e6a040fcd8..93af0be923 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -31,16 +31,16 @@ namespace osu.Game.Graphics.Containers protected Bindable ShowStoryboard { get; private set; } - protected Container DimContainer { get; } + protected override Container Content => dimContent; - protected override Container Content => DimContainer; + private Container dimContent { get; } /// /// Creates a new . /// protected UserDimContainer() { - AddInternal(DimContainer = new Container { RelativeSizeAxes = Axes.Both }); + AddInternal(dimContent = new Container { RelativeSizeAxes = Axes.Both }); } [BackgroundDependencyLoader] @@ -62,19 +62,17 @@ namespace osu.Game.Graphics.Containers } /// - /// Apply non-dim related settings to the content, such as hiding and blurring. + /// Whether the content of this container should currently be visible. /// - /// - /// While both backgrounds and storyboards allow user dim levels to be applied, storyboards can be toggled via - /// and can cause backgrounds to become hidden via . Storyboards are also currently unable to be blurred. - /// - protected abstract void ApplyFade(); + protected virtual bool ShowDimContent => true; - protected void UpdateVisuals() + /// + /// Should be invoked when any dependent dim level or user setting is changed and bring the visual state up-to-date. + /// + protected virtual void UpdateVisuals() { - ApplyFade(); - - DimContainer.FadeColour(EnableUserDim.Value ? OsuColour.Gray(1 - (float)UserDimLevel.Value) : Color4.White, BACKGROUND_FADE_DURATION, Easing.OutQuint); + dimContent.FadeTo(ShowDimContent ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint); + dimContent.FadeColour(EnableUserDim.Value ? OsuColour.Gray(1 - (float)UserDimLevel.Value) : Color4.White, BACKGROUND_FADE_DURATION, Easing.OutQuint); } } } From b5ca7faca4dad93ea1bb0c31a3426f2e74433d67 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Jul 2019 11:40:32 +0900 Subject: [PATCH 1721/2854] Move default value for EnableUserDim to base class --- osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs | 4 ---- osu.Game/Graphics/Containers/UserDimContainer.cs | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs b/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs index 0d87e64447..d0ef7a2c25 100644 --- a/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs +++ b/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs @@ -18,10 +18,6 @@ namespace osu.Game.Graphics.Containers public DimmableStoryboardContainer(Storyboard storyboard) { this.storyboard = storyboard; - - // Storyboards current do not get used in scenarios without user dim, so default to enabled here. - EnableUserDim.Default = true; - EnableUserDim.Value = true; } [BackgroundDependencyLoader] diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index 93af0be923..f5958a092b 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -20,7 +20,7 @@ namespace osu.Game.Graphics.Containers /// /// Whether or not user-configured dim levels should be applied to the container. /// - public readonly Bindable EnableUserDim = new Bindable(); + public readonly Bindable EnableUserDim = new Bindable(true); /// /// Whether or not the storyboard loaded should completely hide the background behind it. From 46f7bb885bbd445c851c4259663d3260d2ada4e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Jul 2019 11:50:06 +0900 Subject: [PATCH 1722/2854] Move classes to local namespaces Also renames test scene to more appropriate name. --- ...eatmap.cs => TestSceneUserDimContainer.cs} | 26 +++---- .../Containers/DimmableBackgroundContainer.cs | 71 ----------------- .../Backgrounds/BackgroundScreenBeatmap.cs | 76 +++++++++++++++++-- .../Play/DimmableStoryboard.cs} | 7 +- osu.Game/Screens/Play/Player.cs | 8 +- 5 files changed, 90 insertions(+), 98 deletions(-) rename osu.Game.Tests/Visual/Background/{TestSceneBackgroundScreenBeatmap.cs => TestSceneUserDimContainer.cs} (92%) delete mode 100644 osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs rename osu.Game/{Graphics/Containers/DimmableStoryboardContainer.cs => Screens/Play/DimmableStoryboard.cs} (89%) diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs similarity index 92% rename from osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs rename to osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index 6a4145b24f..5578fee42d 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -38,7 +38,7 @@ using osuTK.Graphics; namespace osu.Game.Tests.Visual.Background { [TestFixture] - public class TestSceneBackgroundScreenBeatmap : ManualInputManagerTestScene + public class TestSceneUserDimContainer : ManualInputManagerTestScene { public override IReadOnlyList RequiredTypes => new[] { @@ -243,7 +243,7 @@ namespace osu.Game.Tests.Visual.Background { player.StoryboardEnabled.Value = false; player.ReplacesBackground.Value = false; - player.CurrentDimmableStoryboardContainer.Add(new OsuSpriteText + player.DimmableStoryboard.Add(new OsuSpriteText { Size = new Vector2(500, 50), Alpha = 1, @@ -336,9 +336,9 @@ namespace osu.Game.Tests.Visual.Background { protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); - protected override DimmableStoryboardContainer CreateStoryboardContainer(Storyboard storyboard) => new TestDimmableStoryboardContainer { RelativeSizeAxes = Axes.Both }; + protected override DimmableStoryboard CreateStoryboardContainer(Storyboard storyboard) => new TestDimmableStoryboard { RelativeSizeAxes = Axes.Both }; - public TestDimmableStoryboardContainer CurrentDimmableStoryboardContainer => (TestDimmableStoryboardContainer)DimmableStoryboardContainer; + public new TestDimmableStoryboard DimmableStoryboard => (TestDimmableStoryboard)base.DimmableStoryboard; // Whether or not the player should be allowed to load. public bool BlockLoad; @@ -352,9 +352,9 @@ namespace osu.Game.Tests.Visual.Background { } - public bool IsStoryboardVisible() => CurrentDimmableStoryboardContainer.CurrentAlpha == 1; + public bool IsStoryboardVisible() => DimmableStoryboard.CurrentAlpha == 1; - public bool IsStoryboardInvisible() => CurrentDimmableStoryboardContainer.CurrentAlpha <= 1; + public bool IsStoryboardInvisible() => DimmableStoryboard.CurrentAlpha <= 1; [BackgroundDependencyLoader] private void load(OsuConfigManager config, CancellationToken token) @@ -387,15 +387,15 @@ namespace osu.Game.Tests.Visual.Background private class FadeAccessibleBackground : BackgroundScreenBeatmap { - protected override DimmableBackgroundContainer CreateFadeContainer() => fadeContainer = new TestDimmableBackgroundContainer { RelativeSizeAxes = Axes.Both }; + protected override DimmableBackground CreateFadeContainer() => dimmable = new TestDimmableBackground { RelativeSizeAxes = Axes.Both }; - public Color4 CurrentColour => fadeContainer.CurrentColour; + public Color4 CurrentColour => dimmable.CurrentColour; - public float CurrentAlpha => fadeContainer.CurrentAlpha; + public float CurrentAlpha => dimmable.CurrentAlpha; public Vector2 CurrentBlur => Background.BlurSigma; - private TestDimmableBackgroundContainer fadeContainer; + private TestDimmableBackground dimmable; public FadeAccessibleBackground(WorkingBeatmap beatmap) : base(beatmap) @@ -403,17 +403,17 @@ namespace osu.Game.Tests.Visual.Background } } - private class TestDimmableStoryboardContainer : DimmableStoryboardContainer + private class TestDimmableStoryboard : DimmableStoryboard { public float CurrentAlpha => Content.Alpha; - public TestDimmableStoryboardContainer() + public TestDimmableStoryboard() : base(new Storyboard()) { } } - private class TestDimmableBackgroundContainer : DimmableBackgroundContainer + private class TestDimmableBackground : BackgroundScreenBeatmap.DimmableBackground { public Color4 CurrentColour => Content.Colour; public float CurrentAlpha => Content.Alpha; diff --git a/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs b/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs deleted file mode 100644 index 32b333528f..0000000000 --- a/osu.Game/Graphics/Containers/DimmableBackgroundContainer.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Game.Configuration; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Screens.Play; -using osuTK; - -namespace osu.Game.Graphics.Containers -{ - public class DimmableBackgroundContainer : UserDimContainer - { - /// - /// The amount of blur to be applied to the background in addition to user-specified blur. - /// - /// - /// Used in contexts where there can potentially be both user and screen-specified blurring occuring at the same time, such as in - /// - public readonly Bindable BlurAmount = new Bindable(); - - public Background Background - { - get => background; - set - { - base.Add(background = value); - background.BlurTo(blurTarget, 0, Easing.OutQuint); - } - } - - private Bindable userBlurLevel { get; set; } - - private Background background; - - public override void Add(Drawable drawable) - { - if (drawable is Background) - throw new InvalidOperationException($"Use {nameof(Background)} to set a background."); - - base.Add(drawable); - } - - /// - /// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs. - /// - private Vector2 blurTarget => EnableUserDim.Value - ? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * 25) - : new Vector2(BlurAmount.Value); - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - userBlurLevel = config.GetBindable(OsuSetting.BlurLevel); - userBlurLevel.ValueChanged += _ => UpdateVisuals(); - BlurAmount.ValueChanged += _ => UpdateVisuals(); - } - - protected override bool ShowDimContent => !ShowStoryboard.Value || !StoryboardReplacesBackground.Value; // The background needs to be hidden in the case of it being replaced by the storyboard - - protected override void UpdateVisuals() - { - base.UpdateVisuals(); - - Background?.BlurTo(blurTarget, BACKGROUND_FADE_DURATION, Easing.OutQuint); - } - } -} diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 97f77f789a..8d6caf4c71 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -1,14 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; +using osu.Game.Screens.Play; +using osuTK; namespace osu.Game.Screens.Backgrounds { @@ -30,16 +34,17 @@ namespace osu.Game.Screens.Backgrounds /// public readonly Bindable BlurAmount = new Bindable(); - private readonly DimmableBackgroundContainer fadeContainer; + private readonly DimmableBackground dimmable; - protected virtual DimmableBackgroundContainer CreateFadeContainer() => new DimmableBackgroundContainer { RelativeSizeAxes = Axes.Both }; + protected virtual DimmableBackground CreateFadeContainer() => new DimmableBackground { RelativeSizeAxes = Axes.Both }; public BackgroundScreenBeatmap(WorkingBeatmap beatmap = null) { Beatmap = beatmap; - InternalChild = fadeContainer = CreateFadeContainer(); - fadeContainer.EnableUserDim.BindTo(EnableUserDim); - fadeContainer.BlurAmount.BindTo(BlurAmount); + + InternalChild = dimmable = CreateFadeContainer(); + dimmable.EnableUserDim.BindTo(EnableUserDim); + dimmable.BlurAmount.BindTo(BlurAmount); } [BackgroundDependencyLoader] @@ -86,8 +91,8 @@ namespace osu.Game.Screens.Backgrounds } b.Depth = newDepth; - fadeContainer.Background = Background = b; - StoryboardReplacesBackground.BindTo(fadeContainer.StoryboardReplacesBackground); + dimmable.Background = Background = b; + StoryboardReplacesBackground.BindTo(dimmable.StoryboardReplacesBackground); } public override bool Equals(BackgroundScreen other) @@ -112,5 +117,62 @@ namespace osu.Game.Screens.Backgrounds Sprite.Texture = Beatmap?.Background ?? textures.Get(@"Backgrounds/bg1"); } } + + public class DimmableBackground : UserDimContainer + { + /// + /// The amount of blur to be applied to the background in addition to user-specified blur. + /// + /// + /// Used in contexts where there can potentially be both user and screen-specified blurring occuring at the same time, such as in + /// + public readonly Bindable BlurAmount = new Bindable(); + + public Background Background + { + get => background; + set + { + base.Add(background = value); + background.BlurTo(blurTarget, 0, Easing.OutQuint); + } + } + + private Bindable userBlurLevel { get; set; } + + private Background background; + + public override void Add(Drawable drawable) + { + if (drawable is Background) + throw new InvalidOperationException($"Use {nameof(Background)} to set a background."); + + base.Add(drawable); + } + + /// + /// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs. + /// + private Vector2 blurTarget => EnableUserDim.Value + ? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * 25) + : new Vector2(BlurAmount.Value); + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + userBlurLevel = config.GetBindable(OsuSetting.BlurLevel); + userBlurLevel.ValueChanged += _ => UpdateVisuals(); + BlurAmount.ValueChanged += _ => UpdateVisuals(); + } + + protected override bool ShowDimContent => !ShowStoryboard.Value || !StoryboardReplacesBackground.Value; // The background needs to be hidden in the case of it being replaced by the storyboard + + protected override void UpdateVisuals() + { + base.UpdateVisuals(); + + Background?.BlurTo(blurTarget, BACKGROUND_FADE_DURATION, Easing.OutQuint); + } + } } } diff --git a/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs similarity index 89% rename from osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs rename to osu.Game/Screens/Play/DimmableStoryboard.cs index d0ef7a2c25..45dff039b6 100644 --- a/osu.Game/Graphics/Containers/DimmableStoryboardContainer.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.cs @@ -2,20 +2,21 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Game.Graphics.Containers; using osu.Game.Storyboards; using osu.Game.Storyboards.Drawables; -namespace osu.Game.Graphics.Containers +namespace osu.Game.Screens.Play { /// /// A container that handles loading, as well as applies user-specified visual settings to it. /// - public class DimmableStoryboardContainer : UserDimContainer + public class DimmableStoryboard : UserDimContainer { private readonly Storyboard storyboard; private DrawableStoryboard drawableStoryboard; - public DimmableStoryboardContainer(Storyboard storyboard) + public DimmableStoryboard(Storyboard storyboard) { this.storyboard = storyboard; } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 55e759d215..0396d85d75 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -76,9 +76,9 @@ namespace osu.Game.Screens.Play protected GameplayClockContainer GameplayClockContainer { get; private set; } - protected DimmableStoryboardContainer DimmableStoryboardContainer { get; private set; } + protected DimmableStoryboard DimmableStoryboard { get; private set; } - protected virtual DimmableStoryboardContainer CreateStoryboardContainer(Storyboard storyboard) => new DimmableStoryboardContainer(storyboard) { RelativeSizeAxes = Axes.Both }; + protected virtual DimmableStoryboard CreateStoryboardContainer(Storyboard storyboard) => new DimmableStoryboard(storyboard) { RelativeSizeAxes = Axes.Both }; [Cached] [Cached(Type = typeof(IBindable>))] @@ -124,7 +124,7 @@ namespace osu.Game.Screens.Play GameplayClockContainer.Children = new[] { - DimmableStoryboardContainer = CreateStoryboardContainer(Beatmap.Value.Storyboard), + DimmableStoryboard = CreateStoryboardContainer(Beatmap.Value.Storyboard), new ScalingContainer(ScalingMode.Gameplay) { Child = new LocalSkinOverrideContainer(working.Skin) @@ -455,7 +455,7 @@ namespace osu.Game.Screens.Play Background.BlurAmount.Value = 0; Background.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); - DimmableStoryboardContainer.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); + DimmableStoryboard.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); storyboardReplacesBackground.Value = Beatmap.Value.Storyboard.ReplacesBackground && Beatmap.Value.Storyboard.HasDrawable; From 8b67f88d161283548df4ff732d7f2e33fefa528c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Jul 2019 12:04:45 +0900 Subject: [PATCH 1723/2854] Don't expose dimmable container creation in player --- .../Background/TestSceneUserDimContainer.cs | 23 ++++--------------- .../Graphics/Containers/UserDimContainer.cs | 9 +++++++- osu.Game/Screens/Play/Player.cs | 5 +--- 3 files changed, 13 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index 5578fee42d..f0893abadc 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -29,7 +29,6 @@ using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Play; using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Screens.Select; -using osu.Game.Storyboards; using osu.Game.Tests.Resources; using osu.Game.Users; using osuTK; @@ -139,14 +138,14 @@ namespace osu.Game.Tests.Visual.Background player.StoryboardEnabled.Value = true; }); waitForDim(); - AddAssert("Background is invisible, storyboard is visible", () => songSelect.IsBackgroundInvisible() && player.IsStoryboardVisible()); + AddAssert("Background is invisible, storyboard is visible", () => songSelect.IsBackgroundInvisible() && player.IsStoryboardVisible); AddStep("Storyboard Disabled", () => { player.ReplacesBackground.Value = false; player.StoryboardEnabled.Value = false; }); waitForDim(); - AddAssert("Background is visible, storyboard is invisible", () => songSelect.IsBackgroundVisible() && player.IsStoryboardInvisible()); + AddAssert("Background is visible, storyboard is invisible", () => songSelect.IsBackgroundVisible() && !player.IsStoryboardVisible); } /// @@ -336,9 +335,7 @@ namespace osu.Game.Tests.Visual.Background { protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); - protected override DimmableStoryboard CreateStoryboardContainer(Storyboard storyboard) => new TestDimmableStoryboard { RelativeSizeAxes = Axes.Both }; - - public new TestDimmableStoryboard DimmableStoryboard => (TestDimmableStoryboard)base.DimmableStoryboard; + public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard; // Whether or not the player should be allowed to load. public bool BlockLoad; @@ -352,9 +349,7 @@ namespace osu.Game.Tests.Visual.Background { } - public bool IsStoryboardVisible() => DimmableStoryboard.CurrentAlpha == 1; - - public bool IsStoryboardInvisible() => DimmableStoryboard.CurrentAlpha <= 1; + public bool IsStoryboardVisible => DimmableStoryboard.ContentDisplayed; [BackgroundDependencyLoader] private void load(OsuConfigManager config, CancellationToken token) @@ -403,16 +398,6 @@ namespace osu.Game.Tests.Visual.Background } } - private class TestDimmableStoryboard : DimmableStoryboard - { - public float CurrentAlpha => Content.Alpha; - - public TestDimmableStoryboard() - : base(new Storyboard()) - { - } - } - private class TestDimmableBackground : BackgroundScreenBeatmap.DimmableBackground { public Color4 CurrentColour => Content.Colour; diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index f5958a092b..03de5f651f 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -27,6 +27,11 @@ namespace osu.Game.Graphics.Containers /// public readonly Bindable StoryboardReplacesBackground = new Bindable(); + /// + /// Whether the content of this container is currently being displayed. + /// + public bool ContentDisplayed { get; private set; } + protected Bindable UserDimLevel { get; private set; } protected Bindable ShowStoryboard { get; private set; } @@ -71,7 +76,9 @@ namespace osu.Game.Graphics.Containers /// protected virtual void UpdateVisuals() { - dimContent.FadeTo(ShowDimContent ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint); + ContentDisplayed = ShowDimContent; + + dimContent.FadeTo((ContentDisplayed) ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint); dimContent.FadeColour(EnableUserDim.Value ? OsuColour.Gray(1 - (float)UserDimLevel.Value) : Color4.White, BACKGROUND_FADE_DURATION, Easing.OutQuint); } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0396d85d75..8dc16af575 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -26,7 +26,6 @@ using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Skinning; -using osu.Game.Storyboards; using osu.Game.Users; namespace osu.Game.Screens.Play @@ -78,8 +77,6 @@ namespace osu.Game.Screens.Play protected DimmableStoryboard DimmableStoryboard { get; private set; } - protected virtual DimmableStoryboard CreateStoryboardContainer(Storyboard storyboard) => new DimmableStoryboard(storyboard) { RelativeSizeAxes = Axes.Both }; - [Cached] [Cached(Type = typeof(IBindable>))] protected new readonly Bindable> Mods = new Bindable>(Array.Empty()); @@ -124,7 +121,7 @@ namespace osu.Game.Screens.Play GameplayClockContainer.Children = new[] { - DimmableStoryboard = CreateStoryboardContainer(Beatmap.Value.Storyboard), + DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both }, new ScalingContainer(ScalingMode.Gameplay) { Child = new LocalSkinOverrideContainer(working.Skin) From 671f7f99cd139bf255a3d7e393d9e8ac211517df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Jul 2019 13:44:43 +0900 Subject: [PATCH 1724/2854] Use constant for blur amount --- .../Visual/Background/TestSceneUserDimContainer.cs | 2 +- osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index f0893abadc..dc4ceed59e 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -302,7 +302,7 @@ namespace osu.Game.Tests.Visual.Background public bool IsBackgroundUndimmed() => ((FadeAccessibleBackground)Background).CurrentColour == Color4.White; - public bool IsUserBlurApplied() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2((float)BlurLevel.Value * 25); + public bool IsUserBlurApplied() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR); public bool IsUserBlurDisabled() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(0); diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 8d6caf4c71..7b4feb1819 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -18,6 +18,11 @@ namespace osu.Game.Screens.Backgrounds { public class BackgroundScreenBeatmap : BackgroundScreen { + /// + /// The amount of blur to apply when full user blur is requested. + /// + public const float USER_BLUR_FACTOR = 25; + protected Background Background; private WorkingBeatmap beatmap; @@ -154,7 +159,7 @@ namespace osu.Game.Screens.Backgrounds /// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs. /// private Vector2 blurTarget => EnableUserDim.Value - ? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * 25) + ? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * USER_BLUR_FACTOR) : new Vector2(BlurAmount.Value); [BackgroundDependencyLoader] From 44855d8bb2203af41b8c57c7f56b1f12172c208e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Jul 2019 13:45:34 +0900 Subject: [PATCH 1725/2854] Ensure any existing background is expired if set more than once --- osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 7b4feb1819..08f1881038 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -138,6 +138,8 @@ namespace osu.Game.Screens.Backgrounds get => background; set { + background?.Expire(); + base.Add(background = value); background.BlurTo(blurTarget, 0, Easing.OutQuint); } From 09d679de8d6367ec6894e470318d3cda625d5fd3 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 12 Jul 2019 08:50:53 +0200 Subject: [PATCH 1726/2854] Move text display layout to Toast. --- .../UserInterface/TestSceneOnScreenDisplay.cs | 4 ++ osu.Game/Overlays/OSD/Toast.cs | 35 +++++++++++++++- osu.Game/Overlays/OSD/TrackedSettingToast.cs | 41 +++---------------- 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs index ca14822205..4decfc7dd6 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs @@ -90,6 +90,10 @@ namespace osu.Game.Tests.Visual.UserInterface private class EmptyToast : Toast { + public EmptyToast() + : base("", "", "") + { + } } private class TestOnScreenDisplay : OnScreenDisplay diff --git a/osu.Game/Overlays/OSD/Toast.cs b/osu.Game/Overlays/OSD/Toast.cs index c6e3227cd1..6572830d10 100644 --- a/osu.Game/Overlays/OSD/Toast.cs +++ b/osu.Game/Overlays/OSD/Toast.cs @@ -4,6 +4,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osuTK; using osuTK.Graphics; namespace osu.Game.Overlays.OSD @@ -13,7 +16,7 @@ namespace osu.Game.Overlays.OSD private readonly Container content; protected override Container Content => content; - protected Toast() + protected Toast(string description, string value, string keybinding) { Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -35,7 +38,35 @@ namespace osu.Game.Overlays.OSD Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - } + }, + new OsuSpriteText + { + Padding = new MarginPadding(10), + Name = "Description", + Font = OsuFont.GetFont(size: 14, weight: FontWeight.Black), + Spacing = new Vector2(1, 0), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = description.ToUpperInvariant() + }, + new OsuSpriteText + { + Font = OsuFont.GetFont(size: 24, weight: FontWeight.Light), + Padding = new MarginPadding { Left = 10, Right = 10 }, + Name = "Value", + Anchor = Anchor.Centre, + Origin = Anchor.BottomCentre, + Text = value + }, + new OsuSpriteText + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Name = "Shortcut", + Margin = new MarginPadding { Bottom = 15 }, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), + Text = string.IsNullOrEmpty(keybinding) ? "NO KEY BOUND" : keybinding.ToUpperInvariant() + }, }; } } diff --git a/osu.Game/Overlays/OSD/TrackedSettingToast.cs b/osu.Game/Overlays/OSD/TrackedSettingToast.cs index 8def8476f0..454ba84d70 100644 --- a/osu.Game/Overlays/OSD/TrackedSettingToast.cs +++ b/osu.Game/Overlays/OSD/TrackedSettingToast.cs @@ -9,9 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; @@ -20,55 +18,29 @@ namespace osu.Game.Overlays.OSD public class TrackedSettingToast : Toast { public TrackedSettingToast(SettingDescription description) + : base(description.Name, description.Value, description.Shortcut) { - SpriteText textLine2; FillFlowContainer optionLights; Children = new Drawable[] { - new OsuSpriteText - { - Padding = new MarginPadding(10), - Font = OsuFont.GetFont(size: 14, weight: FontWeight.Black), - Spacing = new Vector2(1, 0), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = description.Name.ToUpperInvariant() - }, - textLine2 = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 24, weight: FontWeight.Light), - Padding = new MarginPadding { Left = 10, Right = 10 }, - Anchor = Anchor.Centre, - Origin = Anchor.BottomCentre, - Text = description.Value - }, new FillFlowContainer { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, Direction = FillDirection.Vertical, + Margin = new MarginPadding { Top = 70 }, Children = new Drawable[] { optionLights = new FillFlowContainer { - Padding = new MarginPadding { Top = 20, Bottom = 5 }, + Padding = new MarginPadding { Bottom = 5 }, Spacing = new Vector2(5, 0), Direction = FillDirection.Horizontal, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, AutoSizeAxes = Axes.Both }, - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Margin = new MarginPadding { Bottom = 15 }, - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), - Alpha = 0.3f, - Text = string.IsNullOrEmpty(description.Shortcut) ? "NO KEY BOUND" : description.Shortcut.ToUpperInvariant() - }, } } }; @@ -90,9 +62,6 @@ namespace osu.Game.Overlays.OSD break; } - textLine2.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre; - textLine2.Y = optionCount > 0 ? 0 : 5; - for (int i = 0; i < optionCount; i++) { optionLights.Add(new OptionLight From b24a6e64bdf994fd729bf4bd1e99d4161460ad9f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Jul 2019 16:22:02 +0900 Subject: [PATCH 1727/2854] Add link to bounty history to readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2c330e403c..35dc010d93 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ Before starting, please make sure you are familiar with the [development and tes Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. We welcome all feedback so we can make contributing to this project as pain-free as possible. -For those interested, we love to reward quality contributions via bounties, paid out via paypal or osu! supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project. +For those interested, we love to reward quality contributions via [bounties](https://docs.google.com/spreadsheets/d/1jNXfj_S3Pb5PErA-czDdC9DUu4IgUbe1Lt8E7CYUJuE/view?&rm=minimal#gid=523803337), paid out via paypal or osu! supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project. ## Licence From 376d228add3a4fa34a0d16fbd2af635fd20a994d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Jul 2019 17:49:59 +0900 Subject: [PATCH 1728/2854] Use new logo style for icons / readme --- README.md | 2 +- assets/lazer.png | Bin 39498 -> 77579 bytes osu.Desktop/lazer.ico | Bin 93005 -> 86650 bytes .../AppIcon.appiconset/Contents.json | 250 +----------------- .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 1237 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 3602 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 6457 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 2250 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 6069 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 10842 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 3602 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 9677 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 16681 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 16681 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 28381 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 8946 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 22553 -> 0 bytes .../Icon-App-83.5x83.5@2x.png | Bin 25862 -> 0 bytes .../AppIcon.appiconset/ItunesArtwork@2x.png | Bin 251219 -> 0 bytes .../AppIcon.appiconset/iOSAppStore.png | Bin 0 -> 350640 bytes .../AppIcon.appiconset/iPadApp1x.png | Bin 0 -> 10106 bytes .../AppIcon.appiconset/iPadApp2x.png | Bin 0 -> 24966 bytes .../AppIcon.appiconset/iPadNotification1x.png | Bin 0 -> 2227 bytes .../AppIcon.appiconset/iPadNotification2x.png | Bin 0 -> 4485 bytes .../AppIcon.appiconset/iPadProApp2x.png | Bin 0 -> 28089 bytes .../AppIcon.appiconset/iPadSettings1x.png | Bin 0 -> 3192 bytes .../AppIcon.appiconset/iPadSettings2x.png | Bin 0 -> 7156 bytes .../AppIcon.appiconset/iPadSpotlight1x.png | Bin 0 -> 4485 bytes .../AppIcon.appiconset/iPadSpotlight2x.png | Bin 0 -> 10768 bytes .../AppIcon.appiconset/iPhoneApp2x.png | Bin 0 -> 18204 bytes .../AppIcon.appiconset/iPhoneApp3x.png | Bin 0 -> 30946 bytes .../iPhoneNotification2x.png | Bin 0 -> 4485 bytes .../iPhoneNotification3x.png | Bin 0 -> 7458 bytes .../AppIcon.appiconset/iPhoneSettings2x.png | Bin 0 -> 7156 bytes .../AppIcon.appiconset/iPhoneSettings3x.png | Bin 0 -> 12085 bytes .../AppIcon.appiconset/iPhoneSpotlight2x.png | Bin 0 -> 10768 bytes .../AppIcon.appiconset/iPhoneSpotlight3x.png | Bin 0 -> 18204 bytes osu.iOS/iTunesArtwork | Bin 99849 -> 142214 bytes osu.iOS/iTunesArtwork@2x | Bin 251219 -> 350640 bytes osu.iOS/osu.iOS.csproj | 41 +-- 40 files changed, 24 insertions(+), 269 deletions(-) mode change 100644 => 100755 osu.Desktop/lazer.ico delete mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png delete mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png delete mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png delete mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png delete mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png delete mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png delete mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png delete mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png delete mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png delete mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png delete mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png delete mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png delete mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png delete mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png delete mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iOSAppStore.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadApp1x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadApp2x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadNotification1x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadNotification2x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadProApp2x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadSettings1x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadSettings2x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadSpotlight1x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadSpotlight2x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneApp2x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneApp3x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneNotification2x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneNotification3x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneSettings2x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneSettings3x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneSpotlight2x.png create mode 100644 osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneSpotlight3x.png diff --git a/README.md b/README.md index 35dc010d93..590442f01b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

# osu! diff --git a/assets/lazer.png b/assets/lazer.png index 075a8e7184ad508cedd8fe9ae549d8cea696b51a..19b5fc2151e570bf321596998437072943422c26 100644 GIT binary patch literal 77579 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±JV~w(ThIDK>wg};C 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 X{r?ZpP_kcw2Ot$CjR&O)ra}J$c_$^Y diff --git a/osu.Desktop/lazer.ico b/osu.Desktop/lazer.ico old mode 100644 new mode 100755 index 0c894dca412abb8566e630dbc82397e4c79395c9..a6aa8abb9f3f10a2aac07c1168d9a2adb25ce1ea GIT binary patch literal 86650 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

@r7l@_>dLwGP1inIvgp>RYo9-;{?Vr` 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 V!WImv>?mfrbAj*puHX3W{{h`h-3$N# diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/osu.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json index bbd5f4f3ba..af4b103867 100644 --- a/osu.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/osu.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,249 +1 @@ -{ - "images": [ - { - "filename": "Icon-App-20x20@2x.png", - "size": "20x20", - "scale": "2x", - "idiom": "iphone" - }, - { - "filename": "Icon-App-20x20@3x.png", - "size": "20x20", - "scale": "3x", - "idiom": "iphone" - }, - { - "filename": "Icon-App-29x29@2x.png", - "size": "29x29", - "scale": "2x", - "idiom": "iphone" - }, - { - "filename": "Icon-App-29x29@3x.png", - "size": "29x29", - "scale": "3x", - "idiom": "iphone" - }, - { - "filename": "Icon-App-40x40@2x.png", - "size": "40x40", - "scale": "2x", - "idiom": "iphone" - }, - { - "filename": "Icon-App-40x40@3x.png", - "size": "40x40", - "scale": "3x", - "idiom": "iphone" - }, - { - "filename": "Icon-App-60x60@2x.png", - "size": "60x60", - "scale": "2x", - "idiom": "iphone" - }, - { - "filename": "Icon-App-60x60@3x.png", - "size": "60x60", - "scale": "3x", - "idiom": "iphone" - }, - { - "filename": "Icon-App-20x20@1x.png", - "size": "20x20", - "scale": "1x", - "idiom": "ipad" - }, - { - "filename": "Icon-App-20x20@2x.png", - "size": "20x20", - "scale": "2x", - "idiom": "ipad" - }, - { - "filename": "Icon-App-29x29@1x.png", - "size": "29x29", - "scale": "1x", - "idiom": "ipad" - }, - { - "filename": "Icon-App-29x29@2x.png", - "size": "29x29", - "scale": "2x", - "idiom": "ipad" - }, - { - "filename": "Icon-App-40x40@1x.png", - "size": "40x40", - "scale": "1x", - "idiom": "ipad" - }, - { - "filename": "Icon-App-40x40@2x.png", - "size": "40x40", - "scale": "2x", - "idiom": "ipad" - }, - { - "filename": "Icon-App-83.5x83.5@2x.png", - "size": "83.5x83.5", - "scale": "2x", - "idiom": "ipad" - }, - { - "filename": "Icon-App-76x76@1x.png", - "size": "76x76", - "scale": "1x", - "idiom": "ipad" - }, - { - "filename": "Icon-App-76x76@2x.png", - "size": "76x76", - "scale": "2x", - "idiom": "ipad" - }, - { - "filename": "ItunesArtwork@2x.png", - "size": "1024x1024", - "scale": "1x", - "idiom": "ios-marketing" - }, - { - "size": "60x60", - "scale": "2x", - "idiom": "car" - }, - { - "size": "60x60", - "scale": "3x", - "idiom": "car" - }, - { - "role": "notificationCenter", - "size": "24x24", - "subtype": "38mm", - "scale": "2x", - "idiom": "watch" - }, - { - "role": "notificationCenter", - "size": "27.5x27.5", - "subtype": "42mm", - "scale": "2x", - "idiom": "watch" - }, - { - "role": "companionSettings", - "size": "29x29", - "scale": "2x", - "idiom": "watch" - }, - { - "role": "companionSettings", - "size": "29x29", - "scale": "3x", - "idiom": "watch" - }, - { - "role": "appLauncher", - "size": "40x40", - "subtype": "38mm", - "scale": "2x", - "idiom": "watch" - }, - { - "role": "appLauncher", - "size": "44x44", - "subtype": "40mm", - "scale": "2x", - "idiom": "watch" - }, - { - "role": "appLauncher", - "size": "50x50", - "subtype": "44mm", - "scale": "2x", - "idiom": "watch" - }, - { - "role": "quickLook", - "size": "86x86", - "subtype": "38mm", - "scale": "2x", - "idiom": "watch" - }, - { - "role": "quickLook", - "size": "98x98", - "subtype": "42mm", - "scale": "2x", - "idiom": "watch" - }, - { - "role": "quickLook", - "size": "108x108", - "subtype": "44mm", - "scale": "2x", - "idiom": "watch" - }, - { - "size": "1024x1024", - "scale": "1x", - "idiom": "watch-marketing" - }, - { - "size": "16x16", - "scale": "1x", - "idiom": "mac" - }, - { - "size": "16x16", - "scale": "2x", - "idiom": "mac" - }, - { - "size": "32x32", - "scale": "1x", - "idiom": "mac" - }, - { - "size": "32x32", - "scale": "2x", - "idiom": "mac" - }, - { - "size": "128x128", - "scale": "1x", - "idiom": "mac" - }, - { - "size": "128x128", - "scale": "2x", - "idiom": "mac" - }, - { - "size": "256x256", - "scale": "1x", - "idiom": "mac" - }, - { - "size": "256x256", - "scale": "2x", - "idiom": "mac" - }, - { - "size": "512x512", - "scale": "1x", - "idiom": "mac" - }, - { - "size": "512x512", - "scale": "2x", - "idiom": "mac" - } - ], - "info": { - "version": 1, - "author": "xcode" - } -} \ No newline at end of file +{"images":[{"size":"20x20","idiom":"iphone","filename":"iPhoneNotification2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"iPhoneNotification3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"iPhoneSettings2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"iPhoneSettings3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"iPhoneSpotlight2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"iPhoneSpotlight3x.png","scale":"3x"},{"size":"60x60","idiom":"iphone","filename":"iPhoneApp2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"iPhoneApp3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"iPadNotification1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"iPadNotification2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"iPadSettings1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"iPadSettings2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"iPadSpotlight1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"iPadSpotlight2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"iPadApp1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"iPadApp2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"iPadProApp2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"iOSAppStore.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}} \ No newline at end of file diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index dfcce1297b8ad9b84ca073334fb12d949c51d250..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1237 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 Y2YK{MGIEg$@&Et;07*qoM6N<$g67-*fB*mh diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index 8a21d9f81d2a2c0f6b8658761476e32ad36565f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6457 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 T58IaJ00000NkvXXu0mjfwzQG) diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 4477ece73a546a35bbb5abc8fc20c1645683e607..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2250 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 diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png deleted file mode 100644 index 0f8c36a03870c9e7c0561fe0ca1e80d9fb7446a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10842 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 diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png deleted file mode 100644 index d563e8cf5e44bfb5c1dd733564da90f9379defdb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3602 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 Y2YK{MGIEg$@&Et;07*qoM6N<$g67-*fB*mh diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index 0208f80e3bc60c86225146f78a3497db63738b23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9677 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 P00000NkvXXu0mjfGd9th diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index f7ad8bb6bbbf3adf4610e6d613db4057bc46eb45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16681 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 BAs_$% diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index f7ad8bb6bbbf3adf4610e6d613db4057bc46eb45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16681 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 BAs_$% diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 3d85a133aa71f1e744c51c96acfda1d19f9edfb6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28381 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 diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index edd53bc9542a7dfcb242f917db19752046a120df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8946 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 q1s6C9c^_cU&A_fDRS@U>S36fzJQ* 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 diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index edff3788f08b1e096269fd187f08fdf65594dab1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25862 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 BVm<%> diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png deleted file mode 100644 index c04f6f3ec4965f7b06e29a1bda5b8d5fe27ba72c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 251219 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 ihBCBv)8__?8cC&XLrk0D^;$L%D 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�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 diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/iOSAppStore.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/iOSAppStore.png new file mode 100644 index 0000000000000000000000000000000000000000..0e8bb029bc964d313c07fea891a1fcfa1b10e139 GIT binary patch literal 350640 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 z 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

?+l7 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�Kv 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 z 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 VRQY{WNd^D_002ovPDHLkV1gW!zV844 literal 0 HcmV?d00001 diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadNotification1x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadNotification1x.png new file mode 100644 index 0000000000000000000000000000000000000000..8c483a0a7a92ab5a46e6cfcfa49f8fdf5ddb173b GIT binary patch literal 2227 zcmV;k2u$~hP)Hg1+lHrgWSWcKdPn90sKGrRqvPeo9CG3uKX#J{(IASm?@+di}}l?o-=)F3E6 zwD^Ni=!>T7nL9I?X}YoAW$t|Qo$sD|?zw001?ah|SeB6#0T!CBEf+H4bBB+JJu8re zhoBb*p;u8ID_yBf0ya+zcePvJL&AGs+11_tpRKn>9TgyPA7ZoSs0)aX0r00)%XR^J z`jH<$>RKN5V(7OqK*TS4xZz{h!*f1C3ECFkK$#7nA@pGN!$;%jYv zwjAKwmYb0gKL(K8-kPtb5${A?tlI~wzMrJ6wTdBr=Y%%%EaEMQ&o}4FQ^DA)s*}Z> z!FI&AHCpoWI|RUqx?7s@$8!5^Q=anY%X@i5{QA6kNcMelpE>R6eCYFpmMsVT zrI(b06~u#xf1yS}_UGdMvD``!0~u->P=lA4?YN`hilQ z|3tHka)7T{2CGqwjZfMwx$5irQN_*|e4l)UHmiYuz74Yp1t^#>hrJ3-SOXDcC_o0^ z7T9R1gAN8V6s;5)ieI5-7aQlmJn}lUna#nz!j%5V$X|o`xX!dHWQRV27P1=rj;t2b zW$~+pTw@bIek?ZvKPDL<64`^#UNTAck#RBsB6*5DP4<%UA_FqU$I>2EH_cM;u)Q~SI+rg`Rn{L z_AC5qq~L$#SMj%U$6Cz0vP{G5Y*=%5RT^yu;}-DInZ=349rJPVM6C3K^oO)8y(fJr{l>k`ead~!ea?NsT>_Ci%bnxC;Vy6= zb6>{xYV#Ue-+LB$7`JEXmTRm^AtP)R9u{)KHsMiWGV&)32xCG~*nyU<>-!d;FP=Re z4r3qYr~6#KE>;1F`>_J_P5xC?ROxV(DIHdCO*p$HRQI@7^PwV@Pvuf+ z5K}u-6REM(K@W$srgorh0{i?O)v0c>QtHxU-hBdD(>iYJ4b2sIOVX2K8m~4gmYVA5 zh^QEb$V`rCQ-|7ZS{nuL-t>?3n=-o(6I(7vocj#GzCZEo`!3>+v;dYIfPu#&ZWzzX z2i^rZ^Mu;6+rb@?NPG+6)c5T6zxpzGe*M(x+{AON=PiJ>H#?ob-|uwRK0yDg0B4PV z0id6JRRdfL?*ISMZUrf*k6*ChPpT+|w%)~?&LZ-nb17Y3hFmq)L#-QuEuI;_O zzdYwVMi9SEyY}95Kj(ZtpL5PcPfu@12SNxudH9;=dGLK-UFP3>q#+@7`M-E|lkan2 zys@ zorE0Ap?WEYT45b7G0=ntUDA7d4Dr5w(B4hq`>|1+J^LjBdXArGR(fk|>o5blu7^*C zhH&QS2^2s53C@*y$^wvzu%Z^E89^XI1a%LXdjKQ5g5HyFA|iE6{544lg_2B(W|JOA z`VM1o;60Q+y9jS_6`mpB2fnJBvxh-IMri7xNZdkrw}jw}-*CF?b@cbXjDWM#(Kwj( z`S625gcoiAOC@Ngp(xZnR|)U>9z3FD5G8)ssDP>-z+NpwpIyP3L+`*eO$DiOU?!bb z22?N4K@bIsMM%^_a#nbx4X83o>eOsiv0bSm=uSZI>x46V2dTz3?0c?*l6sIn^;(Jm zH_ z3cR%qB%7KMl6D%2MNLb#`vEp=qC%iSp+X=o{8AMgKl}l?zZJ5j5#BvAB4&`&F0kPs zBu0ibsGTAVqmIUoEZqCu5ogS#E@u@E> z>r`o56Rxe@#&rH~NCxCGTmty!yK^v(zX*NTGtfwIp%#Up&7vb>utu93iTlm6O!%au zo@j*3wBg&!7nGsA7xZK@In2TTEG)p--hjiWjv|eGe5%Z?o1@8a=002ovPDHLkV1i)_ BI!^!q literal 0 HcmV?d00001 diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadNotification2x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadNotification2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a45b01b91c65e3a8d8feefe5122bef85c77a78fe GIT binary patch literal 4485 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 X5L}ax?^OsQ00000NkvXXu0mjf-pQ8F literal 0 HcmV?d00001 diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadProApp2x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadProApp2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d2ba8f3a7ee97837b87d619cb0f7c1f3243d7591 GIT binary patch literal 28089 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 Y0}pcCLt@!TD*ylh07*qoM6N<$f{85HlmGw# literal 0 HcmV?d00001 diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadSettings1x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadSettings1x.png new file mode 100644 index 0000000000000000000000000000000000000000..43d577040eeca37d88ca15030a9994678c44f14a GIT binary patch literal 3192 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 X5L}ax?^OsQ00000NkvXXu0mjf-pQ8F literal 0 HcmV?d00001 diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadSpotlight2x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPadSpotlight2x.png new file mode 100644 index 0000000000000000000000000000000000000000..717603dd684e9a71f55243678b954a70b89f0a13 GIT binary patch literal 10768 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

rD-NlRi2>YR3M-xKh z9c4Kh`=aD%&%Ndl%vr+n}RYro+f%j>+#f{sNr+KALrZ~C8bYLjxa@X>BCtfCeq)hRhhs? z-~Ze8kq>^v>i6|nL7#UsKJXM&7f7IQ(oXjL*|Yt8=K*~{qg%%f_dd1Bk3Ou|TxhF~ z2R;?RLiDUj3Ce<@%1B4+NMiZeWYZ-&PXE^PtU-h`2#bZxI%d(;2}@#BvKTk7g^9y+ z+U;{+_?&&}Z$B#>X-AnK$A@;mOJC9E1dwPl($bZjwyd%li%*|(_C-NEjWm9lkd#unpyJ7=zK)B?Fr-tiZ9*AMTJ!78ttRC177RC3a{L)=iQ ziNBvx0*C4bl^qiqLU&7=r|EkGzy7Q5w)gz{do+<;#bVEI`ukM=gjLIj!l=b9RwR4E%ntJ5Nj4jgyMV-8EAae!#FgIqyHd;Ln%J#0GK3n2Lw@H zPOxDKl7?(WCIgPBKjzp3S;`I}e)5!*Pkzh*=?iYABQ3gyyY0?zf6Ko34}WhPHg5Fw z%Pf+kp9L2IAED0;S)Ite8lKa6O&Eb8T<~F|dU&Q1G%5_S2m`Pnd6&2*6C;dSET#$1 zSiZvE^~>+Hx4h*oHhbD>a&4-gdu@LrK_DAM$HY!DEFoMEs5|;&JZ3CH;4UU`=SfKC zeXQz&M3W~MV_BZ5B6*|pQNW2oEqIP|o-hK3X-0@iC1^yb3=IoJrf|Z;l@L0SW+c0>FCp}T zdGqbfx7}vH@C$FV^UlI zm&P;iCWOEgL;*C6DMuuk`Cb4*QYqY>G3OE<-UoGqt2C%@^62f=7dF*@tz5a%ZWFPu zx#k*Mdio-{Je5-YqG|Yq)5JdkAfro#C4_?Lz*xlNy;nhh{LXXfH|dd5so$LXW1Fkt z5MSYz=HI-1tNrwW2kh><@3!Zjd(I9YIqYq1(Q+fS71{|zC+$s$OHpy#c|H*ccqV`F z9r6U1xH&Og(|Dc`0z-_@$52=x7!^QrA#iw~ZrBK+QQILcgdQ1Q^(j3cyO=72?ELf3 zvsJ6Ew5zVVQr7n}ecwsQhu}yxYr{B2R(+|TMTPGhwHs6~1Oy-;#22FY>;T6y<~UQD z)B89wP^SmUf$jw5sNM~mHrk_)K5CCW`k1}2<^|igZ@*7=z|eT1KsfCHxvY9*+|RqD zDCaW&eX@TL9&ieAacAei|!XHzExT4OpkF`aOI1+H0FO*{kc<+nP0NY~9**wr9^CKeYf=$UJQzCddS- zTrf2T6`6zhKwxs8C}5*m!a$|tCQE`v{z&&JBQOYu2q1k#MN0zIScHaE4{6m=t7&t@u~Y|Q<0DTDG|KIji;*;3ty(+W(J|SkPoM4=Gc;4COtJP!?WH|E$)b3s2>W__ z{iO6GI&1mxp+k01*Wts5?TAj;?a?Rg1T{nTY5 zCkz6vLia^E{00JZHF9E}97$meJU|`*_sVO8=!8RL z`X1CI4W!}D7`)SSvfQ-P&6YGY5}eeIsZmLw(lV=rM6w2pIo@P}d3;a$(?lI7e)fP& z+8Pi|dy(W5T|+ezrb%0a=gSP%fKLzBFD~X zK|*L2AB;#ijT;)O(?!3Bkt2nx#uB%#o&GV=BA!9UpM(z+0tG_F_k_@}LmY>-QpxEy zw2zB2xv)7-6>+IPngJ#Pz%u}oSHts!5g4S!SOts>9 z!U!B1L!$#{g|Lad2LvW}2$vuf1cP*xaft`Y#2&x=K-?r|D1)a!^sGFHmz0_H_SA;e zisuO@@TqN9I`>M#ByW>2BsnHjI%WF#c;cqh{=fYE|2cuHqxRn`!K$}7`_2Ca1L!4r T1Z_6R00000NkvXXu0mjfx&MOR literal 0 HcmV?d00001 diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneApp3x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneApp3x.png new file mode 100644 index 0000000000000000000000000000000000000000..78ef8d12b73234c4a0d842db591fddf8646b5efa GIT binary patch literal 30946 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 Va~q+uY%u@;002ovPDHLkV1i*114sY> literal 0 HcmV?d00001 diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneNotification2x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneNotification2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a45b01b91c65e3a8d8feefe5122bef85c77a78fe GIT binary patch literal 4485 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 X5L}ax?^OsQ00000NkvXXu0mjf-pQ8F literal 0 HcmV?d00001 diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneNotification3x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneNotification3x.png new file mode 100644 index 0000000000000000000000000000000000000000..46ddf1179de6fe5a5d0ea2907a1b2823ad0d14b5 GIT binary patch literal 7458 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

#ey>)?|3_`yyL|7a`H`_C`|$RPW{X#!{8F#Ruy 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 literal 0 HcmV?d00001 diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneSettings2x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneSettings2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1ebec1390b7d9dd55bda6e79f655d2ddbf5114dd GIT binary patch literal 7156 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 literal 0 HcmV?d00001 diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneSpotlight2x.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/iPhoneSpotlight2x.png new file mode 100644 index 0000000000000000000000000000000000000000..717603dd684e9a71f55243678b954a70b89f0a13 GIT binary patch literal 10768 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

rD-NlRi2>YR3M-xKh z9c4Kh`=aD%&%Ndl%vr+n}RYro+f%j>+#f{sNr+KALrZ~C8bYLjxa@X>BCtfCeq)hRhhs? z-~Ze8kq>^v>i6|nL7#UsKJXM&7f7IQ(oXjL*|Yt8=K*~{qg%%f_dd1Bk3Ou|TxhF~ z2R;?RLiDUj3Ce<@%1B4+NMiZeWYZ-&PXE^PtU-h`2#bZxI%d(;2}@#BvKTk7g^9y+ z+U;{+_?&&}Z$B#>X-AnK$A@;mOJC9E1dwPl($bZjwyd%li%*|(_C-NEjWm9lkd#unpyJ7=zK)B?Fr-tiZ9*AMTJ!78ttRC177RC3a{L)=iQ ziNBvx0*C4bl^qiqLU&7=r|EkGzy7Q5w)gz{do+<;#bVEI`ukM=gjLIj!l=b9RwR4E%ntJ5Nj4jgyMV-8EAae!#FgIqyHd;Ln%J#0GK3n2Lw@H zPOxDKl7?(WCIgPBKjzp3S;`I}e)5!*Pkzh*=?iYABQ3gyyY0?zf6Ko34}WhPHg5Fw z%Pf+kp9L2IAED0;S)Ite8lKa6O&Eb8T<~F|dU&Q1G%5_S2m`Pnd6&2*6C;dSET#$1 zSiZvE^~>+Hx4h*oHhbD>a&4-gdu@LrK_DAM$HY!DEFoMEs5|;&JZ3CH;4UU`=SfKC zeXQz&M3W~MV_BZ5B6*|pQNW2oEqIP|o-hK3X-0@iC1^yb3=IoJrf|Z;l@L0SW+c0>FCp}T zdGqbfx7}vH@C$FV^UlI zm&P;iCWOEgL;*C6DMuuk`Cb4*QYqY>G3OE<-UoGqt2C%@^62f=7dF*@tz5a%ZWFPu zx#k*Mdio-{Je5-YqG|Yq)5JdkAfro#C4_?Lz*xlNy;nhh{LXXfH|dd5so$LXW1Fkt z5MSYz=HI-1tNrwW2kh><@3!Zjd(I9YIqYq1(Q+fS71{|zC+$s$OHpy#c|H*ccqV`F z9r6U1xH&Og(|Dc`0z-_@$52=x7!^QrA#iw~ZrBK+QQILcgdQ1Q^(j3cyO=72?ELf3 zvsJ6Ew5zVVQr7n}ecwsQhu}yxYr{B2R(+|TMTPGhwHs6~1Oy-;#22FY>;T6y<~UQD z)B89wP^SmUf$jw5sNM~mHrk_)K5CCW`k1}2<^|igZ@*7=z|eT1KsfCHxvY9*+|RqD zDCaW&eX@TL9&ieAacAei|!XHzExT4OpkF`aOI1+H0FO*{kc<+nP0NY~9**wr9^CKeYf=$UJQzCddS- zTrf2T6`6zhKwxs8C}5*m!a$|tCQE`v{z&&JBQOYu2q1k#MN0zIScHaE4{6m=t7&t@u~Y|Q<0DTDG|KIji;*;3ty(+W(J|SkPoM4=Gc;4COtJP!?WH|E$)b3s2>W__ z{iO6GI&1mxp+k01*Wts5?TAj;?a?Rg1T{nTY5 zCkz6vLia^E{00JZHF9E}97$meJU|`*_sVO8=!8RL z`X1CI4W!}D7`)SSvfQ-P&6YGY5}eeIsZmLw(lV=rM6w2pIo@P}d3;a$(?lI7e)fP& z+8Pi|dy(W5T|+ezrb%0a=gSP%fKLzBFD~X zK|*L2AB;#ijT;)O(?!3Bkt2nx#uB%#o&GV=BA!9UpM(z+0tG_F_k_@}LmY>-QpxEy zw2zB2xv)7-6>+IPngJ#Pz%u}oSHts!5g4S!SOts>9 z!U!B1L!$#{g|Lad2LvW}2$vuf1cP*xaft`Y#2&x=K-?r|D1)a!^sGFHmz0_H_SA;e zisuO@@TqN9I`>M#ByW>2BsnHjI%WF#c;cqh{=fYE|2cuHqxRn`!K$}7`_2Ca1L!4r T1Z_6R00000NkvXXu0mjfx&MOR literal 0 HcmV?d00001 diff --git a/osu.iOS/iTunesArtwork b/osu.iOS/iTunesArtwork index ef7441433ad414ffc56fd513e5956d279aef8551..1939459992eba68e1067c5af904fa25624cef86d 100644 GIT binary patch literal 142214 zcmY(q1ymeCw=F!t0E5op?jg9l4^9Y9aEIU?2s+H*kN|-McS|5R1PJc#9)eqNcZZ+v z-uv!*f3G#Gt55Gb(o?JYbe&zFG}IJvFv&3i007P#B{?ks0Qsef3;?0MoGyH(?*ITG znZ2y6hNG;atgDmj2W>YCD{Dn-7wZr9R$7WO0Dy2zjE;#Nr4FHFlZP$Ct5Izy{xHv< zQaFq_@w)uEHI0a+B|(>@H-hu@JMXWKMpFhxh54EubJgWFU_5*2gxzg*fwat*;04JV zu0%}6`Dt2a$(oxGjh(21{Zui&RQtxNW$Po@dFFAJ|51bOG3ri3v3}B5LL}m}at;#S zb&rVHNC~#M(1;uov|K$+DmIfM6=XqmEbm&Q71>UIv$>h2r|hyi1u7z1cN)v;t)Ezt zu}CL{IzwJaes^DKr1lJ{uPk@!c%a=}e)Vfsvt1sN@ECiWf9&g1k*GS_4w1=_*x{ZV z1?+pC%0Ff~st)_bJDYiy=^vMJs@#q(p0b@g?845Sy9&orvn4~1EsXEk1Q@s1G`rsx zf6G>Q0IfSZ2mGd7T}Vp*iYJ8@x44{EQGj9vYgpe$ zNCp%CB7gW&N<3wKS`ub#MIZ%d7ebbLO-7497cyqb`{wGaJ1-Kw?`Lt1sLtI_ z|LK7h;7J3i+Z7ik;s&mqxC*7osCN#xx@2lGa-|m`7ZEk=EldH-kG(A;0fPY}0iyvO zCdgmKhB7x=*Erp1Jj{Bp9L~uNSNw^6N$YHPlN?Kx=@^`dsEI#x3a)8d@SZVYgREpU z{{|(M?cWn;b7e_T;absb72X@Sa_S;|y01dMP<_(TdAIA9z(S?#sw&(nO%VM`_I-4M z;JfR3z4Ki9MDEzAC}6R;RBIuG;Q+ec+GNyNlf(rP~~VwL)T< zZ7au~e>wP1X4~mOb5jn>-c4^z;IJC@Bjd?2>$a!0;FY-0su$`_V4sP^;K0w ztz4ZsE#A9YT66k1yS<r!iU4fgWZ7$<6h@x?j4A|3@mS{=we*#qxjjOY(^S z7xMon`yV^vT>mlt|2XsCk^YzTB~(dFajyS;Y?7ENolFJ*02J^>PWqh>(m{)N!cyPI zZG(y+mz=Kz(!l_XK!XIOukR`otsP%!;U|{8ANc*s=*p+GYO~fU*6Fz1d-Mo#-ce0O6<_?P!hBjB*Qd7x@`aIZ)7ViP+F&9?CSn%aEpki+~T0O0j@MMx>NF@%zo6vU=i4w2T^1clE?&(yh+?O$A8411>VkOQh>I}x?+RAEw) zQXAQlv6#{;f|Wn@yC81HDQRhGRQH#@NXoez$g`FYLf8ky^j`Xa$nLCB1;EeQ340Sd zT;^atdQxRp`bw>C#1Fg>epC0h8d^vg^v<&w!#FYx3H!Z+A8RQDiImdV020hNd3oV` zcxq#iNE=$4KDI1)@xNHy9|==^%`raVHznYt`XE?uL00XCZ^qk zz33GIv2toC=CjT+Dg!M9FEWq-@oZ{*PlF8djE~nAAVcs!j?SFX2SGhN399CcjBK&> zQFO~G*+V-e58*V=+S*f98y_y6U=PXFr&4843O5w|G2F=}r+@n(ZgdcGQx5(wSff~& z#y`>e)SCAr0o)v_&X$&w!*Kf_J9gi1{cI#aQr!#=&mLhBMJ#6-xU#Y(n4Q_kzhjSg zD?WX~h`*b12*y%6)vUJq;@bLLgcY*-GD%~i@{nYuDhJY?u*p0_0l`u{JmpnsAlG73 zzKv}0_Dp4JO-RmIL^v9b>s?`wysCpVpkVAqy^qtq12ucH$thwihbuLL^o(Mp-@-Eu5C*NlU2_&b$ zohw-~d^&Y$cZ3bUzWk|^uYYp=`|A*yDuGZI@r>6MR6@#vVt3N$8%*d zy}XZ&$j6CBtWUMALXzZ|p7i50atQ;L7h0Wv^AidZN*{|&ZmeR^m>UXB-p)6Oa=i?e zAuKmiPPbga3&Z0vFI%gtSp3|=Q#SkX@NjGkTI7G~fZTCzdOrJ- zLDP5chupwo9t0Y%T)JZ!bVUJ;M^6bzaw9EF%f5<&wCJ88%U8Pf&IT_^$|M3>d#Y~6 z7oK0KSB5G1bgR4T5s4TCtb5Xb4va+r+kV-h`05SS`P8LA%%i`=Dy}W=RU~onw5-oUVT}PlG3P{QBRZbvZQT4evuP`j`re@z!h6r8C3^&Q>_cK(4{dsE!+Xn*kAX#4! zcc|p63Zpi?E~mRi$aaJU49onk1PJ9WU2aAS(jk z^&xr`U$J7!XA8}^n?z)s>vD2sV z`?j&+n{SUt8&x3m-|jon1y#v}m*m$vsNFk@t-CN2KdNR#hSo5clEi$_y)DAw)_l@B zhqDH|^4srUbQ-l!9}##dKnoi*>G$PH`U8%>i#)pG->;iDQV|cX{NrX1lMt#~8+NJI z+b@~a5j?vaQ!BN8nNm;rv2okSAK&b%msk_+rbPfw)K#C|z%;O1$X~%qgq{@5<45>pQP&2ymnSH)HVtN`f==4<;`#+R=s4pH#p1Sns z*xQ@8hjSM8;$n1ZPsJKy{g@dQ#TUVqvfXgmFRkY zwBw0IzJNl$!_vMRoj^DIcCiynlo00}7nzDz z!)&fvWWs&^YZX}>@^kQRPG>CML5h2!#$8sw4W1dW&}^^$x85Ruy(`eR^CV5cVrXmR z++3f6ebepX<`XwBZyvwX9A)P&Utw2ASA;=w(fwDq)xQ&!Cce0Dg!8!yYF@b+dputZ zU`m{Sd8)K+;iJqA_|x~*mlU_?o94TpAS_d^y;09#KAaO9mg|N($09Qva9A?+c>nGS z?Bo<{1rU)^Y*dw(G>%4Gd1};CBz)C2ih}oa<8#WJ@!Y-pI@H(;VD7gY&nkO#X=1{;BU@KW9@!tZwy7(Yo6ecS!vmMpXbaEB-q{6tH5_vf`lOt1)LEomcz}tmmLW`iMdQOl zDe5WQ%$&PgSLNqi zx%Ac?+sk8k=4c_|NP`Keb&?N?k)f**Kb7AR00#PE zUeAZrETzrtFB6i`BbnniF5#1?9dCXJ1G$IY2s3oQmtye{{!`+-@H_l1XTvMwdMm|x zyKObA6EV)ecwUF|M892@>CYc34?P8=p|l)Qr7Ds3n3hemG#jw#uOyQ{Z8AT2T#oXc z*rdMCTQeB_)WgJq!5fHZS0<4wHC_vO%5L-MgDr~-5R49f3zT0w`1`XIdt+^<%4)4) zI}41COAS)|LXE?9gy1>wKkYeLJRgfFyo3)G#QcIgaj%c^59H_ufp*Z4*+VXnbo&X7 z?Wv;fM`dUy7eUUA9euL4Zw-Wn1~(N0FeNLwn~QtwWB`Fz@4`u`%rFwK6TLH=-hQri z&-U^&EF4YL|5pP6+T6dQ2*v6-{^Q*j=CXNwYkAr!rwZ6MzamJxmh?S)CtTKW#xc2= z>ffee_`v9Y6*|P(!hf*TBxGFq2(!8~vi=pZmK<&IM)_;eN?a;-Pjf;M{DVW<&*8E& zmNV!4U#D3?w$GP}LNy3@r2AHz>qZRDDeH#uW!5=yM-WLkZDZ;=k04TZsD-LkXwdmV z52Nx~nqHfNc_dtp>Ae6HQn~V#;WbG7=^w#b7>Ox&$LcMKDK^s=nyOx7uQ?)%!+C+% zl&k*G9K^6Ky;*$#$5Ra$iPVKLxtLl1cCorCn1mm^7mga}%OJv1vJjY^j(7z#%_hW) zXC?9aQ1Gj_d0!v7Z6@l4Es>Z}dSxKs z^*eb{ov0+n<{k@OX)C5bvU&V!Y#}qL`J~#@i+nrM(zjepLW$%H*2xFUMrr;Rvl0!u zC16yPLIpF2Uhg3T!JR~^I4yIi_oi*CLyF-GXR z^4w&$op+uL?pM@>+A_vbC2zbcA<0}U*Q2jEDufqzn{6rViF;KY%KDcQGG)bQt=&ZecJFtK-^>i--2i36c;?jg% zYJc2z{BaOWQiHtqGK9tRRjJO$({4<6qjih5Byjq-Z1gR=H*LSto^hB}>@^U&ieUu3 zybD~r!_<;A2H4DWH{W)Lh0?G`Vy$9IZGARyK&vK=kB~h6o$YPvlgK|*@_sVn)C_)W zz1Cc|pfW_$ouJ;&?=K*numt1atL5?2Pj=^O>wTs39Nenw{?pdWmAOpH?g*|PZjW-< z(rP5q$fKjl>?YAmoCbZvvfU%X3l0y#2ek+3q_LXMpt7;U zT*F+(c}o6?oH0!r`-R~S0Bg_|1Q(KVdMik9-e#i9()2MQ42(%apsugQJ6?>+9Ii#G?UY5eHeCBTL7^|_tVi_kNEJafAoG9?~O?xG&` zV;jylxeTA^=P6^?{)I~ZGkauaA;3^8(SMR6Mx|V#`r9_^0BeO(M(9xxFU$9s1&2#M z8`UfaEN3P)1z8`*4hw^QW%^0qr|zi`iC=pt+(ndDiy(VL`}Vgr4;=Uh5D;e~^m??2 z!g+A@2MT&;d>U{4bH_a+wm2cR?}cWWoG`VZ>*A~DpqV5!vXp#VI&8qF209m99nBlq z!&r>Y;T!wn7MD|#MG)s}n~ZZd84rk^K7iK9Ydx8!Hu|a!~v^2a@bGsaPAbZ4pptw_C5KecJNpMty_m`tm=C6r}dy zC(gtzM33nN7Ulm@G2ZymlH#8Hy!0IpCV+?@37^=)op!a81m&we+0_v>>CcRr!&+pf zn(AgeEH`VYWA8SeUu-FxoaM_xgGXlw>5Z{7eviv}CvAOaEH6ZE;)3V6n*Vr7XqnKo z#M(yB)K~s*%l>-hVY9QvkCfJ*y1f_jt4#cDPAabnUPb$Dz8%SumY%#D+bZ-;Zeb$7 zp0P8j_3q~Cgn>w5muV$kF(ua{rRD|6PhX6G3BQIw8NR{vsr&a(@=8r5D6dTOHq(+# ziI;y6p}pi3828t0gw%fjnAVo>kFdN~{MCJ2R$9564Jm}m44pdzH!}H{KO4d6SZ&;g zT*4vEfm9S^`}RLGh_RYsumT>|>kFmmYGn%R4x{y(WOGx-f9mv%$MpQ*p#Ll`1Bx7& zb2h6=Qs0_Fj65POPgut7VaMI8Ip@;AWBs#eYegiKtv2P5NygrAat*mXQs53askdPU z-8iSMUJ&02%lOAN;M9C*Ug4g=*gT)=1VAaRK}s|w<@b={HN8Mq9yf5UTH!BS|MJr6 z+N3|(c4VNo!skX=D_Uc=V|#6xgta%~A!l8oyQwT{bzyutf6T`7xv3`@aM2ekq|<&} z^j0BxONXs`ypkTeIP{W!i=}|1Po@nV1^6lsqKxEi})Y@A%Ac*d-{BGmkl?mzapxrjPcRW zv&Dr$?0EvPdTGCJRzPyMO`l89#x^ zzOLD@#-8|)JbB=-dgXlx0-U5Md0V>pQvb6ioDHMXSgXY9t0nd1PLq^E4yD8x7Wk+L z5UuweAT5+q!=p&+dSHqAkTGEnzxq&$-;*sL1f6bX8_oWLwX{RQ&+mqkK~5F3(D&IX zLa6O2W=+-A$I7Q`->fjk_bT*!bYXP4=BqoF>)>k4WNKlnI+>2u9Rv=~5r~$u!uBd7 zMrhKS63#<_%`^>@-d9sJYeBbo-;t%SMjphmr4kf6p=65kB8cE2qfNfb%i!}aN3(lF zDz8xMRWb}D@0VGliD~h}epzp{zM)-b#o)yZ5NGBihN8?q4e5!tc@;=anfUV*%8~x7EoEVId)tp$I|*7UwczoKutEU+d3K79{ zCISLJ6Dw)Z8~xtqGb_JV<_h0H?Ze zRwaAS+Ix1jAO#U~jwwaAQ1kllQXt@KrL%I(Z=i@lCv9xC-Q_Z5@$5fq8X4**>MIZ1 zZfv6=u@aWS?c~l{z<|)xAN=EkChG`0a3}2DG<`F@wwXRgU@JkgSs5fO3?pdP7fCJR zmuUKwMdDLYqUI@_8fZui=w~U%j0{~#71kR*XVY~v^*zs&%>ohGCGJA)<%% zXrX2eI?O$&8TYz-H-G$4GHxD7cltc>DfzD(9m{cqS{{2(9LnZwy<0z~S%t}rximSd znBjHs;QtHRv%+4f4akamDMV_50<}+Gk;WT=&g`XGczzh5%Sfbg=hWmEsz+?shMx4* zJqkYz)jjFphJptjpGrr+{B^>_-(dXVdRv-2&o?~B;b(f?kEH>c8_E|9xxTq|G&U7y zG36G(Vl&?wZlhKm*wzu(i+~y-0}L7=fKxS;Oj*U#jpbAs^1kEUcT;GI=(a_L->BW8 zw8KyG5n|nU=gLy_K<>6|w7V?HXf;_PV{S@37nPzFgU&iN#qOsAqhffX@qI4u}*1f?`HISpsxpbkrES+q6LdXsXILsQT5r+Z1Rgo4b)gr@HD3md1 z<0rV9!p%1khV*li6tgWQ@g$hf|GxVs*1!n70IQ$?Kd0Js@PlVK~ z9!}pnG5q;Ajnv2!sx|agiX?F-t1v%BT|Eo!I0lFoyL1?cG*Ra(t*vUU>TD5&ztyY5 z@;RcSJzq3_bz$XR?PhPb?KM*;ibb^>)id!4rHx(;+UQVQteFOa@&T6WVyHv&?5+sa z1FowDeV0uw?Ze7@o;B~l(#rYmyMl+*z8|>N$E6&ovBeq^LdSETrgl9IkJ(Kj==C%n z$!@nIo_z`f2nRyi6FUw=viIFnZ^6wqQq8xjuDGtxb9G^LkHcg0@P~cAUe|7dS9rM7 z7wd{syag3(O2B!-mx4AIow+s&KCn{BJsZ6G>Lweb(1s`ub?u;^%m7G@QMU~DzZq={ z;94wB1n$2AI?dNld}#mtG85Lgj?^z^2e5LyWgS;CucFt1Qy8}byV@3l&w15;^4eQTPW(qxs9XqH` zWJM-aZ9!c<^b!4P$Uq~w{koN;^*Ns=Ehkz&5P)Mcm`h~}DFeD=Hmss9bp?OH0@&-& zJvmWC+m|%@vKgYJkXPQC^Gk|7Fpm7FH!Frz2C?A=oL#V3JxN^_>qUTDk5T6Ht}iW) zvm}JQ|56s&i**JGw{an-fdwmA3&7{w)WMAzq*8~fvaN?6S(kB=h1Xe%1-`c%2eoOH zSxFN9*-OKcY!MgBe5Ja-5n{9DHQ85bo616Y);eX#cGsZF51xX~y1{_X1L;GSb~v)x zeS~Z1LTz-Ms|(&wHL+xTz{Z8bN7L0nf_lhPHIcN_6`DG;j5po54iaryqY?7Mppek^ zjX;dqB9{rMzM}8Vh%fKhvu^6$%I%eR=xobXVV6tUiqIjtqI4uy&Wjwg#kS8niNvAew#3}T&cCgD%DlOLjO@kj7lQT_$`LOr~Zpzo7uWXXA^SH&klGaw6(51bd zcUj(x3tUavMEG}&0%ba*5R*3~Y1An)kU06k8&KEYH3K?$hriRmOQ`MK`HBFlXq_IBkroV!C$n;Kav{0MxHHWacSqJdX>V_m+StjzX&5^lC zs<&$D)(Yz;m(tre*SMH^4+-7J!N-#8#;a#T?k1mi`RorxhQ4QhATCy{Xho4SlQ8cz z)p8Sv3S6g2qCFXLMX)_QNP5d2Vy;M4{5}u>k;7M}7qIV3ev-<6P z=XBhkZgw}^5-@!B(>;|#L1@u`K~-33c}A)Ay(tP-Hu-8A+B~}%2|>z?3qeZH&h?Pu zDtg@m^TU$RlVbhx(|s4KGtaPKSL@*Gk-T~?+Hq#!3-3i!iRjdZZZ(=_y-W0|q`aQK z`PxJ%BK>J;q3cJoDQf{kd7-QDH5n3L^_J6_4~nMZ!uBASBDzP_{qQ`SnuY9! zKUkw~GZ+0ko_xoqE5n>B3WMWk!@LTWmEKQTeFy0QlM<~2R%sukwf|Av5`?vqWq+Q- zOKzfbo|KWY?#RRUPW*DG;COj=wshRm+MEyO*m02dr1rE#wo91?PEG)V0V2``rp_V_n9?Sgw z1HF?k4q@Km+uJ0}jR;Z#+P`W)?MbeCZ*D80+*?1Q`=h+h8BVge)4h`l6Z8F3GO?7o zfmU#3F9jy<&!5J2?Ebau7IFKPchBrP+0bWAzcx}-+-TDOJkuavaLhEOj3H1?8D-YS zn()W167y2CVfyWGQH`%~g0A~nL8u4@3ZwJ6>Pk}hN3NjU8iifm{iQ}7G^~X>Bx%qH z@mBBHf?E2cq~q0tmv-~ynGv%v<-pXLF7gqEF?oOII_DKlA44{^(!OldumLv#5O`09 zfeMzoQ@Wus;p!OvNK@Hur;dr`!w|Rrl7|s>Ou;YE4#T>bYTrM4m3_47Zlr z5)i-k|nWN2>ZX+_9RY0<=U9K>3M(zy+hD|yc5cRKEbj$!L+!*}TeL0PP zoYq$mD=7kczgS^7ZS+Y#zn#)rV<{_d6tpuCK+ckezO^glC#DH_s5+@LyKg!fd%mk! zJD8i`E|radAY)3?qMEv!VZ9K{G_ZJk{6hlM@8>q|D~BIllN6u(;71H+CkRLVI{WxY z=l6mI9F{U(XuBsNpma1g%q_Y3w#$8@*Bc%+HGRx3F@xk?vqGe#&Z?AJzQaL4GsG^Kj@zi47|h# z3hw-!J*4Pt|9xrEb4$_n*(ANQeMvHKkbtl>BTF*lQIU395qF$O)Rs z;l1YGNB#daFNT!_SnJ(@G~l(qpt6Iem}6LqD?t0~x0cT8fJuA8l*1via(y?sCCHi_hh5Gn>g#ZjM>{ z={E_vEp`vRC*RVf^(PE(cM;F{dV8-6EYsk8w=lU0Qh-ZPT^eHGPl>)7=f3xX{en&@8_Mud-vCRkQwk46yJ zuROp`bM_%z1ijqUT(`WCj?P*O3`8-x6C4NjrecMgYVKh_@IRtpE3>`Q7(m}{Ajtxo zJx8>G5tl2i_zH@Ohn2aOhV4PR#@a7TPAF!KHT*al@xhXpQp{JpS!@4R>i%tZ)d)djroWDPrFCRB7gfuYJ^MN?e{lLaB$W4#_}JtQj{Cw(>tDzaNZX?$Wtv19mA!$^NB{ljGdU~6>pg^mQBzk(c0)^uc*S!~K1bq-1_T)4ET(g0QI zNSA{`R8|ufuI9}%#U!nt%m^1 z8}57C%LOJJvw{b*$8PE28t3Tc!G28M${qFOj*vgD9?z=D3}%2kL9TIwE|Rs_E>4u} z8Nh3%++j+pk7AX&aMWM;*v9odrOo=C?LW4|h)7%nSCA=jVl=QUh%eLR8=ge-*C|Fi0t__m(qzikROps2sM@gx5 z^MPukp)Aa%a#x=HI{Zbf#63xP1?{&|G`kN*xOPj}m`>TlE?O4TlNf@$6s}ZUl`|hok-Vq5? z%2mY^WY@I;`9SD6DJO|{*Htl}#QTiv=210zmk z*XEPkWaQ*zkf{IBvoe<53aSoR05yYl&-a;PK%JSqkKz^u zclzQI1C&>ka}G#rM^j_s1g&>56=BbJn_Q`JZC*Yx7)YS6^7^I_W?p3X9u5{ZX!+2##3Uk| zWKT{|wX;Y=&r8U-%KI&8^IkCb&0y3kY$bv}Vg#5=$zh%a23X*BzB6SMpNMf zA;TWsnZ7LbT0Q(zpFsBW_*;v;j>ma7#WYDdcbz{j>vOSEZy74L+O+2>I_YEpvmUJV z+|cSuY`bIBPk!?`cEY92>*A_J2j8G>7K{FXY@~I%lVG%J6&@bV9^3CcC|BU5PNToSWfg1=R}$O_48jSIYR>p?Y6H1=l~fDkB()0dF3LV zQ=(O_$zQe~r_5p5h~F-W^ASnj>jdvSAju-zT1b5E~j?)oD>jm`l=E+k2` z+0{W~mxxuEv*fFx_VG)Sh>jsH5Sy3NDmXx{aX?Y|H*+@q3=1ho$j!7yi}v4? z8tmhS(@ewJ=Uulhw#~I{U-X4vk||Q%mV%K!BMy(%T>kzjkKdKmx{L9QUusDt z)f}Cl@$B$yTh=QxifnNsKHZy+yPBoI_aZm-$ln!6wp7WbwtCAkQ9g@TTwwmyQwm|P zN;ohE^Ak7InZM2k_)f!}HN|}n!z^}c{kv8fx9uscZm|MGR>^(%*g$g9d^T~9Guy(K z-znS@wo4;LPW>)d?=^hHSd#s!P=3r4c;mB2DAM?|KMWvlr%r^-o(+W&3%;VD4@$J3 zBK$9li{(XH{<9QtLA)%%;T$Fo8?!5qt?rUKU5gDjyV)6w!16jG^cdT^i;!#9LKkw_ z#{O6(KuRhVT9TH!Qp>)@CLgP+M4JV=nQqY2iv(kyNjG>l1@#BuMhJEEXlTy z1-1VgaVguD+%5ym+|j%~RGF;#N9L^EELx3eOc-9?(w{`!s~Q0j4O8q_Dyo2{artb? zu)34*`ABFx0xq>%D!*xteG`O;mIk0a9UZq(Ki!oe2uyuWG>!9fSp1;uK!732{KqeNm1MOMAD@?A4Q(gL z=4@!@>j9BY)RPzsK+0O7dI!@5vU;xIkcxQ*L=cr%z0WAy(O_Fx!|#h&i+`D-7eFumo)Ko;s}J<(1M4Jw;|o$kY}bO-Dpqy(y;=U5eE zifEp+MdEsxZ*8pgf@F)L(r+g2H@>N}5r+S_ z^~Q_;?CyEn2DUY7hduus1+LV%d)&;Z2Z%Nsc|0wQF1UyV%okE;50^$;LVLp_Aetzt0G# zSY>}fn#vFfedF&Df!GBQ7AMHFX}R`H5zjo0qs++W)O>NG^($XtZQ@B=`1AeqW4znW z$y%kFZSbl#A-Z8%bky}*)c@>)dgoWHCUJUZt5`8rs2=u~tKrqdvg@Oo&k)5j^IjQ) zzx8O8q5J4WtaNh<^;7SSd|-V+HwD@kjo_jcm6AJV)nb^gis|`1U!5eu;Pt_5E-Hg^ zN&V!zgLe`?93z6b$3z50a^>-U@oUiRO}I*5=EZ2wR|MT%(E~n9mr=wN5nKR7qC`g@H6lPW^LHopTe1%Fz);>77SIlmqwM=g%6V-M)#o-g zdlhPhJy(M&WRSzEf}!+R0hEK|z2xtGGbQ!Rwbmqc1OHoU6+Y*mH}iC6`FVd`7I6KO z-V+bGTIEg1XJ3AB>*##NQ|lbt`J>W5bTAEw2EKCtF>;Knhw3rmD-VKyqj_MZ_DT6; z8prS2n*`f#4Y|rR-B68nj|bQ+KI7C)_Bize_tU*uG53>&8Y2H|Fsocm475!}S;JD| zv6TB;{*?3km5J}a#Y+^2vp=I42$C6o+FFoL>A#xf%}`>)t1r1s*AwY$&|#B(;}1$y z?Ct8VqXEZXA53K-i*=0~m5+~xEC`t7SckHhrvJ&DPCN~)!0hBi7SRpj?DU0;U4-)} zDryTMZw^VP$kYBddwFc80bM0sS7qB_Wk$apRs!2_jcQ^}a(ZpoNX#LQ2jCiIp05}T z?Whj>`+wIC&w_hdd4zN=kW#Q>zlf84{S(-mLCI+S5q5An_ps{(->PI<-;FdsKEeASt^e!x!w_yB>Ul)}g=pZ$G^6VWAm?;n7(R%8 zSONG^;qCPx{m)H*Q|u0}-6?+{iVS*syYb!JX;t}HQQzmfCz3M6XClf>Cs@c#2PWW) z7X!E8{LE{EU-hIwPhx5^L(ET3_iI6_^X5DAR2Dm`$q^lFoa4MQxUaOFNL#Rw&O^;< zuo|~^dYnX-nk^32DB5RG4lNP`dzr*QS>o>OE({Mp+J42$&SHZ};7GV7th1P0k!)V{ z=JTST{97>YAR~s7?(zE5iBC!y&Qi4zj3rmEE^}X`Nz8})#GII76Im>l8k3ithgxo01UH`TaJP;X zIX(sb(xPjZ9`Mh`AFo-jIXSs%I>LV5em+OoQ>>wP3Q|$`ew%Lo7C#2R6?b%WxolIN zNu>t`IP&KZDr_71AZa%(VY7K1{5|bkVmBB0hf|$B{c!r7^bP+f9R-e%JiNR&;*<1p zK4}iSK7Hhe`QN(Dd`D{6qOzZtPWW8?qC&%^e-L!=Y;k=L0{OZr7!@TUc8`d{@M2_$ zKZ{&SZ`fYf8cgN4ZY*98PjH`)fg{eOB=I}XndG7qvaZGxIA^FjYY9`@HWYvg-c4nO zMOW{v1&`&+|uzt`9=C4GjE)tALb7`?KD6oTd9yQdvJ)R0p(9&D zp68LYBJvR7F3Q4|c^=$wW70&BxrIW06wvGuYi<-n;)xs`hMJ?wOuy-CTWHXM$k&Yx znS^!9_4R)Fqgz{mR9Iv+FYP=WM7TqDG^~ruaCY$=77G)HopsS*HC-PFRMWG0+a%xk z2KVsZPWjZ3gFx5R<`0$^g0n?e3kiXtfvJ7q@G}neh%L6s_^|pP?E-!U|c7&ZY?jXjQ%j* z_Q0w@)5uyKtNb#!zvhm+6d^wVmRzlkNlK?>S8M(pe=&+ zp@Rp*fBUEbX-J$%3MG1;;&-zPp1u{a1aL) zb3y|HD8QJ9owSguR20kww59RMZ7G}QoPzaC8+>dh`PK$&JNaJ%C{zmOz zs|)%(xHix))dZJ25z=wfiTq+7^M&os<=tLRW}OoQoq6WC4x)N7%p4yvfnZ?U`Z!+9 zQkNlqDm=r8T^r{!zg~@L7(HdVHA2_8b+D?T`mWQt0oPphGVKvC6mGri4%?h+XlQ5- zO>7TpOChzKh^6(z{8OApP=@|D#tYyBnEdCDE%HRTAaq&?C%)MUZAqTUbDaHB zlYcJ=1u%d>p}nMY{SYd&mvQ|N06=aLkn-U(q5Q)q!_42_AIkS?Ngwm?w1K*krUwD- zb7E>j@`y~^Jt(i$BEW@y+mD^8fJSh;UYkwWRoglr$g}34agEU{n()} z^|1V1&W=kESgoA}*G5K*l`PWx_zZ#~OuJCs!*OD8KnNgsJrD*^I`nErbsf?ML9ck( z%fp_Fc87-_dqmR$N5X1tKc3PtOMj03K?#H(q#^Qxr~opL-^h5W@Peoux&yiAiy{CT zbkZ-N5C9yyOkCc&lgYTd_wqWN1u2+JiqUSutr;tcYC)=)o4)zQ*$V1 z9>6c2Y&VCdDLYUU?4ssL<4xwdZV((Re7*Pi_ z6Au)R5Tf}`(_*j+s9t5(^8e#cLit}F3*nUJ`4L{Q2=uVJUPXw~xICHrT|=RMZu;}O z_rsNX84+sLA&{w_u7^Ig0;KI0?uwF$x&Qz`07*naRC6u|(|2}yrM9!4IebdbvIa6O zuqxX_fcbv{6qR+?$4(v(AN#laUm@jn!1{_epL*tVDBc#ZM* zsIFFvxqrrGuA81=*dLc|7KOF{i1aWDT9P9XI!o9}0GhVw2fBD8x>($t&t{UDJeC)%tm;$5@ z(rz!@by0ZLwbzMhO@%w|x!a}#mP}t{O!^FU|g=vObelt-tz6^V40ziWh1MoO{xD%&JnJb&T zW+XwgE8Qo={D1v3zZ%Zpu|tkFlXCXc;KFPU1NP7><_{%B%Ru8t`0znbJ*Wd~`Y}_& z;6)fFAA2dB`Rm)m*?-ZR{<9NdNK2m#PUN$QAv*p^h|}Oopty)Ns$MkpoJM1){<&}7 zQcpa>>y3f5vWy)cKk55!-b*({3g~7Loz0t0r^-yNO~_RI@S!mM(uq)7t2t%-1DFNe(+asD?$SAx*1&9m{SLXxSb;~ykkAI}9;^PD%S=s>{ zj;8-OLi4=9zc(YmlO`QU(E(6hDtPgpZe*=;d8$g{{5$ZC2W=y&D`1mTEESiXCqq3VkSYK8_ea^ z@kd+Qr?$?~`eFQu)-Ogdwf{so^Ywee$d~R2laC(~5z{#Zs_P0N?n;yXJ6^M7LclhiA#j_P z2@>cJl2F~H81mXbOCVhnuf_anbF>)GJwJi>D3Vxx^HXE4?K{74Ja)mtg@$^3Xd(C( z0>DBL(suyOHmqC;06g#LVIPt|NNRSnT=I@vIx&^n$Py`!`LY2C2EU_aen0=AkA&6o zpv5cL*7w^IIUm$YfEf_x--`jnoIRR-I$D|I*EXj%*Ds3z1AY9Cr^D$#`%xJG!BYZQ zwj-gc=r9}9dRRx`97uZdh{+7*@Ac_Pp)C|*xF%d!DuMc@^Od#gjkRFWU@zQ_dTFiN z_4oTYEg+|{M>H+)6CH>p!Ft86&6+OIxMUL{rv*7C9PRNvXdALBglx8aJ)01qK9|!Q z4D9MFt_T;)H{kC3@6)NJr_EUoEjJ}AJ*a8hMS#PpVh1fxP5;nwzNZ0GJwihC55S=h ze}EW+u@eOYoXFAe0cItbsb4@M9-*2QAg&f@jTsaIKn2z{@930t?w<}{Na`{|&fuIA zqT`0|?gtXU{LhY!*@h8c{QMWf2XB6_q-L$5l>lMRe&*?)buQ9&YW|y(cZVMs%n+E= z-+=O&=STBr}ANtwxuO5U})V1)y{O1z3dU~L6lR?5M4 zO$ls}Zverp0g5PkVsUu~(_kEv9`JXo`99t$$Atjha?cbIJ^|pzW%6a(v3*DQ(e1Z} zQ#!hQDee^j9^=XH0p{(z0GdC4V>p=qDieZ8L!49qfIh+xxVj*X@|X{Jpq94QPZ!!?9Z(L#Pd4Pr3eSSM52EgVOexPBVwxlR7!`;lrXY8hBU()=6oABOt7U z>2po^3nh)6M(4zBevQGy{52p($Ii;pYdZY#@BLx8`KEX3ofxY!H0Ez+|15W3o0Y*?1&zV10$prR(p~BkzZbCp(wpjus1jxY^A?0Fi7_{rc z3&W3YyETlAjF@F~DF^{f1*Zal&%cA`(NC-bxN45#3t0e2P5%OmI28cpjN=L>&o})y z-VqPdLNJIt#v(TPrw{-Z0GJ8X>hws+0Cr^N3nWWEN1{W=kryWLsp(0R*#7f>`k%uG z-u)gq|B3mlQ|)9h=G{FoSXg4$?J#F;e^!_|`pM)k;Rix9b9y|Ce&^wE=Fe{plMf$| z15!lHW{3$^0HoJmIRw&Y%>6F~k9u1#+zc?=#0hxAD*3YDmw|6Y)|+kRN}dP+#-Ma> z3=^d9r@%OCv9(T=N$+jUH;wkMmf4rN;z%>{Fo2v@|U@$_63kXa>xFC|oZ{)rZ02+W& zYHk70t(aTD8JIr@&|nGpz2E$N_<$V#R>+(Ou_z78e3v@hA;I(OW`H)&oA1{}0O7~tI{wvYk?K{8dDlo`uZ?gEcjJS2nY3}^V!1@CG$(A+Tq>uN zPN&cg1b~`#M{&@;0p&*yhj8RfC|$52l(wvi);Y5Vh^xZ<;ja9!1Q5DRVxo0Wz`$BT zP#w%M5FtRXFWIvvY?lwzt#{m>Spo)4`=yHg7Qlg&$ukw4@`Cgm{ZwtLxN!l1dH#yC zA;<>r!H*NFTM@j=hRt>HS|I6=v(KLS~#HjgK zVgAhdcQY`;%;Yb^;iC)83>(OYZe)f4~MUb&XDOLl{LUi_S|Opt|Z{h#%-f+xjz5zvkMS*XT*^ z#F#eHQnd5c+2&yEk8c(O7aH0C1a(EQsOvyW1z_;-hkYkQ`DslHY#5H#0LD4i07wcN zIPquzOquw+C%s;EC6GUPQ!YVQwl`)vpd?fGC42UUi+1kRJ^^=z(b3VqwgjY1J~G~l zB9ne=R{#Ka$j?L6@+GG9vG9?MxwV8aUFH}6l7NG%?&Fa;w!tej)|0K;n zXZFu){@rH&3`DIZHUeN{QllR&!RPxWY0jJ&4Wr+BIE;Mx&M@`Darq`FK18Ao=I^k= zBo9L4574bJDXn@^5_Cp#x>Zcu`KAN8?aUg;0H%!Df8Cn3>I101&_MVJyy%yXz)LvN zEQ4*J>K%>JO#Oh_JPQruK*F%+10-#^S7zDby$St-Yde7lo-2_RUxx~j}sAwc!Gbnjj{2VNW=d*ZQh z_{fpWF;Msj0q&DPU83n7iAGJ(_(?td^8x_l%Ey>Dp-~F}bb+6TSXT4_;2mZ0J>nVl zXwA90umCuOYhEjzPUq!2y=F3fu=qlnF**DVO7s8XZ+$^a{cZ{?(fp+un8Tl#?QDPv z_k#JO5isaI;Ag-RKZqJ8|AVJw?!PCDedEDUmU-Bg7)6|q#KY6Icr{X>vLI^R7~l-j zC3D&znpqEn$gZ^i)zVb6-Q-lZnX`4xZ(!1DwJ`BA#nA7eQQTu^M#geJ&_d_|-(Uo2 z>1m#E)0VkQf%ADR^lX9!OU;^vC)zy>DFOBjpdHW|f-4^64P&N>^P6c= zJMyQR5CHsOWUANR-Ft01;1=x@Ff!_nf&C=}qyoD$Wr!mE?%x~-xFQ6k0bHrI4?r5P z+KPfGY(!tOC~VM=t@x!?N-6|^3M^{g!BAYO`-7 zENQ0H7UQD9FObwUz8YqH;K_J^$RkkYP4wiA6#!gJhR^~f&izb(*|NX6uN!-!0x))+ zFc=<}Z-B;rNqYvU^Cj)6M(_{r`I!)axS`;TIzDywujg%LDr;S5EdiGr96IU31j=K&;sEWR zl>n`TLI7w0YLS^|Oj3-UjzIdW6^IW~jEtNK8#itUfAod_6yA5!&9WFudQ+m(8kuWc z`X&=R?z_FOKhAzw8yq>M)tJ_g!yof;vOJ7_^Fev}-yWtod<2s}p$g@4gpUzg|I0iV zu&lp}Tyyy|cqVdNaW9c3ZbUj?Q zabJV^&o)KlV^|iIYA_{^3tFu>1VUr-R)yJeo4J8GoY0iOPY!B0i#A=fB_1s1s{{}d z64Sg%MWSI{3kU(^4-KUgTnLZ?Vvd0l0N6F~+2@`M&+U6Y3=a>RGF7jyx>na*#`Sms z^-ISzaUr0L39mcv>RABz&gO6k8Xj!AG_fvG27)?)p@uIRzC5jjLI7wGYSW1skPe|! zs|7j`OxWxoHI z=KHm=&)KitFDrl!%b|W1{HxZ?j=X~yK>>y&-TDA&D`RDc_CKXQfUfj!5D^gZjqUGM zEv5a7P^?+4^W|$m2ZjTlW0|NGr(SL8!@L$;FMdCKvv&Hp$W@53jDuvYK>tAp5CH0# z=#4QbAM$G^FfOVt`)ei>kIVRnsXTowlt#uvIA2o&8|5U3<1ataH2WDtRw7_R)2VVd z29tv#5j@NifF)FL;@p7k+qQ-8-}<9)RNeH!R2UmgTu_|L!pN8v;7d7o`^VUjnRKb!n`=(gFPL5<1ZnLoIQ#t-wy0wB#` z@|h#y%wOCR#=iBaa8~bxP=<+K#~Rms+iOHy@b$C~4nlhE1fiRhR*e8Kw^tUdLeC?> zWal}Rb-5N09~}P%Q=gMunJ`0yTXs%#0+)+Y^O3ZeAv=W^H4Ey(hiY}E{WHSNdVw~t z(P?)JIp~o;KZL$U0>gAr^kbC>KV@_zkSX*)?JCPJpsbC9@CyiAv}XWZ6c_3()geN-Lj>-?)Qe+FGQ0?5Ung8BR4q8yg^W%K=D z@eu5$R~gB9L(SIj^EM#YzTszUbuInM)Dr{w8rZSa>thz%2%C>g-1aVf_4%>@}Fr{_t@v@zZ?&^D+spR^1Y$ zY_vGq+-@KbV2bIuozntszkf;A{T^6q=Qz}hx?*{X%l_5~#t9DAC>$(iXG-7+!D^(B ziDTM-S2#qCigxfdLndt}V|$_9BO$uO(*7G=$_iW=E{*sCU=15$kNIxFlLj-*$5{e8 zSuhN%teuDMBCe;;j)c#B=GVfneDYIa&ARpRNMCmNOZWDN@pp5nAI^TP z@%O>R{GN^uHPb(cds+wi{L=$r^eZ~Z=lE#0#7}Wr3@PDkF$+tEY0Gw5R2`cQ5bN>D z&hBQ{Y#kbj55DwT`-h3(_0P7yjRx=;he39&Y$QPNq83sckGjUp8mZudaWrP^zy7ER zaeAh9TgZ-$l!o<-oNK`XEP^q$1%wM=iT3Z#gN^0fLM)ocSR0qs?2$vEtZQkz`~ui7 z03iTbn&|-O2toi#1wd*+QD~@kp0AY?%eGSlRTp+kz50r)ekx`}`_0nI-(?L!}#BJjKzqv_N@6WPy+S_lAhz)}AJ zB1Z?%*#z$loU2ZpJ{jKgj(3I6{qnDc^&2;a8JSQAaQH*>XY3GQ{@pbDqxJJ+z!Df# zFn`L#*-x8;jC}pxa8~R4wfHu3_G=C~W5%8Ti)oa>v(S8MzGP}|j(_SC2Xj77zdYO8 z-L9!o^IM7b@5}z0Wxy~Z<|E@NiuEB&AK19i*x;J~vE-9xL)c7MP*25G2fC`y`{I{j zk4E?G_?{0@njy!((h8gVZ`SjgK7fW`!mYJKC60fMS{16*ujzLP0rbxkM}st&P};jS z+cZct1Ny=DIYNL+K&hyY5=~db5>OtK|CC8U5EXuuv7UgZ#>Xyns;2rMbI8 z0B8j(kXZ&+mUReEb7H7k>NSe=b~b{sp$N#Q;b8uFB@sDT6`NRn1?6CT0FG z{aNNOVt(jMIP;}j!`OEolZ8N72jV3N*(4a|MDN4^BoE+<`LhvIwO0TsoCAOQJ#f-r z?;Zc5_CF}%*=(Rc_@(QOo6RGQ590*qAaarFfq?z=qqb4FuQx0N71R7V!nT!w=0-@h zN6&|8u#PPI^9&okQY9>h>=$tOw3Z2Oj++Ka>lD&P0RU?N!Gr-_^|~v8d?_P?lT8Q6 zDo~asV3k%|Tz$orT3$UGetg&MVS}u!Q5l=dNV`(6Dw%@IQ3${i5T}UqLI7h3KQOGD zFm=-Kj;sG*`k3hHLvx?Wf=BL6R z{!hOb_DDi4iy4(xi}4%t&pHyx7PDHnf@%IL%Ml0WKPvMr(y8h=`NW}c<}0^{@gF{; z4T!RVT#w4NQco|tm9$wJ7pl-&8$oX3qy0xB&_=;sP73q0r9WLRC(WMj#qkd>^+sTs zruBP-G3t&#Ku3c5i(LX+txrelP$v`f&q&p@DseH*+nRp?*!?-czx)sSF#FE$xx!Et zqLbm=0#*s{-I;9emmWNUFqq$8JSjoocnF)dT=atVk%mA|V8{pokTl{!Mxblm2xbXT z#?TagdM>FB9Dh5!Ze6%Sdjvf9!VBT?r=B!RfRF!P;}zw>#3|ot7gwIG>S+HNd^|4w z3jEJ-~?-uPx^YGMg z2=~N;`@)(3@}n^IlstX0N_1p?L1EHc*xYAzhDydT~uJX3}9mA{cFB@l#wh=t<>axG;&8YsC zh9LxSn9!3tM?g-2r3P#>cHfrwpR#%wP0ca`rko7Kpe z&U^0;Cr)b1U@1G10ry@AFeh*+EPx}`*12b@hdF%4kP8IdSv#_K)Z61m#y@{apidD* z;h7vd6AA&K2{_VGbR62%sWWH7@BhZ_3Oj8fB0{P^Rf6C31-~OK%Ly-5Be1uepVy!OSCHfD)*c+%e!dm zkIQaJn+AjnmILA#2tIW>6aqkl5cQ71LGAE~qv10j|Ap|Wk9{HxX(>>dF|bC=-?ec% zk%59H&<#YS90ULltnWXYZQP=PJ9fL6|DW9=um6#_1hLDSzgNSq9cnK`83CZ$awseW znEt&_=9J$@4>0~-FZ-kUX#Z%GoeHk8cgzYPN)Geyv^LX9dm*>obk1C;jW7=Ss-Cs0 zm3DO`4MM<##u=7?^}?qMH`&2G(Vs~J-RYeR0qxQmXoujz21R9UPG){gY+k0*48zt; z2*8?}(HLO2d)GzA{BODAHlr83LsE#>jzFQ|Oem8nx3~l_)+5Kyjb=f&pbgNt_?yf0 zDG+8K=lgvd;H=RN*QT+Eg#fSsoMH#Y4x)2Mb+qr#%j5qyKJ(eIY0K7Zt-rji(=C2s z{_L~g@jg?A=J1y?e+>|0{?Rlxewf4wzKpVX2b+tYtOaxKv0wmx@Ct7CvLplpZ0 z1d#LxYyLZ`v9;CWT8T0Jv*l;+;7>^~leKL){?%#?xQ{6lOF$?3m}v@_<6;o%w2ctp zZdqt(Be1I1M&|u{y6i7){6hC2l==vUUw-(IIscXR=pZ2k1Ew}%w%kMKTnK0f-Idmu zKbk+=mE&wk-lyfz%=0#FTMYeEJYdS$8k7R|iYu=&pP?Vzev9pSQRz=$zfdDP{~hKX z1%c@GPYLyKAbyW=SS=7Y0Af`DXQZgTT?~Z)&>(OztDZW2D!l5I*N5N#kG~yu$ov#N z{>A*$<$gHfb%pt>qGt97qcf~_%^xfmaX$OqN5aUL?hG?J>|-&Qzk_liWcPUWuH-ST zHszNEPR~p&3CF+1*zX@La^3J+(;Vu-wv}%%GV*8D1q8Sg0kmC6L+(U5HS-Vy>AD~( zSk-DHQ;9~iV!r>etpqc3aa492aHGb|th*7I{(`WwH$VmHTQ5mfwtihKB28PO z_jv1VXe1@QF<+Cz^e^+SV7ep%nyBDaX2YOCwMbhBm)NG31CAJk`RM>Uw~>&cs%idO zsWgxIdmhA-5CCl}X^EnvxX}e!+SbY!VAqZd!xK+E84eykXiGI26GWtXiy5dRKWF+b z1f*BK#c|Nv$`%4Z4a|u6TbWBjLi?Rx|BvC_Z@*CkL5L-Uw|Rf0Qvy;iPJi79)y)2Q z`>X63na0k3^Cw~CukQ{sS~gN*ATBoZ7YsVZgfQo!DPe(`l;hu|274N?5U_7LpPdzS{J390ydp&}b}+_*xwMA?>j!)N!&JH*fH%tv zJH`Al;ikKsb-VtaKLSh~=l-U9S8$l}JTNRc?zaQO!?Wzo6@Y2XvGlL}-6uo&`%gq) z0BF&e6cQM?2cug=^cTsMcUj}h&mD_qe+d+3+OHPhlQO(zWi3B(IFwI~&53V}UbysA zH@rH0>Z6|s$4;IQiFA5^CDAh}t|egktY1c0iS!PfmI#J<~Q!~EGZVcqIAW@7AidE}2~Wy_c3kI{)f zhIm&@pxtkQnf`k%q|wIA3Sj&FtIw8fu%RZ*^XY(#b$$TaKNe7gfxIOkuav~08p_&n zuzU|@|7gvuDlFuIME)p1SU@*Q5M|4CBfWSC2*$D$a(dvY)2GAT58k)vX9nb}-*3-_ z0FdjEXfi_a)^a-S^mpZDSA;+O{O^W~g;X<=QU}%u;c)sRz;L_8{PBRr^lyw!Wx@PM zg!B|EVZ$8$bfRB*OmqGWIO@`eAvJrpQ#~^|Yw&x1^pE=nc)lDk0~~rWuj^%|SA-O{ z{Ox4`9_zFlB@RTr3c#85$rGA#>QpervPTx2#S;<{)>ha`4aeh7!LR4}&|z3n9@i2< z(H|TGZTvAEKtDp06vZ9(=2#{r5R{+OI{*9X%=y=A5aS$w04xsYt&743LV)UoMQQaK zZ5O;}Z+PLwec_2`pSF`5ovtr=xYvVA3*XdBMQ{i>k|3#LZsxr2U;Bkmhbu3?LWfag z+x(`>{m}aB&2xFi&0OK*Ab86BaV$jYMby>c8o%qAF#0!|^B=_wrY_7&*v*u;gyN=i z{Ai*1!EE$qk({c~K6-7+249&+woPQfn_dN#HMm0MO6Z{7od^g|KK?q9U#&b~5WOg; zTKUN6__9$5Bwg~bDwOlTSbCXMFLI)5~O6rE;K zi1?&?>9q3F0Y*p~v)LFIzNhSd421y=c2 zhPj+Pb2|L~=i*L(Heiu**GkKR;SylF-I(`x&EE$MW!S+!Do+D${GR8+nLqz=m^pJ6 zY63>b`t{xE-SFl0hOyW~szodiwEmeyBJXEgY#uP?mdLap3d7ry!Z z@0t}L6*SJNK4=3^GH%vJ4(C8p;fg}UM4$J6=g+)7y!Xa;hn0ic=}%uvIQ=>ECjpE( zCg~gmT=RG4k97fQnyZNI`2E`3|0{Qd=@Z)FZxJ^6X{(`~;1H?+%%+<5pVp0~RXyEV zO?B!u^9S`flD3zo{}jgDGX-M|E&rj#=jafqan+^JOUJ)_6Trv5IP521v38%Kg_!*1 z1UU1xhr-Og`{FYS(x{j`d!i7aX|iON_eo)5aw40p{Ovv%3Ze^JdQcZ2*b+WB%7Nx5=jtX`TOVVS2w#>0%n9 zvl!M6*8DHT+_3`mQY)V7u~`{y5(~(%c0ub1T-)e!gb-le3DYX0a3;6RbD^q5OGGj@ zYb2n4y|xOaRbk4cmpGZ0-SvC8Daz64c$oS6gYpYFsAp)DXcx|U2m#F3i)NKB(YonBb!d<~4MXz_Awb~U zv17*{8^o6BA17QO4D*m1)^7-Z@S9%Em_OMX4N$*t)|Tx%&)*Slzvm89@US2{T_DbK4(BjNF5In-1jNMjzwh8n;e+pf zPk7T$zg_~O$OYQ?u;y1CNnp$%bEgANGDxx>*!LQVS#$i;hfjx*uih0VAKI^J2-RhY zY5v}%PF&d=o59;iU|{!x;IbU9myCt7V0xWA?57cU){LVe!FIMx2XqP!<`CEbv&Irc zKl7_+6_W0+Vbg_iTN5aAKMbuLN;J>8-_EHgRBBl#am*|K!$YBb;Iz>&69NzrAaPg$ zE?6H*S8k6quTyHcaG?CG{0rn5IBP`(xO%UpIoiVgy72M$|6Dk9{Ky=-kn#YcFSoF4 zEdVLr!BkFO9+d&uuVMbE& znXlXyCT@E+3L@tK^KVt+hUqoyKvhB`QAc-cSs+>^0zfZ=Tv4U{uQ8_E%W~_L6`8Os zRklTNy_k)DV<4bV*49tpC3#HX`nM!n7j8{h8(1mrU+dKw-^(EYXlUl) zGa<-VsdVXiQR|0xB_JrRUma;4JNtDakT!iL^M>r0{^yLashM~)s1 z4?X&bR(P$}dn!waR;+lxy#6jr0U-8!?0h`@A>x}hZnX9OS6{7t{zb@ag9*-{_JFwx z(|>}*{;8!a#iR8zpYOy0=8w}KNGnH~k+0tq#=iMz2>2j4uC`QIeCiW>7}qr(}~Mn2moZuJBUl1 z8663~^x;p08?L!dR9y&(1Pfz!YJ%Ax>9RS#RUfWEoC*8f7|{Qum4^%78o8r5Ve?M`>kp{2bdk(5yxeG zj?}H^M-H5*SS`^0YSTMR1Ef;{5O*wjixZ;5Doa+EWuOC@4!GlmF!MdpG4p5Az<1Q3 zTg0GCm!D^w`IvCfE}+O8rRtDYNok`Y3%t`0{A4eet1iFNRt1b|lQ(;hbTAt+yIn?? zWoH3MwST;FkDodbK5)~`;Xi!pSHjx0>x2_B8x2cZ&7?;|%udhygLE)|5p)aWkGCuY zPZo)>Tb>9bU%o5Y$$t1D_@FtL5Yxxq0&T*{3~%>F=l-i@2UF;z6@V#)X`M~b+fX(~ zGAtqe8U`{e0OvMzA~06zyS%`YH~pt&fmS}{gMe1i@X}b_zeiXztCdX8%Q`^6V8$CH zxbE64`j*i%Its?juBz+g%z8pjfmlF^-)?|j;uI)n!_o$g7oNR5(btQ2?h2>1Rq&nn z-=hPxhG+4GV)MAhyK^~Y9OztbV!mx)L+t3O>B+E79-^Q4&_}~Y&HK-=w>f5im@@$i z1apo_;=4}#MM`z%k68?}KU-+Z#62ZX|B0{OFM&W3YDQ2yQDnd6fkfU&VDprAm_KDO zs98SPsQ{kz()3Tg(X269_H_^{`=Lag88UHyg*30cmY;_QeGT(P^Jiay#R$}Ql^p+U zhsMRIK);j;AYk~KVx9T>hr`T$FX|w*WZZMhJUw&vSIQEQT14Av1LRvYwfwja4bsv? z6Dm{}aHA~C-CC{m`sB}jEWA?3x{r>XwXszxF2%OW+jI49SqT8H4ZwUrhS9OH@XJ5{ zi{TYly-bW=%%3^t>}Xl*a0mztx|0Fs@74wKMe4=;uW5(r7mkOCZ`>bBa{e1woz0nb zvh04#E1?DXsv _=jXxrmxx!%u%Ov#;xXuW#yVvD1}cy+sdt!D)QR4|fGIZ!dc`N2@y2^{w0CgyhXX*4Ztj3s~l z==fKw4G00#!d;vs>-BN1^z+_pEF!!i5H=#j(a((ccKdo>JsNlcpW(P<&Pgaj0s-ji zF?swSIvv;y$W{TkQy|e8pIXCj)1bub#8d!WpF@MRBEY5svg&{vtPt9}>*6pjUx4r5 zeoNS(>E9GzTp&nVa8BMX3kv|e+yexJ!y%Xl`uO`ltR;V2ZO5q6Dh=4Y>E9D|;wl86 z!~EGi1`k+-VF?=J-+44l-@Q-NL#ApC478YPj!vDZNhZ6ebMXpT*aKmDPojUXefeYf?3=C|?NcDHG16Gz6Z#*Gx4 z@}q~s%(owxX`bzXMH78Eam50=S33o6StCI}FOa6%33R`vR@o)6%u0i-JQEOwPpfo_ z=Fi>ye(n7FvM_RXWDb6%Jfoc|^n2RgkB~)yh+~;>KuB;@+W#j$_>pk+6<5yn`uDnl ze4Qrrn4}N_2sryO?@zT<&eWaHgsJa65!d=Bn1?3Sh{@CPT&jPn40415yr;V%g=77x zC3MsSAzDLR^IyTGdht8)jWW?Bhtg@OPGsEeJOmRs~gRw?t#D*IVyW zzqk@&nUK}m(69alnySsr%C|ljX1*gKfMwNyiXjE?^quwlrOV_GkSRjdGivZH`9gP3 zO@#7OntH&WD?=g^0yOP=$?iSjlOOt+BH}lq@|{M5xuieG!#O_%K&N_up^j+D-_O4B zt@8T+<*-icLZw>>=~AzMAxWpb{xN^eS{GCsm_N&6^nT*5=fe1x?$&zPoJ6c>hKxek z)HLO7IhU@LK^I$v$=jB@Wuuib+xRaV;WYq+Km|rL3sabXucu`2TUaM8r5no_Yk;S7 zuhk7$09X-#24BBd>KDtnZN(QP_BHzeRA@l`lG^#oX@5hqeDvos&30PW(X=1}({N88 z52dYg{<}yk1Lz+ZZ-ykcUGR`Bp=j+mN6#|l7HdMjJ{7Q8)&&(G%ZgJ0mt3^lwz6g_ zVEr0R1te3idD_%s4f;0OIW4ND%%6^APIB%4&)$1INq$`Uep&6Od%DNZVEkYP-dlp8 z0Ueg)5=$<5@7>+I3Q3QOP$;AaJ?KFZdefW!5Isn`p&Pn)wMOeVuuF1Df(`^g0vNa% zV7#9;)m8d_PX4kgtGdk3RdrQQ=S=m_r8#*r^PH0>Po7i*|H=1$;0_(UPZFrsC{6Vr zZPj7+Unadk0_7J1$6&(zW%O5*x2(0llwr(ut$86NbSM|KXBO3GC}!-gVeltZV0CR( z$AzzGU~p9))<8dsi9*)&dE(O*aIrfjPjb-5q42OgfOblgzzgF>n2UOU zr`Ubp;R7NUA=a2j3y>>qU#MCmPzJOAVU*Qc@|Zo*v7vQq+DXGw!1)_q^&fX|br_Bn zuAJB{W~~H8I}quWzqu&3r3apoE7%nv2SIWI_u8x9awm@*x8>oO-o`v+%rKn_-17ED zr7CXoA~B2l=;8(UKmYgtqx;IsUsMN>7>L6k_MwJ=YQBsP7xH7xiO#N$W&vpyPgQ!v z)8Fd<_Zzp)DONqyU8t*BS6mZZWvNpGc%yWsp1y{4WB5)=lDxr|PE6IGH;2}A%>IW> z2&{oD;# z`-|VWiSIvUTLEopG|)<31!`_;euty^Q=pM!v6>9<2+K?^YKGv_;iGT2v)Nd{}d z0Mn_|yStIil08cWWjYbcQg&7T1~@@&+(X`3F)Dlv>hh)!cte1|t6))KB;?nAoz(K) zoHQNVAp-`Wd7^#wAAaa+a@I5P+7mkFT$3djFLVGQK@+3OLkIj`erRW))ftLuk6J&g zVs{Fm3=zsf-nJCMRMp?xo*>~>g2;aM2p+942+6}kBR zqd)k8tpAV8!=C(=V^|f|M{{Y{K;dS*QweJSoZc74$6mZf8}Y|+>G%GfB)o6>MyE4zv~CE@&XG9F3a0vx>SMa7?9v#A#oD(FM{Ff81S6Mj%LxE6M}_(0=;74y6Yfv`@ZY-KdvwgNB*2P%L|aFrIcQY` zfU*J|)KvdTh8X;rw5ewq0IvScTdw+>&t!WbW}Z>#7z7b3tJ6dbV+>+pxKy+;a{0>c0|TjehK}RXTbVL*IW%D z;A$|4PR1=4(bI|3@(-|Ee*KFI0pLu7WJ(B-7u5QXKS-E-LIb!!M;Eli;(oabW^z?@ zzJ}I8%fNMM0qS-DQ35qWfD8gp9y@MAz@_U~MamO6a#d{f6KyF0AYP|Ia>KjR7oK}j zr~Q4`P02Hvx%*}IkJLwy#Ak*e{;Bw1@@07;gmB1@I&Jmci*Dur{)MaG!XuT?o|9%! zuZiuYfPmq?*O~;pxL@>5pa^IVNVgq^j{$_gG7SGZLM~zs!!Bc)31MP;+h0d4=YJ{E zS^LX(Tn!DW&CC-b2}#w2sd9M9RZi{YA%7VmC5-)3X#hUC&h+21q!^o+NdW!M*T3mr z``Wj}Lt3=}S!qcqZd9*B761&z1HnbyFkuE+6%qf#Z@=bF96P4jKYRXT!#{N*tU${O zRLxZXNgdhp$GX4X)o*D(ySDt*P&uJ}$T9m5V3PY3+XpfhC*c!nN$#N zM~X2psXhM_BEW+R_<;B0r`XuXL!+Pl4+_$IEMJ-fx^{Ef)&EBR0XPqU=a^Q?hN{AW zK{5;s7W}!f0DC}$zhq=uqx?fH1>9~rVY8(G8U7x*@347`o!628+kj)CG3GV7%O8=1 zL*XPK%+#?$EXb^i8CRu_H}7hig>!Ugkr4M0^5IWK5_l#TQBhHobuG=FR+U#HI|LssR2B3|ik@a_UUcf)S8=V(`bdb>N z9dZhV7JwaGyv`d)3%$On!-77R5P&wA6WGYq7)srJ{<-JfcfR(jyQpPkymH2&o#({g z#_4`Qu7KG-<+o75?ANr#!ZUpszZU}7Ue&ypn^kSw@{*P2?`oG=;_8pkW=|j;c`Yt1 zy8r3F{U6*T4?p4!|Fni=>;Gg%>#^;xtYE5tG50{ZkSNX7uN;v4v=BSW(Ev~_034tGF?i8t<{RjetYHq4+_Wnh8z{p#wfF?rs3 z=Dx82-t!%e0P#aQ2yvc(q=v-y>X*Obo_zE%sfv^jYWEhyKQT>&fHDGofOY@S7hwFh z!JllN|$u#(rGOPKz|Vm@&?k9$E7^0BLz;BeH_y2>9d_ZSLyygk-mVEDUiX3rva*%|=r7#_N|=9pf9i_(?GL44!04}L z)tk|eZM&gq0n$~6-3k1*!w@h4XT?9j>pD94hi98Kn-0T|dEucY=d?4R6oWub*y2On{NES|Ews-*(|HTnxM;~OH*n6DCn`NvEe#xyNgyPmmgy-XIf z3^t6{{7D(B1=L0S z$_fYoWX%sZzpP71;Hh1_A*+A6`js|7(~M^4EvwupAFs+R+>B98yFFixZ4FAFy&6iE?RGbsgPxAo9fWBa(upNek0Ykbv2dAh0wRQ!3%2EKd zG@;*I*cG5t1Dy;53(dWsiDb{GREjrQCiti#CAtqz$1arrju;R%s{b+tjxNAw!*iERM*MZTT7af7 zDZP!FOtXJ$yb(vj76cCl41wLOz$B#KIRQ{G-2L{8w^?=AvgIaBo>K$sqNz5)3W6ey z0&U&9;G9kk)U2^M8t@q+4}ok&6hrY^0OiZJV4MT}Tk)1VXaU;FYUf480U2Hnxdi}D z*J6!6t~|4M)ci5?rYg+3u=?kC)0Gtr{rvjA-yOgX09rZ(F9;w6kY5Ot)6FKpX8+3b zsfQnRufFnCIsKWjwIbX1ANmDl1nDtkn)gbcf%)r}H7duxe+e7w?_G3j|M0FT1|2A_ zSKh+x{IK|YJ z{1@EI&%WSp>15efTY#aF`qCR9mjD1WUR!H5(1Te|Ymmf58o~lF(D=&v;pK90{#Lz2>~o8V5RxJZ+^$!fBdAa+f;D*8<;d5rc9?lgb2)P8u>0=s{k}ZPt5ETjSUwmkv2TUC&{Hk}psJ&F<28TQ@MGrh1%$Q6jsO^Y ztSM-P*c)ac5NzB*fDnLS(KNjA{sVBb;{%PeA|d1XC!TTN`0`iXgyPo4j5o~wTeJ)z zb&e~HKFk)>3F@~63rh>?C~H4{-&KG7QE3i;vOs4HvQjMto)!YpskmsVff?ke&0_0)5pZ+Z(>8BJlW4#s8htP zY_os8pkUkcFC!IcKWgW$xz)dZTb;4Qvf^@6+~WbUortAKcF#r$edwM=z$R9lJ^wMy z*hEl#(PAi5EJp;!qPHxqdDVXrjqguyAfTuc@?SchTTAJ&;)|KcmAg|OK_#(#7e0-d3(hGOkuQCXHn-c>?=Yu5Am}Y`@T-yRUEU4&G07yQst%p}-b3nezA6Vc!2M*pRp7UD6 zc)hic7;zb!Cdw=TG-v;1c|!a&;~XLg7|;6I=B%$dm^LIBa2taIG^VQB%1 z4suAnc6QY<2;3k{)ZGhDJ*&flUN9ciBBUr;kV$kq@*&}9>EMiK|1jg2(Zovw>2m8I zi$i_9p%zH3i~oA(L8N4T?!vXpHv4CfW?hYChJW$wpN1Hfj$>kfsx*0q9nK-(6`Lr!?f5AKKqqU*_y00|h-oVtvM zk7`H255E0fDVQ-8Z4VpYw8t2g=hOlK47hzZ9wZ;WApn#g@a->YFF7ul@>p|$klc~# z|Jg^Ma9?@pi<f6rGTRLSkQENcddV-GSDU`qy~P6Hu%e*d)a+WwgA^U+5#l-jCcd3w#1>* zR&lG4*2q9k5}HT(hBxJ15jqe6D)O&Se}I70#oRAkzv}+Gul+af{*(8saWwm%6Z5CR zLnw3YKh#xiYN~(QR=`w${*kMFEQ?giE=$`G;4gSZQk@VXs|`djD+pYP$>|e}$21{r zyfk@j$H|5PG={MaPqx&hjm0pul+(*y(kwg?&icwkHmP?uJrvKMDV+776)umw0QEx@Z^ z`l|cC{_;P$eY@p{U<^Yh0U)j)miZw-SewE@cw63;J=;tiCQ7dkX#}6g%g%n z0Qv@oGc=!Le1H6bCT60|6MAV3IJxf8Zh-yL0wB;88Pl}CvKRtXO z^(#iAsWnBk2?l{bJLl?8+~+D!9rjzZ>21lk%F!iP$Ei<|_G1pH$zT1#O;_11Edcfh z3h=!N`KS5(3ol9#xX-Q1f6b(LMr%>T9o^oaOmD3|J^%DlWyH$?Ju}JqF_g{rp(`@D zz!l@OPd;suU4;XK;uHVMoc)Jo4ulE}{}d1eR{rK~w~k>*Q6rO__{f?JVi0hcHSM^> zhrRa?#Oe1_w_;$c?YDo$ae$*RgwSyfz#jWL8Hak%f5**s0lb1|c3@-*XlZc{^!od9 z;fod^p#uo;&}~i&WLH2dSI$&~%^S#8rxI%YStKMWgIr__@YsWoxIcRBdv+YXr$aFP z@d%klW?&*nKJlzSydPYZJ;Fh2MGgFwm%r$i7MIk?ldXTDL8R$PHOWf@C>@Dn`Qh*f z?}idIY9C*9>%TbfDpMY&G+AwS92gn)09!qj)emc9u0|b|cBVbbYLm^Z6Jy7ZpYb8= zCIA#+dP7Dy)D-5Qxxc{V(Ez`5SuTRLPS3JHs5i|e4MF9I1ORr36=^>B!5wJH*fHL=N#fcEd@RMHz!kiX6T zb>iQ5bQl9$|7gq#mY`wlAK;WZ`=`MP>R`6^k0hYk|Jskwy4tn7Nk{Wh#^JBuKAF^M z901M>V;NOcU*foHwR~PEw$lj%SaX-r9Gi_yXq!0&Y9s8M4Og%7>AMKB;}Wp?&+T~0 zn6u8F!4|-_WFrKq=5#&;1Z)8gaZpe}TL3uu$I|?^Z~GRWA9>&*_l+-n*TH0RSf4y7jNwxmW^-odT8F=}^IiC5EbI{NMV@*WHoBM9>qLwjcT)Hv8KprK4<_}2|{FnK0|KRvH)f#;f<+Ylcuz%h9T#7%sY zZH2GtuLv4fwcLPB0YQ%lhtP?g(n!Dh%GcbExjD^4Hx^PBYw`t`_cyfwKyAG%USBv~ z0mCu?NGM+b@I#Ff}ROnUORHBPP(OFzYfxnEk73Km#z!VIzU=4=wdV2pFf}?OC$+{NdFQzEO&M zXlOD!0^Ysi>hE3l0s!x*I)Z=s_u7NLWTOg7I@nGpt-E&=4crF`fMfR86v6Q;BdFK5VgT zAn+|iu;n3Lh1a#zG)`-&>1s8&Sg`$3XZ~iJkW)ZmOCy3nj;=H~r1!~rl(zt*`j01; zeX7#?Gi@q=0QGj7% z?T-Bm5JxFw5&!_4y_)fQfBTWWXaQztb&hsUB0#!nox>gW-hwX-Sx~L)mA~b>)ct1s z7xG8_Uj|mm&(!}Es4Q9gCtnEy)gQg@Y8ZZu`Rt$eBQOKn@&?0ILZffW zeKU^QWVdA+UEU|5a)^@xBZ&s4o`|Rv&W4WODB{W0()yH7QkJ{faCD4|WcGA-4 zpLyQB@bt54Fsa^{`LpknK!=XX^)GY&HB6{s=@jhw$L+7+si~2xzc@FRmjAslfn7yR z412{Lw{K?r)Pvad?~fa6Y?i$}^4yr>-g{%J^%1T%w96A@4Y~S>k9>^dc{Hjv!m73l@=iYEy1h~M?esm zR{YM0pdGwGf!1I~>-#~w;Aw3|e*ZsoUwYvscU(3A_+B=*zt*S%6Fin70KoK-9Jwmb zIu@SftzY$*AGz9johUUXUjGDjs$h86dZt%~rb;qCk!ZVKL7l5#l=kGzo<@m)w!^pC z&oKW{pZ#~j5kuaXL8p&B-kB{;phG?4jnXHyr<<(>VBFd4AE!TqK#moloV8o4{?m`l zeygQ@_1#YOTbL=vI}jk`51`IcKvENEDyIZ4E$wnIKlh?c$X1QMw$B^dp9g`4w0Y(d z0H`pt^uVksjBSAk0kKU$ley38)INS*Ab{B;81T$*W@gT$LE?viM&b6I+wRPfQzlJK zsKeBev}Pk$k*NQRhR|Uso8qx)VD>MDU;E^mEdS5yJ(0FvU?7eo(eBj}M&QznurzVn z3PKC8Ed;b7C3zh6egtjlr17%%ovYOQt!M>10%DrgJHh6Eg^HOy3v|xtoo=A1?Y}?^ zEgo^BUwho}9P%S>kHBYC|1t0A5k3DW;QHmKuFi4oOzs%(6wY;C%pWa4kw9KdJ}+o# z7@om%2%@XcKlQA8_|$2)vMM0}++?0|gvNFG-RNqK?uMupjFcJvjgIy zIbex^E6<*A&^3J^-I`SJ>@c`^<%%S!7u@|PPS~1F1=S9XNubcOX8+beNw&=X**B^- ztG;#K)jk;Q6aPHa`Tw%)68GKwIcqY7p&E8ZlO!Q4=QQM&v1B~HOXN0}r<6E!q zt+j1Fd825Y0}ZOjHOne=2YpKzwM1B(rNXp0*QAbnTkV^#-`1pu-yN1eMFUsV&pZ8C z`%dE3<$K@KYPXa*OoI_Zmmw&WN4eZ5fln+wmTLGHGLNq`2YiCui8_&w!#4$KF*J#+b0G-YxXji zHw^&sAd;(+de8)TCZBGWi7f$X5F(IL*&BNK!e{PlFTdjU?%8LZc0x-8jSdC#F9V0k zz_2>P`04z5SJwd(=J-cG|F@_Dz^ZKL1V|9WhxOP8^}Q@zg1(xS@S_E&RBBsJb5f-3 zJI$<%aip#`YPploC{LyQUhnIN~Sg62Mbr_}E|wcI*C>niio zsLpALc83hN@suZ;J|U+$6=^`hN5D(MK3xrFe^*RTU+TA|0>}9d+odH))G)e}WjsQK zFH9HcE6i9IZf)CN$8~YVEi(YTXm|b1&s^nmr&_oF!8?|cqk?hm8!DHzeCDE@5_m}# z|GSi*0!yka11#=ba$kP&W%p-)`&XJYbi$-X-}FC)1OT8%2tcLJteu&E2;MG$gAu&% zfG|q{K_(fhd)bUi9TbM|Jq!BT6={}NR@_rhKjWT%@)^aK(I00$52;r42@HwTIPXD7K)ZvxaX%|e&lMma#w~|buhhi|=P>iBGY8z{fh9Mw zZ{AJrSukclA?81^AOQfzze7SmsIFB;58=7JFxuJLS`m`_8r$LS$`D)zadlnmx0da^ zl)60TRnK3y1Jr5~6xiK0JsB2NF7dh1n`A{CwR8Q_yoF(7 z69AL1R@cPLJtHTrpV^0J4gUzy){hz^2dp*2iVP@T)1f~njvmLrKwJML1cd#YF#j?H z0AN7?AOH-0|9Mo0uKrqQOv?{IWnL)qfLgSz_=g!BgtDe(m$40@i)E}p9xVU@D`&*V zuCX%~yJeyqtrI?a1#rAx(@e2-i1$iT+P5j~{f=o)uZ$4_n7wuK<=!jZNPZJtt4o_X zb#k9u{NtD01P9_st7J#KoK5-@kqDmT}4Nflpdx(*itwOfbqFuxe{pJf5o z_j%VEnzMc?-^94bJn`8YLOx52i|%{B_idA$G5(;uygp{}Z) z)uBIcY`v|2t!>fzgo|L71DG|@F5~jgU{zo8aJSYe^^^6*)UhohOv3l!Z0YXB*v$NM zptvxqyrY@)a~#&EIh8K=Y_KrIC1zmENuy&~Gz|wh9Gpfwu*F$S(S9()qO_E9$b-Dd z7Y!<9i1b%vY{yaq6F0Ol@PwfTIvtGtJ-G*d|59_Z2N1x`k_WZ`@D-l(1X!HvTt6qD z04)<=IXcp$_;b%b?+(jFFmCg`_N%p^%@`MPHA@o!LZ&cr?+j?xEd6I@F!=MvetZi& zocR9PGkt^xwgIv$U|G8fp3$K{k3R6QZyc@VG>`oi0?Wdh2D}B@gn>f7>crJwe(aC@ zML=k6FCTvk{-qz=01fD+-ed5yu28Uz@+61(kL7B9+|{E3T@ET)K=F>C*u z66gT92mX~#2@D$Es2&w9ixe^j43$CyAe#tV84N^8egDh}?P7cCIT-`qG!40Z3Tf;L z?l&)#E&zn8_}RSH-?Z)nWB1$sn%Cx*z**B6urq)q1ZJU^Zd`XyKlX$>xNpBYkR(cG z{|!~WN+>F|0%Vx|bC2Xk=g{oGcIl>D|BVbs5IXL$zzk@dJe<)y45%`Qb)Gyh{@T_8 z^d2OeB_x>vauD!)gV^$wQI7z?vgUXPEdfvg7+@z-#(VR8%J=X~;s<(&Ry6)GyC>6- zXCqybp7I)jcRuRS4F4Ks$2S5vXaPRedOrq<5Dmd*e1t85ERVobc$X)TjvW;;91j8h z$8yqMc?4t`;L@#YLR;PfLayas)9c3ylpSXJ?ODXc)8ALj81SH6{=WG1b8bq4Lv4M{ zO~l53bj~t@2>{ASZDLzk@cplH*58rw--l`}*8a;7G|GT=hIi7e{Z}O)#-rvuT)Z6eh5v zY7DHB#%h#*-&=|Vri*ge^Y*9C9o?lDNxj0iLt4APtncuE4pr`325G<_J^>EP)o_@s zl2HBVna7`W4;?)z^op06P)fngf=nK1K9Xu^VJHGXr`p8KU)|A!O5))-;mpZX?%~r9 z>Pd*A@j|Dv=^n@{7(jNA#>kGn|1_FZMO9Qq_i!*Rj5=*?9Ee{DU7@9^bBx68ZGkNq zY(5B=o1yv#0M?s5p>2Yb<&Fsp0)*{O>SN%TCEwx3?J_ucEn2sJ`IcLKQwN+u2h~<+ z|7>Q=_n1Dm18p=oiUZx$M5(eUkFQT0ag}MU`HPP>0)7LGv6AT~1?nX8gZghSxXMeX zoWm|5@%3V`>y*Gc27xeecug17b%ow=s;@V+1b}PcMdbya2rL8W5W7boe8~Ol&wu6? zb}T4+zVQndZCcRPDW8(6~pZ55FMiIkUdYd0C|=W%7%QD^7Dp&*>PF44lw>Q z&X*@2mJ!B3^k@S(E2Z|mooql{n3&7RrXo8_eF=QAAeQ11(-w*R% z001v8-EH508;m+z#@qv&Fq9K6(z1+;D8sq_#2wjg|BPb7BEEQA4f$>JwK(6+~w#si-o* z9d;hHMVh_S!-i`Zlhg7C46$7wz745QLMb#DPm@zS8Mc4!ndjWe!^aa}Icl2?c-in`D42@TFq8r*q+R{bJCJqf z?p^oDnFrlN_n+}$$GOBi7&gOdUnWKOq*3`} zO97)w{H%Qj*GPG0^AOM0emyEJIea+G@NaO<)3rk0g1@}2W+n)0DPTN2zD%#q(SK@G=klcRR4fH!24LNtKUrLU4$HoizxXK}t z(=vwL=Yf@c;2pRIHcyBV9%Kx7=EVK(g{PizS8iOBED~@}A|J|NZ6U8L*G&-sVgRXl zsQgNG)xGre3+~9l`#f{z;Ge*R;)rnuS?`eq6V(-te$3!S0zmcbWf}in)f$!>CjgW+ z?=2MHh@V+Q=NS#-kBH{bOjj3}|F#wY`ngHl{xpWmoc)JZXKO6G0>(0k&ed_vi)uPd z%L8r08oom)F2q96m2uVq)B%=KUjYw0yFL^1$0tCnpt5hG`+H@?$mB3D?4(_lH{2`I z0wj5`_n+ND`}Xd0&prOMRteM=2}uo25$f6g9NrJ|+PN1Du*A+C%p+-jZioBg^DoI4 zzz%PbjiZJHSvFfQ11^|9fd*plKS2Ye`qK}k8dBqh^8A;3`Y&1lGY%RxEU1kTPzLr> zC&<$c)@2J|v)@s}*AU^^EFp+>|3UOsyxaM0YaY$zU_&J z9(6|!c&9)h2zjBkup$NLg4UE4`BIjW%2yadj`JVXB&Uv_5N;&7ngL28pU{cR*pdgr zL!*!%+h5rGfBCi<|7|ulN#iDlM2`3(dmf(^~bCpWcLFZaK}CxDfuZ}oU|I_mDoeTUs6_dnq7F5i(7x|1EiKq}oc)1hhr8n|c!i2-)$#ufL%6VI6QAL&eF z{3l5)NNCn8+Y;q@BtII3cERm0`(1Uve)giP<=q032aYjH6HW^pBak;0R0M!gJoIDG zNMYuDl+XLS5SZZxy zhV7At^i1PnQ+8F>_$&j&M&*3t+=xSR4NP6~2D*hb%k55qaA>!G&tBO9JnJsrxT^ls zxr}Z+GA}s&(hav4w@6mas?yo;K*!$y*&RDH*_Qze0ze35&He+@DH;I)?k^+2DrZJx zC_^XR4jVAQwm>`RW5bv27o9NRo!>P3cc(a8?VaEDH~d;bH-@oBZ`%U#FCdy>=as13 ztsGiNb-#*2ulX0~XVmrUY_pCTZNIz*{hf-*b#eWzi=IiF79c8=!+|&*u_(`BJD~9t zpEo>iO6TOj;||C>pw|=zeiI`sx{tsb)a`HufVNtMPPQiFzoQ2ayT=}USTTeY7`2RV z8bQ3daIl$wf^ZrJ=M*ZZCU00Z_Wola5&$Y(*w)H`fsLzXnr1un$G~P8!u5x2Ffkx* zo3Zo()3H-X1LGM?^%}Y29WazPZkn+sTWQ`kD%RnKHcd?qp$kSElDs22VM0Op9=-2S zK&JVKswD&2$7Kib_ydo)+jno< zq$)Lzqio8&-Bj~dKW_c{|BH`2?v5TlVobOq#|*YULrsyy%D@srMM5ad`X3=c@2YQ| zm-;{Fagb%U#E9o*27%hXvKeaL5l2l&@a#xz8UEZ0nv>07{vaLu6#6C1Y8b(64P#ozP9QneskVUpE=Or3zkM2fy!PPp?|8={qlkeUNI=YuCoDl$jxxjCI}Zt4&CRT z7O(s1n?ILVz|K(8ofB5oIYNnVhb92DkJ0cvC4^tOdEGtr@Z)Z2LF+F#5aHyg(3eaA zP>sWL-pa=qMKV(`f7rjhV7ma^4xu76RNgYSa5~V>sg5;{zRWu?wlIF$tETyr*#blU*13Uzt*=SFDEZbE?h;yN5$6N-|EZdD+6(}IcN8$(R`Klj)JR5`G06ocS{#)`2@`vHjL#z|@{1f+I`tMy#Q1c36=& z%P}(;OOM9QQT+$_Y^jCuJm3J~wxhD8(P`2yUnn*R%yNk{QUP#8^I-7i;3v?(7)X1U zM2z1@_w-@3EWH_&w`kvx6EF1RvH@{X83Vgjn_y42A<$*y&ba!)Rk!}>RX4f!s7{?t zY{G~?rwQnioC00YEZ&xeH{{U!1x12e>;O0mA+aeyP@T}^ma}f|+ke2_zH`UyNj4%U zWYD{qGQJMQ2B5tS)Lo3F~kFx$RX{~}$29}xx61FGyU$XtR-m4bTf0KAI zOi|YBA6}MMD5?KZ*?{xy&=&(3K&4U(_RZ& za6!`yV6OzhAJNbP_69gNyx35S{=yALfDsBpmi98G24O>~#a{%?P{*7^^XoV6y6St$ zZh!zYk=f0#TUvk?#C6Q2`%8GrwPn)+v}`YzKy_G-?w)?+33vVW4bvcb7T3yv%3yol zfopyliU80o&fkT~F)_M!=azd!mXRlp9P=u8X8!^4L~O-Ndsh9QtrIF*Xkb(S>p?1- zbv1s^PSQX3doBiR+8i0Y9nxWd85F;*7GMhZiqj+8_NU28w=k)1{6;kxy@ig3AAmOK zYU8FW_tre}EWXbRK>u_@>a}rYn)eWpnaOEw zjd;M#&CEyvz~MiUG`(T*rhkPt#GrE`jSVS~_*^E>y#_m?DQzrx?@Y3IVh7czqGd<&O!bh{e3zMmEZE(AEK>Mw&9e_7- zt~?rATg3?yhrZQsYZeU64U@VKS(N{(5A(MIrM1w~aP@;LS_+oIA80V#0JMG_8N8q! z0alj+5Ml_}Sf=uDO-NXR=k3|KOOu0Yqv!&qF5{Vfl7=_{NF2p`|Kq4h z0*EhT;uJa_4UEAH(x@7K{iDmS_NlgkP@am~4(T!A)t-@^M*5`pWK0<4R?-4s_=m85 zFNCz7Fm0KjpP;ZQ2(txO&)~cyASfVs*@zx}xTC-aT_vvuqtv~c?jZEu7xB_JNiGJR43f`iNcFzJ=n@$`H`q#VcUQ3j9>uBM&_2j_g0=)}pfkLX2Tv zH&Fnnm<8|2qsQGoc}TMU2r~{dOFC4eQx!l7GyJ3j*4g_{o)Q9T=dVjLl|V4$&RL)) zBb5|_XNFND1=Z*Rg8D=L0rM6B(E~O9i7xPT0y9w@J%G{GsoC zcYBZgdG;+dYXs5v)ZB@?GC&axF=_D%cw0*h+>7;C5RAK* zcDqx@PU!5QRke@DUm5H!E4l_Q0JJuS#{d;&uw0b;-{&5C${o;2N#_0+J;2iS+^Y5w- z9`m}z;M2ct_8-6Ob6>$0U}E$%V|CWJ+m}<>?~cKB76K?*xuNYxjD9lCQU@_2mYl1< z{?yejY8k+6o1*kA0q_HNfsdrlToFlw`X4Pok_WGF3%qM_(LMLXQ|{WWn_@}L%wv8T z-)AW{OWc7A0PVm8DTgZG)X|A&PMvad3k$-J8ly!3h#I0jleEtu)_zKW%F;YDCF}p^ zu4f+rY0JGw;)QL2_egaMS99zQ0yRdr(o#mR24&`-$IfL+6PrBgdcjiYn;1_mFrbhT z{7i64TFJ!!Nxp$8HF;Oaz3o~89dFCA+as`TfUjF*-UAwT0OxMFsx-naRpHy5gt;I9 z5I;{KQw#xFdM1b;0pqkzyyft_;3q_{^xm^8A8|N;hbjP2K>|E^P5Uk%Jap7s|MQ-g zCXo_DFA{w5lsj09>JRy&{x>OD>;E50<#SW(GWZ8bV9H&_J)Y3C0I0XNLthNg)odxC zZ_Lq*aIYBtq1DPd`){-uX<@D-70M-YElk$3?vg)b0lMDhbNMJzK`PI*l9Zd`N8E~R zBi7GqM;Nnp(uBbMf{X!U^?mr3<{IkF$$-h^vyn{9@c5CV?tx>c+{)TYLkkdiWFy{y zZwA^^TRKWo5ijM){qM@EJAU60IYqc%9YmO7{XYl*F#j?z!PbtXJ-s(9 zpfG;bN$zD;I0I;Ui+j5(+()7eyb68%mI4^d-PeGl3T}a0zwIw#1R3izDVN!!Cj?}y zM33k!1GJK7;R+_%jxr`(v4_1Ld6w~sB^b1l;c7M<$-8d%6}1)LY*H8>;S2K?ZL8o9 zAnb6+10+HOyCBfQ#6cSA`(*=g=EO;z3vgE&nHYINa)1ps@8jfU-49d%Xa%xa5vq!d z$WzBpxW%1vm8gz_TT1IhL@T4x6cTVse(V5{fKj`8$JIWOx~HVWeD0|Tzwhr1%v`|I^NE~P)Sr_Po z?To3?;=e_Cs~pZ;sdF4~3v(MloeE_sBqX~5I2g{f!XO4b(ckCg6_B%O^7ei5k!AtA zvzDOz=I7_d15b+m*^Ia4^RONUCIGbKtL0fpxx2dTo|garg*k1tV0|7b3uF=KnEdhv z(js|uQn^%>d9U zrAQXBU=1lKX)y2pSNW;|Q1!4iql{h|8~iW}E_yDxfeYNDA~6Gy;W2>;xVro%qy@2U zfsHf;+i!~}hoNpzsW))f9nTx^AKEAC@1>$n!)7b&UPyxzQ~hVy_SY}Zk>qXEA>~B372dF>P+NoPW-6zdT<&LCRt^2X&Wk->UlbJBkU{K0{ za;=@aWwu4%2Ygd^;k%{*E>ECtqOQKL{$J6SqY2@-!s%f$imh-enT-p@-w@QFS=z$D zNc|h)QisT(-Lms8lz<_X2ndFA?g=Sz4>8Kqm#qvdN`I0fTgz2_s1g^IVdJPBCZ$7= zB#wO#`2-x2A@>CGvdp;V3#qpKS#kOkU;$ zz~qtLz6hFv{;6{T>WoR$_+boX1%k1PJ;Z*z3tGl%n3~(Sd#`)yk;mMh|M+$H_|h&p z214NWO$BaiHLnSnk*oYcY%lFW(X-YtU3{42rMq|BX<0w+-@V5=W>fvIJeX+~4PkJB z=QUx|3;@NLFR6ibLQ})gBWF3N3Q_-e3VcHq&XKvvQ0^41^_}2t7bb|nI|yWH0f4{_ zK?K2kbko^%_H|`m9qiDen>ex8Uv(ySWyg&DyZ``WQg>+shev~xgD=EMB$4}GBv{tIpUqrPmbV*fMD2LUHxIzb?ukZN^;^;IgU zx=t){0-GvImq3`guH#gNKk&9r-(fu&K{LtMuHc1!ME(KS39iy!%-=t-^$XO4@~M+< z8z+|{qzML|E2xVR+!Udrx*^m}Dh=mB)NYFQ+~MFEp+s#|gCR)ER%h&}Us%4>4ICf< z1}$1YxVn9}_Eo;7))v5Vpp4M|+_!y>qxQU4x{Wl7%*fP-)~E1xgs${I`p}IPc?Hz* zzz6^Ve1O*k$d2?y0LUB2@0OMU=tH`P55nN*=NH_WQ>W#6c+$I~c_2!(nZ3>ufAf-EH=xbsZE z)qM>uhYpv4k=joJ=K9s!u735d1WoPk(KqX}8G*Lhf9S8>(&~E!xOZuc?3HkgfUGe@ zy($Y~+(=+U7~n^seG=M6Q+h`4af&xM>3Ah9t9^7VEgCbf>(}p?;JmJ5%Ii8$9$`99 zojd?4RH3V2gP^A%b09<0e-K z2vGj?P3{A^bA(N$$H=9e>nATw+KK88Z<1|Z&Q zinTvrVUTnSg@pj}2B%`gF!;%bcDtzu54p+H`^EH^v?irl7c&h46^!vu+mIgOOcaEI zZm=p{9h(8thb!%=4n473LV;$uQ^)qY*{4ouplODB`KAPc%Wn0z7u?!ApK9RA6(@Al z2&T%3;n3@`35noDznjq3sOg9IyJ_Kj`XT*}i0OlG7=z-OfY=a3g)7=Yga|wv(e8zB z5EA2@e4_EgI;JflsJ$i+Cn`-v0rzz=nVJ~qx->IYt$VJ2cvY@YwFQdh1ZF}iSIS4h zLKzt_Pkb@%sr&c4g+F-4O&wDIf;pl-r7mR1MSrXt0Y8rfZsIn#=oZ;NhcRL}3yDat ze$7_1nuLMne|X2;{iok1WmvMle=y#~cBg9)+%Sfz$M17<-+R`!g_)qtEI)>^F)jpL zfR9X*I{9krFE{#b^-1onP8L^x5O&lKCg5DV<8J@wH{9y4&-qhQ<9vzVH(05y@N`O( zz|rYb>UTyfLTsy`2|OkMmyv(SdVp7YW9?(v0Z70^&_ml}9TjcwqGspThjRcB+D+O~ z8)FhAL%$$@`~mEfAs}1h!`TxsdDA3|hXflE`Uye+ea8&0bAZyGWH16iTPe+PUD4@B zYwn;Vp2G(Z`B)G=ejq6zsx5t{M><;=j1WZBYYbv$plaMAr~iR?X8-+zZ=i|>GqWf_ z>K2R;bo;3Hd^fZ-k=P@4@+sgz^+)|@>gGr8bJH)Ka#If+@@HLxbJLC}cltSuH>ifW z4}uw;zE=ezXe=7Q0xOcP)S6ZXHW;W)X$!$rkxGqO@U(t&FPxTE;EG#$;{&(yi;rDZ z%m_iij)e|+MP-cat@SzKTeE5y|IFh@-0X8F-OLlm+~i)Z`+?IS7@1U`Xg#p4;9H`` zw%I{Se1nSy&Upam^m)nw-)i4FZ2;b_trZ`8j{A1Gi6hcPJR%r1vtIw~hQ@|8Grzm& zs&8Fz>u3w`AFs1AyuWxdPZy{fGyUoN4@mIa;WbVq1unn%od`O5C&Jp8or1pvhjt}? z==0W}l05X<-Y4Hoiy-_?9$bt&By|5 z0hY9dMTK+(&+6~nXmVl6gl04*)}DL|_=xfunOpjB3?uipL?um>Xh}4j9bMJnKnq~o3Y)18?myrT=s>))pM30g$~S-t zu#c?yPftrzrt;R-lujWC0W24kVG}TL0ifAug>W5LCPpG+-92gU`M6fLs3Q`)=i5-ji=`8EYyF*21ly=yqg->Whk+ zHlKO;kemC`gKqY*qf&QkFoO#iGYlAfLqzyV3*cw`Ago{rS(wnKyjX(f9nDO8)8Bys z10!%A1Tx-<=uK)_V)FPNH~q{>vl*yaMPDmFSBF8wt;?yTAe`J6afO7iiiRuWq~`( zJcgTsLM6CQ0x)!V9g>G=9k0|iEP>E=9W&~fvdhbq2FzsfZ*6i-(Gh#%?Y+c`&fDnvOU`8!-Frh?h4}A#yTN{Yj zMfVLtY~SKqME4;Nh!yuq$MhGyP^Z=+wP)z?MF^YJw@B2cCKQ8UeSGKPU2g9AQ*Pzw zAG*7L`;H7ZZ+anxOnpa+3f<&e>PZmunHKCjzV?Wlefosfy*1+pRuga$ved`fL4k%P z>Ys6cZwPmW_XyE+Iu+3X06+jqL_t(N>zn|`iPK?S_K=Ukbe*Y1iLX$ zjT4p(rcTN}LBF||&bX?MNxk!Tzi}(Czvp3(!1DQb`5VK7`d@41&B4t_Wz;FJmbxll zs1wCB@Rrnt^h6O(&B)F22gcN7@jyc!=w6bya*Tvk^1LO_F(luUifQhHl^QrT1de7y?c|d92yq;0L;iAbUkTBz>=G zVve??jqwM7R?eRFPRx9#4#Rum;m6$n`B(qh?b^9$OUG>g!{{$C_-SngghqtrfU4k} zoLnz#6j-_d5clI|?YZ+BfQz?pn6$G)hJaW)PB1eFqZNW#G=SKEU6?-rX+Kew;~von z%&5GK#n1?|+J*ta$dWXqV@E)2Pz8w7V*a6V(^Q#lhvd^>6mcS}v#N#qS!=HI-+0*V z`0Ar(S#AV~nG3T|R8L5;sQ*b5Dq}P%F#QB)yzAbWap|4!$zOP@C&bL^n!K|Fz`%gg z&Vq_mW9BLoqcoK{rM2=Yty@nW-RtiB&2QY=o96|pX4nW|MPTX-X}-7A%VwgQ^-JBq z@ZG1i)_wSmF0?TmDu%eOq53I?(WyLd^ zAAW)l%X-ty2>&*tX53(mB%2Lv7T7{Y4!MH0m*BKIlY?#yZ1p!DTdL6VppQ|hb}RAM zB$()Zdy)-#@?v9{P+v+l``?(f^$nU1(>}1=qPo#10@K&kv^_K-BajDpMuT8{NNp~@1Yeld$$+PipK`Zl>2FSi82B4Q%qPQ4|$vK7f@peAf3sYTQc-3VR$0uySiG*zDHC8_rOiJ z1OMEIV(1q;^uq@Mni(0?)o-hxArP>2Da6YH62<^l$0QMXzz~f&stgZWmK+TZOD0XC z_3r>zqnvb^Qg?*;Yyp_bzqO2YEi1CK!roFh@JAc>2xL=(!P5qb7H;bBlKT3ACeFx^ zU%|jA$3W6xqP!$e8-FmhGJ~~)3W%-=l_|57f#wBzYHXdA!Cu255NJ}9V$6!epn${7 zfRK;q8$zMCaiTPS+6f>VfCr8q7t`0ISp6Hk;Te#=iV#4U5bZ<=2)hCBErFoGyS!ll z0ss~A!NyB+kLnucb?m@lci;YlNxg*;lR_2AA~(|}$+BPQz8fxTA%WgvJ zI9Z+RFi`N>jAeg5=%hVR1F|E4`oqYX{oCNrSUb|0hp0ecY(S$~nQ(Kz_mEro*3)Ki zW?PIz&)KGA=UK;$UwAe~1hIng4=Y$v`eSRFYHncHU@}4^_zfSLkyfT<8zQZMF&;_F z6RN|)t53LTX#;Nm`7hntTc3I%0Jw(;egjPiFk=3byBFNT51w^%pF5*^=&NvPf)@M0 zS>q2O;M)W(;XD=0L<;>lWK`g4W$F)OEa6RFOQ13g^}5m?{A?G1Zx8yB?OnH`^{p@P z#37m$=?F;JZd9dk8CxL(jbZiwy0(66h zBC%c2Zwt%?kj)1I7=^C^=#UZg#0Qv^KLYqZSv+C{NDWQp#b6;GJ+WwKC!g?x(J)IB zlUbKl=bD57Q{#AmQvaQibj5MEML?s$W|Q?CX7N1d2{v;H(w^#RlH}ZsTloIdZs#97 zE0w?0wPDu3PYdmAU5S`Kd+u#7GJ+9=cryc?1>%Pa6+*}}AkltoVZ#cStvEdE+l-9r z7XS3~ZsxfYBDx79<=ZfW`YMzXjh2T-0w=0{8}| zgaMhSIAEh+&>jSRXK%U*2L4T-zuhVry)W8kq%V*MV=t@rZ9}CS<*Q!MFUB)Ny77KP zik25dV=SBSt8`rLz9rUh9#Vqb7nlb8QB&KP-fTM47xE|qu&XWWI0Vub%!VKrkc425 zQF7jaOg@Y|Tk6z($XD<7O8_{2_^4!qRpSp~sTbQXul4naWwI9x1eJpX011)>a9pwm zpr!SJ&+2P8s_gRGiaR3ae_)R+EJ1{+zlfClEMk#A0i(Kx8LlL{_3O9Ys+`bN7NshQ zLF`i$Gle;={WDmLhFL8ktj|awDVP^K2ddHpR9DwD+u!MS{*f5bvnM?P5>6{iV)`(C zaBFqdjGqn?@KLlPWv2&5!DbGDH27>?5*$GksfU&@aN*3LnL4`1E&cnK+%0JZmS2BY z36+?pv3iybSX+LSBLT{A{--aQv7bhtU)ROIH^y#BXlhuBFnuajyx#?wx?U*;G(y)5 zm+CJF&uRnnR)_l7z#wA3&<$S8xL@Lde^-wZ^Ou&X;23NIs(Kk(^vAEWG(YvZGXX%v zoW7CY&QSP48cHi~q$-+DCuC?~Ka7BCl(XeZ$eRis4}tX8er`bh)AJ5d_d+ z*b+z58CVwBCvR&Q)M;jl%iP5uydXozM>QK4gC+Fnc0NMEzvM4)Dr&!l?@R43OMjTZ z^ZbZkYJd6SAH8Tsa>j_5 zE$>cdp-KIj#o6G45sgz&v-xF#gn`nGs915q%1m3*GLDVrUwz!oz4Cyp*URcSJ&;5Z z0%ym!Ri?BprmYY$Fq|XACLs82ry8iGfmB*Xy(?#ZCh0iz7aKd8d1ybh_vEH;!vLiR zltn*<*?-Y|%1jS&x1Tcgw6e_Hqr8%)jowhAJ$C%m8ZqOO&yl~Buvz+zj6kf+MgbZP>h~ z2O-2PSpW#xwq8x;rkWt-oH%ma1ON^|^IpbO5o*cLCud$-G)Ezo!z22v=5_SX9N%y*x3)2RK`0k9^mu-Rh99Z*Pv~^q`B|MjsIiAY5C@w;M)cfk06b2c!a_pVq zqrUL%r`*g_M+U>F=rSq?SH3%5d00CVRIXq#+l1(7HOB9In!G*>Ls<&)QczFwA{?vRxQ1<*>GS)s`KDxp+$f% zn3j6sA0W(Ps5gulK|r(BiTyiG2$+%w!N9xj$x1Q+v}L;S>wAQ z1eBe(kYz@lF6Z{#|3dR&#R6a3kN1?> z`Nuc?09(r#mD4g1`R3zVca{JJ{+Rj0h=`-=j_)iq4Wd;^G1`V@qriOks}I?E?@3=Wc!t9K=_vyWHf|DxC4>p< zXFEFhvQ7xh>-Z#q%r*SVtU}1M{Yn{8`?HM4dTMEb@(AJTp9=l;;L_ouY{Gq1m*{Ku zTk;Eek~+T6o?B}QgE0V_TtohP(pQo+maxbl@*+d>gOI6UgbdVQb^yb?s@g};V5Wc+R%Sq>&`ru( z(N@0Is*ZQ;iU@F|dekP|!WT}v%7H~OJP89>#4jZ4&(s(D0y@=J4_Mw?zY$&-5YhzL zRwK3d?9=k1rQ>by{Kc=ePGX&*Fg7Z(Etvh>DL18)<`6*42oVAxoZH|W7S0>hNp85r z<0cHaOf^n2V33CVS(~(>BdwKAXhiE$;SiKu`M1HC3APlG0}hx;8GNAb4wn{1D8<0JbCs?6Y@@q=oK~fH!VftZ^hanse-iz!ZCfyKKZ49w1=ss zE_)CW0bX#gOM7f>Y?X-$l4n?ighD}fMdlleOu(6V)7;#gc;tR}TV7*#X(=EMtob(u z08lNykbn@skJF^NS53+ZooGaO2Y{d6yyouPchC+2GUCblbqGnP36Ht2AVj~QE{5^5 zl_`~qRxr$(;a_>P1sFRX1iOW4d3RctY9IT6j^Dha zMsmZLg@$?fv+nl)K!dS^1dZ=KxDQmOp6h^s4n?}^U%S&W|Z?okoJLt@{+a=7t6I1p6#9f z@ITbOAB>@0sgA}AW#Eo^?fr`?rc)tS`sV!sB*TfWxK)+Y79*q`hAqs$@V47Q*EH0{ z%HbixdCRww04Y09`WKp9euPj3U27Uc*kD2bq4_KYOdu#U8qiXV-XGxA_^Dr&M?fZA zsQnpF3FsWCW~~%8Rpv^_)o`F6;0GSS^7!Ec2i@g!=iTAm@|6<>Gyoykk_u|y1dk&j z00iK;LR0+YD8UdH&2;zd+9j8UI?zD&028b&hR}~lg?PQLP5gKbfRMxNe_h)-%zu2Z zwEBjdU7Y?5GvHy6C5O9O9@!A+gc?Bj!k_=jquTl=rZ1%0`ggMR%!qu^-31#8j$Rle zQaf44*B=_TYfvN8&a|a|23r~J+Fl-c2O$7>Sd-r=!RXquo0L5OXCbV;f4MQZ+LNbx zR|Z#xgOqT`Y1QKo1F*yZdd09B8jd*YPI%wY6u?9x|G^ylByG`z zS)Z*h^^nnqmx`ZrdY?Eo1ImXNGK@^Ps;S{`^42FwgRmI)xVaTUOw$6 z52;hY^e}#d;ek+)Od3OOYs$h;F#fP^3SO-z`tE22wwGz2U?lpGctoqnUppPu&y)*8 z)S7|nj^@Lk4ya;f3sJu#&sb{r*@q9inJ138m7jhn$z9#My)(Av)cHwiAOO>+4;T|P zjS?CK>Pg)hxa~OToKw2DhVO(KlOmL3Wr0I*nUoo>#8KN=w}*&u!<8R_i+$9Ml2#Ht zn8wKR)gQ(M@pxuNV)fD?Bp+T9@e^~+adM_|8e9NYp`&);rj7#u_5}V8C9?M4Fs$qF z{a$(n+8TkjPF}RUZ$v0~L;gJiv)6WYc8}i>hc$2?3zQV0eP@Q+^yY8@2<65UeN@MiJVKxGd+0nV4ZGnjK3Hb0RN2 zD}EhKMW|n#ciwy28C;M*Cj?>_AuT{fTLb5{Wv_bnvOhhLI9bXCcELNLt$$NG4%ZIN zp-v2X0*%yYDF3R+-v-XYxKQ49zCE*XW(n4x6MS1es83QMZoxk=bEU4p$`36OYqQ+* zKEXpnupX4dGObm=R zbof3JudEXZZ#Sg@kN^N(v?_)10^S%`?OXY>01qw2R*ih3uxn|_?LKrsWY*Jm`m#Ul z$49~=AUofZ3$t>#KrXJBBXpqaPLzQH?O#)JdmS6Yk)^XisQhWBQq zmXu*fb+X$7!RgE)H~Z`fcjvEvI);WW3k@WB{~VRN6Yi zxt%M2TgyQ(W9!3oW3>xE1OeKfcBiAl(3Kvgi+$IHWee>g^4}>F!i5$DXHf_+2nCjo zk4kT8b<1popW1Kj>?K##5{lpcN4yaUg0=eb6}S5Hb9Sny(U1pDt}Z1<3w6Tc!-67Hm=|eLqL{pjb3N01UP3r zLsQL}7@7L8Yv+>Ns~=|p;Jk{X=)n^t2SaO8w{x}}E7z;0!Isw`kQgYwvg4BYC zqdiNz{bz`2=y=q?>HQGhjNohJNoG>W?g53!mb&Z|E#3JV=2yO%mV8*bIBWt-w06-W>go02G{{Af} z-s*48ne9mh!jf6gQ5lV(dmbs*LbxW6SuV<>`oU$l`b)uvA9Xu;NexPy@=LPjmKl?+ ziSjV;Pcg26r%e!YPJoSI4qw`HA_^V?!zyF@_mO!Ppe%(MPia&} zFX1Q91Oo4)I@`Oxf0PJXi3Xu1)MY2P+bu0Dx@$MD+pYlX%Sk8a=-+km2n$L+CE>t- zZ^+=pO{Mg+lN>34EWP zkop~`KLZ>dp}cAUaf+qtrV_+(e$pc!hf?+b_<{Cm~rWg{oUQM7% zWKy>}PKIC+{26tA2c4sz`ln8CH0!oOV03ZHRG1JJdKdNrFIKu97GA zOLK8>Q7)o)s_)2O1hb5!Ep22l_K1&LWeHC8Pqj>@SFHW7{^Aq0sfb&ydJ0I!A%yLk z#vWr4V!5RL0Q@a=XvIg@C#5mw|I?3^uPm|w)bi1lvO>^1#<8FM>t&v@LSCt_F#xF( zrH@!N`=Q`Ky^l7r2|{bt8h$%E)B~;Xx{khusST;IT7m2Lr164KJ|O{ojwLecNC2lA zGe#3^jC81+MeOm1B+yUm&)g?*mG{2gdyP-txP8+M(}Vv6v?ivjOYxN~Q+E~i>T_yR)97^#gmP~H%5f*PkL3r^ao@!5Z|zYq&N{uPy=qAUyD z0^_XUS(V+w#Iap&=E>upXhT44#-j*S4Th9du~2~|hAGgQlTb(fY}=|dsKv0WpI$dc zw5&DZbs1hE6wqnS$TB2K@*w`i?j2@1KP%X0pFHLT0ki=KF)$k#soEW(!)yx5s0R%U zP7q=sLO4f~3cVtyKr@d@0Ql$ktuF*{dtPY-!8#!UW@4w{wa%lL2rm{?7VpXkmL>Tq zGaT+*(8+tM#_HSW-L1d;rL6F;8uLW^P}zY10&qzX4C~;1ZqlEtK_0+G+g-iu>hF=62WHQzu$i5JDYWYK=AtH)%UYd);NqwkxdQEtPSJ{hs!@b>u&$e zTdw-<1-%l%g`udoR65~|G>TKZe#fo6{;pen<0H2w_qp>rJcq4$wqILt+W|TdZTbQA zpsh*>CSYjE2jat8cYR8OMp`G__fDVOYYu{p=^3283M8GFmS)To2om%otg~A`1%u0VibE8WLowvTKs^c5D5gz4mlMqgMe5B;vlTU`z$fB?18X#%1Xd zgFnCRPXxcLa?8j^$edeRaFxf8`PmJF2=}(Y&seenTn@yx=R9E?W_1qpM=FJ}{PXYJ z&Hv*kl7MBD$eOY3ebG0H?%v>@QEK+cDSmhU{7o_5Yi{TFpEE{mb^**3_DCfy{8PTI zt@X+;El$b9enJ!i1~XV$@Z!O5<{5bi6eBU_m?l5+Vb&dtbHb>zx~It?WgPOOT_FPI zs#+SCtGCSinAsdyTldb3?V~GelUl|SBPWn2f9y(Au4`+*h^y*@V6%4aiW$-^BMji= z=+ptZQ9dNQfjM{g7a!{MKQYy!GaEoC8U#OWw)(S=OzX5kcoS~SW@19PqpskQc9PN7 z-~xE%M+y2vc{~SX7cDr>D=CL+A>1)5R830=zz@v8p4^s`t8yVje`(yO$eX&s>}s|Y zkRrh_qXq-0Um!j7y+{0&yp}20E9p* zRy=fCzGG-Zo&$Nx3qk=gCKS050{|8VMTNSetFb3jm)ap_QgQQm=I|AkU z!u_LJaJIt76EkgotzRt3=KY}#l9(qBEo$Gtj3r?JY}*k7_JHa+s#_sI={42{$^|57 zsl*tOUN8SdGygyPr5FYtuv9+ZDVJ-%Kpfy!{uO!6TYc+Ock4g?%+;>Q8zTb-1GdE5 zoV8hip9W6BiewwG1vv-%Ehh}V8~&i6^1`L~q-vgkYE7y7dyu`O|yHv+shblmYaCW$l5eh*SrCN3y(4r4?jDuZQ+9di29L}K zizI>ofq_WgWd`y?m59-u0yA;jUIgCbF=Agg{sD6sQ8B*S!>Y6cY%6UEQ5|fiFErsN zW97|res@7pg4R-4jt5l;{|&1@)B2~zZ~NG7$r0Se?GfL%L@eO}yf9mn-2}^7<}tBB zyvymoy`Y3CK>V`@-;Zp04=t0muik+xf!#O`ID5vWreM1p#+xG*z>(mp$qB;YIIO zq{>*3BcLhNzQhTLi|gK&Hq&D~29ued`89`lw-Gfv?awq3nMxRJWZ(yGw$0ryAJ8y; z-oeBO)(6T$mhuaoh%zB+@J$hjyZ`vM9Kq-aSk_bdRx~J!%It(&{wLW5XnUEJs{v(O zxu_p)6JRM^mZbFnGkJnL2_3l5oIafFK1o{!RS)plP1=MR<00fBtZ3UFD*CBCa%KFK zj3-6x{n5t2AwYnoM_85suo|`pDIT+aoBbmYbqmyyAAJqAf1fhCWmDFB+OGP()c$XL z?01krBYCmna9PlThfFhMYfnU* zm(cEIG}9zBxs6u0gFD=3)e785@H1Zk?RYXz@#_Rz!%Gwqvque<#^32;gk;fwc1#+`o6sO_0#eBN^xjvXrkJNqi;1gqejCZ01%Dq5?ib=Vp&M>sxC&NpJPn=RHFT zlAA9*|6lS!ik2}ZI|*!7M{dkU5@zkS5&4T~NlBiRX*y5c%F4Zd`L3H}Z#;}HZN1P` zm;sY=*S*iPrKI8y0T$reqnW?1?4+1~;f!0*c?MH5*kj?>1X*SY472cSE8UF!R{UBa zpVlM{u!fjQ%cZPK9fNJKrFztOUH=d#KN$R}($-d~QVuy14WK~ZVBD~z6*NTtOZLCu zKSiJ|^UjhJs8f`ZFG|<^yMEYMj@delm=*xq1P`g<4N&-R1lkM%Ab!{QnBj(gWORVM zQS(axkd~ZNbKMWerhx!P?>>bG)stJYQD^LSAqc0+JiR&DR^WQHiZf+F*?fx zVVcN|hG=35iPxlF!;ypn|VcqAeOGVo!D{?#p zE-*ga@ysc=>jy8o#n+y8bJ|X6*7`aX(u9&kaPoLdic_6d*QEuJ+TRTSIxGpK25i_7 zRsZVy@;~qf%wPQiij?4c*r22?&-yI=Hg$^b5j*Aq0@{$EcreMbRlwZ=%kTx7!7qjx zx}tI3O9|Cdg6fB5dwl3k%lFNA5KL%K%sE$k`Op?42yN}u911Nm9YjA;WDnM1l_w~{SqqFCGygTY_ci0tLG-^-M?~DULO)|X zw2ukJV;0IX>Y5p(rmt9BMEX=2Vunct1qpQiFljN3pVi4;_RY|=9GWQNId;>7)kXD? z(Q-}Wu_5e*s-U5TJ{Jj{Xp737)2^N)d-e+t%nFMX&_C@!1ppcp%jpHV0k%E&p@Wn) zH(fCZhR)75w?kh*7_GdZZB1;$g$%qv-xqB z>V@kP2IL?|+xFJ4Xe}CHKv}F!%83!nMj#+ClSW}9b|V<+539cr^G}4CH0`swwJ5Re z>0Go)3Czyx^G+B?XX4ei~5bb6&)|K3kC^y&3bER#GYDW?3{*Tl-ax{+$A&B5z zk1U9iC76oQCh^KxeZlIq#V{qEiYVKW=9PRJFwt z`;B!81qcHienlOyC-CDe0k}k%v(&F+T)|@QeSapke-7r4kVdUKhR{CY+A&gwm&(UV zvjV$hYTq*C*(oZF27}yNG?X$l0YJ1sn~wDS|N!`;$a21Kv*M zmXI%CvRsUoW7CCL0RtZ~gWOQxP)FzsG#c$d-q5%C`bNlGawA=-0)UOm7-r!JCl8DA z0Wdc!2FEuL2=YQap2K|utPD4#4*}-I4^^rN3%^Mb&EWCR*3#9eagvbq|7Y*LpX54{ zJkQ9=lF%9;2{y@Qv*q-3&&=)3+KsR=+dZ?rJKKNY{>1xX&DM79cGv7?dPlWxHdRxA zBnS{%t3rvC_x*T8yo}6xnfWr`dsziG0?2wQ!rj9|d3bnu?B7)lU-k+O9cOEuzsR?N zDmGXeIrjw{`^0=?Fcph!O?t-D6_)=_b-7eIOhT+Iz;w1|c5gm*{flS30D`wWoY+*6 zw}>BHF(6#fNX-sF8URx+M< zHYnuFz=RI8Ld*`IXe$zfJA(v+rsDc#f}?Kj&2yr;Esela*FrNO7tZ`liy_{cFaT^q z#iVljBi4!J`dTtWq=BZ?n+PSWh5Zq^r6x)FGwc)p_<)Bz1;UXdGk%z&?CVn(N%s*1u6Bp33>945{opU*4AhaLz-Q zS3o=Hh`i>M-P|O=LkgZ5KL-KnaHFQ|0G4E5!N5KwfF?+&tG0Hjv>ok_=Ll63AUNTW zf~lI$1UK}WHh{Ll)8 zYzO4x7pFh$Wwx1Q?6iQt$Ut5+nA*&rt$qkqCXmfegq4w!0WuAgA6`d&BTB!NxL0}L z5p!wGx-?6iUEysY!OajzOlRwf^zVuNng{H9dZbJe9 zTLJIfzw3SH5Wl8StpFe*$MX37Q#+G)b_=u z4DoPY)WB#`8R(dq=n1`32{(-cV{ICID~5ah75Mk^oS9S2u>T9QL_R`S_j_qsr-L4KA>Wcps>J$X>*rR-Pu4=34yVrcDH|OB*iO>HF zkND-G&rea{tucO5yli-w6lvl&2AQ!T`&MPn;7|%LLo$|($sYp8N5PCfPX;pd6}giCfU1!byOUGuA|tXnR2H_X}BaH;=1BSEZ-Y zSx0X)!K{iHMlFdv2o5Mf)zwLKT;r!&Ss@y1YY;mC?ATrj0F;?#>X2uXsEV>{Y{OkA zMpeon%PLEU<#19vz?<$_L_l;8i>rJHW|(rpQ1 za?h+m*!xGQI1`b+2v*NH&gp%wZJ^9}Y8d<@s&B+KE#p`Zs>m4SQf9OmrGav#eyY8g zb_rTj+L>sS2yA)ew|`my*^eFBaLZd-LK&^M@G-NqDUnIagpjgI-l=O913D&OGv>&+{&~QVUPX$~O-wbETpDRLPsCWB{m! zWTPNg1=`kh9uuc%2(^uwBd|62+KiAvxZwxNqL5~fCxpru>gn#YEjv1xEjA4_0C7a~ z8J->VXbOX46~hnObWj=ym*jEJb{>qxrxUXV7vED15>i?>9=f$}ozj$&ddp`~fdC*nd68&PdAyEU7fkt32i7!88L8wVQx54Un_WA2Wew?S~iL z?k6|g&PUh1M#Sn}g;@wBX2puvJdP;F?xZSQp?I^7ljphvIAY%F#FgeHy`L3lN-F~4 zLuvZtlf&;apzDvoi5Tx^MWU2!MUDH_BN+9+*>S*gwik>&O$*SM79h>b>?F>W0^6}e z%hJr!T?&89+7PgEtQ+)401tU&8z5)rm@_Y7tyAMzQUFLm$BrDax#))oYxnF%A22FWGP2e5iL z$S_v^rp6qifh7a^N4WLaweLM~>u<=)UWSDI$Fc=DgR~;r=@CqOF_dtz4>23M4r~TQ zf}TxLKr^F09hX)B?Zv4>-hVy9jLKnIHUfub7qKKY=e9gSVs&oDpb>7_+`wy=Pq<3U zWqd0MbC@(TIQ*t0_$kTj!I$MAu7Pp2dJ4W6O5i4`^(!g^oJ2{ZRVoALtMt}^J!*ZbB7*JKNz&Jr@O zX}e??&}g8x?pcny>QX~9c|oYs&jcHmc6i4(>eLC@3%q^GHGZgNjdOB3q|eQxhuq=6 z`Mx{$fBe)fpF8H=4cp*TmNd#zd^h!rt`#-NK@Ev8DO>LO)fqy53{S*Y4bDNHQXJgJ zUzvOfhSdpR1b8M8i+0tEY+_Ib#n`k|yz;@80^}BVQ2~MDuy(F3Svw#Pnz+EayiiRR z^B^zeT{Sg$A*2>S5;{Aq+-;UQuFwjAT1*sGEE*w$6f-1W*L*vzjQ}ArJi^TY5rhGI zR~ya<*yq=9l5s-3`lo9C2G9m9ok^+V>nDSoS^ba0FBqIBup{;O&gDD4d}L*Bu6pAV z3a}D*)ev716nMb+kCun_Kt9n}&?XQB-q8t(Z^-qu3?)%_uKnOOcld9AXxadCqE%Vj zfsk)!t-hdj}_#Gy4|U>d07bMjOgvtTmo#~N6E$9tS>&R%>I0Km6^K%Nmk zs1GbuhmNUo%rQIU&GIL7YOv<~1hl3epMn(_S{`YA{lIPh>XOZ*sI&d6PwZUyY>*Uu z?^SIIy#4%4UR2jG%<=<*o+%6b34B+_k4h`R^SU$vNB-TPc%#&Lg#h4=`{`39ArF2f zfb)S^-(NS*I^i2^{Yi1;XLa_sp?#@kg!W8jQvg?m0S|!~1=bZfhv3M@VU6oi&Z^!NH@RBX)bN_)kp&fXaTT@Ogk?T=>JRk84%I01KF|RTWHC zq!od~075)tiT*Sqf9ypO2-D-3F9yM7;A7pNnSUrGDdwlAuPO$pvbv$0;fhifmITLM zknH&{>PH>cphei$mbvF2T(JR^^8p;&+HPwvz2zrlh>kGp4EN$aFmD*x`9V8Zj0E30 zq0wI*pPtvgbHT0uNar;S*=HOW>c4q}P!3d~P>t%#Ac%6pym*anmXHJ>E+qn<_v~fo zc;_)FbEug53N)q#n3vuO7!Zn4`>(Ib@NXytWHzqoL%a^fDftlFVg@@fEugL;yBWc? z2>^z{Dgt*E)`E=m2{#d4ot&G1e&$aUd<@BLd{xOcg-c6Hc$0pr1lKG;_5r!g{%5R{ zeOVzt`-hL+vtLRRfF-r)?q7NA96z>LA;gKV*jJer2Ob~bU|N8*C=#+woXg3;v0 zPu?;ILbjFbU}xn3bGaaC@RxS54{f0!h2x_^J5I75NUJE1n>Mi^w(`Q+AC=6K6YMw! z!XBXN09zP!Mx+JgpD&+9G=@AJ^;D3`1c1H$9hvYoIvwAtE;tWQctFMM3qmL` zbNb@(*MsvV)EAKh4E}{@|7iwQtr++>|LIe=`SZ`hK|6YPY7{AZ>X|EJIYv% zC4H7?(}qBvO^d46FL0<#0DuR+9#QHsz`^u6zOV9{zzDo&0l=(p8o7Z9T@b?>9u-Y$_RPXC1355$~O|H z`bQX;A+8>Ll@MVIX8>S8pTACP4b3k;5|xY8Ud&RFfEO+VSZ4WCZw2>R z_jLhGrLd7$Br|K?diSbp-+AJW{`L3mI9_%FpbcR5)4*dOvuW1U(ME-IAg5Yd;F>*z zcl&g)!=WVD7@dbDIKL(>fCQQ)xfNdf@$0U2`JU>hW_Y0`7b26QO@D4Yr394iVWVX-=F~KJA5C4WUOE`2ZHIxsPGlgZ?H%HU<`-{&)%3*8b_i} zd_?uXwz6tvksFaR8Wfq2V)?2mb1+T+^STG#aZplKp>_d)x^1e)yxdfki#>2)V0BE| z1HP&kR4Nr_4_B?Ygv3>C-QMNki)~*~e!g zd@7qI!RTqX51Jf1-a5h)J`7wNoDYzOX?^u*0~#BD1a69>!;HARhekv&~L3YPW$_ zfEkpS1Pr4?_0^vF!+X87_x^Dqf9Z6L9?^h+cuJk3P#PAb002M$NklE>1=7S$bHKul{+p0MwyF>xZP_mxHh=G%=P9)wwG22#hcp%j%Wd z&a>y6?!l8saz#8&D^UepQi5Wl_pP72JD|7pm3>Zb zi;gC2hA77Ip}KPRxsQ$0249s8XAp2)?qEVvGndqeR3EHtta2!WQ5dNbbHX=MZ!6zq z9V0$db0*(Qp|ufH5HyQI8_>P=#I5S+-L>z(>IH!lI-<8LWU`R%VCMEBFzh0 z0q%LnQVbMABq81!(di5OTHhZVK`9eeJ#7u_ughSPPmhB{ zU7XiVfmSWHW=KU{?i50qvcI0;P2Z=hP1 zL10^2zD$TJ2$Sl`mOp0yNp5meQ>XSg`zp9<-ZEHc4D0~S+H zbBtq40DN6Q0F69tF{`xl(%5I7`ZClt&=EQ!w&(SBS!(GDzp8yzybUCi53D#41Uk2$ zy4?@1yOlRjyVZAIaVxK$a!VTQaqrNVJ7K4zh5L9EWPn90z(GCy5D4g~^=m*MunoTI zNPLD@RneaDJ=}m&u+0qxJQ7VDfYdy$1A|T;avj+`Ogk*dG%Y$~!nR717o%VBxkvD< zAfPtqhp}%KV4E#1p_K`1-4S;dsl<)oTieMp6kzebNDyCbkk5(hi&Mm=u+sao`Osvd z_xQQ%KG?L~9VUPybcVHj3Mr$Z5H1nN1VthC>n3P3xs!0gQX6%p$LWJC@p<4mIWPZ; z?R&U0?#_5xU{mP^{{AP>fv`jyrJtma4*9k`VenWsc8xbFqB-y5EH)(v z0V5SU<^(-4**saWqTZuM_Tr$9EPp<3c7 zat&@v3~(AqO(54b3z3FMldfj3q`@9{-e@j4+^hF&M+5$r>&gW?13#|VyH8~hOPP?i z#=wpMG<$ZecL6*Ixf^o#i{anYK$+1N(moQN@V*KGAoAB>dTQ(y_-f`G*lH92yu6E~ zwYl}&ZEb0PC;_2~#!JUWPWe@m(ZDcZ>8GJ>xX|?az=e=+2LGi!%Au1y3Y+ivRL6Td_h9$4+pZ-+pdrDBne_67W2O~YK6}jU1)7H? z@JSeOQv2agpiifX0B}gn^SBP~K|4U1>Ah(NsOs>Fwq!Rsgv|RY6op9~xTsbh8jWSQ za`vcenXp!%kPOC{cxgvEFOBdx$5I^)05i3IU$zUh{|dvH+!Vq8k^KT z+>=|`*VV9~H%gY|Wpv%K2+X`V@G0_&j$DEnsF!e<#7*r%NL23D+0aoFB;@!3S@)4P z`%v;v%n!V5>&Jf#05updBCpIwICaoE80!dWW(-;gdQWU7z5BrpTNYTpc*?En5FU== zT|RTn4lIJm1|2KM5X-SjAHzWzE0d))a5~&ClA$CDxl35ph2`2A*D#1 z`H1j!2F+g<0y14FQ_9TBp4Zd*Ov+!u>NoeCDCuh?Z|$39xX%0pEb)RLf0hHLYXKT= zeSO{5*JIkTSNNcV+D!c)^TL%Za?PpZV_}W^Aet>i_xXZv$C)<0fj^1GC zFKbOaSX-)+L4pZfn|X^rS|4AQYIoYYu8~#6AK`AE^lC=~$6!V|={;ms7O04@jV*x8 zRv2{JZLgB#Tg`#IAOv{z=Scaf6ZV~@%3OQ1(G0XcyJhRwO=*6X&-vwox+uN{%$g&gu&bZFSWhTAN|pA7gOn zqYRRfFM~z*u{w7u70>GMP1RdDO`ZwNlKPr` z_vNvRj(tgMfxU<7@HJblk53+VQv;jrMONNWI~q>v!=zR_D<2G%_=qNDN4%7j-Ie;2 zHOWH#+pPF<;+wl+8fbIqT6$ex?ne*nnfR^&xDtKL|x1ZPX1dNx{EBKg%|@eKYVj zx1#Wk?1~Pvx8Q;FywlYL2X$=zY;|7F83ey)oiDHhQtTqs=BdlR4hJ63i6TE)#23F# z&V#ANXK7Hz>yV$ww}`6F>kzAk9_qweIc%H*_4~`}^3~_(1P0c_hYl52t-}gt=!_+K z!S?RveJJuyz)cgYIyA)cMy3Hc#*#4KCRei}$i~L0OaNfE-)gB7W2==qK4>=n1xwwQ zW?+^RI(qdhl?RY9fPq*YL|g)3<`+A$%5hc!&L9nbv4~YmqX6FREW&TdIC4rm?coH@0o_y-YwD9u0cc)&%qf)?U6JeIwr z<)F@rvZ6Csm}Nk>z7y&HPr zi(Pe(K!9boUbJsLc3VID#69^>zj52|er1B98DVLlHoxnXCwb;tA~5*3-@C5*mkP}g z1n7XnKaR%&3F;Uze#|<}5ZiA*Q~MmBy7(6~0O>LVyc5rsPapL{bR|ee$te!h4>HB$ zdrTp|72~XFM`;~-2Bwu^C`bD&91LP;;P@dhv!|Y4YxJi<6%7>M5Wa{@8q%i&A$S!60vleXEkbpAR}<+q zjRQofJJ7?-9U4-B zJAVKuS^!l}o3UWDq=NF+r~~@uM^Hmbk;Sa2r@i6br_b#OR0Eo{+8^9h9njV;Y0(FO0z=hNAuJXO8W{Rp2mp$mQTRGn(buS5n=d|p0z#5J{Jp)G$5#WWWADDvMHY=~9{v&{ZSuo>S* z-j#f`!J9!#|E)jN;L~8B)YW`cVErFmgDaa+<6~J7ZtI7z`sW8N)DsDcX7I^m1)+d; zt+r-Y%H42;`csnrS(0N5(1ng*cSiHW@UPIezZ8+OV|^4_SqmXATo8cZXPN9VR+#_* zaP`gNSw~~(-G}!zH;On7aczG}2>@*wtBnr+ti$-hUyW1@g89p;2s_dca0I3}>f39~ zl64SPrQ(}%R~SmUpsr*eH3S^=0LiLQ-Fq6O-l1aYg?x9r6Jd|O9xKU5WCc87TN$Um zJ=t+9GCW&8f6NYju@2fd@d~--g|L-3&d88VIGL4$o3e+NNLLjY;T3_|2|N^zu53^` zQl}3Y^Y~b`|3v`69$=3xz#hzz@I4fF_2dcl6jC^53cOU3hpZ4Q0uE5+mBAeOZD1_=>beo~b3L4%DgnXyzs0H*pIb}KAj zJn7mJX!a$jtf|g%@R^MUG6ApCRHM)7w*vQ~hjK&s=5VK_V~1Sx2wO|#4?w$d@bO>* zCd)G>5P0NfL#2K@t9%e8fy783l2I5)IWoA@)`Uj+XFNKjegCM^ed!cLptLNP4g^U`He?22hsQDj{)X`iP;i^2 zbUXo0?k^HL%HUVpxdi|WEtvhMTv*lF;r^ILFa6TcX$WhoSa4r{kS|GmQ*dQQ{k{No&<}NtQGOc{3!71>)EBMRI z@*?xB!*06u@4f0;AK%geI1)la2U`H#^e9Ej$e1-}nMC-J=X&yZJVUwQCV}<54KszZuDgD;)n1ZErF#C&w7ZDS#)6VQ=4yHx#yn#mv?<# z9ZDF|*LcDIOkd*;%hE?=__wMV^-P0*WDFA}mMR%XVkTAPSR%>BmRa)=nT9PE_rM0Y zc+{5Aa{uu|nI23Unj?T&5$}y3TKe7(linbu3}*hGm=$?dYBq<}7G2E*{1QROJ&|LB z4xJ1+jv1bWWR`DMFdnou_>k7CEQK$Ap9SWksw}W+ES#y06C3f%1`f~W z@>Hdb93KF|5O3}6^R6ieHKC6)J!QQB+Kh*K3yuU&G0LR{P}*_TaFSp!7JgceVW*~~9Z zs|*WYZ%1nVsX@7vYO^N+w#PDnb^L>z+stxi{^cX>X^Ur%J|)5fm-?n7vPC0xPK0NAVVXMxa|hCA$b2`ZmyrBE(UTnY}ZkH}$>RJndc+ zr|d@@iY@(Fx+D(i!!Jv3?OBN=EU*;AnY++ZibNqM1D!(KXm$gThIqOgG{@-ss|-wkCgqoL(jPq>Xg z{g$7Bt79;n5luy?ATS$9>B#W8FW9KeTh|{3`sd`K;IJevg%LM9{I?FE3l2TYIdWr8X}=Gowy%j7>}=`o%(#$3ht- z_hsw*+Ie6q4gDYjH-Q11@+EMc`18?*{*tt48sVa>?M#+9R15*D|AFObX8%j5SLX?0 zSD!J2HU|4+{((gwLqE#9c?0brmWRR6Bv9k!qo+@_@~{{qz?nJI)%nXf`H2CQ|6uTk zCw@C1LV^u8;vDLX$MOb9=8R#?0weQ=7!aYBxFS^%1h>I~KvfQC2Dqj_qL&V>C6z4B zR*XMV&ky)i9bOZ_00*nsOOB^GmD9$byzbUCQ@16WIR_TNTA*B2#&q_F{_S_%iq;@) z_Ah)5*8iyj_J@{LKG;A5PV&U;U$zZ2T#WzP_^^-lVir`uQb*R5aCGAT`g3>s|Nbj? z^zVM)aN*0$%KJpu4e{U?_$LIk0G9D2&hP-4d>Fm8yosOccUHf1-qyOUZm`TX1@90j zkB;W`=OK7&_>h45AhoCYod;Rj zAb2b_pb4-t03PINa>A0+k-z()d?=jqj+A&0?X&O-crjSEb7)Rpk=LR8#SA2p^{9-OjFF6$QyjD2f%?j67ehdS#z>LB2s)omT0DhaGa$ssG#h6+Ah*0yc zU=r-8RuN4Yxfp4A(GT1^-#K>gJayei&)t$N^BuDrIkI7ZCLiW^qKeXoRaS{4B&m`1 z@ho@aiAn2+|K|HB`z}B>AQk~w zAdXO4`Ku0?<^m(qOn=&%I?BuN=bex2XiR2 z6%mY0KK+C8!xBJ8hG(nma*Px~8b4(G^~!N~{2XXRoFXBM zCPrAAA%+cbiue4RJAfy`4?G_Lr#We7llWLzV1y9hD8JYgSy|I9hy0kZf=jS{5rNmC{sS0Dl1j^ow>* z(rRCOV28!n;QvIbY>ZG~lJSX%6$A!f;K45rAEI-=x8+vkoBrtk`BS&~_aA9jz(toxsA~aYh zihvX{j9}hHps@9OLol3t#;2~_Xmn@T9>a`p&z8224Wi759 z55f~Oe%_H?@?y`rpQp%4W0Qr@XGIu-egDZr_hj>_9at24iYbJa`#h5Xz~JxInDD~y z^^fSOoaxfQpf8dNRh9P2!+Y-DgZn;u?66|u3a@lf9LGw**r{`@Iag{8R3Wi@K#6w! zYkhggt^de(^nGb2vxUeEEbs~1nZWB>6l z-1hrd-OeAbJDbT7ABi;#Y#Aa*VW6T>s9foA5F!r!<=bxUomWD9A=2RAIHlQtAibQb zG{f$1V#Czc4*b#53ADCuK6JaE-_?LdLz|Unf~8sa+7DiFtFN85JT}-h!J43W#1=zl zvTLuOvESjJyeUn?bJu>TC5KzuQ-AZ38FY4K496g7vuF4O|C;K^B~Wl+&`=1xgh%Gd z5+_X(&QSTUzIo2H55_>^Y4C5Hm*6%EV+f#spo8yz{b(@whxU;ZRh@ORqyQdB)F9Eh zuYprLE4({kK1>SwF8(77k#6M;b)rdV>U;z6bP)h52n=FvE%n%%vpG*nfa)8DT#7icaJuxYbd&^_Z9%i!tj?0bA94|2hAZnCeKk7PHY~tgHMYt^3JS*Z zLuzMRqU-?J2X61uc$Fcq5|C)tVAa0y*tNdC=Z*5%%T6bCQVmJ;+n_P?V3toK14CoU zfco747yUXtTM+R*f8?*fr%v;{+hOK(`>{H7&77WV<`#_LtU9U#p_75n&C`daF2CSb zr7q{Jag&rmZG)Ab<6s5-73Bf&S%(Nr*8b^~WQ)SAoC9}?4O&1-f?zGF{;;KtLvWzi z4&gZ=t7eul#3Qy)_T^65c58?k>~i2lzx8il(7LqtL~9+qtpSh$$7ZRVna=h^X2l3g zw2RpCg&I1+h4Ctu+f7agl#d1zWZ|6#GxOhYR%^N!H2BAkNwkffVJThrYt8(%&p%)7 zpX6EaG64WyLYq3lo@V}N06ae4PYH^`%n;{RojZ@U4u57)_9O+52Gy=q_mu?Y5uX4E zUn^L=Bg7c}f)3wQ&;1~^2TUz#IK98vsK<5c8Tg|IA+;jIkr2zbRU(KsErKdj}~v zn`?@w0yQ-khP@;)yo<)RX-w*h^?@arN84p$G87ti3gFYNEqD5eCS;<)^R@(@A^3YA z3JU- z5}5d@16k4RcjeVnrZVkeaD}%(I#gRj7CquMv(+UzWWnIa_8HSrQqIR@0K{O$PJ_@( z_>MFRk`?d_+JQ|yV%Nk&9jc`I#@4@Gow8^3Vb-pIdaeAezH`>CD(^P%q6(x8;1g@) zYZ3w^2-tQ*j1Cb9G*EJolk4GKP5d~dV@D+rWI-JyaBb@v9Z2AbeHXvzJP}&06QzS7 zz()dFnzJ6K)dm07zK1esZwxNZrP6s$ShWQ@Q^3#fVj!N1>k`K!h!l7vd zp+j|-vK$xS19jadK8l~x8Q>ylTAfbIavZ#A?<1`f;&G~KLyp2GZxizXpbPDV*`-9- zcGVv%3m(!;O+Y;HlVHFnU;;AY5NwtLM}>lkph|oMEo%SOW%ZdQ;229(Pa4N0NFw~x zpQVAeYwb6EIm12R7!yq}OVjmD{eSEEQ+M{*2^CRjFKLvD?`IMKtfB`3hAW^7pT?3K z29n?NZFSBBqFM#CC*Ki{O&{VEy-=T#tc1g6rl_4&n2!En9En9?FCrc^ivITHd)mHs zU+d2oHOthjmH|e>jL(=dSrNaCC3Fn`NmS%uX!2|*`-jpzc0Vgwl>K}QRa7z59ep7=j z{0pD7D|k(8$cvjTKfr%r5sdFfjDJr5DoO{nKQn*U{HLg2f!Ne+jV*?jM;NP@TsktO z>{Aa`z?~wQa8Eqyv39lK-(pSnI5trRpnxz-N7K=LLZ4tS`A{yXJnV?&yN_kNao8;% zmDVh(S)nIG6K;4Hc{vWV*pVb0#1aUb014+1=s}Fx@4y8x0wiYi+MDbT6ESdSny>6j zdZWaGWyX2*htwyfi7>6brqRpXg9<*G%Hr;b&%4tNfb`<6r%nXOH` zpWZMPJe_i1>h}gSYTe_{zfvs#1Brg7CGnPO;+t@3{mUHaAbjXx9bA5z{zdY=Sa(Jn_<==b+!e{1^7HvJ@}h20uAB^TP6_zBvrSy_P_PP*JcYcSJe`+3{I<3 zT{G+LBdIe0lUee@ER6gRj1Rifxrzt2ZAd(@4mQpdFc@OJ8}DKEPJA4lRRGIo1>z%v z7Bf?6R;=^14jf$k^v7uMSCZ`sqQz$Z@82ji_yccAvvFL+T0UYYE&igdXln_9S+J~r zgbE@G2C;-?kd}5#tV)<-=AQ-KGeEP0qT0bo<)sc9n*clkF3a_;Evul)rHrx=@-Znp zY4kyfd;V(z)0H*{jSgD5azR7@B7%wc@p%L|ZPnWAP<}=tkydqVNy2HSj2U@g~AGN`O&kEiFO}tl`+xJQQpN7~3 z(*aDYL)u~Y_-R-spO(I&?=uMifdgtsB0LG>DiiKc;iIWCG^4zF>xMG;*quFb+N=ID z7{Du8LlBmz;8Z#>mjM##m$g*uSgnF)2oLs+Xl2lEeR{{Wess?)m`ww~0CyI{FUSvN zP+`8Tlc#L0e062UiV+My@G!$=03$GfSzKM1T{1{o2LoOD7K>BqtbW92OI*Pp!%ble z783bgtx@aBV6yezRoN^&8O&y9K__@PFIxNVS=;Y#>R*IN)&q^#Zx9^bAbV%i;aht!>oQJ z#pP4Z`ga5X_(xuiLhQ*O3w3O?FCL9&eP7`B&^PK(0X#!#COrTyt(U-tZ^B*p5UEM< zr+h;rz0S5jU!YTBPx~yoSz4suLwiP9(2l7yhzL76aVE0zeKO*(=I94HSZZ=RY8=o<4Nk-Fs&t0QY3efJzFLu>Ki$W4RT?0N#u!2$t9%*%A^7cHzU#K% z``Uz;_{9!?UHe zKk>m}_}B4$UPAv4_r9G!+$ucylOL9LnmR;hC{Xs&__VZ*k}Z34Ev1ND2HURe2k;BO zp_BUHPYei-Y_sgjWw6bvl5a6iscV2oJK|@!^=?V^#MZ^7af$T^gFh=nv{bNJWZHnH znZ9k6(+;R?ue>7m?O?MIu@sRco?fn_D4><4fF@d_Nfjy`lt+P7_)I-A8vvsv$&dK$ zkF~B4*(fNXvvX+T4usL~vpz#UV+d^mMsut|d%Voe{KqxE#Op>fd9h-RC2N@tU8*A(C&cnM}yvUV`6zN)Q%2pJ3(*hA3Iju*-YITFu&&Lbc6i5GN)gIMG5Vlw0X_-J`wqI=&W zYyOsAxET0WG%(vqf6y5}=zUu@D7&hAGaLU!Fd4MI+x*i<@oXmee`b zU+ZtPvgIoeLoaw)DtI#W7hW6g0M2+{Ev`{QIdXpHqkcz$q|dZH+C6?`)UViJkLHsd zS@Z*F0AfDVbVcv!znDm+Wh;0)tsQqqlVtKTSpY~A7DbvLkL(l*>Ec2iCE|P<}C3MTrk{USmB2Rxns<{ zu&u4~K)9pwF`?f&lYmPX1THhSoHgG{J6EuL>W}Z#3SJ{77)w2m&sAL`JpmpxPU)|V z61BVhpb3N~2?$sgEYG?#xFTQmyvi&v21IR2R*xdih%}$jQg0Ef*H@*%q93dxKr@VC z(ujvtz*;`^hdvHlqqrcib_ckkf?bpn#0^xrxIAP$4haLj!JJPgysL|gYmg*(@P zFgFF&B%~6kVq+=2{oAj+_cA(ndF*qW8uS9+{D8>AH$0H{*wDqBK75epL_F8EuHAQ= z|NN;2RdtAESRuHaoo=UqQGb~IqhA{gg!bx?z~oUzk3R3k20erA*?Yf_ zYYbJ^Wx9TIP*ken<&IdXM_?a~1%PeIG)xC9vEKQp8ONaN3WPr(r&1Qm;(>0FvRyv5pTc9Z`7ZPamZ9O|%@w(?}lqld!{C6kGrL znFLNPo1p?N8{nPVA%p+6mT1f?V&o~XK`T3iRRk}^JBWNXGOhf{OdWwb7AtpzKl&Ws z4_^@g_73RM;$Xjk-;U8Bu+;Bj&_@&ReKxJ!D6qQNl%O9N{v|J~xMh6j_~g90h%EpD zz`Oyzuq?xm`lRJm26{BTU*Ec}UDw;et|KyVl(F+7&QCW}>xSVz$Use%E=rzcAUzQ< z7Zv%Ciujs#0<^TOVm;IF9%quE&LB_(CI+cf2d`U8yf6Y)VS+?{_{(2=i^(NwQI1RcaWyeNK3RN zm4EYOm=$BG%W{Gcd4;J}u!ZM87okY$T*kM*w)(Lx3E{6GdCSTlr!%HJwZt0y`&v%f z`uXS50O&BN*h%FQFyl*=iwUyYnz$>&LO)v=CK!*0IPozdZbtC6M>kSc3+Am~MF@=j z;OKQqT4>s1H6H47j{z&fOW+ilT_(}T-Xh0k`|eXb>HXN$B|fM;SXM6O?>`Sc8A4mk zHfY*Epyl96zKtZZtsH8s`f-JGL1qt}V4sn?2cE z|9nZtS?Zh_m{|M1#r8KuR%KVeZ=4W7Ly5Cc)fuz+*U5#Zic@9V`rTz2{#^-H>Y*-) z6|)DVa0vi1{K12t*)y>AZAY!)d`M9A3D}Xi$`8ZAV-=Bl`;zXsRTD z%+4FU09flzqO2SOjXci~%$_8U8fm)N!>91M&x@O;D|qiGg#xHLX~v=$3}?7>BRsal zqaeJZ(HL*m)GyW*%%2BsfJPAW1JBS}^E_{0+>16v0fsnd{mo-o^-QlGGJq z^(X}2F9XnMeZc*1JRAtFtf)W+S^~;GQcec@?%d&{QghsJx9%|m(08^|AZ=N+7&k(n zj}oB!hjc?nbps`q%m z>wl&DO$`LFP(RB%`S*-1OTzx7w+@AETFM9cq9Z}=|FyIpI3!Zo{Oy3{#wyC=P;KJb#D3R`3L!5>{FgUN(iNr(>GEeMOQ1WoJbiyEV}|iV^z(x7 z{a8Zdynv>r8-rua>l7UQt9KonzSTYb>z?#tGWDip@T&g+hf6wA1)Qdxkk5pw1BQRg za{F6(fM0m(lmn@uK>paZhPJItfbYOJwxLlHFui;5bK}0AAHU5CHr#zTGsVEvC>st*m0> zofCuY)QNObe&TCSTi5)t%X&`A)Fuf5_nhd|>=FR9lk_xBRuYa|)aJ%#r9)l1q5a9_z`sJ^F$fDve!D=5J$ z@WyBa08XltQHS4kz=wk$OxQxgO~}R0LZ+Bnn?eY<@1Fhl4_xP#21VACv>aeI0T1L$ zI_96sh9JZ>fpcJUui8z=^==ZxaL^E_RFlgu(${GfL z^1+X?QYEtid8mNMYtC zI!B%GKV51JsVGti4UA`iXZFtk&#x`3{zgM5iCG2kTQi8)(d>*L4al;@S}x1hckgI0 zmJe;!RhZg9;v&Fom|iAO|KTs48!B7{RG!OPS8UPA7$x@lK>tC?T}lKkh~@ZL_K5C^ z->6bMU)^(0|MR=<@ZWvkt-f=?HSpJeMOuL^t?}vDH4*d? zy7r52>XbSh>5IW1MxW3&N=s-r816;qI!z7|iUVsJpSa>D=RBbHfBp|r$IDTcw|XYO zH3w)R3$h0=+{)A8zy7X2cG;HcV73p?w2ZF;?GnJXZ-)~p+3^sBoEWdRtMN5uUI|Qs z1vt~rg;O6n6_Jw*ZNGj*W7+4=fDc8nqCReD3CH>l^`|ToaW+E3G}-~4-rFvhIp9MF z?R1Gcm<16|8@B#6yyUZf93^c6UK%}1H~S5f$XaIq87bloM!>ZFSRVFG89&&Z~r*hcSD1tpNa^j4pnf^Sp002ld0L(_)ZK<3U zumfndcIA#y6`77&&s?KTK&R7|mH;6@Io4SJ)y-?}nfx9f-8gJk+*S|C-`+eL{F(mH z!lLZ~sun7xxY>Mx&IGVEfAKhv9ZhTj8q&I`qr}B#N6P@u{_A`0@c;fNZsW&qD8EW| z=eZib1g!I_$dYF1?5S-w%YZ!xK@hO=)*m_}YP&s$l4hIZLk;@3W#FiK(~!W~zW+?t z{-3!m%^1x&2YI5bs|es0C}uEA44S~kk(3$2!7$M%zV(<1fFX<+@<26KM^w8nR(nUek$`oE;$Bfvyhv>Dft(OhkOPtP)X{6$P8% z%u~>TkEHi?TSO_ZOZ1PNN1$I*hNfsZyeb}|l?ix4;L0m2x7jm9|I1tpK?5Gy`5jt5 zbY|Njc)*Qe_K5J0>D04-$$yj-iy-~R+^&=xcKLPMRA zt$Va7WDK5J{wBZ}{S!&3|cd)h8yJ)fjW)NGp) z0=jaiy73opN!Zc0M>z`WYyGcrN*yXcv>`!ir_-@Mia+qluH4idC|@!VuGTq%l4pXJ zgUrgGV~BC_Yn_H%WL}a7$)Ei}{^aFEN=0hHG7#>oxByT5vB6XD)P)X~jBr|^;5#u4 zT6xkRv=m&8f819b#4Gdg_uh3s^M@bI90)2y9OpC{P_S`fFp)rLG!i95v$*k|PG4;e zCqG;N@~JucfycJwQZ+vf@svN%Wo5739RK>c9p8NTZ@=%BUpuAu~+3SLhMpwCqQ z&{hzR;LI5#+OjeJQ$3mlljjGurV0E9*%fI**M zY9zuvc{{Ye;jZ7kr9*+F+J(t57G^Nx0*KAB=A+3{1-^oeSyBIxvZ&c4dyEkRT3_4= z9eD{HH71R-|4ArJ1Jl*)p9%_7;jPL5KLM>T@47=$r>=eX73+vhh5ksh>Emmzaaxis z{LnyW^(xjbwa%>K#R>=?gKX+ zdpNL3gBNOUgaEFz1>66QK*>7|a8|#8WBCn8^~|f$AZ{s|(gK$R!aWdHJugkr;ucMv z%7uZQvavGaS%bZm4RiyLU&L)V6U6B=@zpN^pQDuVbCIPTC3M2G(JtD6(UTQ^L~luy zX#~c#*H5eORUX3+X5f&<=!9o_KNK{;v$Ozh-cCpTH-aeji&F) z+wMDWyzPwu-dOt@B;iv`ISeZWlbGF z&Le!srdTvW{W>?a#p=KO-nD=Fz-|2GTgvlEeNrp!P*_XV+#*L6m=-%kxoG(Ltu~H z4OkUB5`Fd|yNNIMUf~nxJ;=wy>Pcn6rKQmK!U#!^6(k`ArtgBX~>H&0RjyT3bZBhX~;k^O*`eDJTUlI z=I=ijpYkJm8z(jR%ihHEE6F1hRq9LfDi_Sym&ycng}O`y0FEw%TU!6Wr859l)u;Pc zst@|k_EWAz_E+u)A@AMbYq2}^0_8#!0+`{qWYj~hAdGdWUJn(D$hS;zm1VNluy5SE z=Dxb|wfoz@_}_@=B`cgG5RYGA3~A>Iq^ClH7>P@?A(<~9s)2;pZqFejXMHxZLlDezGKwk9RBmEf24u?UFWfGQ4 z&TLD_cbxdRgj$0#N{exb-iRzl>rQC?DO#hNd*V&0W{s+;;8Tk6r7lJKEm+ zSi5_k$Tma9dr>nQQqOX+{p1pUy3+QTsz2gPfkd7~^0Mv`-c1NGU2YTsL!e>#L5Jrc zBr*2QD*(_hu4)OT-XNOc4^Fb^8_A!qK31mYYTMpT;maf#=g?)vRpdR7J3 zKXC-J(f|NJ07*naRNDz4do>GINn_gZZq^IPtn?&|Xjaje1dT+zEC(Y?+TUsZvFRjB zqM4PfN%0Ymip>5~7|YESEd$_;iP1F#@G**!Ug_wSUz_y^O;^_b zyPw{0+n?Yj_O7XIQBPOuigpss@tbJwJl%2~dBTHdO*sWxeeRDtA0 z18g5&Gecu=f@jRu*LCF?BSL+$x~SWUeB?XDsKMXe(w0Zc6xcA6%boy}3Hr!);xbbe zZJh_3I(X-rYkhvl1`-?iwRP0bBrVaw{S&gqV2`09o-cRaQ~mwkITHj{-Z_dHjKr@QZ`ZC`8y3TjB4yg6J${T(>+PCzR)~<8sskH-B`73MG z{RWewp?u9oKC)*|xOzHF%-0Y26=2vkKBRCh6X}cm`_a7m%zGXHR(0Q&U!F<(pt{-A zuctE0byk#*!QNtXz}7f+K-C(kOK6MF%N4cQZim%HRWb@02-2Jo6X}oc2ojDM>9>oXSF1r0;Cj)BJs$K zTj1X;0ajrY)9PtvnueGyW~C}&I?|nAf92ZRHn{TU8MpfFvu;J}e@#5z#f~)2%GFT& zrm7Mx@Qy(&%x+E1nc^9_HyLL8);%CX9zr5O*MVC%Enu66Z+>vC`j z0~HM=?c9Cph5R*@E_qhN>^_tLb@!RJ%3YWC=(t-I&sH=OT{(B$3@zEZMPuz##nEP% zH%2*SLP&sz;tjdCq5YjqzyQ{T7Y>w@K5yfwb*k8oM`z!`&o5ud_UaV>3pR9*?Z zBYxyL_Y(Mf%4c8NjhKcrO_<@@vZbO-3xH!M8Ai{w2I!{^OIzGHt(mChJz(S;OMwXe zWdnmgOjn)2J@V4hs!Sv{-IbePyOop2M)(2|Ag?!wqG|&jBF=&k0+yHk?9{sq*6g3o zQu+GU**DzpKl_6+e%)O-bHN0F1`K8fMIhg4A=t|q|HSlk!rB&v%GnNjk%ZT|DdVmK z6q*frP?VZ$o`AQ&ZV!Pgr-H(4f(=Nj<8(Z?pUMHtRcQi_sS`bB0~%J>O>Mt23wV}H z7=Y3WAHYPGdY}{j#(Jyrw)!rqwuiPsJ22X})d6at>Y(P7OIX{}El@oP1q}XKM=}9? zt)Iwb!Z!@Ln8~yxJh3cc^;?EQ@NQYB>8&sj$;Skq>r5aJ|Cp_KW2%H>;Wl}bpAC2i z)9gx=aBs17FQFO@7RFtujXUCJTWbEEjORLcr0QjM!NAGD&vFuZiTN=YFVG)jmi{0( z>Bs4WyE^o#_3>@jIIK0{3r9^bV~g*yoCUE(&b*>eG&*jPuQ zgpl3(8#6wXP|LCvTkp`mbsnm{sm;;u&~kNg+l^5lLGVl%l#1UkQHU7mN$ItSCH@E& zTAy9{ka+U{{L;K!HaY7;eB#G_L)%_sT`_vXB*g9!VnZO8=1DcsZ#EUwMg5r3njk~| zHQ<}ZK>~44JTPNg>a+631gF`Tme{Q8BjoW{mN{!R{7X@5!hw;rIRJ`-59J}+Ef3nQ zX6-UfGs``bsHg#;O*6q!Kl|{(1NYIFm)xtzPMao|{>2x8uUEq;^4<-{nDuGCQD+2; zl`qW7f`4deU~AGD>1xh^a0sM~ugIX|>it{p!6O-X5t#YxNp*rCx1lCfhv4332imr|Xh51J+TJv#eFLSma&>LY{ljG62cf) z2097o6AyU^0xVle+h=(e5RV6FgTSEP?J*IYl4pHh zRhwgiQ-6RjLa1{xOxwwQMz-!*z9fH)CG!N}F}AVP8e!`dLco)UkF_mcwptn!#W}r< zI2boU#-DgU`0l^)yq}lCw3Z+0(Yb;FDo2#pKx+ ziSIPk-r-SS+k`qdG&|Q!l67e7BsIgr7=}(0ewigY6?j5mhGv}vor7ll81>=Nbepo=tYBLtAAt|t$2=RH0MjD)KTKFS)1*B`3*Zg^=%5x6L??oe z2>{{`6BNsr{-|D<08oYucwzNUbdOs)$KtJ1Tzw?|%A$`2#q0Ok35EvS4C!9+q;UjmWk z)p$5(*5IKA766;}K!-u0Bg|}AMroN7%?p>z>Wu!_Rn)_H$wjda`--TZMgvW6Q+>-b zMWh^vAM3M~mCrd#XhX)d;}}ADkWN=C56Xp|AOxeu{`h-tle%tk_UR7-j&i7$2=O7n zv+y|JTl~nF$gZTM?G8Tey$jhk9!$xzUkVs+5QyR5h7AAWL1=rCRrrSdW23}rv1jA} zh!FY073~HFzkyFa!i;~ZGx!k+OH#0hCGwr^E%%#G-j|7ib~_2zRINxUBJydjNC1F4 zu?BelrFDRNcnKF{Cf79ZaWwGL&1de^@l%qjHA|#nqAIh<19>x)nH_ z;jsm9r7;+W{bUea5#Q)FP*H}S0D=q3s)|A+RM`|iJ$?Qi_04C@qe2K+UtM=!-nwSX z0A}@o+J$wN7zW_to7u0ifHd+ zGRpgL&pBnXz#NrAW!vzee@d{)NkxN99x8;3r5u4PGyJQf|Ib4_=3V2w*8dr#hgc&G z5;hNU7t@%u6JG<5em>gf#K_A3<(pUCr5jhIe9)OVLx_hnPcg)abCx{bl)P3b01P+> z!DL~${`l|yoj)<~`d3$MJD{dD#^nBvgn$@^6UVo+U9F+|BzEE;h8QA)EpSUZ?$IRw zVP4M$zeH>d_h@8Q22^L9lvM@ms-!CqmKpvP=!o*tzI=x~#_Va(=3eq-J`Vh`)kEqG z8l*|VRP<3xFezfmZ)gG1i6P){__G+pzl3-UB40c1m)g=c#lVk!2VvfE0(dwULI@aX zt{jT@?)6LF08sJhw>|oih@hVrzY#zFPlW>z@Di@F{(=@j_qGfmi+3;CRfVV+VUR-utE-Y@5Xkz{-YQg#zQx3n zyz6WFV$gj9vA{nx0WZV+9AG(x-yd7BrGPQ*kL5$w-bV-KI(Wo{C0zn#_Fuopza%Gg z{S5MQ_$4bm{~-0-bsMt4YE(__pI=5*7RVttY;}6adPX!Am}tKa=30h*ZY@2mV-R|B)960HZsG=yL4a)y=A5^v*&!4zYFMnaG7CSeZZ5ITRRh4rJf&ySKu=b#{ z_RlGKO>O&#PAfoJcOXj`>Q$AkN%YpKWtfC_+BY;m6vxX@4F_xu8_VUFp*RQB7ntL$ zKk}Mr-YOHWVU%Z<|H->W+(y-Zsv)i&FP$-yPPvA6+8XYwLKv&)pwR{(0FNU&A^KK=T#yZZ2s`M?1Od0%M$135Jc0O@+h;NA~@VuJpo`r*XDPvi>mx%Nbw zB8XGs;<}uV962}Nl|qOk4%_}X23KXJQiy|*xjzcbGF-0MeAyAQtn#C>_=Dk=f<9Ou_!GM)_)Jo5u&#Lf`{u*lQ_ z)p({`)yZOulDcSMHY=~cOfweQ`kK^h2PKSKcRVQcy{L5fj(l%>dr|XKC*2G*FYDoe z!<^L042i*?3E6%HwkJ;8olq%lX`eFC$ z&p&jZNhR{9-~Ex<0I(gHGoC1z6JQPd%N|rP@E9 z+#Im8zGBb+B8_XH58(zaz%Hj*?Q@u9dA!f0eIw8_s4=@Rfp%Q8J4iXDxv5R^TV&V| zoQurgthhY?hpq8vg;7;}T$wM)mGDBAArO4f2D2M%bX3JS91TF3K)u%43g&`3t>Y2^ zZa=u|KK$x4cjC~|AOH+BacC2N{4!|BmsN ziq~)twzk~+mp*p?@vr}#83y7%-W|nqQjyV7lpVifAEPS=4)x{dzW-p$PH|g#>x_=d zedY^ltiS@*^9R4}uToYQCI&kTwj6fyP75pyrCdZhnT1OC<;Z;@Q!~5hY26ad7XJ=> zP+LT3X-geiQ?uK{0{R*|u6NIuKjIgvD)bKrp~j%eqMur2=3HkWvzh=lzIDVkbbyz4 zKo>Z`ASO57rU775ICoImBO1ek?-{@#oOgAI6|?_q5AL{&+670ujlpZ9C*!Pg@CR?r zoE~H@W^(w7IYHcxN@<3; znm?dp7VZnT8kn;{VKDH9hm$MTEHGz{YqN~V|5+2ij9-`ow!1>@DeY7WC*FsMXe|6} zkIJ!#-~P3MePC}Pl*>!iJ0vTFcuXXg0v6&SlyT)XssHy_=&SWqq5enMMAIBZEBZi$ z&z;ted;jxK%trVKG_YBPwzbFVwy!7{zaUlSGL(e$9@DV{>wGj#c)SQhP*r)8O;PWbs)5AIvKL9a<9Sm4SD~Qk)G6(HV$~2^bPwnjHct@RD{?)aWt^7%#2_W+9CC?wYy_u z+}76pJ%*0M=#)-j_#JPcz1mQ_05Fc+*dWPduitfVoILM-{qcM5mMkQlIt1?)n1T=l zB6*&S16S-lIv03|1x3B20VgmdDJB%ArM zrj{|Bq0vl^=7Wsl2l`{#0bIIzSs9mwW=oP;)m{N(fxsr)qp3oYjM3DgL@PK28riKd zGdDv5a_{&25A$JOcvYpNGhCEGqDk+2YH(FKc?oA42kelW7swzGff-Fe)xod*R}J;W zg+~{N)j$2yK@YYRzzn_0AOXOf|8V$^E&DPAs-H$kWZ4<-f1?A`toSi3T+;*-W*aQV zfhBk4=5_b}s#Z2{~CIKKFX;ZRU=RmUYSC~S^;1uiA zZS4d7#fQI>rR1(_D315V9AKqj%J3o9CkSDk2DnoHumA8>w`3NfWq|uKXH9bv>zeaW zN+(-4ZK$5>QQysz<3G+cwh({-=KmjfCU`A?1c2dQuN2*MnWh0K1?8KSm{~uw{}+WX z-QcOPnoIWSG_rZpoWk1b}Ik%eV0~bKK(;Xv6*U`@eBt-MS{(K-&SI?%IA$&nLx6 zlgyiO>l9(8Brv=Jh9iPkUq9pU3DCFQ2lFGc2Bw28rtx52^2??pXLV~QZ1!1CeJz{) zKdST?8TqAv7o#xDAP}qUio&8DZ&gHgjbQqw0jz7er4GW`7KlAS=|IPapNv-Ma#3nse*R?((rF@=^zj-GdbD&>A=3BKk?fFE2zOt?!SFnNB^S!*F&slTp4G~ z`o9Esm|QZRumzC&2ugha?CO{9f4=uC_sWUWLhZE-pp7Jq7C=lo`o8Mt1q%RV3&ord zKJx<=2p?9&xZi#JzT4i~wA}!vmVudsn9x7X{}*ul1h0X|K?DKan@@o3TuU8QK*qn6lYWp-KaEO0G*}Ayf({4z+3$Yg?mWCNEkHONNY z^7|q=5Dfx-g*!>^?yqLjj0p_rANX1J1Be}z&~tgo)QkFS5_KE8a(H)0BhA7K#H zKbQF%$pb%X8wCz-SpDvK-N2R@!9Gp=LrfZ2*_q2kBsF}rj#hLtODr3J@N)1k%f))q zdo2LvQxE*Aq0%SJyCXn7%D&F1s^&#i@Vvi&)dUmL5!7ewb9l|O zNB7)sv<&d?|LxyMJFmU^a?a2QgR~!<{TfP|k(uwQOhNo8`e#yV)htDx152PrEP(HPQvig>CYtY?Eg19aBZ|RLKq|udO)D= z*=%XHw(chs2R(O-r?0I;XgFDBttGS!a$X`b|?VY}by zL(XCSPZQ|rWrT$-z>_Vn{x2f{AmE&k@gK_zK~S+VP={T;_vuIO-p$+Y#M|G|T()Df z5ZsHq;TZS2q64i!qtUf;qW)tK!?n&jU7G+v2Gd~7$VPv-Pl?7+Cq9dJk{VyT@Vfi? z2fuYU?%sB9z4nIdJ(mD*LIt%d<`NZVXRuN%Gu(rkzJ&^5u7Rbx(FuXg(?{Lf4_|Xz zHzWWQ2!LVmr=VWMfZYN@R=Yc2=RlsR!1HtvFCxEQWSUtD;IzOOCC~x(dfJy=Ft7vX zrR9GKU(Z2u2VF>_aRF4QvOPXBWl<$Ak|Lez(%a~ES5DYaMu6*y5p2b@7kf4A& zWZHl^ZBqO?LOoCerFoJ|%6q=2ak!_V9$7!+{-8aRfB5o}`899sYNU?mGs8e*Vg>AA z^rp0chfe?*#-U+Y{muoq#8wU@xhc4qO<_JF#R&+5c}(CSJGf(bVzI740QY2+CZ+Q zT{He;l4isVxX@?oUnh8+h48t8;9>kHJOEjjQhGAHC~dIeA*VV60eitE;Ov zsB?|?^ucJq({7mNiXgDNyBoAO)pK%T8UXl8=DdV6%w;MRAxfA$(^3DYfB0|iFMs?O z?yR;>^d+S=7^G3t@T+3bftVng;uD|`{3p9^MLq%6e)_uG`q^jl6+oemXFQa5k=CTT zd11r&>`l!Qvr0RLc52N1k6?qpRN<~CxmW*R1XXyIT%(u~)|e?%?Wwy5J9y!`-JV{@ zUT*uVwJYl=bzmqB;qd$Rohme&8RA5Ut1YW$bHo{1W= zr*;1Zo&_0~^o)QFzwrJ4FTek_d+_+KJAdkoZU6Ip9B2UiGZ;Mq@R#KPX8&(5UE)8z|0{6lt|Nx=92Wx2NhyQJk<6cr5m*li3>MeNH^P=<=i z080W@g<6*YJdt~u>lqN=vkU-Zi0$vGG#nB9<&DqX`T$R0CqqS6caZlCs~<5 z!2pY^i~${*0I7slE}oKSKxrNddIqeDoHALNCv6$J8AxNro(K0L)5O6);xvI^LPr+k zeIu{bb?cAp&wrgfVj0jaSIx5f82^>lBnz0F$eF>oz-Hr;UjJ5x?0^6MFWreF$Aks}*4s)k zxOZi9g?j)0XYWnGEIX<@&v?1-m0A0~t5jN%5D0--Y;2G1wrRJUu^TVl?(ux%>2IdL zY0tRb-8OFAc%i|@m^L=Xjm>6uBtSwET0t8ULI?x`WJw4lwC}2tYRjt1H~-%`H{QMP z<;%>s+_zO_RK2`Q#Em!+apIg4Cr+F=CSHTFEA%Lf!r=Vyuxu!_Opsa$nk@A#Ahmf?e{S?I*~Q5P z4jiT=$*D8?CPuXkuvMJ`acMO36Zbn1wpUL3GiDFd%LYC3{$vcy(xQjJUZ_7zr=O#1 z%ZZ7BP8{n0<w7#?f+pC|G)1PZ^yD4ZsZwwx$%?zIHeyH>PneBi_?3s0b;t&pE0JoX#4-XHO z1TXUETLg$aLI+1qB@*db71n2;p0rt)gI+oNkKoeB{5u&#ssDPU`fd<7 zpIj{q=99K)U<)i&a43(?cbTV&(6|pA`)k_WCIBTLw8kU) zWyg_t)q{^d6mGfe>tSYWA{)(XYbs>ru3&%Sw9@K;Pbp{ z5H&B#udqVP00;v5KYZrq(7$no>;jY^_EHMsNqJ{#s*Hr%t&QMYqzox;3tzkIo94!{ zAm(oe1d*7w%tH%6UQokG4B2VGmOvqrIR-jolY|Zg01}?u!MFXXt$HA2CFsS#H76;H zB{*T@z|PxTYbRa1TVggM;72rhME&1%P+oZci+leqKfMzV`R@JqgzLZY)v$Hd`m)(SJ^%(~ z+@8f-1Ona>SIJ-y0^H!w=`G)7wE%J1pqZ*9d+M6WqSkNWo{@2s-xq{Ib8^qf6&_5W z&x{}Id3ty(eDNz^3O{|}>%-ca)nN{w0L;>nhDZno#;pDG`v3AhbdW89F(OmM|ejwBgXCq^@N8GEkF@vtmq{(|5^FCZ=Mi0(QM$aCUSV>8$L^uHhJgs z6aG@fEgGTHAcC4^5DtMJ<|5~Ej_rm^g$(OGRPw#&^>{z$W_WrYU zH8iA?7=^dhLey;WJ?8{uXZ5K0`}(vijqQvIXuwFv&H(~K0yY9!p~50+{8JsG8b7Wl zS!C$(8=eWYCBfre2!IkrKgrC_R*X-CYj3|LeCwX?grBm30IVu!kQIdrxKM6Jb)SmT}x`wqO&Szss~efGg)VdC|B!-!7) zW2;|jIKyw4{XeTo984!NAik?7kPj0@3=Fkpyu`rBL(3rFx$o|9`Aye_O)EFp@*3^K zR=()v4?2_bx`YYxtpYZ0eSJfT)1QdnxCK6Hao{~a(#^6AuHZD8v8(m!20~kw0P2Iw z8-+&VrxsSbYF*{2N5gI3{zf=9dsq{G5r7vl222{s6V_MELaAfBBM2a1$iSnZj^%uS z{uv!8kwvP7@;DLStF2it?}fl1t6NafH?|($3l(eujJ6CyyR{(iys`u~>syxVmP1#x z02&#aeF}Gs9(VGJ>VIS?jGVnKm@zl7#2_AFTmNwU(+s~LOYwukw2c2US_HHqb7IuD z-E~K}@BaJT3!=(5H|r7tJUDNQAjPY-U-H20KC9;EHB6pg3~?EZXU0QnWEfM_evOdT z{zYynk!Hxu8-@F9L&W!7IjwH{=I!CKo307pf9Qvrh07>Ro zQP?6+Yyrq$#va3GZR8wJr_a z4qulxTg$;>;#NL+Yk8671wf01xBa6-Vf@ap$w22^u}clK-Q?Y0x$ zCj>CdLe<%Qkaf1tx~24vY5_#|i`v#Vdj5_udYZQWnYuhfpK<&9v}XVCie>^lK^ox# z>;E94ieSv(`ETF8=fidK{O7@Wmt@6s0vyFFuwp~K~`CZ3~9FY-?7H% zZEfp4w%r3;07-qL=j{yrYtRA+naaVhznmWUqFnOqZh*~3fkE12fC-+gtcK=O^;7(B zvMuxayjQlLLclADcV2DK&H31bt%7N_1zu>dXmS6{c$j$A9=UKAbB2CL+RSyIw*5UP z>wg$ZGeO@M*2=L^nem_U)S$Ute*eF4)3ssq<}F%nz;d6m(qH6dcGY`-UKp4t2*eaQ zkhPdq*|qHg02R-M%4g!dangj@##pVTfG^#Cb9i?D^GYX|y*LJf8QX_^?6*z-&DsLc z;bXD|&^pqfQ`Mx?VAi%Sy=2wrHMtH496-zmIkACIJXodS%-m z+qH%{z@-{tYjv`b0x`tp zFYEtFV661XAdszpm)&?x*sq-s1A2BIYj@MgqV$@4m~|VQ}kss0GG=w13nX(MtFoVBKdx{;pqn zN*LHYlORoLS0G}yRw3^l3Xu_ZYkgY@7t1K!)a@KW+f4$=LT%5?e?Uj! zvfq2KWc;;I>J#z3v>&bFRMnab(u=X(O3nT+*rOc)V$i5=BS}Uh{DKbrc|M2PKm1^| zG`~y+36#N4{e0K`{}e90{;IHYVuekZQvT)Z`LRjjmCvh(2sqV>%*RubXZCv+5CDph z6*l}$t$#Zp064%Az}Q$`xwHY|bM{Jg*-!cO^N)uszw+hq;tMZ?KAr1c#JB^s4*}In zwx#@SV}H>CKneA~K*wVcsCR>#R!Ruir6}M{%5AKKuKfxGt3<+X1-2zBd)2>E3h&nX z=MI;lJwia|aob%rcOH&f+L)8@aEJp{U@KSuNq9=3Z40y(sHMenNOQCp#$SC(7&&Ew zc58`gTAL?4K)V8pPvl_v5BizoA8iANw=h2Ric=_$96l7Tz4gZM!-syLlMtmPPEeJx zb3D;ZJM#uB(oo5w${9SgH?{ibE4L?bc1_-HtK)e_9-8J}`&~x_fccd#2iW6TOH1F! z24YL#j&(cisKxsp{DEfVa@fOefU+%t>32=t(V0nyvR|Tr03a;@uZGXqA}zr5342mt zGkZ&$YV!s@ZWW`lfmT9*1_0~fPpnO$W|5+Gq%IpxGC= z5CHFa|c+otW(o8gehXX?7x1gdfpRm*U~`(Jk39{^?TN*J42`kalc4*bGz zGc0!DMD2|!mz30#)~Ht=lV^X#sMA8bK`f z+K7-0=Ri`=46L3AV;7zx*z~T;2~@o;VT>=S6jeNSEZlI%t>M<&zZ%9zM)i(KYXU=}1>uNa=u-(a z-Nbv(sr)bW4FCUGw1>fG@d)ieq1_oH^Ag6Ulhu?s5YYh`(ZYs(AS(U^AuU7R27%NEd1RNus|?2N8U+lf&!sF{Cu_8&oo zh9Lx;j3IkkVD9x-yB6gNcW~<}&Hi`D2~P#>B_5Z*ObVN&%OF2%|7d@?S5e?*L4E%H zhaLzQUvpVlvwpo~5qG7HCqF_g7W#oJU!So@IOEk@Ch|Jy?(A&&34o-?`ub-~W{&|z z;OI>aKx`pz1O1JS!ZqG|Lq;iwPq?Q8MuvyOw;sGVT(2_%j;n#qJ@A3VF%Ws?sRf|p z8R=Yy)xjWxI;&4-L686VY01_Tga8We?a)hWuD%Y|`t~+K!w{hLWmPMIwMK|QCxTnw zw_gX+E8G6!X>Lx$=l9S6O9LJ1yZO+x?~SHPqSdXMUy1?C{%YbiGR9s#X`Wh*<3fKa zl8GSu{-M{+1f%ylXh%m2m-3H1NVD)8Z~scT{N^je%*2#2cyNHeb|HZJ1CTt~YFi+T z-PQWWNXnps@gpQ0IdWv4>fe@@GWbKh5;7!cKux4R1gX_k%O(Pt$sfbORy0E~(t zi{}rYxh0IeZ2O7f97y#%-t3@hyp`+v(r5oo4)9t_RGUy30GO##+h3#{_UPGmTQ0% zpVGlZ7=}C3XuNQYyqW!vYuPs$|KKLG|NAxj|HsQO3ah48%SW7eP67$I5D)*cw8r9e zPJr+kA%I^>OEj5Pe!PSE+c?WWOJ3RIgGuYh1A8Lkn-wlQCIC={%KqWG@|~x%R!ri! zcP+s9$Vj;3n|FlkZoMhYNy_3JXj=-1R5DL300aqbvo4%P5LgP}0p~z3mvbOo1UI|a zl*)E6n8&{a(K`r64MoF{>5TRT1|R?087K{_ayunVvnkZcJ`(bQ!bj55c^Sb1FWOXn zh-JImdg)e}pMu#x1bC@F!BZSI0w-IbUD7r29BNO7vaHA1&%|r?N&_1YXG?kj=E1gb zeEuh|8ZH%FAJXg^0f2n7xUaEr(;Z(8H{5b_nAYr{b$%@V5%NgtS|Jwe(&v7{=QNWppj|(sfo#OiwRQm@$x52{r?VktyZp<0?DWs^{wM-rBk|>#tOUR)?nZZ!!9-XI5`Bx|2`~Ku_gj z5D1D&!#c2GMVNfu>2ms$XjEVp{G^SsonTK&{om~LpE0iUUkV$ijka68hOKMX84qF%5n#}czdo+e+zk&8B})S7T0PmvpM9u5Y#oZqp&Sdn|~cB()LKe;HsUBt*K^dBLl7mr_E5SMdbWWm@f75{I%m~ z6a=$uCiwcS=MV|llKH9asej8tCp0?^R0dj9ZQI{cS>Yj&nPCfCa48Tv5)Urdf@4dj z(VG1DYfcS=I+V?v{=_9-SOwbIC&c`t_P3duvi6VRABVsZ^vHN)Dd$IzKODYz({*8b zX0^Ei&d$Oa8>s2g24w3(8tej`xepohhJnW)LIQF85E`;P(G3WXl#M&SBV8V$;{rg{ zO39B?74<^uez3~+mp38(GoG>p ziu+nzxSH^T?L{)I@=G(7HOWrFI0c%r!DR zYBm+%LRk5`|kD~0c;73(jeb8 zix}C)Mhg&^0*2It7zC>0`?Ov(CdU;0YbTTSsq9DHc@IOKH{iMxRKXb=y|Vz)Ui@oS zfKf2evjibq7KVY%>@TmzfKbzBM_abM?7i6nw4fha-KN<(G;>eeS>H6632VaqPPSk) zdm*7!<7q(nZZl*#dCO|23@K$`H!wXGrhfA5Ffb#BJh<3}_%j5#XwAR)=swrZ)`G-~ zH9q;q6aSJ4B)C}+gTC*9`@_dB|7=(%pZ|`pfZ_O1xz>*WfZEQ00rxZoXv10d!Vx#LD!9k8h@GIQG3A;sxp$A_OX-U`>}~8-yTkC#wdtOJDn?_Hj&*;w zfP=rykO+8%CUNHVkG%PD0cPRo^?=xCR{%0;>t5W1ckUAkSp(EAWs-S0zlrOf5V0i|1IxK8_6$ex3W*O z@3@aR;fd0ow)fKekE>%IeDvXP&go}{EgLtRx2OU}dN}VPfFMyI@HdVBrFi-pz#s@1 z1s;*sK`#eZO@_G_4u)e7>&Rl>cWJQgR^^S_rI0e1(gD#GK&t;E0mu^`RDlq~NJtjS z+v0_LoQ+Xy09`r0)Dd_7SK0A4KeqghNT2^B(;JpU38 zfX_+o|KM}YG5*-V8XJY}D`niRa*!mHJ(@n&{_np3-tf*3zBe2?HY*&^WKYpBe&F>h z?s{1caC*-{aS=>%fqr4h&vwD%5(KDU^!X3-kD7u?a_l%aR3=Qi){`%AABWz8XwkMGp{&gEuU7+ePh*R6KeXS+thm zGkhOQ)zdS+7K=~5Qh`pyn-|K+rx(`#skv{-tf76XH*VS6X6-o2GV7rS0B}H$zr(K# zjxlSF%s*BG?G{Xy$y(p|NLcZ6=Y)Z^Q*ybEP>{4g7wxzzJ}Q6zw(}}}-N2Q+m>9zN zb+mA#HQ**=@yLK^e)f(_cVM77wpDu0XD5%9X@&GCE=Uj`!?1(?iSMa zg(gji0FceDnU07`Cn5CRzl+&rU=gEMq~*xZ$3@MO#EYF!GC9 zf)e}{H`JLlsQE`m_)c08-Pbg>EAp!6vApH$`LYFR7q4u#(dm5!H5%X0;PG1;@u?SH z2p{{x#o^g!pD_t;ZlAOOIy%w^#U&s+ z5S{(@SorPzy(u+3Wx)oM2LCps85CT{qt(lo;8VsRfNX%)eP|fgfp>JyS-eyZk zO)RPP$`TZ9H)O08^b5BzBGLG&ab;fC*!=pt^mtJUmthhclz)suHIaeG&%7fAaLuuPr6rbYfuFt*0;054|9$vSb<#> zhb@ZPx>UjhSr$GOw6%Nm$M%MSHJX((35c3o;2BLuq>K$~~MP83{cna0e@ z^oI3ifJ$_*3CA9$I+R=5>Oq9Z2TAql1>TZpGPo8<;ji6*PpG|Us`5zE+G&4veN~=E zyPlvW=6$h61u{cKz&P0g2P9GVdBG4#8kg7a2?M8Za)&=9h6?Q#UOp|uzo`13mqGn6 z3LCWNAFr;^ag7z^*}lGkuy5b<;d9qq5gy<7Tmq|G{`=5~JSe?-$xh(YA=hDozeICX z>47D_XmKglU&DZL(svH&uCnpd2D0gA#KHBqBHZvK`^*gHMM;*WcqVEb}U} zPLN?#y|VUi{t0@3L0A{4T@h9a!m)Y-sEjiaW(SQ1%R@7XW3n>Fx|rt}7T)$3Q!IRy zcx_w$7Ea+Y2mys+Z306nF9GR-Nfz2tY>RH76K;iWyC?iHB{i{Bfdo_x)8U~Jo1Mj| zyswml7Jz=k>zmWRyi4hh{9@gh@!E1%C0G)+iaN@7@a5aW;H!2yt;Eneq6pW6a*6wp ztp0i5%p{Jy$OFSa*8X|cBLO~w#s1aWQvNBOHoHq2<+-9Jk4hA0`_zSyeU9%u#$wZ3 z`!@5~CAVEC09doBaVvSGBagQ_0s-;<&c3kb=A;4e^I4Mm4lBzrfj$ODX9KHLoa7;&47An`Y znku;VkImgLaP~r|M1U@b8F0Zmk%*chr7N5mHa`r(dx&a@u^AMCWjL{4jiU!1zCV2E^PiONLI0GF_kOSupBV-#0|~uJHulP&Brw`d*`GAN>~+Trgs|V zmcIE6L)%4=(Ih~)nj$=mr5a~hy`-^W=ncr)+i6C&N78$$jX_pwv7W}Y;x4MhHB3+7 zz)lw}c?>SFTqRmO^pmHC{vE3lhMXaix-H`H=W+d-Lh7V>X=fevYsdXUx09m4iQ`9S z!=^82>zaZV-*tC)=0kZ}P{+jvCk-AO+OPiX-^30n zi0>W?+qdrsAHL*M;dY%ESkO9ak@KRl2Oy9X#|<$vd)ee4mI7?wy60VBSAdoT2&3n1 zmto7P9XU3*mkj@UWtqY{HS@=WtePpfh@sXQ2dk}vX7a4(7W^L)afNAVMAXc@7b}B2 zT~)tWDrQdnB>waW17kLa_GO^}lY%L?D`0amtTOW?MeTyU)^dbNT2j&Mzp|3hVeQfy zx^PDrdfE2*^cZRE1sVLI`iEIJ8^~f*v;NI8{6ia?1n{>YP0aW1zbAa)v!4iCw{F*5 z%_obIw{T7G{X?%#|S^;2wBRP}LzE3-X#s_b(w2U3(LzTh)4nGAHe(UhKJlM!hp#zgYkE6|@ejwcor0Udsk#Q&8*7y{7W9YpDTMUBgt5 z4y2~iTb2mB3v*9fQ34Dq508Hp*)I7=zp)e$Nym~O^i%@;{d@h&z!nX}R;q8?$}lL^ ze;Ai`AZkb>ea7nlF{%D#ACMQ##PF{$CBr}HBD7im*qLzPzzaHB{)+IO`@W-Xf5VbD z=nV%1{^CDskiPT!@T|9X@q2&Y-G95r6>zm?at5EqSYMSII1Up!g_%dZl0L@Kt;H}o zIv%dP{pPTJ{ig8J)6X&wlURJ(mOXmRdQIQN&(b0ryR&@s$Ceia*N~)5Bv)x4`m{x` zU!DRn3M{^GINv3RYS60Kyl2#`HRG`kqourq3vsAtd+eva=3h!67H=8$mA66waMGjo z?PTV%I25(T#^F&@Y60LgJcp#FZ#tAH(wEA%8j=$hX5;M+jyuv^;_)swV$|+h%r6ge zI3QtbbYx51lk@Az-3>$D!l#g-xF3%$tAGfQDme=OfLE9O>$+tTQYmcJt5+iKh1xR@4Mle z>5XmxXK@&OfRs15H@bI}Vyr{_>K%p900M5FULD^5>5qhe`oZ^Qgf|q52c!jHIlz-S zBThPSaVTnxh2dw^RR!Dx%h&{WmTV0i|LHR&fMlfI8ndl8nXS#z2ss7%V0={m4Jrq7 zPWhLziT%q!1D)W|H3`ex?|JqYR|0(OrnLlwb+vj9N&7ObP#5Y$B z@?bf3lT`m)lRTs9|Ne&_41e{}_sduRQG=EDk(VI1q`bxlI?w4lzwagHPh)ny08rVW z0391$ccuICcd>(rN3xp}(;5qR%BjQWv?~BL4+O2aUqS$0`UoIfRyy^!&HXidfFyrh zOtR&~b^vITz)QDSS^S8TL2A8tBlKz zYWq^rzw`kws1={lW^6k!vBFb=u9!uqKTmG8$^`8ONRR7pzA*2F>aFsScHhV;~|0~0d zx7;klKTi9@-+z`glbBz87kQ7psY=4hx7$NkF9B3I-byqq9IdA;f}NRu)m~bAm~>{STu}D8czFb1PU1c zQJq84s!%IIW8_&{;RJZn?ZNE;+(LasLT#<}Y*oE%a-z&RA?`#7Blxw?8dO4O#r*sC ztO+Ck;-#U$0X&X02_4qtjLFlFWc2ssi%yrDX$X)IhQCTy|2wtJ5Dou?pA_3CjSF65 zfB!H4G_06fVL0nJfT}+(>y_UV?03c>qk#>3c;>(s#2 z+KXDb=XIeTtMR?h{z3120y#k(nfb?nDx1MQzkgzii z(&5K+%HQMqAuKc-KtmWg9{-r^5mb)77JSd>%$SR={6e_@k^61=$F*0Hv8Px6&^}JU z1h(q%)jCenJUerjGq-DHCFVDD#1%@7_graw|5;2a4z)8k>xvGIRQgBx~sx3T~ z0b?l)+ImIhTM2*Lo@ql7o2&qpXW5*AVNQVZ%(O~gacotIDt{$G#Ex->%Jx8@U#hyM z?GWtDJV!L%dWDfxYbG=+BY09|7YPBF2I(X>=BgTUw$W2wX)jE8Qpyj4q`+6g9y zJTZH2o+7jpYA*|1G{ZkJ`%<8RMQR7-=YQxmdoz;q$l4KY$S5t2Vz%HM{}8^JE0%g*uYiNcEt25h)CpmbC!YTZp&J z{&D%*iondj9kc%u>>6~wZeDs*^#)v&D4HuiRxPN>mk74=oYOR!XC9wJ28MLVQ#Zo4 zRQ;g6Xn>B)9u9~39Xk>ZJ^x}T?p-B^KfB!uA6gh`9h?Z?EI9jNhgN2b-cn^P7!Lw6 z$=W97KPI0WNqNvI3_thlu%C~8{xjP1|6NURqQ^ghWH33updJ7@e`PqvlRj3$A*py= z5WH@P=qV$>%7#uG&^sV@T>2gih@Hc|;{Y?{6|(fa}jioww5;nAW5W{}wy3jDEqm z4Fj~}8e}xva!6YMTM75_mVe8z)_(M;e8{K8wdIaJueD5(%6UYxEkM>#2?Ghpn- z0DLg4R`}d#b$z2zDhKNvJv&Ae9E2_|qpp{@UEkKL3N2Nkqbi?vtQQP=aBXahWSAfp4-EZ@>vxWryz{7%Q)z`lDjqt7y{Eh6f zWoN-Zp#VQm%(F9pp8b6sza)wRK}^rJH$$vd&pj{TgTp8UXn>TQ1Zkl3Z=4R}|Lm;LhtB}p z8Blwfc{2i3qgeje8dxU4w>>kK<=`z~C%vg1TM-cU+N%1ms074){&=iNq(@`^T_^xB z{;S!w%&$Rb-vDDLof9=&dq^|8owPhS^@el9@SY8}3rplT4NP+O$s_7R(t75FBjlq) z*b3Xk{G-Dk6$2jji9S95{IlU>mwYDN{lE|8%Vb0XG<*IXmm}kkc#7VMnr!^?u z#cxV4df=v~mH^WBEbocRj`3&Gq>RXMOVXcSM?8aJPK}R;FWvdIFry`bb51)`#`(i0 zu@*QYlG#5WkX7DG0RUlnD{~1M<_7@(04ir-!%XOx0C4pCPYD+65|)f&m_BUfUi4aF>4tO=1cahi%>EIwXy-ipypcBZ@sy6U1@i#i`Ns#{5Cn%i zWa<)lmS__M$+**lcI^59h-za_V4eJhbHn6M$jBKohQU(=U)Lgs{D{s4oU$Pd&|J~&;ULpY~Uj06za3wBR zK9joJ!_u6k3u_QNAO#J=0>07HR4F17SN{#+L&+PCir@BR1(!sgAJ%a+a@g&mh2pQGWQ^A>L{ zzxOQ5alV!@(1sSTyNI3?0Agn>R@>M~&iG@;(P{pR@#Mjv8q#e4y&wID@Pi-TZz`+e z0SN$yvP%JKsyvW13p82)^G2wv{@dY1>M-~8i0ga;q_Q5ey-W;DPle_&l7z#_wUSUnz?Jgz!)p0;{i zcfak?C4g!;6FZCs=RT4dI#lzI~~J;cT>$H zy^;_@7uW79Givl1A%H;s`uWZPJ;xYW09Ri?U5V@EqjD3h8rApl36dJ)$yV+A$Kzis4*Y4f>FHs9wQ7AL(QUN6X1{}p zf}_0_XgA<)hsVc;+lEP!b~TN@d`Fo1l?%gwPChMT{$woLP?Y=g@PGsW2u>a(f_zx_ zKSf4{GwkWt3*~FfUv&HM;X~oCKl1+Yo`3k;uw~7+91c<5mb0G&~eIXq`ODSHp^1|wn`zxVd1vuCxQx@+gI@WIc1JY03d zby8PqZAi|0iZ5tws^le5=@AC<4oI#@#7QV{CN@8rNW)N5dC5IrFjBpmp ztcC9x|2VJe)qp`@nQPr{st7ZG(Ws$4>%)p)I4=yWnM{oSGIUzxv-so-A>iyMVbXb3 zp|C~!{VTlvS>EdNn{U50yzdhq5{=&}?HSC!l2^oM=Mj-VD49YA{<8bZI4iBE{!-rRFTN}c?p|y52~h}um@?DOGoZf5@lUO@xrNGRmNj+~ zZVN{dS7UhyDXZ7YHK1Aja<_jM-|$cb1AQdN}m#5iAKMOZOC9pZMhu{MawW7XrYR7I``>*V|x`ZC#x0I@m@0A&~=ox%OGow;_I>bp!*Wd#5 zN(Rk?I^A^U-@GP_oW3a~xHL9DrkZT`yH^(f*jME-i6UQUruO?6_DJ;}xa@|j!4W>lUE=sPD9I6ZI2-!%L%c~^MbWo z^I>L*0MH8N4_)i(wcn&uYVK>a0QRpvy$M_h067ks_5i*>DJ%n^%>XYt8kp739t(Zr zqhZDWdVUx=W3wOS8-Yp~a?q}-;{8vD@PdSZIDv|38ok2&wa>q>Q(OLUriM0D9gKE~ z{w|7^fBve=!UsR`k+6C7I%ED3KcV^Dr$2utgP6uLTM%PT3{2{I4FI@9XI2Z@_uNNA z!1P-Fm8a)~sJSpbGi}QN>sPN0XPkOk=ral|y8!$HBr|lwjht2R1`&vDqM49UyAw#a`gyUM8AU+Q&fZBhe3B$kgmQ0@zD3H33YB08fF{UMT{*IZy zug@-kkWGNt?X&+_wUl_qH;Y`=Osypq{wOO65eKim`G)X^ z?|OR}pBNX3bjHpMpLoBz(O>zjKWpsg_hcLMtSlow_v+QyOs@i;g}Yh@!O;D^BVAD7@TA_Nj2_lCD-1Xm1y@UFLoTjVOZpu>CS4r|tpJwQY@R0gdu3S_47Ki}d;)-GI&Fud~3pg^u&2&csj2w0P=t!8>n*Z1< zb_?}0eMz6fU5?88!~I(G=ZIgv$ty5P+N3pq*8Ma1W3zv0;qJKWTj4EY{?EU#Pg*(I zG$mSrNaLaN@PWu5(kt%((&O#UUvQg*9P1IY?9lau5dggX7U~$@ryXA2+0OJmKBm$7 zx)27ZHv13k4}bfQ9|#XW{7~p0mV=vv@(mzCpcGikLJ-J1!2HcokTM3racPNrV3q@9 ztT24mmN4~8=ZC&A_gt7@Z39qnI^s~vT#cgUWdM%W;ZnGPN_6}an13q-C@O~PKTk(b z?P#o84FVy|tzHU%`S-fv-$H$c=B`zEw@{fzYov`VsO+gXzATKrY-g&4h$I+huSrZ1 zgFkGRa%KJ^h%)*saLQjMUvLSdzo(vhGW^}gJ{a!)!M$N}e8R?0mf@%LKhpd1YpLF< z!aUxe5e!P{didsqSOSRczj$YXneGbMnZ1xxc*Hkcvmxe#+I30@bi*w-g>ktGK6~%! zcAcA*Q6Q>!3~MLyX)8@eYy@&(BSRAEpmDaxEh{1s!}G!yz(j)kN5KB09u z2_XoTPxM0v?oN^P76E%4FahfLX;DhhI)FzkML8DKZRZkwYCK~=)_t0 z-GWM@nc;kjTH$E{2rbPL<`3h;cBw7F2&R@obxiT}ufH-({`l#x1@(?}f=U``c5Kc6 zLGAU=D}W$qjI__caN0WGDxH+e1+SZj4j&GG_wf&gKl|&y4BO=Gk9IMQlwgYL|H}RU z5&wKC0APJ-fNB_h=aPgI4Ubwb0GNa?BE~u}M=-)v{i>bzx@R9dJT7)ZZ0N?Xm1M-F zyKH`j!(hz3;6j0OaxDcY4KuGbt5=81ZoVO`nwknPf5}Vbq))SO9o$p02OuAqbSvxu z3{K@8kr5_OS=w?#7~Hhdb_mSg^SBHHwatYkiK=z8eUVY{+p>6F#iF~ z{9i5?zh^k}hweGIbUFp|=Ww4qnLmJoD6;G??3KkoC;eeCEmEk`O1Sfd>#huMe&?Tt zb!*m&cJl$opN?BF{_NU+e;LLjuxGrUShc!^M$&rAG$@BVFx001JwIN7Gc@2EWL zadja8#vcU&t_TGFiwGP$q>*Ngcv$`0^3IHmKD}n+)!ll8et}>sWFU9TH*XJTp0YQb zvU9gvx+)Ue082<>K5vN-03s`3mq0OWM`SRf3DCtf0W)J^_U^~D7nd!IOA7(yh+)*k z*jU?Ch|I~wuTS++HodKkJ59~->+)zs}WdVkyB zQk@p*C5E;w3(y-aNfzYeV#S*-u$g~>^GmYKpTc4OFn&Cj!l3gCMzAz9-;L4VR7$eR zUvbR-Z~E#j;ZL;X@5L8iwC$cQ7{u8;%v%D0aLk-man-ZqonIZ)^u7{*E1&bKS|{_# zRRDm2GrK>kSsqN^XZW=tK@=LImH@^ZRX6oE(EN_7l-&H;cLR_+L8!Igj{ ziJJGo!2@zCac_9pSuY9e*Xd|WZGSPp6RgiWCV3Vu5dwfD0Sx4o5MYd+69fh8(AHHt zMQ|z{d+?bs$5xvqmID|F^JlxC(wD_G+Xs{*=3m&Vb^lDjR;ynE`1gPC{qVbg`j+s`yT21wOikKk zlQxOM5m%r6i*D-PwE<-0?>$T2JHEtgEUNN3zVG#GZUO+r${D{b&0|23&Uh)$w^*QrZg*S8rojS(O%jpv_=vxjP4fzG%6# zPrHOx{POu>{55+ekqQ(hIZhm%9>M%k`^TAoq=Sn>U1KDg1X=vse*a9q;3ob2%rj4i z_kQ^O;Uk~>c-X#qt4%&DnLi!?*^%V!6q&x`TBPw*M9GqBWif|zHNx6s$#M|@j>$-A zRz_$r?KIGsY>g6;)K(R9T%micrTv`_sBJ)u4CE!ysWJFcf%|PHuX;kY4y$~Z zcKrOD9gGwfQXm9O%2VE#ZoN^K_{DI+x#xv3snAQF0%6F62x|G~9@p&OIPgG7RiJcO zkp?M32i~{_HXsB%Ax(h%1XNiLpe-lI$0Y=4`J&B%0mzINVG-%QSH~6G8aePM4`#Jc z7qqi&gh>X55lP)41hhLaT%vlmnX;KQoJ_O-*j9`y#uR;*3+|{LIU0RndOXbhZ?6gC zuihi(ubkg2jJ-*#F}G@K+!F zyYNmO^0RHDjQ-f2AR6NnM`!TRJGU`O1%N0Bq&&cxeEOV1-pVnameQHKCItZPPZlAc zl`1^TxM^^hK9=YD@mkVAQZr6#A zjhEQoFVP0D?3L$IKs_~xbfKPavT0%dFvF8A&~7k3Y_05&ux1(n2Cqp4_C+(labXxg zf0yS5U4|G&a>>k}HGkCpwj|I@p_y-?ZA^C9?zdMK|D$65N&e6d>Wa4G!=L$h_}#a@ zSu_7Fwq#bM4U%?%m-sL888T%ljVcfq%S-RcHybLSvvDWFeNzGe>-;c(#|Cc>7``)p zUu&7)NHAEi#|40FJdNEi;^U0I{Gc0K54HGag#gE2{RIO3K$f*4$lw&@nNKPUU;IrFFU$Zveung68hlp^yt5QLxVANI1;3}Q#NJBE3i=)h!i;aAKv zD80%wPmMVmPc{Tc{HU4Ai@xFPY~-v>VdmFf9R_!4%N?`}ttkCL-dy#r_(Q4uTX3we z5mudnQrIK4e`&j4f(+1OG5@P|Wcz>ni?@ZtM~-MhGiu8oF#pKdeSDTL1;lsh*d@;- zU)@#SH-dkbO%MHPNCSYR2{Vq2nTCV;mlH;$Pz(cSO-q18%6BkNu!wJYCe7R&I|eip zRA11E9^7bXkO4CdSM<5&Mq-Ol)=x4Q#_=UbD+0&+d;xl)B@2>DWQu}}P&fCH|oz;SEmQN;Kl^-XT zKDmq&%d9W}uw)93NAQzEh*C&}1b;qJ|28B5uu~I5_{(5S>^RaqtGrrPkvR5 z`L7Z4HzKNwiV#H(ecE;N*yE3dcf9v6!=+bV7PfEMZW<~kVv*_758V4Ts`}&A*Z&<{ z;3h3qsH}s(_jD)r-?{|=m|k|>Kd$-5W`g0P9cXkrN?a}|7XsYfWfUgR0w5UFLx{bP zD?2(uonA@DIL=-x@x#V>@(F^;>ZzIV>|PrRV>%e)Q3(Nf62xYJZ#ozlyoNKH zJuTfk9RyTGo0h9Z+x|LRvxQ4%j77nG;Z&`P5#a_YDhFVzifba@$V6Da=&3*h3ugTK zPyF0jVdj@F2m?B996O>?yHh3lL6eE%eKPj@k=FfNQO!}=)nM%}e{R8~DlEuPa5`!aoV;@;IQ z0Ay$XG}MqB$;5RR7&FX2&ej`iHkYL7beHVBL!A)CnTTU#y;^|zbcDPkl%*TJ$lk~J zYFA*DEkN;jSU0^g+<42)cI@qiFFQYsO-$P9frd;t&7dHD3^ zQNn6B4PHA{aN8=gQLsZg4$iubLW{tuwTdiB;2a3zDX6Lf?c8?kbr-G>apB@6(aK z4~zLj$MOnf1>Iul694`%e*!c~amM^*^M!Z6cf9xA;V<9!p0In@ZqW+UIm%NxTq0jt)8FW>&vFsWsM z({`VtbEqe@_O6*NMM!;RL@X9mnNiFzkE;m*DwKf><4?{Fu&j|qbcAMM)tJub))Af0 zA95qYJj!h_4}?2}M+~XDHK6*(Fz#dw2xy(t0{GZBL8QwY-$fN@69cfzLqZz{R!-_r zpI3zmdG{;1_9ZR`42*RIdhwnoL(rDugp0=5=C6=XjjO^Qx%lO1Uz@z>ovx<#SKOh) zhr)+G_0jMa9q_Yj%T`;GvK<%t)+e7?Cvz}&esLK9kR=e5s^rWcz~|#s8b|!e5b6>D zVD30tAkcWM{YN#tsc|*?&ofl2`r(~f?E+;-0io7%o&T1w!9H_@;Wm9wI(W2SK@2c8 zjkgcW1Ow2u7^XGzzv$A>hJH!3=bv+47}0szbGQs9BMbuxU?W+zQV2LEc;ynfD9N3n zNFxeb0qO9I*@D?8uJIZ>BIg~$&wA=&<6Iy3M5UfD+mx2UCZ=9Icr1*byTu&*jGnhc@AMve z6loLbV8+iK3dOs{{GU4Dae9?ofTU{Mw5k2=XkR^3u71Y+MMDo9d@+1L%>Q@Z{)b`n z%xWl^z{F0>0M?U|gC7Ym@cSqLSZpFB_bx!jjog*gK)v^J& z;-(wJ)^+Q{Svo?Pb}F*Q&ER6)J!ZmZtq=l~Z&BNGig!I`gT0_FHKpL7dbw<|*)kmh zO92l{2q0)6bs|Jn-jQSZ3X#p$1ww(K+iAZE&DNvY@0n@Et-Q_VRqywpEd4N_XxE!l zd(tz!yHWrEFwvXhi^syq1v|9I|J7mm6sg@sQ=QyJ;$RIPiZFd<{%8rIU3mt`s{|37 z`9p(%EeX!x(K;OupS*ss|=GklOv67UnauE&+yVr!mgoG44da-2CA>%-ptZfUF0~(Qr z%Es-;GbjDwH(@@$bOf9Z7OEZ*^-4_u*ziZIr02{3TMQxHB+ z{=!Sbw2b`*H_m8cA{ec4<9O3mz?-4)_r&~Plt7+0^QTQzrk(7!W0jY0?F9Yg(edo^ zYp)Ey^}GK;Zp*cGS-2OOf8+&9sjn~yci0gC&Co zL0NH$*-ww@ooo>#HJ3&M!pE=}+zy?zwPxIXdg}~-vhUo*KF!23b6OH1to7 zg_+-ctsDEH6*OZ%=#UaY6JVsG6J{WcYhT+bL|wkl+qd{zLU^J1qrTK4?@Pm#q2hB79NFAc;}z+y+N_$GS=k%1G~ zU?Ml(_LZ=4?S^pro>MjX*ET#cwFRP&62r`J?tfq`t6$rMzPW(F8Cf zk(IA)543`lcpG_2x^5~Iu)@_A#_%WxKN;9!`W9blF9>I=1j<^$r|M|AaQw3z(iSie znA!>qes>}m!4^)_KiwQ6oSiyP!P4GqR0SEes?m!D7Z6Gl; zh;4ox=<`5ws1NV*>}GIAWt+xv$4Z}|P&UOV4ndo3;GezX3*pUge`|R5`R8o%gwSb* ze1gZ93Z=>i2lR7;OTRaIYlgb6>L+Ve%&JLMtxx=A>2I}nrO1=urY->>GI<(pP*&}+ z@p#|LHLpH(YFuuhPuvjEF*7S1G}d3jj2g%Z2o%$~JwPnYBI1Lkp zwPD5NWZ1oPm$|^T-2w3bxE#P>8K~@bpVsma1V%OEAJ2HR zivwLCm=GX;0bxkMAOy5|Nq_;)v9#<6zyVFfvC68n`WcIVX8)Zkzt#0?G=FOk;R!;2 zCj#X|{Cgo7oEpy^5tbO!qC2?Fo%ng}@Bh_ThmpOT)NDqW(n$vmsi9tkF0kIuHa{n= z`8F|h4W6+_v?Vfs$`P2x;KlrBj~)pZUw%pWt^f4@*o4wtg{uxu<6YBB*QdgOd#VH{ z_(cIgStS8HA&zf&Wq(R>hRey*$$F_%07!QL(Acp7TP*;vHXH>222;5Jz_G%m9&e~0 z9XYa}IzsU#*3s{UtWsB{I|3No+3&b_tmZr75CX7p$CCV`pZP>szjmFL1J1H_WwQxD z0EqA#rjo_HOe+gXpDE)@AE*06+*}OgPA%j+ud+ z1r@eyNnoXn1fSR+iqB~Y0bd3d>VPfzqP|Nq6VTcohXs`n+WK;8G&OxUEDcx0xkmFj zQNP0@A~V}o3jK?<9sQFXmy4zCd>l&wZ4{Om&!PeB{ux*`8K&O&vM}{?=Y@W`1TU66 z`zcWz^9<{!W$gEz$F)?bD>Nd{5b29Cf2sVp%HS_E;fOPTT*iO$^B0A;yyH(y`!~ip zA(~i1+u=2~JgB_9@8dpyWSsIPfRqfzlLvmh#2q4YH=a}N*Q+S*csmTo_B`nsj2X$ zU;O1TB16HVjBE>J_`5M=dV{I0%v zQR{Hqq>5vEVm!@@N;A6?!!NviTL>%1!jaG35srNOQE3Gwzy+=@JhE1&rll|Ik#oJfVJ7r$(X-(=tUeav@$ z%*pqrW(@#MlQsT{QW@{*$ZQ<%@(d80Xo!_HIHV)9a5P)ye8k8imEVJij%;b1zl)_; ze@`1Z;ugXeufHygX;;ABQ%>>a02%1iy+sfv{9vN?uZ8(X@C>2G{rdT~)2nkz=k6uj4fF* zN$QVdA5{L+zji^G`e)~cz8UTKiL?GVmTj<#cCfYYUb*#sNbCLfE+%NWisc`Y9U8?E z(dN_TXCUk0PjToEVgAhgKdpWK|M8FBY<53rL|v@v+CKb z{l6uZJj_3?r+IAsf5{*9-?Imusk2K!GX&sp5GY?lkIEU`vN8;vrdfr&1|Gxc5O|~g zLj5Io-521~vf*Ft-C|=>=n)0-+P`c*Wl^3v(F1^_;A*#rNF#F%SM3hd zzj0w0J8!3rW0vU{vp$B^Of(gAqA(tokc2!lDs%) z{zaXi^0_N64gdZRe%E%|;Au1R7S{FQFQi{g0Duof0RT-woG4m(URYV}A`dBhd6GZW zECA3>ti8GQuOkl3nMRGj=q5PNHZ?D7Du+O4BLKufgkZog@30u6cpUYQngDN$au`yM z%>+Kh#h8|FU4?q}jb92QLnGmgJ$t2kmQ6r(8;o%v$@!RpPL`xwpDSnW!gin2N<_x-CU!^r8IL%)0m9M}5c+s&ZBKX2zNK^oySbWJKb}4Uj&W}d6bvU zV?M&TL`s@!xKs`47>mgWOumUoNQCL@vklj6BnKu3fkIhW0P^3j8$tCn5r~aD1PmOtlY*?-YxpEoBzhYuZ;A@s0l`f)RqWERgj^w~f5 zU1)!?{~~h?t`PuS2w?eO&bFfZ1QdTA@C&PY1u{e&&AG-WM0Ry)0BGEDChu$7G;sXo zMF_~xj~aFck6@8?Hw-dxK6@)WcYfZf5MYA~v;;R3gyY_K2&C_b^Y<|=X^7|Q9s~Q8 z;3^pg&P>mQQvj|&QNMN6<*>HEn4bU=MIqp**2PfO>trr-&q-XX!hW6pm2CZU7s6Cb zh3R5#!o3FN!1@(opd3Py-FcK|BC`ayh1;Zmo&kC5@1$P$P}HnN%rOPeW?ZN6aXB15CRC+v3eD? z04|}qCZJS8gEv@WXVtOH{L5$m=~)B~7E$A8l_O>`A%Hjyy2Mw26Q-mXvSA>Uh?TwI zWeL$ge#Pfx40tr0d&b#eTw4X_j%bTu@?DUFA3XRKG|1S#6J&_jGCoV8EHOO!f~gt9 zl%)FXE`fx{U0Wf};xn}*NFW(+!{_U&76ZSj^5-Ah8iT*B+ET1F|H2As4ke5jEP@ANr%zhVefypb@BPsG z!(0C1ZS<7c+Hm@3#2v<9+HOQ+01+_d(hEqDg4_1H zxALj{x70w4q+vALQ?-D7GR*A&!S8zejOv9s4jkYk{;aTGy3fi&5r0e{(IGGf&+cMkglx6v5~dfb2N7 zms9)@0yI;%B3i*~jp(R6ya%#P5t{%V7=+!G1%Y9M%Y;D*G3!=@;oVyIo6>m)IyZby zMn>!uFv67dTk>UBU2NN*9c%4&CS3(w4*^3vibV-6eR9xV&^3NkeyBOTSvyPbT&45<{0pMpKz5w<+MWO572R3>oh;C z{R^X_50vNp+wh@Jbo|i41K}S&{^9UD|MkCwH7i%!#4er8!Q@f*yTw0k1CQYy_gu*^ zy=U$JsPO`S7ng}6VFEA@#4m@%Tc7~}z#BEy0ywir3lJOJ*K)i=5D;n!^c7Md1|b-9 z#K8AQjvPq_j5AX=3{2C+w5__vdX?wz?U3SF6biEM$^>wb1rCwuyBppa9}fkO9bN#IGT5H+}M zNdU(|1iF@ZeiBHSk&K)sZNQlu^-iArIGcc-0$sc;Fev7aDsnkA*#g1=m@ooBtz*8) z63aga4U<`8c|`X;K~ESu3u*yW7c=tHvR|Rt7sh}5RL%Nd6DD7GTIgGW@UQXCnBzwv zGsZu@{5b~vd$RI>N>={7i)k!jPJbwtGKWM5PSHuC+UIW*1ekvWNpzcS%}+h~WcZs8 z{Lk>8{_xFV$BrHP2zvmleTj(!BIJuehbts0FVVz8J&;{0jRyj05pq@4Nu3>p{ZqOf*BbY=tDFHq+<(5rBNSw9+4V6 zTh73M2(~&s>Hkb_t-bYnmDd3n91h<5FLe`~#%157P%;S8?`%Zrk-@Qg#Z0*7%h!cR zpL{%=bNZQKwG0Gp(DMZaGS1Wog&C>a2Kb^ai2fVmPz z#w%4rX_%1+e%mQ<+NRL2{l9ZMlKA+K6D=aI=ohg#$9ioaki6p1zau z?%Y4g!vB68`$#}QQ`m|?wy69$K~yULzU?cu+^3fUi*`o~1ekpitRHybhv5(Z>`%fw z|L)yk`_>%^G%#^# z;e&0_w4IIwPQdh~071jJ)wsuvuX*!(!`Wf_S6>;%&fV^(cV=h(PBvUvkYH|GgTE)0 zKfe6QhhMA4mteGQeyIF6>f}&D0#Bq(1=00;{^{=U=YR9=@b@42VA!>NcfzGbRj9iP|E z@IY`OViLS#5dvI5p!1#Sr)fw^URe9{D-904HxGI;49w=4WZl-w{PNR!W~yt08$`Fj zrOYtU2Y-zB??_|`?OQxztzNY{Tz}Jz;r2Vf5!Ol|*t2U_;AF%&&^&W}#oD~~<(p*w zLSh$?6dlv#b_H0L9G;SO@$<3|kb2)v7KG@;P-+OR%}Qm-PJ!VwB?wF;f`B{^7MP)s zu2qC8!m_{>++E)Gw?N}G@tX0$uM=7TL#3qrCaTc3PfcTDLqLpQv-bY+kud&~d*#jV zm0|KH&NNM$t*HYMVRhcevw%}zWnjyksX6EZ64Q5(Sh~;vp0moDtQ`=W0yKG zF%eCNeqZ&atHU4s_dg03eg3my`?ejzg=p*tFg_`S$)hnweeas%$QQsL?kUv0&sH*j zcnC(~u5n5$IG;ABam#nrTcBY9fb3{+2q~Qo0>r?x6M$s4s!q@1Z%h}je2Vq};|`zo z3z;(k3DLI%W-&37pxw7sD#I&(4U7(F`s{IWcR+bOz{hr?9r)It(SEEJ(``CZ#Wp-PTckeRp!rCp!ZUVc>eEh7k&s~_7Eu?^r6i{z*cpi00+ z8sP%>{jymYIb%y0*r44FB3#FxkN_be$KbWZeAK=G^9OgAL;fuQ@irQuz6TB39ikA@ z?sDrYkFmk{)Ht1!b^oB${nP*YRblF%zf=x=rdYs@m0rFTsXY)jM(z?LKghX=)5`MN?dM7&tb964uO7}z`=jz9Zin8U6>6>g_} z0LxFP|6{-8)Edj#lv)5{+!-wZAb@h3U-GB>0x$ufm|OAioEZPgF!4+0>Eyoi-KPc& zo;7x>geB$!8(Rnk<_wHkzg%&fkWczn24KP1nEm}bbwoHqJ^}&o2`*?AD*qSu?+YKh z_|xH!|Kc5CRz}+^S4@W^TEcZEuTL2NhzgqMIel{+anSue{K5oB;|mnRH)%SSik%lk z`r==Ens~o_t)~T=762&Bg#c7J?%virI0b`FM+k@lL1g?~d7srD#IH<6s~rId0NqNc z=fma5YGcDH!ZYo|gAY{hvqAuQ#56XTN@=-qO#p+(%2I-hF8N$Ibofv>efOR)y>eB_ zlDGtmJ}a1C5eeI&fPmm*SmnorqGdX7U`SgA zM_;lT$|Ur63cV;G1WRe zDle^uCqQ95Xe<|J=fZ$a#~b_kGsXO0uD$&`LqDE3VUjjp61cesO?fRNzaj_t!2H>o z8;M2@Mc17qHXG&Z05DYkeVbO;*s;76A89ts|MABj4S)XE@0O+i@5__vKo}VwHCkVy zNgCMkKJo^>cfJ5`h=KtH@{8oczhj92#*d+3HMM_+wAi=He`;@mTm*nv9D^RlJ)gLO zQ4yUd4xgCu;PmGrv4a~A+GeNFvf;0zkCYrIdL1`!wABYRE1De}yA6e- z1&BkU)x|BCKaCI>zYKNz`J)UIo89u0yWz0WH!L$$Y{QO<| z>~Z3i8hB4V`*b*K?-}6_e&;`jpL*Tv!+<1YTT@R*mJ=Qb$Rj#Hh;zTSCC`Y*d{8Sv zZ~U3Aqo09;*#kyHIuXqF3|s$avjz~QW63~p9)0B5aNwqU!l9e)3&$USL3@k!o;Z?K zY2R#4wcNAqZ$t+!%@`Fc4`BP}qity`#q+a82gaa%Gd+4ex8i~$MY#Um1SwKO;K z+FfDvm8WR-udQ>a)e+7OuPY!#V3470k;A$0-!I2nwk*&}0iFf|3}_O^`0modLTvFf zm48Ywpi3s4i+Ejk(+%NWfBT+r^H*;PtE7#LOSnu(;?I|E$=LI7+NMVf7y{Bv+4S%N ze(naCCM3QxRC`yMX8C#P7HF9OP+2q`Pl#9UV<*Nd?|M08>z3971|InrZ4jURJ}U(H z;6NZq)5JA1gsJSe2m#OTdoGOX+<@QyKmYIWo4@vN%wNEq2rlheCPayF6!Gu>UCkmM z)p4NAAks0Jf|~ao1P%tiPELz6bpLa8_!h(XQ?h$4Km4?YtP-+q5Mc-=kW z7{`}N5HM##)&v#bmNHrVhBm?QZ+Z5=csu1C3oaj?JsghW5GwD?m&lJkhncjMzJSL< zd7HT(mSz8%$uKO&KXTr7TlT_-M%j!%)&aEUJ6(&|)Z8!2ei_QzjvK~MD?_9OtoJik zZPu?PM1Wz;B^_sOeOd3{_k6hMQXS~?XMY+Vm4Wh>bsLQN8{M(Aw0p|b{eAg!%rt)E zngYmx_ZQ9Rp>z{pw6{M?rJk5Kxh?@L) zjpv!cqL)1a1RuEG2<3cOf$0XRq6tAm7A$dh0}Gi?Yj=`3gv7e+;Runk~P<1aun z?v^eIP;gXLVu!%_;~Zqd%tuZ{k_sDmrF#Sh!Q8)UA`Hph>Cm2ap>L%QM$)ct?vv!`6SWk#=hqoe^DTF6+iFejNk7m z(3%psER=3a&)KF+UM^o6Y=O2301d*|Q=fsVj`Ufu9r<~aMO;Vn#ERAi8R-~wJ^;98 zLmU)wToNA#=E{{T!xyf&EIj=9qhYO7aa-g#sLWpgS;1TJ;HNXX!=xPez?3yZv0}=L z$`+f?J;V*)0jM;w573_H0&YY{a1W>=yxO`?0FiJ5qaRp59Y)TS4}r5chrZQXhm-&@ z_w*q%e5^+hfC<>PKN%A(hxWGsr(H68bS+k!%NK4%=F^gr!R?6OLh*r3(_vJ5`6uMT zZ~Vt}ytf#?c$};I;~)kQa6XyX7Y`mazx_p=`8@o*d-7{F;|B%;%W|RzZ13yaBbUAi z=(dFC(UHb5P`~rNyTTv8^X=hXGWc5~TO^iP=+l(>L%V!AH@3Mm{K({)-IK3t2)KGW zFN+58c>2uK#Ta|i%NU=1zkJ_#3$$$spm7j;?@MRWjPPvmXX4U$f2T2|0D<(Yr}0@l z1JG?J+*W{RUlz!I>s}Hcd+PBpJvANP{^qxaH~q^uhM}>sP&|qg4)WC>KH**j3xhjm z8{r|j>1Dvf8$CS?d?zf2&b0u$0S+JbJ>l(!BAs=DOgRLyNpP-+1D>NA zf4jB4^);u2k#n`hmD#>d?le`u;c%v0o)H-MUWaTIAJvJQ4{A#vhPsT4$oRQ$RRA2L zHM(j-P&ifF`!E=W4rJgp=C84LO#ODrRab<+)O!C{ZoS2f{aDfgU|-{>I?lY~Rc{oR zs`JzL2m@3sUbzP#XYNj`;ybP}EHnSu5_w*iH2`_GW|9|3Tul=Yi*h6f>3x**oQ^x* zmxpvv@SgZIP0R}eoE17va8B22Z@S(*Y;E1NIZSCufXpe$W+Hs52b_5n@E(YjKH32M zhnE+mge?eh#>8>M_yAx_lin3%cw~-*A`Dhd{oy&8k>FL3lhB6cS#VG`1sFXZCl9Gk zO(PHqYX)rkf!ROAXgPGQ1^R@1;CLst1%myQ4=#aL4F_-$91}hkWzi2)8#-%a7<ye9L-1j(o6f;kaHM-V@LO5Xa9+g6GkyzX;(REq-cp zkWbu#2%M@TAW+qUnE!LnJ`+B!{r&&tkKZDPWKV|m>(i+lEq%wH)@5Qt2kF_$ub z7-i`O{nT?4=j-+#QXwiCcL z0x`}-`S=ZkjrG=t9(pMJ{LlVe_`To$kKtA4UtlBJ)OCz(0$>?W)PRphM~_Gw5Z})S z+dj+vPc#&im2`nM$jvLv3fTamYFS!9TLQlwDkQb?{zGB*{wKoWZ#)S8kc5k}jB>BVa)vdu0LuVq0XlJHFlQ7X1k4xAmjE36Gj6D>gyiBe zZE;&U9){1~7KUE3H4N$WOFN+tgk$|b;%LkkJY<}ahlJju&crA(&g4ql>;Vu6D1&sZ z4B0Z)$k2~d{MO1Smk@;&-9ks$iADJOH|_}UdH>&rk9^{zVe{tA=92{h&fC$Fs9KS^ z!=xAbQT^EV2nYVHSDZM2ORq6@p?BH%<@@GZU|9puT>CBgb%NVv>3pE@3dDEhPZKbR z+<*^SQW;`?T;sqYO?c*@;&*;@ed4t?jEi2 zW8KeoT;50AkLg=^W!8_du~TdJr)zINj=~@?bcjHKZV;ID&q_$U`pegaH~;A$hReQq zdDyvgr==qeTWRAkgsC&rcN>6(m_P61H!%$*AqAw*o~Hn*2w56e5w-l7Lklcx0CH%= zTC#`(ESomI^RdSOWaiIcBvrX4fVA-!H30Fs{L1&FmA5esW@cRb4;&2t?l*oj{9nKM ze}tXdDFE}tZ+B^MQ6A$F)g~(S2etS3(Zn_YLe-8yF~9*Kg)oXh733|guvSKk_#Oui z;wl4;MI6%##UFcoUpR8lqv7zke-sM$JQD_!1t({u3FW|*%&`8iRnK=<$z|(!{;*F-xLPcNkfGO3S2amo$yKj%L4g;W6bzv zx58Z|KnA zGumtAO^Z#QeFUk&HJVkW0AGiDM)$?f9uF&96e|EZBUN7XndnFA2kgyD=&!p>SCGHx z%Cb>ku-x+MefoZd&e3p21;cDV`b?%%7+nY);F7?d=UQwD{drwx71#FW?3-@48r_Yu zkPpItt5)}PKyhyWZohRySvnKELT=s|I{x(2<#S*BLisoU@!yq?Y1Thk^4B?=oZ*uU zL7&U{c3Y75!vnH|ZIu9rL#!Y><(v#++&17E&FWBKQ~*|o&XJ2{O&x&F+WwGf;pEYV z*F+s?SIWu7Bb7nC)+Uy}r1qZ&j@4!&+QU#Y~@mh)Lmump&IiJrJx~+K)Yc#*6Om z53`yxe&^`2QqF61!e2M~#VQ)*q>p#yhbQE$|JZ{MmiPbKFKfU5zm$g_dQhJpzp9+n z$Jv0f!eqolA)kr>L`WsU=PMIb&s3ealJWXLN`GGM)@Jg4Q%xUq~wOuV;@iHgdG$D|>Q z0Jaq1I1>72AAZ8hz_%VOXTJWOa#rUBcpWQN1!7OOLl9{b(P&?23dC!HS_N3Oy8&qb zXj%zAb&GYn3yU)(>#yBjHooMlvhn8b9Lb&?abHH%msKeGL6uhWD+W9n#;bf>+QU*m zGk#9=V^&-6QzpvBk5#i9wSA`7Y~$v^96Mb!b>bfX6WSiU<11e(|Ld3DU;gX={O^-l z|7oqzXEpdPFWU`w$%tIrXi*7eAzzxjZa0lkvN)vG8@;9;8}@F^)c^*@>mOMw%o zPLz`;JArv2QLFh7IMM#N)qb(3A3Cl+F|IFAZhqO-dY$xzx@1aUq(ivH~Lm z4+tTvz5YiYe5iatv;KempZ|Ti_weCzVE=wq!91m%^^=%1gkkIkVG8)69Z&&h@+BpE~+s7#UTWYMHdsF~ckJeK;Ay=j)OREF`U``S;sDo!9TY;#ve&U%;Ww>FKny?-Y zr`@2XP2%p+yS?xIv3HiAd+*PdH@)GFWxd{+*WI3#SvY$$H_f1Ve>qkI5SGDoVnk2X zZAAmfl5&}VA^hjW)Qb)b2IJ#~K>#RUmOqP`Y2ZXKY#?=5>+JoHmNWMpE2mi*_;V=( zyh#wBHr6bM?FJNwRw0O#dzNx{-d3c?V)BGr-A^6D`1gmH$ zJNZ*)U6~!<+t91Dai0c#pLiMb1bMX$I4_0k%YXKj^1pP@@8A9B|4?qY{`xp9?2CPJ zDc|W3%AWa?g#|rGbGaXPhTA4xh8c&56Ap^VkNBY>?Px9z1x5wn;?RAPtL4#o+=UH(!w3Yc>P$+V)*dR(l!yVRHL z^OTg$6C4O!E&*vXI%L?mg@M=Y^h4mU(@m4Swzv7a_Qu!uHXV@A=ZzBDnKbQ8pX5UW z3~kI;`Ly~~)FFBIkW3ulvVR{IxNe~!a71j56G9p{ zamH)c_`En27!`nvL-$^;Rmk^(<*`(0g+36i-|o-^Lf8i*RI++Vuqp_iY4vfU2(TJ( zN{4>#z4zYomN&np{M^s{&GKV^?MKUHJH%AF!$6b4ONKGy(L3F|yRU0Z=fB$Y`DAlfp0gSJnT0*N5-#!nCYTK$O& zvhLS`KD`;J&7e6ak+hksMseS6E+&0F<_M&IaX;UD0nP!$pKa_NyjhX;Wak4?Pch#TT?7%%|} zbUHelG=regyXq7e6@XPI`RoimUOj005X{_@ZxL@8huNQQ2E7MEHi{GDS}s4RR|eS@V0ILT z?j}Vw{AxWZUXVo(Y|5gqo5e?vSRL4*y-~g975pd&(7|`riei$E#MG5KVuMag!1H>a z`^*E{E;#&1IjheWpXGq$F`fSA+X@m8bDSN>?E-LiX^kMTkB)RErQmJ2Zdci`?*&@c*FnC$yJJ5;K4?Q4(&p+A{_MLEhd+OE zkPWYi>b2K--6+Y$J+y0l#46zQ!0w78*XkRT9agkZY?<}*VJ{RC>WHBl9lbc@rx`&x zc<5mHjgNe&{M-NXAInouKUMbVV_^t>Rt7ETTS1({-(jurPj}yTrVfh|xDYA`q&B%B z+R(Il4&z4uYEocS09KRQOUNq+a0}XoRd)(-AnPn24i$1u4`B00xDN(8th$|3t`6{~ z00#jN>tN8$H{V=-;qUx>`N_`$4OGr_IE%(snyuDhXU5LGW?@LJ$$31Gqy z_RQ|}n}gO`p<1U8M)9S+4KLawVZSG4_gu=eeiz@fkg(&2Ae1sO5r~04GU?NoW zz4FLI50wx9_HXGL-w&2s|M=E&z4rRoODXULObFooQBedxNHVN%XFRAY4DMVha5!<{ zl9jL#&^)4ijem4rTndZ|z{REi%-pMxZ==nHLQYK>k_DT?2t>H+sYm_@L!cAU3Vq_! z4-5vsUmu*)y#X6EV|cne@W2D*7k=*N%TK-YC(G+!^V+gmuM8G3YSK>zGszkIVj}a* zqBtO>W%AfB1@~G|1gIe845ldur@mPk;7tM)ft^|^W?9voE9f=(Vj5|wAL)5ucl8bU zPOwMaHG@3+=+ou&y;>oVK$+L!!n3T9=vA?^MrR(rqCxo*~AXVF`;re=VMbT`%FpGU35{4wmcoUSIz1&;0H3 zllsi?OJ4Mncw1mz?+atZGQ#dW!38)$Kd+f^d~P^Cb*cikLawG*T~`(r-3bPZpOpcW z0UaDGQU>Pqv6sS|1k5TiK`=XKK8eRrKR6_I3OKEVux)VmG5wB7;7I74)iulfI7L3s zsdB!ID8(sey*gm%w+_HX;W`t}#@}U`?G{jzuuQZP6r9BIPzIUD}peuh%)}b?xpv%lf;wm32F0YowS83FbL-2z)l{MT$LwShD zW=5|Aobyu5P)ga`kBfTrDrgL!P{sh1SuGkmx9KOvuwL)aWOD0`);wU)pA0!GWMETHF!d^UuL9yG)BwOXCIUs#R(zRG5Qp$(f-hoPC%I=kIp8K zEGOoyx?)VFLJtNQx_T2X;j||Tn1OtP@b6Wrgu~CNspL*)4*cSNN_q?~f zOJ@W4)bQ@Bu99GrP-_(d7sbLz8O-Zy)c6c4s{&enjiQDSn-rTYFP=#zOt~RQFkA&{ z?|DuPIaUZz2v~k*s~|3p$wXb6NZV9_=%7P9>?SOPc9%AUrQG_TXQs;1^wWAb9$}@W zYOXOxfT1MJbKc-dDO4y`#7jBgyYz8~J%=w*7C4I`1taPJ?oRJ$N)>s9@bkk_(FuA` z_RxQbqlkR@j-Q0*+*S!|t(?SxJHn(iaT0${iW4XA5%7F*jzgCbt{E(M>=(k@l@)S{ zyp;zu??^Q%pYolA0gH(`Q6PIGo_F+F+Sfh&^Ah@TEic#cJ|Q{*kTh@8bjdHemG!2d z>2uieS!`ZorOX*exr01X{`tD4+TbZ2*uCvbca&e$=X(Fg`~GKnfxZB`Gd|vMG1kA z@)N5$f+O#>vf7mOz|MLkt=W3w?h2Osc}s&za^--?MUrGrxpOHyPHrzT0>lhMTnU40ro* z9&O0zUW*hM6@azaDhrg!d=suPGze^mRTu~0Phk&7$#|a)!0)hx!N%k{0COR5Ge}>Z3%Ft@auF?aN`y+G~Mzx4->^hIx#=5_v(X*lR?J0 z2$|yI+gW_dMLB~xQ(h?q2t;PXyLhWWvo@_5utE@rwV)Ac%_W_d+MKg~iX6sA{(59N zz+ui^@h42gbq~rK!VrW2boL}1xX#2K4os~Fu#<42h{MRn89&_JFXFoJgvqe9lNRY8 z+7vTk0>e3DD@n2aAhx;(Z zHHPsR!c+(RPU}_4FMstbBTZC zOW!DnSsmL3C=1*om`mVJK?B$omL7>K;}^51;4?r4&9V-0bo8GDXBj2|og+{+_=fQd z;UB``57?$m!!ocx&o&03pX z<@#3m;%~GhXSJlu)=L+fodV!W|ada z?KW;H?!?=Nwa_Na{2Z=Rxgz{WTqxqIpm*8u*RaInuoU=-%dj;aV}8T>4dt2R&&2tF zm%Z%evKkgJ$>L?I)?OFwHiIUjI!R0Ir=^Audi4# z;}3hrj9*ZORXw@L=ajBUzVoiT%I`?<|LSl4dinh4KVSCfyv?@DFQ3f#EvQqlXOZo% z3ITouH-$>rv`N#teP$p)16`&G&w|4LeEFEMK0lu3uQYMA^rX^ByR;_}tKb+FfE6Gz z4a|p5*pFi=-G}YSC>!3~fn~Y;aY@H=JHW6b)v7oj3;YZX=^wnv=`7@N`{(jizr!gr z{nbB|2XGs|!!o_z-&TECjzkf-|GxXn&EIu%dGEV_y1etpf1=!Y;6QwgBvu3hN5_zi z%7UI{O7^um8PAFUpI1dlKrRF%cEVHKwQAhFE?t(FI;SwejiBfCLw*PcDFrbb)T%*j zAD|qlPsEA=TL}r<*16564JXeDt+D_ufZVf!XW~;};+h%X;cf9DY-uFxIFQ5DT=gL(Geksyi%Xw0_dD} z2bRuFhsGa|z-=5=IiLfxHE>)rm?KAyl(+tsx0WCIp&u%5f6Lp-%U*hu50FVHur+|0 zbasyga~n>x^Uk)uvtBqb$N?{yYAOObb^^mC@-RPjVcisNoODNg2b78d0z-%J=GZ#W z0YnkjTL>J?$+wSxayvX~cUzDbYtUn(}`hE~*Rr?!8t){I}b7siLF zV%tY?kfCcHjjkX`yv~NWX4KtzxKXU9?d0ek7eYeg9yyx9NRo?P_Z!R~#{JY}tphn8HB9N6p zNRb~0zxWI>X9c*qja326T0G(Hl0ExpkXet3-MJC!bR)PjKlnqs$rXhm4rmG<8zo_A zper-eKqNTQrvUH4Dx5wY{aO*s)H!9Z;1G1tfo8ag&oWKi3VW^@>V`q+>py7971MjQ zx6f>!1EDZ*lq=#plVc>`<3FKSYybRP-zuN@vN7N!ky@JSr~nLzzFaR_`AHqpnWg0+{P@v$VS`-l9(+>|5AJr0pym$z zrcL^exmE?1YLc-`!@H9?-;5=!3VVmv*?pFgaPYRjHeH4jmL~X}P8+&BdSgJ^?0R+N zr**aP{rBHrZqf&a-u;eumLL70A1*I_$xF+&-FkhHL?{EwYtq?4$vA-_VY7_P#V)!< zujrzjI5fzNN^)*8>#fC5;3aOT^Ted5tKhJn6Tci2w-UE*aDgTo7f)jdht9L$6D*1l z4~I0)z#E|%*dS`0iRzNIvw8@9^nDaEF6&{LUsv@OeVMQKF(POUU1dBTfX802eQvIyFlcnE&U5sL~Jmnzt@43pT~7z=bCD zr~sTNMH$zg_4{7@L_^~uv^Qu8cbzWmsEwTveVc)vS|iFtG6|pfF8- zM};&Orri8&aO5mL8C0tjRJR$DJ8Mm2zj5~CoCjej0_5hekD<{%qx;H35zuzP4}Smm zmmmItzgk{%%d5*3S6mT{F)kD$5e&)&B{}@dL=p3i*TUkieXR;`p8$u*FgZiU9r@yT zo|*~qo@M<+|VuB>Ht{TBQIUfuP1VuStP#J7O6}@k?0a za$1Ka4#Q^{c59qZ8#21hW=`vE48M^<+a-N{XG*T-E=3j~15hN`I%xSgX;}&2cOlKF z0GuZ^nf24)t>N8x1U9pGcY@_{P8$D|UV%E(-LapzY4lC{uJ>|G3f<{62xW)%VI0Ur zoGzW6&$iQ~5|Htv?}zpMfvJH>T+oL-F3t$tf8Tvkn0M$K18@5)Z;e+7Z@A%xPy{p* zCgqq69*W8MNj??&$$e4H$>>i zD|~vLuRYY4eN#!?*VR>#vQm0c9FE+3Px-%}{#5y-1poU!@IgVH*5SUZ%jK6}rfV~_ zji_<&nV?^>rK6J`zJelv+dryM-5FlPQc$-x6>69Jjl*eh%N2FbiaX5D#M6e2?xX;6 z0C~_F_!}#@OajlxSQhv^S(_d?a3Pg|Q2}VFyF~Y_2KP+egLH{#W`e;uz>gwe?&E>E zY18J|51N^1fxy0iZ-L-cHWv7}L-I#p3U>0ZZZowM;u(&=4a+nzvMfJHf%jf!ZVgC= zOA$ajhWQ=3_g*cjo-Xfx*H4xoctFYJy+ZinvSF)kMq6lm0L1VslmX$N=SDbP z5;HGlAihNSnEdQt6CXbk$kgiFKR!naK!KEf(RJyt`f-keC4Me?Lhz#iFx#g;wWw7hhS&sc%~bK2yzOMZxE%h z$KJ4EW55%if;t6%!Z~gj@4{7>EUYUFF5}B^?7kuGXl7G@gMKIVDQozf4KsBng6!Kf z5v(S8W!l8JU7B+n6+HN=WkH>20*?{^-@FiJQ~;JqMKrVH$0b-gDZRvUSJO z;kT}5(eRr$Z$7Vmpu{nqrv*IW6~;X|JG8@FMIh6(=LKpQ>1vwkbeRu!!cn(;4f5c@ zgHg;^z4BG%$A9#%mp8ut4ds?seNWl5=PEPfg;oVfl@|zcSdFji@fN|uIyA_60p23u zL^`}I4-b-NGH~&X=6OefR<`km1O0(nJqP&sR5UYv?d!+g4cv-IdztCpqFAD-e^B5I?h8T-G=Oz4)$KV8(hG3Zo?R+H+ zOya}|mX_S`41Sb=p=P<1ye-g;rw{Ki3+{{|tipPN;;`1ekg!j|4mRBU;F)qv@V05a zGT>!TE)?Q)^pj6MT^@Y!0Z^2m(DuNO=)m9`UiZ54k{7?UtP{;aI4}C731*c=4j!im zvow?eT{JVVFYLv)-yhNzfj-Zhue0@520)fL67gt1ww=gE`NvhVVbDX85E%X5&l-v zOfzIbyF3pJ?TrBUw^e4*+61MGLGxS{-N@^{P1~GQ-{OE_8WaV~f`xG=?78ew0XP?R z25}tvvp{ylQugNQAldi`@Z}2c;aP)cvj;>cTUf$b@&RMGghhb6zGiy%PrH*TTHqTG z2`fBlGMzCxuMBVF7zogp12GRi{E$9?@>F^I+ujzp3%>3zytcep+XGi$b4@I>26GNN z58fbv}SiT z{`l7N;g5V+@axNUFTA#FQXf7gzJV~0bVB4VnWy`W*UuE_wh7}r5UT<34TL*mBHs+E zibJL&MNLaSF1QPCze2jYI1ZTBkiPYg?pYM@th@^3p)6ohgTO`sI3L3jKfK<3#uzLM z$kPq9nQh@v6wW6Hrx_K1CDM+4IhOt{q*&=&Tmu^lZ-g3J+i;2JTty(iR?|Em97FuU z(}E)oWAIyGhbshNa!$jNHfexuTzS&paSnI73UAnc+6v?RfJvN=V*sq@Z3;2l2gPWg zdQu1dWPap_f291-5By;H9&Hc2=*An%9(@W`OEJ8fF)1H#F@QjYVcdj}mxFxR5@0nz z+XL|lS#23i?wdzQ0NZZkC7wVwKGrM+%pYAm+Ki=r+7V{Sg-N`t&!?St>Zi}{#@7yM zYdY(>X8K4*`k0i>V~;#i4jn#R{`8A~T0ZfIpNzwO4@*(S?AQK22EpG701kvPvoljq zL7T$cg(l7d*x}$|;*sNq@y~gn}I@WdAZJ%QqoeD>iobhM`_fN(#q+fK7h zhO&To&IR}|x7#MM&|aA?!`x1N=0aDbudu3+lSb090`2ov>eA{9TlBpZ_os?&Ij&Iw zSR%EC8XtoETn90R`$J?y1EMLCb~y$n(ryJjn^k}|Upkpe)dqF$8s;Vc1Yyn8e6prZ z!5%SD$9`PKV}sYYar*~8;{^i-Fa}W#_x3=(5(Pc(zDL^wXHUcxsc(Pl+snIt;vMA` zH{V>Yf8ljyBL@c=cdQDq1%Q!XEY9vILj+jJ6V72lwhE*W#390CIy1oA1$pTZDG?%Y z=N3(eakMTX1={wA&eN2I3y;{(*JqpO_Hu|%0-tX&aI+#N1wQF4c~OD0n`$C2xb7tP zX)*o#9(W+W$oG+t{+5>bKV9y;^UgqX{q@(!jP0!Y+-d!C-!~#B5BBl7?Nh4<(TVE* zer4ho{NY!<)~QUaf}sSEhVXje7{pZG!n-TJ{9WB}Rc8k)0fi=d*=t~G-h+tIJCg$D z$D9MRX=MRCjfr2jSHjRnyv>2o_LhM8pxbj}tnzp}!Qq(>8Pm#(<;tPNvyeS101Hv3 z38zt+{j&;y8ESkD=)^hPF358e0vkU9JKbrwVR9qXJGg|m4IIB(7UKd>Djm)P2nG!_U}fNhH0?)@J*vR@^4iz@ zh4Q_B@h=s&2wrjXE6UYZUsbkl-%b)K1HvP1hWs&A8NjD7xMy-ugaVfiX}e&a@6EGa zz$a+Azn)bCuC+Dag+qhDer%)1HJX*6Kz8}0GaZF4g3Z!BS0i7pGa|dB1YW77eP;RF zbZ&=MeZvPW>N$;>mLK;iKg%fA-b#na_Pz zzt5FV{J|&6W?j#Fz1wRTE3s(;~+4E0$_e&;T*ZqZ-pDCUia{oU-Y|#ldV1# ze+nlGyzBvE*Loqy|&pI=~$vDyjLt2!F+K+(Xz+XM}PdcYJU$i`76(GQ*AacL-lB?GPvMG;}Hel!cDaUJf!$vs8aF2+G!$6`xAVBRVAfaDJZU zD`ITsBy>BMEP7!2yWlR2@<*p$Ck7@MaG06lb~As)53RO;!f_OB+$rC;+J` zk^}}H<(a!$3Ixi7RtVz0fhVK{uu8yN1&{0Zv={-n_^1sAo(xgJ(V8y04I16&pujBp zK|>fPHg9s!DYr;i^OZ$T>T8x?wyTZ##4dO5GkeF-u{wr3X=D14W8<0E^3vAFpLx36 zbMHOn>)-fB`IFClCYJZV@P#i-ps%~`x)_i2(|85U7`k!o@jdDDipwzu7>wROk0s#h z4~XgyA6-d(eB(y;lurjPua3F;z|_u0TIWZV!8_X+Zg$)ldM*ke3-~3?nbjwq=?~1% zTSHqTz?X6#0CEMqeyuQjr*Xpn$p@Yf9Et)aWm9ewMBp+W;|jnc#9|1YjMp$sa2kPq z7aI0lCYb}@2gq?qTC_bHbGe3tKxls8#el~hX1SOahBxZ$ctf}v-j|)$M1giVa2C>V zShHFCjo;@5vL4uWcWJt`ZFrWXtGe>3X)F!Q>}V6-B48=)X)*B!AAB&9zVQujEN}ka z?=64vO>ZhM(>27`>H{lVFV`oEbM`(^2+THlc6Fu!@utmTLJkh{;}D@%3D`O)Cp(S4%hnEXK>Js58s+S?)pj`#9+NYHC9KMwKfwY@pQ z^EEv>3hA=LMB}N=ntUwq8N(`oB2GN@WV!!4-zj(9byqCy|IwfPN%_>LKNTr<@4lj3 zcG=cW3FkmwgUB62Vf6$)@nlUu%vAvLSudr9<@-AOx3DKJx=G;8cf!fT8G#!{pG(KU z_U{F$0-ZRUi6a9h;T2{OHF_7NK)VD#<8*zI9HFS*nupK~Nr1M!g{-U4t-k+v27bOwGJzS?H=so;h1fulVP zI6QT_P`dL8cX7MkHH9Drb@JDQvz?tX>kM}#%>GPU!=}R%Pr^`uJY<-0gk?No6)!Oi zOn%TB+8)!@tH&NWN>p4h^rknW2)s1z7udgVU)jEWN7=GXr@%1?Znax2e`T%+iZJk2 zMkPNC6+|gP_~&=+Q3iP9fO7R@L;ZMhU7z85OJC;X4?(p$zmxA0Q~|bfk|JQmfsJz0ou5PU#dNB0W5)1DGaa~ ztl7YeCH2xBvy5qjmX{hmv2+%+#f4TD$g|DYW$KvbH2P;1(lU~)dqtRx#QTw6!iSkL zBphbjOPqxFw+dWlFZp|GfIP_4b`|EiEg%*djI!3*rEwbP3nR^VyF2AdKs)pf_RbyK zWAJ@jpIhbo>X@sqxw^dSyI&b^65R5tTjEp2FM08cbv3IlfQjWe0cj8pw+rCsSkh(MK4&3}Ak2A3Uw4L2fGSl!Vw$c&ZCSF#r_>$6!-A@WwAP$V>xd(MN}B zQMjc7bVXPy_@qA2A?rq+FljTRhhEyVkJs;@Unk_tWeV#)bsS6i2z&6v7JwYMbK3=+ zG6aR`9CenjP$}ZaCG-y-Iu!f)U;Fyk;y(V{?zltOQ6Gt9yY(%KD>U2RqxbgdOU$&e znZ;9NH%kg@lDIyuPL2!g>aG{})G*+g800B&{Oi`8jq#sj+_uvgKQOvkQ1rx4xv=fV zmS=`pD0~L!7_>wRB&DHk(wFvN!k2zfRYh@$h&wQFaNzmO8S#s*kD)lY&6Q`!Amj>X z(#%7y(Ti3Ys3b~(-Z+R)>*_PSv7LZ2 z!0G|v&ve@etQK(KkZYlNl?`_k0Ae^x+np!Fh-%#ds(`7m4#&`<4v|k2LDp#^9*-R9 zo}^JG?9Ws0&6=^tY~D18SMU({2zmYHB;@1PJqh}l)srT1CO&M0emC zgkuPN>e$uu_~TEO?`Rw7Vcp92)vx`T_Vm9V!vEvCk{5HC`ogZAyUL9>-WZ7(gN#kj z_5-AQfRO%d;qN8FyZlCP?Zq^4{zULLMEN^)I>&i=C{E`q7H|f*GJ9h5Sio*8hRYT=Tdr~@QPnKb7 zUuoh5OC{n}GSZG0Fn}`XfM=IH26YsE?(_?t61DgR#`T8#$j2K$#voM&zO;CN2P;04m=j()VCc} zePzJ#!~^g2Df}sv2gs~A^2zcrh+GD6_(y7gh8Y(M0cF~$*&v@~)k<#OI-58n*}%BbW^$dsy&6b3$D8As_uR9PDqkH0&S+Er#R{oM5J7v(NW%x z*V~DZ<{jw)Z`Q!=Sj5b z5!wlE!*!q$PdfW;8xOeB@ARI@J3S#b^zTehNKF?$d23#d&vN3+^7UaHGd?dI1zP&T zOIFHMKII&o?}g8AQ~(xfK?vUtvJ^KSJZnD?S%0Qk!>WE(<}gTsSmo z1#v$o7#HkFm9o=vC_NpA&w&IU)7XMOaal3w5P+}Eli(2Q3! z*AA|PA*@{;0CwB9ZCWDU8G_@aW{GznK3s0Q{kHOssO$mA%*O zEe8%9(Dkn`jC9+TcAHiTVjml5iWD+W4&}w22o59?LwF+q5_X~>7%HArNb)ua%*pp6 zWdW1*v~T5}6g>=OPNy7fSxlLJq1ry&f_nMBgGG_RU`eTY`pGBDV~;-;x1b$4a$ouO zx4&J!^U#A~BlPdkm-fPlF1sc0_g=jx_S4Ub&Wtf1#?{u!lu3GH=dTY$IAindSqSdF zWC5OV#sM4!wk=oS1EwEF2!{dZ=XtHS(hHuD=LKAGZ~&b){nmbmb&(O|GSt}7wGtF? zo6-;5Zf$!E^CNDdzCMJV|peYEJsu{!WV-3PyC&z^F<-W|Vo@3pbV8?OfG-C%i~mTGph z+S#=1Wzm(}VVsM|@(iG=7m(!|%CrO{sx1Q5#dlUPvRE}xp+bpKatZlUoUYcvSZ>jK zP&4~`j~prYKX8A%%6aJS!6-^%K=@y<4`_0wM`l?>lz2I@u6^f{QKQ* zlz^ZQNo5j0Zu+KIA|gh(`mltKW_2iFenXqU*Xh{q1GMiM^<}rAClvKe$MVYNZA#-N zCAj%v=;!n)^2M}$CIXi67!`me5DNmk(HGyRZ_zRP;1wcw5^MgvvXM)GxHB#rvrXT+ zEs$t*n`hJLiQ_`qrcW(fi?^23@h~+De4yyS3>bdla)?0j8`ki4TUoN3K?hFS1YEeC zM-EWnFm={%n1~0q18^DL#>9aqg-Po-!)8|LQ{j9Ca{vDQzI#9e^3v4y}YL;kMN>~+2FcL>v!-Ja&@DyWFvEF_oz z8;}d)Tqyqm@;IynEU(ywJpf2=&{eWt7nnT$=p*F`&FUY2{E71P)4H!gcj(`B=UwH9 zg#LFV{1Nnr@3~tu{5vzX6tByIPJ4FmDI2fiI$f6Y^_f+Tv9qGZJZB`POcl};j&`QW z`AwS&Tj30d0$|}=D`)|nSZZ2AvOm#Z&Ya-}KhpUcaF2h(tXsEHadu~!(pG3954#mM z1UQc#$mm%G3S{3Ps}#g3nCTO(f&cr}&rZ>ABhTS>ze$DDfX1Eu5&q=yPwcbl3!?&X zj)ur68rVek>;h)6m}UozuXUuM+;Gfrz{6!A)$|8?4ez_q_{@Z8a5TbxXX`_+zArmr z3e4#eWIZkHi8pM9LnXK&^|LSzkHV-ggAHM0!AyI2#txn&MGAVyf!8pkH;x)cI&c|Z z_yPtv@=aX$z!OiwlNUeIv7gKp&8OGb{p8J?wv=nH)l6SkzH+yN0Ly8e;=kj|Un;l$ z(XD}}ecbfYo5~H>UtjiKyEkUCySwXUcWUcl*REZmFmRNB3ji}7(iE~oF09(|-d`slH8OxHC(t=I659XnQz=(W2C zbf-RBDtF&=PkBQ3G_+>hw(Yu`VS72S|A1)7VLfKiekBjOb7)Ul&M?tyU2fEY#^aZ!c*-2Y4M%S(L!BR*-Up=V5&h!flI>Yk&SB343PqM~@!We*NQd8vh}=kLfql z%x%&umb=SNUBYyQ6rIcmSjx;;wHzgBJP8qBQ|>iu#i zeNqrRHkE-2oAHJRpLwMjBXBj$ao}K}V1PY)7RDZ2Hta4+Fvd~K2`<9|<9Kg72vdj8 zHw;=BR3=OxK`@{wrT~isdeC-fPu5i<`j4>dr>mj07kuqC+hP#AUgV1sqBfVZNZUWD zo9VcMg;(>F2S&u1L3p5XjBe!Eo5MT{Bpk-I0ij^lgnV?n=1wg z9~&61<6KnIxxKR8$>00Woa6@9vsG4g2**D+$1H$tfr=N<+7~k1X}RJR>=eYc4MbcsKRWFEb z4N{;K#HoF`Q;7RZkNANwUeioV;Syl;gIoKY%dBTDhZ&pw(4AkF6L-=pLc6#MW8s;8 z+fG02j>JJv@Mpa9Ctn}ZgT7fe7&S}22~*2}i@v~D|JVjd1(JSYX5OYTbpJVwPXPiH zao`@!#iIc2o^3)OZ)E}F61l(%na6_5iR|D(BK16=*LRFv?})TF`vE%4=$9HBu6OXEB* zO$LyRtsNLyFYJtWm<4_cd)pQ)X3)4jFbtnh!c!1iNEoiB1y{(a5Z1yLhfM5`#jicFH(O^*Xu@~ZLBkDrC)J>2f;^e+@H2{M6Oq znTg?DMcl2hH>}$VcNOTs4Pn#Y+T@aLk>&CDXLiRI`Dha}bjEZw#!vsTAYkjzuOX5K zUICtkOy=3f(}&ve??Yv^ZClvUy*d;7zXz%owoHynk-PG?G3V!wqLFiwb%JIpAYv*U@mW_ZG61+e0#uKL0l#UV zZDl;pqmMJoveY=>JPxOE8b^&!zsr|p!Dr=rqsFCIbA$nr5zIfsBE;6ZGhE;D7ya#2>82l4#8 z{BQiMF1SqKLl{*iQR}EgVG?)xpRJvASw5aD8@Ao9FPPF_VW#8S9Eb4G&3MudU!n{A zt>8~w02oN5>`Vy41ntQ`u_=8Sn1Li!Bj`}pw_S{3H2u9gM#5%&$R9wE%zcBMACn#Z`VW7_qseSy_K z_gPN_FHBz;6#&zD$xj*x?F*AiKx*sRFm#6VF?1#xoTZ+&lb}gD&+|llK83kRX`o%5 z!B+-Yi__A;!XAd4G_PqhMJfd8$2Jbqar+>C+%?>3JnQdZ6)X8Wfy+1)KZF78mP!N5 z{;b~7ZU`8ck6B{PH;NE>#-qbqVG%6!t2`B)jDd3oUit%<(^B%GNqP*!SJv9d;i#E607H;T~d7KN5w3AYH?nyusS7EYmkSF7uyl8U_ZN-?KpGOJk z9}EnDcx1V1$k3Gvq*s+{+k(@tkquLf{)kkox%z&!X_R;mnYko-C4tyOiMEu1SutQB^Ag>PXpEw;lAmoSdcfdx)zOj{VP zx+5$kHZZvoP#L;s`x>lq7|5k#Hb6Qn0iI@N8xl^PSSd_}CCf&f1;5V@5T5CnRV<}n zfg??=eTFREChtU*G+8LMaKsN`6f&U|aGAz7G$CJL z(hU@Fw*729!}1w|nHt-E2>l%Y&>X={+tEghTj$l{p6ww8W%Mb0hm=Zx%dp)bq)j0U%`F75Q_v@zR{`8kDf}%A zoIhz;=4$6jR~A;Tt{uwuj>_1Q`GPIHN(E1KZCN=-S@(plD*j_DYZYRn51B7~N}w z0<7%u+AIp8U;lNvYCm8l5apdEes2x7C1Tmng}Ga9d9^v`3lro4F{5B0LcW*-2GbDtW20;u#e0uAem+s-19XoI&28YrBJ}#y9ICEFk+ju zJ73=$1GJx8Um3|ZWf12~u#eXRH`_C9@+U1zev@(m8Xgo0GY*)C1<-VThJwm?1{LTR ze%U_f(PuuB@=~&1RL|4I)o|)hdeT)|xL6feg(s$kYuz=DI+F&0n{HxBYr8%(AniU( zg|G27ZQnKB=KXkY{Zu7@_Re1)(ms!WZzs%(AH&uz1sMCx;1`kwv@It4Z}tQ1S!SjC z!ngu3i|We?v!+f%Et?rX_la*9eP#Cj%#vwg)6gs3*!|p}TfSue z2#0f-%D4@e%pco=)Iy}lSwZMWUvQuEn=Xelav2TNW<7+jGvW$mfnNQlOY6sta-4#meA>MF zW$WiT4cB5$*tFZY);(QDVre&+KVx`lm@Nz1zTmM> ze}E6Hv>6qE3rt}e%?-yZB4+w-bTeq&bddZp3mRwp7p4*V%s&Ns6}J6chK$~`$`mXu zY+W57O^YvQqJ}2_3X4$i-XCdbcQ5mk-hSuha*)>hgZB3sg&71{D`i`>#@)e8LKcFX z0Gx-zWjj|wi857W7R0c8hG863Lz#zgZ9Fu}bTw5!!wR3UJ;+J)#L-62boa|R6J!#& z(D(~dJdV0lZ9RCyYJ*OPm7f=j`vH)I?{mLt07pE_b3cw0>IR>^S1f#h~6tDoO zjJ_{ahlMg+UudL2vM@zpx55Tt4}6Q)>75R7ZDVHN$4yMe8_M`IobuGN2ZUB21|d#3 z^Xb`xarX0>2|hDWw1T zG^$Df;T+6AS8bdYh}lp{&$KK2Qp@NR#@{bn#*Fqzf#)&>Mg`!x+`41kW>O#(ffOtj za-GDSnS#I+LTM9L)AWT9aTtW&a^RJs1viSwxsNB1Pd#!)f_(G5LZBGpoJzTHKrK%q zNqgZm8B^0bq$&{!v5;N!7~;qjgwt-0ukc+?3ATU@{7~QzaV(H@3?3=)9HPLe06d2p zbF9kwQD7)UmZlIOd=?TKvlW9`Lfm<^<+gBYLvTB*igy@aVKQc+vSs{A+6(199X^YM zv%*~3e)@&sX4TExH8rM>tA>xRkpj;j3XBTC^QW!Hnl6z77AzJJRT;p4ZUJ1;4~2`< zrNC;1eumYw%xs70+t}s0ms;k9@SaauW~Z4|*EZMb_}O^cG_&2LquxWIKO1@s9Vzg9 zrNF2FJYQRWtoM>AV4;+PC~br^OUT$uK~vKKJuT=6^}aoj?2Tilarf~UYO&|}q*)B- z^5Q4;6|F)nN0BZ}xv~wWS7R;={^iwlgdQnyX;ENQ04}Ze8cVxID3AhYBHX&wfoY+= z)B;b>Rx9oyjwNXw!$hm#Uqt9CdJ%|4%UEerG1ag|laBx+1x5-q6c`nNh8UxBq`>)8 zzyfI~jI0nWXSHA{C8)x0DbSZ13^X*|rj?>gtuAAEM+&TE3XBTCT5g@OY?nL*tPspr z4yatr<$KEpz^>I9{snFh5oaM)W~fMA;G9QvBLzkZhytSmFm{8H0vC}2ApobOo!^;n ziv>tePiQ08n0r=PbIW9n6?H7mNP*`y1x5wndENYD<<}|&W(mwGJz=dDUgf%!%DRTh zI2LWBz=~3!J8!(Ad5p+M3XBvODKJuCq`*jlHAaC^0a#-VG8Si~z(|3S0wV=R3akbN Z{y%t>pLdzx&T#+$002ovPDHLkV1j&!dny0` literal 99849 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�vg)9FoZZi~G>J|56a#Nu`6$e1IG|S%b(^!Thc3o|pCt0POu* z3{guoFO<7EejWk2p`7W7l#IMMU0<#Ii-?Kb#H;N$ZGNiP=vnf2)JVP~lh!|0`XrgB z{(ktAH7P0xWHAi%wqwE^Wc>+29RY?f;_^J$3^${~k(h=%^8l7eHZSL-OeFR;%n`je+3PXB%Q7g=p1J5?-v{ME=>e$;60@&ypWVh zer1RSA<*!#)K%;#Zv{Yo^dq@k)8ZPT1wW;I?IV!%i+Bz$q#DzqDfoM zuPP)D)W~n==P|AXZ!$a#8t7osP1yozw?R;NNPS^FyU*S%W9f)yw+zS$*ms%eGZyHS zchX$3X<*zjTSgou4V418e;^vfKx*ly)kljulnN>J@$y_b9P78cK zZJ(_FlIIZ(gk?d_oW3E7*{yAi%_)J6LT(FM%;laTd}?Bq%<}prnipHoU4X_LA6qL6 z2QjDPS`yNZK6{9qE2B^IR-q36Pw$~xzAW$wTySqbrs;N|2n+)P z;H-nWWD>#biGD0$jVEJbKk=)t-poF9M^gw?8Z@-1Gw~JM8`!<|6dX(li{b`0!sqe* zi>QqLbm`Qf;CFhbB{{O;X2T7YO`BLxKdsOaU==c|yJI59^#wpn-rpLo1NliYTcN6o zi%so~3xc=?#!zPvHX29HR|)p@u2{JfcVWFS zTOM>{B{szYouFBT$QfIutCPG5e_&MAl>y&N4KYS86nGCg-^|Ig2+10nwZUJDAD|9DWvMO~8ow@B1fHS1rB zb@ZOs>fYXa?3+<^I4F+0x4z)D60<{}4rZO~n)qEpRG34r0x9XE2FEARE^q7@6|3 z&@tBYg~AQK{8z$*BG*ySJOmh|X6=DTMft&{6w-8A_TTEhHFk`=dZ}dU!hf@y_KBHH z+VGCkSmJ{7hk{LQHWt)eLos{2YL-otkv)FMdxAyxy?Ws1q2M=68W|X{n}@*kzbW<1 z3OfyLuQZ~9TKAZ~O21fRjXibe5M>^Jb4n4@NX6o~X#Cl!k8X%oBbGQ&*FhVVnX4fP zFs>7Cb{MPSJUY6bO8PHeIm~z`gm*KH7gD=;NZYOOpu%bAE@85lIDn-={kGKXZ(P>I zhK3z{C0ALCx9O;M2rvkK zkIlqopa(Dpy@X%-$~pQjKTBpWvez&>ptb%*?HQz<`)qufsPkDe?Z*A;$;U^2QPGI2 zvhqaM?$U-xckyyNRViT4ATGTz$@bpuIk74!Bf=PuH-qA!#2uZRagD#DG6jR5=l^3j z4YLS_l_sYX%GC(A;TNsjoWItr$HJ*A z^+QT`gw<9_%|5xBKU1G)%;0bVz6(UP14Zl~&*i@vZ?beT)x*{oaeMdB=g64zH|(1( zVPt{xpt-WGeo8P5thN_f&~y?&{{dzsA6G2AMox=b`1U$w4L1iLV-g)F`srkWDTX{0 zKVXz3C?%8TP{7x6?R4~Xh2%t-uu#iZqDTg#b|3^+SFyLGo!8*_?x|m4LhDz}^gguc z(97aR)Gog%tB{L-%gFD}D*C(p`4TngCp-9>8SAexSLH z6+-dJ`)1;ZXiSRYnKnZ=sj=HcM~9@!chwz6Wz^E>Ht2vTMZ%PRCxFJsY>~F^(Q@QW z1OJaneYb1FC;Lh(K$_cje=rTqT9{vlwqqjz1HOWyBFRsUKQGt(Et)8ts8Gc~kh!cP zv>EKDmppGa`=`c|%-^t9NrOQ5-BM2MT1!)rW;hv$4-yi8L=ogBSVTSZhu`U2L0c69 zGN7;iC*$)cU1oD7)lg>|@uj7(Osw^nNUf~%!LQ`p|IfdFR ziXlDFDC$_IlUANMS+lV|^mGx&-LJ;F<85w{Qi|ielXV_f2up==&v1f#qn;H{8n&q$ zP5ssrJs7be*VuC(YP^;NH-M&Q@Wb=Ph8DTpDFRzN5svF2B6!D78)9L4BFWltm=Gg- z!yu$dl+!apq z&B|5&qK^MrsSO$`5P4dX<*|66^L?=fnYi5UDQ?SJq#nK~^|=nMu2E857kC$$@|gD& zPVdisn^(SQ@Cm1trhMe8Kv45->Bb80bi3K)|&5((gnX`$G%x?a|&3k>G$xjhvo&XUzNK)%?6}ii!=&5PV|R^yEg+i9*BOnYUd)R=xw+$Kp;f(ByX*j zB#1dUdN5g$WO%d+iGDy*Bnf#vdm(=FJK?WhTWEC@Jh+;j^`|YVvNk0qs6$`q{pJS= zE(X#$8&I(-0v5*&K1zyY_>6)ei~i$kc(YW=nmap7^vzYa!Meu(NrB$qJ^Fm%#>b}V z(1|7C;C!l4lhEqR<25wdiar!xG<_t&Of*sGIejH6z1@8?`-BP#_)YlRW2FY8Q8TN& z)fy2dQ~&9F6PJR2x*m*4le$?~m4W)k08XHArbdXT9UC_+dKtUbR8q1Vh|Pg$Jd!{_ zU*CuYEsUEi^FBtK8U!V3GP6efae9BPO#@2O7UuU;@l6RpV({HS|5A_q?)ac)tqIDzgL34 zr30mF=A-392V<_oq?ups^3!0n@SO}t0q%wu>E9wm2kH>gM12e|yj^xXW_?Rv4IS_| zXW;abG4Dj0xNWfiSk}}65~5x97~83qFpCqhBuQ5K(*REZL?;y>lt`v+OtgmF0%lX$Vl`(>*&6ox~2y*LOAW zmfV6mt@^;+uU5Fd9$dlPGlADznJa5{DAcN3JYyZh7WR_ypabSD6Pv446B zAY(+)9m!VC2XBdGA1V%86ui&2wdgmk--)sbcyGOptK-857GG=*K~w00_smCHP``tO z`n=!xx$(kVikkgU*za@|9qAGb!Xmnj7S&P-^XjcZQv|y=&!Kt47MCLa{VJuiQ zh3Xcu$H8TE%rYSL);5F1f&$EaGovKFS@phaDGfFd?41!i7KEs07W1))xO(s-BtCC~ z(Nn)s$Mv2d4rS2DlYbVI&L;e#<0Cb7%T1cLTJM{@;Jv2vFE-Xo9!Z5EMPGvr>@i1N0YS-&>%7p zC9G1Kl>~91b1RpSO-Pf>SZOy@f#|$X^>ji;T*MhqF1et}f2ECgmJP@+s?O1Q~ z{_lV>sDd=iy5Ip&jnv0>28>%l2)o{D1;%|W^^baGdw@u5V8?M!6Z~F1$R+f-WR0Ft z&1Uy2yIDz}Yigpy3AskV?$K&0EId+)N|4|oK}AVyuChQY>EOSGgBqt)$(l#0Ez*8r zH`$WDnOp_QU*Bxl3~nnU#J0DsF_xpI9C45UHN?9C%NWiq$AdmER@kXP96Bv!mG)Pf z+CpD*35lNv#Wf|_1vU9r`ZL+6qf~V=e_HCY{UvA6Ac&^NB~KZj&_L}$V2+6geK>)X z{@d1VjHN~ff>g9X8s5yhId*&#=VWF+^hXj!6b2xSQ!Z zx&ODjRv;-Uvl)*m@l_BU+C)#M0?N4hyr#y}A5YPjabL#WRnLRd1+dFYjmNV@t2ii< z9*1pv%>^;LBk_Y{T43aM77iwf{GEDMJ=4`-bm6O=*L!$kw$JsjFq1TcaujxQmRnC# z@aImbVnLAi-`A~ddV3BEI0VV~3MW(=0;Nzgi7+}czT&+BUA^`NA$|)!r+Gfoojw`U zP;c`)2nGrjIM$ZNqWON41AN?WCe^hjQ*!)IQQ|*skZPxDH&?>&qZTiSLjW{Uv(`@* zlP0)~F$^9EV=$x(FBLEvzYqAXDUew(%QoA*85Kz?vvNRTgeF6bsN9VfYt-6}xGMKb zkcOg2et%nG)Y{_V{=RH)gGiPj!KHECS?kl-C1qa?FhR?q&$)^7*Os6<|98M8#_?pa zRTiMhr&Rjz^H^WBFwom)yKNZcEm`dLijrE5nhChZd6t9fw!dM2F3Sd?+z8^Gp(+{WrB{?)7sQJp_FQ!{T%mP_ zFv*@>@Lt{KhXU`g{T#063ziq;R4GLRPpC?zTlXKZ?s3ZkX5}HT!?0w5$`#?9hAoX_fwUT7PbbZ@Zt{c3#E)>I;g(cp|s3NeIJYaxsWj z^TS19!hsV(0OI`{7qWRV|IfB-IVM(AbGjUfD%T2;twXBOaxUWm!tTfB!k<8alG@Tt zzu%2Q?Y!uK59;)nD+QUvf0ppevAhN#K4h6e96;RtRcNugxH3juQ00>!6SQN|Qx(wF z*`EE!Py%-40cZ(17X;Kyh4Fhuj&dxi=rs?9kmOxh;}UPY67J2tSk~NO;(!`{;vGtzIaX8tCwr~m|vwM$TWS$eyOAKxP$W)7twOe{9XDa1pu5mNxa_Q66tOE4?0pWIt}&B5j?C7X`2G zJpNcwkZ(V}dD(m{uySBC+4wa%vEeWUu1S+Gt(3)slAGwv(;OW)C>9A|ow??g7qU0; zTIf&zypj10l4rk2unBo&x zw$EOzc(}?WzH2r)TnV?p1ookw_~J8Q-hsq`b=~lbPfoy`#=Ix4ItIAKaBnolyV?&V zxE&;hZ(b~LNL#ReH+V|7GyCXdc&{g!i9J>29dLxL*z@7GiM&>olI$Te#b(uoKyWhZ zkABnP^e_cV+;6hPLixFRQl)G3f-}mGeRF;l*fessZ%yU?Loo!%xJQo=VVY3v9zKLhCK`ndLwXLPpQ;#o9Y(sUA|}f zxwfJFhxHCM(9^rUruw)3jT-WdMQ7@URRe!FP}lylH->PxrrFwH8rxwz@2ACK^EUVB`wIv9|*4)hk@IR;tM9GUt9^lW1=hyUk z3dgX=3+@WxT%!vRMC%fP-5brS43oXjOJ|7X;4l=81NuXBJ*;VkD;t$3M}8SV>jlk5X7P7Fe26^;>7z)K7RHu471{p+_^3S+2SVHPSqIU?^zG z{bKBBCdA2ziwO+U8}hE{OAMt52zVhPj~Tw4l^Fq@EiG(Zp0_1U1Prw2Rgfjrml_$p`?5E ze#?~Uph%>OaRdk~TUhk9)13lc!mst`Mz`i#26^csRHjr(wP8$9HFk!5STM9a4eXuB*GYYj z4i5$*^MNhGpe8>WVn!>;bRtH6EWG(iRp`M#rVa!uVmM_oB(m&>9{E0^G07fx8;n*M zC+_>)kN_4n!h2NS_(Ss+grr#JO{HkXO$DhwHrgH;d&1T|?3FiMg+zl?Ph^7!Q+93;iM41z*fwo0v986t?b@T-S4B=8Zm(ImxW z4pDdwzQ1#;w`DLo`V+>|;sUMow}L$bgbi^xBvfXOUdn?Yg(I_7tL8oH295OExMNks z-gLW}b@{s$q^@f_BoUf#myx*O$K@&0+9}*(#%5KIyNryQF510Rwcjz zzI4v|*}9ibhluCQdY|crv2zPYuy$DWR2fJy>WjYNyJ2H<8r%Ax z-}Sz8%@>$+=A6CPUeCJk?oxWpMA`u^MoskS19WoV-`|`}oF>VECYn_@oCQw-UW)QQ|W9$-L3T@EVw9n7+$!otjyz`u*}( zbqLj8$zP89ut2|7jLB4{<~KGcg-S>Pn%LE2Lh(!=-)TQieggONNj+4K5n-5G#l>Hu z#Y=)!GkU0&qH!zZUZR&$yL!d+hmnR5g<3G)U}$?sK^x4Y1%*4`NRL-(;3 z3OZbK@OKi;rbMqDuA)j6j%8Z7QXx(u&_LaQrbFe9qB}B_S^g-FL2kXd?b=pFt|znWDEIR2JlZ<~YTx$Bq|F*ufg+H{F;jW={*dO0if!_7;~8yxd5SSv za2liI;cu3Jdo8j7A|+NkQE-va_*caOnIO*)iS3ULnazBuu&BszSKUq z@dKyRCVm9;v?5}-J5Uu)i!qfji^1C+q#pO%j#+K;1mfxeee7X7pryakSS+Y@M;tMX zIMXEDWYFG3A5bzkn2#Ee5}Desgp43MJ;>zn%EhufkIY$mi3Cf=w3p@9mlxel*5aKX z`fZ;(mPHJV0#U5yFTRgtG!!>66AaB6lx5A^#fM;?bG7T`=Lrq-EgP!?QhdK_g#X0F3o|Tiyo_iz3mK`;jD!$ z;xggTBCxP!j1s!t%#%}|oZtj^G3QToeDJ_Y-M-?U6>1FY)LKr$QPX`Mk#P`Iz^@kl z!7!jv_`RG+kKa4c3eJ?%W?2%^Y;{5T&?k;>rVq9ak-sg2ne%Zzgj-jcl2Jz2n>zv7 z(BV_>f)5aW=Bws&*<#eKlKoi`?4VcT1Yz9#yF?!3_X|vaA^NwY{!iMTg)uK?g(uPAB z_dG9ut6(?;GR}@`9Nkkfv zBJfIbR>srC{GD6*5<92ptQ-{>mZXFo>`5Tb^yp>Mady9kv?h<46D zZwHCg(6y&}i5m9AJ+244ydEJF6PRpT~PUF!Y-Jj`iub<$1BbriJ#6T-J z{d^Ops&fvcJ@P=|sI+Ju5?pQ{BKZxwFxOFcs#lknrOk~a>}WRqWF^sWSJz1_f%f8S zD@%#csODo!D83N&Upp`3j^2O>ztHjkPzFLo&Th&)2I1h{7!*;NJ{CMEKizp*wbbO3 zkNH)u_DptLi|U;C&T2HV0H=IgYeHoVBety&$dy3>}1F(uNQ2)b;`FPxw~r^+T&jvKS=H z>~$NhCfHtgjq90ermTNceejA=>((spQzZYG*52cvi66GGo4p`WoVt5k0mgGT#>Wg#Nsg zWR<^mdEQNGRaVX^I11j=9l+cPBaw8{1iXiq8^xc{R|a5~%CXGysSmFP@Ia7Y8ELMN zMrOl^?;zoV*o-o=oHf6*5mWDHsPWsZdjZmc+jRaU&+cw_)00}l?>0kXrSf5tM~qB( zV}(CmvV3M%R_eM*1tW&8qP?#0g{Mw}dYBujbVOCQk35ZI{Oxl?4W9Q+3f^AKE!ORG zC^9;yP(B+X;eQ8MoE{H0-H+45UEFPE#dGO#S;FWvza}u+2+?TdsYuDUm*U&Chyuv< zNNoXQH&?L56Z1Ndm1&IfaA*0?-RevL0?p*2cG?U$TOo{G%KdR!7Ogn@!u%-_1Tt@u zq=G`+zA4Vdv3Z>RbN{y7eb#xa;H#MVWV|2$)#&7*%TK^ZV``NVYBcM*V94uKN}3xdQ#>K@%EUAuoZs(B0i$j=%X`4P{#-FbD=rh}q#O ztR0~~(_^2Rw08H()ceh(nt)9bPbT9q9KOJb@1t464EIOm#fZ=D&ZY1Ju5dm74)~0z zOjeV&Y+#2T^SI6IY%~LqI+D5iB3F6FXDf zeN+F;|1#L`Vu?6==x7uDCE>SX=+TA+Ye*Fhly<;aQ{@Y*$7o8@Dh$&?rPiVltcf@i z43Ht;F#_DSx?LT&3h*G3?o*I)f8Ar_4GbHufYNoG#t2NBp&9fvS|3!b{mTRZW^?%m zfIy$$qCFo+3|8g!M#=v2;*&u8Z(Yq{zy7&B3$&1SMx-{Fpv6FJrgXj2wb{7KH@FUf1AN>j_?- zYDL|{EPZk2`sc^?GO>2vAHWjJWSnDO{jH?}9-*3C)5-}ZgL6=z!0AEL=lin`$nW>M z<93(Uz9Yhaa@3UMnLYM|%att7mAR{=xzIiKC${JEcJE+;>csB>AYUapzClfejr#D+j%>$M7ywCHV}*0upKoBVJ4u~NxR#&LNXq?IcJ3bGGm|9f3#6B zlXm4e=^0;52ID=|6U7SXC}E=%KD%3i>y)o?p+JSxU)(I#uew}*YVPE!j7x`}_{{w? z;F){%VgP{4CxTUNO>H^p7#M>TYRZsG__r4tNS%^v4@Ew&)c(N-=$;KN;#LUE`^yBj z55-VlmR}cuyPHLzTU2VIrPE+B`9o&9Ua43}_fKNn-;%jK@Z02qIM3U|E&$ud65YNT z&td&?T?donlqgPOhJMOvy6UWQ#|Srb&8<%_DEyBJsr$q!JiW4ZHyQ z3mE^lejQ_M_TE*+$YV!g`s(r9PoNWTHi}xrs4XEZZ_VVgK2ox@fzovCk08B<+$EBv6K*klll=#>9BqHw z#a!Dd)Kvo?4lsvT$KA=%km3HXAIE^(6G-O+m;u#<$ao=>a93@ccaLh3;WQjVtuWU~ zOybvbj!6EtdXtYY`PncQ<4^ zGs8^+H?`ILZ_DoycSyHLH-#M}6huW+E*_4>*$G#F?i&evJ& zLxKhoMjC*Oo*Jtkd9vz1n_sjC3ek`|tE zb+Iah8{jR!sTK`(Je@@R?@LevH1358z}Oi?rg^?yCfG@k)TT9xY7hnDim z@1!aBvR!ZLSBN1AtIxrVSCblCSCN~dtH@&s!?L@0E+8^AU)uy7#Oags)wg-Akcl5Q z9T^ed39?N{z`#6&`?F2Q(X@#VO`f`D7!qlY;CC^e*(*yI$f8d?*DGxAP3@I8w4j7e z`}V&gK1FnB^(KE|Kn$~f7S~nAL|`(XXL&deh5G$Cje;cXn|Xf{CK(Ib$t95Uy7H#%lnQfyCk)tP`(UlKm?ENt_sVp4BKn{ z5g5`hF7O&zJM8=)N-mezOsh{)Bml5A7h=G|D$4VJ1s&HZbsSF)Bq-_Y{aNya4?zm1 zG;$$Yx;*7U-t5#snBn0hfztW4thg-n10(M*lu6UfE2MX0%UWVVyKlh0;$`90P_r7L z7yk-Ko4=0AdC$%uum_qM0fkA>_SP0C8DyRa1tP(4A?(xs`aFj|4nd2xTF!T83)?B9 zgeU#oW5z2ivZtC+52xOQDQ&Nzxc~77ND@PGWO`r>byArP+y@_FMJh!f38*@cRx~m7kcnsUsx>9p9X=KxMX&OwY-ZrJyD&#ReWpX5Z>#&r(&iq<2a?#Le;mlEKP{5b*75f{6=<`@7~3U#g5agzo?X60b8CIP$He zCiB4XK&hlUv^>1B5clj=Vtk#p?$RO2KU|y>^3vU;|&NQO#Di~m|b39 zBTF@(%@$*oiUrnS|MEv_gOHMdn!>AsecE=DnI3>eaIjqchSqv+kjVZ8m95pJ@}CDJ zd{;Ym%oywk4~zAyt=k0HoBlcF4$v1erG^HEQ`hb+L7Cy9|~C`>^p*6H)2Z}JCi`qt=O&dXqL+X~>O6G_S~tMVO96~53L z4Izr`b=V6Z39FSVC9|KO(0jPg1Y9}@wBF|my!&``zw>CR^TE9OIl`EkXbS)@GkF@f z@KzAeB9qPkJ3CpYSCO*m4c2r5GF+ev?bf{NYMZCI-lA1F@bD!ts+}821nNl4&kUAo zt!qpQ57Az<)1h=j^iZNNFU`XFPlz0nL!H~e1H>Zj6A7s(3)ciS`8xRyvnICNv)bzfe)BLeBGag?*-I#V&+~x?12v?n!ggASiay0(L^c&IKIM!~V7{e0G*-Ly$lbeC=w)xVu<-h7qx3 z*I{)Jx?C8LSN_*EKV81KBboa1=Dkd@kl^c7j-OkZ^=ND=$qHYD{Mx|$|LsnBTMA8T zHboCLqQ1~;P|k``nIV{oU0pR8^#)hl>vj>*FI)b|MwC16ZO``n$Jo_zD#a15{kOh^ z@zj;tteB9oMq6zqYnO)W9P5G$2J$cG)rrln~O?zzZ;| zj-^(UWo>-2%PTTBJFl})Uaq5v*NnmxMwyvPeOf9IeK8J?C-Gj&)zpup=7gP;hG28h zW)%Qkg`8&UpchZK-81X^xiJQvUv#%7nW<#AqwvvFef~C`gL|kDi0XG~SJn(%Gf~^| z=!)nLyy9X-aD#9xpqXNev|8b^pKB0}J<4>8E@I4pg?>@=O4 z$?APeAcpPxp;2YiTGe1x`ZW(@Rx#65o4fO0oB;|dIpTQJ)herjU9Py~t}D75Z7D70+Qk>tb(U z-Mi1-29IpjF(-N@_hHc_9~2j=9&}1RKxirH$g^)Utlo6rtmb3i9MYlv>bVvmtH@OPKT%sx*r$% zQ0^nV4bq8dt^%|&@i@=^^~7*B-4Ve!H)K9s$&F0UzhC|l31G{L?E@nfZu%q>Q73x0 z$QgQ055%Cx_7|K#Q+D6%-b7j$ZbgkZYqBdaT%31TT|}Pp9&<@z-DSYCD&Q(tn!$<3 zR9*tZtg#>ri?Gk|Ukyf3Cy(sNa@gU%wLuE(F@?Wc^|^;uHHZYYpT^5cCd0o zSUY(0t9NbL>Uv7MfjftuTYE~U+ITh5#b2=}Sk7`se#c;S3Q6fl-EktZS@b-8AuS8TnrOxBQf>M?Z+ zGcZ#jt(?YLnvn3-|rVcW-ljCn(%E^$(z^x zbi94~jZ1xHl=}Di%+XrcKyCvuU)lp=p7?X9Z+mAnIP28gvK^QHxisJlAzbTt09xFt zh)=!yZ{<40QfZ%%aIogFM&uAL<4GWvWXkr;*z$L~`M6Z`OIdX|RL}z8E`M;^8kB+} z)h8GRO7U?+mP$2WR1nF@5wH_&ad!m6aapuWMched=>{_4rHQYs%fuHigm<9iar`Ho`u0 z>e)!<%Mh!_`yG!uN5$@<$fQHJZFfuC96Q2lKm=@(M6o;Z2EY5o&436P8eAKI-{BTd zts0PTTEWtEzSr7_>KpPLrJgjSU`Z%8N9bEBBgm#emt@j%{#WJn1XcVl%eOWx0gWuu zjPZ6MJsr5c0r|QQv1f$09fD*ABPm<%6@7~b4)6Qk4Zj8K@}`(-H*UMT*4}S692p?F zCBtvK6-?0fVujPnWbc1C4t(xUCr>SmOZI;R`Qp)Zc0W!P6R**ImNKbFIHYKakEx<0 z94xH7VVAjI3ggDQe{Pp&Xn(v4ldu1V`B;3 zp}VewVf&oe^Pfb>!X}YFk%PB8cwr49@-vdqpQ?_vCkP-5zP<#ptVK93fmem;U~7^L zM~cjgj(R{Ud{yq_bFoPjjaZn<9yPBy^rBW}^Zx4Mey>{`j;Rzyq5J`dMBKU@l_>u3 zZY|Y@ulZgH<+~EG-~eyp`#=2U*#?FL1tu}ykG#XNusZKo{S+)CNPa!t_jq00aQo_# z%uUCfY{n_@Ai+vA4m%`4LyAJ*6KYcf^nct7=taKE0ki+Ac7X_BAVb3IqBi9UC9+q= zg7>}iEpOA8R2k~MI0S|i>cEs+hwg`u>|APktOB(guiafNEp9$Ve30dzx(rLOU7ro~ zsE8QqzCEtlbdcX}w`=GdYi8X_G73!{@U8khvx>i8q7qaVTrU^wQnth={1oD@is`d| zDfYgyA~S6>(5Xndmjtbpc7*Jsd&%ePH`l;}_O2hJq9&=r4(^ePhqVcPzg>!81pNY5 zXkau>zgC_K(0(hHDwitfQPo}Yli?XuwH?uzw!#5F^hRP738NrxNrb!(A+KfdlllBU z0feRWqnz()16l+v18**xGY3hhG(YV(qUU5{{^xNYm#dk|1;Y%VEdtfzh}wqa*H$$M z|DEwd>R(}WpfCrCD@(U%KS~$zT$yxr8lAsCE-3T+7Qen!cOfWNQHxXI;DHCw4wZl1 zkJbw;Z4}^12S|S1aA#E^+ixxWy>3^l|NE8uZyNiT)cqm0V!stTJ)|FXb{{~{f`1EZ zT>nK}5tNuFeU7tPVGDgg(R=n5cyAHfmK0~6pd~`+NA3iza2pqnUM<>{6iqI zBy2z?g%I!-yznfRStK!iZu6;d-J|;pgA5Cjr?nRWqeP_&rP8R3%7vFPh){>=vM5c>Sq~N@vllIR=TjQ^;CrVI~+xNoa&8`@}kH4B* zJ@1)Ak))`fzs*(OzCRR7drS%+BJStxea1K4i)dKr7i~3jJKMF9>NqvVVAXe9m`@L< z1b~tI94@cXj1i~&u@gm#rG`DqLT!FGyxUX^OUgRmlmy}Js8vS}e9%E81rZ*@JBpm7 zD=AzRTAJP7oy_4h!qYqalR?)2EBoWat?mkU`#QS1iG>fq>Q^jv&1yFOgO{T9fs>nc z;L8r{moLYhS-a?CI1U8G$}~D*Q%@7V@I9ztE||TDA6Qc8qat}b?So#$&MzC?P2E>3 zPQ>n3_NQni3gSgtNWD!Fg#xz89>;01jQrTAJb&UC?2%WLwGyGeqGy3Zpt!HrH#fzG zEMNY$0|8F25K>dcV{ZHIho?6IA9a=@)E2*Dgn5-S~s zl|JI>;S<>A+SQC?Bm-YWlP(pBJ2+b2xqUTjU7o6j+zVYwY_C?QvR-?Um8jqj3%(Tm z)iLtUes4!qs?Q{QvUu%MK3(jq1NTOA|#E1HxI-_wcF34IK zOwIlpa34Fd$ina_coBzEIHM}#fyv19KSgt9Dd{lqQ^;C}rH9G?8L%N;qK)%=!$JnX|>^7A=o|}j?FOyamJ0L$P(|xbo{My_Hf+p{x1(=>P zvBim3q6$MOwc8{0LPDf-L4qMrD-L|omTb@ z0k6Ms0i|QeVSTUyW@-gWo!taT1a;{G=Fp-tc#S4s#WCZsBu3q=KgJLVsM+;?ZTO0J z4@4X*iex1Uj6xH4b=XBGfFf$S={lOb!QA7EVCZjWMiwf)U%y{7dwW~l&T+1BUFD(L z@rX>prQWos{1LX?&2x+HF-j{VT`kh{aCvMpeoc)ka9(q3gkTZ`&!#8dbq~kmj9-6f za9*ophVCh7J2h`y!280O?lxus0&ta%ZEvUIrN^py81avbhj7+W`BbjA-(*tgoPN&- z?7$1MGr{@(Xm!fFTU?W8@+bt4E^2cqq)eLgpC2B1y$m`sRQi6R{$o4ivwqu%%eY(7 z&G(zn=TUDHy?r**4aMynra`5EFq2_WxTOZgbr(+u&w}cd=hGwaXO6=A)J{sGHEmAE zq03H3Em>%XR>y#ZZ_e*)`0l8J5|~M}$pk9;MAW(PJvc0VV~2hV*xzA&0Z|L@CQ85U z%n@yUJM8V!=G&YdFlJXCwjEORv83?#k+mxIe%smA{y0bv85tx~;-vc>Aqt|^FHJ^6 zk~dRCph_{60{Ia8If^m5tLC~74@YE(6H(QQAU>?Lg9arT<>mDMYbaNz;q}GkA?D{J z>V>i^k@+L@@uek>=G0y?sTM(BSftTkXi$D&?v*F+7K>(Pe1W^46b~ZAdQ^`|_%gXH zPg|-~{s+(#QXKVSNT89ZJv~vp*j0;ZIYZ;!;b-$JVNCCz8-o0hpIKS(O7turVZUPjg zZSnJ%jKeU&6F$e0p)PA#VG-hNFpf+2ZC;kcCB>s>6Wu9l*>Emoy|ab~I$2 zx5AW%J!G&B`B7siiqD!vK@y^z&tC?T?OiO@?|YwF)T#rn*`rjX8gbTqIz8sfVyi8w z`5aOZCbbb(Nc;`gu+a?sn=Id>f38^R%~f{>|6pcN1?E)_?>BylNf|_+VJ#OxXN?q0 zdSAD$=`4PvU8O*!?d7*{AXwypGK43`ZepK)@F;(LxoE28qja$18YIUOf}~BJOm$;D zJlGjGiGxh1bbacdW35WHCq=grN^u}au?``PMD%6HNfett#VgYYw*2%{Vz&qJUX)#T z%A57iEq;b9xh;-B%N7&cpP60MU@?OMJ~rVhPfmGdA_H-WC*MI?CH5)nxrjzgqmJW5t$mD^z>_9I~{q!GC)N`HkuKk8m)| z7=b_Df>+@v86GvhRGDr1N%l0%|8pS1cQO+sT!YyQd5hFNOv2ALTrF15u~Ca_HD@r@ zm&t~|p85nkjp(vR-uhn8PA$Z{pE~&YM$m{1;PJj0-LFGj2R z3BRIAQvD1aoRvPq-t0M9TW?b+@h&&J*X3Qh{)asNa8sZty(DERHl8A1in4y%?S^iz zN**C=rDSf8Oh-VI@vy+U9)0SopCUn#pp4i$Uby$|a`Fha?lIZ`aIA+hXWfNGQ>XrUB-sI;X(!guvJZXIgo1n%!7C9o9&P*ft$-IRRRBH9*WKI{ zzGh(i z%jCYw^O;t)`#Q1DAtwpZrb{)l@o>F%q1F0ugrf2mmtAr2%a5aoXk}e5$$so4i^ubU0=IR&IeMCt z)EfsP`BARDFn0Qx{H9nSwwu!R&VQacJ-q7}E*MVK_x~iD%l_-%S{wJRA5Y+Xb_)AN zb-bq|PPW@apwi|t+HI6;X+ZjIqRQ2_BFhjCheAA%LR z@aRH%tgKKyZn=4Q%ut<@jB8=w`=_(?y)N3wT^1O#kK5GKi~K{Xj#sQfL+S*>Lbg&+ zX`gnw6o!mvi&@A+M((}ZjZ$|`S-SS5!s4a;Pb#tXk>d%T>)w=ZrEBPbslmwEP}o>f{fov4 z={k>&jMTA5IsOn#D98xU#T=>)oDId=%5%%Mdh8VnJU^9+H&;HFF7DZA!!ex>)CN%- zoOVCo%>+AR5N&+;HQ)sY<|Ua1S+(|>pmaTqGOpjO5^q(dZNaF|TD|{E-^um&I3>{h z1VIgX3u|)feys9i2pN^t5<5TZWiUcD|yjK*H=3E8` z6tLJDr0w`IgG?(id$Rh0XRhMw+KzfLE*D7Zt(E|Xx{#06V&QZu3nFtsKi~Wb1QfOQ zt(G+3f(b!@@H#fG;mqAS-IoNuLV9qVKj#CJh>+k8QmURbGlNV4TE~t@U~*fN&kMj` zv(~=PU8GQL>Sw$CRk7ft5c=1{+QmlW+shy%FIl#>M`6sbW`&rkV}C0=`(ajw_?^T> zMVJrdCrI}roLq*2`X>_@O8@M4b;+COiCb4BrE$H3`t@5Wg|4Jmp$WLqOHoNyd~)Ifw`yu_FLUd_`+I*cgNR z0)o|vn$U}tVrJA@A%PDH3OeYTObpc=1~Kg*Dd1E^BylUII+91ZXD`in@AuR-uXWw= zRV${N;SZZ@K{Zz8pEeUy@zz<~tgim5$n*RV0!FnM|7^q8up8ZFJ9q9W;X2up#`MQ= zX`3uJY29D!r{$lg^XI?9CSCWQO<&Gg&ZxzcKGR5c9WM;PaOBft>?ULv{ydBf`b0_W zem0T6YFZH|K~7MLff)R0uyF6?CA8F2X-eBL#Z4qgGL3~kOI_aot&XfrW5O~j5nYg8$>bv-HHE42eP#0I8KS0nrdyza;MJwJ=Xq=R>e=%b|;pj~ zh{)1A$|le2DSc`byPO1e0G+wZ?$}N6Sy@@6Y-ebxn!5~6_FHe$9Eue}yLd;!`GMDm zKGfN5MJDSH)WsbHcZ?!n4&pU--4;jc+@{V$ZI~^(?}B(a$~1kcZ-)|KR^RjIQsuhl z=H`7&<3NqSpd3Rl3_>L{nc0*nA!wlkJ5ze?U%!hC!h@z! zRX)vbAyjm+3G0u*vix#2ZBU*tbCB#Ggz>qaP91Kpe%-R*K5KJs8>-cOpi45g@4gz^ zk-Llb^rF3qdgvP8fnT-nZjRO;Nb;{zT3>`{jVOFrTmL+<1irF;Ks6TgaYtdJ;E1Z^Ie|bqK>k z8M>Z7Qnjo-GSJWJwRI2H9~>KD#zoLT6I#2lz5HDVIql=v>F zqUnjHK_enTL3uYh0S)V`7ZipwG?ATYyv^pCistUk#1<%d1>;hepyqyH>)Egvkr>PB zXxP|YdVm0qAtg0H&J&@C{ed11BAlWpJY0SZ)*V^zdMiWO%nkk0B3Zp3jezq)bT^m&`;O4LwbYa5MqliS4E@7uy*?0>2-W z^Sfp*rUsTG!rUg+YLne(>mIK+89$V3<#`ofb5v?v*Sh3)7=BbBCBwZ+GPHR)C+2+; z*Zq`av?_2@wiV?BQj&VgD8JcR3eEbW`=BtT)Xt?>MSo7MMs^KU3W!5Ee-W}8MJv`9 zMGE2dBdhkx>t0sKcb*D8oscbb>|5AvWRdOFdi?jzz~3d^M#e4&Bfq`aQ1H{rB~p92wW z@5RMM;cX2A8rX>o13&pv8?*v4(bu4;rHgmH{;aSlxIehNf2w1rCJ?D2Z>J2614DLP zfbav2Wh)NBjBGSz!em^*_3oMR#(Jav>eQv$8$xTe)u$Y309>EdMearV#Lcxd_jQVd z7qXp~2~d+2r&oX4EwN8_I}Kx9sM%L9Ja_luD6iMj_)VWI$zL(D?z1x7T{}X|3MyBn z;;*-cY3#~jB1=3DpysJTat9uo<<-Ng@LLJ=iRDU!mI(S{im}$j}tS zlAL@Ha3IPd`MnFNIardVqO1IW{z9okM>7xIO)?L0LXyOAgK`s;;!42GVPpmqY}Kix z6Sig>U$u`d@OIsPmMXiaqOD+X&%PDyL&hqs{G_d&Bn=* z{6H=)4l*!>?QEH>Pi6Aa@>mZgmJW$7)PQq$o2zg`lK7{AdYBIfZ{3&CCN` z8>)yNFf4L+o06qAO_kP=n&Bk9Raxiod@D1&xmWNH8p1*Civ_Xd30RSW1iC~9W=T^% z)W0jMsHmR@1IGyD3;@Mzc>KE`E;lHBvEDw#jzCMPDxuDOT=Uqz^`uP(FXwa^fGu>L zo7AN)$jznsD~@0rbE=WW;t&vMha@tZ0W&m=9r&tQ&+~?IHBCBIR&pJ$N&h5l+@3GT z!YOy=guz4qB$T1K@|YzCz#oJ|$^O>b&p2fWZT`y=cwIRZ*Hyn)x`)_k1}kBHEQ1Ng z&s6pFM*#Rm>?vew*g$^{q*xOXXD9L3Y2i(=rSu&J_h6K#eZoV(GWa3$RIg?Tm` zw6)s<7=L7HHOq=x@sg2&FD!f1jCQ^Fw4XlxK=?4!l0X)fFICPM;jp2JijNA8MHQL< zS+g*+vBB>pYed8xPAIqzwIt))sdy{n`?(OmI?al>TzO0ZgI=TVYxcPCu~iQ|NEtr% zk12SmLXSGP3X&K$IPUtiTj7^8o~xedkOYBBtHH*Kr$`LgG1+5srpC7iEmM)0XF5jgK7%F zgIvYUo2}`v!anC=Q-;1uT(myf_6HSIIZz0jiaw~Xfw4-LjGZ2Rk?xn(=e4jRXP=kv zPzpNj6%EJI$GBmsxv~hIH)Xw&jj97p8m+c^1^fA0*Z(-r@{GP1_d>@hu6S=TA@NOp zA)C6kR2*4(63U%66$~HDbHcn!qbWVG&%-dIQBGNhW%Nanfc;*mowaGiXvOq5k*dX- z8j)S+pwl`NFrm<#X?IVwdgFFdBBMAbHxreg@E5iR_ky|oX?sJM%gIgDMOjl(e-Yfh znyS5D!>RJxLXKS4cMD)rekz>Jiqo%02cmb1cfI7-%bgv-mTHSJ=i;~oC`;S#RUN~V zQbmwPz=Q0&TI^ES8vJ!vM_IhB=q(nC8=rThWR8x=KMY1JKn|klk-!S@c83Z=xbc!I zWBXXL>7+;Ih$Xkt6K;)tZSad$SY9A!6T^tmJPAp&b|0_J=Aex%J3cSf_hqcG#JEbI zY(XUNH1G=jR>uermbRQuwky@ALA8+Aq);L}*?7UNe(yR%;62>vnC)~cqz>HfV%@hI z?(ZyiI;WZwqaiK$5?CUVYnw7x6((Hh^?si_7j4yj)PLW+UPrY2A+|w70(@xixHE$- z>v^3jd7qOE9|x@TXA*@0Xg#imIT-$XlV#2(yY{tq)VixtSry%#y=U1FL6u80RU-&A z$V`c!Rh@qYJvdfmRj&3z+JTgz)mchJIZBBJ%j6PEuughfXqjQ=YDTFvL51#TfZ_!m zf$uFOYFE7%qF-z3&q@( zk+fF2P*sb3kBD>!8nfDHBA3Z73uH*;s_c&2?VTXi#JycN@&?Mn3;NDwzy5cAuq?{y zAs&w__Ytl$ZH#PT;W>J$Xh{`(uhRr$Wy{zKH>nXXk39*giF<|g$`-*_j-w-U`f4VJ z3HPrByPQo(AW)!Cuak}xVx1KR(@*leH-^RA1J}GFjPI5y+Un%!i%wF7iiL`;edWE% zbBxCR*zIt^(OLYD7oS-6;VifvI4VZhTNs??SyFF~ej(2X1GH##>Xz!4@PP23TJukz$@>0eoSW<+0FU}~oCJj<*;^1fIWOiAx#ZOx2UVs8h+}NJ!FAl!Tz*+Yxb8Xv`pvcg~ z4{Q;>Z#`tO+u;lbdKCw`5b-?i6Sotl?gbew<1ZetSIJaa58KTS!#48IUrI&Y!@|?; zyH&T0c?_z!m#8m$)wSH3x^3d}^I}COIM^&DsJFvvX4bgR^!@ZDukLm`X=iL3@Ru3y zOjm8?0B~mx5KoBP=^IymbfW|@RL+_Ji~GaGbXL{OYjGIm^G+-oSb{vT+>m<|2iq6A z6?O<=)Hlf0?m;DFWT~(SO_EJZk_wv40KoYG8(*`T)mu?H%VLFX-KYakcd6tsg!lWm{6DN+ z?`7q$YC4tA!(;7oWyN>Ko3^ae+HKT~s_I#rvnNNx==ZG2Q68zf$jL%SLpOAjR#bRyZhBtIV#bGG)C1wUEjpC=fpL{q1 z1NBl@jfT7sLJV^VTVVYv$6tlT-%`bMIyySoYM*mZla@!hQ2M9sg&P+%EmrXs;I*iR zxX>qe^jno_s0*={pw##CXu01Fdcml;U%n1SFsDVZ7mS+b8yE}iMJc996NnZHn?#>; z*_|BAaXl&@q$NJ$FB)@H%2b`{MOCbx9~AB)SQ)|dX2YMgv$veDI54(I$F}U29m< zy@rZ)r`3iIjpYwdBAxOv-^^at8m#|%hZjwgA|<4Zm9(dT%>xa^+U;d9PvSH;DABIz zWFFL{v?!l{A)5?BlSGQmD=~rtedB9sYHAu(n77U}HvC)&anrPDM_?L~zEDeoKY8{B zKPXkQ#tUm;frW=S#Wj~q?&HFhu#i^eddN!O%0~Y2n_12FO3luZ~ zuTy8oKF-7rh0Scqt=2}sO`Kh>@Mr&Q$F!A^qW*& zx+;XdQbwmy8TQPvaCbcC&?N}R=H5b#*j%_143fr)bh<@872xW)e;C1N$!|rs7;oMM-tn zAcl!`DD}U?gG(%eyNsvrlU4waOHiR>Dvzm?xP4SbO;VEiLe;@pcp=h zJPlTqtYU=|L9ju4^ygDfPied6FX?UWqRzWVCr;&PCXHZwMqkNytl5J|C$k$9&vjpL=ToZD@1^BmlW{$#kQFdF>SGcO(0B*R4rg~8EB z>tV{JchD6qaEcIfC?%A`2c*Ym_{V&m_0PdPu3JyEtM&M5`_|gYn%!%o6j%-YyLlf} z#e0-4Z+fPCmHL4P6(xf%lW2O71r3p$klbitH{uf@4q>IR63S-&_&L^_e%fdGh!LD{ zJfxY;@v8S)l}hgB$igto@0%oOTZO?JtdiHZ-r6_KOJ#j0Lup!PWqE}$gV8M@AkAvU z=;2L(3c^7XhZUR*)OXg>YhY$(WnR?eUg20tVTzIro{Bo-o4MUGPL!fpq#xa=VfW}? z*-Mi>n^0kYnFXPe+*$Ft-j(VAizc?*-LQdC5LIGA)Yb0*!ATHT4pWA3W6Zc8v!2Ar z(P%x52I9$_?GJ@hMqDshm?E_#p%@=qxG4bBEbQP2#307p(&}5=D%l>x?$qz};SofJ zXfXTH*Vp{-=u!62tL}XJ!no(HPu4T8IWIv8?rBiBDmJdqji$T?4+gGG&k~#Ufi{&S zNnj2hlDzZ~4my5iWK?TNV!mGky8<)8ntRFlve!S!YF3Y9;+KYQx^7NMb!;&E_qFG> zF4k!4`_v!I0ghMCH5R{-)6-dU^Xq>$STsF#-EQ@w$H>iINj>ybXCcbst7wIZ%DNOHVgYEil;B5a4Qh< zc@R%uIJRg>`5lOReiI35fHDw7#}Q5w#AktBiK%*x1C_44d=k{ld1B&t!Vw`H4wC-k z_SZk5&862t_5*5U6fVn98AUh<7Hl^yXy}O4v3x@70b64EPDIE_KGmT3Fo!F6@Z z`E;uLHZ9wO6&!1}A-oFme6vQ^)jZMb7mV6f<)MaFz<1c3&>hY)S%=JDYzihX^SYDf-(Y)ZGBFP}@Sj|DB}=MVJw9=qbakZ?(gnkaC^j=4Ty+c!q9<5)hD*M!LI{?vzGSy1S7c zx}~JMyFoex=`Ja0>1N*h`M&whk74eaefBAIq5+r~wXV(8*2A_qf9Gw*IDvi2dVH zz@t&_lhbqcnUKo?t8~2ALtA&23dtz{uqMl85L*Krc);b&uozA0LZz$u=ZH6KhzAGH zpNNa|m%r@5v|-33Fpdl1S=giboKr@OP{$vq$n{@{9w7Hp#WMZx#{yA!aA5X8`7_2A ztbFTC&7?S?Ni5~Ozkx{`Y`LE@B4k$WS=66`tYLGsJhV}Ym5PN7v2OSww0JgAU+`?o zVWeNl1`FNz^sRc_B!`)5a+BiEXb?FptQ^u%O^pcyq6Kl6SuQSm%u%%QMID4j;_C{? zehBoIXfUw0nRI2M?c-KxF>+>0@=G(1VfChWZ*1_h{7i2Blfyxk3`d-)Sri7Plr(;e zQAZoqH2wX{ld@zTyRLfWG3}Dn%7E%AvSMB2I02rGDeUbqZ4|?dK$Iepw)X7MK-d?% zM)o?Ss;cQ0={m9zGoJzL^${q07zW0C@yf!fP`+_WkZOb?@Q!;o~ZE3R{;>MO8=#lkl{DisWlEj;OThQ9$)~^RO1QFpVeTcoaw@&!=PSWA26ITpP}jieMK_x3-ns)3^~n zDvk}d)`n^_An!g!%FOnXFvR2|ze=PDP7#EaqH+)gc{n{xN??$$;Y(ulFesM>x}eLG z=S(30ysFLmr~yK#;_<;n7gX zs%kP-rf1OzEv@)cpbm8ds3oxyC_!#{s-;i35PfMnWEq>R2G=bbKo7~bH?plZG(asj zsmcK%#i$Lh;VT=@+FND z79a&ixa0+`9FOQbh3fDFr$4Lvfk8${dzm-w6?f7&hIdW`2DD3{;iW$f;-5BXwnIBpRp0RIn-TELxS*e z3?jl=;cF7lS=@EM>4DGGWhSt>cyg6PM~Rq-XP<*c%Nj@fRSN7@Eu&$2`|TL2+`SE> zU(ksnvJw-MzT`1t{uMFr)y7Dtwf~W&j})x#J2Q?wl5QqHU9?}J{PB(3EgOA<@LF&}X#Kt2jn8NlMl1U@}MrFJm z$01IF3r*>1Sbi84t8ZupfsE0Lgb*a8zH2Fm;6TJkiHGjZp7ee#%vrV0(qGOC;jGlB z6MW~%L*k&|Q})W|+^as-un`;>%lfNO|4s1M$JS@nnsClt;#^B#k6APjTd16`H@(0L zA=zO}$Ky`7Dp3W9LJmpohjE~$CEK>3Xxt{2PmP<^QlpUp9EgNlKQCDeL2S_}nkED! z!9e@*^sXup;H}><*((O%e^kZ)T2)1o?uirua8Oj_6r0?vqJZKkH9&NHd>r1U3D;U8 z#u0_-=ueXzY^RzMKUtQmgtN(O>dg}{?=`>$ks(3o{d%i|w*-qB*A9=tA<7>SE0nb{ zj>y54=WsSxs{X2eEU;T&IYKkk)swT<8wRh>Z>4aCGtcS3>^K*u)Rcz@qKED@^t)R^ z$d99aKK;@`j8Jh*Cgq0@!*n6sKcBOf6f5arx^)nU4efNU=*w~`)9|XKhf7;_{am5! z{GZN>SRcekN6}Y2^y~jLU~SSw@lsDYOTGGmT&j&F`Tm% zBbpGVQ^FY$(Mg2FOudTwSo3k6H4UrB`u!FId7P$XTsuVt*6_$qXV?2J3NgN`uqBfs zPzKMQU~8et_0kgYm{T4kCfl$M4XH zK15UnXsDbjt4JbtmaaN86jF@}--4#~-vOH6VgyF8smm$7Ke4*_@x73;-Oe`?J9;R? zOd%%PP-3-yhGxZ;wPLvbEN!aA{kH)nNn7-YU&ACCEzPc>C=y)$3LM3s@~zkY&HFbK z2l#;b@#Hzbd>!g|Vg&v6Go6ln{p;x9T1Y1%Y!)U5u`!-NQs}KcLe+If0{j?V?6Iar z)l1fFY*Gve=#}tR=E%l7J7;HS+X*-{<^^=|YAe@DNjn@CNus*Paz$UF5eC0ZUkNen zU5gkesEP6T&`8OLH*kY!ytdRFocxu~&2^R{F^;%*iI-fZIEE%t zJEaSE(pzr1d#=SnQ1ld({Z^$m{RJn|`F1d5+ReabtSY>gU~7n~_GN$RmpJga3NrzD zEHCElec)iLCKNdKe*XPiy3;6-6b~Y-=E!0VV!?&=o)^b5kH3$w%Vu4gQ)l=DakCIs zGSP?igV2)LM`Z*XL1*6T@l97v>wb>y4oyO<%+k8g<^&~d2{Etx#0Fe zk}E2oFP4rE)B#^>5BOt;blUU*8h9B#LLZmc?%;haD&?n7p8$8^%F2pvB^Uyxy31Z7 z(zEgozNc^%%-eJBDmY1Cwopi@|z6yTX2(lS{wR?LLBV~3BDZfWCFvB{ z`Z(oJviiRnlS4qjr4lf!zOcS~x8~RAT5c2$aKZ1(a0a=p_Og`oOFaNEzT&M`%;FW-7#@`24~k>xSVX6Ug^yg<+Gf9FlZ)Vf!~qG!meV;# z4Fc{cbwBEp^YOiU_Ms~wAbe%`?EN3U=EJ+<$R8h%;D759Mpu=bR|#sKmi;8C6l@h^ z!@RC%j0mi=YAj{KVf$29WrJi>0OxSJHg^ByPh{ zbe%KEw(a7UHS-^sQJ3vW>N5pnv$RD-hW6fTo+tOZ6@O# zVw_43IQ6hQ>(=9kv?ko}g(l}{nPl(;QoZhJRqw9F* zyK*{VQEfEuz8W?$G;iorY>!ne57ER%-sxsv0e$&8RW_3WCh!!q`01AO^emz$W}zKi ztPU?ja$Pur1ak3qjvmCNnsa$fT^H?NuQiWFqqOy@O`fTjXj)K|O^^Nr9}1*OrEc1b z3MfBN5EFl|RzEbKKvMUgM}-b-`_ZNKK2*CmINNK`$B_zf*B)EJCJU&gC#56{tW>;X z58630wtD%0h~YzB`uN$2(ATqay*KU3TGj^C-+|)u#yhK$QBbllbTlh3g8#_9n&@T6tBVl1$rS9p1a7w6Iau2r*bNs4BKc&OT;EUU^0-n?j zH8oX#f*;*jO1*+7o^!bq>6AQgM)wS9pkfwdU&w;G*up1rQGW%wuj()!|6EO$x$eRJ zmr#f*{%0_1$A|QPe+H>_@uNc*HlZL8N3K~l*O30`Ovn`qmG#(_q?l<0)8~^TROBHI z7XCWXAK%X_SH716Z@vys7@n1kWXa3b3Kn^}3@#tgN895!YoiC|EA)T47o9&IcF%0~7vaJmo%G&-F42)a#0ppX_6&ec(Jn0+ZQ zEimO`*IvP@Vj5rVr(GJvUmo&j?h@W>5CFa&%FU{+YY;s;6RK8Fv z+kSYdLe#+H^|Pb_#`P@`*!@;j8)R3339W#z_n z#tAl zBS5{vgAGB%#Bgq*i4*%cLqz!uUj8V(#5+iz?y|==+H;`dLsKa(Ow%J5XMO9^Pr${d zfTzF-IoR%7Vr)UMZy5DXF9KU1k!2R;4lrB-TxRyZQrwK``i4Bts|~^D)kUy^-6Ly> zetehb4sxER>haOBd<*h$k#MB`b_XyVq&6M)nnZ^mJ%SLEnG? z*3+jck@^=vN2ke>uu@AEFy4NGX@gfm%MlR>;*TX0C=pU9*PXB} zSt4-WjRpE%``+%G5=iQc7wywk4d!`=#kJmQzi>%9{)DC#1~LT#*o;V(ThNT_a4DUr zNvV_`g)5^~pV<3VY?IT+^C!BN$F99jQiqqHcfa$tpj!i1`@ws%du@I- z;D-}OFh&7drTu1evUl&QH5lbIJ4W|qhJ(Z0gNk(u6XGQzUSD5>f`R}JS5rG0D0qY} z|LZED(U5|pVGJRH6vbib<>>2%ro6x0qdk#CH)`mUty05vN(u}f{ z92In&lXrXUN0L>%Ykexm?pd&thX*TeK(yCE;GlN<&#&#~MV8ov-B({+J99g5lf_Kq z0{-?C*N)QyF`%09jc?Nbj9SJDP1Ns5SgGrh6brC{{h=59b}s8&{}#sIQ9Nz3{$xd&pcd=rmo zTB%lsfrZ7Q*Id~|1_D$5&`l+ycJbQ%#)=<7>*mi5kBdV2Lu2D=*r+U7zBdC{c*!{p z@v&ji>u}Eyj(l!nQHi0ia3=>5Q*&4}e(RPKc62!k((_~it8l)k@A*O!>eSAE1G>V6 zmt7XTW50ka6=%hg#f+}qTQI^mjJSZE!;KPmWV;N&?@PAiZnYk6)bSkCLTT)hrd5N@ zd41MB*5&4Q5$P8y?Vh=zy3Yh5ZLISnCQYEkVTZ$ot|BB*;w43j4=W7Ur&1gd^ZPij zW=OXPJeXOWa=5rJdEn2qJ&TQ(gE6RlpNMt>*f_P;Q}Y<>^8Rcv;tGvC4X5W0FEaXT>q^{7C1+- zFrNN?L=Ge4G1+kOciG}^JS^$NSk@t7_^tN2-d1X<^4<7w@+KiB_#$}cuC)-?3!qOO34UuPwT zEC0wh()Ak#Fpk)1RkCRl3Nmg?MV&g7fp3%mmlw3OAcK!z z(_1t?wmg&9>N8j6HD|OMbE){8ba!|2SAsx41ZKBnOrfon@KK>5l-4XyoW%1P$+$tp zY!M>~bw@CcN&@c8Rud^>D>=%KbuK$&6CLNSHAWV3m?(ImG}g7BAPJb{B3-cDckh;+ z25w#`a+3lTP_PmfbDEU))V>5#!im*V<@Q(*WD_}a21Pbrx4Nz+oJt$s-vsEtbZgi6 zO;yCNo~2rZ6&R9F-j`vo?XA1GZRe5geUJtPdX=kQ_ZW^NO2K^@K%l0|3u>I^#!X=V z$(Ab!MW?lXHV`(Nn_>{3YbW|g12;pf^S#1ayaj=zkX+C`%Sp>&Xdh@pEoYE?&lDoQ zLEPSa^~)}1HSE+2ju?(53%MeoDGnc@Wjp*WgD^?JPH4MfV!?b75r`p9b>Lp;`ynq5 zkLo4z+(aYNjbB6Ct#HFh?_bmnh?|<5AY(XcjiY#cd%H%&3s}*bFvvBT8&s3hrX*Ul zCVR~f5%fH3;ZIDGO>-+dZ+? z9~LmSUB_)HIUy)3>g{O$H&KD5gf>dVYi`xvdtHpjlr2zR7uJOT4G=Z8K+RP z6vEebBsN%1CCkw;X)Q*PupwX!CMuX4@lyVgu=oIM6nGk9Z!9#dOfaC+fVK8^)}f0N zaOHW;z^7NwpkoehT=tFxO^J9vPFDqA0M0|OM}~$1pG5uih|m0d8|-<7cA;m%-?z>U zHJ-9E&ajSd;0fpA|!X2bG*4_P@ zmzVc-psYkX8%4t!_oNegB{tkZa?VQ)q#CHxhv4>+@Dy$MSL})F@rReWh#W+98N>+% z{BsdS@d*o27{i$n-*!B7?BkC^Mmt>GjDJzCDZ|~JYHG&iV9p^1Aq2?AHugo!zC|F; zdAO*)4%>U3S>uO&Ly=;3yzF`0e}z5iw06lC`GqO4u_z4SeAH`R;%q<0Gdk&n72k+n zdAEEpFc;u@`MG%1KamvKva!4FH^1cJk?QXZlB2xJW(=`H(utS;<11}u4fu0TLkwDP zB4uO%2U>z$xiIQ)D#_`$e+#7J-k^ml(5f@1LZ?;nH5pUd8|e<#&_pCTQ%fK}-PRxf zrUL@4j(}4V)=v1XEJCzU;C%RC(B3#m2BH9!67b{7ND*ppc{rKAdih5L8p~0FJX>mH zfdjzOkFM8MxNDxXjKD`aHhUd5g?(qgar|gs{GlXJP(eUKhL+FLyEV&qedr{>KSr@Y zvVSKQqd%|CF5sV+0hYsa%$+4~b;i^bHzDMZP2_Q&oc#HuAS1@c9##+_8r$#9fbFWn9L4n#*pM&L(%zLko(!y~)eESStojkO!$p zOT)vS$D=xj^U@Q(*ucMIS6HERRGge5OG3dTR#k0~=q*oMo;6zKH_5wi5l|`Ea(3-d zLFwqyFB2IJS`WilYZ7_JMZ(9FothERJ4Vn_p;}RKF83yz4&?U70IHJ`{RUP+8&+73-)jve>|4TAE=idf7MG^J?!i zb)ynTbOXqSkYT6cQ!huGm?9n&De`nLz0$-ei-cn)!7kO(a-+PPzi-iNGL2vQlm?kJ z4WD<@#!8T^D(g=@L;|kIm5<~!PFva?Yj;Sn{mziXUmvE;EW17bPE3lvs_COu+~}c) ziG52vcwIey@u}gVrlD;$o7iTADD`UgW-EvQ*zoS2$Aw$b`{$*LcdMVp)jwYA7~o-y za@#E%dHdaJZ@gdd_*=&j%I?fF-zC<2nDXNdRs$KAD51{By~FT7lZaBgK!8e{6U`U? z9s#uCL0mex-XM5n+bF1u;Z$<6+<&*X0U7@v_8d$Bn0v$_OWWrvOw@$YgKQX8FhPfQ z^Sa2-(y^pL3z1^Y1QdNPsI%%@)eR#gOY-H9O0WA%o7F9Cp8%zQ(d(Q5BICcu89sSxE9x`%32iM?6- zbGfzi{<`nyo#7l^csi3fYC8T0Vh)R2#Jqialt2Uwc*CdIee^3ov+j;o(0K6A09dfTi&#*sK$mE}LZ zp0^<9^2ggx@%Iwfr*paPrfzubtch+ht7N_|>I5`@Xv#JIX?>6*o<)%ZVvk8TyW+Ab>*aCFKQB?4+Q1ICofpg&o9MaqI~z19KNRmV;_$un1- z#o0!9wWWffqPfu|x~ReY4|Xk7DB|BDCd-?r4`L@fBAd0>+}7T3wX*7RW!&@Wu_3?- ztHBx3Y`V)I1!WFC?2?vKP^ee^&hfl|fY0@Ne?lg^KNBT8*gBqKCc!2p_&&#!OGQT5 zQT|m_q5f6H;NZ8V|L>QPW>>upfE>~n%8UiA^xm4BDCYhR;N+z#C!hQV_Hm#DnmU)= z+MiZCN+Hg)Kd3ih1J&@d=SR5#Hr(H8dXga#9HK9ByOsa42|o@=kMGl+QQ<*R$aSO(>bFmmlQXl(Kk&7Dk}-&@N^pV= zNS8LY5OuR6lGS1cq$ktKVmOS9j;w zmA1#eQ%uG`RhU2^wmOSo)^FCy0FIyYzay#bv1F6&3_JBQ_x9?%&1Y*?*;AH~D)4sg z&_R@L=CvKW%U%E3@CtmG+fKswrn||&3=Uxngb~j^nt(dwH6cZ-7s*)CeW3tDM_vSg zIs)g*D6mTtQv318~c$M%KDW`H4Jguw4@S)qVL(kc8rib$QMnLdoIeK6_1COzl4b;cb>2O z*@wRfvR`zgKu8*Qe_2i9IIQvAs&*>WJch>r7&L@(?6*yXQY-LgCTz$d z?&;o6dO{|oXiuxnOqI&u%tLp5w)sXT5Wmx-K`wpV0JDmn&jw0jJSp~W+K6+ zh54LU&WwZgX6C$$`Yw;5DqyO?=kED|7q_gCj&w>3ipXnfewV7*w|-nPSKvY?Hsa{H z8Oa%U`n!nj9TONjHL)OPrEV0&z~yPj+FrqzchLTcf4Jw_>@2Ja&qyIK%X}k1`<#v( zcSN5OGhVg?3-;3g(<5~w;7W=dgc7E9-i-+-lx`v($-{wsULw@3YFzbXL7?@-PX=jHF_`)dfce)2P1{b8ljXC~FH#;wk_ zlc1N_;fv?O9=OdKq!kQB{HwjsjhDV3nrL)a{r>(onJigtSlCG%EthL(dU9iKhBtG7cl{vq%2rQ}R1Dn-!NU%V;q&`ev1PtC!?LA2I3rNL?ed5g;t5 zYMz|Cc)j?kKfFH_KG)}Zz#}(F!A#%^H$CJhjv8P=i>XEphm+vaAL)cupCF&*j6Gs^ zyFL$!zSbW+RjXh*)4Z1s3mYK-X6kvVw^(3MY``glVeA#JLCQugoHcgl4W?!8%KFPh z&b8+Fv-!jAYR#u<~y3EIN;uSn@Ai?d3RLwYE%8;)i_SUn+1nbCGi$E-_myM+IiU?DFTEp(2%zhrx3lS z{rc$<|Jg}dOv?DL4zDpE^3ZC1(U-61M!pS}R~wkH?U+bSvYR-b8^5YINGi3vBs>o_ zI--v+02*kcNIFW$@f1}#z=aS+pgQ(L)-X3oe=DjO0)sh`)_%PR4u5rp^{zJ+htwCh zvrdOB3-Q$!x05B+qO1&j=>ztJrc$ilH*I}T$Vg%!O9N@(_UXz~D=8^V zu8cm4)w8XVd!qezoQMc7E?H+tUerOmtQUY1{t zpI)C9@c@qB0a^*b2H!E1h+apX_?%^t3M5eO+FpC!#fm;_e|G9)`=jF%-t%@xcM?7aDn z?$e>wbFNvj>4lBwE0AOL0s$FE(5(S}S+uh*i`~^#G-a~DdJ*1S%R2Tc#=KNY&iN$e zxv18C1d1~!=&x~#~y8oo(PQaSNC{AlEs?U((iTQPj zJ>!A9?8=xq>Dqkp8^6Ol@3oQ&F0iMW^Y27dI_{yF$yC`!$sOoVtKkO(3J9_2O|_mP z|85ootq?xyluP>3%dio#h$9+*X6er%2nJ$J8_U&I4Yw-B${Z^?9EVhFY3sjlNsuv1 z#*|<5U&3LB(}e!VxnZ}Y(nGX4pVXHgYuTM;X<@l%=g;dN?JYp80a^-Gdb^YDHXAl` z(_4t@*wwA?v_35!B;mm8AowE!-q(?bwQ|s}JUHL2-ad&uwsX|Ft1opQHTVt+3QT#> z=NKWNxq9X)It8u+hGK-+9WHZfu61I4+;oQ3AWCP8s5}PE&W=U5=MBLt*tyMa;x+%Q z=u1X+*uU*$X*oZDIAWE}L|6b8Lm?P|S zLhF*?G8ugYIvM61I@x%^ZOj^#08yPWHnsoK$mbto);v0xv;5+A&q6s-dTX;sI;JY5 zuc6KQ74{2xC1CHvq-KmW)`4IYJCOfc`}B9;=wbCG;E7C@IeQ#w4fWd&(=d)2+a!_c z%~um+n?!tULSRSZu%Zi-U5BFl==U6^>sAr+#CMCb9v&WG&e9c2h=7*QT4+ z0XF@X`N|RS*2hv4_H4C|2!h~141=#PPsT^=DV%ypz&g4yLwzMN!uHhTJ0S{XvNeri zs|ugPuxV3?M`^Es`h$^ge!Pqp_Nupr`1gkdEN zC8znpvO#!PRXp$;Fa3l}(mo*?!Gl{4`lw04&_4l9k=CtVr)M=3kUnbPH#2=I z4}%uxe4|yqkRw*``S&8`Z8pqAR8gF)*M)|5jlM@4X6t$5^ZCUAQ<_~UH2^zPq2-jF zn6e?{C*FL{W02Q?sl8DVH`mZCGiVXL}r&7J<|Kx4zOKpwQA7mCLj@ft2|tp*tKf6 z2mW87stQZ{>HPTramCx)+mqFz9W34~gDF7td!(Wp+-z)Y6=9*We_lYtcP7=N&Giuz5>D0i}69NAQuY+c{+6=W5fjsx!k(6JGIdRv%fPHoH_kW z7|N6^WwU{HY*FZ6(jgsgjaWBRR^4RxQbi@Khk@ z4OiseLSu}ec(4r3PEg%thPY4i3Nm;;BbCVoe+aHE4drKbP(z%ZLp{)C1YdX9hg2%7 zLNO4XiDY|#je)iE4%v>^$x zdM90URFi|1^#`{f)PKoj{P_Su=xZ&(5|@7K;;)I2PlIjTB7%(l?ky2fyz^@Z)nxQw zHCU$sh$iy@I#8?Zsc-*HO&77rsCvxsOG8l^o%K!wD<5MS5dDearG)jVI7R_?>@ME9 za?cLWei`G1Dbl7R2Li%~&c#XZt?28|X3_h_R!hEB!w@t{Prj5oiu@VY)v3TDNO-^e5|PILXtze21souy(zD9YlF8&Q+6y#FG(+g4qm zQNT-spdPstMd~6oYj~YudUv+2if<6F%1`(w9vW)WYB{ww1`123&UQN$fR@oH=C$i= zDHz&@Gd4PxM@O_v-Se>_gcYms?EMb7na>Wm-dOAD(cZ=5eE$GiDi0P93v_(Wv*dyO z*>n}{=FE_#!9yiCmuT;H#7~2Jc;Al=(mbv~-+K=5w>yf-?EU@C_>FZkYzJG|=Wp&i z*?rD;N!2P*n(L9m$ab!0)OSB#%6S+3BO&ahZw0Ti4?@f&*!!q<>~+SHD_6ff!S>YQ zy-Vp+8o7g3)~aO>D)`tMXTym&uyll6EiQ*Id8;WR#oh-#bd%?jiCm3nrFWKnM9AN@ zsYyE)bghFMfvB>bp$>yHCJ(}LmRrtidNl1+avV9bf@lwpXdX5_IT6S3nUYw^v}_&} z7Fyu+X`ZDRbIDem|F!{%Ou!QDtHb6)biWD{1_YeovtB>B>1VkLV9wkM|MbBPdgr0T zd*625@yM?J^hk=}Ynk7=>Pe?WiZoWUA-1CG?p}Xgds{iZ*>FHJX{phd;{i-uwQk0f z4`=HgCM>7MAkdZ};67!tMpjo9ErC-K)H==Y-Cbd&V*9iw6PJ>$I4&sidfRN|TcDeg z>s3TJN#L{mFHe2yW-48@e2QPL-!Aa==pSkBKh)>hMid$Q<|AL`H>4OI8;p8WGDV+dhv4fLm89`UT5?2r#{CeaG=nBON1SaibRGT??95Nc3o;v zg(f8#K{jzL68f#yOT-_V>=NkXI5O+H47xR^C2FLUb-4uy)I|R?a@g zrKtUdR%F^GAzOIVAM$0U-{DvH*1^&pf&?0;8e{ffL@TyTO^&~4ibyDY9H?vZ2~RkF-0xo0z!1C|DApeX%`=`Io&}q^ z?9lege8t>858*jm1z}-2XcB=V@62qux3@4%@HE8nyo91?URFp zLZ>r)dhtSTeBIB{Zo8g4{&z>;avd{V+i>wJVLHppjZ`lyG3wkHab(A5FWc2p00p0Y z$gOhyI2EkWxRBDFP--c~S3h|mJ!%s`ip*r8jeZLzMz)IzpOVgxo4*<8$4B;@Qt?2e-iD(ZtNSd*$5GQgm+_M zKcDCN(4=xF?^B=lzeZk{L~M}8K?@tgK=rxE-8Jj$CLmdoFH|oxo3W&VC{d8o0HNE8 zc=!GM$D6OyyqZ<8UC7!VW2 zBe@8ulN9QjGMx{(Po1xOE$ZO1R)|rHs_owh^b{oWUi`EijT~3pjO#vSzk`z`!IBHG z5aF)>jiRlaQe3j}B`p#N1eoS*V)fbf9k*qN31f%NfF9IB&GMbU$rbBsE?!)>F_Sar zIvUqpI4~Z1y!LefX%aWgBm?k>=|60^cp=jYxZ=J!L#U7hNEY|1EJbkkJs%E=KorBV zCij-^FBrui09dlovs~g4A|?c3@9UG)IM`LS zDC{lM;Nh))Z_$({g@H0XfSnSGQvv0d4bVod2Hbq;TPG3YZFrR$QAz733%CybCl$b$ z@D!s^kbTZs!}|0st8I}brhkd!jGtrC$+6M-Pv#U?8Uu~kC?|RA zIMI4-H^D=~yR;x@+{oSTo_mYUg^<~YK-+j&6K}NJsdwwr9QV=pr3^bl$cO<8{5FEb zgTDFdsw49n5F~N=^W4w=WEbj{KR;Ww5T0Oi`Ffo8I{GwA-kymc7aqM+^N_`c_aW(l zjGi_$J${{gsI_`gNJnZe1!ZwJqePcBaK+rn`>^Qj?A@@FE*#O}U%f+s`a2 z(J@=x9Hl@X)ry;whM{ZeGWp@FjP+#n1PNr62>w&J>Y3=;;)9-pOSK_Kha77b)9sO* z>y{C@&%QRy{cot`f5)vCI4Q`{?~qUbvdmLtXA&?gD)625Jcv0SX#U7#{}cfUZ1lPa z@bhv#nATv~f%9mib`oMDSJkHKexKqqm7SyxyVVhMy^a=5?B!zvXR#ME<>_v!o;swj z!OL5;+?_iBM7R${oH=N7(_!+>bZaM&gMH>=C`hNdv(O^)TrUbUN=$ zDV`?lTA!(~R$a_yazuXT!J_fPbMKs;lZT`ltaHI>DYoin6y>J}qk!mE3t1s9o<43R zV|eY*T_Sab`=3DTR-o`0=_;}co9MO_d2Ep;f0m7*6WGCF#|)w4JnwQjB<~TuC?-)L z%B0WXC1-0gE?FO*+WE9r{tb=&O#T=D-OG=|#We`A=|Gb_NVjFxeeZ)_^PB*PMZxJJ zm>(ns-MMc&XztBQGI~`6a?BdD;iRq>rns-L>Jh`)>lVKKC}{O;L6H+_OEYPoe6aXF3{Ni1?iyGB{o zTS)8|4M9{%iV|ZmDpLN+tey?>j!IPYaC=^zndW++du>)t0TBcU2|r^sU=H*>R z5UYCooFhoF$7QdFBL)8odgibNCpe;fhjoQJd21j0D5g9YpN zpol;eVkT_p$@6$k&%cW)jo7>5)dtkXoAk?+V^ry7upwu_P^8#JU4O~l%UuBGlXQi+ z`OapH_|R>@qfbfClW)1O5rL0!J|nR zU=$-j;f=2zFUra};GAC>GTwrc3N^j#HSArB>%ubm zl4?9A0_s!cy5vj1VU$jZyM@FM)=_{!4zp-gO@7>xkP)&nDFwRj*Xve1eh&~ZyCnDUEc8)S`fNcXziS-3`*6(%qdRAl+R8!b*3?e(!hA_Xlv!vhVG^bLY;T zL2sbz_~j1Ne2OzKz@@$ONU;}rU{CJjTQI$m0+oKe_DeJIzRwUFR!BryzdWt1zV8%l zUo@8d?9-4m>Dsyb&eIg-^KiK8LrnOnxe7-=J!OQcu!e%DQ=`ttV%6DOkaF69so}TT zD`AvSu`qA|P73qoA+5T>Z4n_mR!qF2*@Bi>>{lE(kbq+u9KhHHI+=M|Fpv>7YIbfr z{?YHmKrr7y_7JV7N+J7p`saL;HCwo-SO=5v0DIk>4wvI3Y+!povXi-@J?n zh<^~DLq>o{2WS&>p|>VK7cP7hrYpS{59_URZF}(pA|#}N)Qtxlz*VE5?E|RErq!Nl z2dMGObdQ9Z8Q4vyp#KayZMS|fG*AmK?$50$=RwS3>M07LQhE>-mt^0 zNV8e|IQpUil2u>Q)ahcSnn}w|58Jty<_)FXZ}*{>2@ayTPGSZhq~T@uSFi$xZiwKO z53J&81LA~MlZ8NGLAcz>32<#dV1(Ks$rhGHig=pYU{rQZY)M&^8(KM7(eK7O-{MIn z%;@)8(@)=pN2{j!vh(182&(S_dXH3`?Q+gr2Y_lJ5p@^YPy%oj&jJc(P?|i4#Frb6 zMzw|75m~OmkSX;bA-D=R8eGKwgR_sl4{5Lae@%5Ov!1N?PjRUwxHW{YABNjh_#K7w zU5}Q!N#MZLZ?8!$k%FGXBIEH5`;;^#TIi;(pr7QMoCqMtM@MVTwrR8h24a-cj|(jS zrDV+v6}0pmdgY!yiR0Y!wAI)SUoRsn7bum*Yk!8&IO%_#%I&tC%ohJe*!+UqhokuI zKaNo#+vke~^AlI}yLSd<@?xfq7&K}>1g1&@cV_&bsHqhA%m3fCMIXz9VfPU`xDfk|veO3A+0*b$>o&$vBO)h{)BDwpBCFKX-?XLBMSg2>a9kjczb?ndJ_e=2x*5a-mMc0LMg214RuUmoE zj_{GhaKw^<vFm6K$H6z{ zKMLq-xr`=v6hY{Ceda(SxA|4`!l`1IsK7B8f#;%V#)}(jI?hV-U(p3 zx#m))_2I3k0hB*7aDq8SvJ~B&(m#{q|M2Gu_L4U?>^*nVF4q1ZZWS&^to68^e5v$~ z^JFa=gK%b;?O8pY`8Yu7d;dBec-M@D;;Cx+$6ZLz^xEq0#JNwH??!+weo0P!vJCI; zOZ#oJ_9kXPy87%dYOV8@5u3_^nrN&>2!ThiO6iv30@y+`=R2`UfXeTkZPXJZb zdOy~?*odJ2a(;fRVmq{aKu@YAAi{fqT-x(Vg}O91DR~pGl8%NjlfRW}c_-TDwbj$> z#lWh5`+CFINx711Gv=k|slvpHwEHPbyY4GpE_m@8($1VMRuM2#L|NpL@jBt1_wcVY zPWV3Q@ObJ%Sovve=7*)C|IrFnQsQq~T^yM&q~@~JS(A=h5d(v&{}MB`vXb$ZR-P53 z^X6;1ysj*&F`>_RU*NQl99V&DCkr<%Fr%fF?MqN)=E9J>^=* zL)u68#{x_INHeC4%XGAmBgSFmSDurc2QM7k2Xlaz;mDACFQpybpn!<_ws>HWpk6dx zS~A~7)Ln57NEvPCWdHUkG(|rvgA}u*g97fe!nf0d!;h9h1ckn{;Icpi|)xKKa`+^2hNXESxpFWrra-p-!NBj!zZ)L6}!q zwm;|=@&bv{U4GTrSQ7V++`3uR9h^!O)VUumqA!E=hO5ZM+VyG#%@~8J%32{(%4)Tb zaj~&)&_X2&lvvYZIe3xY7hn7Ko+8*i1>2);wqFebce?_N!6YWRmkv6`W6F{sX?JLo zSo)6^2>P@R-wO#96#`u#(!t+<(ZdQN9TTS~==9!(_b*c?&q=k@N*o)h=xu0ivf$x2 z5+?TRqwx7U;{>x%&Vxspr8|HrYSKrN-|W*8){*Vq^=M{U<9==}@Ll4|T|_`ya!1Tn zEuyNP(KK*JT;^B71QEn%H{(r%G@eT|RIez}ECw#v z#%y|8tyM1oM3!N;o^UuHoF$V*7jIlmlKpKf=f}HuK$Ic(MXyS`THM$hG}EeIWOZ&$ zx@eIFVGMD=WeihF^kAJzM(uF?%JUl_g`>v}UJZ3=tjYZISIVu+dVl`&mCtShP2;S< z>M7UP1=`2;ga5O8RiLtoRJK1^p5b;L<5cuvb(OMu;bO{%F3eAi-dwh|h+xOK{4x6W4#?)vh&)M)b9F#xiCF$8J((@=^b?&;Y|Lz$B&c(VRC%J6 zwZz&%Ti>B2zZ|9KU4Z;P6M-KM|LY)SN(V~3@6o|T?>*nk)bW>nPE^rzW#rk55EVVA zMVbZ6u(A;qmvEsTC!f7%$$tY&GqDaMMy-IX9aA0U@b^Z1rO?O!y&03 zxz|wDXxGL5C54SZT>%Tsct}cCWnQyFAR!0}NK||bdk2LdOtF?$>&xAU>ww^0 z5GUJBfXKnOHN$FxNzTQB&usCIum4KF87@1TMq4$x-8YJ!`)o;3-%E%n@OtR|)oV9z zi+NW~l`d-rcp_U+T#AamUc~=k*vU2&gD)Fop&&PnX?~627z_FgscxPC%hNrn+V=#S z!!7s*__E=y1l65Hcwdgt8?$Otg@OJ6{?VFHcY%gEh3pbeKXlOK!;Wx1m(`Vc;QEy? z89MN*EX97^_*&fW#*5FMfuoSGC)8HoqHeexK4tOdy=fHcVCChEyrIlZprNeXNcAUv zU}xN?HRwt4dC_I4DYSNB zUd!3oEF=NWW#~OVK>fJE*WitgwLk}w5ZIn4p&l=L!P{0B_e*(Lir4fisFCZF!Mb3o zLOz}kmfK8tkelN?#*E`N<8< z=(aD_1*MsjelIt?tS$ZvI%})7)uU+CAgVai&U~nsZmUC`4A{q~93DX4h(g_RmZDZDnU$HsPR9oi@6Fz>vqDlmNap{AsfLGZT%R;A zJ@Hl97(DTWL;wa3cmGvF1sS85iy^TsCk)=|>?Vh1q!?LTM7(^kP5&FMi~o>#r?#4|}~rPv_{7cAwzV!r{@VhZGov8PVjn4I)gLI%khP%|PjiWUA>2M-{c%Dhs6P)PjFqIsksgQwU z`sAsc5T{0afn(+hB9Xo2mVP8b!CkwV+lwUQq|G55_W5EQ4U7BCXE}OnMPQZ{zW9Yb zz~1%enKC-#qu32EzV-sRk0Hu#fB2iV?JJ36%Ep;zuI3%wjbD97YU`r2Nba|tNP43< zJt~@&To(tuNmG*`wtdw`u@ejTbYk#d9V!WRlU&9;=Cm)9Qq@M^wkZMKC9-pUwiisF zhLrp4nKL8;fsNw8-@=e42T!e9Z)r2WgRH!noJK@BeUP_-FfRLz1Ur9ei*yTK45(pc zz)&78lIg1JLJhSY$D&E^@G#gyT@3cP7xs=D!mjlL0a!CroC!SvC&NsB29?OI{(mg$lkI%e<43fuEB~U(+Hvd#&isLh>W$=z;bJ*|OO#P1k_WkIMGVH&Rjd8l z;>LzZDFh7;RAlHXqFOQ7(jZ{8%?rmX^>J>l>~vju*(0VRN9#PspV^3o;)4)hm0Yif z>D($C2n3Hy{_+zat$jy@Q};#WbL+hGv129#L<=9_fH-H*1c8E4ZE@PiAW(%Y_H-R}0I{4!nPsdU&F@WR-L3{RYKRlr!ra_bQl2o9 z>j?ac%6aqHLsSf7Xtdo$8Iu4XY{97drpm1JxFv_W5ut_kXWv#(3!M``<5)jCxxHkskr?m?0D`a?~I0Skrlr# zvHk|H*a(Cdd^koY)n!S43MGAw~5BnL#Xix7E0+kdpKt!!B9t(+HG2kM{2+3bLsoZY}#A)5}2< zRfO`0PoA(%3)tE9b5V6N0=3Kc{9n6lRAKdO!1x6bjt5;58|Z4$m5S<+kc4r z`SEz5n{qi8;&Jk4l<;6$cbL}0wNayJkpBtiZZztlH8=tTiGArxZfm*x%prqi(e1P< zqQS9erQJhYk3nADu8V3LW&%$v`1bQXHijFc+0okI>TP?nf3^63U|=d+z&qjGpG7Aj zC;+e=bw@M(L2u;)MRd8>qzP_MJp++8LEEBR4MoP-4D?(Q3! zzZehmQ$zFqxY??VObXwTLMyD5#u`klErKW__2ao}0-nktQ}2XEeFs$S;6dPYJaWo* zO+{))zY8;LLNKI=OjD0cRTEWdxc+YR#+4BUAMnBc{*;nYDfms3C*KmFJh;hK- zqJz*GwB85y3BK7K6~88Qw@vhMCTS0_K^ix_%f0!;OIqG>p5AK19=KR7Eo^XXRQCBx z4f#Ef)MYp@ht9uaOOCs}j|>Ts8N@&&A7nXsSYio|qzD1!uZs}e#)3a{bZ)leAcDb2 zStZj^iKJBdbfKW3?+A;l{X+vdF8c=E+*tG#vo!hS13T>$m}$VFfp+?6?y}9fd?h}h zNG7xAyvor(-4@+l@6J!+orDWy!ex~a;H!nVA}krl(7=NJ=?`Y>Y2yM1JqQm7n3iDm zg*Dx*I+`Ae+VBQ9;SQy@@$RftNDkarxNLBE=6*f@BQ8W^fdzC{n)Z@d!;SJU#$%l= zsT_vM(^m>#JKhJ1t$yEmi0?vkih6WYH}7%V>7Nkiq)u?=jkJp^8!OORW4#(d^#*&d zpnslHp-%o8C6>Q%TfT|Y5Y}}t5U*4X$6XIgJ!2?hAU~omEV2Fu(6s}ZcE%@vzoN28 z(|)7e(iYwj9jqEY{Fwg|T>C3VRGcyM%DUx48bZXrz1xv+fUzE0PUjK_5G8I{c0xeu z^*13Q0k<;RC-MYjmsTrs@My;d#M2$WvSXA!N=Im&y=^>J)I~1CYO0uts<0eLU%VK= z7R7L7NyGsKxNa@D61=r}xo@^;UDRYZvygk~3JJ&_d^b3c(8^!ATC$_IeBOJcI|Z=} z7v8ePdqn^REe<&rf^-I#`{!an_H#raQ2IBz2oQf(4KkGgHMgY!F1WamD;U2tSUeV^ z!x(k!zGNlDh8b&Hkd^Q!K0qks6|QXKMyPoU!cIF2CCWdBxcp4py9|!a)>{hHsqv&X zisw$c;xLx8Uh1?I5j#3oq}j;^*!p2g*tUeNYD+){mR6l)Pt&41MQJi8hGZ73p3d** zaHFVh0)N#_^onor;;LZ4V;j*EAr7ltu9hyj0jaqIwuO&Ad(m*s0C#6ebX4#Lyob5@?I(D4fXO;iv=Qs7r9uEM>WG$XpJpJHPLP2=bi&{bGu}o&dBP!028O4_^8910S|%`)A5CXb*yg!*Sae zIIvL!nyG2ys-Pkfsldh~_!H^Y-CRaYF_GMzP1uaI@gyiqtCsQ zU{24U%RGGcXp#WThvr3PdmV`%gKTMIp%T;%O3amNOs1x$?njQ$fn_^IqOjZWk#~N{ zF5kSp{m+9bL4Dbue&H1Sne0At=L)Qr0^u_5{UNxtoHK3NePN~kmBR3}?QMh5o9PV_ zr8VXqaalyNDazhFV9Ecco z709dO(_aFoeb55}vQY`M>L15e@ic1 zQrB+jS?uoeeif@XiqYC*)!*}R++k-e#;L!(vh8G{#^0sEjDuc%0;d0j4mWRHU**7{ zt(ViU_7q604XNYWR&d(5U=VQlxG2mMC1EOmQu_KRTP%So=zO!!=RI}2CLo%|Q0q98 ztg`jAIRMzVmNOIKg0t5al`LmN@U@Af5r(Gi%Tt``8bqTJXfpXIn1-#2{`I=Ns3G(o zZ-1}f$pDOi9k6F*P=idl`VnZ1h%^gN&+N%J{by~C)#$xA-b)ta`+jRW1sRSA1TAzcB8{jbZBzi`08;JWoA{x--9?_NZw=NE>mYOj_v3*H!KL*F zRYagFMk)H!ZeB9R^U%C7PmcW1WGra5v+|K#`laX)Ke4nqXALxizhre_ngE%af_c|H z$H&?tZd=s!_;t1m zgXN!^j~5MEkP+;)8~CGf-@A>{W`_ioNDtW9Py%|%6Bnquf)bl zFx6v;&-n*@wJw1E!SwT_ia#}wTd6Qd5QZ=_`J_JkXQWot_rakMfJ1UjMgQul(Q zM)A0`+-Hn`fycP$!cF4%B%L5rI^mTh3NjOORjM_>22D@~XY~6e^uC-pVq;=9x3<1B z0IaGBd4Z%CAvRDQaBD$`82Hldz;2Mrs0JM{lK;5M3Pfs~)s%J%Mu=!Ca^_gs~)Y|ZeT9;YjLanetq)ZOB_$$?xi;4 zclj%K7Vkl;A_I4$ostwNMpfk2C{vL>VaAumkh=cBG)vR^N!vb3I$LJ#r6& z>o+D&);C&4#t|R%y-SOrvx!k&@4?mNHvAflJpbLXkmFF3V?VkqpwL)JNrRRJO9%rC z%!R?l?(Nvzb9egi;#_gsReo4ebNWqPUtK>hrl|9=y7g29y58s$M^Iwl9%rv}`th&i zLOfOk!yCHUk;qQUJWs2+>6t0oVvCgosvsKghw0y>ikbIQTe<@>R*M_mO;jON#fKg) zfUi0Y2?+_OoT~%00+jM$`zm!ra_CS+jR)@5M^0Y;WuI?}9&dd%zq3em{0p3zl#}ae z@7a@H&sZGEhkrj<$zSo+%ftjU#-obhEU|Ds4R$7l&X((p2e!j0C)dhCyU5@f5{ajT zj=bhHUvb0nmT&yr7u+C~2-6j1>bH1-#w|uQIv=m2c1i5Za6v|MijHrP_B_pVx4*R# zM1hmlrRjFNDtGTenO)vZkjEF6+6=6P6?JK}2%{cfV`rwnR zFIEvm1#TlYV@=y_q69gSl@hbs(a65x*88|1&Sdpd@z>|Kn8FN=R_b?gbIu1E_FDGQp zk7_J1%O1+%eMgE2>N|{MdwNdMjhfUzt#XV_W!E>RD^B6eDLQ!1GR$MYRA*;RV&Z_O z0ZEovoo$--e5_cLS%}ATwm(PL_-0v!R&q5BEjB{`+|49VtVRQD5%4B5U`5iz(5ec$ zr!4xm$Ge)&({JbfC`luy6|b=-)bI)Vg1OLo7z*eoyP0-gELGZAy~Z@7WG<9PGI^+C zwk4-px3xyUlTgQwFdmFpo<=(E6v^ExDIz3i z1RYKZY_O%#_IU73RNlbb;(j4%u@>>&J4>OQ$0mEnz?oJYZqO@OHpk})GAil%$4aRj zYAl%3V&BPl6h-ykyyxDlJ{=YhOiU-OTe)%`rnjY^zOdpNP^FrC<)%r$7oKt@8?Nbd zCVLThir#1bB{H=JRHazH&4_9oh~!E_=C=cmHkLQ@>^vR_9i!cAMrGf2jyMDuM__z8_ zY}_BFKweuj#6L9`so&$E_74xR5UF0}TZX{8n1rjv5aYKQ=o`pY>~z#7W%JY1-;IeT z(qRw+8X`6kviusyI^w#AUSQ{RRNMP8Lk`cq_5r(|0Tiokd#KZ8#F^1`x9NS>wi}lU z1a>2qyT$>@mSGPzEt`W<{A`UK6X}g;1uGL#1pR6sZ)ShwsJoN;!6ke5IClH#Z%0~o zo6Sm4L%P>t%lQFDHAEnyLIXmj7%@roQ{a?TJtus3q%-*xh2<)uK39yvXl) zm$DXc*C=*od5f%jute4hUE}*Qe3)1`3KS)@EM|#)MChl_R2333EQ;}64}U*J!sMQW zbGCeLDDg%@LJM&vbz|~naXbmBoG`@P%8m))ioeq_uq7-%jtQmZxW{a-x}#K+VSfDJ z4=Z5@@8$D)@=_HN06q$#+g!S>llJ=5-wVacUku#{#VU(?BidY{lZMYbnGl+py;;hg z4Y*%e6rE=PXa?~SFJVc%=*EH$964FBRXF99-$p7;59f^Q{ z1df#P@ZUvP53m9tri#54^5;H01+~eFq2a(2jBk?gJAlO)Ln42Ep>27+oSZcyd`EWB zC`>&w&1`5yA-pN#A@?>Ynj_QcRr*rWRQua=Y-v;3#foXY_uXk_O-JGc?3CW=8I%J61SVlU{SkfgCb2Ivm}G?2A+1Y9KjYHXH0)LTK7))WlV^qn3~4L{`}wCB4VHo zqru9ORbm$+Zm^#7{S7^F^~I~?Ak6hxS!;Z?ywFj(CE5 zpVKAnXC$uBK^i;m2s6yB=2JXH9JXU=EO&b~wPO+g^_A(ne;ahszChsEA z=DsH7N&yK`GOr^N<1EZbN-&awgXgzldu;EF*#TA7oy3L-4-CL35%@Tv_N5LCe=ky2Z6*1DvBNhU5z%tMLR-Y>7B%x*XY0mQe*wnWztV zrLt;Y5yS>=_w-1gqjqB@cdIbzXqKGGmrio8Lp1ud-#5O%{l@NZ9NzNyboABu3Pvyb zaFC?v0HBj@s)wU%}+g|XFW%XBW|M*YN=s-ZA5T%=uf+^bnqZ;ETBL5212DA%e}j z`zV&Vt8o(Z_MkqG82gyqRyvHIS{_UgGomnS?Nsv*76`>!)D}5L@Go}8f8FUduwoU)7K?8`pTiB5L1YTlg>rPRPe4ERxtfrOAX2fy3EOl*(+>JMxw30q zreotl$!xL`5h5EHK&3g}W!x-~n>+9nFD5JUu&c|G*kVk=FS^-EB!(EkuNkdxJgSfa zmptMBN(_fdP;(NYSNAZwC287j5QkhVSEaqD_{LJj0^c zImtTH!k<5=LQ4Xd3M1&E*RQ~d1gSC;PIJ(4wdr7#xpvfyGaH>-33MUp2%k9e7Xcfv zDrsJ7$;3hginncC8uRA?A9@=bwdu9;yv869yvl&12xe@U1{W?(MPlzolrGs=OQq(QREnd8Ni0-Rny`#%i%@$_8foGB(0+Q{dfYtPJ50Yl^qAn`pbVr{ z4v3jhTrzI+XW*B>MV|c)zD#B(s)tN57Pf${`}M-P_W0?v^K8|&cEXHvn*X4TdB!vz z9Tf4;E0R9<{_2h9bCEg_;@4d^IWu)JDbTPHm(MEu@;++#`LmMLmY|ba@7Fwp`MNCx zTpHw+pZm;?SLt{1M1TY^U|AY2*}R9K0Bubljc|MjF*G28K^cIzfy?*1x@GlwB!L8| zYhhs~r4K1pCxB_u=t`gN0A<0zizuyjT`jxD>Y_J7KyzYv=4&3?9aeM#1 z`Z42eXdg<=`mjq0yI--)ixo$ygS#n7a_W`SI$vvop47}w-RFiN-uLt#-Nid`FfWud znqI*UY|oe+Hcx2rEzna8A!gbw3uf`9+SK3M0}hiJ5kUEYdn(YDj9$#cGaT`JcCq)I z2j$+cUA7yh?$(8o(u}-%5;U?}Ol4aJZCDY%oj0`i(r|t{XP;VYtfGYwFzL!=`+1g7 ze^F2RLr2TE;Wk&@x~ahd^B#Gc`p1FPw;LVNa$>jD^U}HXVnBul3J@#GE@;gmeDo9I zeNh`;)Fmh*Qpd#wE3flh{gV>yelp$Kxaq?m@0Kc99nSj{5-}*krD*6Ho~DpNg-MP3 zmj4cC#)EvxS!sNxsS=K)IQP;}u3XEybpmeI=yL>ss{>~_G1i*$9!IK+ELLK^s~i(e zIIKMUUaHlB9l%)REo3HZZzymxO02atL?-N4T4qpYj6&)Nh~7n-`(M$#HotK?w5->9 zUTl5Umt{0>$F|0pTG77V#6P0)rb}wy&$OlZ_OF1bupeGL;pyb|v4MDJ7oow#^^z@EcDaiashI3Qvz6Q7L_wsUJVoD6NJ1Gi5vj+<*X<1mEm zcA%4}!b8%>s;T7=P?OP6R}10y?YjHy!{rl4Ka!yS*#Mz!PyOY6hGN35 zdV6lh9T!p85E=K^!clQ^q>sPCpnUHBaMQj&1Z9u z9JIZcC_moU#=k6`1?dIipE0OKq;IuY9<6pJWi-J_wB#EWCzGYVr&(($&UXC#4iTOj zBCtGXL~0~`Tb&e@=iDem#{l`xAV+P#=66`yT25s~E(XaaeVktvf}Qs}etaD`GM64n zpq7;Gw~xxo?aF?%Ci)bt-LH$-I7wJQAE1Ib=XKUJFv}*`;10ze_G`7@y3INB(Y*7a zWC)m~?m%sOxm`1rLw&<&`H$QZv0mdaOnh2|nchJC*M}KeSxueXCcF|6ZeFJ6^ZGN` zJ~riZ>%>wm!v5mt<@-5Zql!O*agiqdBckdYG7z_o!!~oSw{VOkcZkR!?Q#NDS;nY# zdMc0`;Aq9Gk|W_h@E~?S4_xu#;(BAc>e%ezk^KZmCX=8cG|9 zGvl0X5$8r;`q`WetVU~y;r-*xhgwW{&^O|~+W%>>#ePkAYImQDL(h$4x^imb&-k9B z-<2*tK7Msch(xh&Up)%ll!##Z=->|cuw~itxdJSXW-N0c%S=tKm@YFWACD2bR!Z@F zsCat+&tLA@`f9uc_=3ItO&IJ0k{RBzyu|vRD76?sEi*J8yT|*H(Lg2h2JkA?9<6aE zA32C1hzOyPv(6i(ejZt93)dy~cRmljulFmI`>#zrT)f$`@A0i@G3kw$Q`z@?3Xh7C zRZ||d&!lOriCRP)-PdT((Tfv$1;&VEhIXEDC8~5=Pocf{59jCX+#8}+HDTCG? z2Z{h|A~H%vxc)WajrAHd!om5D0oaww+vjzhxxxhMw-07d59XCd6H7wFKFF&|OG-zQ zEj!$zj&XL=-8A-Jv>YWr7Y=Yf z%cEq$H1(VZDPe7Ks-kbD9!)U`eaa~`Ko}5ijCbImTHlkSY#U-p?Eo6vJ*`JJ1+0=Q z*OF*%u`>@JJ$6L2NGsF>@IZ>j&pOqXG}-s$)+7?FZAb6W+_G2jl_QdO1y#5|>&xVe z1#*1F5Yzf3z>)7lY9oLL$jaIR$f?mFU!>noTr1I?c%mB9$_?|ds1hN|3c%9QaJ5U! zb*_NB+WKTAM5U;Jp-tB~r)rlW33Q`_ULY~SA9m+hI>*Sc^x~lqVk4MM1@_ru16GGB zBX_=&47%xb|Ll?Sw*+z&w!EM@q7NgArpt(80z}^h?}(oM1_zI<{~Qfg*DUt)V8;p! zfDffE4*Vz0WxSl*#4?a9u@kgUiYw5<9NST2Q-fip!$eKuGI>TIuPuc<@zk!-GZT6O z#sw*ZsqN;@i&EI_mg?*W*h9Dp5B{WYwz$WGBEeX8=VK=5cdL4Ap{i477@!u~iijP|9< z8*I4RskEf~oT>2;ya)zn>wLg9kK1NhtewTK;GGPlX_`@40R?XsuY^`!Bo}?min^F( z%pqTiG-piRbVCv6eW+-LyP^R1)hfQYGV_xs^3sce|I5vi{@oF@)9;y8ehFmPx!Vd1;w_}c%?08UIBC197|`}Vl~ekaK7nxt$ddUuio=MwRCvXTj3~|vTXk4bfpn$YxtCDzb4^U zw)>7I%oy8ODMq+@%H?OKa5k&-fUmsG%h3%qY3h#!t7LHhYn zv-j~~%AexLyWawr0oR7vWD0v(XDu`8<#`1*A7aaIP^`pX=1cr|`5Q8ll?bH8sF)!H zjlRWuQy6e~wxPZm$5y$ft0JmF*8V!gqSwJ5NtZNiVA#dZ{-GNim%SC2Zd4iKt%C??yo zAtKK`Z1)Ndh?rpcUDEY`Fmz~tNCiN_==QcTh~TQKMHhrKbN8ahIxMQ2`X=QpA8Kqa zIw}~+gLsCVtkSOMFuA_xrv$2E`cjMs@~mmKoXV~Be<9`g{!6_E3vu9#U-IME_v4cr zPCtN;IdEDh__id3ANq?MdV74dYq`g5L1j`*PXNb7H;+&XPl|VxK4Qa4l6SbPe#Z@0 zj5O?5sbqnAl38vLM(_$@z1aD|SvL0S=lpf(hBC~!Knd768HL9a zT9KhmYV=`I=74~pFX4Fnk^%N{=v8d*58zYEmP-`$2~K+)12K3<03)B)`&^vNncCA2 zdXIPe@5vA&PNttsojuyY*_J(H&qNfdPeO7>0{k_XCTwG_F}3AXzX|c@ch@LnZp6_J zyB{}NwU(iW0S{a2jasSpIv4Mq@e<5+!_1rg%G&}|Lb#BQyq;oTCbY%6W^CUaKR*Br z4wOF62iOnL1nF7v4s-!-_{$w%$+AXjn?5t7jX5)6qXA$aGTML6Mel02D$HzUv;}s7&!B|IG;}&JznyTCLkL| zyV(TsIgq%OPkuOvxloSgyrT{- zF0O20n@kH4;^j3VP+a0u4>m|ecB~;ko8Wz}?`_7E|0_EBQ>Wk!oa%Ji(>-ZlfKn= z!!2ErUh!BG1>xX&H?9=M%EE>=>0j|tVjrXx7FA?{w)2htwgnyp+Pd>na199wrY}~p z_VoN4M(WUZ+(0?B3x)?#izz?e(DPeTP#kB3GSztF!OFA1WjyN7JtE;76@?^ri+# z?+qqsJvqF$g$nHXXZGNlujeB6=4ffS1OX?|>aRW+3tWA4Vs|SgRr{>jmr_YV^HN{`~`+&~y3| ziR09r&V0$&VwpTbQ%>6AmVXUtMQTR5^S}^OkCBa=L=Y=iQ+cBRXy8gp=tOVI6=Cv>N3kukGc00=JZhEgnW_GgYAy;|lxxAFVaj(i$`_dXBe= z(u~jL$G;FrQ_!pz55xi@h!~hOzc1!+K_F2|i(7PVgb`L57V6XSz9w8Tea40=^c|XB^gr`EKl1lH)zv*PJp2cBiv1=mzja{Q_?$s z|I^9ZXTFDznI#o%1v*}Dt#XEH;H}-xmR3D!@Don>V@cI`P#HsaJB}4LynTy{Oyxzp zyvA~lqK#!1>LV}-;*2d-tk)VyxqdSj*JXhNG3Tl_&gc_mVz%zL&;!$@>FDX`1clKU zq}WrMv2=T2NVP1bcFoU-!WU?vKP^9At=RDjadWTNcNi{f!kv@z%&c|Q3N>rM2Nv46eVG-Tuv7QPIoeEt)Hui$x8Y4|1{3kMuk6+Ha;<9Xr+?Aew& z#Ac$kA{n?BHlV3DeY#!LMR!l&&J<;zJigTK;sQrRlo->gpmMoc@h0E@>Nk83wtyrSK-~#geooGMGM7SFkTKguKJo<=| zX&+XcX7D&H`s%#WU`{^v6cQy>iQbuyB+F4Pt%boWje77v1cI?2@%!A(bc3V zg^vOhyBEo(e$l?Hi0O5DoS3J*IJvnYf{KnAwi`b+?R+)X{DYqcp>;y|=T-(FMc+f! zJd3!ZWjskqS)jG=SC@uyg(BfAFc#yD6q#!msaaT+ea(t)0mrScdI+~L;Vz)(P#4l- zPSquqWN~k(P4pIR(*dyBPL<>hsY#r^sSwF98xhz@hY&@%3sOj&IKSAR5Qe4A9zhWD z-f3{y(7slWu=WIYzZ>uWFb4bQm75%v4DVb(e%yilqKB$iOz+kH$*`?~uT;+BU!2hC zeX0Lrsi)@-5uUj6{=#tSR-ItFmh>8khEt9!MK(bN2t{Z}ET5lUM|6VIWbw9VEzaO^ z3+p=&{fRS6Fq`cDiaLO!q9NSNu>Xfkox7H7={Bg6f!}fCsw+-}FpIPPmm>)rZp2TO zx@SqYySR_+HBFQ)$*%3|u-C1;jP!ITea(nT^Vfft)1sfr`O!NwKXM$ZX71I6>UV$X zE5JKpjQ$kNvmAyH0DxA?*K^U@p2x%G28jYPd}1?Cq^(6 zMc-wuKFt?FB#n^jBEL@s$u?q!@< z`s=CP6<1;e%bLP_yczlgm6F+m;t~Le*#{Q%9I6znaNOz>DCQ%h;4rFOK_eZo37+P% z6z9D26!N$4jK-{4Lr!bs zCiF@i>YngVTkv@;PYp-M*rkO9#BOT1@I1iT2as~SPxbM>b|xn9%D=ee1)$Oq7uY#R zRR+XzW(DoOAh@Erg9^4!j45*O35SBFfBYd!Bx+~`f%tFNQbVrW`QVz)Jvc7l;i?CJ zSYIWMwCHJq{b__;HZv3WvI%}%TQ0#~?za4Aqzq+>6Ild>Qt!0U4Zzv$Qx-??iEY=6 z9aUAE3Wcq2K%lx2MM&3?rL~Igry_Fk1Ghs!2lNVv4&;jt_l71(!Jv@T`s1LqwP6Y( zg}?jPNC_(I)1`|VQq;2dj?5qmw#5v&0u&J0Q>Ho!m^#A9%}mB2TQ@iFXPjhm@t^gn z{?F=*xKAjq2Ot6({o#dyr+-XKO1(i5>#3w_960rAmuoulOpt+3@W&nc$4+%PKcgbA zy!y}aB8hsDQeP!&g`A(s+3fVv_}HvSA5`-muZLnu=MK1287T=>nOq)NMbj6otuYZs!%#8db&9?8`LiIr7Ei58Ak1tg?a0cntuUSJWVJES|LyGtYm+ zDZgRNDB>WtZLcCM)MmA@Q5reXHW?IbyaYX4UMF>L*?d`lv_m4oVIspFK*WS}&251O z3o*RkN>&UrvFHSf6As^%C_>>QquZOnkfyySWwo)Qv?=}5e?axY=T21Q|G`9*Do0D7 zVA|rOA8DOsQvdE7`2-1qJ)Zrn@xKD8}sHU-=p^@>H3^x9Yd8yPbj&^zOaygP5`}Rq+o#TBf^c zCjWGg>fLajTS3lCR6GxN`v6W^IPmWl78XeyhWB@eAXw_|LaXE=dQ2G|RipODU1k}J zX!)#+5AgwsyllZrT-=0SdwP{boyg6eZ$Xwfh9EX`?u1hRMrDa%9{H$VG|gupi0cAC zO~KZUoT3Zq-8xZw@&uQa@C_URkcQLH$+bga+;E6V(tX{c`~1!F1Im0Y+klCnaegMp zvN5emwz3gjOroaN_4^VMD+#PZ;=4baD2VU#l2eT;V&VRwyA7&IewOumicf=&A>@x3 zzkD5}WDbGAdz|=eJ@*bD?mK2}Yxyk79kI}*V8$Fb+bsKQ1UL+pg4C_59I5id)DCMhR+)thL`quv!1s%(jZ>p zF}Bi);Zqz8YFq~Ci5tjp+3}rfwsP+l?vCsAzl~|oQZ$N)A@_Ag?lVH|^9n7&BEs_v zv)QKzgk-9G{E0kMq71{H{|l^0uCpgZ9{@qtkZ8Eyns&iFf*g)C8Q;qxX!`W=dk75G zq}1%Fn%yP7_aMv}%r~QE z;{x@5FcWZ5A%Yg8m+W_Is3ukq$)sNrVS!D{rKG0~4#R_kr#?$38Q_&@sT-8>Vel7X z3xCU6`j+os=RO-b>+bIU@^t%Ux%Nx@<>sa>^0zQr7@fIbdd!B6UfZ(QEALtcif4f& zeZL~Y=ziUq3OP1z(uJg?r44R555=ItP(G?%jn!oe9OuTbMPDEP2Efe8NWe1SOGO0* znbBpfS-pSPp+LFtIwZ2QxmRo*Lz6TbM8yjG7#Y7dlVRId>V4{p+z%kkF`wQ z?4mCx6N4i5=x45YOMPG=lmuS`HG$c8R`C07Z%u#Hv1}Lko<@9n zl~&wk>p#v<1pv*6h&aIhTLY2JeHBX75#?BU0%L|il+SeSI1$J?!=Z-Bu?0@$l~L^1J#ld=Oi1Hif_T5~;CI-hXBce1 zgZ)^p$%)1H%b+U}YkDlm_E`T@(}3&o%vDzC2OP^Z*wt#sUyfKUV~F@nanOhScFt7U z)TGJXF(X(dYtJVpr8rZ=sK(H(^-ruqbo1vB^)N`q{O`#_xexzv4zBh6hkAZ`MPaf8 zo3PUsz<{wgU#Y_hOEXyGPSEhQ9@Xj*2^B37U7XB674Y5i!yVA(MpVH+ zwe4i*32WUBG4=nae)=_YTOwSZVcbS%$OP@9qh4MY!Iup=x>q`~yU8~D0-Lai=P;>> z{DSrNHi9Jfx3>-uMXOX^dSWD2uJcs(+D*XUq2aVOPf?6%|KTzG_s zZLICL?}aHR4#XVH5*e5bQdJk2dPl=VfB4;;zi#Z|)W#HcsP{djpg4c2YxAGz7wq6P z14V3eCa7UgkCBQ=OO^280=oE0F@B`!4_*_GZhuRuoKRXUm^Z6FtoaPn~ZEvLN<+M2sp! zNM7w~lhLnXha$KXEsp2Yk7MG9eC<8Gjp?+Msu_)%Y->)TfYndxHBS&`sZ$2HkBFdm zBHXE3&+1m=%M#x*p67GOJ%j=Bv4iH`kt}pj;3aashI%d9n*t4^{HY){9MRDs3AWMS zVPdx{si~>8+Ys0@=M5jFuqTA-@fct-NlE0rN$}VuQ z8bnXl6l9*k;TcEN^wLhnD`Lzj(}J$!l6#^mt4n8`?tfTV7eNzvUg9!MSfDTkx00My9-wb3JzG7E|}k zU#BtCmYS6D8jZ3S=Ei_oscx`?QhW&i&hvOj{WII=);rPrKXLpSc##0k_1^^NB1(RH zU!Y=X0rEF>=8rGUZpJITiO?{V@k`Jk|Bhi(x(;_UJ)#Dzq-!HpZ_rU`AD6wBxSxC* ziG|}5@=;)W+C}8KKnHWFoLlIbOLIy*EmpX?Z?+MW6l+0sdMJbia*-KkHl`y73U{tVAweKBTcpX`Tf&ZV@s5kGt~4c*uTR6X#G_)_F}QE# zH$SZr3jUT0SfOqZ?yilYV7=paSh~%)_<2z<^ffFr6gtMU(R!jrh=#$5>&hAECk|Xd z{0oj9J)-AaOPHMw{;*(}@9<~O2Xe<-6q^PJ1b;z1?eQE74qS#@t4A8fOd2_t{EMLa z*fabkMVJ|jos4chF40@bsrP^9kWd-_EFAi+GY*-bhph5*T(xc!MmrA^YD!Hvivbtl zPYv&%r#AfomZNIp`Bkm2-t&YQgAZE)fvG;-9QsaiM8BtFPgY4K9W zLZ1(GJkN*&n0y95VN8h>Dp9?{i7KxP@AxZ)BK>X$vO*KOWz7R>)yR-6(v&bCmNTFb z#_1=0x$o^D)Fpf#=&a#eOlKH=dAEF$abdf3dpbG_h2Y)ZyX1@%(7wZ@43+Py1CyX? zSa3J<;fgXX@=4243Fn*9O!xevgAe*T@-v)Y*HXBRYaQlEPtZDXVzghV6_Az99&`zd zh)`9}c1_^Oy$l;)Gf}k>f}+3`wl?Be7Fq-~Vi-+F&dkbBf#b6WB%k?#Y=b_t&=hhz z+Pkez_~}THm0#=3%nXn<9D@ZAxQn*B9{F;)c$L6=>Br^oRL2RN!ZiXD@z3%|iugD- z!L$@rG2XeYTM$bU$)Z&=#!~&6U_!1SQ}r`Vq3@XY##qFIn_?sK;reVB_?}wk8{Br` z*CV)S7^B3VvUKg>ozlT-y1&{woT+$j(M*e9N-QUIte@BVK1S6ag3(fIl=835AJ*?Aqp62Z4v%3k4-50H`h%6XG;~>7jkZNvMoOp@hiG%Rh4+6@NJ9 zNT&9`T;Xz&mTqg@G|4LU)4m&dV=TjI&ch5O6m>=9&c{d^_yCGsCylP7w{ zgg6y;P~NYBfMtCPokUSnQv+9f!-wnX)kB){L$-mo>afi0_te4q0`l5=;*TKV+Am2x z?3b4`;tjCkMxE1=-_b#0#kTrA8ne_($Zp%P2oF_vTIaT zaGhvF(WS8B6Ece^qrMgX44&iAczC2cu@{%?`Qkq~t;FHZhdHs5HD31?|N8?fv<20w zLQ+8~Fg#ox8Ug{s=DXZ-)Q$^|GbahEcCMnD?&E)!*jyK%m$O7=;6r4HLsri1=4A>L zPc!WRn*9$OI=ilm4WCU-b&n^|;6t%H-5CG)#$@+{MZFeP%5Bm6#|;xPsx1?gk&yxXBJ~3e*bCGPZ*&s|v2)W? zGQQYcM1N11;mu`n@cV+pLV@Lzii?%700tIX{%*@mso&d!fd{r`Z1PDV*N zZl5`WY)N{jvES+DWL2i*5Uoi98DDek@un`@&V2oHL?H@V;qWH9ZBFsRY6EdZztlrV@4}{S%F($|--amcp zN!14Vhc_~7Z~`&j-R^yk3i`+bA-(%YlivyKIDJn@BU!Sv3ET=RGy;tv^3vV^5ulSm z98(r;Y#eTjfrGMG9c1sL`X~YUtCUJ;D^BwY_y!PKAKC7D;m=l!`cTG_s7 z-gJ~kOuPAb_gY}w;CKyzD?@$h#q5axxkj0{xlczsWW!fE2qb5SX#6;6qaqYz62|== zLV#le`|pMC8t2z(~AQ7eq~ ze?;00st*4@m5gV`)@wx}E9!rgtgq;Z#9LYGFS*uVYM4_o-a*k>M=w2loL2w1&3UmR zP>69b(y5H+Yt40j`V>Z9{4t!>QD-IfTiy~oLlTVMK)?wA_oKvLzeAx1falXz^RnvI z^FvTk&E(>zi@)9up($e_emx4*sb8OS43N!Ly}L-4=%RlmBC=@zG@27{jQ6Yvs})z1 z>_1cz3*7GG)1zG_*-RgKfth*4lX1-~$>z+fLCdm6`~9P%a$~Z6nF}+^H`&U8I04uf zF%GPwGdi7bIEkFtBsdiBub3d<`9P|Y5$ zf&n~%*3P+5WqC1Ra9+vbw<8A%vYiKJn?bDdP9Igg)^fUG9TCdtqAu?g12oQBC&H|< z#6t%`3*-xS9`$Z@QXjA@J`Z4wd&QNja)^4bpA~I5qGmNw2ZX=An=UhaC{Xgd&JLrd zprW!Zq^c_K}i$@CN7c4 z$oS)*>dZrP`LU+Fd$hsfmGm$sb68yXjT;)F9&#HnCZ<_xGZ+-fC zMeMWj8a^*2hB)tkKV5p){)hIxI;v)8{ZB(V1jSwsa#DM7XIUL;Zj{YT49|`+B+@g#H=1aOZ-*` z_57p0b?4cNioduVx9htG7lPY?LRie_qOcH~%_Ql(`y1+j6Y71|9Jr2ju}t&1=gl_Ik)nyx0$SE%&s8B ziYzn%=H8BqU?s-c>yEf~%t0B}QF;Lz=MkVK;IpR-b_XVC46^L*wQzA@mrQ z8EJn#0U@f-*gDnvV<}=3h83(VJF5~*i-5{tWXc9PjmoSpd|Tw!%%(#TTsaBq&A80{ zYJa`@w51wos{poI$T~P9Eg8n04f~h{OTIJ+?-keuWmYyv`K4nejr6BkVolk<#vszd zfP+MWuLx3IJ8}3dly2a)zumOkN9eoC_abezoDLW>>#+tUPx%#GV1*PSBa(>HaA3iV zh=_|DQ}SIk1>&;z+1v-TGmM6XcR%EJJ z1?EnB7aOfWXP_!;@EzckM6v*7_O-_N?M1D70T5lGCIRBY{k4n}8=gtxjE)vzoH55z zf*7F~w5q?RN43LFX|{X=C{F;eH%k%P=44v?(bT_03gf*r8hYvs=3n z)Cl&rZQD&gaV88R>XKSGEv@pB7P3{?is$dIZwtI(*-wb>_LJ}5O#qB29+Ke#Yz%UU6v5Ge&XGqhYZ$&r6yHCJYGV zw*6YyIP+1_koSi!BcU-K1p_{DC?_4doXC>MS<@h-;4jQ7ert`Tn-;E-0 zLZ_jm?7(S?h0bWkcoQfcPfV`c4}KVmN%wvnK~K$y>cr;@MgJK)eRbe+yy{BwMFXN< z(ASOimr?z($W{VsG4(fNW6~HAeghZO=VsFHa!u>My-?!CfUK>p?M{nbmwMdo16-x; zALvs|DM&;fQy3Iyw)r3=aCU5JX@8@3o_mH=5K_Tec}ELP2o5!5(h6c_+8O!%>9F-; z{^kII_zp2KbvNmw4zXb%3@wk*j(fd@WqH=`%;bP6)uMQknUQfnH{I;sMWQuR%l+zA z@IIZ#`y;Zsptj8NUmFU2X`fz1T!X5a2>v^d2uAaTyGshb&EO18PKJFmp-9I|YR zc|2>0=pf7_7O!?pDo!^z9@g|d7SH;W{Eu7F2>0~NUgtbQ^uKL6{xKmJJG^+;IT#&F zJneuXMqK*QV(|ztF+w{nvz{b#WisTTxJpzK$=X*yxf%BG{6a9ZJGQ^{NQWW@V|2nO`sltJq z8aM}No>pvUKDj-ZqcYRk?CtFZ5}!u5-Gi%bX|d}~{uXTJ#H5)FTfv3aJ_>HIk*lkjxA*2Zn!Da1{^e7`ha*B3&EiqIkQib1u<(IY ziF2Zxd0@Di6-~YCFrA&J!R0$dg|BEEVbP*WrCrbNvi@L0CDQ9jPffMnT|mkv&D>65 z4Y-P_W6-d6crQ6$oN5gJ`5@K)=%vGrAk#IS$z>UkGqV_8{SmoX;1(7xHVvY3xltEy z%(_DPF-<1zBv3m48zHLt&@Z+vf@sVXelw_xvfYclB~yX*UfAFn%k+JX6>)YSz7}DS zDI&UqQaI>`bBLSet5jG03m_XE`amdpIu`365|0o9bOrDmStS@12I zlDUIl_2rQ!Z>r<~WPsDQDDUR38e;xx$-(-VyI6XJ)GA)tO3rb`S1oToABPnXGCl3rw-IA|5@gH`oC@y!$KWaw}mKP zu2i`i$cwQ+hUd(jMkE$EKbchYUS%!7ET^6j#p&>R>YW+{J)y5D{$ zLBxHj*w(?Vi}g6Wt~MF-s}ND!zIL}JS}P85XdFx;=lXyMK`axQPo~`06Jkf@7wFPY zckYDQCr|&jt}4bKzt-q`0)RxeK)jgqvfqXID{{pK%Q8Z_2LdsYz4u6Krx6$dKO5ivt+o5UJe~bkuls*^=Oo2K*1C^}Q9FUj9ID>c;B2gql@wtE6Ofa(IOSJ>e zGf{ufkt!yYq(h6za=tQmaK7f6J<*%5Eo(dgz|2&7z&+?Ob0|!QVANi>SrQM!gtX~# znKd7DP87v*-0hINEhqh6v7Cq@N5Ff~B~eEoPoN`S;9JkSb7l6p+gPba+Sr+qZX?uj zG_uh{c`~^7(#n4cSR=hgt**$1xA%+d@_a3ko+JPQ@jdyv+^`zS5=@)_!W)Oq#D22| zQZ6UUUm}4>Qv8Yi8x%Dv+7K*MF~gmNR=>7OGDVl3%}Q7!=1Ep?xMp3Y@)|qNweGqz zC$*5`lXXqbYse5?wm`~tB2KWi8j~t3^&LwUvo%ImY6WjR4`lP?N{_wKVrb{7-q8xc zc1Z>Ihzf(P_Yp6A@uc(v^hHkZ&mH~A~B%WPt^$aA77!6 zpus;6{F>q#aZ-9`P1TG#)PK5-S_Pd1 zZWek7qCh0OeJzKw>qufMAOXVt*jGGe>Wrvha}CX6qFZbcB#0 zY+LJ%RcPtcX_ED^mRBE{A&zJ`_n$q`iqS8_3JRF$j@TRo4$){l9{?FmQ=X#o#7_O# z;Yt1PG0P8_wIKmZ;!nKb@BxVA!J^2i5VS)3<$C2TAF#9r?tX%_5f%^!B20DW1f2u6 zFh+PYTQVJ42zI;TZ}9BC2?IeI!-VMe;}+2bS~qn#-(C>?ZvpxFp-37r!q6dHV&q0w{HT{VS9I*HO{ z!j(LrC@(L1=rwW!%I-0^s#8Eb``I^{3x9-G7pufy8FkeD&Mx^gA~bQpijJO(kkf?W z_?6;W8;-L?jpT_h^au-XN1+TBRd+xYjPW((-xA+BXr)`wrh5 zlu_HeC$5T7_L!Q@Jg}Qy*AW>AKr4K;N5(dXo{sISBsdnPy3|-~lstwpBW01lq6|)R z2z`Iw%{{NaWQn8_B~pObtNw7ZEWe*Us@VTC%hZ1p7@oxHfSX0=hhTkG$<1A%S0tR0 z4dXi}LiE>Y#>`4n{igQIs&}@f5hG-+TnNL8=Z5G3nfk+Z*~9r!2chLF>v{LAv^5Uq z`eOYTcjVf1PG}H|>h+O`aEvd*`^JvRk^_~l(HZvCS*h$kxF`15K{qxzK5q5&km%kjT-R zPj|1*3pa*UMk)w&?OEjxMUX=4qIY`W?8>amL`6;QFVmNw0O>>u1`E>jeWehv2V&$Ri+u8xN1g^Iu~15u%__4Uj46gngPX zI@Ao9#PR*T&NbdeIYD*W*9>B*wvULI)F!yhv>1RG2S=JZLwA^`tthUot zyPH4*w;=zPr8A2WT@<)E&F7IVofkWJ%=+sji%%Sfi`mn@Cetlo{?7g#$Qb}buCGM0 z6H?uu`7M`0qq)|C71Jiqu^{UwSGIgUokY?fYp5-8TrNi*r{w(3tcvjMl~xC3!hFk zrb5`1n#ZE#oduP!eJNRn)Ha8WULB6gm>3tA)76I9eZa$E3ovLUDO%2uw}w8LBzmyn z?U|S#@~Eo{c*RZO@N8C3(~D}L3}Cn+l4ZDw$v@si{nfvPD{D{uhl8=X+Hnp?*}A`sj9ZT79n!3)0nA zrurODsB7F*?J_mv1zw^T z*v%{WH06C&jy9|F>*Rn9kBBKe>8DJT&9!zH)dGzG5)&nlW1G^EGdSZ4CyP$2KB^YM zqv|b*X4Rl#K(3>&zf&T5=*OYO^I|V7D6mF=uqsDog0lhd0sY)~2jAv8jamY^UV!PH&o9-1q1H@!^0D?R9#VzDmw} z`}D^6X^i(3tA>%rbe0{QFq<9&LL`CY#B*y6dFZ^3*OwG2d0~oPgd(Cjwof#|Y$wS} z#=cMVi|ztsF6QPcjK99wt6IJq_DFMV#~$6VflD%COyamo*)(J^I9Hp#*-yz%=eF_C zRM@aCgmirlhN5bSVi2L{EU|3sefeQV#KPvUUb{;YwAs%9QOjZ)oTWH^-6MNAw0a(Sb-VI?rXHL3n? zos+kIi=Q+}T9bEfZ2e7O}*1J9yWCeD8q<>qBs&R?$V z5fym-pg|oxfeE3Zv!NoliaSrRpL`!Xjd zGrY9rK6HS+5%kA$Qw9EWCUc$BsVe?>h_bpqDK znaH)yaQ*c*;P2CbzpGC(J9M<=yT&N}v%SQNF!K+$bjUc5AFdU*r8=YY7s1Ti`hY@1D|K$Ux=_p@(0lv zD66qL$;V>DhhDeoyKDZI`uje{OkxR7gLa;Q`hC@YEAJ8b=>`8I9HY0ig^#KkAc35C zm9uGwHv`EWVxZy}UiE9G`5yN@ds$jZin%SNUhmDo!0-=Rsd+!Wuv< zA|cEh3J5Sz%X2l;|90-Le0CC@2$-=BDU6do5$dxu|7iL6zjinZtH4AErpyyh&c62* z+MpY;2tauZu74$$>bT6!%)F)%8%5WTnNyg5q9p8eGE{ea;L+j(;=;gp+43sgRhRd7 zKRw*orSUD1g90swYeku%43kE#J$;U9LCVl3#OmRW@HMEc!=g8PlSN7tDAX@6B5|@J_(@qYsBKLCsAeqwnUgqPE$&TzxM!;T8DjgXui6? zIb5zV4TH9y=#5uj&X4Jcd|1dp;Q{?Nv~T_O}YvHwkBB=CSBD{ct-% z6)Sw)n<3&80?pV;%`GtZOI!#G>Jx+GgO3a*xI^8ivszW&72r{ zqe!#Nz^|W)@n0NoM@Pq46kb9bDreCsW*`VL5B{fpZz8mD$_MzEXw{rADa z`x;0ybyZqHx$}dQE+NYFr*#g?&Bh&};Fh`QbJ#@5C*1sgI)+od)=ntKjx3k&py)Cc zlfom=0<1|i9^4XaqviX2A2I9>lLGaZ#0jbyTP5JQ_iySt^-%dNfZsqaL8?51H?OE) zpEqNUq!y)A0O&ZtZ#&}%Zq3U94}P)0A^}oZmubv&$oE9o zs`To7N}a5eHRRP@CVzMG_yN(fElDVH3dl>lxoT^`SObod^Hbz#@?7P#;(bs+Kp^SZ z2o5}1TmoSsr4@DW82E0RE;8mnQhp6V_(O53^z#lyacB~dI25ekG@GZU4vq$4QhRP^ zhdp!ZBnm5e=XTVe`0d-bT01Qfcy)^_VMIN1EcqbA0Sbs#w4blN1L+F1AkdtDJyr8b5#mbSyOw@Dk;QYa4bgHC5kYm+Kd(DWHSSF0@$~nA2^jGf5GgSFr zRVK0!if(725sKY&q6BacHr?qw1_GT;dY$V;y*<;KJEK7T#O>l@^PFFdW*>qcBiu zk?yF`4>^w}N5q00*bB5}jw}DSOcH?N^x&?2~8&uYt7cMY`hG z+}TT#!c&EP++hK%Geuj~o^|K?f`Wp5fc4H+o`DRKSuoTfJdMwOAq8M;8L3R9QHU4F z^@tu8KJN|YF#r948^|r}CsXl%<~7oPD&AGHBxun)WNqms3TqA(1vP{2O}$-3@BEEJ zX47I#}aj+bq zx!^nn-fO=Dt8Ni%svaszN-7#dJ{}%dFlPv2Kj9G)f<*!(r1Q1uig{`U>X!dS1-E8? zNz3?>!A^_=49!bmeRp=QpMx)C>*Z}@^9LLc(o<3Zmj?$W`5kL_eTU`^q)&=ePi`b!ikuJGuHT^0!dy~! zx*2kC_59>K}O-c zjCJeV{QIWaSZ2S%sL?>yn2e#P;{)wzyQ1t)13RPl8xqJK%kGWR0r&qg)61_8pS%5i zcPeuiZ8ggn0-D;rCAaa`kTByWPQ1V7ghk@;z$)$Lozet)3YCZ+zP;0L> zt|nB<`w9|F_WQpiVRZgVHK0Hw3~XqZSN~(HcS9TX6hz^(;Di5?u}&P z+RIg57-aq7&&Z@fhfjG0~e(S+hY>u-F94CjSHti*?&*@#15 zN~z+|g{;janeW0_5u@XIuqhOkBhrkgmg<;@*gYRwP)C`Gac)^SRF?>?Pe^Sns1A1k zfo`Zjtq(iMn$zgx8W6-x#%BOt(2%csiEs8snqGqWmN{}V^6so6p)3-uDmAy!;7An^ zo8c)VTVBiM#JS0jbV1&wDH+hJ4*4Iny>{OVUqA8NhnBK! zHk(E1^0NPoxZVlGg=OZIaer+HQwQ=DU z9LMV@XDxbsohy<3S>{Dj_=075L@u&r$x*rmtJZk7_hYywaA<0?R@Ssdxiamcd1|yb zA8;?+hI4qYuce%& z`7z50_4q%EvvBIgu2ja`aI!_ZzlsAV!b_ecQBuQ_+DH9qlb90U@gH>3Wa4Q0W4`rr zZ75xa^^Z*QM;c{f{_}4LK?rnC@Qgjs4`pW5veU~Utue1^8C~;1n%5{_KWDuQ#NwI)#V8wj8;U>VBE zYfBQVVne^>>7K^Yfj1=?)m2NSTTKtL1=~q!c?8^=5`Krwgkbiqc6fS|hoX=ymJ{;< zgi0p@(wdR^Q9mSFuXeGzthS`}di1TKMhiQcFBKEL(Bs)*BL^~vXIF`ogFNn=f8v~; zVX}CA_G4-C2cGcas}ae;r@`MV%E5yGg{;%&LpajDnAY1{adnG&lS zScY$hg-LAZH``V@Ww=ek38%!3no%)T=N|T=@C=gnZdddQm@(x(XuLbqa{soII7=^4 z67-)o1QKY80sEtGk*)lD<}EkjH~og6^kK64O5AxDf~Uo!{Yu{|o|Rp*yf?i6$({RV zD_aX`Otz;yxl|>v{FH^MmbHgs= z<{(9mC`~OoFf<_EG<9;-D6!z7P;ARr6k^0(ujVi+uze9(D9A_k0@wVpY zi1BB4cCt+r`!tf{_O|{>WprER#RtnO`Wp&#X;d zjeV$^Hh!gkt9hR~)`0_I33{GCSn@GdU8Q6|)R5GXOUyo5ywS;|&wCZl^|jki!1Y#D z+xSK&<>JkIX~aZo%pGG_VU|~mj$<8xzToH#<5JY`5 z()&M|PcMvDr6vfh7Ly9USo=|;$58q46+PO1cUre12Iq%ttqL62VJN?U!{(_nncG-> zY$Ou$gE*sBGpT*FMsz4?!m%sdYQ1bEw3NAq0>+hxk<}-H0RB3vD!Z`}GW~d1P)|tU8pgI?@t2U6{F3XP3I%VJDktT%E?rb^OY-i}oCj*K5uc(-!cj+221+AqSwbYT-{Ak7HDN<$Q z$sHt8d#P^1rly~mJqU9OT!ocLKY#DB+}Y(ZNNf_hI`!P)vu;rQ?qw8$qMn=xMx-#f zmdKUu<9*urnJa_yq|O37+G{x{VAZ!Ke`ic-R8`*6d~mL!62(*JIBSRN&}XwPpEJ^e z=g-)0US0Z(ncB4$=eoe{%+t{`I&j|I}#4%<2N2z~yTa0O@HomZS~I+^lFu(S)6!jHn$?b_-qkkImaP5k$Ued{^x#)rV3gHC+($bt zA5_*ZPjlzx$JG2ICzt<<9%3R4B`~+k@=cavQ=18_KVbTWiYZ%pI}<)c!#`W}Lk3wx zKup4~lb?^SP=(Rz#lqO>riv=tG;6jn<4Qc2_(5Z@jd@^QZ>TXQR<`!fLAEeJNhD5L zMm*UH-J=%X7yKd1kUx9%t-M{dw`Iy%^`O|S=LupaTO<`(ixG-VO+qP&?Pyh3x z%3=(ulr#{A!4^R+)lj%2pYn1BVcImXn0D>9owL-nU6`QC@EChYTaSM92U}Ru{TFxw z^NXG{`)NXvARRm>%x93_wVY#OQeNLYQLPVXKbV{xI+FN6@qu=%Vh1Q)sXS^xz|Cmf|)VT$~)JlxF!(qFF|~Y z@;F`_j{?EIYWh1$k%R^&;fWpfI$}6t)>bnw%ISuArtT-ZAnRNvr5#QSg5p`_^I-lj zkXEkm-UnEAGtNSK>DPU9r;4KP{IrFlZpDgP*xRN#Vv-7Ko+Bqo;bBQIg_R;rbCDp; zM&>PBoMlEFh)qK-N}%I+0F53H0W!kS~LZxY$91=0RXulLI|(<9uopG0Ad*>L9kImPrnOz|s& z_dv~SrDYw27QOm^1)dt3UVWaq72}47cwh7q@4i8@UYRvWv#6g-k2zCjXgBY20G~>iaxOotUw%ATUM^rCxgne*z*gBD7DH+6p|cOu zAldfP*iF+D%-2uE6B$2s+(EOj`zg$FEsMhGY9PC2Pbs|rnsl1>XY=>0c+5>Rx@pNe zp6M5eTce+`w!-7Di|bsAr@LEa&)!a$lz6;gtY8&?F8M)&p{&2VlMPop@o%-&HgmZT zQ2Lb47QA{lo3UZ0ONC=l=~9G~7Gy;BsIj*&lz@2(laNZOl3vL5cGcF#so#A9=R>ZXmC%ZNWL%UCPkdNp4i)xyeH{iK)886Es209=weg}-yTAF&#QZ~cajhx?1+y!Y zW#z^%{HQpLqk^{sUKoBi2~2W3MvNDhTeZ*RI|cmemaV2b++~Ae>}}R~SH`2T-s--O zN`b@;c3r6w+&(LtG|$m|zx!uv{fA|`=X_52heL~pY^OH6@KdLbf|0ar*|0+klUp9q zr<=<6W6rphbv}FG*uKvUlv>66H ziJxxOR%ZNT4G`R=C4 z_xVMjTeighcEHwYSTY~ZU0zC3C`D&zRPc9Ig^SIVhw?df2qw*Y+VvN~{9kg3j0ILr zo$|LvUJ_7={bPDbxqE|no%5(%`PmI#YKUE^6CI;9%U8d%d#%$oOcjtF5DqOw1h)`Y z;$m^83Pv{LX&PXXyyKL|UZkA1`yEPzEks^SPV>q9yA2iHCQ|xDvMOz;t))Hbp|{sO z^tx`iiu+xpYzSoq?OkjU<+$~6VYrDEnOqRB7*LL#hM9CGbi_>P#YKXv5C*I7Cuz-# zMEVM{cZWCA>W(UA(qfbMP0_-y^V3!4C8`>8V2{Gw8Ya3^MZ82c5o`low^*jlciraY zCfmGXIyEaanGO2G*XGx|cS09Dckh2L1+VEJ*Shr>4K`M2bnH1AxO=~)b;4eyJo=l~ z^|6cL)b)5xpI9|0xi4+A^vqP+(1o8!wI~Ja=KZxE&8ryYqO;Jm$SU4ujzG@)?&hJu z{=lKY;lMUiV1m>@=4#6_{|6>_^X`lHr?f_k{*=DdwYJ;I4n^uLY>pI+ly2?NWgQEl z6Al8fm7>lxBDr|?mNHu)ONN2SifNRxvbMa{m%MTYyGu7L|00e?05M;1>(rce6JiQ;O!m5#Bb+{*E?fY3xPnx zaY^T0!XXOpmt%>Rw53q)`-{sy#>y404(EY#dxlZ?@ zO+I+Fm{ex#;-SZ+to&mkz&0irBPToHEv5)EIS)YL?sCLSmUf3kij3e&H-&9zYxQ zgzhW)lXhMh-5b^0TU1Ugd-Xv^1q=cr}x>nBXuB7;)2@Pv`&TegeQ>od3(vGH`inzy0v|gwL2f5&~|4$gn^F2vY}u;QC_Nog@of1pBc(9uPtbM9757i?skCR#r!ZE2~xefB+Lhpbd4@1WE2>T5dA}&bIVPRvKerbeO7l z<|q9JCygt_G1e8Ps}_C-;7S<`s-eTk?hEby*hl~qLi`OdrTe~qK539mYSnr-alB{^ z7krptp|VFHht-hh@Sj$#+rVxnN1@Sc)$Ig zjOW2T0vq)+(Vh9P;}e`wLt+H9Bk)1>74UXQ0EQzg2xuHGU$U3Gl#UqLqNCl+fMFwF zIfZ0B15(GJ-eFkDb;}9^nXjZ`B9|6%2kI!nj$**kAlU~xCOB0p!O|TvZm2k107C?GnbB?kMuznzHw)#o^8Rk)?P{~RIK5l(?|ckqr-eR~tIXRH(C2Is=? z!Ox*bFAET{ubR_KH3xtR!E1S$ZePq$o5M)qZhr#W&VXW+VJygJajRR*Wx0d^iQ{k9 zE}ot$VFBdsnA5S%5KjSt&i>;Hl8xV=0hmZSn5E@wEKm`g1#}i-zlICKj+1|t3l&r< zxYK^X4PC{TaOvCa6b4vWy}brD!Q9*w-+L~|<0~ow!odjS28kdGlb^An#hw@&9RGCQ z_RP;-A#FHjl4iA-pWt)!tbAbX>AeNo9p&z z>O>3&p$Dy!qKM+K4U1 zl;EYfM3zhobWvY05!^s17`4ZQhtV$K9E574TGAPPJUM9q0}9_Wi9_S{lWP zL4HciR82#bwklgF`?=_#=C74$Cf8TQBZlT;p^G~^b+mUmr{|vO=lHJ|35H!=m`A06 z*3HQ1hb)^4g+ps6UfTo#!S2%hyu9i`@hE42dDL;R)pWP1aSSq61d#eQ*fh-F=8oj3 z_Vuw#Fjlt#M12qpi-6-k_4CGYZsmFXUxbJU*D)V_k_%uh7*d%0=WhsRy6CX7GXw!l zBGO1;-J>>m{sMe%*gymTEO<~_l)B>L;j$ou3XR3RuL}JLjwTru%aZfK>Wp1Ro$CFV zYz_yk7L8mrn!)-Xy_T2!aIqDAC;}XQSTjejx1-L@ek_=gOj}?B$nrIj-n)!(At&=P zbx#+j4rX{w?ltH`C4?jSO|FdEovW_`sWDQ|l>i?Ua!}2#GU!-*qlUB}#txpmS;d>9 z)5?Kv;&;ur%$t_Z((+98L-m7K=66qS4Mdd#90^*G(SQNW9bfJ01GJehpg{1y`sM7p z(#CxXs_p=~$NEqovi$%EfH zr_&FnVAO6FYaxj1yv@W~U;!MPGR;R=CrHdA@|7TIjQo7)Ae-8W!>mnMFE*Yv*bKo?{*+st9+tx^tv!e zT`&xwB=&#}OjiRa&@C*fDo7ogl$>Ivhue$NlLoAyTqN7#0XyknRckY3x%025Kky=Wp2>$812_nRwJV5Kb2BHD9p2Wx2_GP=`e zOf5xS24!0-m{?nf6dg zPPj}tDp_8IYC^sdpo;)2<}zK#Gzm6qQzr=Xl-YMkFA?BIl#e#KG(!j=f+5oFp66QI1IOk_cWn9{8$iQ~GH-E|3pllZiD zz8!W63H61Pp$@~UK8rs3uo*P&5MX3QkO10y$dKf58OsER=PG>&N7&Nb>l-K20k2*I=%UgI6l7Q5PDrgtL#By9H4^~jQXDfO%S=9_ z{3yRhA|44HVs&^`S=QhwiaI>yBMAVp*aWmIFb3FbiB4DY7BVBV(AXe=a&X|!M(pW- zZm0+doJ|5&KDaM@6qxq&7oJ@Yy@1(|_=a-f9I;*jRTWW*a4XZYnoUNP0RGXWX#`VX+*y{Ji6n63Sz@{DLsIp#MvR)GNUb0|Y zx2i3b+OP}wJ|mxcij_5#jF$!!-kVgtMj&6Q01hS7mmMf`iuZjkt_;47&+AF+^B+}y zr&^Fx8Oe6T*I+~vZ3%}~(fM}ys!9ZBV4)+w6mXczZ3J~8{ZsJb^eNFB>MfDWtHKev zx;IlesYBUW-M6+TO~KkHPKOn4nkp@D5~OsPq5x+uxh*ObSwjsd+gYaUu&s2=G;I_j zq$kMWU$UA<^$r>~cP*IQ`-Rh6R;S4wxEE&A0RLDsFQxC2QYsRF=^VVP=g<+^OA45O zmo6pPuTXj7G62tc;eLC#_`_M<*B3g1y}mFY)FK}QBJt@=C*@naU$0M*O^;EK)%XGP@>v>7@N`@*yYch2XB zpfE1{?q@K1_OQ)O6`S@3dZr|&4+XMOt-gUo1lTFENkp!y5XAyfh>WL!OxHz1ENL#b zy6bJse|5$XSbXSPAzy!RpxIVZ^;ylJja9p-H64>8io8VZ#YezZA1tnsfQyn-vG)fB*KDBYG>M^>5JpMP@I<<{UV!}nB| zAGQ+&Mt@`ug=in*%KMku&?*@w-WXB%b-DgmwNe5{1u!edof@%_*qSydv4ayA=^k=O zcGoGpeD0`c>p_@=`3Knw)&LA8j;ATv9pQkI%JNh?#wJ4a?OwvbQ6@!MRrrPLOxD~M zvE(89+Gf9)HI38EYxI(=jU=;w--^+^0FdstLEz3mi{M>u=ev=**9e z!gs3GbwxPEl7VZ}%DfOxHdy6q?|$YL5U+vjv8D>jA;- zfS_3qHo|#iUzekz*8m9CvQTmI4&f~M*}(v>e2SJ863kz>F(Ck0p)s<|8dT(;*2T+F zdo6}HI%aSEsG;9K*R3#PD0+o`du$bIB zOwFjeaiW2|QbH-K4?BV_Fh#rwxn%cO6_|CM_l5J5?+A}Dd% z#qk$gizZRYp5C6)2V7HZxXpE&Ty#mVmMJ^VzePU%uQY-er7s!0qqqAF@~Up1qI`#u22A$mCngE-k`nRH=F42ek%#3MGdVF3%Szz6VpMg^g;6Fi z?k`~=wR>p!bdhzY!gph-RX^skbdp}2b-z4-1!~JxaCd{G|NFpuK`dZ+yX{7yL_;>6 zc}_DYi5)m#`3Z`m>OX2HFtP zu-~sd^7Kp^kH%rfsZ)SIqwkKG9###AN{e|edpgq5 zI9Juwp+kW<(AW|n*zOfR&vy#F@*5A_kTE|!)bCfbq6AOWceQ?WO*-5Q+Q7!JA{7jK zOH(zl8#DXzS#G%DZJ=(48~?0&*-Q){qG=8;jt}x;2EKCP2h_2YOuxzgDr{vEoPI_= zaO{(&pX3YiZPHvrWt0y`0|sxF36U^!!WII&@X$?^N#MlTDjEWCf!FwJ7d8v|at~1@ z3!@6nf6>T>9J>kZco*qSz@JJg?`vaVK-M5agG_!}#lytEHy(tVt}Bs|mTQ;oR~fG) zJmC8AQywM)5u|Bc@UhW?%fjT=HoR_7z3#KriypR(8ly>-x{`OuCQ9wI&)E{2{HH`K zy`tSgiqVgFN_R7gpc)N2Ya}$U`F=Z0@7+;PbxK?L@H$Z&{XkRw&bnT3KP^FK zaa(TmIs*rYMhI7k{ja*`8P5o)PhGD!ab&&~HM<7nbQI%NeZ9!dj*ij9B{u9o3vI%x z!dVE3gb2m*VfB`(@j|GfKx-#2&T0iuC?>26F=ic|^khyRzlAwtPP8Q^}Q^eVg?7^8u_qiWS_N}JjQCjdS)4sIThhBpj z=Q{ZV7!arg^3=!NhYBDhX}S-VX?C9dN~YXIAf45aO_uS+PAf$>P0=Z`x2?YGtwuL` znd!2y5uFVuL6Xg97wedEi+^fVWDv+vLIA{j9Jvh?N`7RY5~ur6{)O*11QzP@O4|4s zZ=XhQe2i34{*)0L`dNnqV8C;4*bXkEaN*mTo&htHr|S?laH5kd!MDLb3R<}ZcU0Lp zU5i)7-jT(Ee%PCL8^op-BL~4JHV>HX~UX9^2ch1@e-997QX~aOJZXR(lRVo z<*=hnv`fo)*L4p5m>hP&A|UBVLBB{wDe_&BW0jRHpO2o zM%yg-{y?x#kgyPKDa|}bOmZA6P)Q7sy1w9j>B!*)?wH#_gE2&)nSgfrG7Ex1)Q>|;^Cf4BRL%fFnjE8CbQX(9&hy;2esf4YS>MN6C=_&RO-w-I zX;rsx1ru2quH_8vg9x8Z%*z!`s@0*tye@xkuEtovq9Zf#PR}Hc+7= z;v*~+2Y``;1v1SIig*t|&JSS^d(}$tS{d{^MU)0j&ZUNtb?YgN{btPSfHDiQW=%giBxxs zRUM?*;IQ42(|w6X^gocDBoEJIrk0ASATRHuwnzkQH2*uOe0 zt74vG%h28Ky9YO75RZF0zEH~ajz6YA5$6muSI`MY*-oc{2_^t=6qB?2yrLK`pQ1*8&HMFL&IpBU+=EWWSkd)| zEGP%VF5T)Dj$>XX;8KM@n)?|m03*C78nEw3#k<4-RwV%<{;I)Uhv$rUC!2FQ*$8gE zn+HZ5tYo?|oA!4O1U45NgbgS~8Y(Jy_un%;+JB^hLatC#{HS|r{rhI)%O^Uz%w-H< zB-tQOZ}+1cU8=ZMbWv`m+wfn>%Hu`pu-#_bd4u=n1a!p(pC@Cvfl9Y{a1t@f7B9pk zuO;|~vv*%zNF71=a8B92kDabCM@XluTh+=vs3{it-2ltNbbeAaP#$O>{aAN`NkgmrZQ~&?}7OnVh_`)DGY(iZbeqj6{wS00`hU*G!%E zpf>^0tO=CkG7i40$cx+0myTNWJhIt%iV#j=nlM_~ZHwqOYH}ZP%*&X#j1qJlZz+^?)7oA(>p{g zO^)U$413&!lB?KXC-gM9Tu976ok$sC;jl4I%(|4y@(ybqAe3 zpgxiTR4bzotH!&Jyh0gN2hY!s_iR6Xv_RgQ2;YbWX{&Ic%bEF)@~3qpr<4?z3t5ZZ z8mm+)arE8qP$mJV{UV-gCo254u-@#{rPKB1zGHUbt1;LbVD)JihiDpd2fU!I%)Zhg z53@rPa)4PWabm>hINS|SU?uJ;bX@h|MyReLvyGaVvJLloLYSsyzrJa%1+&7!3J7o| zyQn?2|8XD>D}=y4oGMglpbU10?n`TH3?#Y*rZe!3Dqv@Jn^q?=rqIRTtD;%0rwM*1 z)^cwwDHLkKA{FN4RgKF^mTJ&g4aGPFr2<<9-Z< zP9`<$VJ*Q#TMBUp7RVrz^y7+Wk|Ts)B(&PWbuBHtBGP=G|8@Zr-neMz1YVukjbM!Y zlumcd8A3ay1)E^iUmPkbs-!|1ft665_VEiFE znG7?Mi&*NO29?c==2K%>VG+%rlrt#0m!`7;aONSv{;qvYur~d6zJ`>*jNRv<`IMO! z?q=4`I{6Owb5d9{-zXrgUvryzAc7fv7T(K&78K8dh}3=Umzs$~t%> zXcT#|K`{4w2PW!mbf3CV+qAjVp!?OwaaFQuC&A5n>7$G~z-$q_MtkXWf~|^g{8#uo zzAHN5S%RW%xHS~ArpT>yKhZGz6RN7MISY*YMEzx?b9ZLf)-q*C`gmmFd9H^!Vw;Xp zK?S%>LYb{oG=HV7S7pHH{*;!M<^g*Q_%<(w@}s{QIQ!=zS@%J3Xk@5%>~q4$KHU12 zc%ohxx5~_BgAXh%?5h(Uk#S?8IK*T9ZubQrvSs{}8D;(Av!nyRWyu6*O;!#!HC1IW z%K5U|YgN+JUmP<@mK|4VZ^y>YIx-}px05dSw0^n+qvplauocm(@M8kIR_`EvUbdD0 zu*;HAXF-y$nf(e=%cqOY*mRGYlfPrpKZvkgMt|0R5kQSd zc(2(lN&aCscfVS2HEtz~rxx0>EzUJH$R?kq>m11c>2V(Ca(q=yOZnP7;odEIe7@0p zJn)5;9zzH6{`Rtq^XLg5pTO^DfK}udAF_CFLjp{PG0Gh_xpom%U^Ll3En2HnLCmU` zroH?Z&Yz}FWp@egWH;E>+yhhjqAM^FnQ-{I=lA5ApzK)k8?&*P~_p z8e4Dx*23Q`TWY>fLYLe?Ofdl9N$59Rub|H!1x|xI!nPSs(F1^>Ut36sBG-a z#R%ZxvFTg&^Z%vkI6}#VKt{2}pJFlooI6~`1&{ixP;W?R>L`IZt=j6h$hZN5JoFag z;8c?Cg;4yQNgPd*%^O+QF1diMvcZ3cFY$#MgRVwNboMrt)6Ada5Px(NzGCbK`Z22YnZzQ*rTd@_J{I72oiu#cK{vTp%lb*? zQ{xFK8M0-dlA zj|q=AcVT&lr=1BgztAbE)5Q;$i zU3|?_`}0z%s2U0gE&>T=kq;I_)Mm=9Nn5yZHsuFQ$`#_i5R4AcN+9tYV*C5%O2lEG ziilCYhl*-^C4p__N2>sz`rT4n==OAZmj`K{neEO#4{?B|<$E_H3a=)>5drK4-R_0$ zJ7}vbj1E`1rX>M<=c5jG3yl0M!2b&egAqVYa0E6;n0b6EjrlGZ2z@j%YUhEe#9&Ri zEIc2v4h~7Lb5S$g64{Fgsv698-Q(cuy z-!m4Qy~XCq1VV6#yK?0(7&*BQC;S5CvfoQ?9*GjXTC*pX0*ziosHigw!a(0{8+;oG z(|rlAT%m92N8U%YahvyXs6*c>%+Z5A{>v7Cv#a+7HOYdx6Uu8tgWq>`_l^kI0aaF{ zVJ#izCL%8QBz7zK#?xVZC4ctLM~5GGe77jY+#>aGv?d5o-(}t}cSMdSbgX<65WeBO z6UY=#h^3d|R=+FaJQH5Ymn)be>NWC*_NOPNJs9d78B2*dOp!g+%zFE1^u+(<;XNduRj@nK7QRYlsTv_R~AF*PWA4i;deI} zG2-bnu(aQaxD096<9A*zpBs%9OJ+Jq#U_A`VxiezV7`ej0)gXZnoKd5{1ohcFgGDU zSN4L1cucu;RN}t+zQI>HA?q|`NXB7~0UXT@^#6z*%^`nz9--h7if-eVkx+laK&qZ! z5M_AZH1x0r-^zGOc-t7cqhiYI*Yt+;Wfs%4sEYAG&^Y-(bMKq7)eQ%lV$xIwM?FT> zT|?|eqq$B)+mUWR_o8MhtZc32p^<&6%X`e5?M9`Q?qFq;tILsrYRpqhfOr~FAMaxw zSf6t+Ng+TZIlEkTaU6Th$adGr!0Wa8uHNT&2i2>IrKJ^(6U_;`A~{NINuBFjGQU%J zp2RtOe6>E%E?Su8uB6y5W^|sc_%6!#;?-&Yw79E!vfoj7MDAGJ`DN6*p5dF6dLM`_i@He6woKh#2Rf+B;UTrVWIVcsA@<>)ZAT;V#(7A~ z;%3N;$KEam1k#RqDX@9kX!o+Ny&Au_D}ZEiA3R-LK0a z?(q=3OiM4Ze2C^+e0lCaoWF1ITmR&PH%GdPocDN10Mg2mPH3u_lWzhioApxROHt?*p%19P<2po#}&HRFsErJCc{(D~loUP7Xm;@dSX0act~; z@YALBRyOeR-h2BGHTZ}XhN+lLyJQV-`*eutz|>Tjg<-xd{*)*W<6xCv8uemm^@dbipx*z z(fXNUE}K4_EteAe@kFjwYwm7y<1EqNU1+sIYo+qX*_2aBgGH*z-71g*?oEoT$FMaCymNRvOna&c412Nz`?p^?q6Z^SLx= zA#jsnh6kC9E1g0JHe~$pQX0FZB|zf-YL$39)=|Xg@gWes!%Iy3jksA~p{bovz^a_T zg$U{9f$u+`FY+)DWcnF40NMd7-gxi%a*qU%!2WGc2coR{bN=t3JZu*Nm+cp^tN;KN zz*xqYj1lIEfs8JaeDvb+W?u~ND{akRZ4PHqi0$N7NVV=EM=0;a1V$NkY$w&xTVAA4zLex7_N>^+}2?@YlB8jc7#!ELv#utlD33Y-mK zighXha*^JPkIc6Ob4gm1vZL0ZTyCoK=(q3Y*1_+jn=iPmh%<}~Sf4?tg@8)W0Kbm( z#*gNmu0z`blPdpG25J|+O{#ht@Wb=}elBi$fvE1Y4EX`x*Y}-S5>}qh~=NiWrb|7z(%ji%e zw5&@pGtN)Z%Zzub(u2<n-+Qfrp_cRao# z!Bgq|6KC#|22~!kt&@HxALA!ytF(uWryAaHUp{MFRvDYxMjwPUuFAKxldGYTEsjMGoI;1}$G{6^@B$qyiRbJPm29f75 zFb)-B&TppLnZ8?4V#)Gpgi!y$$jSW0;&uCs*?Ef1C9t_`lYf#3yC`)2r2%=i+zJBD zGFtegneO;~_Pt<`_CFKvF?ZDLeP16}X0`1`PEqpT5kFHtsr_B8|n^^Rf|C*Bsz#F$u zY8Q88foQ}14a8#X{esaVdvtfLJL4dKK_6dAxbzfWgr-udRX|$}Zz~wPHn&QLuoiUc z%I!(3+(WteSLHXq!)njC*4@jBfp{@0ynk@4mc_f^T+`=75dz zwp>kJDt}gMyqWL$gol~Qtropp7Ex5=qwMuNrtE#IDG!@#v2%hybbqNTm>7P|ivHQ` z!2U?3|8CXKOfykcd|Y5-qNe(3(g2;;EzawpAz=^R+K{--;y~}O%5tI`39Z3nqMEFo z_OE${c&OVGB*A|F@bV5!K2CXUHF`&{{mLe-LP_3->*de{xx3#Q8B$Tj{Y)8Y;_ZJJ zS>s42=(hV$=Jt%tN-q_jY1Su#3jejHay{X5Vgk$ zO7fHE3;F ztV`eQ#pX!1R>7-`HESw;r|+D~>kiLr&XTX^MG~yk@*nKfhx=5fU(2 zd2UnY6aASJ$5}(~?dqc>UfiF)Pi%b@a=o&*Or=te;AY z4(&+w?XIiF3@}}iGsirb&g#Rx21+Lud;if7hT5nTINlnk4S)K(Uo5sbs)?B`>!07V zE9R2#(sIonc)Xr+vW{@ECPtv%3r6Ju`KDPbV!+gW3d|G@PxEB#>5L`s{CuLC@jFuzW?0AecnR*XudAFh0zGk8V?oallb3OOXqPato=r-0KCY6 znb$y(5f_)yke!tNjagp?>Aa8G1hsO}ecb`KW81m~;}U7E(!M)C>8-uY$sd#|yv(Q0 zn55&i?*a*f7x#6n<;JgkCnn~vZu!f4=&Jm;vjbEozfW0tt=NWznw9~>sbp^h4hd`F$`j=2BT#>{JMb5Pt{&dbC^kGsh# ztzCurp?@Y;pV+K<24bT%GyqmIt48Lf<|ze25euvimb)(V2_v*IXgnJN2%NhDdT2!o zjEh;ZV6sZX1V%&o2>+NLf(|m*a$m6gFfDdtA`Sk8STx|3?0 zWTyCW>3e8}|Mhnf4u?C#+L6X(bHZI`ftJpNb!A`r;I#tR&ozs6ACr<#o&>(+<9)4H zo=zf=X^Y!crTK=b_COwVMlsdA#H)F0IVqRs^XuSkyr-d+x?#^m74O0!o4!lvaCMBK z0@=(*XlV{J`47*xirJ%F11{5_F%LsF2S&)!u@@3Y2o{o_Vv4O70&CZJF!f7KwWdnU zHT6qO67I1M3cga{P2uf+imj4|jf2;$zQw1G0`_J?E8bs^2mTm5q`uFMdBlR?#erlm z3WYu^=0{-zG38-P*nnA3c_sTovERDa>}=ID8oS+Oyy%$kkSU&_bvLi!G-t=RN`-O)T|3LDO?OPxv_*PzE7;SrDbK_J%wV1cYyRrkZ zI)L^%zTV`A3jkbexg_{#G=%fIN99NwcQbV3sI1kr#XB-Q2Ai2s*HzPsz0Ne?k+!tDXDcGHyNYX5PD*mk$%mW#pcxRJVepR@`c7HX`_BU!w=lS|f=K(i#(Y1fP z%3*JKK@tT46VM{&z)R5*rEP2P-ob-7I*q~-sU|;pHca+cZ9ktnE`J?LCpkH}!w#@R zYh)&?n#Zcd=h5Uu7XT0;p);Crn$X`ZEZO4i;by$V820#;VtC-v+R*D?+nRBDc@<4# zldtO9ke;Shguuavy%pCGOMtpq0!aVvZPppVT8htcRqHoa zZ|3;(Yz-Y(35E)f7Uj8HCRaQ8+h&{h)~m6ESbVAvRdz4cziU~s`Mi%-J!5%di~oi|hrL3Gk=8rzz=s*RF|i64WOd=xyK7`<)e}|t#QN7N zR`pLjyqXire$1ozKo9E_|H8oj9+mB-u|>aAVAaVthi)RhC)pbr^Ci;@D(7pHk3KQS zi!IYS#Z<Hdhk-xVdv}nE(?E0bnIf(NQzZMm|DO*{%cv05Y%pA0NQDWMsPSpya0` zuToYVrxsk(_zJB=3Jbet9#1NNNfjII?58mx#Ib>mG#ICTG*cL5HyKc#g*X5bZl9%G zSU9Sa*4@OvEHd%uMzVn3rXwcHJ!k_+2=sz(PwLiNueZ+G+|sI>^F#6^i;LOMDqlTb zm9MhywI(e1tiG1yIk^992`~`zu!eBdT4E7sJ#3O(OmsQ?_z}9OYc$u2mWx3j$;efsD8pEA`kOn#*e>H{H zR9NWz`S(0bm4dFjdI(z*Lr#Csw0q10Z-jQbI5XWzZH@`-2tsg^of1Y@Gbu3}_PnM2 zqWk5_)hv(W#Q|+yEp^7>RslGVzr-Q^36ZAt1eFU{H;47(k8n)<8@9vn%FVI#(w2mv z(^K>OTjGPhF*H7nWC~6azz@iJIKJNeC_u=|*KBt@^Z#ffB+(L&2*p<%dAs^AB3%<+ zw5}ZeE0!j}vz&Zg-_{-6ZQcD&IK((GQ++#NXbrrR`uZ9;9}Zis@|pU$Nx#oW^!eNO z|u@Al2&W_q!1X=%u7th0+-A+4Q!eP5g; zG^QEUH85a&4|aP!+56|nobjtB?(zBKe(@@e>9g%j*QyjUG~N6Q&77(k+Q1@k$nA|i zWy>qlA%LNj?LP;GT*S0bN{9I42S5ac1^zUi>y+afAC4v2RX#l9n+U^C$Ku>@`IL@a z4fvi}KqGK;(ojps=mvYl&^gii`I(A8|E17Cu$kIvNyLp)6o6bm!^3-doS z&xLpX3fMCJSQT-R2bw1t`)WHNK=`YNEs2>YigR(ao6?HRg6&|R>dQ zCt237ZMuC~h7J$C{s~pw-jRikme5ZAeAfSd-C&}Dd);zN+G9)G$iQG!Mj<&ld2F>n zYpnF}o_oI0pCj$n_zlVEt3Rs=G563|KmG>x zPsg30Z4J_^uN%2>$38$IMp6xlT3qFobiyQs87NBbX&0BPtsBAmr5R3^$i_dtAg|e9 z?@F*E94Xfy5#?i@F$TTs-B99aO4Lo?LnNsK9Bj8X04?2A;kU{jP47| z8~0m2^`7+z7*|y(P13l8u<8WUs-3`7ba(WxG8{oZYl9mZB81$Z&Ta*ju4=7ap_tVm4m`otc{!5Otmt((mx6ja=&5LJ%4F79Kzw@v9 zXeZH0GXWWAJ8ffpq@+s(kJEs(P>|#k(l;%AzCjMqu|y3#H^cs&=+^(_MDn+-cdn91 zh&=oNX4a1s7a(OEjMV578KzTwM#uQc8N%E+g@LHqWiAG0PL;obd3mujNO*`BXSc?q zYoF8-lRyj%_XGcQ#gD;?fKy}Md6p0xT%w!jMt20Z5pNJ4WIJSI0RZwqRRzKYp;xI3_Pu<`|DjiYe+Krh(h?;0PP!=sW)0b2CJD zQO_cf+JpekhRJ0Vmf4ujm+d3xdLAf^rA@><6eMEZ;1k%m0x2-WbiH*lza_TF?Oe;A zA%wpXk1l15@=)T)`|GuvXN2@+Pg~`eDW)7SfHvA37sfL zcCqNS6y$rq_eNqzsMg@t!NL6$(Z+(Y-O`q0b}Qa3#Rm8tS@@mor8x^lWu&|{;e!reJ^?~YR=?W3T>iwI!@P zH#6L+ph`FaYfXy0KTRI6b_tZnBqlMdXARtaiiy#`qQllmcSpdT;em zEN7H_XSpO0Y$)hEiqj$9xLM>sn|7zk770?zpb_^OjNTg@VK+@#@DXn2YSlOQRk3-b z8gJU$8i`$~ySk)@{QB1>e-uVTVZqn!&#x_X((V&Dc;mYEwT1xW$@5+!FVbL4gnSMm zf-(k@z*Lz#`jd9Jm(QN_o-gzHih4&N#Q7PI>-E|x->eHJT1$Q+y@?pb zZ^=IqjD$@$rhquzetX*Z|Pg*|MZ~o2|ZaU8u zJbtz9pgB#Gx5^NiVKVeRb9GaqPRxoo5KYzS46Ok-eS3=5m>}2$-(UiYts{b+2pyQL zUOI@3;~+SvA)#rd)uNzcZ%m6?=_`x*T*5tt2N|bK|0^S zR)4%PphP`)47Wd)mXr)smgR^ad+9b}c>Mh4sQGd{S1oRgSU*U^Jn+6cNOQ?0`0>en zJRyh;1cWXuzlv*`i^Z{eVwK0$1!b?hga=EGLkoCIcOU)j%HMtsg$s@2!~2HQ*R@&R-|tR+ zTWY)V9`|b(UzM8HrM}7#&mv-w&iFqxU3FZO@7Eq<5(1J6h_sY+NyF$C5CO>nlG5F5 zAc)f4ohmT}RGO)D3rKeelWrIrd&l?pdH>t<=bq=>=f2K4*L9u5{<@o?^NUB5;x9da zdYVO5(;YMm_4m1kYtL?}jY!$4r{c1F1Y(K>u$K|@p*d zHwTeZd-E+SES)m8IPPlRAN5sxwFTLmSUnFbz~CUSCFery#qL5Ay;!cCwQDmwg#Nkh zVlz1v|GXbjlbu{6p^tr#t*sUaV^6O=R}m(9Z-(fF;~gJ%#0*^_V5qb?Rj$#&t-{;E;moMR$q_eED7emh#o;gj`S9d4KYG1%4q{Vg$lU5g=k)>TJ1y-Pc_Ie=h+2 zSt6R;y0>S3@%XPkN#=3si}#Mmn=qPQGBPPvqPO-63JbNk^ztVYu1ET7W~FkDIz9Iz zciY0AFs69;=;@8pbs9v=cfy~(T>xZj+yF@8)}}o^6KoC0h1Q%6vWd6VF281j;`DW- z6nGVyS7m%J5)T1gHB$s{x~}ZwOK1z9Z!ixSK8^@}p`P;7DE_3Zd>ZtWB0ncbvzIoh zT79BIVo5^*cgOo;P~wY{hu6%VI9Pn8+H$;}(fGJ1?o*N2M;^&kH72~YnAzB3Dz2+qZA&pXcoYl*ZJTJAt}43Awc-)pc4g@WF?13s&N&N(pp z^8BPBrHgrl#^LGko3XvBvUtZab|f7k3k`3tyqsu_;;(lp{>u5>d&2{rqSeyCs-txK zD62&LP?j(j4RIGPYTiW4PM;x2}flg^E{MvIp_IAKF=5 z9BJ(tvA7#1bqRVQw>WmtIF!klKKylI@I%!I>{q(T6T0}rA^&f)?R2zS`9Z?Jw(9BX z=)>DXG*j4dV=jLnaIAi!q2B274SMD!1=H%O?!)O2;*FCwR~C(YX(5rud3SRT5oe|B z>=`aA-DB~HGWBtn`lNpTtJeSq!jaM8s;~wh7{h zltTCbldu(*eZ^j(FILz4*B#+>ciIwzF2mZHc$Om>7+l!}3~w-ISMN4c{j(h-71Efa zYbtc+21zS}BpRRa(9$?YAedAEwAg-4rw`9kqdJ5@V8afsz8>d3(s+O_y;MPJDj~Jm z$;r*D5!w|p!?X$K<%OEMRRtf^=IKe%4PV&dQV24=sehlg0{ZOfq-|wQ;#S7%%KiZm zN90dPR0*Bn;-u%#vR`5bms0fI`;IOxC% zEH?Rx%7vg_;0V$eLGOrK$?)=M)<0I-A$RK^d8J)csZ>fFC}iGH4Uu>@ylNP3oY*{R zX?vFFyE{yn4iyCmHQS_RY(D=jJm#ZwM_J%KuD}xqzG4nNw29kzYbm94G)@IF@Boc@ zX@JZZUfJ9$0NEO%+-j4XXY~b=>c8|JwOATdxf?$CM?4`*{&~NoN$fxu+?RK40LR@X#v`yLby8E`6LWo4=9U^m`5P1w+_HPVFvfrFTT`dhu-Wo~kbQBIhJjoO z&OrNgg>kZ5U^Z(hR1|airl`_Hpo2~6pmcw&^RuV^w1mG4d}M~ToIRUGm_}KFV!LdB zS&pqnTM>n($*!OsObo=GcN`rqDo7HmuM9|fg09#!n(Qt6WoU+Hm5NyreC_LGS(AXc zW*(#kG4=ICTT|CDgC=CXW)x_(oSu z1}PPEjUz~rGDYx<1I9wr&nh_HjXI3%FcAY|h;-@UMC}<2HIJx<=@9Sm@qWIr<~$=+ z?UAntm|6zMzg*Y93@f1AN#(t7^z37nJxR^pQOgt4#(U@T*}1mFiZvMc8ub>=x&c7O zO7u`JL(O5d^psdnNFs=6l4Hg=vyL)NcQ$3&e>335dO*Rx46TgCWa(}mrr;`0BpTP` z!4$RS;U;D>ji zlm+i_ekU;Y;x>*~LWlCD!H;o8%96Xf_#;5w?F?Bk6zm?G`$|87)v-zh`8ixA3}i5EMXo0=qH;3k7b%Z z$_V3C;%|bzJv4sRgEgn5GY^D-b2ZS;0c;aDl7x_UyjtWJcBxfC6K)x*JJrgRHVjYf z=fWg@5^kWqu@`7AJ!B80u76W-dYGkdlc^K!wzj|yMdwct{pU#Is|;A0c#hr#g;YNT z-6iOExA$C}t?t>3lCw0E{oV|aAX1q*%Bj#M~q{urWA`;L^} zCV&M-I&39H7wDM-r~XKPjZA%dQ})5b_4)8wROp(!vba6ekMToUwXU=B-e|TtoI6157dbV-7>}KWV!z^c1O|_}{po8*wV${l#a~MOwFIOvoP6+*+03PV%hjAKID@sqIU?t@yX(mjr5@R;l-Z~$yP>#7bzy-I`q7}5zfVZPw z2VdierMMZycumbtuX%}+Qw_iF9hW*g%6nml6&BeK)V6S5Bzk0c=?wp~<&me^WpH&oRmx^+zR9aRK`W{XuGi8r(J zP|yhg&G+$ElJHk&Q9%pg^>{x7A0$$6b^Uap|6A87Tn#Dd>nN?fG{1JLs%3n;(w;5y zg@M}UaIGN%9ofhD5!Xmuq_9rDh)Dl2Sw3>!>fK?T=fWm52VN=+@Z>ox{UWBIP?Y#c zx~2|8yKxfc(Rt?Ia@tejyM3rQJLXX|av9*J?NVENNZ4TKs&f>BLByZ-r8&fZ9Lgjh zR7|JAsa!+IBQojO?`ARZQ2JYRur3Wu;L9FzVThB|`y$=^R!v=LE_P=de6g#_XGE9i zxY2I*l``L}n1f07mHr7X={FVx_WVXs|8n!ms#=r)}}SNe<0cG_Lc2dwk(WH9U}VLYyfJ;u}365G(=wd|s7y$E5dx*KgrL z`Ud~MpS0;M7}SJ%%GlGt4QKLSb^xtjUB9!>d-o$<=_(){=QD8n21bvkLt*_<-_Wze z+SqTKUjj%6p(FKyE$ctMPTgJZRKRTPeV_1vo|xwxK{*)Zd{w5-nUhCNhwURZ3|$(0 zg&nkd>OU{&i5hhzE70j0IXz{nsQBJcCXn!c@V`^)3c%Yk-8G0*1SrFVH$*&kj2ln* z>|Y%d3RZ^tSDd@sb=!O0hn9xiSvZ|hOH)va!)up`f=A$kXWI#`*kB`wEp#hyRZgSe||BT5sHGp&cXTCuMdKv>eu8%MI$O?{4gEJus zDlzH1=%EC_p_HBrcZ}EC$54^OCCoJvH?c#`dvt0;Z+3c-o$@Hj#SR~tcmP?`-l<(Y zfP0z_n9VJg(^mJVa#!^HRe6A(4ZUlOkW`;e=guAf@;BpzGIvDvPrrQ(s`6elV-a!! zeOm+|@SC~7`&P?2#X@59u0@p1JPVoM3`&+Q1BMEE<$0Xq34znbPi^%=bgsUS#*BTt zO|qY*Ky)wZJ>AB-yYDGm_;_}J?@cYedemjVye&+vpXcFW`J)(duf6J7p%!!ap<*z0 z@nU?iYP7t&Th$2d=;&Y;#K3+C_^Q~){As^En68w#<9PS)Z^5nCZ+3@h8Rn^O4V`da zU{63Resn;Rd>q6XV}AfJT9rj!*e1vf>( zV$w8C>F4j_^CJc2dEH-hR`%W`ozTdiIm!e-zfhx3_%4u$`!6d*;5Qwt zP90YcGW@~eiO&wy;@lq(If7Qm#_G0CLaMq{54!rPkw2SBkZV^YxLC4EE5Hc;pJo;aW;HY^sU2} zhMe_;5Ii6Ds2%oh6t4Qmc{`ck+=o>Xz6En~;^cQZ2fk}4!%pg^ESo4v&M0SgV2to= zg}+}$y@qI|b!10|%f(-vD0=7-{Er=b_~36h7_8)w1K)w}L@)KK{Xa##A`75EIJ^E8 zw%oHi z#L_+C2ePNit<(2I5-SZif`J@{bz2<9wA;{0N2~t75flSZ#^)*DsBxSzCcwS7lyKAe z8CsU2gi+#!lPSi8nXt*jM{4Qe1e6TI=30VLdl%B8U%sl*0j!_QTK~=Y&o2gF72!OY zi9hq*Kf%N`UQnmc^(H;^<16>&P-!o8VDR;L#pwj?4MF!29kA+lfkuOwh)9 zr&+p|cj(sNImGU9h>@P>RjGxVsUA0kx2QQECIG`5;3AW4&zHeTM+zbkiJ|~WJ|Xvp zF4o<7wa|Qz0NCf8qh7|Dny_{)^!(!S2L%ecFpxDK-5p(ul^M6H6_lfuh3uz+8^VKj z993X>`ap+S-d95o?;)X^MgcugkNE78czoL+CC0<)bFbw_KoDi!8jOT||4mtS1_mQE@o_5#DCSG2yQ# z^%)E=LpHxGS(D+A8>E0Wg^*k9CL##}0+CVH8X2ffVD$pp~6`@1N{ zXc+&U&tHn{+je&N==Z~V!YRmq9fRhb=%Sa?!)5N3o^rEO(8&X3mT(re4rAhj%w9!j z4VF@bf|{*1xqj-;HZmW?Vh>yGCMj^ue_YT0*!EGBV75Apo@Dq9(XYi6W%Y4}WBSgb zwv)8eZ#8?+y+0;@_Mi^IYTNUUN4%*Hal{&^Y#qJkAMn)LZxx>(KkOY!56=AASl>9u zfTJ4XzE%VnObP3?1z20&51q~;lGK~hVZ7nSX6!Vb?J((?E`=gKgQT)PRy6YE0hd~Q zcyYYV`&Yq;??^%Db#(U@BJ~X{=G;L#NOcMBXH@^J^N?h3+*psJc&?tZ zYCK|$=$Lc4c;p&ailcU{6YXZ}0%YVODIesuf}J?c;r(xgc6Qct#yoy@SUA&@d}{0X zh|YHkdn4p+_Q-ZrjfM5Nl8Wut+#1Y;6>|4x1s`ws04&Cm*bPrH&JSjdYUWTusu)2k|bg5kbN)ts&#3v*pG9G z0J8&6Cwk%v;C@YR#aY`2I9gwxz|SEO3&Kt$-Vhbnu`JdS=GdG|8$vpVkM4#mj8@Np`-|gL01b-e7B`&^0_E< zr+ZgVZ{x^ny??>Iyv}wFfB)#SfH#Z*KM?ZN%c%f-bAEZZJuZh=L94@;0~IHLQJn&V ziuO$Y3B8rb91b_XVPn+{)7|oMqBJW|Ws76ZC-KN$U-c2-<9zsegWgug7 z{rdptzlG~D=zxmi+;`4GEJF0GC5h4JI+{Nxl=A-c@iS^nu%dKwtvryUj4`d z$Ig$^eK<@GwTzo3|Pp8bY#s#70ai^J~;|DWqwsV3-mlHo)6Tf9FY?8++ zlyz(&qscy`3Oae(^$aYKZ{YAQjoVx>XxlKu3~5r^p$g@-m~KLU)aJFD_iTLWZpiv( zzm?N|>#Nl829KHo1(~18j!95^?#ni*wGaZlgU&1^-kSs_?mZ+K9%!{9bjsw&)etP<%#q%Z< zm9>21gVOguQxGURGfyAJ{I&1tbo;ipxPR!#9L0IRCpvv7Dx0Th7b_$VHhDT5`}F_IQ%0yYjyeD`;oPgk!GDlWp0-CI2O z+qiGkC(>P)a;^@)DwICCVH5jTi8pJ1pL4z;^La8A#d$Jy%9X`obrys6_nv+t)|I!c zx$(BE#;gesWMaGG-opN|KWH$9cVG2Td?*tiw}|w}c=DdVJw9-?XAWJ-9mu>itaxM&;67;rpuN0q`+Cv=KpR# z%7~Q9yNIW2)JC|T8sZx}OWt)qK|#ayUtBlt7jnGf`?v=blXp5|?$OV6bFXT4c2;b4 zAnZ$R-DTR;+yZ&Y=j@N1`#@G8OFfGctCt?UnIqNzA@I1+fQTr(Q8Wb0zX@Uc#C_S# z7`QugAZ^8`pu=oBFT|VmLaG{=Z|Sa4w;+3I2%k=>eRwyD+){bMf&Q~((XQ)R+%D%R zRaX&S{Tp&rokdVq7Mw{ol_+e2b`GU{&R4k4q>vBSl9CU1QdkW)XP0N!9!cz{#eZ;* zuY96PuS&W82cNy(;56vccLm2P8Xp+&rUQxuuHW8>GSm@=fSH^F4zv5Xr$|8EO+$ky za;!udCDjQ<{qg8p#1=Gi=UwBp$8k0=J-KEOmjq2|ZlZox*rGBXzZG2hWky&tY^-WW zS(QTNbNpvf$`2iS2PEaxf`zXhe|q~5^JnLmEc~3;L@d@X)i~=};Xh3Gb?#cQUKRsm zL#4nIE zK7Ec(W#R4%B!q0NaaZZ6@Xg7$RX^4RGgqZ%!o zlKr+`47!cft@bo01Kf>41nisR(tD(L(0OfznvG$u{BHjjd-~C^z8c%s=O;e!=)=cx zR`ta{MT?lnr=`~V^aI#f^gC*_ljBhU7wgJ_X*4?0`T*ITwKRCmywUJGOe?I)T4kUm z)41yD-F@nLJ#mk{qJgllLG@ko_5T>#Z7Y|Z4_dUr>e<4;pnN>9B6uMHoDB0`7@hW3()(+C>!z4g~W+j$8 zYvPBt&QiBci?EZ$@5m%5$`Cc7&RoGD9t2N0- zpg?S`d~pE>yegw`(|OZPb=OLqZsR-IW8+=?IrUgIw)Y{}@z7iwmOpB&3^;ntik00z zeoo@{Q(~*UB-##E*Zz6+xNv{?6<--V&FZ`{N`xSp@EDxD7N~Gv zEdl|ibjp6l;KXR~^7j7%zY@j29}|}o&#($cau}c?dRbPT=>qLXYSPg2n!4+vbb9D< zdSp>rji_YKF=XI{PbsiT!@>1X<&n*ZBgUm(eVl@FW$`M+m$5E`lah8sli|hK#c+{r zvXAa@nZ>%rglv&-=6KLq%hr5LS<8KRO~u(({p^f4`A)nMyQ!v&Hm|czVZ{SuQm4R~ zA)RvJvvMR_HrLVYSWi3rY-l?lYHgx@p#EN3aE>{Cy(NH8U{H5GI!j~uq%EimWydQg z_^pcWB4dB}UzJ%d@~jM;`sI(W6xVx5RIMVQCag?ht&Jftu|xUFFgPiiGbQl{BCBKj z7{=GY04(?T-R|J%up#B5bRYEj@exF;K74&(xTJq zC#*U76{^UZPA6>NO!w)x;Cj#oA4u%!`9vU*m99P+$KrAdFXn~P!=IvyVF+zsn1~b=iXDs$brllf9vtYZeHaD&nvD zN;o=>O53c{wV-|VBe#hKt`J-&CR7KbRm@_GTI%^*Ier-t_GvPJ*VolJ-iM^Y*)?1` zLH9w1TeIP|Az488li0ftqRY4ydzZMeNhE1$!>2@FrFUA-2MbVzp5t6U zS%&cs^agVC^R);!UGRL?7X8te?|qNZ(Wf5u5(w5luwTaL^@*>DfWYiTf=slbBsOcrG1!sgawZ$MeViaO1nZhpaU3=0$JKWVEYp2g@e#JV-_3foc%*cdS@KtKn6(q#W3PEf>?MQ# z3)P$hXSJdL9>^-)Ny48tuqJaJrL??ITF)UxvJk;=><{Zut8s_=MpNOZkMbvckK>tB zE97IkFZGN4;I%9ZC;2DVknVk_+Om8t z+qxYVkY?1qC&y9@H@`Wo3mW|szW4$Jk1c|V!$RABy-sed*xdBJz^UzLvmoW2VaMN3 zw>mLKSwym7s2AT#y$GJS3*#tBxDP-l3$y{y$yOTJ;&O!;i;%Ez&Pb6=UikTs7^K|n zB39SyTET0#sqO^ce#b`VCQi+^!RaA<*z}o7vKkV8cXHFh>!}k69mWd2p2L)mcU?26 zcyeeidl(lFEaa>dFuoA+=q5DqDk0?w&~=!cR(%f%;k%1^<%JA;qF^v%^c#At^Z=M9 zuvI!!J@}<*2M&M7rkA(zy@O9lAy!JUFnX~|7}OX~Aj=$!=bZEWM^*0&)0J@kKKFww zcv~595SJ~1(A>YP9xKrS?VbjFI6y*7qu^C)Wg)QeZx6w-7F`|{JDx(bd{2EOX3bKF zs}Ow5U{0|Pqa&r->N=8^%LYLL^Z`^m ztFt{nbMnw+7bGsB{`|izj1OiZ1>ggOV{cRR2+`*TtTq=U)e-|O48Mwll$!{&r6#`L z!J-%Pn^19M5Yi`({mdYNf@nol>X_=y^B>xNJT%<>@W_TGdg$8-z(GtvX{V0H->!*| zX)X-+xD^(L48r-FCF2@LI7p@m>87muhEVJVf3J`=XcD7;Cd_ha^g)LIrXla13yPj- zMxBe(1kGa1GXmc4kjgil*hYNJd4&ne6}Gr+i6eQAE+pH@rIb!H%5Xu-`1IaCLVI#EY{+9f}{)C?6a zjx^54*{dU9ltlOl7+%5}E}7t1ssaV`5_!)Gq%*8SHjQRyv0O;Z#geY$Mm{6f0Ikh{ zZU!eD)`;HZto+ek^0vvQhbnR3y!{2n4ruFFsNJo7BGLAJ*U0UY*3zpTE{-GV29wvD zs59L^srNZz8=IEkiy7jlA}*chFBW#Ma|mdr>h1 z1%4}+?i1IrZgfR89*c|@lZK+Z*iQ9W-vTAx61~o|>%2!_nZ0*CvpODL{R%l|o{d9; z*XT|Q+XFM}tlBx>!UQL(AfbiwSJzy~ox$aq*tQL4PzL8L${Kc{D6G<)~^t5GR2X|nt z0ms1;YrGoBgmt$?$`d(D9@ZFjtQy~j~(=@%%m^ZN6a}0xVhf1llPz)-9j+pZSdHE6baT?~% z%o}U;J36wNA&MIn`hlu0p_L=6qGnY=(u<>-R>*GtEwqz#x!0elw*(xLspkX~w)wrVP za8-E`?0J9PKI%X4PXGf40!RT}+-wvxJ47s_EN&wsBbDE+prV)yC}#Tf{b zct=uP;;gWdv-L0B5?U;Z+6gEs|HDAliP5mQ@>+L1$HlQd*?-Bs7UHXPs+ug(~*r-0`YP!1?7|rdHt0L+-Ogn5ooXNKZP&=;MR%y5eg+aVt=Ug3MU@PWvc=(Y<$;p%0GdyoY736hXp zPw1(xVmI}mDOO>7e|(Q>nnal_l5-k#zFZ7FvK&*TQ@R36dgFb}hTjiheZXo(_3B$^ z^iU;R_XP|5$CCCrn{-Fgy{|2is{`_;?_HA1)Em;n@72sg+RqUVrw4J~P!osK&0ao* zor|=ZiRt0%!U2cS7Bz_UAm_iGDz{(5t%bS|V}a)S4+spZQkkp(rA@mt?Er$K;yfN@ z2__n4*e-MN5^$FmArGuCw_YRCChjr^$Kz3}Zw82nlDejcpLXmzgh=%;81R)$bKYr~ zKl!>1W}^dX`OdY>)TTErMZr3Fdqn#YO@9yScsiBud2~r`669)yx8*X)ho|G%KLs6_ zeQ#9JZFzkGMUVPIWI_JKKGm{b|6NkXtQ1TD^Jw2mYNA~w(97z}xoUU?1dAKEC|@-$ zU-@iJ{J1A1&(#p`wdwKn|#7m;R@&Zo~$ylBk^o7ZX_K?n%BVCppK@aW4d^o=Ai~ zI}}W>FDNrxljBvGuAw)Qu>S_0RHi`QsI2^u_Tk?|7J|I%LNk5C&AzCxoyNn~I0fIc z%Nk$RSnu_~UMt?n`;w*)1>!f^wAu@0O!HnaTfEe`-nyuw?oFy#?|t^4l4u#T8h{*x zcWU8AOw^8%U+We>nT6r(_-^Bq0JeP#bH})VJI-n}SoT}v00AqV&Z|FgT$ElZa4?{C z_2K|EJFJ#JQ%c`|Dt^+tRs*dvGn&mJ-RDFCUsr{!naKg&omS;w3MHCCU#ricMtz%9 zl*Db+G}Mt+gCJ{dB=SI7W_M5ky)!hYKpkxrutKW}@!y*EyIvdb?BNW%;LuGEJA-YJ zpM6U-4GI3_)g{tjMy!PLaN^IBrct~pwtGui%=EgL=SeT0pefZlO^6O+xtQlFNzqzK z7T9z76R{d(D#CK4TptC~CAt{gB0ge5cp~%?fMVexLgqAhw#JOrJ}xgn?~WD26rD03k+sU!gGcmO>T06Jwlt2VKl?=da+G%0i9*hT6rExP@p> zU|qL86ft&0rOgCeW1(cYMO2D5vJ*4mfor0nou&Dd`qOK&+^ChzfQc5i3ALlL5+*i3 zlUmebX=q}hT;;iNo_&iglW6=(l2SQUt2r8G3B(}N{V7&yt0 z$Nvx;Wyd{yZ)$Ojm8>L#7SVcG*K%>4>Sez7U)x+t!PM5mqI?4px?my^u>F_-X_7Z7s|!Sf8^Qd-nM3c2Ro zPA0pv*%0|k)HXg11HvVT7+9#iHM*P*Ux?IG)=$4fnnjy9KRk%6olf`6&;B63#kx1V z=p@Q&8T_T;BNZ?4GwkAa$F%ghmONCj4S$Ka2iEeX{$0$PAawFF#|F|S;B|@gxiUi( z;5_?oSti(XoONONMNy~T_k)FJyu53=8DVP;-sZPS7I);8aP<~HH`{GX%m|cxk!(4$ zh&?lb=wdIR*v@fy__>d1T%hdmaDS!A5TD7LyE5KsBbV%lFGOFAe2hZ&u1WW4WlFj! zvHYT4eSQb}9(Vj1_A`00yl}+AKaR-b(Gb=Ih2Y0a=AZr0rH6wp7$b+X&dx03y2UKN zNfFT}TJl=hInnthy&Yujj9i9`Zd52ntVGtY=+J|R>nYxbgn`&Yw-4n_AGtuqh8@yE zGW?|e&q;yxvfy;=5jBbP@h;^NUGga9aT=UFe(Lzf5-l#n&}EGzf^(g7u>Rua4pqleJTZ9bxyX?5m)M|^DO z=`4Wn757Ge3}() z&cQ&8a;+&5Q+FhQ+s^?glc-hpV&8lt_Sn^II?yDAkRSue8V5@e>!U0|S2_O{gR>;#-crGBEpJA2 z>DZG!+Zl)(9dhCNQ+pDE6(>{*XXD65596nGpIU_2ELn6!s(*+}HAW2?_eT?rYq>no zEN57wlx7F?ti(n{E3E7}mtW8z%tSsR1iY-bs+`eC;XX{LJNX4}?9p^Is1|lCCGT)O zeeiyKG!au1k-3QeDgf;a-~E!?o~L_wzl@Fv0J8dRd}mMV*wG@LDifqKLjcOJE9k-b zkPu(B03bADdiQK8k_AcPizKr-?Pn2}rRA^0tUnqFDndkLRYa&gFpm}2b4x-rM-bEm1K^wiP)dT1Mt4pgWO4N7i^bjak+c54)G#wl zobO&B+edrf?_$`C?|xnQE5^GdUR{c)9iSD?=4UF(90n%sXW0YecnTD9IiE4V5X7@t zsE-G$NM}-Qckq%KxnNUiNy&|0Dfj&9WP53n8+vX*Ckz44?6sf0I)P(_~5-TTvNtqm|=BU+bk&9dyf zms>T#eE6qGV~r@DXyelhjr`Nfl?x3kBecC?$Da_riGKkBXW#vm{r7PA@Mh?^p%5Bm z>Tq4*X)(p_dKd4}%{0PtDJx{%^%H_@p=MDVRTzYKIyd`N-(aC$5wub8j?&uVDV0t` zy$(1NKb|>T)B|=B=X|_on=2{Jm3MDIgim-b@v6tCz^bC%cU_8(c5zIDBt-n_gCMOz zOSur0OTDAu%twTZh-H$&zmsv#sHAP`(55$+<5J)k9adn&!|*SsY&da@Oc)v`!Cc1N zT;K+D^12cpi_bk^f%JE3*dE@#EZo#e9TF<*&jJAU*%RSUOc?%0dHmjaql-Edgl1T^ zUuv02e(tn9;*yEw3Be-y3^_kGQT#fyEe1wWC(7@@{T_&_wnYPG*g|*oo-ZDbM|te@ zD0OAMv@RO)CfGhER3+KRGwG*IY=^N}jW-$I6)tIdEep-oxy7v$w2$E5rOB}U^X4u8ul4B_+y?1RVDe-ZSpvl6{&g5T zZ)I0&wXD#faC4;>9tqepPY<=qtbh>-DVufAr^L}7UNvuV=&sGDz&dVxht#^|1Bbpo|v_((G%}mVaUDcFF3n;v$p|7rkPMZMM zCMhO{>^D_X4=EH8Tt#886|Of0cO5$~o1=h5T;@P6MKJGS=CT$MDKAJ15Ape%t0|lc zC`CcksAweCot=1;_x<{yecCGMh$Cd}sCVxH{Z-!d@9_2Avr%cT8YW>P0dbN+bAjys)a3`8ORT834RjTduM#Yj|^}kjiz>M&t z7+r4uxYW-uPk2Wt89u2{mtkdjiSIy)J<~ccqSrGh{Tcgt_J;xJz6quGs zlP>(@=+(8I47jS%g_%6*+0I4AM&)56cYlEWay$b!{6_4;uGdvTx)z`1$^sYuY zd392M`UMq`1N0UjSV4(cb?U8uzybOOhydH!JlgI{VfUYA4A^$++}cC-7JbH%!sgQ7 zfsHY*qwpKm9?P;(j>lfb9KwFa#dmgkl)NmXc}(R+S%{d2^8@j+rL$F6n`1kz*cQwm zuj*_bzfIhj@Cwb!T69|Op*KBL@aca#BA`XbLUJQvtZepsz&Gx6KyNP^aN1&ysdBsS zD~S{0#P@hb$30IKGov2Od{&A8aAH0Nhjl+~^mAO-W+dU72T+sL)3f}Au0K{M8FFyXqvKu!P3TmJ-YD6v4@4arz)Wef&H-3fNF9}4fVBKueBRJx?F^~F_(Q?Z zp2Gq^2;5pE9chMt?8CwCIzkaVN2&6F-Kr{w-EVmw8SQ;i^VR&}TuIE|FP?=tRmr3J z>I4Kh5zppqqZA>=?NJDBu=QHkdur?!_lw4wSHuwpZvl-UmBpc<_{HEse)m;u1})^9 zV`1C(fkl@F|`z) zjm^F5Y!F-ltzv<}Dj~)<6Mm<4JZ*UUD{x=jmXzMnpY+m3#lgy#;HtZ>q2c{g9H1KM zdt*#eBJ*T{`1W_!VyxNu45 z5`ynQSh<}d49?%(=9^0WpXrCPRgk_ox}erKq~EA>oq zzaro^DdB@6yh#uMabwd-D&TxHIDg3C%5f9>vA6K^LD*Jn9SV9`sr>w^XcV$%bHEx8 z6$2937;N-)hBrwc4|I?&2?^fOH zT+EiMWmYw6&|Bdr>`CvHO&{D>E(Q>feG8xbI2>%1`xod5Bm%-O0>I#^ z&Y9ivgwX*jM2G1#X?BzLL!0l*<+VT&kWA4G2{#qNWJ?s2EuC~6iKGj&IX>H(n35`) z$bLn&GD{>?Z5vN}W3DorEIS|Nz5{}I$rJ+l9%mvj8*fVb6hgLCN=or z{$~5Ku8X^$I%@tENW-sHi2-$HoAu|YSAU?UI-MuzyvJ13ya$#r*!fMIM!*?#b1I2{ zog07$J5()s9Z1V!q9~-Xsq&?Skohzs$Y<55@7nBInJT z$87%n1pI7WozY$E77L@;?)72`2tx?!ycV9^Uc!4dnG0T7^U=j~W#==;z4M*%f%>rX zZrbw`2=?dAbyjC1&JSazZaLW@TjuP2{pV0P?Kn zBJF{5W*wXbkpjJ+P!j$EtA{_(>d6Drcw-k>TgUVBU0YHC3 za;VGzc7`?8+Vb z@$%xL#*PKiR_Ko-bopOM?ECKABq$@yY2{@a`%eG_9a1<23OB)jfEi|z@`00=taYlLE@5;Jq<t#efB8l51MK0D)g<^-}A*kvh{3T`%o!Yy@1o z)W4CI9pC#u0Lwr$zpBP|8wqdD+%+TmqtT^$7J9`*x2#is2v`1oAoD> zBMftt<6XrgDt^xVlE{cU;NV~cNXbhPJROD#7Vm(}T{mRmp5b#A11pln=&&$x?!w)@VZ+!Q4 zd-{nd?Z5%;-<-%%^g$K?Ar;jJiwFkb3zN!z|Fj`G{72P6X92IcZ6d-B|5gq-;#jrv z6?)y>FQLFvgjft$g;>^#e)amQjQEN0{!~`{Pkn70Snfs`up||K1Oo^VN;fbNKmf3T zP=|3fAkMqD`Z@+h$6>@uH^LB5Zx?pjMP~ z_x`8xqzg>t0WOVwYO}88crrnjam!Q`DvWT8_$q&#m*Tw$9?U^L59toU`_forQi-Nb3HF;@9jLZF1epg^D$01Qe?mE|lUkbwa6 zcNE{Fws%5j`ab{6bN1a=Ua`CGxXZ2nXQGJ6l8-a}qOuSI@Ln{N*9ZiKfx-0r^gOB# zI6<7c;KTtJ;I|n(fTI&x3K*Y57$M;}7aF0!DTfpQ! z$874sn{E8o12#6DBY=(ot92hefLlE+o^^!|7DMLW;~bb~VtF2A2TXw-LKPRR;F04Z z?6|M#hrRwepj%p6vX4If)ZYEayY|c9{M!Eb_Mhzf^&3foA?>X+RZa;?uMz?!1bPU8 zQUK6H;FnpK5ZDO>>U>|At&bczY~OzN1$*x4XY8K4@3j*$^fx^{)tod$75o2CSBLWt zb9Zw%d?2DA3Rt1cAqw&%4XH11bH-mk+&6<=1OqU1!c;J1q1WLEOk;rNdqpbzZqVjdc)mDtZZvhx};>-sS7Xq|IA_G{t zvdFuG0H{)P&-ng5Hhx4#0*}c?z{vwPb>DHDkm0~Fj0P5Qi*Y`3=3m7?t<5#n9Outc7Bwv@KO%k)?^3b+O$g2D^@ke4R8v82(VO;5l7^qF|MS+Ld--##vKzjl^h*Df~3okjp zG!SrusKQvH!!vo$3GQABUs+ssW`F(EIqk2#Bm;lfWXb=ARj=qMrnp_8D1PZbFc6?? zXt(shGXDUYuI)bvwZ?hdY)}xPbRz))`uCa;--YW|xw>rQG8{Pep>W!JU)kjChi&{D zMl0lzE^9c=o68{ivrU7WiTa7|53YCGYAJ-FvSr!kHmK4QO+Ho$uyzAtTnYc2?= z$`(U5=_Go2DU=qSNsy6>HdF&po3K4MZ* zKMPyegj6Gw+8Db2#=IA83XFqC<$)jg7`!}c&g-mtS2R#3bdQ37EF6HV8*tPIgeMyc z7#9Ta(mi^YMx8T3}hfi$nFS6(_ z%l_3xt@yi9KZR|I-y;9=(*QsqsQ(YD>VFo%U>mpk&VzGU*%0OX$U?wZgS}%2j|q_p zFIFW8s7m0uaYiQy$e^h$uZ#JY5ka;ED2Sgf0l?DpRFZ__LhzxV9N>XA?{cJrbSmO- z;*%ctQFC7BSaAU%!Xyg^V8*w&Z~#pZqj}gdUx1jOo3|r}4%?o&J$8NRhAm!QwB?m$ z*90I~tt~xH`BOrmgg`$+ptJ$dkF8Rcsf0iZ0)hFj`ow$tt+&~;+Ux)Px1O~J?!Dh7 z@DniiSqIb4@qSKB=~@4|&WSV4@G!4DH%#`qF^1EV6(a~A?(TGtk3}_(uyeg`0WQ6| zl!hbWAf&XdqH=9Xmi%>u-#e#m{iE|by6?JGZ>(6AXBV)?1(ocu$_4N!7FC>lA~Z3flDO?WrH;Img!u0bU+82MoDj z@Z+3U1Owp1g}~bBgT{d1vTHZ4+1F>!*)M+kYx}?d*MHb2pM7TQIwc@LmiBLqQ@)iD zC?U{45Gc0*^iNxqB`G0LgFv87l4(?v==9W#j`#bf{nP8O+vATsYDbS8vpuu3weYnE zCfq4ePY3`K0=@Pv``|0q$|I%bmkl!1?~#zK5ki6)U~A=Q5(r3N>DQh2NJ#RWb@icMa=ZsXUaGU>+PXs;Y0PEK0g^1`^&dSzYJ^a^$>+o8=z=Q)Ia z6Z^_g)U@9JVf0)eFa-4qJsyc&x15eu+!XP2t1Ml}2LKBvRj=TA$8VER;83%{K;pU6LrvpK&k=w7YJUuqxjSCh4`ES*r+Jz8NN1?VWTxKPS77Lu z>Rv?%pce!wd7QfY>D#m}HxQ_>%zKGL@V9^Oe%m*<*Y@w(fVbX$+m@Df zT8-cvKwAm|BFxHr34wlsK&b)HPpwgwrG$VW5GGA#Y0b}4ML)Q3(4K$pIs3tD-?RJf zxyKId*WO8O1!!>2%xa=`b${KN)bkvP!_<*{B!WU#d7+2X4_E;w?tm%Yi)sse99|HN zdT{uqfR=i!C4tJT2;Az27p(gJIWwvI8%F&Wb%dW(Up>LRc(nw2_rOZ^n$Cb;U)MH; z6v5ZK5)F3F9QC(R6~jZH@QyUt^+}hW;lucu9G}#pq8N$+cVS2-?bNmL8F0~13IV)> zY@t&SPIKD${Irc9-DeXg58C+MM{M%0BQ}0mrwPm!tX0MI!Qpg2ga9>xz$W?9zHvT` zczu&7L-iTh`321MTqFpf(heuxnGX;K@Sbv2&52j9FWUPbeqg`-{qO9}KmOiMeRRr} zmo?wHD}?|laFvfG1o{F31z5E&%2pPlguuu^ATytOz4yw<&e5aC?4kP~u~pn?V)SJN+rHHCeOmReetOC5+G;TD*CKAKVsz`9(|Kb4D>@Up$_g2Mt8}}900ICA z>5ID!h1>?Ju)vIl9?_?(Znl|uW7v4c5;M$5+zFxF(*$c4q zi#8@XO7maFBJR>75|^VaVRSo~lzc zlt}fM6LUb~aB^5CA6*DghsT)W?ccl49=Pv*X(sNm`MvWtw`b1Yd;dMVx+r@Oa%qs} zfxVtu`BXxngg`eSPznIL0p&7}u0WtZ!7#HGxqg>Kha>(lvUB&HcT3&>5&M?*{D1TQ z2W@U{PV;bPc4oqcp{=cGXTC=;01_BF7?GU)d@e)qS`t1`j{b#l6&x-#F=CHGo(h2m>vv#Sw+P|xB_D`_}wj?nB z5tf6|19k%HAmEUmgNLqZxkT;+}Q%1+4RzA=< z^U|CeKeE>*ZoA1QA3R}`w;Z%F2?5-2V8rXOxyFdBGNNFG9011F@x7jbYY}KkhwmI| z8Sy>u1A?Uvp5C{W0JD7P7 zSAD=Ymu(CpA=mW|0i^4LF|FB|8GG=72kgf`d_z|LU$#3nq2@CGsO_QOUARehQ z%s*0lCgCCwSJvGd|8TK_q*aH{t@tN*PAQU4XZZ%f2>3N$IIFqT8@)XBw2G2EJj zl!H)j6+djx=;2_Es0kpBx}zC%6e=23l`GPy5`NwIMBAyfjmqYxY<$m@giXTfxLkNC z0ft;`PC%L)js)c5ckWrif(ICpAK;aSMYh`UTNIk`J;Io%g7M_!q-y{ilbxc22M)Rh zz{QJ~Y(w)(J#g>nEs9*a;e$Y$j!xGu&@!n`n*ONgfEM_k7iPHoX=wu>(;3Rw{)Rvp z2G}pSa&YRI0hOBo346^RB3&Vdp{85CA67hfy;Ess^~V zatr2isUm8yT`v&&{5XpcVz{?1*KMSrRKBRpskuPD8q`m4OCaj!`YcWqtA zb@j9`!;yJKcxjzIvj_l20HSP7nh>tSZpr3@t-PSsQ}+M<*MHfsbyC2WUwvt-tI~*bH{8rsQpxhxB?ttglbUHY z@4_F@{~|t*_oAYJbLq~I+R)p9KafolrZN$dMMIz{y+QFWw*U-EOO0?jBZH3wnSmTs z$r_0@c&_bmU15Mjn@3^*App|A2@$Ze%IqUQerrGQvj}N!&mOx?NBq6?-1GLzOE23U zx7{vH09pCZVxBlDv!cznq9TILoOpo&*%oNpU)gS^2;l-uT=kRZU$^^Qxu|5K1EaWlr#UlsU8@Q=fPBQ7005hlap_R-khayQ7K`R=&`n zcZWx&;y$+mBSqjefp}bC5)2w^UMB=RD7ygr_wBRWZn@ch_2!%Qx4*w@mo7_iE^NV+ z8eAz1h>$4nTZ2I03*ZMdEOlZLeF;At^mVwlmR1NAkO6cMZfuObMDPPVN5OV@_5efh zQ*Hqmf<_$1Dg*{uD#>E{xz;fdj{R$xbjcp3-;em)g!zw*tWVZp5sacRAg{7C1n}`~5$a5FrHA&3R}#;S;a$;Q{!cDi<*hHQ`p3`B z{&v=?pGg34Nh+z!?D5abWH9^~Yp`yp{<8(3bi0QDeFj5{qvk}$JEjZ*R*+WzJCTYi zWFQ!OU)oTbFz58XG<{hDHCI*J1MIp40WuU=kuad5EdmGyIAx`(d2(#ebTV&y(>CIC z=0|Vx&X z;$@xrcLo>cU+-LLN3s;wnSVVykyFnr9dxLkfenNQDxNU@T)-|1pIB)?0+Z{8qI3sN ze#t)vT&VP8#lQOA*H#tNZ|{C(hT1=8Sg$D&;tsH*^spR}`48i7fGgAoMGwPJI4cpR z40aFMK0tjHTVoJ=CIjId$@f<)1<(x${Cd^MYBB@?)umOdupR5XgaOzHxW1-Ee|?;p z48{V}{g7tnLeA*}l{;|8Q!X7g_=LHHATChW0~!U+?2cIb)W!)gh`>qiygPh!=Q#t@ z2?+t_bUMJHLx*f(--1A2vBkw}+9IHKO9`Dz!B+y$@|J->q{##0ADI5MMsPBIGqHC6 zTIUJIWc-qTiy$E23j_e+c~JXpxdmYNJ)}&bO%RC8|C$&pqBzLLBef28Rz)lc zNAlOtF3vd-H}^|fLE#)$Qo1cbfR#B30XDR^oMQwx*dB1{x=jmBkA35&AOyfrZf)*P z(nJWrDE0YL-zW8qntv;}JCmz9+B=LAAbh`do}_whyHP(Ek|%R5a4H(tt|_7KoD{IG z@Wdmy+_)Az_Ue>?`|r6|&ttZ-ykbks%XaC?C0kpQ5C@z|R7ERY34sU!aCBUShc}3; z@L762hHvjZ>AXrbZWl%nurB&>T&IkTORzO5nw_!?q7b0e0BCP-DO1}f1VH$~1ttUo zF-Z5|MZ&jQfEchR_b@r7Cn%(UTbrrU^}yKRo;|a6{Ma#j_2pOWU;gPQ_Q1Vj{^xbZ zF{{_nku>Q;xH$2m=c4?`02OvFCum|KLwW}euIkTS@NuF{&qWUSK!`Yu)UjahL!> zz>jZI>92lv$tu78*s8z$$n3-OR@FZLK|0Q_v0}wd(nIwzcKIBrtA@EC!aTydgs9>!iH~LagYg|?&LQ$Ib{b9 z9I#`@j_bbQ%>UKJMKM*HcWd)92^BYie=L878Ul2M$oQ|XulqJ&5pweDeYKh20#6XO z2Qbgk-EsXq=YaZxEdH?|iI0JfZ4aPqVrsi3rxL^!A4EKe5AUKN#Ad^d& zU;-mw-{)VPe)>zuBNzk0Vn3I1PX}put|O{C##&g|Z;w9oh>ZQcU{5^uxSc$H!ltL$ z@1KgwGmLHA(;g_nRDjeYjP05(U&ToI59=dDS7iRFW5l|7^#$w@b*$LK>CzzF3taVr z&MJOaYW^QzFgv>_1AihOuWRo=X$+xbEUZh|=3|esF@$yOW|e@at0wwNR*R0-%|i7c zuZYUfmYR^}RD)5r9+amjOM1MoCiQE_7jARHvSD&4s!i$N1Od$zk(oeMf1@cmOC*nj=}XZDLXf2DWX)oE~b#;U=)d>MKO#4W&R z14lk#tCfxndC=Gm2EL01a+)s)>iydhG6dw{bDj8sDQt8N(5V6C7J#kxs4`sJA;6%H zhCISPf3ir;5Sbk7b2ewq!t|q>&wz{b`DQb9-~mIjgOufF)Qy&^+@ci(ZR?ccAXjC5vh&H2QtADI6s-Kj8sd5(z{ zVuSn8^BMvI1R2CwTYI9CrJ7^ve8oEDUq0>(0x*=o()-@dk{F6uj5da+<5Q_iPP&1P{;HZ| zgn*>`t6=HUsC-q;0j*G7I(Yex8pJpc;xV5`;Y(n-*)=U8K(yS5Fo%} zG=9U%da(Z$nE#~1w5Wb$_*s+-&m7$c(b(KAi;_VQ068860tf*_AIbPQ9Tt?3@4|<` z%MfQn3xiXBl32_?KVEFJ{nYe>e9XZQO4ZC1+&ippOgw2cizJl z274Iz$*PGWA3h$OibE|@SVBFQ@Cg1L1fLwDxXp>=`>48Jq~sw6OR0YE{8 zYp~G+D>6|4kQ3b1LdE#o=a*&RPZs_Ea>lAs^*8+3`|rY_oQOl>MIT*r>$&Bzf9+^e zVFYJ*9hbEu!2wSLbcXZ0?px56p%ao30=BQV;1|ZzM zt|MEQq;9SYH#RFR6&Uy|l<6Ds@_>3_j?QZgkDh#p-wEr+@Q6Hw4%lGugF7w{HVzB@ z5RPZUq{baI*t7%z`}gm&x%qipmsY^F>(^{qwgFfH4z!df5~7k%>4p{pVf$}YT8V4} zj_sdcUAj`Fd@*RKuKzUDo52>YaUa}afHNpz{Nvzkh_dCE zdfGu4z+i1mlkyaf_T(FafQS@GhwNjV;6F7vZAXtDv1gxp#(wbK@B5j!d*|k*78|6K z#PBG*!~92Me=!*kko-`jxZ)r0>F-1JEC2u^F61O}uYXY;LAt)`uRFvZ4180Ul7|EX z)f*!Gq~2eVVLupu`>R&|&s%VGYecxcEKAJWhq!(QVyJ5#;V(=su?FRRhY&#UTUiuy`L&Lq!#=?3hKvMG z*_c#!$2ew}c{dAdCsL9OeO^u`=j7H+?B8z-vJJ4IRo6uc0hX4Qe1Ry0jr3Jaw7d@+1cJ(c&HHm-c zTQZk>SIY~iB!#z<&0^T_#fO$_b`n1EA0)TqEl}{ap0P3+Y{~H?^{3Emc0mN8gZ~fF%U8N@s z#?BTn4Ad}K8 z)d4I2YzJWC%)?)#y@^VDQOO3%p9X;O$B+ulevjjn%JRU6A$zHJ79!2GYf+CMx(k%P8Iy?<(I+L`~jI^PZEI@0x#FW{YH z0XY&d-vuE7nt}DW&HX(;ZufjM7ErpEahcO-L4bjcgb%gjKzuP%G&>k>CIKX8nl*KA z4$}>vFt4KlKzWb?Qg*K90Uzpva04EG%nlwpWG_GWqE`N2v3u^m+YaA!$m>)qcbo)~ zCbly6N5WhPz{xisWVK`m`oRe#0fsxkZ{?iv7d)H@Q$XGeTvd#}TlUvJfBQ<7{m)+y zmi-ID53{#-C$aw<9H~<>|7~e%jQ&BuL;+x15*@NcfK63nw4zm0R4F$Z-5snwcmiQ< z2ear{(v-U*)Yzj5v19e>gM_sWfa3z!t}NNcmy0(3_b+YYp_4ZKxK0ev=>bjiQJila zp{g?>xJQs*wDKJVH!jdtc;<7`7|4FONqc#{R58^2;V$T|+4&p<0Ac6G0TmXlM$JnR z9?*YB4;^vifY=5=An>za{ld$>anSs{d}Se)y``^%|i? z(Q4JA>jmob3AQ)eo8w!mCr$X;F*i90Y!6I==}9L-@|o(ETpT@vF8X6GN6iCn0U4IJ8=?+ zw$QZbrUMHLcH-Cxc2nDx#jCcmvMg-?jvt7w6aqv@4Bj2#`G&2^=yDNF4Wl0y1yT7& z17LIJKM3XBE|1a=3wPm;;c*u6!WF~CEqu)7^rz5yTjj&Iv^GE;0c2YtjPIZcz%b}9 z99{|lvh7vAw;clTZR=9u;?9+IVQygGJ;pr3-uvK}4cVJ_$`Yh^b>xsmSku!}cGJOw zZsGs?uYS)S{pQ0qKd1ADQSA>nAlxJKA6E?Gw{+Ej5zIgH94b*cFiQs?%x8egrP4JZ$0)V*kAHT)BI+q{crQB>Xqt(^Zf`ZY$;=Al(X4Zq^dz_m&V)_XsBjthkK; zj;?b9fy`Cd2hf!?LOq#Um(qhb5FQGj*G%}47U7)HAep>?_z!_gKoRPjsSnlmOFx9; zN)tPR(WHa`3lai=GZq&YZFzM?#sP2m7B)hYKo}F@t^6Gv2n5xCnD4YeCtZG+f3Es| z|Mb`FGxIRQcDCUR1uf4FU`@I{uoDb_s)wq{Es{k_s*@@vc>ygBnPI03&# zaOH&_JU=IZRBX{wVbx!70zP?;`bT)M>JRgu2NQj(%DS_CddVtpown-lKQlYUo=*7@ zZ^Rud{u?r`Q(FFSS4R;0uf7Oe`77P#5QsxuWKbbdwRtK#3;|(CI}LbKF(m{dGeUER z@F=6Th5%hfE0dK=*KOnURh=5J7#?fs_d0i8qYS z;h~CoopUNkSB{GTgiXUiaX2C1piT!kaq|hA-@DfZ0hcac7EOtIWZLx)W0fvd`P>Wv z+OBa+aC4Zj^3Q&M1_{h_a~OOgQ_m_p^bH2ag8(27Mrif?xY0LS_YZ;q(!mg6+`k_~ zHTSPDjDtUmgd3s>f(Aj16YfDsfM7v2QMe+ZLSwAXzm)=j&ck?wbHLbGu=pQ@0nMPO zpWhkx{Wpi{C!a9j!(2wWIK%b2Q%?&|8!9 zc*%UoN75qNusP}kZR1XrbeP5Wy0^oMMF9c;L&7C)dV1P!S~zGYjvuoH9T&KG?W$e8 zc*z;H7$)4CnY705;BX@}_#CyTL?Plwyt zK%(2#uee1ZGVK9!dCWV0v7E*E)1eU%P@nz~0+a%PUXO+$&7Z-kajrobE&exGG+6Y6 z@yFO7QojCmAccrj;^0Mi@vWzywg2OP{7-w~ThH08CvUM?Ed5946P(0>V$xb=9`VrFWbg>8I*;H-#a7w06Dg_iQ{vZ9=z-6$J;H_X3>wi5E95g>t`5G z3xLiEgj6vs0D!|G!oY#&O+2pcp>uoY?D)~+cJky&TUuVWvtOUh*aRS`+Qs4M(aN7b zg8-}lYtVLesZgqf7}wL*4?pG)Mme7^VD1h3_@f{ptk|a#YRhBjuEAyiqG;2m&z>4dP|`7H$i-GTE+Qo+P~!j00=1jGp@3t zBV#@~XRB|1WR>53YW6oQ{9o0If4R>;11f)YX*()ydBDtzQ2G@@fbLik&I$M?r*js@ zEI-sCAh?OhV+BoGtDG31<72C5bw2$U(mK=DfT|eN z@qO6)QELRkqh2%m9rIf3e|*n|Av6t%Kl5YMii>ghJ}fT6g9=0TWnmHFP=k{^0s-a_ zV4-Msdd7|%I&4P{AMvq$?Z!13l3Q_Y(=?cR&0M9+Qa%R=FmHx!h^+oMLj~raJ^xcW z3c85-4;lo)sCZP>Z%(OwK0bWCtU>6{U=7`q&YE@M8>fd=wdJzdRTFz>d1(~KYWJ5? z0MKrTmgxxs4DvA0o26g@=VKY4E2_r0e}FJ2MaaE#^ohjfA33Md+SqM|J#>Vm0ISwR%Ub}OX;!@plb<` z9Qn5)se2wsvr)?T))2txpKqhcB8Jw{%C9L12phKYVq>&l3mp)0jvtwU1*2gz;u}iuF7Ru|J51IOTYZUR^R#3 zHZExOMgHTGw)*K2xqv2<(R2Z{!PpMl07|zz2(X%55dq^?!grTc8QbvzgizQI*?xe~ zTDalv_kqwXLayzHYMEjR0%A6+V*WWUu&TL<`F?ChiyFz?UnmG*o=T1ybK)Sc`J7Nn z^4TC`lgU=!^D=zNTd(;>P~(nKKu<&As!)jcyEj92`)vk;oo zc}YOR4g?;Zn*ThDO@B%Oz@{K718fHZfdLPO|MKFw^j3^?SpDBAW@dYV7(yK%0&f@) zL4Ag(3jxAFggFeeNM4DW{j%v1g28&^$WeRx$*1gf8T)(Y@h9CfZIkT+82)Ki|8*5* zO9lu4hLQPawubRX^8vv~kpKjss_nc1*#hCrKb{#U7*%hq+Qz3BY*{M*OTYNg);~FC zmFp{3m4Qjxnm#p&9SQGF{*y^$^j=|q_+#~-t8}}EKotTcnb7OlX7}liatb1oe8rS| zTb(zMM!>K)l$gRS0oqnLmWj27fN+9q16+}zz^|@Y<*K#^sLzen;Rf{4vzm+%|M|nEy##FvJkfjV{piVZT3ufL;%V(7gif z>BG0FbJ5x8b7(?0(_bNYReS|n03n^m8h7Wd6aaJw&Lfly%z0qUvICt?k7^GFKAITk zEkcMiuSaCla4QUTeCzH%(G8(170A0~wir2$vLi8frwnseferovCUF;V)J7qqUFE z+49dnuw_>L|N4cm`eT{7MhrO^6Cr^5R@)hg=3)(m4fA6~UFAKVfVS0Ji|Ij#)7a9*+G4uz)0|T$s zdpGtM37|AEy&_1R~Jtw z#B(VK$U>yk?}27VW4F1}21Y$B3g+jR@H+x0ci#%+vo1w34!oI z0DxtF7AS+jfO0mMHJy7XEAtHTLq3h)!y8?f`sX={56j5k>#x0Luf6n&-6BE2 zBx>#H8QKl~X=3MiJ5-mFxw)Y=W6Q zB^?_mjQ|}PSdmb`bX?%rycQ3_1XV96ofaCwFWOSiG$lk++C@K?BM>I@JsoxspMe98 zyTc%-e$?6N3=bK?CXamLn83p~9kRoR4%zIUJ+>&rfQ#3zB_Yx`7`F6#`P?Z8zz=Z2 zz&E@4N`0Q{f6yFY>j10&&9T!ybw-{o#yXjU#W<v@E4S=$S`04s|<2mO5({5y2<}qv#h=VKMo9hSjA696) zi+DjkXdg6}sFly2*;%{olq$pGwvwH?e{x)mca`>MZH|uktsCwXW;>uIbJ4Ca4=FgpEzL? zf-hSGE?>TES0n_ueqBZyOV=X^z$VccfAe zdwfuprP_6*%p1%zSCo|Dw=)MrUnI?IPLcdDEW+SS56l=c4B*DHvKQ(8d+xLEyz-j8 z`0R7qTXvh_*WiR4d=igp{|z6L7m@`SQSCo3%pm#+!8!pF?H#ldetI00HWD1bpsakZN!pCc zSpww&tVMqnnPdNeYYEQ|wu^{)AGibx!&x1m`H?y~AQ0(mRQQ`2 zU6}Tuejirr8;bxl9qoOEbQ-^hw~z)JzSGjMiL3bOkXzFLXyi=-uK?emVa1sARsM9U z&CgDMs_*6T8HA=jzMznbN?5KU{v;K&Bafv3pd(-%%|v0$o$<&ZWT3&G9Mp2QE1AoK zB!eg*lbnoF5qypmGZ+kf#MS$HfPhq*(|F+C`|V$U^w0KfG5@#Re6t@nQ;(%5q|Y*B z=zIQ))c#?7xv*707R}i-BsnlI5FD=dFXAHrIKSuscTII?zrSlg5G)G>wWIb(uKoE7 zyYV0I*vea<*~V9_m`LT{C9nBO7x|JUIWP%0LT#X|iee3qKjQ)|h;^yjmB;?=zFlD? z9jC!m24u1OM9UO%As}T-U+oh)R7gPOMvR_}{jr4k2MLU#gHDB@95KAt_f1XL)*Lvw zykX<#v=S{rKvnbT*gk0jXaS*lOMn`ZKI|F+p-p|R%~mvN?;@4>Z1914r~Zy}RmoV- zrV8i-^oW>02UrS6Lj%ld3&8Qi$84VrHebAW(KseB*p5mk1ineXm(T5mfM}9+X~2k{ zYo0@4-h-jOJf@ksm(#wA)$l{xk;e4&wA!&~k#8$o02&1Vz##GuO5VF-bNSPdq7zk} z?xe$}`xJFUh!C_O8Yibv@1QjR&Dw2O3IMtd@gdHSfgaRc8Y_vE4*@_JppD{IZ28!x;q7l>&giYTI%3=P4FIG#`zzEkL#b#=er3 zdKn3{6{-AlQow9R!;l)#rI`=8b1~mCHnJ{`7fs9VcqjZYIB>7mosONUJFoabhry)* zpwymiaLLTfj2%6E*bW~)WUDKycKPZRHw>6%=CbtRFo3Qf>fm&V8_-q1&p2o_RQ7{i zu*Q!;V1V(zJi7rgY*4L7JC}5_-6MwDi3UK7OPcfz?%ow!&;mYpsUK3WY=4UJ^R!UN zi|`G+Ml3*w@1+2s!vG$IHrsk2de(8{JZdjnLFk5TGa~m+K10f#CBOUE`+BUwH zksc6zCU`35mLFjRTw_axgy4es8U;taH#E_rYS#uZjsnP9))AwB!SG+nTDAmK8$U8q zeYEBfP_46}$E%%M5NEC957AdZA)HqV0s5m&ov?=v5sfhswFRLKuCLk{Hg0rmVD-|H z1nZ(L79Dgu33g`e9DX&TBq+^aC?o`-bO`IO>jz^$c&}H~M+qH_w z7zX5ofWtQ(_R|5*UzBm-TznCjy%@c`?+yYmyl7oyVI7#|uq_z+JL?d>!~AnEa?toe zvp9d{KgQdQ0Dv?CGarr!+{!!?G@bLtUg($Etq=hgXsrT=?)Jab@@52Ac`qTbeF!kX zvx7XQLIyZ5ep?9qq5 zY4_Z9m+jrRHyQM)LPoWBnExUNu43{@Pe4Us2f|-#Mf*_cOY_((@IovNw{ju{m;*-mVu0+bWuLHe?WR0)lGDor79GVa2+QfE;qT zwW^-uGsbZV&Q@S*`!~?YnBZO=Z#1UVGNZDM8o>h)hac@V*11pLpg$lTTp$xk2k)=y z&R;yQIkOoyfUTYMYN{9``51VE z=Bfuey%RA`kcpRQZc!5j6cXB8XasL zbYXx}P7utz)czlT_)&ZHrI&Pn#Eu<3CTV?UhCwpttN*&kQJt*{6A7!{T)1X11pk5g zRRzJ#fPzVt=UDe_9lYMj%D>k;Re8a=3gMy^kM+}+ZTWW}+sdCl*Ixh2$?<-vu-SBX z@lL9fDmO=q(NBifCGbZ5e@)W%yzw`*b?;ck*fXepn?oFSEXy)shd>;uzF5l7rY_=- zc;7U_&<6m9;mD~7&7rSyUse$4Lx;EqWYIhXH?gv2c2&m&N+2)>?E$SZtyRqip?lJn zk*{S=b74TH>K;r#+~XBg;xYjT>^0Ua7CW!*e2aDFIfW9S0j8&>Y_I6OJvu6I@!F!D zJ$Fuf%vaOFk^~q(Bv88DKD2S&002M$Nkl>=@|I(r3hr z8URK4$HhH(i_Pqc?N$STGEheP1jCiR{6&s%b-_fo6Ty1~djyT~!?tF&Nat-IXbjul zw^9S3z3?2_)QY5H44N=_wnEHp2Yd`juKG$5hyxb~tDgKl`fK=V05XWNNLq15|K7Xq zHmvUpG)P?%(qIwO%Unsi`*qX2;EBq>KNok4t{xm@c37}AIZm7wnI zYlO+C-gRL6b5+QHL#rGcAD^?8cTd~u-@erGev1KK9fVU=?aO|6-v;3AR#bt4dJTwo z0J*l`Bz<~7irv@649tIGP_C~_I(&r_iR6_6Kcfx}E^p-mx`D826>7A;IVgmVw0q!_tWO_e z#z{Lq|0-atdR*PECNxQwE+H!?ykg@j;s%~Ef#^R#cQ3krTqv9LCFqm1i}jA`FER=m zPGAawU>4ch`OW(u5aYjQ*KaJ@AKv<-Ew3!4@CO-Tuq}WvoqYMcT?j#fHUOnz87s~4g^jkPLo9-QiFbmFNL0CWPX zBbO!}w`jmRR`&U*0NX$$zcXM7f&kL453+hrb;2+VE|`C|4Y11pzk_|dIX5fuLwN6>G;WUvEJONd(LVp zhW^%1owwC@#Q1;kwKM=OTJ@S%I=bL!iZyAZOiZlXWJQcbmPqPC8M=`L1ToA%N7a?C z4G>U2b^*XJjOA9~$PVj%s;|mYkzjF51iaQy10HHsm!UwsEi1Uqk2EF?9PitV-lpKp zy$E7WLV96fwP&`|Z9OAa)!@pQW(GyeDQY8bN%*0PRWIsK+)sePJUn1h?V6ORxt?)%S+Ulen9{;|AhAHYN8ImdbWq2A5?CTKl)w~SpaNhH82 zwk#*RX5?1UfpCU-LldJd|p<+_MU03u;9hIx=L+9@J5P|NhVa zYOg-`ZJif;ppiiYr$BJTbMA{O0Q1X4?2~He>(tpCRB0y`G;uPg7BS3#k_mNP57gg5 z%TL{*@?VjGqm_5g*yVX9m& z>LP&FjQ>T7u@~j&LY}@dDdS`~H@4)f7G21A3t1r4Vx-cy$3<)vja$O0#j?}R7IGN= zAOS6l@phEgeY{7@1csgVQU2o+=J;`K+lRfY3eyinORQ?;mUJ6uob9LC=@=ONV+)!a z{@YGwt_^U-?9vUZoWEw{7jLoZ14nJ_@O&^77}IikOD_`sngrY~6kzK>wyB*6Q#cr6 zreEe@1Obf%QcQI)?xAznSNG}US$c(fJ3XTxoht0ciIYwPBgfXyXP$Uc^ue081njZD z{r%mHO@P?LN^O8nbc#Ajo$au(Yex7t=T7^4D?Vd^e)_PQ~?`j^f=)yG!jAY*_Xkiox)AAHDu z^!gk2$_p>r!Mv4!Eb<~zN3u>OvW{dK;V{9CS!6>F#yfQcr-4+td&A`vo#sXI$;Ik} z*S!t^)cq@3!PxlZf-V2{V_W{shqfjSfaAk+#`zoS!t1Zj=ICyd6jQDGB4JCZ=jcKiW>)OJzGb?mlb~SjYyE z3oUk7QW*8C5}HepUR`F+)jRZv34!JGU7U{`cR;I-?5cZ6jvtm4Xfay7s5VJi^8E?C~t zcj1&S_PpodcELYv)6MUs;Dq$)UYnI8?z?YpS7L^(2w~s;R`3&L3gyeoH{dsHks$zp zm~M2GrTJ6@@}+-q5Z)T!cU_F~gxZX@E~;Kv3)P0qV1OWmiwtkJA&Zep z0^)!b=U(>JZUG!d3_@d41h5lRs`uI&%wi7*YF_2lCbSFMEq**&I*j9>J}Ym!e0}jO z__zT$-MfhR=tr(jt+q=$+LFGv9fu7U_WJrf%~T~E&T|;6jp=VE-A;w2b{suBAx0aa z^Nt-=<2YT8$X)%ys%Le7_?V47c--v3o*)eHHdR2vvZ{|Q9-a~8^NqqpS9{h2R`PM! z1<15f_%I;9Ni~9SggVP3+s~;tLXWsb0{SJ$|eBM*KS<5t5>ht;`OT; zu#gc6m2Nu_plb!q;Ko>?iGb&BV7i;Qjpf*nRihYp=cdl07Mde@BlTZD#(NBsOFIU4;(Jree3o18uEWXA%=&ocU++ zR1N_%H)cg0nJ}rh3qAB4f$a4cxhHLa%K1eZ_|x%z?`g09r#fB-<{yHj4_rl){>Hcr z0!meK90AV|xX7>$(qz-uU{{}psTc)#!u(HbhwT@f=BR}ZBivKP z&}-~G3C;-!IC_@%VhALVDP56nMh+!gbmss(~|yS!v$noFzeIyMlYzylHl9Nw!f0m-P~=J3`- zZI0G+?v?-&*Eh5YfZ?Fq1@L}NRa6&@v*p%V{&YFDOHKHZ2mxrTg?;;U4D{VD09agH zv_HN5mJ9=~G|dHoxEn)AB^?siZ=-YfU>&yhF9iU*?-OMTeF6cW1HCMD#KV9^73aQt z?z4Y+<45+)lTV5HKP=T(8PXE)Z4B6e|pJQ{`j%2{^hie_mlLTV~y&KkxjMh?{4ge69GgxqI%XY z;;icxNiu{iz zZ(B+7>}b8Vm^fvF)?n}dR)ri}N?J5WmF)mJO4lyQV!ez7jz4~j*{!l&gqxspEfHgft^tuL9xwvsjq7I&nnqVb%5gft z;X{Y)xu>3SVG~+)Z@=@`!qWjrIf6WsOZhqy5QrUa@GDyi00zGe%Nhs*bvmFvI6>0W z)3f$~R{nqbqo3G|&pzii05B|7b4-Lxl3aM_VRn*alH|hGvk32l^h^C%1b4>T~nju`h3zK2d^4^Rbp2DM?(EyT^?e0dU9IB9@amM0qzJy*G2fQ z$|`nW4BAu=0jE?%*ttD(&6Za>7|v>o8~17?Tj zZEQNxY;(}-2lamJdhiatgOER4DEz!{=)2Q5$$PuY-i`FU-pVM_Xpz22=S_bbS3qn$ z0SmM#8Z-g+yAS}FA$ zm{#}hD4O6}@^@DdKz)|0ks)CqfCNCMOxm4rk(kJ)%}Vdw+`K)g zmH(go@JII2bI;pBodMg#{4*JF6*2!*iF&~hH&!p3W&gj^AZR$$2!b3W1)iq9YkG*x ze+GGTyx+zbmu>CcGq&=Zk8DlH{8bm1n+UPUVDtlE!t^7eQ~iQfrLdaV4<*}r8K?t9 zG=~4yqbADMqn9CbAOJjB@nJ>}eIzH)FnIaUpRn1P83{!t0LX9y;7c3~T)!|>S+Iw- zf66)3EtRcyo8AN5(#&+560Rjft4Nf#Y5zeC5SW9|=V}Qg5BMUeg2~UL8zNod`7v%= zQRCUfJM>O{rda*gLPEzi%vOc3qzPabByhj6?sJ;WX-r2UK&&GG0yf-n02bh-T>xef z5a1ufro#-R1F%Lx-mrCof(-ZV&`+1)sIk2q0y{h+nf|!%%k0K zJp0coM_j{=U&~Av1WP16Ckh!O55VsFZ>46kdg}FO;;Go@o=bd8wU$m#6 ze8LvQ{5PoOfw<$!KhJrXoXLR;=AX(8tWyOMaBxj}WmVst%DA8dVTZm&NqP+CAP}*o zc>%LPJF>^|^U`C-s})ob7> zwSIsn-FyVB{tML3plFpJ#;V9o$`$~c00?tkC{h%#7yiOOpkgV1=i};Llxd*y1c3|b z!xVJ@?;$?h8uL%?Fzpf)NE<5;2SW&>MjwSz&0r4n1`7*r1XXi)uY~}<$N|yslWcQ| z+v-CTE^E#`ec8seEugX}8v!e_4{+xp8{0drd0rEJoZktHuo(J<_DA^7)#UUI)Ik4W zI{-Amz|JwM12mhrh3<)PlN!|=@KOKqIz#J#ToSi$?_PWCkwR4NG(>aG5k z#SN;>El!u#QeIXXWPt-SHtNQcO znyq~#=KptE_5a{&S^2*q3&oo-|1qzAfS<~MKnhjo3GD@(a6?EP0(+a2^)9F%nE%cT z)!JP6K26S<5W%z=XZ2};cUmlsK^Uz`5xl;qI)`ZGJ1Wcw0anMvOt6eff=3YvL001Gr*3w$0#7aB;8JtMG zEo9v?y`6rfl!!8X<-g5fEi4nTUe!3o*sg5uRM85+-F4W;=49U}+A?AcQ>Ge2uXh;Y z4BTq~bm0Jy)HSGf0VuxLnTO=LslE!}+W}}jg(GN!@92MrbUgHf_ucPv!-WeM?ae>@ zE@Kmbgo)DJhr^fhcQXhCa6@c1j`Pq|jegm*ScOM^gUdE;@NNHZC~v+-L(DDUGqeEc zfQ^p6LK-2R#&4m+X8k(|y^ivO7KA=wD_GKv z5QHAwzhK|I|3O>H%fUG7!pgl8bm#+?iBEBH$lm$@ISQ^LXi$c+k z85i*lnraghG+4GHXCk!el{qnNbm?uDm8n8>LolV4t4DGd|ks_M#qe{gB0{VIzqw(+@E{onfB zmVfb~j`=&QZ2&7x;Yg~Li-LewVY>B=K8isxUuB|Sc7P~uXI*F#SN-?CP9v3WR}c_9 zvHI_V!+{;(TZDjyKE&+o?6j-*Z)W=f8ySR=NL9F1D~x^MU<%p3wOQHwpr`{;1fSB> zFLsUhNJvaL_%w{Atye$htzxJgnxnitnyTDLW&Ym(Z0e4=)n8-&+3($Wr%z5vu!cq% zQ*O_k+XawDo-_igmzTx6Yo6FUV`DR!H9p!5Apnz#7dQcYy5RUjFZg%D_OfE>g6;H~ zO3P87Y*pM(p3q4+r-8yF^y=*NjGZ`k+!iDZxO#2TE?t)GqxID+nWP9IVFy`=KJvf7 zFOE&_X~(y4!R#lnPY3Y%C)F4Y-@;2wBR_sULSWpBY?Fl5dbaSy^y)F|Poex_PDBV$ zuZ-&upwnuWa4CFtpc8<> z5QG4E1KpF@(oY?SkQau~)-{-d&|!XVE@A$EYOlQT?O^RcjaVom#{Q6e7cs<)OILvx z;sN&zs-rzN({ca_al9YQzZ;a(GxblojSJUo?ayNTf2$+@bl>>mQdn_Fh1v2W@aO6m zr@F%IKzwGAb-xrbmLoD_$_;|R~vwF?4*DQ(;agp6jAdWjSIJ1rg4LL`78g4wFFRp+%( z0)A3Nb^_aKB0CI6xhEuCL%OD)sr&^3fmW{pGyzKB86t1cINl@t;);779Es0&rQo^l>bNF7GyllL$j7t!RA<*XXASh)%&08dG6aeHV-XYIY0MH@8j$*P55C(4=*8&IL z!=TVg&JK}dNYgR7OVHNW*?W$f(3stF;-o$D(8KoQAG~2Ne(QNVfWg0XM&t?OAJyK{ z-(o`{G53}K1oKD#LAz5o4E+@eIj9fygz5JyQ(plZ)qbq}ul(*~TY2+iTl?^wj`)M1 z$b{V{Y}!F^ZZpfm;GrfU-hg}?`b%M~?th@)W2g@8nyl(!_e(SQInwShwWF~5PoIW( zozb=^U*Xs)0Wb7{hD_8t>1C_e#^+Yc-pj%cTHdttF5n9D4}PMb?A(PC1grltQYkII z?c(APKCCtdyuS0&Y$qlHwXpi%dTY&g(rimu=5-y>J9bg01V}SrY*qq*8C{|a8ej{s zXgiuueZiUiYyl#z#CNV;^p|YXU45tQfrt!Mpk7{|q!j)xkF>xe)tS&Fy-9|pp)CV0 zt=jpE7nNixP_071x%M*B4^`J5$rB_80J$cn#!*ARtU*kgXrg_J9ciG=sis6rtN`e{ z6#xXa|3HJ~w|6KZ+mXOy2s5)~aXL=?5j;}XF~`NY*>9bG&*%qrI=eFcVTJ&RF&uFb z24<`)g9iS3EIG?L`fu+Z5N#MpAQXs?s_m7<)`uQ=(4K$hTlUmrPs;lLfyTi{QT(Vs ztswE7ha_NZ8JCg4&BN*2D{K!!t8y%oAx!0-S5HdCDuM6!PbPi<8l9f2`c}ETWGjFE zOh^2kvW@dnDU_kV+Tsq*j5~H!vh6DRNW+?4x?oG!WSp31oIhWcN{}dR&w$ z%s)hLs@wOtNFB@GS5>t-30~fggC*0D@RiNU@ioZ_S+&?s3hfFjoCaanK&iJY?GaDM z8x;8N5Dp$+xG5m19VSvAhGL<`do>fzTTPh04j!jGt%&rkrngx*2>fx4t5D|6LU-|d z9H$rv{OYP33WQb~e_RcHWUm=^0bp`*w4EFJ6F%hFUuXsv4zdn^Xo`q5u?rBNvtj%7 z9k@_zXgr>$lKN)LOBied2qZx`LSR5MW}FW2z`ggmK=shVLHpv&8M}D-l7DafAr1+Z zj;;gjV9;U2vo82G!^_8)`svshb65WSVC+U=^M6Uig5{4p=ei-VDm z&+!{~;)W~j%X5eae0t$Z4S-%ieMs|U;?3{mnm;s{gJf*`9i4%mc5yJWy7kz@kJ!sE zzGRO|1K`%1Pr3xI7M8IEV}}5Mi7gL1kTy&)7cP@m@a*?r)eW#hwa0qq)s@MQz5c8a z_ykw4tgr6Nn((^R2Up+z)K-80sgC%&BIcic!1ZvuhD5-3yH2JZ3B;nXzt|CX-%Z*G z&NTVxDp&v8(c*z&+3tSjcI4dcG@$XPCZ=shC!*}6Z~NE~4uN@ad>aQ#mnuSb?5}3t z;IDM;hX7F3x_SP*xP|PT0QZSx3xQh%nUKmXE3Byg=Mm2>qWY)ULtI#zB^EY|yu?d0IHKnXx622x5{Qb>|B%^v4Dl zV1(K{c)r=;2MrWf`J2Dv6T-m!*VAZ(Jv@oe`MJ0u#B;3r5TG;00&?HK?M+|N7VPM6#&EbKx4Z34VS5tBN8d zrHb>!}}B{<(P1V6&?B4z7K0)>gFNAJu=h0913-mD0)XdcYtH0jMwa%hpp~?g!e5tFkG^l|Bmdzp#Iw&Ck!Xxl%*;Pnj){so z@|?vx00f*1(}Tf31Rbf#jL*1rsS2*X{h6)4bK2HVowLf~a`@1xU&`6stewiOWx5cH z>l{X4q}3;#uN~FOrr|h1d;ewhk1L>r zJj9TV*!&Jd#UAHMWlE@n9;v?A70`uMku(w4T)NU{_Yj~>X=NI9>wSdK&iuEbGqCCp z;XL80zPqatMqQPX%2t&|yGp66-Q5)^!K_TVwnCpX{}G&wU5*O8wrrIvn%i%z*!aUI zZS0t~1f;jVK^wT5w(cCy=!*)Oe&GPnk2A+`b$WJMuicQ2h3~*U7Bw#XXzmYN98!6K ze1Jg|0-!mW#6tkIcYd!u_2}cG9X5RX;5+aB&92|jDGUvc2-v33FK`=KhlVB0bTa^e zO%O1{iV7EH;Do>h`(bNRVo5q$b0YE~owYR`?JTlC#DOs8+MXkwpskSDUD63-o4K%m zKhlnk6$tgC?iz!y|oBZm$*uH2F=%>T3oe-ZP~goT6xwSS+y(zy@9vnq+o z6gZO!oM-Btbxd$^zdv=Y1J<9b+6u67=CUondCFFP{gJJIdcmqXN4V1tlCz(jlZon= z6M|!~l-}F&L5CrjSd*s9Mply6Pke0YIeU2B_@YDJldM>V#-R`KrP$$66HZ zVmAQ-rkRM@PU*H1miYl94&tb{<8Ujb9f~l*H(Q0Z_W@0sp+D@V_JIzVk%r1tVmNCz zsS%gn;`IQ3y0A|VZFX+>Ph7@s>=m@RNLx=1a9o!*z`5&A7mUqGJBSkkG~gQApKx(Y zBtV6}$U4Sm^yl*+Y6DQu*hNr{$l@E`)AeLxPPrK!5qRXLL)sFs*KRCb7X!Lxi`TBD zLZly?ncMW&VSk47ESf*)dk*GYoFC@_=AO-obu@$P`Osbn{>eMf`aVzx4~0A^gKxPM z-GwbPo;P#Eg@F$%_{{WY)>!Y06hg?%sYTSlT`Kks;z%{#n#^b+*beenQffCDq^6s zgnhkm;VBA&vi?1L7le(Q}8 z`tPLgr)E_ZW_q`dG~!rQuk;fsI@vFMf3;g8-+kG4TlbNQ@A!`z7sH)dgtl)-`*Z5b zRNSVLH;3&I_@TcNSww#zM0S``b2RM4Fh4-Q6ho;wUf=mZA7I=UZ2-3WsXn$Ky8tYl z#I}fcr^gf?+A8|R3dMJT!Q~w!ejK2CEIUPZJ}%R`)Dy2*U+S4SaO2F(tnHuQC;3O! zK0ot?UAip$Ne;Ids(vT@XgcN*k>6}bZE^u7Vf(NHx&~^3#`%Z%0j{YAXi6)10RC`A z4S*tsK5WsT^FkwVFKWAVR+I@kk(GbP2RZFZI&6Dp^*^sW5Pu3meGlo)QUH){)$+Lw z5MTf^h@vDeFUhEOgX&rRd)^@7D+A=_6F1v;UwYNP{rrn|-(7dxtdMTakp>eKj4_sO zeL&T|h6l#>pfmr}Yb(q@5=X8G(1>xXfKC|u%RqlXUIVj( zR5x^?ROjqc4Oq1bYG@JwbScka&W0g?x^kG@hq$Jo22}rDrFu%}5K6W^&Y3C(gkT8) z=f^0vC+UV%$Ol{>h~(~%y@tY!+zarIurH`Q^M}vvyTeSV&rizo_w@9Xwp?NSsvy4| zdM7n;LcU#E0E{L-tw4ec%0KW9W@mX#ng9|4XhC3%?UMUuZEQxG0I@*;BuyQg+X1T| z?$Hs}ujrwB44?Aq`$y`;WRX`N3ZJec;Zk)3*P@*|zh~a@*H>Sk)m8v$R1&VqF~*ua zhu$OB8JMs=C+ouZusGa z@C&lXaC~5XebIOj{zI6Usp}St(UxGP0H7^kEE7pXAQEtO6`b@}@GJu|P3#5S+kjP* zC=i5)w9o2C-~XY#@!i+$fqU+2Wc~@KN!eKSkH`HbaceiSL+zc5=l0@*S~eYh07=07 zyXwF0S)>u`7@2>UW~NFcm~UJb^Z(Xow)~3^Y+XWt$|YF_&OyehSRH?a=cYRvy|LlgsOGm|r7syOzK70ULHD=eN1oa&=m zJ?sEDA*88CC(0iCM0qcQ0CS4I`7P6T)Yg&l->o2xlwqW2#QbCZpZ0FQ^GbMK5CF8J ztBz|-h1GvFYlg%D7#U78(FJzly4pfC)c%!1z7B3>vJc zs9d@}!6)%ky}bT{74&4{z+Lv_{3YNa&?|c6BoMHZ}QukXw zeZ^LO|FJFq`jl;acHXMXn$3yVYe#$KwONnK6}w?nN9!gt!lbsL6A2(%OSgLn08iI$ z8Fp}L{Gd)fBj&$ds{dK&GY&vKsOWPdK)Y=LkS>H3_tNzW0<^KWbW;JCuPbe^|9}y_ zX%W6K|7-*3l#Bfp#?@EgcG0gWGeR{#t9uvebgHhqPM7(HtpE}PU&cHCb!lpE>h4@<`0nuV&^~tN=jZJB zk)zsSv1VUiIB!eKH=N-OA+o;#r)GVJ_1(J%FqO*}B=V81!^{N%7kS{+Pv9BwKtK?* zjGLM$6~29sp-kh_Sn^X#guBAZe^Q|&BZdyR03(r1lKmDhl*f(yw-HF_En85rC8AC{b1sca< zV1!|YLDKpkEJuBGVIPSJ2O$9OSx4P`)n5!f0uF$hI5*a`xMJ%cowt?W%F_RDKeozO z+MkB;pFRQz#9F_&xYC7|Cxl-&)IELDPV_&V`o+;RUOvpR1%rl z_3PJlJm7UPg!#-MiA9jbG=}*e$9zB~b-2Ykr`7vI*k<_P9|Z*A&bEN&v|^gOyNA4j zu|8J)gTcSR{5PkJuo1?;$c7W;MbORmr&zz{RJ!NWF7vD1D{-0AfIuJ&T%_hw8r6Vc zhG}%+cntj53scd65Z5Kgz}UBUpWSoUJ@$iFzh~dL^>$x1@Q?8a;WTRhFvlTU{1Wb9 z7_LE8`)6*6EC19eZb`oDc!aqKpRBRIi z(*i?GIGqT7Y2c;54d-)Ep@(UQaA(E8v7WrcM8hm=s_}4H@Pj{K$^$L|KPjcuQG3}m z8*|v&TS!}7K!srCAGQ5}xANNytp)@D>!Qi?1QJnL(Q50!)=JjCP^AE%eXuN3%0PfY z%OKs@5J9F@evJ*e#?j7xAOr+s9KR-p3n2goRqp)89roN)&)A)}-=RJKGmR-Tv7q`7 z0?uRp2@OJq0U-ItEcx2!j(Pz+T}%$#vW_~T^6$p}l8+JV>5hv3+J|5J5r1o^&RIo! z{QBD1A9d`Lb`ta?HxhFrRjz6k#;T~3WgBJt?=%^#&xRiIjKyp}#WDsbT?AOtl0)o3 z4NeG&H~}BZ!Sr;}k$zka8Pj$Zr@Y#bp`)(0G?vEZ12sQH9S?uZiE3vb#T}Evw&s^KJLw z{sVXO=1oNcJP`SsbwXR5f>h&D{G7#jL^r7Sp$78me~K7^CTz*a_k?KdmkBp2)epRP z9@^J?1`&M`ui6FjFt>quNLJWKVy64pG3pn`=7VfArB1g z>Zs-UgZdGd01S%qWAR)%x$o#OKF|Ep9Uo!cUt@5vmi1fje&Bxio$tE$WdJyI;D81n z47OgFgh=+sntwi71+Vz9dj6poXfdfWE2PNT;2o*M%&0v8Mcn8Q2+pWkHE7I6+%Mft$p{pQoEv2>z zG@Mk=$fm6~BKZ4h zL(6@nSD^IcsrTOh@a*iqi~fQkgJm9hOP+y|w!#vE@V?VH_xpD=0Q8&UWA;2cz7Kst zU}OLpH?32lc^rhvmrtB<|MlH=uX3*Fr@8t`rsYl5oL=?*8UHoU>t)d zfKQR$0L&-=EO+Vm*ct?VGM|Z~;ii@ZC^?`d1e~8T=S!u<+i#tud3&xAKvYMzp$uD2 zfH0L1Q510SnE1aYx6!NgNyudM8=_bD?b+j&m-n~}moB@j*RN)Hy+dQZXBx{2r$+P} z9H9?#6>~P9tioyk33kHwgI1VvhaCC-d}*y+8H|K2iKxo-`@h+aEU# z2-#1AHv$`5{VB<|DgQc=QNT0#+%oZ#XaLA~VewRVDL{w!ojy6xTL(OrvFDE-b$_RX zf8Ublf8YMSt-=CKR$)GEm4BKv#Tc&9T^xGbns7rdG6M&bOb&+wx1jt3Py$E1{?#kD z-I})dd-Ts=SonY0z5czWX_s1;u)^9eZ^X)ZFWY<+0_nOE)v@(o-UPVKrpNX+%PW#2 zOpFpy5NIY*s@V3=BBZ+8H#^a)!PCXtQL4%oabXId`T-%_WfHhL(X}ksE$koTPI8jj z7I5O7Qld%zN0f>+$Gp|=4-eE3@pvUB&K#T&*e9hFpbU(CQBpul3@B%U?bbA}tpbS( zpaixPA^}j4jUU<7<{z{+>e{#H&k};6EA$(|oh6@zIVU)u61XATE-lQvX(b1|dhMFK zbMKBRjEO3#!+a$-RdJ|)2EL4J)o%Xqr1dhAbszrdJ=j+c1;YG9+kVMf*vTWQ{D)(% z1%p9oH@|-ZkLACS>_)ObK6yrP;Gk{4tgUIS$ID9*NxJgM56NF6?$sW z`w(B7ox9OwdD{>_#4|aid z`hE;Co_1QSXNuXeBMn=l?Wi#QZFa&U(_5YF+l6e4B z(F#i7(&Cak^~M|S)~#Fa!NZ5P9Dwia!ZQLZX3C(3|YZ#c2hc;t}x1A0{ zf<1DDVN~%E#X(`;d`~ZE=oNj|`V;a`jsl;jAnWPgX@qfs2t5qZS+xdxE*by^OaHNW zdJqg@gbZAYSAaG&n3eQTd#Aqm{4w|8JMXzC9(&A~L6a~VH6&Ug?gO7bOgR z-SP+QpwAHm6+y=Jk1exha#uhNx!|n3C0y*_8OX zMeAw2H=So5rG6KTOlqY+1$gl=DXF#Lh5^8E&^eROtU@L%^r=Jp54yKx1o--!(?-Xy z&zv?zGV2EV>7aRg7F^vvN0$gj0K}km83~CpX%&e)T}`|vDoK5Y>*x}`t~Vkyx>uKh z46p=c--rC`o$KX?Ynva5VFIToJDmCB&2#8}yh!7>j)SCjI@x@HgNm;n}RoX$}THa6Mc1 zuL;=moF<;KLT;SD=~n*jYq$E_vu@+UZC6_vZsot6ugwdM7A9tvTsr5@%7*&unuOx4 z?P7M;@_61&2w_bMX#Rve8gzk0Oi8F|X3n}a ze;ti3(RD0LNybCl#X_4H=yvk)5ef9L=DJ}ID}s2jPbZX6{3kT7%3xgU1Z&@~Lwb?} zYDoaw30J14TxCvq0dUTQfzNl4#TakMHZ}+o(1vkH(!5O<6ZOh?2ri9DMnAwc?8ek# z$k4auCEqSAEJ%6X?JiutY&!$wp*cw2o(EH}&V#GRmvL-F&V3Z*_aXRRAm%sS%$H_o z3*9VSDf-=bz4aTK;r`z9k3$H4lV5z;KL(02ZTXi4Z!`d8Q6--18U?b(i688KyFd0y z{lWJ>(VnRvx#cBUltZz}LqCWZn5rwt;ad2H`QjZ9p8s}ZGTc*^ZzNKT8*r`=N|uBT zT(G9Eh{;*D|Kvq6z*U9)pSj^4{r4}-0I+dIp8sToKx_4(`(?^SNuX|aE6><>lEs?w zfH{F>1z3`sZI=YuAq0a7+DB3V{0LK2EQ64LpZHJ0H$u~Z1!#l7JYf~<)Aa4K6@xL* zYP(Wl*^uf5Jayj0c*>f$d-2Gvw}~-F1EB}+k?}O4`Ise%M6|?`K>>#M{Z&`Ft}X2p zX;>jI6FC8Z_&L`{O;<&zjQLQls2IBzM|&iFa1&ifcfmJ=i3} zeC*I6w_lF0hmTg=>2u#G^gYjMBhFQ<+4c_|nyVVy>MMLG@S%1E9u@M>m|Rt7THcTV zi`YK#{IgN`S>LZMwUv9TS|!VWdzgIA0iRpIM<@Wp_TgxUe>bj013)*Z7~9CCfFF;{ z7@G?rtRHH-zhC|AXYQTX-ZbUEF<=?E@BlpjAtQde2~++x7%>23KO%icNq!4A!r%D7 z=rWamA;fxrt%)+_KZRda6Y$y>SKP|KeCbwxd&bqYF0Z{svLvW)5sXHE*y4RXsczkj z^=I-9>9c*<2>bVW1xE3m^@R2v8r@(Wp7=o-mCp{KZ|O2@acXkfw*9m9g(VRz1eV4F z`#O8WR!P8W&=&S-hLRnP+eULEe4r7eNt^KhG8e{y=fCv4?iKQCkgRaGl1A)6cjz8K zWo}6h2q)#&-CMQg{FMb6BxkiOAXylA$Z00e;KwyYz|rFS0*ZJi zvcj{%$1CV8{qi4l5HDx>I)TLgJlFhnZ%E*trqUnQ`SY!g9B$3|#&`kTc>M|c4?LvY zeL5Nda$aXVy?qKWsC;Q0gcCNHv5YV5SS7{_69P&9@ciek z`!~TZ5S<5}f95MCHI8U}z2<^Voq`nNS@I!@qc%<9fVxAxg(xAJRQj+OAQrgeFq z!Icl&u0WJQqOP>N-9|JU!Q8hK1z_(_&t=OPGXKZ2a|-y-bI~#}PIlgE`C32-Vjby> z3;^t0M~eM?h7$s zfXR&U`bMv@xmhPYtZe~qhfLqDMFT+JNF5&p1qiPr%m2@R@FQ*a_ntejZ=ajc{=kjG z$U*Tp&p%ex#+Bv)BiIZ8?FVP`hLRkC8)5&!sNuOp6FAVZ_5R=omug<(xORWVt^VhE zxB72ix%Drvx#}%>{>?g>s22}=Mgg&PwOun05uu_TW~59w5U|%et|803HJ;o=0a@#r z+zU+9n{FMl69`4@qdqmIwf`n$W--RWA?3%0VAA^_!X?SN1_xE@$r1v6Hs*5CE(PUa zelov^KHd;87!Zm+)lZuOSO>n}6}>aR9tv47?6xSd`M1O?401fzD8&yH4IrmYWqH=` z4v-vT)Z>HmgE@4QlB{#-`cy4b(b%y32iKJd@%(M)_4vgSPJyuQe>;&~ouM7! z8cbz4Ewn`-xdGg)!2wsZVtk~`c`aQ3u5Euap6t?=F{!@Q>cCL9`jBp+f4;xCq-;oFScCs|I*t5G` zD;fa0P0=xK&-1N3=H=WTDgUpYc-j5x$3Jx^kH2KhuR$(GG72(#%VGq`8rgXIhS~Ek zCT7;}G!q9e3;UNPJ?;4yfofWFX3GCRe(KggzvQY96n2+8Ni--L4|+fWKb34f7l1@- zy;^SoU=oUDhZGPk*+#J&dzJ4HlAhm=VvX`YrL5bmPcCbbNF=rmbE{Zi$3Ro20(|gI z6h1r7nq$fYaGzj-9Wjpb6^}jvvW3Uk`&0dHECtw2=&lU&B-o5)n-oBXF(ZHs0XR9> z9Y8q&S`0L>1M6%Whb)6kkH7Fc><&Vs^_JyRW(*OWK07*naRQKag ze(2tL<4rd|SKscBYH&2>UQ_;ckj3DbVDS9&Jv2p#z_1AW56XWUcJRT1&Q>xQHxk7X zo00JEuk!r=w@=;brBohPDfr|i9@F->B zFiz)e{4k#6{U5GUpAYo46R$mM{--pqY)j1&%`wCEF6z)Czi5j+g9G1YzPv`M)gX|7(X+Wc;Wr zt69~P=ZHQb4;C`=zXb7z`i>FB-Q)ZQsJE@sZ2>XZt?`f|{{=y)@2U_)l z9yXuZGn61yksUtVKNtXl$A5r*bcgRb8US`kz;VC5$)vKm6@zkicE)}C-4ERlzVjXT z&YN$!C0RQgqm>F6&samn+W)LkY|6hl?64{S7&R>6pYY0>4C)h%830nap=7eP-|C+( z*fxJ_f4%4v(2TgkGc4oDCLAeeUXj3Nl|Km}T(g}_Y1c*%3(@OvqWoLTe*GHlv_ zt1dMu;c!Ol{wKBme809LeC`-N>0rt#pHUa=49gpH)p8xh|c$taw zs#gu8HdN@tx9~7$%T+x8O7_Q^|4znxj2f21Nk#yoAJ`%h`L94Bf!w}5dt?OI<8I%* z(a(XxNGQ}QIkPzpr9JSQ3F>a658UEpe!gow9<`oP@@BD8`(Sp z2kk6Oe^@MRX|84E#3k%_a0w);m1X?0q$#@GlHhHAO6wd8XIS@90}_)oya-wlM%ex2 z&Tatq!gc=^&5o!4sZY+t^D;Ca*+}0!Efqg!PtZvq-Y6rU%1DjtiyF%aq0-#*2bqj4 z;b?TnHZ^Asnb@n1%)slk>MTrezhV(2#+jAd6JToPrV31oVGSo@t0iR=S426(Y8#xr z(vRkAqE#iJ3N*4C-K9(uWJ(WJjo{r@22_#MW5Y5@#xDK1U|F;i4-nMT$4Z<9CqjtM#XAfBHLF=fVp$;#Q^U*FNf6Pv?WVytpl5Z34Cu{C{m8H#T_ek)^ok>c#8SxwzsL-lc?H( zur!$caxOIg&W>2;G~yYZT9vK3Vad1J#YqtTB=+YNz@i8di;^*!u1Y?nt_qM?gM@1JF#Byic6d)Y(&+qk3)NBh?$^}cRJqsm*PEHWWR=gys9Ni923B814uOpL zqzoKcFW`C9to7B;ITNA6MiNFqGv*Fw04(rAf3Co(Hg>>gS})||)O^bMM!=BXozt?t zSD{>c%^=ke-YxgYR-AtYsmap{tqdJ^?4m`KE7H)iE6&I&tiq1?Pt|7QJ88fi~1WXco$V=8f=O(4POd6ed368+ZU% z2$$;=18YZs2@U7|_Q(V?+ZpTG*FNJE9-RUIG&E2;ooQW#w8_}C+xkR4fj}EyFlUXg z-iQhBoUQj9oNW%ApRM&cTfcd}E3URPrygjTVSKzSdHFtZK9K(#@auXk@TEx|pHXAb z0YH99w#IoQK0QT7V`p!-rpnv93){{HADf9?#$5p%xDDARJJpaPm2&SvAF9E#BI)Sk zpdw3lH8bTs5U#K64c%Dtr+O!FP;Gb-MdJp{5M6+@m~d zOnwGwAR<)VZW&?1I)d()RESs!s~tr9ub>(x4iTK@G1Gi&Uiy)hkS>cx zCzRhV7FeOC3bS+-H$lylrW4N+C5-w?mY66 z-_c$ug)FYNRN#*W3xu?8eW}eCWkD$rH+sH5(dGO8ttaFKT^17}4J%3XN?;G96(?aKhJ&ve5sy7pns=~)wSQ0MaHzMV( z8P+He>WfXt^^+GJ6Nc_i1Yu6Q6*6)<;P`LOdmV+uZ?%(DE9O489ro+v@D~+l*xsXk ztQ1!+u~ZkENmI(7?q&rtg+a5;nLyXFhn%a1?vQl~G!*elnil*@{kP)isCYL_u?%Qw zqOoYGD(*$jE-2@4L`vXl)giNKfQ%VwylvAeDKd0#$5Fv#seS8p_Rxd$Bdr8Y1j8=K z|2*w@mpLvLe#JIdc8y1jDP<)kkMh3K7R6BCEYyZ6R6Xx0!XuYimR?xkG(SwryE~U$ zE8XohwOzkIW9g`Y>WWN+y%f+jodNcdQ~J9WJtu;a#U#wjZcmCCIc+X-PE(hH&e1S) zeqBFuor<(f{249OrTa(AJL1Fg7sl4YpiAcaGn4b~^WYm---|)%{EWJHBE()<%+U0! zq6)TQq069a3E~P#{x>)PiVsU3-#FpY!!K`!EwHvGmEK|-txZjyyL7e&LX5Pd7%}9 z#TrF=+jV#bA>5Mu@4Xh;paDqn{(w4bD#-eRpCnO{7;uw)ZXU3Jk;Dv1?$hj>_#!PU zmq_s;^X2=|{#<-jpcJs3Zy_%rt4r^WgW(fT1wJF|CH)$0#0O(Z9NvU%XYcEcD^d%^ zM=)Dw#$kg`bR^j2cJWseJ-N{pdzGgALwD}qRb-8EjKOw)iKv0CK3yDyj4mCr3yk=$xFJ6U6c!a;N9_C9?pL_>AM$nw2cXy z0n=;*J=`@pz4Og09oH~fJ{fJ+8|g>;6Qv_8=M^Y(&N@g1HR2c`Xp*YZ}WOAl*E<5FgNoevY9s8^e~%+=E!CRLrVTg zry-qZ4U>8cNXpFliHtdLU8@~l5D7hyn6}6SN=Y_AbO1S4Ri&b%XuDJ2IG#>#mRrs? zpgxZPhneeD4fUVLsYqIMlmIIbn;ejr59O@)k#vb7;b`?7eAo{^JvU^TrPu>HsQus* z7X*C9wu^$zGHw59e=7-pmtQ14Dma8W4H^eMROE^}7!H9l;Bzh&&RG|l&M@v1_?LwO zM*$hFqzXaQ-YuTI?DC6^P&A`T2+bjN1~nv#0&sD4)XA?g`16S%8W57;55_0>T*`+$ z_<1(G++X(!rl~ploE+*R5huyF8wIQ5mj>H?$l$$sz47ZNgEB@|<4)*7!58u;=}pzA ziM@yQ99+NbUag%n^`(~;IOa|dqhNd9A?{CtS&Hqu`&K3?OyGK20zf5=RlNfftMQH-9!>BK4&Bl%aj5M{eP)I8`aE^yrJRpH% ztk5jwBM@zX9`t+K&W8TS6;_gH$u3mMtU!LV&%_S@Jxa>*&*-P@GfCT@iU~zH#av8HMZ|j7cc3Tw0ofM$5hiyLNs{(9CH1heV@>W)T?u@+==wCq(rhv{MKm|wgtRD8KR8h) z)eeMnAePhG7lT_EzmRapo+*~jN1(}Me83-GQEs73nQGM%`qV1lF*@3#!4pGB5|Iq6 zK;BEZi<6JtTIFYp&R0ldDZE(r%x887pn}-JjpFg%^R-Y`UU@F$NC*DP}t77HU<8$l>u@b=?CnP)Sl>mjy6X8S{@1}kF zb>Li`f&gv6^~dWhqOCufoM+K#NIol+jZs;BKiJzP=Kg#8m4pOCf&Fz&+H`6YGLLiV znq6>R3b?KEHA8tn?9b#CooltD+w<)+}iXLm>X3{Lvt0)z8e% z*DFQ5-HPz5D3j+G6}5*0<(9eui~w79vk)oUy=pxGE*}zi`bP};y`X^RSKgfqJTZVp z#jfJoln3|M?a(sNBpVou0Ps5bgLW09`unJbe*!Jz-q0TibvFcq81J5}4i}#1IOi-G|r1+3LsF(3$@DIEa0T9FL4{Bjm z>SK0ooGK4h1zBrv>K<9IT4|%~CjeUX{B+7V*sQ1-mny-&o+00VO-*(ojA$o=&>lBi zJ%x<5@R!v8oiIKuIb^953|Dr*w9`)`*39DFQESCD&Hj7j`!Pk@9_aHO2ac#(GoM>9 z0{L<6(V`{GB7W=DA)Tc7EhZ&CCptESJTKKy{8BXpb2i>+^t`Qah8B5p!t1qo+Mle8 zmJQoAfo8I?cA``~Z1CHQkx%zq!ZHqCfgM~Ar;^*Yo0O6L>!<0COJ^wNs+6oSzN@8U zW=&0vW!Urr?_2&%%fP_=?Y2TLao5wfr)$wbo9ueNm7^|z$`9RcX%;~M-_IoNZPznG zT1bS7OxyZOdhidk zslKPAv%CDgUI9?)gR0&TPywG@bF1I@bY@h%u+o?KCa5mCVQ>7!v;$m_j_y|c7S*Sf z;?LDS{O@Md=H`r9@1HYq z23Q9En6v>m(+SFVtufcvXF}QVKXl@no;d@ws~4*$#lEdOKPvE&{K^+Y@3fOCNu$AZ zwwD6a{PCT{_Ia^h)dqU5JTx$8EeIC)y2xn8*|I_sD)+hY(q12rbz60J+1SkZQ;qZS z+5YM3S$7t4Yy8naCiBSloqInWx}_-y(er0-6RcKit~C(}i5)Y)vz+Ix_4~E-WC^#} znmpVEiZ^J$p^oNpEq_=Pz#;M!_UD05dYQ7J`>Lq-)v!T$Zre1E2?U<_Q3rP3&}zFj zwz}g9#M+&FMm;+TAh~W=4qF!r5em!qvK$8iI?(gF{nQj4CH(lUs8AI*ug%Yak-i#p zL}h3r732lc4Wtqq7bH^!PvC*32=!qIzgABFZ+AezrFe<}qhS5dOZJTu`aPYO^jmmY zXRS5UM&*;e`gkq#_VW&^!Sj1n24<>dKx;oI*CWe2p{vZj2u!jg;+f~Ruf)q-?kalk zS;Tner#2C=>Qww7xVV-9u;xl3@max8d3$#MlFRlTA`csQkl3;IyyEC=r+}6&{$)PN zm!{Q`&y3O;>jy??QwVf zO3N*h2~^%JKC8|?PiIa=1qMsAv-7Zha_j1S!*vXn;26ce+Kcj5OrcQy{Z>=g!M#6k>C?+XEaj}*xl&*uJkT z3TGRozb(4t)G5{oa{zocrH?^+e4VD%&6*^B21_o2jt5^%^|a#@GkcO8?~L@yw$G~L z(Y?mr|9Q_xEW$cIQ1`s(o0lH#0uQRv`sO&1Kd;H#r3kSjiVp$!Ad z%+&wIJz70K6~*f+*R{`lE)Rd@x>T*C zm^;#UK7Rp3jta+lJ3zNrOZgwu*?M=Guq#$oS!zt{Szmg1_Gl^U)S^s*hCe}!Wijot zwY%~YEEdtc>P}rtf5X#|G=6o?9!zzL`MTvJcCoq6(%+8|vZs_w2?3bC5+sXpkTcC2 z8GrDC?;!;5&F8zHXQSVT+adMRqFn7K$8kVF#XD2{lNgqrfh}}eVmRWhe;F3mF!`ec0JW*OI^YUk zVCq5>+w(_3SFC)kX}S^oq$EZ30qdhN_cZUST#=l~<(08SviXogRkldf5*#U7_>I05 zGVrcfq@0>N!#qVZ>-e-t-Wk*X{q@D}r~x$*psVt_r?uDR=HY5m*v&O~-aR)zzRZpy zl`BgrObT&qFfu-GdiUowSn;L)tqVBa4%syOW^YqhXO;Dqt8Em&vC(odm*&n=LVQ@!sBdweSk^2@nfonwMfu_zdA*O zlh$Ya_GDjQneA%1F+A4DH%9;RTY_?3s&vbVZowfOk+1! z2*p}eJpV1~8#N}p@4dn5?e{zAPcy{xcPQZ1LF$J(kB{dAG? zoR25zrAZS~I)AN+%{FexZ}jqohmTogwz2j(28ccail?BO&H78*3`MW3InNuKT+lTC zSTpc$XF+m+FcbdCoV;w4O;l};4Q{kv&BorjDCrb`r=Z!GZ7C~5?H5(@KI84ML2}(# zM(&4=v8)Q+;h51Rx=Pv>;b&1=S8T&>VLV;~xj3D%iUo#qjMKGzip}-I=b7IR!#M$P z{?5E6k`eq;h`}DL7fD_~~fLZ}DnIQ-b{9^!EkHFa@@T9qAZE~o5_O|3};QyV|k!vuHpdv(s$iD=2ct0tzEPH<3V}} zoO7Y2{^bBvCjN<(N2V+_BTAXYNGhz&MvcR;G@T7|IuW~LtqcrZse^6T)@-E6>o2Su zKFI=gJ5`x(w+QqPD?eFmId4WvWzwH@GgXGP67{KX7_a*+ zQEL+icx6K$WNaG}0Bn#1#8d^g8_y6%@RcRZB9>iM@OGe-yV`TK6MfWa?=LS9_1Ui4 zYNoL~bcNDG`yCMLDneO)n}?kiP`%mfJ!{(U)g{m5>YoIadbJ$%wvYmZfS`j9eb?&b zf$xB?G7Ox~2K%8Z5BG)7WA`Cjhye}N5&wj+60+Z185++zH&>(BV--QvZ6)w&$oX9E zk%Ft<*;>rmys+lcOTV3H4?68PdF*Id9|s4WW`h0Ot^R>f)w&Mh)+U9cuO}$y*a@|p z3i;Qwjh%d9miM!46+FAow8K!cWai_+;GyOpp_Fc|0wo93e`pO=N5h<7l0puoGUEw;z5DCBiKHrR9*vlNbVB4!59+ixj7x8S<)!pAW!ZUNRvV z?AkC?JH5n%=rE*DmNGl%PLE7G)<$T6u^hHrZq&YhyNJ078Y=;>s7-WXQ~w^JA^W}3x$vCHE;ygO{V`UK3r`$IJ)JB<9#b$ z(qn3EoT6d`R9r`{QLN0M$vP^I%N!`))?FZfY>Qdv`0$%k zI{?4FN$T|kYpB{H#e@4(#Xb_mj2yfk9?RWF36b6>qiKtPxMHhMl<=cPuuHQ#w5yK8 ziO~!eV1mg%1t^Idg8-cSV-5HWpoSlza(Qp#p1R-Vd!o;-tm8VdYIxVa{Uqv(ijGGO>mMQ;wG6X4AIUARc(k}~03 zwT>SW__1RxOx@J@@|=aXYl{Y$_uy3Lk07uh2OPKb)!AT1JW!%=*HTyK4nq+)-Q{6} zU1{OX8$U#PTZ4+~0=f^)zF*u)BY%$^T|<`4nh<2?Wo!a>Tm@x}sjF3zX>x<}6)+RY zo}}6$y8uJ)Ep`k*-M1SKc#M{5yeBp&d|4;vO|gA1)YjPe0)>_3uS;Lfg*L}YX{c)M z;*rKNwYqL+=2Hf203NC;I5|mg4hF9FELJU?E=2$j-6Da%^IEEyf z!N(bh9aE_MS^n4hLf>WrCjk~Z+B=_i02Q0$ZqEloM|dZIhuiPfI2xjey#I$rF6$XU zfvC!UU+=Y+GQ7UK4OG~23vUx>c(WR5DE`|OtC%j*d#v3p$e}(2ng~ zCh27cL<2t;r*gH=qjSaD51+az{sRSbS#ne0t)_{t`N8vgv0K15%N0wkQX6$?n97tn z6-WoJ82oy#SaV>x;zwQ&hnQ-6-fWP#gaejRQ^UG`S}$+IBi_$Z8%yyOI#=ZKtZMzc z(XtR5WmQb#?|CG3EBkjaGeUvn1yl7NvN~21g%`$XTyK!xEB^r*|(l;!K`T(oCSxcO4=BeAFHc+I8=+UCJ2_i{_1v9X!6@ygv-c}X)7bp)|_ zP5gqPo~ub{jX^j)eoH;tE^C#K$REE+u1c?O&6aO)KXiv#53KN6*Eqa0(rjI6IDj17 z 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

?+l7 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�Kv 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 ihBCBv)8__?8cC&XLrk0D^;$L%D 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�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 diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj index 9df3ad5516..12505e73e4 100644 --- a/osu.iOS/osu.iOS.csproj +++ b/osu.iOS/osu.iOS.csproj @@ -10,25 +10,6 @@ osu.iOS - - - - - - - - - - - - - - - - - - - @@ -79,6 +60,28 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 23419a0ec3f15d821d33e041f536236951adebd3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Jul 2019 18:06:36 +0900 Subject: [PATCH 1729/2854] Add drop shadow --- assets/lazer.png | Bin 77579 -> 191397 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/lazer.png b/assets/lazer.png index 19b5fc2151e570bf321596998437072943422c26..1e40e844ccb4f6065bbdb28a591af35ed78ada35 100644 GIT binary patch literal 191397 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

grX5kAnI7=yXit`@W>Pbv5O3$W*%9No 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%0D!HvI2srO7R>s0u0X(o9pX;k-Xz_y?7-o4VgT%# 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.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 VCItYF2In1!ZDDF{Qek{8`d_IH0SW*B literal 77579 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±JV~w(ThIDK>wg};C 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 Date: Fri, 12 Jul 2019 18:24:49 +0900 Subject: [PATCH 1730/2854] Update android icon --- osu.Android/Resources/drawable/lazer.png | Bin 39498 -> 39318 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/osu.Android/Resources/drawable/lazer.png b/osu.Android/Resources/drawable/lazer.png index 075a8e7184ad508cedd8fe9ae549d8cea696b51a..fc7aa8a092cb616233bc83a3328413740bb57953 100644 GIT binary patch literal 39318 zcma&N1ymeMw>CV3OK^90mteu&A-KES5G1%egy5dw7BsjsKyY^wdKL$Ed5yp`j3?0000q1$k)=008)>2m~M@{5cr8mD~JP zbJviQ1k_BD9{i~wyUOdk0|2Nve|>>~>@S1>035x&mY#>6in5R;$eG>T3S?o;?(OXQ z#~J_-_7?h6bhh>|r}TDqa&Z^(7NP#zLg-KVFEIx-<=-YAjv~~0D(aL{AUA8ux9q&^ zoYbNyl$4ahZdNuz8q%`=hX1LFP}_NUxC(J_czJoTd-1S?+-y0x1O){-IJr5vx!L|$ zu(|uVc$j;$xwzB(tC9a|N7~xm(#_u0!ye>9`B%H<79dX#5o+qc9R2s_UvYZa+x(9w z7x#bD`a_W8FAWD5J158g2DA3I|36@VY5q@`mF0i4a`kj``kRxLC5N?>wX?O0hx;EK z*Z=D7Pj3Gk`2Pvg+uZeE&i+o~zqkqi6}gaxwL8ek^REL-e24 zfBVt@KmGhe^ADtzrI5R)g@d)F$3M>3pR@M&)vsy&f#V-yVUGXRTKLbkAfy7avbXV( zHuta=<>urP6yoF;;^L;{68NjY$0N+~A2$Dp;4h7oo3*(I$W03bauWTUhVx%lN*;Cr zcHVzs|7r1WurSA8J?S4k>tEgWZ|R?YD~j?5`Cs<{QIzf#ydnTV9H1aAq2&!c?n0br zThiY1IgI4!%)d)*$PKj~Nv8Qs_!~e@nkfT|CgNhIhtpoL1Q6kg%RMmHi!&FFo9@cb zI*2quBa5`}<2G}0tsTsyMI@y~9BH;1(?M6$=Qs?wSU0?SJ*jXv4Y<)3&~7=s6IB3# zbxjwlh0OB;Zr5$|0+QWrA*}F|8k_%rf8bR@_Ti1&3Eo4t1WdF(WO#lpIuho0+iM@W z4?a2){9NN!*I{%Q#-2;++O>|Siq)LR5KGOUC45-8{()d%$Y>k=2>P9*gQjLl>=fa& zypLkWQ*x;yv=$T-bUSpF3}cJeb5*`f+u*mF-O+<48?{O+wCy|7gnjI2glkN14*uD; z4qvl(d$g9k&ekd+aYKD;DD%n-k7R;55RXgsng7~(LS3--pD z?}d~LueZFQ7|qUiH=&sZwT>(pnT4*Li-4I;d610T_&7g)g=teJg>J+>PTPjsB_U2i zYwz=`ggV9`PKpw^>PLa!ND@p>wxA>n+2EO*A`&{~j1CDgC{x z4f#kemt>xW^Sj}#AO+XeySzcW`fnZ>9UuH#sv-@IdAk>Gp1g(?y@hd(j(-ysig&k9 zViI$7JJFr*eVecSP&D$6_S**ifnVFvSoqXpG{L5@k9UIlN}4FnAb8KmBv|8I;L*0j zc75;a7UF~P+0v8B$iEExu@QHb&DXsH7X}*9@cjqwu+9X!?aS5}yGdNFXV=Aw)hyU> z{FVOwCUetu8FGlLRWE8I!F5cRd^jVCON+4K`|Gl@xIOLxGl)jyF>CC{ z_E-)e9nqHy#op5_NzFwQ`m`D8qBj@LmtM~?IoD9h(aqfPrG^ADO-fB1va)AtRn^^> z9kS_mk4`i$SL=h!Is@YXO0^COPGNhcCu3?Q?a7E%_)(f%ruZPzc6E^!Q1 zSDi|z$Z`FNDCl<<36nd&R(wmXN*Aas+G~iLUYtmITy~2D{8zU24C)1e4qZiWz(+pTE+#QwV85 z$)|&CP25kglfO#6eIGQ0=C^0S{Y$h9bnwCL!XH0d6JQ;j!V4nu&g*QRyQDPi z>2uiosqq@8YZWcN3z9~?_hG#I%KCPr8B#?6{R+Oacs#}tfc0`cRX{c)@-9I&*^j8J z5r9@5LQIvgp6hUXE7re6prCl;mm^(nz2J&b_k!g^DP&$#44#&aodWU+dmMQm3SFPUYnmExy1CkL*2Uow zlaild94?+5F2HJEuGQB1Ha%|zS?+~t?oBA&F5bOl5x55WMNZkk&F$sbRT{rARa(cT z&}-lhmSv&Y_A1R@tE%?d;z?%@th!9Y$!s!cheQRf69!1Y-P_h9iL%B4NFc0MjOtn^ zSyLR{L*C9;U%45A25%5Ik}}gQ=s8^9QQeztj-3|e^a!Vzs%-q%2h z($BC2!k&Ar%7wD12pt&yxxK)Nu^fx4ISin_Y1+mE_BGN=h&G+l%jvFe568yk1*qO> zvWqq!ZtY#05SXhYUC6J3?&_>eMniZt_nB%{~*u z$G&?fo-+?m9vi=X_f9{|m^jPjb81>E$ZqeXrK+tOBAYPX-x;ru;r7xSa6G~?4VgyY zas*b7vF6p&24v0pX1Q) zwiq0ba3MdYFsyFZ{dV!c&&(07)^0(1C)r(qZ_?Wtm}!7EmrY0UoPyIa`=zv{+vBS~ zaE?C<5$t$GtSJo|Q+db*hyH+{FRCy*sw^(tl z^ULAO72ME$C$E=N?BOU8ELUQfZHc8|$Pe$!+STRF*BJE9RsyQ`WE=+3OL7|=HvV9^ zSDULtDP%^NA~RL@GZcA($l|vX&6lDW-3k@sPCbycMqiGa!yk$tWq^O=S;Uu?0qMP4Um?{H|P7$T5h2^~;JlTl${Mw~2}4UCPs& zaelNg4$xNFH*e;B%W=)Qu`5siqJaF2KD~o$hEbrKia~OPh z%kLqRubEwm!Conz-m8)!Pi0id9=52276BUzdw$cW)-WhkMUo02*u?9#yKHUu6WdFF zd^OQ|PhgZ-3?6&^*l*L(nJPJD5U})$$4}^QN^MkOsfYpVu`qR4-9!-@8ZS>#K!7vn zCAY~BzAVof?oUv2j-J6AY*F+*CO$L>%G)ri0_1tmtQi_6AK9IS9({}B^xwfN#q=dm zBofEz6W5$7vBBH_s#_{^5ZPU%Ixd#~?nat^Rjvr5vK~4TH$Z$|j7GO|#ck9nG%HCq z7}`X_Zt?*xb@X&}HA+EYuEjtTA%4ETw*7)X*Dc4?e>nauzyQ329hqA+{iKNE{1R}l z7?qE~o6w<)P?2KyntTM_mgM(8p;`h%sGpoY;md3ej?#Lo?$tp$1d7qTL^nUDPlr?P z^JK@uHuBy+tdUdHcbid(20jb7wBBX(3y7s>`t$NL*Ec7JPl?-kBB;L0E|%G&!gE}r0ciPK-pRX4{Zx!5lz0Yq?M$8iSX^{YR@~#N8(WjOaCiQaS&?;*MY*fxe*YwrSaRN>N5zBbjG(%+BQJY_eJ zfN!Yw$TciOl8J^pjHTAlCdNtPWxvjm%z>TT^?(9!ZTvu1^crr;u2Or|Ic{K7smKyo zXNcN=#nZ+0%6gJZs?JeHysZla$dwrRL_hS_;0ipVdjTpH=n^N-UbtlE@OYlrd|F)( z16Sa04OPi}?&sBF+$HVXz{m7AKs=@WZRr2S+e@@SrIOs^G|6MdZzFVmJ0h{BdTP)0 zt4is!4w5CB10P5J!75->6Y=Ytw}nRVT~cnu-7>M8+PVTmto>Eg;;skyt{_xK;92yi z61e=m!TpxiZ-&1$a zJ^Ol&puIAuBa%xFL&dnW3`Yz|`Ofr9eKYBYxgt-nKmIPB$O4HBRz~ z%Hci3tCx}oNq>}=YaSP_(~Ry#v~i5Vj&)?{u*w7#7Ij9M`0~a zJbyQM55#%|$K7%S3CE3IU1V*E)KZzO-M{};^{N=qeFTdP5)F)ArHcUd&pw*7Z8pEIjipd`CD6_f6jk1w|8b!dPivuKe7mIb6*o-`+ zT2LKNjjYZ1Jl04eeSPqLX$^u1Rq%MZxw<%%{4Ut*L@L5iT#+1~7eDo3ECi_JfPPj6ejXb)N9E{C$LdU%qCXt@wYR+MQV*Ox?!>*&Vy>_z@BRKAYmb~*%8?( z3U3pp;QM~$YI6PO1Q#DjmkVvk9m_-caA}{~$Txwr)qD(K&jiW_T-?BKh;cK;t z0E5Z^za}7pacH`@W1C^r%VBJ43yck3;GdzP8jy$V^%gyD@60Rg%s0!u7R=m)pq0_B zCWiW&7+Hmub63}XVe8TfEt(PB6V90k`i#0b8B6cqGdt@gkF$p3g)WbsLhi;t#@CBH*JuOGW0O zU!^REogfyzJmslkudfAB_0~k)Tlp6pC5V>49IB#%A5@QFwVY7VsR>aUqql^!h zVX|;V$iNi$QM;0u#$avRU*kV?y&c0rooc5lLv%H_;BAkdF{jm%C z=M4%a7E}c}sB~vTi>~CQW3#=mSTjWo%l~%D@M$<>Epj|i#VUeCDTOB_NzQ+Y+}V8r zlk=s+;cbKd)beAV`)65Icm~yQPeZ9)c&iIkpPG7@d?Et#P8w`d{EYO~Qe1#yK6)ey z*qJsgom?>JC5_!T12^PW@C*Ccu3-*4u=if4V7QNwbAP{G{jBuC_yHwANw7B9V zlreY&_cFRd7EBoB+k!KSWEXkgU!2Y=L9;!i>)@c* zc~;q0)m&lyZ{_vXuJ{Qxxn=ae*^~l4@S9h9CMLNb4ZWjajOL0fxb+eTbjktsSav5( zR0`K?m#Ce)vHcu9i+kcE;GTJG_4e%C!i8`>n2i=geu}j;eDUYwhP_tk;F%LvAajNN z7$~iPS`o8pB+S?`$T!OH!I(_2R^+OoPc7ibwkKeYE51dv_)|O)!A>e9)T~jPxxZ-O z%$k)v_oynkGdLZxcpK0xEW8&%F_6Q*iYDF}mgFY2KPjVr5^dyaokA+f5A@V` zjNy!6l*=>9@zVirN90|rtIG&%4zv1#itr1fdr%o)6E=QtiFK|C-rDvrhkNZW{ZLZr zc~`E29PLHajITxl;#aUm>_aFYChMV|khCCf+gp5%SZTNy{pA&d(EKfDp|2TD3MoOC zueyH(hhcT}?9WW%G-yy}y>kVh$p^?|omIz3_$C%xFMT?-SGe>knV`=!%9Kd8bLbOa z$OseX`o&4xUaW5WZiJhOVo1I9*bt9oF6bqIs26r1t)xXC!FL0xpM96%@z8QnPg?Ap zd9N|P?Eyg@<_knz3KxYDxJXS%kxh3ldU4ypyryPv8y@+FEZhFd71wV`+~49QXessG zL?wPNK4t6veE5E9f5O=Ec+Z~)S3d(bF#4y3$S;8wYVsis%6j^GnJ#9c0o~!>!!^+W#_O>J)D{gk_tr(URFer2FdlVg9wU zsO5?NxjLqrx@59@Mi-+aZKLDuFE^6Pd|;2Vwi%2zC#&7K0K?D*>(vX^8`2;d6c$5O zILs(BwyF?M|H*yx*| zsT{pzj*CAProwW+$mhH|dzd|VNxsjrZ9R1WA+R>(zB(`CK>2xjY19(b>jV8@-Qzj~ z#%iTFxmM1vYoBjzPpX8kXn6ZMraT_@{aexk-k{nL;!@p=XD0BBopD2u6mb0Q@T9(2 zs6@isUdZ4=jA@_4VPB7B1VDc#*x^OwqtAr?AUIyavTf4muy)N`vx!gL(Z_IKwMz=5LOoPYoLeA~Zc;y6z#hLU#jlnCq@|W_`6Td&X)4H!mpx zw8uRQ{R=e=RE3wA(CQllD0X7`Z{YT5ZlRSPzq7QfdQu##)r9$R0ILT$8n5zfA z2Nh8jJivkN?@S)`*c$iXsClyJS82t~28>81VEF3?~K7U z1pQZA0VyGqN$6_lpO^tHjklG=3;{Xu8Irt@#&*Bddz5L&OS1}{{kL+mH;c?k4I+sm z#Bkxmzxn_ffg8jYksl8zuMQ%&Qqd6EQMq5k41y@$U(=|T3yPoyT4mu`B}fjF4M5Vg zGOpLt`8%aef=a2jr$k$|k+%bsOzxX(i{DDFQ&XXl?VO$l(A)3pSmX6P4fha*^OORt zpwF!&tk0Q(@^%9k_rJ}M;gO~=tJ3XQbvTkO(Z`d%^?1aj;?S@?1#5 zms3C4o3=13FSHzdH`UG$1#POWVu}ZMU~VOLUYe_m!qUUN{qA~c5BH+#!wlVDS|A&~ zhuf5>g&PguunoqZuF%Z!Rrcj0a)t~kCk2? z!kWFCwMoe-G#mTeL9_l%gkuTbY1d5uX-^oc_TL7^;883lx^MV*kkQ{^(L#dL-1L@y5Nk{PhOYUF-xa1OP?TQ#z!me(>49H(V5e9!+`eDejQKTlAh|bm|KQ7 z@%rKby3~@X-U;gY@Ej61@2~-IznPLKrqEQ$Y7jAdBR~!}_+9huM(Y6Nj{f({C^n>a zuc&z5`?)Q1gQFof;L)=B=OLRf#@2JRW~!S>G*`UTV15jNg)Gf8m84E&4%!-1?WBZ# zBsx>5m}D+qV1nAryH~dBNm&Z2#y2XUT~}V|FL*vL~RkY(4^ZYXDnCa<%EdUgHcSpAVzAP(R0n$K?4w51IP*$BR-?I}+rr^#K-_ zef=Fz^?=$ioA71yS$$Le@EJfU3t+W}Ikh%N8ohmrTzTjyi&F9)nTH)}f9;`5lZw}_ z_c7~D%N@gyFh)49?!jZ}GXl({gU3f}tyANII zqU;Tc^p#;2*fue4d-bkTY^L^{ph4CjX%y{+tf0{PcL4V zc@!T--~hia$&hPX12TlDqFg-f6IHRy9?$l_n_^+hoNelcsNin(+j$cx9Y3OGUJ&G` z4(yF%hM4B+NKb2t`j^i^FN*65Ul?X?V8Id=xk{%KtLHQSV#p^*Hon7FjTD|COg zE69ML>BLLnmp`Q_F@?v6vkbcLUEh|#lem}V_km)nVuCt+qP`h9!XC6U6{5<%ch#<^ zH#HCX46Nic`hQj`5qwVicdl64mztoW=%W?(YbV4*c^uL+Un7+|-V6m@#P`GWjFe5t ztnMKGXopcNXW6tx9$b!OJbbAi(Rgn{)|rz7qdxZ{*3qTJzsxL#iN^O*!cAntC08K4 z)gpF0m=`qt_#(3tk<_VRavhL+CV4raSdQb;9i@kJ#j2!hKX(~59r2uf4}-Kq>#w_G zpN38Y%Sr*ggjf}$9Pv$!I%_=VEUbqvne6 z(I4^$g`G!s(Mx%`+gB)TV1K~1H+Nff6HHukNsN^P z`m~_-+qeTV;?gGx0@M#Ev%}ZA-r(WAMZ~rHqXa~?Tfr0^aWYsKgQnd%@Jd{XMr1GUE~Lvd-4x`E5@uixv5#iL@;_NMw!rA{~U@ZgZkkpFEoIx zkmfjcGN^f=Y$u4S2}-q7TQN$#0VE0FJUa71J3m+WYNZ?|YdP#$!)E~TzHA>%I>Q(v zpfst&3meAgUZnCU1#HQY&&1%o&FMJ+i-{1;*^#exVRIdA?f!|YW9?E0ZlJpc!xIK8M3c+x^QDAzXpRq+A#kXQj1 zu8e?(b@KaWp_S8F+r|c-vCw-!#do?aCNYsUVq{1y3n86&5!b;GX;3!u-8Vnu=f+(r z{{1HctUex%nU=M4bc7%(@%NP?N^R=-^JHE*i^5749jq?l*3VUs*Lgma2d90o9~pq0zeYQZLHC2jHyJQJmP9aiVn~#dpTHNzBpYl)6vXv3 z;z;jdI!1mWXm(-(mM6K7b!A}f!V|(HB(I7yoM}$s8&C>}z%yyT`WDUw{D5wC?wH&? zLO?#;Y8cmralI8=%5-i*`CazpW5K(m3OmmY8x253{xF%~4NmjwhfIe?O!Bgggp<0` z5%fwDmurRUizrMzI5HTN9>2AB%6yDW;j~jdGU>px*mXo}w@4U?C7-P~Yr{nc4!!i- z=RUkKbG)q9o688iaCreeZH{j#0QzQ;lqAvj_CHpr_u&geI5ze5f%oW6+v6|cqZfHv z1XLQjsfX9R-F*p790^t3?|D+!3npVQwIsM4C4-?D=JqdVzN;!f(IQ|bfaSsZ8NVih z)ocp*+#~6j9eaqOV4Qnj#NVB)0=R%BVLbhB32;Xl7Xi(9K|9t8zlXTZ($R`YM*(y_ z4ibDs`WblBXW$=HVUqzhtcq(-L?!Id4=u%AZKVOPDBB|Ri+4*Hr9uug12eYs6oqE) zFogEx0SlNOQkkgqUkFm7Hh=PttIp0QAn^`x@jeSPSWrEuHHPki{Ae#isIHG`{Uw4q zHp8oeE#J^FPxisQi+}dE^NH(g(g9pp%3&xN+UJScynN%$Jf{l2ExO65?J)@Ys0LXh zHD@rW;=b;}7M~v5RQA*yJ!kS$)A#oo?eloZ?c5bCR{AIOU znkATB0ZmOspj@v1aHq*&lF#jrUl7hcV;o{Q>2MP9?xb_>sH54uyx z>|R7f%nI`rFR+B#Z6m~~KdT&Je#OP=njXu#Bd zuaY%9<+gT@Fi8$!u{aGHOZ$G53Vr2Ps=s3iV0CNs-S%_GFWlg5!Sb6jW!z8QuY<$! zqOa-}x2cqiH;#nPxoiqV-980VuakpE$yf~sA1{P4maXyJZUqz0RP}%>hw$B{+UssR zTr1Fy`HL2j{R4hwbVaWoH$*G=O$^En?j8$^6;7*|1~`Od8LN8uMoqw<$*s_~6w%m~ zTAWG95iY~V)J$RC$jg*n0@!wM_aSdL%P%;of{=~Ce>ZO>vw~ix?@X4Cvld^#kl@!I zHMeKmu9?!~)`p(aG=uUq#7vDNy55PV+99E_jv?c7P-ER9c6ThB$5m}et6F8;NT!tTU(wJh+ZtV>mIj54NJ zKcTq98zu{d+uY}RsfTW`UWY+tc#yGNXV;kEC$iH9K;U(@&K=*2`b>S+uZ^9)n{7zS zls8cw#z`ypQ(nsjl+|f`fxP+yi?z0&D0Y6R_T3Llr%2{YSX)z-VV})hZO!^D3+{`c zJe{HZ$mn#MhsV^^lsaozB%OnI#)HY^tGXlTl{59xl2@`!!(*lGBKqMCk(A%_(Js(0 zp~MWWIMY5j?QhZlz=9o^aJQ-)kSDRLS~R(y!u@WX!+KZP?;K+FTlF>j%t&|-JdzQy zwf~Ka1U;vvL{lpf{xj;*g~bSGKBlFrxG@cKZWX7MYA~M7rRai6%wXnj^G*TLqFP?7 zDi3shn;I)BE68p_NL;hP=I3uyO`;($drdgpJ|Z~Yx7F(1)<-BM`tWeVJ=Md?rzk_s z2jdlA)Voc91Q7AHe7Idfv{pt_gR7AwO_%3!cMH&kr;x5SF+SB!vbt=$FL6VH0Gw7R zKc?f<_#AKf_#6K@s;1rK>aNQ=$Q!F+3?gP2lRw)l+?;t2GjXQ3w=42+=O zrAwm*=HIJNzeB-3jd{FDVMyqR`QmutzC4rB-3{h-Tv$I?k;eI8p$x)>3GU0pOT-D! zvcF)2oh^N*4joP-<1>j(l|#(3jV|51jJ;08hBJzpj(;}NM{WLs=+Dj@ka;CyZb1{xULG1^2y^?U zEqibzTQOE+4=_kRj;6or!$JbAn>;tn`!vK$k5rf7t!xS!sA$SU@G%m06<{^RQ5Q9d zUw*A5OWQnc*9C6Aq4Txwkr_u-_}kG^@q4eWNSOWh5{WwAuF=t0w@h9B64t5uyG@Of zs?n{7yXj(ujL3F-?Df65YjU3SQ(&NqxsLVToV|w}uugtB9RZafJdZ5QZfy*ZnlMT< zJiQ%Rh0Q=DqXSE21iT&uE$Og5Ij?Zk794U*Xd0L*e)6Bv9B~_blXO&g)_>=t2Q=P7 zqnV`kHimFliZ^~ZeGo8aIZ)5mni08N)3f5Kl$ONdK#Ah4wX4G@gL`7f%!xSfev`=1 z4)jpq#KWg68g`%zX9*SvtW|lIP>K+E6(@%8*+w~ew0!sCJI93C_7w@i{p2H^YiV_` zzG0BvYU$e{2XSQH10jzQ9&Hq}Ki4zRk6dP84+F58sOPs}m)Of5Qf=aW!;ZvEd|k_7#U3eNP~pQKHNT-H5_hS@^TR$l{6AUQ_MfqzU)-ox67!^=IUmEI zzLf?l^NLY2Su*#`z3C%wMgkZ1S31_zvS0PlVcS`qn1MG7QW>32siJO} zeDT!BeHHzViV2nqKvhs+o)`VNKqj5}Sb6b^T=~_Z(N0_A-31y?gX-KF9J#em5A9d( zAbr`aJ6q~0<=d5Z8~39Msut)db)u+L6q}hO{S2guxRtQ?O3_NG=R3v2b&2r03lMp@;=x4v+Fz0`9B8`yjuV87Vwm56;oOKUD?!KRM|uC=7VOBvvT zOQptJIX&7d8#iKu?riy5(+k^E`9ip1oagncj=-;a%4461W40!zR+ro);vi?Wy2Rb{ zmIR(KjC@Y#kc{5kyqQtG1%;UpvC=PhRPcg4M7If!s znliY=4c+K=C&;DWo7F>OP&!s&^5ru#jXH=vDl(dnOYW`$x3rLblPv7wHJ0UldSlD| zaE?!yCgV;cc+;ztGvAIeOFU!qmb2p2z?|F6si$Jir6>K8{#a=NKMor$S3CQeW{;s7 zJd2VTgz7lQ)MOV94T_#Emv-M1otzjyE=S_DpqleY?N+lfd2=>b+rG3Qv36I{c(q}% z```zao@UN>Y)>H{tvSagFM68yyuBQ3{LTDqXQnSA??s!AO{i#-v_@PGI^9|)@l@5c z=<}1*rb9t%o0cZdYcvk3z*L(n)bI*J{tQ1&7p9$5anhpSb?NzVnwreiN7IjjimaJ> zTXj~70x-W4uJ&Q)dI5QNQ{kcnR$%%%JXod^)sD19VgD@J>Gf=YvG=HJ20AP4CjN1# z3rNw63raj*AHxKU;nB3a3m~EA+{DaQfUlqU1`8jh zy!R)qT52S?d1gNK#6-eYKaKCcV+;|>EhYg7HH%WsP6xczLWdok8r*B0Na1IGOGO1& zY~}z2(tkm+d`wvGU z70rJ}{VF|T0aXIOpGFtTt&(tiFB;a-3uuH2z39L2gw~o6IO-r7BOB+70xdq@vZ`et z%qb6}-EdP&ns4q#WxamO*nKO;?y5$_@>=l|eH7-o-=VPo74V`8P9pU2UY)}TKRS4k z_O$Fw+)CQSPS2wokWZtkGL^*`RY%`mfD(Sb^k@kHqFs=4&v$ICN{<|;(M{@@S&Ngh z0_@U;r{jlRJpzkqB4P_PfUQlIOZI1AmMWR39vz$kWR;ECpER6KFA>O^uv%8Pgm%2| z=ui7~73l-h1Cbp3=Pzj!$}QQ;aa9j)R8)CA>}Y*~S$07|B)n%PL+aSLoYOxIwyRbNfWQL+KkUw?k7pl{A}1+nJkN zHl_{hB2(JZM(2inDuRa(3KuJiLMN#_Gqh1R_z)tftn(bq>vMB>j~w}ubof;R23naqPOV4B48N5 zQOdT?9%BCN1#JHDw>g3CKC7Q?D`V5uI)E^U%HgYH5f8Q|_D-YlSJCir{iY#<=Wtw% zP;H>hOW?XQXAu*kK70>DWe<#u`Am9a5&ayrb)L zfC8!v+#}rR((SUI)DKsRm1*h0H7^58tB=)p3O=J-qrg6JNnlB%Up7+1dH2$vrLl{3 zMV`S)nOz2Jerpc6?H-_DiMbU@&zGoB-q(HosjIyM+A1jJ{f-f_LYls?;b&)32 z{Z}C|)WOhAse8JZ+d`;Q)kbj(8(wfu=PY+|H#6@6EUMlt34k!=%efm% z4@2YgNBQ52YKz!^-soclPT|UoNj(hmjtlo&2pRl{010m6uX|L-3AlzVVns$IsoQB$ z+gTIwI{OQ|Hv zRg(~B)Jz|V+l{{%wMeOM%QoYk6D^kR_uF*-Ap@_yrMz<|hNKrU`K83XZ@A zzCKmJ-rw{aB{&0PH%QkI@cVv8r#Ceor5qNuOkC39xzQj)Gdok=9LUOwm*6}*CAqoe zDapDP;#@6-fI(V>AI}kPABj2 zhjE%?CHUuU%=ghEeu2&OjVHyH^X$s``n4UtQ-4P!V(rf{ z0y1EgX<`SMM_6jnYt13Od!~H0oA>~C`->^Fz08RZQU*g{NK}4yK4euWD7>Ry0Z0x* zP&Ki%jNQfKP_uf3-woZjs_M2WFe?f9C4r~8L1>rVbAZSiCKI7=Xj;4KoW?tDE8Zf7 z*LJ^~(YkZi&UCPmLzIgDULb3Fo-0C5V&FSW@txjC4mYNW&Fxu+K6B}5rC9gf8F&Uw z)C+8wM$L)vH6p2hHM86Ab^XZrb@WPEz&T1!9@UsXT-}^#Gacrq2D+jZM{>RQKr<^b zgk!qR{n*!XhgE*`lJ3u|FKdzD*uIi8q>sxVvUhV(6JR);Uf4)|=q_9bRZpir#qG^< z?a{>#>@3&bChqTEj7O~8&avPK$Eu{Zlmrojoew_VNaB;MH9%DkBa<1Rxy-?6`=h-q zBKjzk#$l{-y0Qmuz12xdc)>@t#^t0tfZ{~ydND)Gy&ws3B<5oNET|z&qa>17);Ui6 zsyem+Z1n=OLfn!E9iP^w^^MxEodpAd$M{BJB+-5(aWHkd4*87E+_NTOT%Z0c!m-%?bP7KyA z=?=jfZNXGoWRZdK=Y5TqZn25NEZDG0Hd z=z5+Q(CV(JH!3u6el8=T&<2%$L52NzuHpZQqQ~Ywi8LjIxqU$=gAu@WPLA7q=1C?=)yw+pr;~iD7y{R>w9x-+!w8eBU64Xx5 zm4QW?nZ=Y#+T@4PK8%~M zDNuVHnORBXp7CH(XFj8{c&=*plot0VC6?v#8;qXfGG6l^X6l;Jl^^rrV(-+xwkqo()nNshIAFaK7Zyo1fC3wY0Z*Cr~Js!ksMT8uSB z3^Y3y?Dck>RC0JZQuei=yic>7g-cF9QrJWQ`6y7kDyNFyA4DG0nxQ2!EbA|+r}tdG z1EGM3tV-V0bPTg~9#-_*e{+e5hgceqrTRM;?8e8NGz33rdS%^399G zVSN@QzZ1f&txs*n3M>rRBGK4l#JCrM)9Bo1r|TpRHIwgq;8e^L#2Y%FK;kzDYX7SJ ztANx=1z=AF{u4OnPo~jD_muYr+2t3D z=-aHjuB*s?CDqDrI#o^Qwk+L>9lqh0UZ6`$1Yf@HMq$Xr zikg39S8F!l{zx%a!kX3$Kb0#E`g1Kv;jGbH0HAG3gYSYDF(PAsjT0__BZ)Eyizcp4 zFfI2}+bd1#^H}&awkj^tZnFyd>{Crhh1R2ut0>g&F>K3oU*V!yGWL$7J0B-+En&_6l@FRc-! zCYXVdpR*=ObB0X?|5$$n<%seT!B@}hB$EN2*F`pY-8kGu&pnMbpzEE>bC|$io0_4be>RkF|P`Gj}zeJ7EUiuJ1MG23NW!lQXGqaaA`f_-^tgO zF7I#DSwi{o{DjF#7aUWX-dSrugjx$8tPXfS)nEzsh|)DpR5rL+zyET{I<`bl5yJ8! z!XBHq7$QRBZqinOgef`0tRYgKXzwke_`#xDuS;7n6PK5e7luc$O5cgt;JBg17-jyq zQFy=Y^tH=PZF|~eqzvv;VF*&Xc=;LP?+O;1s+WZ!k_ z&~h<=sL7xzQGJjWf0sf)AOI)M-Cc7&?q#|l?ESgPq8g@bp+b1Nv)1P+m1ef;J9(6f z27M~dL;f2^l23*8p!JLI3u?f~vO0<0a7Km_F`wsfl7Jg7{%K+4BSmKBcDt4)=p3>( zQ=opgi1GP31L@LN7J(W6Pnv69Mdeb_`tavT`V8%IGY|B04)mArWASjVhf^-$kODw|t76lYEdL#Y(>$3B}Wo~zkg z#NgjL`9py%K4+TPq)cq#5~00?DIo?s5lbe{aW1R!Jma*=*e9I8x79gFghOf=P52@9 zKlnhDab?MIydB@Ya>H>}uf_BfW(#Sa)kOCe!Pkj~PuC)R;0;291Mxrm!0!wQ0H(_0 zaM&}>SF6@Jj?-Yu6Cvchiyu?%D$b%wdI*GG&;>vFEw zb(UJi!Q}%~I{L{2aci}Cf>I7i=xU^8OIn&3v=BW^IP2OEOB{ujIw9g!p8Nu>DVA^T zt|>}mOnh`G=co1m17<*(zxkx^G3`y@HA!gUl~=fpFFjlkLtc1QW)CZ-2^eWb4Job3 zLr2xc_=e~%;gq1;C#6NTuWS0^o}ngOp}1u2jAWUueqHl3#6&27;k6g(y3nnE`H@1W z?KYHPIE1;3@eqE^S{ot!sWoNWR6QZ3fwIQRO8H`2+NCd)r~a4bnU4o<=|xNI8=`!$ zEx)I6w!E$W;p5PK1iuVm<#eh5RH=3@p?K3A>(-FLtIF6{cqV^)2cJ?037$-h(PEPA zlYvSUfIBD+uXVE7R4bkd$AvI5`6e@zWW#>?l%r8aVl|&6^n)8N7PBiuS~fIY2+(2z zV#0~mDXe_(3+Ak}rNGIT^smxoUO1M8JgF;xdWS56MK{o*9LDzTJ(XSuRDkr237gE| zo)&Yk6xUk%Y+O}lbW(!BI_|wv1XNfP@U+hf7OxpKq1%KKW=7@uNO{nDz#C5-cVn$I zTfgHW)z|hBn^F`=xCp%Pe>D~UYzsx1ybfKqX8-^|07*naR7qMCx9lA>#J8ce+W@E{ zjN-`R=mZd6)@36EzpWu4d|5#WSPdwiUYANhsMt`T_|cX}n*tC`Xw1s+gyvp0deP90 zoGkk7P)ymVujWBEn^ofWNZa3}#8RFy{c2?l3H_-qJa+;x1gpb1H5En%0@r4nHymaX z(bhisO*fYLj{>O#2rXe>{p-7vwd~Ao;9XAG3e%thz?W88dm{`kK2YvRq#}L)t}wCO zO9;xPK0V!)C3RI;_H09-ruCEXW{zs)1i*JPHghofW;JLC;sLER%^2 zSYR#xw*Gh-q7o9@41|R!-rS=C0>DNNps+Oe-~!PfJhp&-OYpbM_{#^`55VP}YDc0KKc?ecu2P9$8ki z$)7wFcL65jtIV8_^tmdNd}{obkkLINWK>LLV@g-XCu(L9Cz`TJZ?U5G97##Nk&wr-lJ zVtZ2yKraJQ2Pd1uaJlGlKx-?G$P$plap@ITY9{828$EtZI~ZvfBkjqswju|oa4{<= zP|mN zyU-zIpX9bXNuvPp^O#~2U->TWw!rBN7}U!LyuT@#E`VYMpmw%Ms};mgUV$JPLJRsS zA_&^A?U7Jb`Sj&6x3&4(>0KU8@;{yJrinRCfqwr;p7K~jTbAv4+H4^Npb4PiwjSGB z$z*33aWnT41t2G&X*Go;fd&9EX`N23WF{^&h+i5M;ZT2fzgknj6tlQei&9X#k%7^f&$**g*27Ru;=)4p(U8cIvKsw~Fo_s~&}OPr1DrI$>S5nA`Z~<2K4+ zEkqkdpg1@*!-YS=p0d&khbjQG+A;;Nt#4=n!={29lpn%@0!XUl^gWaBERW_8#8RRa zo&ne&LmYHEg%AZm*#PdbWV!%K6hI`qGL2jiY5OZtkKj}Qtn?`E~wjezjQ{ zGbogZfB7I!&RuRgW;Yl34vT8!W8W8B9%UWg8-jq&BP0!XVNx- z)i&X#O&ch4qdknp6kKNFrS&)M7t+1ZF1@3tj=KS;z{tm#4 z4hfisq04~ZfILuYb;xFMp`R_1RKcu12)Yr=%W4>!0=NN;l*N+i0?61DnXhV0GQYwf zi3MZK0YJ&NzlKSh2ymvdY>2Wgeu~Ey)JB<5<~KTs9)`2VhM7k|t04j9^1vv->Q^7J z{=0idj3|XcxK1Fg6YHAMEg)H}jBB;;fVu{Y-*S!H^G-~RhOoWp|>n0QC^Xn;24mHg0vkLgirQrMyJi7%<^ti{wR^@VhbRw zOs7Lx-H2!~HX9er1C)HkK^P`XO)?O2)F%^w*rmt>&k$~si1Dyl4`@|YMEFR6vH@pZ z(W1uc^_ZU}#<1PSy-&DxZTY>pAVrNZ%CjHg_{mm!GmN)~wkqO}`r&wyFV%ElIVcBG zILji^%3s}?_J>R8!|Uv|J%{2qZQ_uUkLRC@Q{Vd5$MN^AU$_5)AE2=h&piY}!}+u% zupEHHuxuzG{49XuvdKzVuO0%sD?WJHy;uR5$snSbi#XUD)>DZ5rfMJBQ$< zysUyHwCev_U$1@kv&DpAqy&=IrXVR5=hUa}Ha~%;Z6v0;oOw9REKXo?3sT}u zMHJKd$%w{KLa-!jVC{4F2KR-xC;TFLgHHvgEt9WUN^K27ec5eRqMV@9?q5w5tiU~c z-~sUsVfh0;hsv1`Z*kM^0M8jH?gC^21U(vdVkT?ligij*I`a&KP&GB1y2eKpS6cZf z2U$-iKl)|2arfiSJ+5iPk7_xs=HA zp8SK_@17;bFr``?Zow@<1(~e%VqE@0&9BfX#DXRgt^D;}<+#e|*4K@EGv5*QAEsf3gk#A(r}UOuwibf2Ftmp<052+=JiLNz zq6`SjrFjF9RzCJMJUCW2Bx1$o6hwbN#{>LessJ!FNJf__;5K|!kxwEZ2xI~pt4UZl zi@N_s$M2X68kbZC_{2_^B8XNXai4jf2D z%F|8@jxS4M_Vv>0XN(?37C*5Si6g^rtRF?XU6Xp?sODNDKP}tGaT5O}a z0=+LjV14w#73aC(Wm;m)v}mjX6ac6DXl&npBe+dR5Wo?oqO|%KYtG2sk8Q&;zm8#B zfT9;uGg|3`XOr?5rI&kmy4BBp2>q#sWbF$N+MWn@@-YgPos`%W3M-2WNWu2HSkq{d z_KTn~veSn(wAy%PsnVl$XEbP=kn5Wb`%(9%J$>#(N|i1+g(v{u*d@HaZ8H#3R*e+E z#2ub0fRG9UA;Rh6l$kaJDZpauD;36$f7Vc5#8lT1ys|0iY(0ddr-~h8Ni>bmOg@>$ z$+>z&&TvLHpZqgH=_`s+Y#x3?#Y1xB)RO{(0a=r<`Oq_Njh%$F)fb8Yg@DlZ_6t80 zf&2wr)Wa5`C9x9V#9L+xvL>N_K%QsMY-xDVo>~CBgSi6Ak~lq80*!y#dWe8HB`9ut zJ+tsj7rSWV8G!i2SJ8o2%ed@K)@ z-_q5KeDZ_=w&9YzZu&PrEQo_}-@biHUiFbL8(lms5)V_$%D>3n!Bvfh0Wv(0Ocy{J zV%rxT8m~$M318@KL9#DWN(piLdBz<10%U`?d}h1svHEMa&pZSO!i{1GKg>^E2+8)E ztwspp8>odf^P0W&Ld-9cgi#=kJjDpVnT!Zl3n*MnqO|-FA^R|Ly!Wa9bcb8{o3FX} zSqZaEZ&iL(46Bx&ZM?>|sxPTW^K!#9X|)=7#?t(?42y0Fyy&R>DyWgbwjL<6}LxxTKFn1k*|cwGS;6K0}q53D-xqjW0oz?wr8KVzbimO ze=4;A24o_5ZCKD+FnEJ-sQX=xDC`MEXev6#ospUHw~(?q5$yGkPnwB(F{?hVbcdbdE7@@E{aSSK(PXFo5$RE zC@+WYmR=Ao_sZ7=8iz$Ms%ToIO2)_~5%QB(hm9^^#E6X_P&Ogpi6$>M+I-<97rV7D zKb$aSU3^tMC-x4nl;yx;8kmBOF$2Ih>|h*U=KrSF;;ZJWV+qe>hMp?hYwt`9ED`Ig zqum8l;m?He%x?;=Dm&3K*~~*-LztT(PG4dAZ7pJ&k0q}x@c~S9cA&N5yhPEnNrjJR z0rP*f1bB5_D>*fcDf}07Y})46A4`@+!_+MxsP8X)ljYXxI%Sv9X7f`^iX(OTtv5i- z)57J$Wa*aN^R`>;V=98+r(-mO%tkPjW@Sy(>z2Rl*iz`89d}moqTNu7I_2`-G;|l zN-Qrg2a5D%-Wiiu;Yj&a$;+HUAd}FXYrG2qDu7FyeKLBndH6_THW$SuNtI*YfAI>p zhi$#i)rvjkV=Ztlzr?9llt}tcj1jRLllCAqtEZ^`RHoCr_bPKxg;@wIoTq*LTsA1D zvHT}iE`96KBGrM?r3oDWG6_J#81w0fa{283gLdpEjK4M8= z+8d}FU_u2xDraKet82hiUoO5g0}#QZ_2tg6s2V($sTbjz&8zCUblA^Ff)-|3OI&#- z@2XKK!oYi{73{h*nf3{uz8BYEFVdJ{d{UWdwTJ-5AkjKN7*hp6-4U&8pdvQ4Ki}Yx zwm8$SFO2Q0jj7LuiiIHBL$9Y6dCzyf!ma$}oo;+o&M)xJDiQ^S*%>id{`MPmM!-ed zQBCGF{WJPg0bs2&K{Zu$Q{5KZ2qc?FCJlso;gA*rX}fdqSbpubrZ6_{f66xQ;7|w? zAjP2?n7Ehp!FjHUiixTUv9iJ$;jUQ8`yH%^tHZ)y2q6w+hz3nn9$fsP3? zCOgeQmIrCEo3H^STNL9{rUZpFOutLK_C-`Z46eGs?fd@MY3NP+5j^s&?XzI{ z8Pg~|{WnXJZN0z`Nbf=2FTzg4OPEvIBnfT(YmQyYExabORE*Z}isD#aY1ynkK}G4p zFa2}HM_5gwQM6A>sbY0&wk93?3(r4sOUVdb)Nq+XClHE=cU3@_m%5>BaeUnMjy0GE zaP`3z_u$c|Wp7@l5-XcZ^b#~chzw<>*l<}Lf>p>@e1q{f?|Vu+;a;O*D`v+@M@^J( zKz<0CEh)Z4XYnZtOvvG#n-A+)EVjx*vnqWy9bv8f!p&Eh`ESCnQ2=Xw1M)HSAv|en zqlxveA5OjaGU19qxGcT=%EVE5vAn>t8W!lP+;FM&HOKPyvtIx^8IL)>n>_?QyNTxJ zw35{@)b`_p%QXVPMj)En%Qj(K6qX4;JNcw$e<~!P)YAxw2euG1q46)$J66K@$>WBT zjgzaZhf|Ev882|TD9KOdg9CIlJf%Gk_8Faf+T9sTfbn$t)8=1*u_+Hir54Y&z=qx# zVJ=L+^rZ+ZV7#3nx9yX#9kQ47s?51Btw_&5fldHO?Vu&lhL#8>Z=R4=qjR$=pAju4;+2UN@p1< zi#iPXPKM;mY_)lT@)#dKW!qXU(!$|blhv_>(aZ9tED%q#-OO~8M}J9zwzPUp;^CG8 zK*88-Fw8nkT(r5um8G3|Z>=+VY2%IY6USt+JSC)7ERhkF>Oyp zOL;^Rkbw?B9b9)|qWKXLGo5t@5DxmM^bhGL(o(Ps5byj*4&kOP|6_@wONEMhrM$`E zB>Ex79_ffNYw?Je1%yPS8SExB)XYQ-uf7;_gwWK1&}6exTCe^=O<>XiFSh<3C1MkV zq!<)t%4g6I;I2C7S5^q^TiGAd$pG<+9jMBZQ5~Ntb?oVqiNEcUFx~{$v!#)uyDeK&ot#x6DE>i*tB4_7?%PuL6c&bj{jml zA^gUeN>|~-nJ*!QzK@^NaHtdpEw63y2F|on)~``y_z2)G{GkOKv5c7oS_i}dhwPQc z!3xNapzz7ocUXAk1&ss2_`&A`pR@oDZDFrJU*ysx$OnF8??K!bpm0o%>Z=c{NV>E?p}Ge+N6UsQDc`1MBaWXu zk#vs~kC}rEa}vK6l)pI>WyOt8teClP{b&>yxEbmQqaK7Wca(rR|FXE(X0$zkDS^Bt zmeq3^vTcu@d*hXb;`HP{F+Wl+NYzi2kzT5p*%CZ zjsoyyD9Vg)zM(L_8Q)(NjCWc6w>8Yz>B!kNtzc2EO0O1tNti?;@2xok8xXWM{d@r7 z#8~&rEniRe+UNKv8aT9N8vuWQwTN&1eguixcPBR7%BR1X4E>b}KiP>P5q_I`%N%|x ztBs%+;j4TnPZT@+$ODJo@IuXQwDEr>Af{UV)Hh{j)|;2mc=O&T<&1sOlnB`mbl9k+ z)dqj~%L~)=PO6YYmD(X-1x$fSO1`|{fE5w|wBCh2l7P4bVZ5KAKQA(s(V! z|ICuBh=o*p3t&Ssj4YQxxdMn4X*`jgrVOouN>a$arbIBj2#Eq{!wcEu>Yj&7i(>NQ zZuJv)CtH4TbhW^gK$V89OSs{8Yx%K~j!5{AuDP|_A8>10xmT-s2nBeV@LQK9iH8-_r0Nq?CY; z%XVt@z{AYgK#g9VQ^i*)nW?gQnz{F@!sQWnN>+(gRz+0%B>Xrs%scObnU) zM|9^Lekq6I@EcyLrNn;NPvO^Zz0Xbwu2opse1$uHnAN}1x&!Y|VOp(*PtBCpi@qVcRP^FgNdq*cNK*3tRo_C6(dF z$})-&bcvBe$%x4?vRnc_*=?VrDXWhv##-UWTI1w-&9I5xi>hada*sr6seEoF&7WPcX`O!jFcS>GfcLd+M9Mk+_os|3yxAy6Kt#8~kYm_WQoU0$Z zGx5Dp2H`*+Lo5s?@&Mpa$+StZt$#Bu_0|u`FP~=B(_Q*oS)XsP-`4NV6t2isoV)@ z5h6{Mi{EQ4yYQ$5GKcZS*tS`P5D6L{s!e!n0EB+3iS=nH1u&kB?W7y#Wbb*$O|Hj~ z3EO9}E*_1j3OCQPG0xWXf__=~(^i7Yhk@9f&qP28n*xu7M+hk! zv{P3GONJSVJxpZZvO+hWD+C%VfHeY4LNev6$z(Q-U3ELP#4_2FcUJg~r>JNBk6D)j zZZ&|{KWNy`r$hdfi+MF3myktv3>kutDJjWt1=J`2_Z4#r*+_s8nPO3rqGwnQRbU48 zG(5!2v8swFNvZTTmEf09`au32gCm;{JmXG2eXCn~<8^j8rmY!Z5s#FC4f$D(iJGPe zXk~mV+wUgFR>@Q}6jfjr0&&16$9TyjyCDHu`P*;WOg$6e?a7qCk%l%OmuCLi(Mm&CVDc!MVPLAXUnF=l6bAS>^4olSs~#!w;1P=-h{ zTmjVzpg;PWTh(;5eS6ipt4JmL38dLHL(JUQan&QugcC}gLB93(3DYI8(TdQ)o)Hgy8Dtr}&qPK6a9F;8YEk{02Fi#c>jx**GKub?dg zDFtr3`^X9{1Do$pJ(L9~dXECqOjWZ2cqT8DKow99kSC)JG3=&fxB_yukqf!mzfV(z z9(5ykwWaD4&IQ}@6GUNNU4zSX_8doEbp*bpFG~(3V7skSF`@#DzVgwp*rpE5T(da? za=O{nO}y?nN+6gCNhVc@M>*2giJ4clob14Z;BRWId_$}IXuZtYNIETnMMA5$-KUX* z2hAZkyz=~HXI?2!cIQo$mFNkM*~{k#u2>^h@e1Qnp|urbjx=Fsq{UMsDXIxhQoa** z9+1*~r1=C)CuZq2I~orTo$q>b3frnV^5z0>`y+(xxdb6AkN((QVMKH0dSJS+k?YFq8hJr3D`1!pK66K;WC>Q`m1)eEizz?in z;FM$zjb(3yi0)fXp%G<>vszbGG0CC`B25FImYwl`()T#!Bk+xe4bz`4a9^_PB*PWp z$yypDzW>Na-8p;T?G8#{XbmAi6@VP((UT|L_@1ZT$q#%kiC3FbRieo^^PpnZR=~PW zVf7B4X)xi8Nk@yv@FbRrZ2%#2nW3;`E_wz*717qdGr2_q3ZtKtgv zL}QrO=2B=v)qovoGw_Cg2ZyilvAq^(6xdXTcYGJXccX%xe)pzZ zd>3V3`VKOvVTr!y6-Hhi@o&b4r@yDL^CsP;jC|9-t!toch+K*(j z01gPVE$P+=>J|$iK_DA)>oa~N7zKWxxZAdV-2SUx0YuT~oYqM@mQDH%F}J64`VF)L zdBV!1Vvl*2vO=#}0KpCcDgZx$`2&g~7*po30cQxDeVtZp_7%x>xffj#dee7QqA~mQ z&FKdOBM*elbVXuK(GA_ZKcCcGepi)88JcsD);wD@h_8Cc*Fop_iC+=g9+s75mgHyx zU1M?Pae?=|0-(#fZ^5Wi4j90mE%z6_$!NnvS^%V0E%Y%i&4%vfNTEBAq5j| zX-#wAciT@dG+WBGgGd(93%+X#@E!4>J;L9Z4}FG`%!t*hzMh$DK*L%Fe6kgxiijc@ zOJ<|!j8(nZ48aTY6+ra!kvtodzcmGrI$Wxg)K>}sXvmGZ0R2m)$%@R@ zBvj?!wiST_Xj_3r2wCgjQi&C%w-fJnjm1(`SkZL1C@K^@G=p#2wEn)B#K-I3n*Xpe zwRPQe1t5+_hM<*hCD_bMh0$=UEQi01e*Wic&DB~3U>S}lw`e{@m~5Y4s@1)q2AtDV5x>X!O#coAb6r|YG8+Jr&;Z!C{w25CKkU{gSGYYw2qjP_ z&oBc8&{}53fo*>`oEQxN0dB2Dl@Aq}_sTZNej3zFf6DVb1cVHSD0G+ykp5^>4B7&y z+ga+jMV7Tu813nbRwL1Up$*-c0CvSeJkANe%=4%McoXLzlg9A=k4J~yl4Ldjg190& zbm*ug{&Q|5$tSZqQ-qAM004#qHI_ZFZa}R?lEl$pgtrS#+=Ug;T9Z#pb7mjBX*&-r z5MaFLD1xTfJn)${O}6Rk3g8_v&J(P(N2HEDG#QercC;_J4>SC&^jZ=rjQvKk2C z3SiRzxcmSo*^A#SfGUK1<`mqz0m+e2wgE)|F{~omm`*<5+`X*=@GV*{x##82d6%Vl z+wF2vb!BFk=g{m63Te<`>+f@i?xRpE06jrJ{wnqVz2U$s~I zm8=FrxB|H0z=IO}+q5pAc{e~S+&E-$aMgM0pZJm$V`vp&!x+Wi+q@;sHeG>vwwi6q zF>9Q8c4ZLgP@$$ib{io4q4+n&9Ulyepcasg%hmU1_8;N#{?T1rCGXocHzC5uWvfp#hwAS-r5bqGoWmo!h zp8e+1Oy~VU%rZ|U5NZX?OVHYq4}97*5TW^*6;QB7U|W3Y#ptElAsPjVe6d$So#UKv zoO6MG!2*cghfP+uC0_g@SOHk{X!0?)vZ6!j8*~F`@G<9Qrf{4WtFERF!W>RiL>G>~ zT(Sx3n0O5=i+Ku45q>-MZk~z_#a&l_yG{6^yYYpTqwgj)y^{uImnfEA6O=Elbs69< z(`iXl?>}KVTcQdGV~s23zj9w&)j4;P(FUNVxdB{v;eGC&XEhq&zSB$;GOa2UN%Ng+ zuZHxr8f^Ho%WN~rqD36D4*S)W1m@%_T_=zx@In=SPahb39#$6V_oQT@A9>`#gan@1 zWw5DXOs+EU3L*cU60(x;z<~pa^RS@ITjEre9R+ZmoPV51MOT2af3^UxibLYIrJQIw zRJ`@I&pBkY0W`7zRDp2L2W(a#r;`MC15~(lj{tUyu^nP^#PTfz%rnocu7$G74t3fz zAT&YC7^z`0GY6lm6(H=&Xgn{aGb=%Sy_DTazmO-@w`Zdd>$WP4S8%2GGDMLD4VOnZ zT%1_P2)&M#tDQZIWsL%0Z8~w{=mRFBW%(4mZ)O3Iz$E^tTU|Zk_Ut*&%n!>LU04BV zH&&|23g~Ma?14_g+4$PwEj!Xc;BCuzE0Ubor+GU5C?ilNAoD6yQM#0~^t{5N2o2sb zlol!dEG@lIG)QJw1*L)V_2&^o@b_q~KZ;`}V4D1O^8B;=4?Kbefcan5`jaO^e{=y* z1nX-@Og0-L(#QfJ?dXOJw0(;EnA?;6DrgDFnj{hriF5VdkH*FAEB7-#+avsk?vI_D-W1 zz2dTD9zP0(aerq2rOKwTRPZUnZ|ncn1(=NPato5l7SP-WpiH7GuUw7qJMw#48}N&6 zLmCW35bipluR~Z?2)^`E*Vm+n@nLOw6>3G)r9Z>>c?kl7UL@@PJ&bcE0mT=?gmb`y zH0cTymH$kZd6v8pe#*15qE?z~24GX!;pOMKy-fY3-w$xS90FcdAIOVM7836a)UDna z{T?!zB+%?{D+OR#tgrpP?*FS>SUzYQ5P~p>R{`Hq6#L}yTwidDuf5uxdi0JnZV1dv z_#s6V^OJABuUtjGd3~FgL(nU^dEtGg(xqH$Yw7~e2yBD4`06VXN1mVcr|sJ-g&+CR zVK>QX(ozI#tH60tQfLR!O_x0+_Wlz$RuX8h%zkv0DN+E|2gn)7oPYsSAHDXg zj5w!{s?4mny31Tb1fWVmbcstT}yWSWxHpLeS^vcDx65)^V($Y&A67l0L@G0X*70mG}# zPmYMnhS`DVd9{3?-bApQgq;^zj22xD3yM+aRVEyZ3z~dm+qBP%7y;?}mOLQnwjh}P z3s+x|99@U?OZWL1wf=RMu*AmZ3Se!E28wWtLk4GnnG}GCAp1G`4bxuiS%!0>p%uW? z$Hg~aU)}GSp@3CUyKeP-UQHEC)9^l@uI~&Bg3GKh&*4R;i_`jcxp7C^@}dM91LPI{ zd*6Da&To_h4}rn+f2^+cS5TG;b6ONL{}RRV8zzH|kvJV;CIw*0tRDPxjYHn*R0cEc zgRKDk2FhOZ0){WWDA_QiZspTyyk~xzr-|A|h}y)NnF#p=5dK*e07|4QOCpD#7k+4k zCD8Te9l7jkf3~p#%<(6y7t43L&)?I}VKP?dN4>C95X*j<_dnXW)t!{=%?L9qfavB( zN29u5x1EBp0#E=Uijnf-576{rx&ljYRey+04Q6G)K!VK6M1?uq2y@$b+2X>GOi-Bx zpi3M4eV+58D<*$_!;alx1vK&nh&Or~0^h6S>&H9N_*6$O5d`0fpP4+zD zTi$0@0G99M_y;u@!-97HTFb(OMw=ItlK(7e&k~osX(5MF z0HN#ht?yZ2##tiedpYS>j!kZWr%Dl|Zb+b(Y_7o%y`X^N%k~hqtQL zt44#*oDXQ5en`Quuivgi57`p2ex?*#up} zUIZw-R(I#U@2RgKK&)aTHtX#UkO!;NU-vT8(MFoqPCBh8-AunwD(eM3g zB6HJFtsgp30G9h>CqATMi7z^>V=zQ1UNB$0}8cC=4r@KB};{~u=hJ})=AF@ zvgpJ?pE^N1sJqMwLDSZM?MvloS!R|pzi2*1wnyzyeugW-J=Sj8i`oZciS;i``k{;bl!PB8P@>ojXZS$J zs3%*5>rEsH>kWRvWUZq@b*2CepJ(p=kE=vhiwLry*oPKL9^qd@2^Avtxg0eieFq;otayLbRpd#EgP!z^!SRza& zq@+8;l~r=&g*jhm*%TH7#i72k;Z%ZeVZ4rBZds!s86JM;4KC8qpZT)jLH53=<3;dh z!4L6xrtd$V{7aLmj_}MTGZzJ58SI<O8+0>LTa zgx#Gc{{D09v0_oZ1j|V-R~1+Cp>h)DDM|v_n(--YQ&fW&{fk1t#a0B6Mp4QrY;&dW zP78psx%*1*l=|J4AuXbrsYmevZIbZnST$t2e9E4!gwwFjLA;DTfTWlKUVq%o`=w3B9xe)V2${kn zEE0$Sk8p6L2L#iq35H`OO@s!9KASGD%wkNyYEun@jnz^_w1q~?ye37uMICQeVmvI< zV%{&I)phl`f%hH_NXnmO_mM!IFV`U8s(k`xrKCRz!(TdmpIOmg*JZz;@Z^kL`zaPIu@&U|lGEdXSTY8mnxJT`7RE;&7z_q)S$nfD)&`dKOJl&H(m9A_`-RlvQ;@M70wfR` zm4I<#Y{Gc&(0V`feu$Z6Z#Mei@KcHSX&cLZNPqO(xNHf~3N+J%bR6>=Sedw_7LvPu zTcK3sg$T{emC(XNNlRaDnYinVGfWt^A@<338Qk=smKERur{umwc12cS71j4%qy zO4@hByG#dLhn=x72aK@N-w`?2!UTfD7+?@#m`UOLjrbnbI0o^Dg+-08h8SEh#SHbTVqWW(8NY1x^KGhaj0dWHXzZ zy3Nwci8&kgt!WH^9(XGT@0Cm1H%wOQ9%r9n;H#t}xvo3lTBD$xgiaeUeK&5F)zvym z*j!<))Q-SW56claT0o;Dlj~NPD+Ds_UK-*B`cx0;@2V=cA|Sv?+pV1eFz+wMU|ikx zxK3N?>rt31LxP75YXcv<)E;R}w~mV}3%rlNx%xx{qoq-*Q! zqdM$zUl^mG(Ae&IS-U9E5_;9P#N_i@GQ7QRZ46mZ(;0xbX2B6r>!(d3IcgHQIXp}t zY=UX93ZR2U@D}VmXPKFI?WVwo9R^V$fv{Np!&sc7$*vu|8*MqUwfK|6&c$ zsJ>F(^p^I2O`Pu@jDP9WJ(d>2yx)L4GsXaHU%dIZYO}**LF@n#v$Y4DEM-dedzuMI z6X=#Z?-gg6zH2VfK&gxZO#niMpsKO#gE(jb%}fwT)MdX$sQ!h0E@}%9Y@6$&jaML%7WOrS|u}Taw=qKa&47S^Q;lQD@3GjWXEScJ|W1cGao-OlGH< zN?zqHUGRGOmmB+Qg_bwAa#y;4aO}Nug7`P7^8F^RIS~k^O@xt>V*>0vgb6&o(;WMb zm_RaZd@%teIJ^C-nXkHbKqRB1qcxS`$|`roPSL2f>vHu%6tyrBw1Z00mA=)i{#PQn za-9UX^#0`dc;(d+O);_d_e1M_HaLsC&0PzUL1X5eyG{^3J!mi5>E*z?eoRt8M z)S8%>u%AXrFv>;xH>&({eT2a!YdrTJe}!^wuZ8$w-gOD=Di~8vS%azY$F1{QYd?M~ zXp@H&(knFa3)*O=XNmY(^JktAi>cz&r@Y1U|GwNH+qIHmgNTarKpBQn>fV6p?4aC$BJ_tn)j`PdvZZ!%BYD1a+OM_lM^r z^@9K(spMVW;=Vt0?U?CnYcT*{$DY1Dakq)j`*lO`(^{DT?wG7F=^!N?Jgb#oE_spN(%=OCe?6^(E=M z)(@M(k6&hD1m$$ZzgIi^c3|)mC5IUw2!8z{#rBiMA8mG?EdCP{f1w?Jw+L_ewI$Nb zVOojqi_3SL!{dKv;(a$+CV;~MCl=W0kJPbHK}GY+MA|c0W7?R3>Nb%w{zESs^U{86 z)TwB-%`dCSJ+=UhX7ywO5PvNya*<|M^#~(bXoFyp8h}Zov;eB3ZBbSoiZt=$N5U0+ z`#HR0;3jSCnJ<-H(*%nryXE9VP!GO@p&0NEFF!(ianEw!|hoPjTFUCg`Qlw-#8`!oPz9t?uETzc1?Z_MTEg70wUDia7k zPaK_Vy^>w_!z-)>lJ!2UVVTa()v=B@DAnD4ShxU+^eF)C;HRz#_LtCp9=DG>j@F=Jj3@uR*iR-gsuVMJPCKyco8?oH$W$1B06jVkQ| z5I@rZ)a6T3fZU?5A$9#Bi_kj1}I2uAzrVGty^Ooxu&>c9S7ruX9Y zfwoDbG5T#gXf!1aG{`W30KZnt%WsVsfK~gYMNB3hH?hX)Zc<5QOw z`ft58Y7`VJq&_K+^E}qBQ~|F?5qgX;#?GFS+J^x=_O|nL@w;9jDUk9E z5^5Uv`qR3DNjrykWOascUmse;6@M$H3ASd<4 z7WA3C-k2k z$=@d?5bqQdkWIjsCO}L8m;llowg1JpPL}+|x<-EN0%u5FBMdXPD%lq@`$o+813#AY z&>kIwC5D1DXmDcAkMwWlgo=%aVAZO&I!8Z??IEZ~ze6tzYTSKI3wzF6WqtZ%MDAso z7^N>E@6p#}>ZuWK|+0zBD}TjvL_=H=%6lT)*T_~}aHvD^Wy4p)EA=pxPIzp3fK z2V?;X{Hlq+_T;X>poal4Ok%LuN(|@bAO^lOm_m@mkG7eKf8T1dqmv5FCMMIs)GEt< zmAc5X8%u*#CusqVT;8nY9K>AT9?|`Q^G*DU4bx+7nf%-1flohS>cTR8@P(7Buq!Q; zmERJ-Hj7Orzo&ygKjEz$5I30}DI~?TPG|gs2m4L;z}K{S=)>}-mZe`Jd`yljugJ~< zGYC6~PR%14D^mjJHz3U-v*(DJc<@=P4OkWlbEtEgDS!WJyU~n&0@bz+?W8D!5<_R0 zdnNUm>&I4Up3dB!=^wr&sQfYpPpk4_8UV4ojXnJ`F6*nY5q(nkn$+m8dP|mQ32OGF zS;qhXt-ozsj~QO|75U8mh9;;yks7D#EBnL6Vb32Q26Rx|d%d&)u3!u{1wBtby7eP8)bG7`T_6}R{xOCl z5*WDjat$jm=s387PrpKd+h{&W2d&^JjQ5|L18>~soq1+$0ICJJRlZvvjIOfsT3_=I zPu`_?fWNPSiP@0>^ss_~ zKpqGRG(vXub!crmnf^U*Vf>5rfbUF{5tc+|qHUq-^hYK?Av5~j8jJxQL>dr1gS1n)rVM->hbyj$i2C%Q2n`=i>FaEn6Jf3rka zXT{@L&j5h3e`NAXtqT0L)UmZP!~{f-2iL2z3q&#QP#D?(1Abb6;L&AQ3WP`yynbVg z@#KHLs9lB!0tF)?BVeC@-CbE}cau`fEeq!6`8f3S=+vmLbCw6=Zza6O3=97*2?OHx zMG*+V6I{Spyg|%_W4r8k&HK~o9a=&7Wp60{!zO{fVb#nD1^{&Yk;ECAyMJ9vdsmuC zjDdPca1h)%S>B1l^(m0ShWIfME)z3=DIiTWE%1|alYMr#Nj&jVVTFFhw*^eaS6xJ2NMXZ2~xR+ z*^aeyBj@NYs*B5$G?D(H_9K7B8|;0Jtg}7M83w>W_y-QmH@yRYB2D0C4Uz^;ABYwS zuuIc|s1ks8K?bG;Z16w|fLuJ&5}`kyG^uCA2p<257{S}p2=s#(M5%=8-%9@erP^8P zIm>RT8kPlXat^YDFMhm6gnz{u0pVknXEg|HX3Wt32-IoSeLZ8$!X_9$%zn*+Km+7m zjKX}<3SyfWz115YJ{GpPR?im!GOgZ~`A>Uqg&sz zQzOzW?(q71ze={bl;$J@V1WFQ)Mo7_zDH{O`PL3VPo=us?VWfILcMte$~zU4l@2OM z(PHds!GL1~DhtUkH`Da1`%U7%x0&?I`%Lzjb_zkv_P zp1sU;U%K9Oov9@_yB{Q{>eIkc5k7|B1I+WQAbgkw>I3e)1o2=h!6WAXiS%=3%G~S? zc0WZ`b1ltT2EahsEydk^U()T(5(h#K%>^O){X!I$3yC5e)_X92$k{lyg4$$C4jbko`Z_*#RqqRRMJw$03%5&;}srsOln6a~i=yO$}fKL$VRT zQ0#LNyXKAlm?G`IcF<&=*<-S=A2e!9fXL*V<;;L708F6feFBcKH4IX-P3IZIrdvec zxnZgO9_;xL;WI`M>=8}pIEm-5RQ+lsz8gErL+ZO@x|q=*^;6kB3XFKWH^1jwWSz@N z5I8lGpGd4T+1OXK2Din|^)g}rARhC@X|FIObQSP$3^fHJOGCrZH#PNu0?^#%_4aNLU+C=3Sq-|++TKc1 zDXw?=S2WOHkl(;EyF3I0FeE{c%p*axYNiE11*9agY=oTvm9k$JYLHn~SVV>y2(f+5 zS2NoVn5-BA%)n{}AR>7|#91;jp_c`7gvXrTQy;y45&h|lO!vm+rt9qGI$~R}vU7W7 zv~Uo-fG05>nX)mzXIYm7jH*d!3ovH9JvpC`C{whH<=#|AMw{&CHFkfcRSKS-5~)f^ z&TiQFN2Lw)bbdiZ_EBw9)8OZ90U#TZUqQHPB@CbefgyP80eBR`3>Is80HTrd2^~Y1 zRNAbTDzjn;8C`vLsIhiUvJ64P)WXw+qT$0V4A_Dj(2^(BIwr~9xpuMXmO+mQJRMA( zfDw6sx5GyIen@IQM2`m_nt-xZ4FflpFd3ruz?7*Ik3NFA_~svFApX3!;M9=%7O!&u z9LV#Zoig|S#MBl^`#+Fv;2e__lA#&`5eN`h;=@8hQneC?cR@Z9mSwh%5+j~7;`mPD zXX->VAjxwvdvMHTwVajREoQJ+XArzCBNrZxAT4!^r z%Rv)>2{8ZU$~o~MB~p__JxoLRDrxA8FtU<{zJiuy#SqXCZ1f^~_=yN5_nLhdg5iDr z1u6gDJqvlY<>m_Kl9TGwZ&Qa>H$1gJtF5t_K`qxQ&_-Rsb{9yn#jV56- z&*eP+SaPEnz+LL&jhc(btOPH*NjX+dZ5QcQc5rt>e zkC0vGR;mPq?yAABQdW zb1=`R>u6kQ8@%y8)%iuQzvm@e?d$9J5V^L#`YymdJ8n%H?zU^RKjnWZROv51yR(pL`hUfD4DRBH__M2(3+o zCZ0Rn1=tp~9Vgay;x`DNGMPpZ%K`feaN#*op!qF1^}mYz`bRo-`T<$@bLz&4#hl9D z08u-YfPnvoA2-99>x4T3)Ie&Cg`7ad6+|zT$R)~+^jOsd8bau_ z1YtXA3bFHX_5-AQZfaq#ay}MB=V6LOo%{shs~c+96JlC_ z*(Q9!B1CHBkcFszc4d*%OP2`$YPI^aBD9~DB<`|@lyLqakVGnSumtYKOb^fs9AP%I ztCm(E(t=70zn_pH4;AumZ3*_f{uMqiD8BAr9iu_oqvl&LOI8-7>ChKX%&k+6n#yYJ z)PGb@zA8rWkW_uCB<&Oi0?i)MFm0L@dHl40sk~f1BSie5kZ#B#q?-nyHZuq2JbAPN zj9HE%<^50TLfgNTflgWA?kydc7T`{n#C|ZHQiz+=T27uvEdjYJ047J+MkM;YhJ_1N` zrW1Uau3+et1Cb^C0o^lA;WwncZmnfM>u)t3{Y{h2##hzo>L*wUnDvG zW$uoo$B>6(6bt|fPae1u%NR(&bC%;25fdgJ8*fdmJqG(mdgzt(;wF- zq&$W^=Ja?9;_tvJ1}eYHgQ1d?$;$mvMm^u$FQ)LY=Jsy}KeYeX3j%EhP%lW9C`ZiT zOj)JhFGRjZ5^b{(UFK^g2m@OIBfvd`&-P$e6bNkvV}L=RA&`%Z$EQJDU2} zs8O}m>0ho|+g|Et`jSuVI4iN^4%OazGRkU?gr{A+OMCUzCbS~Y>{eM%YFhBHB>iI| z`t7-2+m{AIpi!vO;AYSM;>8TKJD=*y3!3H(4$o)OV&`(-;d8v=|{JGB4=#1NLL89ya;f3sE;E|G+Mhlpi` zGz3vrUV?!5@XtzqUHJ}kfbh{AtoD#&1U$1&JzU9~%SRD~$*Sc-2@G!#I~I7J@qy^M z+UL4jqa_6>uVpa0>hA;w#?KjF=c$=DrQy6H%l(tm6rR%4XGQS)__>{C76J@eJGCGL z{3Ayen7+QXBAAUL^z-D;evSxzorr0L2u_hTte8Eyn+?b=82qT2bq7}`r_e0+yDG^EWF?bK=zC^ej|*3Nc&1OaIY+B0kx zD@^qo5gDI##?OjLc*`Ye6+u(Kd0xLV_UhUlsNzLH7nc6rt7sIOc&JPwfI{Slq?ss| znZH--^E>o?yFR-VhJBav9g=i!FVQ*JwfMEo09t&2!ZuILK^8ZowKc;k!$L8MA>(DV zQ%3i`w_40$g*1T$Vi<$cDprbkPhd_seF;pOiEktT00008%d={m!#6&jFgfN>FyRJ6%eGm8|fO|NQd<3ci-QC@3!mu ztgb!J&biMy_lYM`?SmXH7C9CG0Kip{m(~CPkPuIi06=ua#o+T#E5rrWT2e(40H}_~ zel$fxyrwmm*H8ffycq$2zz_i79`RP-J^ANSCfql#!O5o_!heEhk8F9M`SPd0o7W z;wySfkmL8D?u5%Z+c=WdkM2jGR@a~U&+sZYu>SvJ54fd)w}F*Lqjxmzn)`H?lQ;gT z4a{#2&a*YZ2c+}V!3enPU~wS2tcks3jXxo#WGWqJKX7{G?>q8P2{isNgHUj2k#%Z6 zXai~jF_h6H6Og7AOED|1g}6_>)|yPo2q6)5u=j65hLCbWbWO9aE@;uc#7NGsC7=~` zuqgx1d3!S1a8t6QAeF99ChedCP3S5XeV~vDA*UOPTJa?iZ0EhEca$mIsme?wLW`%2 zYzywIg0&b!{Uo7&vWi|tz^RIQYBFkSYA6&s0t7!QiF#^ti&p{zKcZLElT%T}{?I6W zwSL=(3BHkM`8}<7Mg;kVp@&J@EYnoNb->Gxg{|^aQW?2dLeQB&2!|4F9Pk8*C}o3~ zx^G4Sn))X7C?v2Jjvt=ISpJT6iJ4$eqDzN@E;Qm@6K_cXuT6kL*p#S7#E>?$9F&0& z&{{F|-UKgVb>c9Xo=wGem(>RP#)l2ritA5cX)JEe>&lLp+e%E8NcD|huzPoKpKIk& zhi%sGJ8<6)@!6!#PDA~~8kQJgHb7P6K$ogV?34mq!Wg%ypdo?y zB4qX$Mf)@6I}# zi~*N~|0YHKvNU+<=bMio7nB%d_FDmec}3E9guLKLzdD;xe$b-$saR%a!v=(EoS@;~ zzR%u9z`qd%07yVri|YOH{{60l2`aHvFyz(ABA{XFmZW?OA{^x6c5jLIeIb*4AAZ#h zBG6=wc}=5haJ9+FHL15vV!+91GL?YA7SdJ2!CAt)Kkb_z{emuo+kNRLvW|@=3EDuT zcHG!L2$aCoTIn{*rt5Iv7&bhLbg&<^i+eMcnp~}S)CRlf2lu_Og~0PcX1@KL9g63! z9Dq7l+EGg*Yw>eEXv+{10lzRNdh`ui=_31YOa4vK$IVEO)i~2L85*VvD4HxdwgxMI zqgh)s*4nu^FM{>EZqwvy`EgKYa>VN;3(ubbv9ChE!t%q^re7h)2h(7o3jV`X4oZx| zU!9gbJRGCl(?BA${c{PU{5jDAVR}2Co5#{DpDICHf`RTM zRD-vdoQoa!19+Z1{u}Jgr(n1D@j+c0=xKb_De5bKEo}-(NgsS4n$I4ZDwW=a%Wcyl z1jJtSD|eR6Cic~z-ZL3Pq>5go(oc-e4&Nd?fna&Z8r{52@?@h78n+XB!?d{b!H#f#akjHA!*1+Go%lf@!id>UN zXVOBa96FyC8qv%_X)_AOhPOP$K`g`?SoEK}(TqL(D_jgo*Nu%j(=@PVe%ZG|dwxo* zY|Dg`gODV@N0Upc3r~u1BGH)=E-opI#4als)noqP2vQr_`^s^TVTAHr(AoJS2c56{ zrUx4~u!J|`1N5Mo6MLSzY1eXHa?$m-BhKJ|Z{JP-@Oka^7xkdm5Z;@dalcj2sO2(+ z{qCerAIEq7hnmjolerpptADXX`};` zyI{Pg9RZSRvxR>z_>dg9*D&*AGYHUc_=2MGCjjqQKdvhG72l~%R;1M4YwvS1%^yDM zcL2H93x9e(F@|aUWZ=9%II>mAS7XLr=U^&I(MkJQf@V`e;o%pH4hBv(b*OfG4&R}D z?vOK4$f=;JTWg4(t=F&CPXaE&wHwIMPImadYY^ zD=j{;NdoaX?QxObUy;J8y(SorNST2&g>Mut$AoR$-36!IIe>-dyOQb@@F>t0Z+}^D zp6+_r#4mu5+kQg5&+X|$szyLTeaA<|#YsUKAF<3z@PN_#5BFW~cBHB0vW6rIk=0AK z1j!{qYRp7wZE!_azY4Js4IMSC#mUux8-AMLcac<2#&V zN>TRIk~#5v>Fk!6QH|Dc37yki?65<7lBfb8K!QkAz=2zfm8r&OAm@(*aBPC!n4Q|pke z#fsLout0X_+flL|0>a)!V<)by56lGC4Y*_H=h6OSw#hTtpwm>>b6QpzS=HYB1(p0c z;8JZp(2$sJ>r$z zCcz9)T2fVuqLarhp;MJes8d*}`L-+UVP4NfAuvwIHdWqn_QKT5o%^WE!g-$w`k&d4 zkB=K;JO@A`aAV*9P9J}Jv!o!K_YV1MvUIARWtLqT+l5w7TH@mEuh zVR=Fko>mb+beQ zG#$rL7_!Jjc40AdvAh7kDH*yRMt*!32ejUmigt8%|zctt?0MQH$%F{3|XrfIpAG+qe60vn^_+dg2O-e(ld0ms;e| z0?EycGD;Nq$Sb~ZAxrX+GvrNZ^YKboA68ATr|UD@RIsge+Gfr8$3)*EDaup!_C+wL zlgs{?Ru*y37&c%1<#nr4w^oDwm))Tg1X<{kOCGkc7R!6_OtWlvdG{WJOBi=!Go0qr zna}#en$_}+F<_pfK0*$vQUZ;rJZtZQm;Z1UQLnpHp=*%RtSK!UJMyAlwD(w~3Wy!L zC50p?Ge*kpHHPzM;t&!NUWcM_Ky{6aGUGZkZ!4C|bVvH;FfDW9LjE zT@5GimcmnNj;WOfTl+s-sM=k?QBQc5*4#x^1ns5tlr%%4Ash7n&O$)qO*wR^>9K%G zJ^hZe00b4c*7#HGb2+6N3!J<4!ch+2lty}Yl$>YdliKdCV zoWFXf+ODEC@P#=9KJd4P$5|H`NDDls1^PQ~9KxyY>i`JSgc- zlPg8Tm5%_78n(PGtji({@Qk(v{t;t}&nIPSgphqLrtGk9h% zYI8BB0a*G+%0;Jl?rp2}c#iJ1-Ges7J(bjSPHMmRUv+lU37d7@t zlV4)89E{BTKsIkIfzNsZXK-?})lSmj%s$Z{KCK{y0O=Bk3N$>G@W1M*b0|0Jph{NR zfQ4JtN|Pe#qLh_g*dR*uAJ|a}*Zor0&e|e`1)4KPj6@ni4q>K`>9G`o&*OHa$mK9p zU~t!s$eA%&*m(k|z;cRdI%~N0Dd%ZvW(V&UBxr9I#`|qp`-qMZ0*W6TnblZsNYqFg z`AgpNz+bjBQNylLI$IE$a`q4t7YF+lHV>isgkART`LKJm4Q;t$?>ZnvMwSy{5rjss z>I=V(8@I#`g~1qPFzaA%x}>^Zj4F1!%7_3m;py_w`Q z@ae)rlgf? zpUWf_BOR8d=@QXGlQ=K%ELKFe?{NI(Vs4CEQv23$l`gPtD#C+l;qi*4Totl|8)dvm zMt;{HYt@*25hn3NN%t}6XzJxka{O^jHNh{LZ;_{U1WNEpJBu8T=x9F=dYOeyWb!Cl(;yg$L#32y^ zEPwOvJ6_Z)uFI6#TCXZ9t^VheoP3s-*R06~jOAJ8Id4S#8@fVi+979+#`H;Ze7g9Q zmic8u_lGVCwiVS5t@i0`RGqV-`b$sgW%o)fY+;O`C( z%@RLS+KxJ$_PESKJ`#*O)T&H*Hh@7#u}m_?uU3@ZS+B;N({i%}RrTGbSl8iy{k2>c zAaXS+b&>aS8Sso9X6pM%vDvbE50N_Nyxd&kJi&3zI_OMgs&HH-t#b8@35vC3QZ*Et zEuwzOmUc|p)K!uOfKyH@J{bqXyw|dx?GAclr~LsTDSZV~GMJKxEwU=x29hR-%}IgN znOd6trWB+g5k)B%C89nzlqnjy0rv+#v_nBU9AW@eVBqD#;;`Vps=CzhgkJtV(B>l? zFfcICfM7Wgpv$V+L?VrX>+V^e$yW0=eX7)o8+WlfDgQi#OKJFA8<+Q6$aMk2EA7*H zwuh=YiAmNY=W4jGAE&I!mOX1IQV#0Yple|fGadt3-awxJ@?QBJ$*ffOPXUND=YJ!L z2Av3!;(*({SUf&gOf6BJ`+|txx9kFq;(|is2V7#ZyMJf+Si&=^`K*l@Qr;r?BJ)S+ zcODw@;`{89W{cl1zCQ(jblpDH$cQepC-ncb;5V(-5sI7P1~Y>)rfE%O4@KoH1?-=l#mf~=1uzvqZkQ=~yMEoyxtrd$SQnD( zagHEb^d@PAmvb1+@IkSh6j+Y*=wtuD5so0bKP|OFPEj{qPgFQpe3^$iRDyL*&HFsG zuC8AKps2W&CiN$3t8-52MbJnm3Zw>!z^kvXe{ykha4(&5zOgLQtz*N_)y|YZHo5#6W+{x=SJ&-R zJ5d=ddr1OVE?wT$@b+@&(>X5XgM_B#UZzKp^cC;8^z`)up60ZT4UN*~yv~jYB%mB3 z6FxsQ4xLCwHnqvJw?@~+B@yDKkFsowHF)GT{*xqCO8q6b(WKs_u^HCO9WvtGt5v_xZXA21I z>|B5Xe~zdkr9au9_x8iRMO}IFWG%=Bk~|$)ETyFKF4y9(F+GPMi*5abp?qK}mdmabOYLWt4#09ldb@GAGMNSezXM-Z zwf4l@eI2*9J;ox zMDTBqZyQ)hc1lbj!8i9J3BZIzWQkre46Nu`>b>dl2Vo^eMn+m@f4x&1fkQ2^HNt`} z+GO5Ila?2A5X-6)PLdDgiFZ4%lw3bU6RrG~lwCM{xyfUx5lP^rais-C4KX1i_fDMH zTX!TIc##9D-_A_?cWu-<@xXiMBQa!oe!nEi@%&JaGM~Sw5oA3U6crp9QfMMdS&EoL zmfROH#Q7b#Ej02etsR6@8ln425^S<{-b)q-{HKv@~|`&jY@--QU69zf$y!)?`DUJ_(a(lDB0@ zv(E5o)8DIKi(!rhHN6I(vd9L&8I$E&Oz|PQz-m~{uNM(9u@3j*flfN)?~RRuB+>oP zv7oKAeoApKkHFL4Peg1d__aA?99@Y^gL=$Gku4(^OPcXi_x%&3J~z5d6Z>H~@VyozXFs z%=Z(?wGEOa2(8$>=@3g_#Yp(@!*kHA8sS0g%WieV^*4INJ0g7Q;u&YU%Xk}KG$f$0 zXLs_hr?r>u1DCrTSP+vl!|17Zw~00t!<})pKiS~5d^fi zOsJ!a$)&V{Ckh_GyDFk1af)zy;kSr9!+m4_bDWnhnnXZAfCxe4VZbheIkb<5JxQ`_fz6ssKe^y8w;iavZh!^8k)cZze~-Xj=PCjY)ACq* z6lb5m!SlnZlS;J?MHNfq*%@~Py&jR@#2+)UoFoiDU-$ae7Z_=p?2at|tXkK|N1C{G zz6?z`bA-u3W3wj+*+2h^wIk4oT=LoVOh*{y<$_Gcn}ja2t!gQY4>vHsSKyE&4S%=2 z@o#7T`}yH%Cx49_2w#o@o_-4=LQ@5Z?&DeYT|Vah%em!o9nF%~z+v(gwyBjDw6ND- z>1%GDhmzC=*SLbN>Vgw^w8XF#jy95Zwl2@i5}7G5j(`fD(V(I9W&A#mDbR1ZO-GvNF>4Fx*gU-b~9TpQ9?jrDHnog9S^7a zx?x-GVG}#eu6r6&HdlC|AopZ%D<4_1=P8Q5wkp?=QAg2Oi#0>m!)vr(0zf3Vq$Q+` zR${(cGj+c)T}mlg<3n~}nq`IJ;JU9Ggn)-~mkK8} zfXG`4sJ-{Wi)(AoM&Lf-BaW+cS2T2gIJuoOEBn7hSD)4jMvm`k%lfzt-BMl)0$nsYhaa*6U2{(~ay%FxbMEvZ? zy(o)iTO~@p)$v^N&W$H=>kS#P`?t_PirN&11b{Hq`rzA7mWj}baN3``^C?QUsB~y_ zj$aKjs^1YrvPVZ&6M%s1r4babPV=c5Z}JRdSQ4rn9&W|%hE*$}I}79jg?OS@2~#lG z4G+uT*lX(SBO3FasV8ZaQ1g%Rx9Mb-6my~0r5Rj;-*@}D&<2)>rhGg$b1CQ^wn~XN zqpmMG^(Ibyi|ZNLrAk#$7nE$!7?;d+I^9IO55R5nPRZy zDvf_#9T@bJvFQ=uYna*^z?epGGea;tmaQ(lq`thY_5j=Ja1Ps0!}GSQ4CIp0`rNz{ zb@!3{#B(|jtE-dy&xZMf?n`xRqSc8~mHfBc?_`LnAd>+KXey%drOX$uCM>lj5x;Y6{CylzOecsYM?Q4&WMqtjF^RvqRvzCzZ`?(_d= zCq0X$5@dl|UyjmT{rWStH@onlGK;MN8^fa#RUCO})n$S-;9w1W9P1zi)ws?r>F16) z$E6SuxqUhd2pEq34&FLh^Eq2JCmZ+;qqw=8)|I7RjZemCjni76BO&?4#`+y~y!96U z1@4fsEDRdxm!nOg|NP*gC1;i;?fOnk@y{W-1c-JLJI_2ctbc=NA+VFaTB;=Wf@*V%k3=zg&UvXCQOU$-hdfOhlP?E|uN!3d))9WaS8s8Y=eR zyAB?O-mrEqFc6xEu`|#WB2{-_ZZF}c>N`P)au1niL=AYTnu}yAh{zRmje?U*Z6<~J zhZaT#k34{iERFQZ!|~z>y(x`H=v`oW4+^hU41nTcmGB3$rsw`X*b7ca?JKbwEua4=(s_d-n=2Z8N+$-%_l2dguA$-i&z{(AGefDA5&_@{RXv3di`~z4?I{lbc8!J_s{on7kn6Ki+OMiKWse)7T^){ZK5LF##Lit$5gXZ2 z?R$NkE*@=cpodZE<5&N&J;`+asw@@|++=EAdRO0*I*a`M{Pj0*Y6hL1&d$1!YQ9rl z$9At>x3NE>Lf7iYvaM4G_<{(1F==UOcnGQ3ef}w$H)S1XqD<67{=$IX_DSjUnrp^RT`^9<$(UnVGGoUQz{$iYd%WJx&C5oqmM{x6|Jf&mncIO6=bdR zS}RXa20@8~uS`_Egu)_|u(7ke_j}W#{!SsI@(4rtunDn88`$S*hYmLLQOlHNg3K-*iSd9%}RJzH*NE+ z^_*x1ip_Xp`Y$Er71#;DR!dD7TnU|O#!3K|V$-bM@PIu}{~xC<*$1xdyWhj9z2!aJ z+(c+Y7W5}vmM&ihJTt>m;)_SarhHO`?zo6|Yahy=RuhsUYKEwVCibCBY^N=6?M&Be zv2!e&8d=LaJlLQB4Njw(X`7wOS25pGvo}d(nO^&Cfu_2E4lf#GX1K%hvHPrAmwuyz zg;hWunL|GPOr6ZY;dIrx3N#?N($T@e&!n0kIr{xRX}~#YHr3$W&y<=<^1sJ-;F^b4 z=afi0=ZqtbEab~}qB&HFQmxqw)UWYvl8)bNm==~6gIwma!k|b?c&zg%s4Y<@u$ewa zUh&E!4QK32e*;aEwFY9-Y~n$ls~ySUN}hr=(LpeKao_FJ;oi*(@!T^xF^QI9C~-i-CvdYJ3)eU zR(73{IJJ||M7mkzVC-!iv5vg1Z){JY4qGejgj=x;-IGR^NZ+yj`#UlG)m+Z^F1@hs zAakl>#({<>WXnseapo6pG=&qC;L78}m$O{MkEaDhnqXpYcP6N1FB6s(VR;5i&#r{*sgVf zbM=9fgvY?I=X>#qVlcyV&w(_7QK;Z|3Krh-5Tw#-q9#}`gl&YfsSk*#8i%=oY0nfj}`E%ByFf7f@m3kD7 zde5D2p1FXwKOefjZ%E>Dd2*Chk(d~FVnUTdSB;^v#^N&171(?-TV$8){|HtWiaoBz zJ6`&~FSUoHY%%T@$?cB0y1Tpuf)P_iPV4PHo~u-$2o^(gHza2vLLC5D(3)^LmCqWb zuy#qAa$XJ)NJ}a-!n0d_g5I?!B}Kd)ra7{N-@PHG>8R!mb>H5-uMdp6VCj$*(!%~J3o28bkPu6A(b%Fgt!Mm=& ztUq0t^u6&k^vj}rw{nVGD;>B~9f0{LaH0x+3>DSF?OAsVR8FP<{{-rWmiLwOOTV?2 zSirz3f1m$}$8qDC)9Wu+DgsYS%L%n@yJez5h!L?1aS@{3 z;|Xd_Xw1;p2eoXygi_`nfqyM>!=3)Gcv=Visc8a1f6d-Yyz#6vU2DR;42u==iZ>A% zncWT_7S(KKH>~T6SZl8RwNxE4tmzjc?vvud^hsMthj=j@!KXB+4SUt5L+s{+pa+@X zrS}Q{oQlgmhH(tpoQxZ@{Jj?TIBr7!5=P!HH^z41n)i@{A3sP#1eujFPkOf;zs?`U z5KDY=T)Q$Cd^z{XbyfP_D&M(ic((kg(poffTw0sOc~`&jj%-G6LXlCuOk`i*A4GKD{gC;vDb zWEeCqI`uST7zu((R+vhDeB+))luz=VD<)Z7yl&&nI-em?zU>GR63V=S?To;J>%Ok7 zwpq9HQt*j@}i^pxbLn}Ew0EPflZGUp@A8quoE|FNF+fD8?bVyu=06#W%7R> zUE{qpyYDluMH!$O|^Ur@_|+8ds6Z5qR!U6|1WLg zC6Dbuk)OSIASj75l!*DuZLv)lV3!j^%t7HWl!2tlYF|O-v&*gd<6-wvc7Q5JHxvQ^ z;(&+qsB<*+O&o1xC!7~Bds<$iK6Je!50tG?0-wA-rK=du*_R;>emC*4!h!DdQNz0_ zjFUSYF&D(hW<-WSONM-1M*P{sOx)U>8V2&EFls$)=fdlAvq5a0h*5VxMTW`YerU^p zx&@y9;D4kj>(Ph*Y5za^oQ=@+Ea3y*1*ay)xH;5Ifx;(E2<4NuPbJ!bfg8|!+Y6oA z>+8_{CoMPGxNrdU?EQuC(mCF(%ENK07V1}9ER=1s^XM_&kNU`oRa0-8otvXrm!%fo z)g23Spbdnj)u2)B7}hX7aEGv$FU~7&AY5%BgTUVGL2`SHs82`}H_#w`lrAq^A|)}g zGHp*dPipeAt!0qZCiX^zaZO0&BJISLvghNT91uYjL_JsCwb>>cC(od|nPyM-*FK>j zVLSK<-^J7Rmp!?j$#sUbNu@Q0UD^J$fKd%IrLQa9oU+E>^x5ePK4s8RHZ%98f_}EC z-e}#qYGYz7l316R>pV#I&ziS=Kennb=7yySUtUPrwyjlHSv11+aeu~gNIR~)4L=;2 z2>BRqwbaY$^~4|j(3K-}51~Y(vm4u_F?hw7B&Ljo7|A(pHodsEMn63~K3-A6bvg+~ z-ausTnvv35j$r8%%8z=vzB|H8(u8!2_{7JoKZpzPh8q$=sAW6s=dR+_-4o_1K< zP+FL{L@#~$maHMJHOW@x3nFxvlH0gkQ;K? z$cN^9mg4>Xs+%rEUY0J==d|^#1C5FvTP*ekPV-`ps8qKeoj+rEr5|r%>-;3feHsgw zu?7yXo1HbM?fhQ*a4~<@wJ_aa!jvrw=0n4AQ!Ul`X%I`)5afCrcK`tgb%C(XdYDfa zVt~P*3<~{8UMFMie@LVM^{VhI)Ms(fX-+UK;%>eNM}t*x$@KMHDwl<^ z8=7~70Kr9;x}GlwdCv#IVy&Sh^$UplmR0fW$7`H6ZlR!c-cLmjdQ`|JJg?qxbTxZn z!z%eV&00;dOV``u_l0l5D4%1g&HLC#DhWfHkp$2WNL3RmH$ZO%(n+ycSis+y=J^et zTlrN$Eb}J2v$;@1S;E~rvHwtRLGAQb>3M}VEaTjsLk5wq|NP=rM9XaTc#!KtS8Kao z+_f?nsi*0)wd6bDbbptHb#!=5AMikpkmjp7N^C#s5;~7{YKy&#q>2JtvPhdD>f(Q# zF^M!~6F6#$hV`bsVPotj=C|z z=)2x_pSUy1@Oldc^foK(`cjh$GpW;qDe=KWb#Fz=g){++cbf;kJ@3Nw)^c!+lZXIf z0#6Wy>65(DDrGKs_x2lz-sHpIJQ(?k8UR&qTd7BD?R7Ngy09!aAAiLkC~zk3wj%Gc zOeJ^U^l`CJy7(hCCV|PRk<^9YDgE7%=XZfdcAE-L`A0BB@fwcdM1QC6X?kMB%Y;3p zlT7cu?pRmdc4qnJpf@FDXc&1h`RST^DOa($(wYT%0}+L4?3X)GelEi2D&sx9t14w_ z*dq1n0S@wXPRn0)O~VPodOEyChXefas&5S_-e;samzVJ>Y?(K1t~6yXa2Z9rE(@rU zs{SnMBSAln^q0*QfB!?r#wtHS!bD!?c@*$G+n=I~U#$ksiYl>CchCEFEqFo-LJh*h zC#zos>HF8G&tJv91v*3zuvR%wFZpWo@L)6hQ{=H%DD;o31SQIl7Y(fndYLzy$y4; zA>3~ghp6(Nj;;6H{m0k<)L%A&NQ!51uYZoMNu#d{tGgBqYS@&Je%0IW^V>ouTIBs9nD@8BeFchj% zXX&#S(T|_#<2#%eEYf0zl`xN*ye3c#^9SI!%*Y(bTGIi$uBYD9za(&|YaJG7axphq ze7G_Lg}oq_)s1eq{?xmaw0-7`r~dI2p8O4yfTsDixgk%X0dQ2Mf?nL8!p_{1WT(tt zRfBe(fAjGISBRzqzdqk<^FuEW_R+lx>WD(~2NbGcI{HNS)-abrs45l#1Xw&9>lE;? zyr;67=kzxcQN*!N)PVKho!!KNXV89;*Vfq$Cpz(wlo3waVA0VEid37hmZ%Xx_E$`+eN~wtDIk*2b~y5LFn}Yp>CXxw4qZ9ZP8PQ7aZ!AI2d3K^$fzyN1h=kF~<>v+cfp6TLF-|w-V?Qa%K9^fo(*lX^JnZb<%VQn_;QvAJJGK)i<7 zlq<*l?}M1s1f|)=ffQ;jrSqaKK80QpQ#^yAxWYkf~e@`46$3w@aW zWOwwjZ=H2gKV)57_I!YjE$HvPd*~k=x{e>$<#siDR4izy-+VmeSU77XS71qC^ydQyUz{`npq3U z;(DV5)Y3$KD0BbWAoQ%3Wm4lET`nOdmTta3^iFz~uA#KhzI#|2)!dKwG*i;;J?Zop zV9GLn3;-Kc~=;Q(ZQHELD+7JEz53j1yQ&dyr2A<4A(P1wjW%qE;+s{ z&JzyH`P?lKvEE5q$Pq(-Ai;aF1HL-Q|F_6UNkG$rSy;GIF6L5~9puz5KdQYTTYdJ` z0p#{&nc2FLoKOvx(br-WQ$*b@o=i7Bza<^)GxhEH zu>IF8-Ck_ReXkYJX@moL(wI!&OOMgLY+$vy7GIndGZbL*A)%EjtTYju&Ke#6s6XYw z*=AA|e{z#i&~ir?zskpBP@Y-QSH1r&o-2u`n!nSJlvWqGDB_)}QZ>%t%yB?kU%m20wX-YS|WnvQRF+R8%^KUQWpmMy{I_ z6H~W+*?;Yt0Z=GKWc-T8=nk7KS_qwxc+ceVI0Ry1+zQ@70TD1>?6h#+V~5y&7vKx$ zXkjM!Iv#lQcrf{0{=Fq}pg*JB>8QleB3D<7{~K7^MFFqVTe%lJ)ZW9PzaeR2(a{rW}@P!2wB4hU4h3ZJ9EZJYTQT|`&r0fnbccnG8xY%4`^sr8m zAz>+!(gyEIk9hCeymHmUi&I!*sGai*lCO5ekd9kCZ6L+UZkcqAZ4r0%`Qi%Cro~10 zXw8do*a2FmWDe$r@g>5CICQit-drY2HSL9sK%9#0J41*ii9TQXZofmGH(wQYf~3jw zg?6pgruBt8nr$6%^?o6dl1K3VfE(svr~u)**AylbA%0G)=aze{nFW?b{B=T`DFq}9 z4X`^vf~81{%##k#3QyV43*QaS+EkgijVHt@QHLPLS_?c+SUtV%#)d`)hiOjohpVIp z^N1E2?ZJrXV41SPOWMhk0XCK4!iu3w(U50IbZ{B<@i1Gfv+Y(^rn0GNe}g09mvEl0 zN;`w5vDDKLn_w&VM=2>?V>&o}>o^TU%V>zzZoUAu`>_Lp*sryDxiWD>(0YHA0c>n- z7t!l9*y;vO$h(0OKQ9zGtDBlCTTSGD^zF>-$Nh>9lK39d#iOkJI?3IlO(dC%a&KRM z&$ji56}A>2n#j+wiCpgXYNV^#ly`Uc(_2+4hT9&N;H#K_2I`aa(#)v$9hYw--|qeHTgLU#QZWmd6q&$HY&MUz*Rku9IgZ#pW1@s<7CJ`+4U-5j{yK{zmzTdxbiOb zzn(_0eNgGyncQDPdsFUf7@Dl{JIUpCZPiYq8Xe{o@i|rQ490aq0SFQ>HFcL0e?WBtjrd-JMlpUzq;4hN$?s&1#D_NsF`QfFA{3J|zI6$( zE59x&Xk9X#n+vX|RlZCkY6-^_p|OPx?Ea{alP#Pw?22dgGj`$Yyvy#iZGU+>d$CTl zT-7-Oe07UkQB~a_a_h&DC??W7%6boH-w|ok0ztlq;Fw>q&SE{frA%Q zd4Y_cH5{bAkF%_>2zRhRjtw7rPz#`fAC{-V!c}yR@A^b!*2O!dZ*PqUCJe74fy=Zvm3~5= zrxN5?O3c#6S>1zpDTbO0=!rfari^quRv{>%>(OBnc%UwGs~5EGUHz4&5uhnb*mtXROhXiG z8rJ=8S9w_Xe-Jm8ap*VHAtn*$UGbg15d1~|=&d}2Zzoo1%ER02?scaaH-z(fNjqw@QTHcfI?h4S&J6RoXyk417I3Cx6 z-{Ou9-Sklo=z`J8SfZLsueUrArGd4tS77hKxSFawSk`8GoVw7GlExyLiuzWkn|a1- z9lb7I(twFy{ag|yX40ptGCSi`m)^JT--oYb-~PLjM4T6iQzn%s7nq8Otr$h4@>OIB zrfu3O`Kv)0jGS->@N^Cyf-TKt7N;C}9xqHG$BaK$CILVk92``Th_p~uKYXKgA;svj zj>!HU+3b?YJ+Ja{BIay%)G!dUrRi|h3{)W7`uJQb=1g4b!Ee55cMa;iltk>w|GGAE ztr5djjww@ao$h7VYse>`k(QEqhwQN%N^8>VV+9Pd2o-APo8usIdSt3Ug;~%;N$Bt zX=`=Azlei5_rv~ldZvSKOsRTtv$>wFywAnoTc2Z(bS<@}4WT?#HPn6LDx1#g*{7G{ zqO^o(Hcm=u_NB;-DiC=-EGdpF>2=w0qXpCIq)4d&gQnH?sVZ)pU>0u|k#elk-c%I; zsvLa2rqozr5uczT3S2R)RMu95&q`%#6>fjZbpgt%S-_K98y?H~8;-YcyzV=m=-&L! zH5o$@oc2HB9Q_h%_#y22emH*p!)aN)1+{3*yl@z19|wl|5D)xmUZOYbcI~~xODRlf z7;jxUf#bXCHIJ8F;O1W3P?9A%m@Fgj;c*CKbCZy(-I zfsbUl2Q-Ke8e9trQpl0dS$Oh^T;ngq3k-|=&pLWk6L=l`S=7HN6xSnsxc8>+&Gx7+>nd^{gz*fyQSol{Lt~3gYkY=5>u!<6zp0;f!elvlYVY9PZ z>V?0V0qi47(xSwHXz?cO`l%saQdGqoAh z$u2lBZzOnJbro9u8Y6m5GF&c*ze>b-0Az4!m>ne$^;A|QX!YLC=~SgWvfAn=>{H2x z8@5KYUmixAO5rA3KL|Z|#`lII96=NO<7`id3-46p8LsU>89PAy?5#^t^z-0tvM?-~ zP@cdlCc@ObjGMex=PbdLtQ>-^qpYI_z>@U0i2A;fZ$|`x07v-7Vnf6_YgXOj0AK!f zW28DPXCIjX{_NmuhGr$RE1F8qyK~)r)p-??sZaWZ@@Yyu!RrW_>BCK8%R6OyMf($W zrC3}%PDYR)oaC3NDKh?ESftbx<(F?=*Na!(!aPyc0j`>CDm3k>8*CGALd3&~o2DlH zCGI!8ej(F4uE}1s&%aT!c+=k>aE~2dFcF)??)|r7%t4x_iQH#BtSn#j9@flPO3FLBk(KKFW1Don!4M$g_+s90I$=Mja|Yrc+@7wo;n9d0 z-1O&^2W%1MMXdOowRf75$&Rs0tVxicrMfP(c+{5=Wi`*9Fa8ao{WZ#4ghS!ajB;Sh zIB~;!HK2fXfa9T0C_9?D%<)MfhM(nE`w>X70Aq$(^^Nb?LMb-lRHWHp3-=B)sw|We z>Pj)Z>>2W^z%u4LkR_thso&O#?i|}koq=TIp#LTLpr6L!1A)&nH33Zc(W-4|3}g_hE>*o?QEQEH(_dW zlPBAnY)`g1+2&-|WZTwMlkFy*Y`pvVzn}Wjb)9|o`R%>dy4Q_xpkZCiX_6Cg0>$3t zHL$!UabffhC(@9MX_|M4u;}cj_td(Fw)hY43i8RY5z@9}N@eS|aM z9=}xF3hMjQhv$mPElwJEK%PRc{F2pK>5XF)=DIy>i){VYHz=#E7o58t%U_WK1z9H2?3}`)F+|P_Q*6hjK z^P3pI$0cg=@mjrAc}I|L1a?)K_{9d{h@4LGMKp2k*;rQLuOjp3-v0aM<$q1MXaB43 z3U*3?|M!#zT*dx27P$@9EUu)j^}sR!ALoit+bAWHXN$Io?eq^O3P8!o1gZZ~m!em1 zFkxFahuzeKBRQ^!aCeTyqQs3G!5UIk99oSn3|hI>7(Ik)-g@d&X>3_$NGJ^!(8 z1ee1v#1c=V?Q%JkZK;We*%WV`sl>O+>gGniL_LuC@r2u)h|kA+rOyM8%Ce;)tEnUt zVU&q|jU46x3+2y7hS~nNRV4%zYNjPWSw9av2cbsPz|0qgbN&?nkV@>vChwfdwe1x4 z9jdAMpYe)$3Z~Icdb-Xk3s(z9mph*R4EK{s7dDzf?-}V(4x7U|lXsa2?9I$Oc$01?+`Kia=0;I^+%lYDHho^sH?^7bh}OLjXtq z5OnNC>zy!0(AALf};TLRZmL9@%`IzU8RY{@eaJS4)eD zoi$afJ(vCr{!^iV*k-#>k#~Cem~;u6oAb#lsN>@f-g32TM`g=g9b1C--_uB1{7=zMovX27QWKw(}=IO8?fK7Y6%6K~^#40L$!OsB0h?#<9 zwqH}`Y2UKaK$wQyq9y*pbsCD4lr-NhBA1&LGVg7u+g!gW=&-qLPHjGkEfe*wu0%8y z{#<6(4OtVAxp(0DCwk=gMYyrSO{Pp1vZE^E&RrLEKy|QsGq?fJpx!Pt=3_Pv&vfhq zUGE7YwQqe`Ir|&-^DqL9vlJPlnIXZ~Ri{f=6Rt=$@bagieC&6qSm{8BrOFm%1mJAk z1J&5v96A*dV#zf`rK0-VxL@BV0bT_PA`WV+JrzC^LdZ`zh!cIRa=KfE+n)O~F>F2s zX+Xy(_Nh>et2tq{Bcf!3D5A=KKu#iIc)ih(z`ge2_t0ij=pf%c#=h=k$$`CV7q*n@ z?0cXNNM(A9QQZ19NYcnBQtp8qi`H8z_w3Uedk}yWJ`>7*X0$StL7rC{Qp$%C?7d^I z+URCX?+fSL(nk2Vb`k~9G712FT02~=+qrgra^&Iw3~}qXz#|Np_P_RrN2JBUWTKym z&33Kjg>mVmu(&Si2sgL=_Ubd_X};uQZ16+0&C7 z(yaJTQD`p%(VYj<2BUE#ZJ7^Fe*z3WdU|cq#^L!ex{G2CNpY#`eDW0VdGqi z9cs1vJt`8h7aieb9wJ!fbGzT0m*e&R*uS2cD{5F_B@HF5j(1(R6$Xg(!z)R6O%Wq} z%$N`|m`C1TR39IMy*tIk3j&j~w!-k%n1<#MpwMvWH6<($yPc`|sPnxYif%u(Vi5pB z<08ceHpf@siYPi0=z~v95F)5hp!`jYutmb>lUg7oWU&@ok3hSB(RGg&)HLwvveusHG z1@!H8-3ngwN;pRYLlJN^^Uh23#7^>pyZ?^fqB!p)ET!|W#qs?L?Tn)kYuz?Mlru9# z2vJ_$2}w7Scz^u@XvhN&@LNOrf4Nyf#TZ4xzK~Ccs*GD%TW4`0LAdTi2uY#jzo}G4 zOuz#jjhNjQX<`a?XFR(7ea}5}p z!*~6|{Na_cc{6J%CsPy2|G@C}-uQO*mV!L6C9X}wrdh6rttQKywGbf~a4e>}mU;!2 zMrVTi?2QfTm1cD0eeq50w9R5CML(S{yA)C25kr|jDn(lFU z;(^jYX@C8gO?m%5Y0dnE?J_%)7v%>>(HqW-+&rjnixAlz&tOU1xM+lx&g%=&4L%AD z4LvM{U25-sl`$$SpgOG}lNeB&p^t%|2n6yIL+ zY{Jmc5pKw#XK3k)9Z!@PkOmCcb771i|F{(V8LA0}C`Lgkj@)4b5kiS>$H?`cOa()bskWcg9yySw|!mIV@o zt=9K#PklWY-+ncOfjo+#6$Ti37%zuJkr`0mFY2gElkhg1s-S{%^*;B{$q0BA*R$-6 zw;5Mpo)PjAJkOP_D0QQ8HV`Y(3bE*KdBc`zBNy$3S`}8ew@q(4UHp6pH-r{ze0y?| zW=Z!qGuJ^l;eeV>p2%_#lo?@~t=Okp-}*LUzxCnT6O*7hZHr`;1^EEyqv?O-2sEcW z|)(f8L0GZE`R zkkp76FO7r+X7vVsYGSGLBeYfL%|O1;G06zoM6m5Vo|efjd_K5HJJtvUQzrEN&|LEz zMBy0=*2LpuU4W=^RnlF%;e)(+dJSG&#S-~T2cb54>_kwIFu`zH>CqwHiZxyq^CQCO zw|q#HuP8FGwakbP4*ABp?+R~MHm8pkB5cPyu$*uswiJpgcIZHcO%z)9uL@3Hcg%4ch&l^7O+!Z$r7I@CtBX~0>} zJqQ*ifC(FEPum*3tQ;KALCt24^-3HDY0>hHxocU%0t;Bj{Fbl6&z3Jcq@y%m_&t6~ z-kUX^a872jyAL}Y=Yp}-9x4y+@LkA)HVK<{dUX~Y4%Za>+5{Boj1mE&h-&Mh@D*qG zt%5x4aymAgq4iCG9;4vW93aRavc(?iZHG`}jqUG|Xvk(TTZjKw>XHBkG7wC~EE<*v z7cB<+9{#k0ixlFBiwOakH0%jMmcf~VJU5=j0|mU>*!X!u41nP6QrRgy-*n_@~WmBs2`vCutf3_XP@Wb{X_FCu3D^6 z+h#emBQk9oRk~i)W{Obu_YZ3@tw|i5;0=&X(%Fp zyhcbd0hWi)GlFVO?x)yfN{vhQ$s4XDuAqVKY2jY~WHdy`cc_w^9iS2^y_Y_&-ZaX=p)t_roSo(T5B2 z%1yd-t{&NU8ubHMrrwgQI!9ma=K_ak4#&`9Ssm-cW(STk_%~#YETIx^Sc3Z#kYvSK zzCd^I2=6v$Q*XK9EUq55TlX^K1Sc-*tky1W2*0MD%L%v!uP2bM>ypker}CnkXPoi% zEk}*SBZiF*yJgVjW*RuBJ+Y8cphXa5T4y@gSvG&6eCQ-o3G{ZRNVIGcb%^SBM)C z7#R$FvBgGnP4axk4`~n;(?vIcrvL|n6LPQVIu}I2`AlAvaQB(!i5*Kx!1;bmRrsyu zv#N%mn_G_66<)9W!xkmr9U@ zCixlT>tJ$oV3!5S)yu=~nO0csQFl5oLORfZJNPE9Emdba?5I6}L~FA1_F@IZR#gwO zqYH4SzpT}|)lfW)Yt_$W_kweKuc3(t`j;U6l=0<~%5n%yx9i^0s}OhBmg0f0v>$ti z(A&i#BQ_fKgeN;nf#8!XG~OO%%>>I4P^>i84%@pOfkj)-XbMIA&8Ibf=E&Qm4$x93 z$cTY3kh3S=*DpKNPW3?M7@z0D+VS~a;Fyy{5hFg~I1*fMrdZbQbIcYC;bU6M^3@r% zMvl5>#n+81d~>H1{ZK@oy&1Kp8!?^%_v9{@+{+4(cqC~}3%sgI7zp%TMoF$*Zt7Td zS<#MX?9_nBvkJt(JxHgSsQWp5!WSv*;dLl7vMzO!1vQ-dEI)Cq=q5}*q$L1dfQ(pp z3x`pRS;Ab!mop7$nUda) znjRy^eVlOiVBTjrX-oEi4Fl}qf08+OtS(o+3VIbs zNp$|)I;x|8@0(G;LfaS?vv6ThwkCG*BoNP#N_vHLz1ng5HCK{Az#;Sw+?KK&de4J0 zB4LrZ3^13vjo4Q#ViN@az^aB&GLz%06f{YTS^JzTW+7c9$Bil&5`9fv?KoMymSaw< z&D+Qn8vcO?ArwDxF#YiG@WQwceSC3&Mngl>zF4hy3P3v(u(boNE&g`Fxlg(Qgl@$2 zFtKcPDazU7+6Zd0(Z?iw;Tv7Zg#ubBIgEVV(;<@jX4_-X_^PDx3HVn{RU9AV=VCRh z9_1D=2siIsZ^LiV{%_(X_qnu5GYNk8+#|6Mwf7rb^NvgU(OS$^N|?^pM8nV6_wqWU z-(0aDJOtafV?o@?{iP<~CTb!i2JxbSYs!!qj? zH^3AY7UD(b_h!Nbuwa{8B+9OtdYUQ8u9c|FDsdXoVWA}uwSZniDgk^$P5uNBV}|PU z2`@e9ll!~Hm(_-ADI1e~Y4m$l_+|Z70U1>gyldvPT>cpO1cS7v3R4|6zlvQ_*8Iux zJ%vvxHRNiQ;CE2pU!~OQ8vd!{K6V?Qt#j(;nr!Euezw|&s*&|oi5;P1y4!Y%akdYc zZX9*FCbY^o!nN^AQ-v}(KA0g6i{ZH$6F#&GDlHAl^hMtJt*tH5kjyt3_lk~=4tTq^ zgH+{WHQ6dW)+NrQCxSQ_M%NJx46LIQ)Iso8R8wKYk0oMYMb-g#{9KAh;QJ5%J89+= zU96E&3)k#x|0msUb z!Y+CV!y?SF*^fh@)T(gB`YU7ey>S%WG?{6o$A+9q{dLs^!B7E?63}Xz*(sZtb+&6? z_W*9J-=F&5@|*@m+X=qsDE&Ix{&@?({T8w;*?bjC`Zv2$0gP4TRd30-T3$V6+m2Uu z2Fu>z1WZch{RrhKhxt7?+{mcLw&VcI9iz)_{&siCPv}Dx9uV6j!$4aB=6Y7`=E&bw zeI0k(k0`-x_h-uWBQNPEBouAlZ;g@6#8?0W&Z`2V3rmyI<}tqkb~yHz#hDtq`RC9L zZ6jJNC<0d2mSz*ZCbVQ~{0zoQUjv!tn(rLr6qcEXF5!;!P(m`&jJTqb^3i-PlvzLQ zxBhb(cn-^_+pp$?~TjQ*0!eo6h3EWd6r4xeWMVCznRQ>yl^ZMlReu3PcN(B zU7`7udu^ByX7TcBg;Ccn6!)915H;wAj0!w6@8%?G>l+QgC zEylp+Oc|@F_gD(k-;hkc$A^7d{j}e08Aq{d47HT!qYGH@FO*)+peL!JAudH45=CP1%&41lQQL=#m#&T%a93LT9umcG~omE1t}e`RTL2ki^IBAd{mmZ)fs z2L#PLMj%I%^29~1a-Ww+nn%UwH~ih~AT=>z2i!jS5_1lfbQKuI5t7wD5^0pLk-r>v zeMR}H$p{B^JB&RXll9{s5=Iz=f`;aIv-R=f|G|%g;LiYfLEAexTt2qDo;o$TA>)?x zuP;ZsUqVGiz@(S(WGCs>XMC3}-6sEY6Z*!aAd-%a)8VN(h*Q~sDYwT|i<;bG@aKqvDCv+wRPP7gGt-@Fm^3AqzCspbr@RYa(ggy%G z6IUBBs7Nbv`@c$ezWqR=eVKiO#A43g?=-1`Unb~ zRBM(6&f$!nC&a>*9HRkmyHD-iRmQO$wWw#-T*h|RBVKQeNs6R>*#~qsXYA-8*)ptn zm1&;lTdo?k8?(dV<5P&b=JL(of6hinM-fHFt9g(sRx|dY(fL6uUEU8b$tq1(N{E5e zxT9U3q*_Pp2`a{G_l=Dy;;vSl=}=8HnWO3Q`-fE=zPDeE-Ot5}3)C0vNGt(7^w<0I zyzb-lO?V%DqIq!ZKWsezbY?>v%aQ}Gi{Ta3ob$AAYcu6NPprHB+=%Q!X6efSGwo{g zbetmw@OZ`RerGJ#JGJUIqZJ9=JiY*T4X)Ji2WCVjtvC5SKm6xXcCWEzw!eTy?OsD$ zPjd^&Fh3~Z66mwUa@cNOK(tzF)+xuWbcv{dP63_Hy%cupN$pmhwK*G7&eGBtX0En` z@JL8+T828xVqgr|-VTIK2VjaF&VCOzGqn9N)Mie_W*%1Dm+@K8CGGtZPn}k2^^Jjq zws;WzHIv6heu5G=hBCNUBky8eI}HoaeY~gXd;PmX7kqU<$N3eoa2vioglBN(tk+VR zL&UXIM6Rz91O@Wqvf$G*uTV_GL=Jfiw%yk4NI2Z`B%11RM8A)n_Wabym$KJiSmSuy zN1b~Wz0PaKrjdgGmGW(yGNw69gWn4{GJdW4VQl1SOR$-Eg-A`PC|gqAGMXZWwUWQ zG(r3OyH%|JMVQ>LRwcbDJg7oWgj58(63po6!<`T(HI-bmN-pIE$_-+?cj3UOl)w6wn(c!ivA&gy!+|t`k`E+obA7Oej+R;;=>ctpJ%|zLr(!^2TID>{S_c&n#} zg^lgn+}gTQGKS=5u`N*SsN6eyN)DI->)+oMNoM#GmM^-Ls(A&s%(5FBp+6$0z}J7` z$>g1P>X!a_-=gko#ugc2)!9xOWBeIHgaod?TyZ_3tEo6{hl~j=Y{c{fQ9;}WRqFPnQp5c zYMYUCfn$woT?RXIQ5L~cauV1;$97*TAiS9)6|sXuX@ab-uSYq zFRmfZO^bVadU}#&yAZ)oQNFKf27XVvU49VOdhGS|^?7k7=Fu+S@I&jan^qh8kSvV#e0YgUAA4{gnu#g#7ceRjz|>-9!*FY_@~Jg%aTW|N6vR5Uh_ z*osI8t4L9RcyfJaMu&K6QGb(U|ED>?_dR5J+HrYm*jf&9UYch45T@V#4r4K1`ckRL z-u~f|0m3Vxbb3%QCToREpnD*a#yCV7ln+bB`NW39a`~pgbBniC^bs=f_~iM^4@!+q zwd*z zhF#I`qI36$v9XB%nF(1(pX0C7AI*V`KVSbr25nw>0xxh38cfQ>fR0(8nyxW3bi%b@Pw~BW9{w#lJnC^b>+JZCM7tlHfN}l zae^#4BF*$}y)99_ypa!4iSi{zS{E|`XJg5a$GDG2zMnsTI(tCKq=RjqQ#AC)P*G4i z3!17kt^s=#?-}h|eq?ca^_yLTNpKoe6(=O2^#h@{-^;g5{}=tU%0>05?W}Lp==g%0 zQwcrqw9`I2%BJ?2=Ik=Sy#=p2n~McBH6$Hwb;4WA>Z|}$5oqzv|3$nYhmzOi%KB6I zMgy|9j^xU}SDT#o+|Iy+E7s!v_4UZix{mh3B$z_gd=TcOBMqWzzWQi=X)yJcfJl7L z{OK4Q!wy z{2y`m8Gp3pje_lEex_T^+l+*JN6waR?pA%gqcZW|^KPoyZ5~l34+0Xflf9PtYi96B zo8v@*)mde6P}HYe;$I}JEb^vLPQmOCCO7U%g<$KY0J_I0;V7h88e7zA?3mVb_7zUg_9{IxrZ@!f1 ze-np_N+zK)Uv*x1%fPbv49H7{S2gX{06og%IBR?NJxW3+)5R;hexiKUAb5?PmdNJp z4-Xrl=f$YFzMtka>lu596&cRn^c=J)U6q3)qd*L$xuRjJmnsJ|GOFoBhD30;r38K# zQl5#)5Jx9>noD*S>pe98A`?`SfxJ`1~2I^6W;U8B$SH>tOY04LuA01ETdmkCPEeE zMcx4G4=Ywv`C&Uh1aL#XIqi=^_L?L>LG=2c)s0La-aKL5>8@MK0-bKP$Hvg=t%}TV zLLWF;Vfu^Hb|z>=(sYx(X^CwwlrF!ZmiKENU5`htGsm=K94ae`S_{BPr*8_U^|C7s zUvSRV(PB1p_NN8+!vSsW9I4|#pBj5`xJjbN5-0wbgDb=TlqiMvhPWI^fw7rDMK#~l zwfun3_o|vbURexJ9EueidXGr*yoSc(dqEO425U!s$NYqiVa4{|@95NZfSuUS8nMyi z?j@NfVu%+ap3UFuy)_53;b?f6~=tI;#|Gv+v`u79XIx#%;nXVTpr2mRG*XNoT znd4kn3IfL9X4fUjII3x1%Okz9Rt|`L4*ll zn8|4HsvY1wKts7?%jEvUYHE~-mY(=@K-pZXWEO)N`2)6EGT)jSg%3%@0&uW&f*WZD zLH!F6F)>FCZfe89XaGSmno)+bp}gIGx&56zy9`u^qu6-HR05G+7l*hz$9r&Yx*bt< zS3o{q?A#l=;86Bts9xq#zOJf>apj*z%Q_QH6WI2icSifSgBF5K|4hT2O9LaD#7(kJ z5hJ#a*2ALW{k+{@WyW|%#X<;yW|_`z{&W=|d}~KPtZ&ei;Q>^Q+4pI}AZH%I^eeDJ z@$&KE_doetj_0#g(`8i#;`Uyo#5`!8RKdGcfgSZpr(a2lU#1Jq8{`MT;cyu z&-Tav1MWYs4Mt|mx;^R=EUzDAS&4~dMpVzVJWpJLhy8H|2e(__B9F7ljY5vH=%q}p zf9>+3R@{^2&U$Nx&tCnLj)jrQPh*6=aLd(g1F4_Jj|e~KFz5<3XP7XiT+|c zuku|+>u^PvF}5LJds#wO^-X#{T@rb>GP$9sC+elUVHF zma>G74M+P2t1SvjX1tAlLfl@E*JrCJxz%^?Q5CNPEHyH?zNnBlaHTUY6njA6^$hC! z{avquN8xf*3@-8mOt$j9UpS7)jFG|aHvF?psG7Z zK5V3rdBL}TigC38Z|F@4esBhFCmQg8g{Hxr>)?(ltE4_&wo=UGd5s!hE%q_erk?;Vo?FZ)KB_n+6!+G}eOTTRvq z_;iB&eSZM;jEfXM5+KGI&^Fjyre6|>rVB=vJ#`3~L9~*I`1(mZjIUN~J5E6vKVt_O z1IpWib`=S^IT>(`C4U>IXS1-V8$<3d$gW%jVDeG3hW&b;P9MmBu**#Qakqf!6bm#) z$+cG#-uPsz+;Nf;uKf38pe#F>rzd^fz!QH63Yynjg9J759wcih!`QjUQLxHW1GE7i zgg6MstUE#|XnkIb`6HK!Zo429t@IjD@1`9sA`jUJ{%g^u8>=9q`4L@xi}V4a=zy)DJtwe;?|_VtCq zoO#*+_8mX794HYpJ2Lphp9Baoxc*zN3Lt#%S>0)b)8&v4|HTCb0zT_$1bN!Kn%;{G z$yXZ8=GX9yus1kn$@+8Y4ANr=^b9h8Ygy7-Pc=l+A1M!iX&{HMcgBn^5FIWD0yat# zIi67n#Tz}xbn9KsjP`vqxBw~PO9>gCyonYCx==WqMG`0vYBZZjY-amFb&Q2rlMS4l z3~-x)Qzw^mlI?7NpX!GBpvPH-8u}nxXAs0%CC+Iqi8DkD=gn)1KX9C7dRF&y*)!%# z%qKyK7mpsK6VoX)gJrfU>{otkw}=~ zb$6Fhy($hzyI?N0OSpu5Rx$~#*-$v)SEq=8tyK4PvS*A3sB+DE5 zn7JD1BYNIWYUz&|9ULK z!Jy^lt}}idZFrAt*8OKSy^*JGLBIXc1X{rH*!l11jNWOsW$(*{dyh9u+C-Q7Qjs`g z^eIqFb$KLK49U+UJ}Ch7U-a%Ot5;6a5e^?wR!P(I2tR-&|%1O&rx zgRZ|H;;iB#PzX&;ZTtbcn70F`Elb&gMe0&m?=cHwGibP6?tt8E^;#-&w>R7ZE}IPJ zjg;f*HG~~oKptS$CmZ-3>ZTnPq4b+PRoVo}>wQa)Z+!eqO$kX$j`}~}0|oMs)q%oM&AX%T6P+foga`Ni@P4c@jH zy};u5tW6jjGbrkJW51Gf#HV72PZPw7OfbK`Z=MxPOBtlN-dm~mLW2{QR&!!-K=PBl(iH|t_~I4hW&XQFd%#V!vh58==pUQh_ZBi z@Ncf{sBa*c({%3cW}_3vfTs+aNr40vT1<8Dn&JxnlD#oSeMbH1k1C)Q&3QZRc|TSC zd;G?nznR>=T?&-S{n;Hj2ie6d0WGeWuHphoEe_D2vQzV?TfS1 zBlrYoo?p>2Lb1j0WCmg;8ghtMTcr1!hL%?g&(V52&YP_sy)U+)=xLX)%P2eO%7}Xb(n0tW%AOTqECrA%% z4^lK}${TnLHlQFz{ef=M@8J2gW-C{Y23-Cm^@-JPeQ;lv)~NGJ8-npQAhn}#a?%k- z7;wb<6W}}rSHNXk)yfw~!Y_MRWrz7mrj`H{FxtK!d&}Z>Ox$&%K53eqZiJD&fG=gO zcZSR1j*>#CfB-|Ov%QW-O3Tg}!j&NW1ma|*{ElsYfJKLK2pgSCYI-|0-?-)L`qUIm z?lyqy1b~SC2XY%3{*?ruWL{*zTKuHBTqOFmHh=GkPLSRp<>ckNo|r^CbEV?qR<4-T=lCTRzcfvxLiUhDbNQu=jO{Y2qcy1kjdy{9&V!TCr+j;@#m7IXeOuO$v04lNm(DU&|3uyPpywI>iDZNI} z#OnRnLW?Oc-|B6e%{rzT;Z5?aowbU-cfTs8ntf9Al6*6;Es8NZgx`$E+wlLQ3tFrQfn zdUuzW9>EJ|uT`|w!M=n}Igiq%VlgaO*Y3Ky*yOdj9hsIE(4+}g<6PQ@8=L&mEePxPoHl>Fbhjy6Te6EyN16Wum5n$r@jJqZFbc$ zF3(Q=lk0FJj^V=56l&X2`~9u{G>L`p+nQKb*)grMC` z=mE#Qs}4y5&wn06JS^JA)TGPs_eku5%6WI?m=hjeHRuefro> zQuKS7%wSJq?0?eeSJ^CspnHAF{dh_U3=AZ*tU4tOp3NZ#43b=8e5Y|advVZS7T9vw zD2$|x?K_aHRS!P5*=*5KDUuBf=Zl%JA6Aq<_p6YLi&8m>gQm<7%3ui^{uRR#K{%yhdQT6uz4p>jOeB5uv)`tHvSb0qK@ZYR68JUaw_I%s| z#Jh#G$6`Yrl~kqk@tyGgj+NF<7()L`e3B(VQpg&G8UTbziPFp~ydu{NIlxy}Z49bnL~1J`o#(TM3P0YHAi4)Xp_( zemXus{BIDLGpI(daKnIRiUFe2*Oyf$j*#@TXl&V8a!j{u-Mrs2 z2Fsx6KZ)--w{zx?-*5;NhctGdY*fJAe(l!IRX|J-`275BgrMUik=m7NlMy@^frTab z*oWS&MTdQ2i~?2bUNh8YW-Ur0R5=8MqjaMIt$E7mCMr%3y6%5IIe-pLb1sjyx+en~ z9nWhhrnHaGatwuGHYXy15n7Rij+rsj@%$K72LRWV9&~_yYT3??V8tsnC}TAuH3)F- zkI-%EzPQ=Hc~brVk6nLD`?a!&011La&aH{vxfWfq>9-5!>pD7ozV&TJ|k zyMXjBz>71QzJ&=c%j?Ww>Qy;F2p-iDa+0a@vvSy29Wk`xSvn4#8p zt<-Fz%(9dVIz8y3+-~v)ZzpKQ8>S(imLq(>t#p-Sa5`6ns9FNGXqc%#lFz!^9=3((}F1=1h`YT0`D}1 z@YA6|miaI7PH2(_5$)DZR*>34XN%{9cANSwsRu9be|HALdT!JFpVE#3_))LtzRqdR zg@_0rIkn}tbe-FJ19o5cnNLs%>@YFY)FFGm$G5x3-9O1S6E^(mcf@08xH60`{}6lt{%%?czEs&=_;WM81Ubz~6mjuOmU}nWLQ2pb z&EwB6^|7t(HfGY{WMau$B3V0jXJ8OpVk~dvx!L4 zBc3o0Yun+Usy6z(DnzF~FV{6LdbzmVUI{)W2|k@J8Tk9gvs{%@ST?qZk#RfpcAZvt z47b>>x#a>BT6z6RAygtjs&7+(8?mu5uZJ!>~jd#s7UQ`?W%}+Jf)3%@BTJj zf^GJ`#Qd-KajaV(@TcQk>gqG&xS=O+W*{U%FF$wlc3mo>MdTG7nbJ7%`Lc>zqpH&T z;eo&Aqi%%srRQVb|5-6CK)m$ryT|}6L|z;T-`96wg6jB^BBRGFM)J>E#L$KMinbn* zHlCb&h<6sz76 zGdF9&9s0Qq>hi#Ybj%nTm>H?P?4QuOT{Qfejw^4Zad-0@N*XZ-od_h4jWU1O2>-F4Z1T0o_s@~d<@n!I^(Yo$X;P&*JAbjyiV zv25OcA|NF7VXHpdlpLsuS)GxyQ+kxCiA*DOV(iOl0$GWcIO;yKYs~6k)%! z6~QrYnhQAV#1Y`Cgb`3r)>e5H#WtT9mDPOZ^{9)s?femF6El0;mo=f?w1hNsLxV=l zecAJI27EAeZJE~x>`05;!#NQ8BLDthu!kQ%H}~U!S0yaunO^oFX{UMLpKY1)_la^d z1Q?N@1^0(kH|GI2%9@I4<=>-(Wdm{XtRYX@z+SMX5hlIy71e4-wO@44JIakR8TyX| z;hTj4*2UhLw(3K^FbD?tK?Gm_z`_?wqMUnOSDm9+J|fP}OL@^Ov!+k|0!BS!k4!$YFscFLRAK50|1t#b!c< z{x|QuZz<8}Z_|%J$^q5a*lg#l+Qk4}Cik@i9xIQRT}YFP(!fgzTcLj zlkj_7-bAs&2Ag2|KQyUlQU6wJ+LF;P#icNumhmYMmZ1rvyMx9~855-Vjrq%#G#U57 z$0TdT$)}twxNPzTVL8eM)!;gf5gzYg-SGz?+n4S>dX6;PMDDxkynTOr5&Zb4=>N=; zzvREtuQQR&QN^Ai=WsZg(e2*3M`FEq^|;wy02O>*9OF8Lt$w|0fw?1e zk&snUlZE4pn&>wX1pb+Uhc~MoA{c;S_}2%#a99{W101mxPog(vqAaVYm=4yZq7zaR z5R9LJ!4}uYCHlvuJsJsb+Y;Mj60WppH?{ zr}+Bq4JVdr#@j$#y*Y>EPrYrj%249p-TqmS$2Y&ep_w#)sHIB6QvGtCG?i)TgD)X( zsdEx?ILq<1eiqOQ3BRJ!lK)zHVwM9=GlV0vaIp$S31UqhXWYtpLg z#&&NY^iSaOdN$_foRR<=`@Av`l!+}8^}fPw{>JKp(*O$)(7MxZw-H;Bxa9LmaG~=- zfm+2qAxc8Aa&Cwn0|v4!-x2A}`Mh- z75HhsLooOx9oq1F}DMGkKIOlwRKI` zJ={D8->GjzaDHub^IoZQ>jo8aCS3YE__saNy9ja}k7O72u~T&T^N*?6F7xLe_~xP?ftPV=ezR zOW<*c+3#vT_i5517Q^dJgVFu}>blCPsJga&h8!4#p-Tys78t+>5QHH_0VzS07#akn zTYBh5L8K)gL_)fxkyIK)c<7KEQlw+v?a%MWch*^J{>)i>=Ip)ixb}VBmsftF&Rw(M zkw`HR<)b*vfztzEne^<}M$cS@QV&zVqp%nK~EoD>AUE}%J zUCDQsBwf!c`11EK#|exPKcc+9`hyiCujnBem@>vs!9a-+-<(q)!(`gR1GPy?aY8~p zisZR#O3i{ zyye|_$fvAhgNJuYwv{-CO@xRNwQr}Vt={g3bG}uLA6j|#D0TflFSU`-$CNJn#J99& zF27aN2^8RJV;JqpvLsuM2bTs|x(5zZs?$6>rIQ1N40IdKEx*gI##_(k9h>&MIRtEu zhdT4kHGg0Oc4qELe9*{hIT)0d0Ag7UAA%qtGGV{l$(q@T%SsU^HQ$$v=a(+fj%qtj z{&@8nq8P&@UgFOE6`N%JG1tpY6R9IHq?6H<`2ojWQH*C+pifbjI_|(8KY^q}_QkUE41niEJXGilJoTy>Q`-eYt-T^+n zWTW*^CXS-R#-u+unHu5qh(`*yq*gEa^TO;_{!?Y+atboeSgh4_v;onT=u2_k8thG$ zLgEwwOoxfNcG{37&BvSmySml>f5c;yrY@A;;Nt=OWSey+fd{1?WF_@uj^aW!5LOHR z`fu*JUNd1lT?Hz{Sg&;`3$8vG6>;|P2yy-Dp|J6L1$`W3HN5Wa zj>QM~tg*3tq8PTOETj(GO3|hL0jD1)`%cJoJ2+N+zunn=q zEhk6|B^7X35p#IaOuD0$w+2gZ3^_0J)VzRU5<-PHRL|9wnnV{om>O%cG|uMkQQjZ2 zw)3W93}8^BigJ}+I5VUD-O_TikQ_~`^n_$6Q|7!6sM!z#9H*mKt21EKXYdcHG8Q&` z;os^F4wdJ^+5IK;Fqo#WWl6rm-G^koH+jJABnE|W&*Rcz?Rs~T_Nz@r4=&YL?F7Vh z4N%{(3z&Z|KOQi{Athaor}c|h;o)W`jdu&D{AqP{9}Q-q@s2|>$1}U7m@lFs6b~%!&#x*^6!T86;>Wd5EDMM^hU>ROu*+VZFT}9o z<2^B?SC!n`5-X(Ko*xWRwA6WtgT`I+YUO*#^rFSdYKDv4i^M_Yr(nGOrLGT#bq;>J z7pDiZmj8WsOo%0bI>iVj9Os^DWXaBgYRv0ku(MaA(=e#>Yw*)AZCeZrFOVrn#_U7_ zXN&JdNw4cnL{mr-zgz7D42+oQ7qv^jRXwwqF%^51L*HT5ZG=Y=bE_7$(j}mnO z1A5lk{r7t=6v5d49xWZDM%()g22ofW{UZ~2h?{sr&&XJ&A%%iN*NOQCNL3GFw%hdn zJT?qcb4O^a#ujzmo*8f(5CGf=?=%C;RAywx>SSe1=~W`OSSV=4=Nhu5!Lpm@NCPa< z9B>kBa=HEpJ>|Lh6*RHePhU@`tkj3FM4Af#WPkp*3AWjro+|$T=s2Mc8Nq7UxP#nS zVEgcAi(WO0G#objwn{3qp$sgJ#j0YX;dmng%Y3Nmj$G?J4IKNzv5!oDW$NYfPkm)j z;v1zmUFMhe_V)Ox(b3l!T*m^-_Nq z<=B|sxHJuDp*SIUxF%o$4iJ@YU(ZZuUG|#XI_lpISnjQ00-YTUS{Qqrvvs~4SvK%+ z&ZI^zcYF+?iLes-72tWz&bC;t^GDE|{o~_IN#~{H=~{bfx5LeG`F>}UfJ)0oQq!-{ zCkqRU%|Jp@ZyXL+_7oTdM9_l9M^Dp>ox*r-%qEE?Jrh~54aLRvd}7Dg)9-s%64c+F zX&*-9zkcJ2lI^#Ynr6I7RxTkFjQc`rW@MbE9YuwDb>6Y)1Z3v?{tOw`+MRZ;9i$ev zlt}oSUcn*c5M`&i$jU-{@MV5}{s$0981hHu@^Dde5OoR#{h)(|iksf1U>R>)m3QL3y zw8wI^Y>4z(wp(crR3L0U{8Ld8o3w}B+vWP!&w3TPlP{`txy3XW9`DgvR*WGDpqUhk zdrYY{e^~PAo~fiwDGfp|j2=u48l~jzn}xhpL5sPoI{EwE!vxGz;C@#o68d3BhR`UK z6Hw=wJ5A2QU#x8x6~z%mn6fdWQFyTV84-m=MH%6k^p$(i3B9mL}48SxKz`x0yjG_lVn+c88}PUVOBnjSqJ3qN&e%8Fs)b z(7rOQZF~r`NYDh}`P_uD&-Qa^&z&s-RMZ{*-4A33PyYT*NJ|(~kCb&??e7{detr&& zPOAc+`@a|2PY9N3gH3i|Yn6(us-x^oQ%bq5E32g!`+)anJ+?jjb&Tb}6?{d4LGkSY2JMfDBnm%~bW}f#)xfZb~ZkbS`hR`M$1c*=}NnF9twyAoGj6VPAiq*nwTmu6I3Etz+`bgbhgL)3$l|Qsf zm*w?kJ}brt?27$*tf-U}KOhz0)9&=GZQ^iBjkCr|eCpGu+K~zzpD_f3eDYLE?O(tD z`zVP^dR2fm<#qq-@3&bI3w*CNVI{1a3$Hi*l>?rC@aQ0gbae^A5l$=q5FoNgpzrHN zGJt30nS}67bGw?|&D>z-z;|?4Kt0M8P^2E(+}u1(n%ggdr-haGB)YR!W&&+g5xEP8PPzPSDA%P+~-MUibX#Rp)cg&-|nVRUc>f z+>ItW7WdW)G_$UbZ%BJwOtko&doKyK>d&}pGNYY#!9p$PTF2p{cc$xhMTZVRDW0vM zc>3p?ALbyaqoyVjDG+Vi+)87!s22|fK+THAPnUdn->j|Xw(kh_${enoM&j(`$3VeI~MbVL;$wKW3UDgC{wiR?U z#CsjjdI|$cVkm}T&dYF8Oot%>N;&Q$EQg%H^fEM^Z5G?I`)pM()J@O!J7ZjUWc!I; zM8H@n<+quGjzX!CN1~!1~_XA{PSLYxPkz70Ueq>s5CUOU>i;fQkON$N*wUM;#11QXOM4+tcckfh< zZC0csVpYhX;MH(X?(lg}_92CQqZGv|LXN6ChB-|CgtsUv$Vu{kGnRvsgMf51u}ydo zOf11;b+VI61*g;6>bt9Ij7AeHrl{8&;Lvxs_3)=*fRxLx2>k)ZKxc)aAiYCc{Z9m@ zbJF4a_JLKvllh(>ZEOCRmT_Xgyu>7pU8%gGloRngl7^s5sM=cXaV`A#vHJfBOjEy zxx4$7gLZ~*fK$2p?8bQEqyRS!(e%X`4gdZVOd=LqH2D>{% zKDQ0EO0qWMg0P7Q$nVF${l&B1^D4+|@+*zqZ1a5gI@ z1By{Jg9p-M2bTAL;eYX<3w_Qu3R(^I^-t}e(Di9&-g_(b^oH7#_OIl6C=!MUx#LIe z7u4kypPmFGlVU}!ln8b1T1e+~6`YF3v=Y2%b@0s)77@x6n{f>kH+KI44hIz#*;3JT5m<7x~l=|1(pT=WHFo_G_iDD!yAE3NAXY=P0+E&n9+!4 z*5AW@=ZAMM6VgD9JEia@xw*MZh6%Ud(<)Tfz1OtHehhv|p+y^GGTwgo zhL)Q}an9|ZHB9GjNCNl|<;HzC4AE=KkXz8Bv1it3fx1%MtGg_~J#NdKfq}0$9Zp~~ z4krljan0L40qLnL;+#&6vxC4UJq^I0&*OhqTJ^OEs*H-pEw5U?oRg4)cdvcKsL{iSjfF+j$WJUQOYB0Wm#l3zm6r(2>&7JLd#?{P!Gi^!mtMq;ZD6{b zGhAzNa2H?ZZlMAM|Xar=tl663%ITGRA#8tU;v%cOXUxGDO>V zgk9TV$Di~Rv$C?b>S}9`@`BJyPz>%e0}PL@ep1=FZwqi0wYPkZw(bR7ob2c5-C+n@ z+)Qt8&REn8&Vc%_>%i|sM9X`5eX#%nrn;K4rH`wjDSKQp*A7OP7%BmZ>-4Ff-eqP!~ z;*Q1giM4|dpbZEH2T*5eO1ArwOmL#FsH4LtzFsv$%>LxRh&Y{rd}UQpH}Gp418*aJZ;xZoVudrDEHX z^4OXj9v;>rs$*>q%Sg`>XIOnFah_rvm@1Dn>!i4o%c}~G5xfB6b&}wY`zdJB;Px`O zZ^b}JMO*)VdfR;QH1Uh!Y&_x5xTrPP46;Bh5{{Nmi}&n_gG^n3=(x^xk@tIO{p@S3^%vFMvQGl2sC(a>Es1 zoDaV1@wr5;H7Z(c}RVrXNb0n=YSI@ zY768)h2%uH1yOliZ#NoB*BjWL&{^GOh@Pb&B6#j6Yf?9XYaX4O^Z#5^eZE1@*1afZ%1@_Gby-r{ZGQ=g*%D z^YiiT*JDaW!#kebjLJ2n>mtZp15i6F=Ytqe(ciQLaXOnyOlmyYPu&XI_kOH9eb6TnVAl#Z@~b3I`jk(P*`}l`m#pT)0nco$dU7m z5o>G)WXbr^^X)p<-aE{7$e2#4rKCiSS6DciUqHbA{rmSvOH1aDR8%6x(CGP>b<;P* z(+7l956s(d`~Ag&$xnIMsmy_MI2jn$#!$}M_znf$$s10+(?@|7>Z^QN9@%Z%uxao$U-R`pcy=Vocp; X{r?ZpP_kcw2Ot$CjR&O)ra}J$c_$^Y From 341f2462232a5ecb1acc9056bfe7c511cd98ad8b Mon Sep 17 00:00:00 2001 From: kakiremora <32620809+ticotaco72@users.noreply.github.com> Date: Fri, 12 Jul 2019 15:17:13 +0200 Subject: [PATCH 1731/2854] Fix README icon display on mobile (#5341) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 590442f01b..fdf5c85b20 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

# osu! From 3c665bfb2970378d565669eee8b6119efb76a52c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Jul 2019 22:21:25 +0900 Subject: [PATCH 1732/2854] Revert breaking README change --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fdf5c85b20..52fc29cb98 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

# osu! From bbc3cbf5632fbe5fd8adffd0c9c29db639d4b7dc Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 12 Jul 2019 20:22:31 +0200 Subject: [PATCH 1733/2854] Restore deleted line --- osu.Game/Overlays/OSD/Toast.cs | 6 ++++-- osu.Game/Overlays/OSD/TrackedSettingToast.cs | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/OSD/Toast.cs b/osu.Game/Overlays/OSD/Toast.cs index 6572830d10..67c9b46c77 100644 --- a/osu.Game/Overlays/OSD/Toast.cs +++ b/osu.Game/Overlays/OSD/Toast.cs @@ -16,6 +16,8 @@ namespace osu.Game.Overlays.OSD private readonly Container content; protected override Container Content => content; + protected readonly OsuSpriteText ValueText; + protected Toast(string description, string value, string keybinding) { Anchor = Anchor.Centre; @@ -49,13 +51,13 @@ namespace osu.Game.Overlays.OSD Origin = Anchor.TopCentre, Text = description.ToUpperInvariant() }, - new OsuSpriteText + ValueText = new OsuSpriteText { Font = OsuFont.GetFont(size: 24, weight: FontWeight.Light), Padding = new MarginPadding { Left = 10, Right = 10 }, Name = "Value", Anchor = Anchor.Centre, - Origin = Anchor.BottomCentre, + Origin = Anchor.Centre, Text = value }, new OsuSpriteText diff --git a/osu.Game/Overlays/OSD/TrackedSettingToast.cs b/osu.Game/Overlays/OSD/TrackedSettingToast.cs index 454ba84d70..9812dcd797 100644 --- a/osu.Game/Overlays/OSD/TrackedSettingToast.cs +++ b/osu.Game/Overlays/OSD/TrackedSettingToast.cs @@ -62,6 +62,8 @@ namespace osu.Game.Overlays.OSD break; } + ValueText.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre; + for (int i = 0; i < optionCount; i++) { optionLights.Add(new OptionLight From 08014b1b99232e698e4118fe003661cdd7c6a560 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 Jul 2019 11:13:00 +0900 Subject: [PATCH 1734/2854] Fix logo masking not being applied correctly for showcase/win screens --- osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs | 2 +- osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index d809dfc994..20928499bf 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -10,7 +10,7 @@ namespace osu.Game.Tournament.Screens.Showcase [BackgroundDependencyLoader] private void load() { - AddInternal(new TournamentLogo()); + AddInternal(new TournamentLogo(false)); } } } diff --git a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs index efe4ee92fc..a0216c5db3 100644 --- a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs +++ b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs @@ -45,7 +45,7 @@ namespace osu.Game.Tournament.Screens.TeamWin RelativeSizeAxes = Axes.Both, Loop = true, }, - new TournamentLogo + new TournamentLogo(false) { Y = 40, }, From 0584ee9ce55c5a9e86940c66754f5722ba4d4cd3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 14 Jul 2019 12:34:12 +0300 Subject: [PATCH 1735/2854] Basic implementation --- .../SongSelect/TestSceneUserTopScore.cs | 97 +++++++++++++++++++ .../Select/Details/UserTopScoreContainer.cs | 79 +++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs create mode 100644 osu.Game/Screens/Select/Details/UserTopScoreContainer.cs diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs new file mode 100644 index 0000000000..f7cfd4156d --- /dev/null +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs @@ -0,0 +1,97 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Screens.Select.Details; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osuTK.Graphics; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Scoring; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.SongSelect +{ + public class TestSceneUserTopScore : OsuTestScene + { + private readonly UserTopScoreContainer topScoreContainer; + private readonly APILegacyUserTopScoreInfo[] scores; + + public TestSceneUserTopScore() + { + Add(new Container + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.Centre, + AutoSizeAxes = Axes.Y, + Width = 500, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.DarkGreen, + }, + topScoreContainer = new UserTopScoreContainer + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + } + } + }); + + AddStep(@"Trigger visibility", topScoreContainer.ToggleVisibility); + AddStep(@"Add score", () => topScoreContainer.TopScore = scores[0]); + AddStep(@"Add another score", () => topScoreContainer.TopScore = scores[1]); + + scores = new APILegacyUserTopScoreInfo[] + { + new APILegacyUserTopScoreInfo + { + Position = 999, + Score = new APILegacyScoreInfo + { + Rank = ScoreRank.XH, + Accuracy = 1, + MaxCombo = 244, + TotalScore = 1707827, + Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, + User = new User + { + Id = 6602580, + Username = @"waaiiru", + Country = new Country + { + FullName = @"Spain", + FlagName = @"ES", + }, + }, + } + }, + new APILegacyUserTopScoreInfo + { + Position = 110000, + Score = new APILegacyScoreInfo + { + Rank = ScoreRank.X, + Accuracy = 1, + MaxCombo = 244, + TotalScore = 1707827, + User = new User + { + Id = 4608074, + Username = @"Skycries", + Country = new Country + { + FullName = @"Brazil", + FlagName = @"BR", + }, + }, + } + } + }; + } + } +} diff --git a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs new file mode 100644 index 0000000000..05d0930de5 --- /dev/null +++ b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs @@ -0,0 +1,79 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Leaderboards; + +namespace osu.Game.Screens.Select.Details +{ + public class UserTopScoreContainer : VisibilityContainer + { + private const int height = 150; + private const int duration = 300; + + private readonly Container contentContainer; + private readonly Container scoreContainer; + + public APILegacyUserTopScoreInfo TopScore + { + set + { + scoreContainer.Clear(); + scoreContainer.Add(new LeaderboardScore(value.Score, value.Position)); + } + } + + protected override bool StartHidden => true; + + public UserTopScoreContainer() + { + RelativeSizeAxes = Axes.X; + Height = height; + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; + Children = new Drawable[] + { + contentContainer = new Container + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Height = height, + RelativeSizeAxes = Axes.X, + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = @"your personal best".ToUpper(), + Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold), + }, + scoreContainer = new Container + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } + } + } + }; + } + + protected override void PopIn() + { + this.ResizeHeightTo(height, duration, Easing.OutQuint); + contentContainer.FadeIn(duration, Easing.OutQuint); + } + + protected override void PopOut() + { + this.ResizeHeightTo(0, duration, Easing.OutQuint); + contentContainer.FadeOut(duration, Easing.OutQuint); + } + } +} From d30ae24f581d14d2068fbec2acf69375f6ff620a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 14 Jul 2019 12:40:54 +0300 Subject: [PATCH 1736/2854] Use Bindable for setting score --- .../SongSelect/TestSceneUserTopScore.cs | 5 +++-- .../Select/Details/UserTopScoreContainer.cs | 20 +++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs index f7cfd4156d..3fa85ffca6 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs @@ -43,8 +43,9 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddStep(@"Trigger visibility", topScoreContainer.ToggleVisibility); - AddStep(@"Add score", () => topScoreContainer.TopScore = scores[0]); - AddStep(@"Add another score", () => topScoreContainer.TopScore = scores[1]); + AddStep(@"Add score", () => topScoreContainer.TopScore.Value = scores[0]); + AddStep(@"Add another score", () => topScoreContainer.TopScore.Value = scores[1]); + AddStep(@"Add null score", () => topScoreContainer.TopScore.Value = null); scores = new APILegacyUserTopScoreInfo[] { diff --git a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs index 05d0930de5..21c16d1d74 100644 --- a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; @@ -18,14 +19,7 @@ namespace osu.Game.Screens.Select.Details private readonly Container contentContainer; private readonly Container scoreContainer; - public APILegacyUserTopScoreInfo TopScore - { - set - { - scoreContainer.Clear(); - scoreContainer.Add(new LeaderboardScore(value.Score, value.Position)); - } - } + public Bindable TopScore = new Bindable(); protected override bool StartHidden => true; @@ -62,6 +56,16 @@ namespace osu.Game.Screens.Select.Details } } }; + + TopScore.BindValueChanged((score) => onScoreChanged(score.NewValue)); + } + + private void onScoreChanged(APILegacyUserTopScoreInfo score) + { + scoreContainer.Clear(); + + if (score != null) + scoreContainer.Add(new LeaderboardScore(score.Score, score.Position)); } protected override void PopIn() From 922c3c89ae4eb9d89c6e5c90d7af0c2ae7222fd0 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 14 Jul 2019 12:47:35 +0300 Subject: [PATCH 1737/2854] Make leaderboard score use metric system --- .../SongSelect/TestSceneUserTopScore.cs | 26 +++++++++++++++++-- .../Online/Leaderboards/LeaderboardScore.cs | 12 ++++----- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs index 3fa85ffca6..1023294000 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs @@ -43,8 +43,9 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddStep(@"Trigger visibility", topScoreContainer.ToggleVisibility); - AddStep(@"Add score", () => topScoreContainer.TopScore.Value = scores[0]); - AddStep(@"Add another score", () => topScoreContainer.TopScore.Value = scores[1]); + AddStep(@"Add score(rank 999)", () => topScoreContainer.TopScore.Value = scores[0]); + AddStep(@"Add score(rank 110000)", () => topScoreContainer.TopScore.Value = scores[1]); + AddStep(@"Add score(rank 22333)", () => topScoreContainer.TopScore.Value = scores[2]); AddStep(@"Add null score", () => topScoreContainer.TopScore.Value = null); scores = new APILegacyUserTopScoreInfo[] @@ -91,6 +92,27 @@ namespace osu.Game.Tests.Visual.SongSelect }, }, } + }, + new APILegacyUserTopScoreInfo + { + Position = 22333, + Score = new APILegacyScoreInfo + { + Rank = ScoreRank.S, + Accuracy = 1, + MaxCombo = 244, + TotalScore = 1707827, + User = new User + { + Id = 1541390, + Username = @"Toukai", + Country = new Country + { + FullName = @"Canada", + FlagName = @"CA", + }, + }, + } } }; } diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 9840b59805..713d4a25f7 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -20,23 +20,23 @@ using osu.Game.Scoring; using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; +using Humanizer; namespace osu.Game.Online.Leaderboards { public class LeaderboardScore : OsuClickableContainer { - public readonly int RankPosition; - public const float HEIGHT = 60; private const float corner_radius = 5; private const float edge_margin = 5; private const float background_alpha = 0.25f; - private const float rank_width = 30; + private const float rank_width = 35; protected Container RankContainer { get; private set; } private readonly ScoreInfo score; + private readonly int rank; private Box background; private Container content; @@ -52,7 +52,7 @@ namespace osu.Game.Online.Leaderboards public LeaderboardScore(ScoreInfo score, int rank) { this.score = score; - RankPosition = rank; + this.rank = rank; RelativeSizeAxes = Axes.X; Height = HEIGHT; @@ -79,8 +79,8 @@ namespace osu.Game.Online.Leaderboards { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 22, italics: true), - Text = RankPosition.ToString(), + Font = OsuFont.GetFont(size: 20, italics: true), + Text = rank <= 999 ? rank.ToString() : rank.ToMetric(decimals: rank < 100000 ? 1 : 0), }, }, }, From d1409d4610bbcb4f58c6cc5fb0ee0ea8b8e6f37d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 14 Jul 2019 13:33:47 +0300 Subject: [PATCH 1738/2854] Add top score section into beatmap detail area --- .../SongSelect/TestSceneBeatmapDetailArea.cs | 1 + osu.Game/Screens/Select/BeatmapDetailArea.cs | 68 +++++++++++-------- .../Select/Details/UserTopScoreContainer.cs | 3 +- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs index 7b97a27732..e9650343af 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs @@ -163,6 +163,7 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddStep("null beatmap", () => detailsArea.Beatmap = null); + AddStep("Toggle top score visibility", () => detailsArea.TopScore.ToggleVisibility()); } } } diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs index b66a2ffe0f..678b18e8cf 100644 --- a/osu.Game/Screens/Select/BeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs @@ -5,19 +5,18 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; +using osu.Game.Screens.Select.Details; using osu.Game.Screens.Select.Leaderboards; namespace osu.Game.Screens.Select { public class BeatmapDetailArea : Container { - private const float details_padding = 10; - - private readonly Container content; - protected override Container Content => content; + private const float padding = 10; public readonly BeatmapDetails Details; public readonly BeatmapLeaderboard Leaderboard; + public readonly UserTopScoreContainer TopScore; private WorkingBeatmap beatmap; @@ -34,7 +33,7 @@ namespace osu.Game.Screens.Select public BeatmapDetailArea() { - AddRangeInternal(new Drawable[] + Children = new Drawable[] { new BeatmapDetailAreaTabControl { @@ -58,33 +57,44 @@ namespace osu.Game.Screens.Select } }, }, - content = new Container + new GridContainer { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = BeatmapDetailAreaTabControl.HEIGHT }, + Margin = new MarginPadding { Top = BeatmapDetailAreaTabControl.HEIGHT }, + RowDimensions = new Dimension[] + { + new Dimension(GridSizeMode.Distributed), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + Details = new BeatmapDetails + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + Padding = new MarginPadding { Vertical = padding }, + }, + Leaderboard = new BeatmapLeaderboard + { + RelativeSizeAxes = Axes.Both, + } + } + } + }, + new Drawable[] + { + TopScore = new UserTopScoreContainer(), + } + }, }, - }); - - AddRange(new Drawable[] - { - Details = new BeatmapDetails - { - RelativeSizeAxes = Axes.X, - Alpha = 0, - Margin = new MarginPadding { Top = details_padding }, - }, - Leaderboard = new BeatmapLeaderboard - { - RelativeSizeAxes = Axes.Both, - } - }); - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - Details.Height = Math.Min(DrawHeight - details_padding * 3 - BeatmapDetailAreaTabControl.HEIGHT, 450); + }; } } } diff --git a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs index 21c16d1d74..2cc47a7483 100644 --- a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs @@ -13,7 +13,7 @@ namespace osu.Game.Screens.Select.Details { public class UserTopScoreContainer : VisibilityContainer { - private const int height = 150; + private const int height = 110; private const int duration = 300; private readonly Container contentContainer; @@ -37,6 +37,7 @@ namespace osu.Game.Screens.Select.Details Origin = Anchor.BottomCentre, Height = height, RelativeSizeAxes = Axes.X, + Padding = new MarginPadding { Vertical = 10 }, Children = new Drawable[] { new OsuSpriteText From 963e025bb8882eb94884adbf40bb6b13cc0b298a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 14 Jul 2019 14:03:14 +0300 Subject: [PATCH 1739/2854] Make it works and some layout adjustments --- .../SongSelect/TestSceneUserTopScore.cs | 8 +-- osu.Game/Screens/Select/BeatmapDetailArea.cs | 60 +++++++++++-------- .../Select/Details/UserTopScoreContainer.cs | 11 ++-- .../Select/Leaderboards/BeatmapLeaderboard.cs | 9 ++- 4 files changed, 53 insertions(+), 35 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs index 1023294000..b33dbddccb 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs @@ -43,10 +43,10 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddStep(@"Trigger visibility", topScoreContainer.ToggleVisibility); - AddStep(@"Add score(rank 999)", () => topScoreContainer.TopScore.Value = scores[0]); - AddStep(@"Add score(rank 110000)", () => topScoreContainer.TopScore.Value = scores[1]); - AddStep(@"Add score(rank 22333)", () => topScoreContainer.TopScore.Value = scores[2]); - AddStep(@"Add null score", () => topScoreContainer.TopScore.Value = null); + AddStep(@"Add score(rank 999)", () => topScoreContainer.Score.Value = scores[0]); + AddStep(@"Add score(rank 110000)", () => topScoreContainer.Score.Value = scores[1]); + AddStep(@"Add score(rank 22333)", () => topScoreContainer.Score.Value = scores[2]); + AddStep(@"Add null score", () => topScoreContainer.Score.Value = null); scores = new APILegacyUserTopScoreInfo[] { diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs index 678b18e8cf..cac1281cc2 100644 --- a/osu.Game/Screens/Select/BeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs @@ -1,10 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Screens.Select.Details; using osu.Game.Screens.Select.Leaderboards; @@ -28,6 +28,7 @@ namespace osu.Game.Screens.Select beatmap = value; Leaderboard.Beatmap = beatmap?.BeatmapInfo; Details.Beatmap = beatmap?.BeatmapInfo; + TopScore.Hide(); } } @@ -57,43 +58,50 @@ namespace osu.Game.Screens.Select } }, }, - new GridContainer + new Container { + Padding = new MarginPadding { Top = BeatmapDetailAreaTabControl.HEIGHT, Bottom = padding }, RelativeSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = BeatmapDetailAreaTabControl.HEIGHT }, - RowDimensions = new Dimension[] + Child = new GridContainer { - new Dimension(GridSizeMode.Distributed), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new Drawable[] + RelativeSizeAxes = Axes.Both, + RowDimensions = new Dimension[] { - new Container + new Dimension(GridSizeMode.Distributed), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + new Container { - Details = new BeatmapDetails + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - Padding = new MarginPadding { Vertical = padding }, - }, - Leaderboard = new BeatmapLeaderboard - { - RelativeSizeAxes = Axes.Both, + Details = new BeatmapDetails + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + Padding = new MarginPadding { Top = padding }, + }, + Leaderboard = new BeatmapLeaderboard + { + RelativeSizeAxes = Axes.Both, + } } } + }, + new Drawable[] + { + TopScore = new UserTopScoreContainer + { + Score = { BindTarget = Leaderboard.TopScore } + } } }, - new Drawable[] - { - TopScore = new UserTopScoreContainer(), - } }, - }, + } }; } } diff --git a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs index 2cc47a7483..0bc30fae6b 100644 --- a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs @@ -13,13 +13,13 @@ namespace osu.Game.Screens.Select.Details { public class UserTopScoreContainer : VisibilityContainer { - private const int height = 110; + private const int height = 90; private const int duration = 300; private readonly Container contentContainer; private readonly Container scoreContainer; - public Bindable TopScore = new Bindable(); + public Bindable Score = new Bindable(); protected override bool StartHidden => true; @@ -37,13 +37,13 @@ namespace osu.Game.Screens.Select.Details Origin = Anchor.BottomCentre, Height = height, RelativeSizeAxes = Axes.X, - Padding = new MarginPadding { Vertical = 10 }, Children = new Drawable[] { new OsuSpriteText { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, + Margin = new MarginPadding { Top = 5 }, Text = @"your personal best".ToUpper(), Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold), }, @@ -58,7 +58,7 @@ namespace osu.Game.Screens.Select.Details } }; - TopScore.BindValueChanged((score) => onScoreChanged(score.NewValue)); + Score.BindValueChanged((score) => onScoreChanged(score.NewValue)); } private void onScoreChanged(APILegacyUserTopScoreInfo score) @@ -66,7 +66,10 @@ namespace osu.Game.Screens.Select.Details scoreContainer.Clear(); if (score != null) + { scoreContainer.Add(new LeaderboardScore(score.Score, score.Position)); + Show(); + } } protected override void PopIn() diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 0f6d4f3188..c41bf17685 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -9,6 +9,7 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -18,6 +19,8 @@ namespace osu.Game.Screens.Select.Leaderboards { public class BeatmapLeaderboard : Leaderboard { + public Bindable TopScore = new Bindable(); + public Action ScoreSelected; private BeatmapInfo beatmap; @@ -133,7 +136,11 @@ namespace osu.Game.Screens.Select.Leaderboards var req = new GetScoresRequest(Beatmap, ruleset.Value ?? Beatmap.Ruleset, Scope, requestMods); - req.Success += r => scoresCallback?.Invoke(r.Scores); + req.Success += r => + { + scoresCallback?.Invoke(r.Scores); + TopScore.Value = r.UserScore; + }; return req; } From ecf0e624849901aedcb2eac6008853773ef6aee8 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 14 Jul 2019 16:16:21 +0300 Subject: [PATCH 1740/2854] CI fixes --- .../SongSelect/TestSceneUserTopScore.cs | 19 +++++++++---------- osu.Game/Screens/Select/BeatmapDetailArea.cs | 3 +-- .../Select/Details/UserTopScoreContainer.cs | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs index b33dbddccb..1de8a5375e 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs @@ -16,11 +16,10 @@ namespace osu.Game.Tests.Visual.SongSelect { public class TestSceneUserTopScore : OsuTestScene { - private readonly UserTopScoreContainer topScoreContainer; - private readonly APILegacyUserTopScoreInfo[] scores; - public TestSceneUserTopScore() { + UserTopScoreContainer topScoreContainer; + Add(new Container { Origin = Anchor.BottomCentre, @@ -42,13 +41,7 @@ namespace osu.Game.Tests.Visual.SongSelect } }); - AddStep(@"Trigger visibility", topScoreContainer.ToggleVisibility); - AddStep(@"Add score(rank 999)", () => topScoreContainer.Score.Value = scores[0]); - AddStep(@"Add score(rank 110000)", () => topScoreContainer.Score.Value = scores[1]); - AddStep(@"Add score(rank 22333)", () => topScoreContainer.Score.Value = scores[2]); - AddStep(@"Add null score", () => topScoreContainer.Score.Value = null); - - scores = new APILegacyUserTopScoreInfo[] + APILegacyUserTopScoreInfo[] scores = new[] { new APILegacyUserTopScoreInfo { @@ -115,6 +108,12 @@ namespace osu.Game.Tests.Visual.SongSelect } } }; + + AddStep(@"Trigger visibility", topScoreContainer.ToggleVisibility); + AddStep(@"Add score(rank 999)", () => topScoreContainer.Score.Value = scores[0]); + AddStep(@"Add score(rank 110000)", () => topScoreContainer.Score.Value = scores[1]); + AddStep(@"Add score(rank 22333)", () => topScoreContainer.Score.Value = scores[2]); + AddStep(@"Add null score", () => topScoreContainer.Score.Value = null); } } } diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs index cac1281cc2..6cd6372152 100644 --- a/osu.Game/Screens/Select/BeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs @@ -4,7 +4,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Screens.Select.Details; using osu.Game.Screens.Select.Leaderboards; @@ -65,7 +64,7 @@ namespace osu.Game.Screens.Select Child = new GridContainer { RelativeSizeAxes = Axes.Both, - RowDimensions = new Dimension[] + RowDimensions = new[] { new Dimension(GridSizeMode.Distributed), new Dimension(GridSizeMode.AutoSize), diff --git a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs index 0bc30fae6b..0300d14ac1 100644 --- a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs @@ -58,7 +58,7 @@ namespace osu.Game.Screens.Select.Details } }; - Score.BindValueChanged((score) => onScoreChanged(score.NewValue)); + Score.BindValueChanged(score => onScoreChanged(score.NewValue)); } private void onScoreChanged(APILegacyUserTopScoreInfo score) From 19680c8df8f7c883df97f600bde0e1f882687758 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 14 Jul 2019 16:37:05 +0300 Subject: [PATCH 1741/2854] Minor adjustments --- osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs | 2 +- osu.Game/Screens/Select/Details/UserTopScoreContainer.cs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs index 1de8a5375e..5ddccb4c8e 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.SongSelect } }); - APILegacyUserTopScoreInfo[] scores = new[] + var scores = new APILegacyUserTopScoreInfo[] { new APILegacyUserTopScoreInfo { diff --git a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs index 0300d14ac1..e5e33a47d8 100644 --- a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs @@ -63,13 +63,14 @@ namespace osu.Game.Screens.Select.Details private void onScoreChanged(APILegacyUserTopScoreInfo score) { - scoreContainer.Clear(); - if (score != null) { + scoreContainer.Clear(); scoreContainer.Add(new LeaderboardScore(score.Score, score.Position)); Show(); } + else + Hide(); } protected override void PopIn() From a01e7260e06e7b23d6b40d7c54d56bee969eb2cc Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 14 Jul 2019 16:49:46 +0300 Subject: [PATCH 1742/2854] Testcase fix --- osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs index 5ddccb4c8e..5f3e7d09dd 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.SongSelect } }); - var scores = new APILegacyUserTopScoreInfo[] + var scores = new[] { new APILegacyUserTopScoreInfo { From b93b26a0366069000d99535a6f83f62e7e377726 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 14 Jul 2019 19:38:46 +0300 Subject: [PATCH 1743/2854] Fix link formatting --- .../Profile/Sections/UnderscoredBeatmapLink.cs | 14 +++++++------- .../Profile/Sections/UnderscoredLinkContainer.cs | 9 +++++++++ .../Profile/Sections/UnderscoredUserLink.cs | 9 ++++----- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/UnderscoredBeatmapLink.cs b/osu.Game/Overlays/Profile/Sections/UnderscoredBeatmapLink.cs index 370d6d84ef..ddad99355c 100644 --- a/osu.Game/Overlays/Profile/Sections/UnderscoredBeatmapLink.cs +++ b/osu.Game/Overlays/Profile/Sections/UnderscoredBeatmapLink.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; using osu.Game.Beatmaps; namespace osu.Game.Overlays.Profile.Sections @@ -15,15 +14,16 @@ namespace osu.Game.Overlays.Profile.Sections this.beatmap = beatmap; } - [BackgroundDependencyLoader(true)] - private void load(BeatmapSetOverlay beatmapSetOverlay) + protected override void LoadComplete() { + base.LoadComplete(); + ClickAction = () => { - if (beatmap.OnlineBeatmapID != null) - beatmapSetOverlay?.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value); - else if (beatmap.BeatmapSet?.OnlineBeatmapSetID != null) - beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.BeatmapSet.OnlineBeatmapSetID.Value); + var beatmapId = beatmap.OnlineBeatmapID; + + if (beatmapId.HasValue) + Game?.ShowBeatmap(beatmapId.Value); }; } } diff --git a/osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs b/osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs index 8daf0bd24d..09a23d2fe3 100644 --- a/osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -17,6 +18,8 @@ namespace osu.Game.Overlays.Profile.Sections private readonly Container underscore; private readonly FillFlowContainer textContent; + protected OsuGame Game; + protected Action ClickAction; public IReadOnlyList Text @@ -59,6 +62,12 @@ namespace osu.Game.Overlays.Profile.Sections }; } + [BackgroundDependencyLoader(true)] + private void load(OsuGame game) + { + Game = game; + } + protected override bool OnHover(HoverEvent e) { underscore.FadeIn(duration, Easing.OutQuint); diff --git a/osu.Game/Overlays/Profile/Sections/UnderscoredUserLink.cs b/osu.Game/Overlays/Profile/Sections/UnderscoredUserLink.cs index f50bc7f7ba..9b3e8ccdf6 100644 --- a/osu.Game/Overlays/Profile/Sections/UnderscoredUserLink.cs +++ b/osu.Game/Overlays/Profile/Sections/UnderscoredUserLink.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; - namespace osu.Game.Overlays.Profile.Sections { public class UnderscoredUserLink : UnderscoredLinkContainer @@ -14,10 +12,11 @@ namespace osu.Game.Overlays.Profile.Sections this.userId = userId; } - [BackgroundDependencyLoader(true)] - private void load(UserProfileOverlay userProfileOverlay) + protected override void LoadComplete() { - ClickAction = () => userProfileOverlay?.ShowUser(userId); + base.LoadComplete(); + + ClickAction = () => Game?.ShowUser(userId); } } } From d093eb6660c7f04ff27c75dd587e08bb67bd48d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jul 2019 11:45:15 +0900 Subject: [PATCH 1744/2854] Mark sprite read-only --- osu.Game/Graphics/Backgrounds/Background.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index 526b3da8a6..436fcd0476 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -16,7 +16,7 @@ namespace osu.Game.Graphics.Backgrounds ///

public class Background : CompositeDrawable { - public Sprite Sprite; + public readonly Sprite Sprite; private readonly string textureName; From 2186ffda555e610f799a3d2da0865ced1b3b949f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jul 2019 11:46:41 +0900 Subject: [PATCH 1745/2854] Avoid unnecessarily creating buffered container for zero-blur --- osu.Game/Graphics/Backgrounds/Background.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index 436fcd0476..0043aab7e2 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -51,7 +51,7 @@ namespace osu.Game.Graphics.Backgrounds /// A to which further transforms can be added. public void BlurTo(Vector2 newBlurSigma, double duration = 0, Easing easing = Easing.None) { - if (bufferedContainer == null) + if (bufferedContainer == null && newBlurSigma != Vector2.Zero) { RemoveInternal(Sprite); From 12e7668afc3245b5395c976ff8e71cd9d5e7a3b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jul 2019 11:48:33 +0900 Subject: [PATCH 1746/2854] Fix potential cross-thread talk from bindable updates --- osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 08f1881038..5225740d0b 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -168,6 +168,12 @@ namespace osu.Game.Screens.Backgrounds private void load(OsuConfigManager config) { userBlurLevel = config.GetBindable(OsuSetting.BlurLevel); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + userBlurLevel.ValueChanged += _ => UpdateVisuals(); BlurAmount.ValueChanged += _ => UpdateVisuals(); } From d92f6c762ba88e6d4335134a9ec131c8c9c457e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jul 2019 11:53:16 +0900 Subject: [PATCH 1747/2854] Fix potential nullref --- osu.Game/Graphics/Backgrounds/Background.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index 0043aab7e2..d13475189d 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -63,7 +63,7 @@ namespace osu.Game.Graphics.Backgrounds }); } - bufferedContainer.BlurTo(newBlurSigma, duration, easing); + bufferedContainer?.BlurTo(newBlurSigma, duration, easing); } } } From 27258e3a9b6b16d2ed845b9e65ab63515366a718 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jul 2019 14:38:05 +0900 Subject: [PATCH 1748/2854] Rename test scene to match class --- ...SceneUserTopScore.cs => TestSceneUserTopScoreContainer.cs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename osu.Game.Tests/Visual/SongSelect/{TestSceneUserTopScore.cs => TestSceneUserTopScoreContainer.cs} (97%) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs similarity index 97% rename from osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs rename to osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs index 5f3e7d09dd..38ebb58e76 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScore.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs @@ -14,9 +14,9 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual.SongSelect { - public class TestSceneUserTopScore : OsuTestScene + public class TestSceneUserTopScoreContainer : OsuTestScene { - public TestSceneUserTopScore() + public TestSceneUserTopScoreContainer() { UserTopScoreContainer topScoreContainer; From 9c1badd2e3771a36c2eea89d090c0db873991f71 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jul 2019 15:42:54 +0900 Subject: [PATCH 1749/2854] Fix rulesets not matching in dictionary lookups due to missing GetHashCode implementation --- osu.Game/Rulesets/RulesetInfo.cs | 14 ++++++++++++++ osu.Game/Rulesets/RulesetStore.cs | 6 +++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index ca331ec339..8eb2abec79 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics.CodeAnalysis; using Newtonsoft.Json; namespace osu.Game.Rulesets @@ -23,6 +24,19 @@ namespace osu.Game.Rulesets public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo; + [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] + public override int GetHashCode() + { + unchecked + { + var hashCode = ID.HasValue ? ID.GetHashCode() : 0; + hashCode = (hashCode * 397) ^ (InstantiationInfo != null ? InstantiationInfo.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (Name != null ? Name.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ Available.GetHashCode(); + return hashCode; + } + } + public override string ToString() => $"{Name} ({ShortName}) ID: {ID}"; } } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index fd42f96c92..2d8c9f5b49 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets public RulesetStore(IDatabaseContextFactory factory) : base(factory) { - AddMissingRulesets(); + addMissingRulesets(); } /// @@ -52,13 +52,13 @@ namespace osu.Game.Rulesets /// /// All available rulesets. /// - public IEnumerable AvailableRulesets; + public IEnumerable AvailableRulesets { get; private set; } private static Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args) => loaded_assemblies.Keys.FirstOrDefault(a => a.FullName == args.Name); private const string ruleset_library_prefix = "osu.Game.Rulesets"; - protected void AddMissingRulesets() + private void addMissingRulesets() { using (var usage = ContextFactory.GetForWrite()) { From d0c8aaba4e1f6442e188f7042deb4d8a0a5b5b3f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jul 2019 15:50:50 +0900 Subject: [PATCH 1750/2854] Override basic equality function too --- osu.Game/Rulesets/RulesetInfo.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index 8eb2abec79..d9cff86265 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -24,6 +24,8 @@ namespace osu.Game.Rulesets public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo; + public override bool Equals(object obj) => obj is RulesetInfo rulesetInfo && Equals(rulesetInfo); + [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] public override int GetHashCode() { From d4c1005c7e820bfa7d7cb1a5b1791e3ba1b0a5a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jul 2019 15:53:19 +0900 Subject: [PATCH 1751/2854] Fix incorrect comparison in line updating logic --- osu.Game/OsuGame.cs | 2 +- osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 361ff62155..2a484fc122 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -248,7 +248,7 @@ namespace osu.Game } // Use first beatmap available for current ruleset, else switch ruleset. - var first = databasedSet.Beatmaps.Find(b => b.Ruleset == Ruleset.Value) ?? databasedSet.Beatmaps.First(); + var first = databasedSet.Beatmaps.Find(b => b.Ruleset.Equals(Ruleset.Value)) ?? databasedSet.Beatmaps.First(); Ruleset.Value = first.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first); diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index f4272ab15c..bf0cd91321 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -73,7 +73,7 @@ namespace osu.Game.Overlays.Toolbar { foreach (var tabItem in TabContainer) { - if (tabItem.Value == Current.Value) + if (tabItem.Value.Equals(Current.Value)) { ModeButtonLine.MoveToX(tabItem.DrawPosition.X, !hasInitialPosition ? 0 : 200, Easing.OutQuint); break; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 0eeffda5eb..b3c3925a26 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -329,7 +329,7 @@ namespace osu.Game.Screens.Select if (this.IsCurrentScreen() && !Carousel.SelectBeatmap(e.NewValue?.BeatmapInfo, false)) // If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch - if (e.NewValue?.BeatmapInfo?.Ruleset != null && e.NewValue.BeatmapInfo.Ruleset != decoupledRuleset.Value) + if (e.NewValue?.BeatmapInfo?.Ruleset != null && !e.NewValue.BeatmapInfo.Ruleset.Equals(decoupledRuleset.Value)) { Ruleset.Value = e.NewValue.BeatmapInfo.Ruleset; Carousel.SelectBeatmap(e.NewValue.BeatmapInfo); From 7e367dc397e9ce17fe089b83df4b324689d5d756 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 15 Jul 2019 12:30:42 +0300 Subject: [PATCH 1752/2854] Push results screen when clicking on top score --- osu.Game/Screens/Select/Details/UserTopScoreContainer.cs | 9 ++++++++- osu.Game/Screens/Select/SongSelect.cs | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs index e5e33a47d8..ba6751475e 100644 --- a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs @@ -8,6 +8,8 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; +using osu.Game.Scoring; +using System; namespace osu.Game.Screens.Select.Details { @@ -21,6 +23,8 @@ namespace osu.Game.Screens.Select.Details public Bindable Score = new Bindable(); + public Action ScoreSelected; + protected override bool StartHidden => true; public UserTopScoreContainer() @@ -66,7 +70,10 @@ namespace osu.Game.Screens.Select.Details if (score != null) { scoreContainer.Clear(); - scoreContainer.Add(new LeaderboardScore(score.Score, score.Position)); + scoreContainer.Add(new LeaderboardScore(score.Score, score.Position) + { + Action = () => ScoreSelected?.Invoke(score.Score) + }); Show(); } else diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index b3c3925a26..94b1fedeab 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -216,6 +216,7 @@ namespace osu.Game.Screens.Select } BeatmapDetails.Leaderboard.ScoreSelected += s => this.Push(new SoloResults(s)); + BeatmapDetails.TopScore.ScoreSelected += s => this.Push(new SoloResults(s)); } [BackgroundDependencyLoader(true)] From d62e42ba1459d5b32c3c721f9a322d9af7d7bb19 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 15 Jul 2019 13:09:21 +0300 Subject: [PATCH 1753/2854] Remove custom link container implementation --- .../Sections/BeatmapMetadataContainer.cs | 26 ++---- .../Historical/DrawableMostPlayedBeatmap.cs | 75 +++++++--------- .../Sections/Ranks/DrawableProfileScore.cs | 29 +++++- .../Sections/UnderscoredBeatmapLink.cs | 30 ------- .../Sections/UnderscoredLinkContainer.cs | 89 ------------------- .../Profile/Sections/UnderscoredUserLink.cs | 22 ----- 6 files changed, 67 insertions(+), 204 deletions(-) delete mode 100644 osu.Game/Overlays/Profile/Sections/UnderscoredBeatmapLink.cs delete mode 100644 osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs delete mode 100644 osu.Game/Overlays/Profile/Sections/UnderscoredUserLink.cs diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs index 16326900f1..597171cc7f 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs @@ -4,22 +4,19 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Localisation; using osu.Game.Beatmaps; -using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; namespace osu.Game.Overlays.Profile.Sections { /// /// Display artist/title/mapper information, commonly used as the left portion of a profile or score display row (see ). /// - public class BeatmapMetadataContainer : OsuHoverContainer + public abstract class BeatmapMetadataContainer : OsuHoverContainer { private readonly BeatmapInfo beatmap; - public BeatmapMetadataContainer(BeatmapInfo beatmap) + protected BeatmapMetadataContainer(BeatmapInfo beatmap) { this.beatmap = beatmap; AutoSizeAxes = Axes.Both; @@ -40,23 +37,10 @@ namespace osu.Game.Overlays.Profile.Sections Child = new FillFlowContainer { AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = new LocalisedString(( - $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ", - $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] ")), - Font = OsuFont.GetFont(size: 15, weight: FontWeight.SemiBold, italics: true) - }, - new OsuSpriteText - { - Text = new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)), - Padding = new MarginPadding { Top = 3 }, - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular, italics: true) - }, - }, + Children = CreateText(beatmap), }; } + + protected abstract Drawable[] CreateText(BeatmapInfo beatmap); } } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs index 98872d6141..fc457676a8 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs @@ -19,13 +19,12 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { public class DrawableMostPlayedBeatmap : OsuHoverContainer { - private readonly OsuSpriteText mapperText; private readonly Box background; private const int cover_width = 100; private const int corner_radius = 10; private readonly SpriteIcon icon; private readonly OsuSpriteText playCountText; - private readonly UnderscoredUserLink mapper; + private readonly LinkFlowContainer mapper; protected override IEnumerable EffectTargets => new[] { background }; @@ -71,54 +70,24 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Padding = new MarginPadding { Left = 15, Right = 20 }, Children = new Drawable[] { - new UnderscoredBeatmapLink(beatmap) + new MostPlayedBeatmapMetadataContainer(beatmap) { Anchor = Anchor.CentreLeft, Origin = Anchor.BottomLeft, Margin = new MarginPadding { Bottom = 2 }, - Text = new[] - { - new OsuSpriteText - { - Text = new LocalisedString(( - $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ", - $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] ")), - Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold) - }, - new OsuSpriteText - { - Text = "by " + new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)), - Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular) - }, - } }, - new FillFlowContainer + mapper = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 15, weight: FontWeight.Regular)) { - AutoSizeAxes = Axes.Both, Anchor = Anchor.CentreLeft, Origin = Anchor.TopLeft, + AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, Margin = new MarginPadding { Top = 2 }, - Children = new Drawable[] - { - mapperText = new OsuSpriteText - { - Text = "mapped by ", - Font = OsuFont.GetFont(size: 15, weight: FontWeight.Regular), - }, - mapper = new UnderscoredUserLink(beatmap.Metadata.Author.Id) - { - Text = new[] - { - new OsuSpriteText - { - Text = beatmap.Metadata.Author.Username, - Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold), - } - } - }, - } - }, + }.With(d => + { + d.AddText("mapped by "); + d.AddUserLink(beatmap.Metadata.Author); + }), new FillFlowContainer { Anchor = Anchor.CentreRight, @@ -157,8 +126,32 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { IdleColour = colors.GreySeafoam; HoverColour = colors.GreySeafoamLight; - mapperText.Colour = mapper.Colour = colors.GreySeafoamLighter; + mapper.Colour = colors.GreySeafoamLighter; icon.Colour = playCountText.Colour = colors.Yellow; } + + private class MostPlayedBeatmapMetadataContainer : BeatmapMetadataContainer + { + public MostPlayedBeatmapMetadataContainer(BeatmapInfo beatmap) + : base(beatmap) + { + } + + protected override Drawable[] CreateText(BeatmapInfo beatmap) => new Drawable[] + { + new OsuSpriteText + { + Text = new LocalisedString(( + $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ", + $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] ")), + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold) + }, + new OsuSpriteText + { + Text = "by " + new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)), + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular) + }, + }; + } } } diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index b77357edd8..e54ce44ca2 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs @@ -10,6 +10,8 @@ using osu.Game.Online.Leaderboards; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using osu.Game.Scoring; +using osu.Game.Beatmaps; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections.Ranks { @@ -51,7 +53,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks RightFlowContainer.Insert(1, text); - LeftFlowContainer.Add(new BeatmapMetadataContainer(Score.Beatmap)); + LeftFlowContainer.Add(new ProfileScoreBeatmapMetadataContainer(Score.Beatmap)); LeftFlowContainer.Add(new DrawableDate(Score.Date)); foreach (Mod mod in Score.Mods) @@ -64,5 +66,30 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Width = 60, FillMode = FillMode.Fit, }; + + private class ProfileScoreBeatmapMetadataContainer : BeatmapMetadataContainer + { + public ProfileScoreBeatmapMetadataContainer(BeatmapInfo beatmap) + : base(beatmap) + { + } + + protected override Drawable[] CreateText(BeatmapInfo beatmap) => new Drawable[] + { + new OsuSpriteText + { + Text = new LocalisedString(( + $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ", + $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] ")), + Font = OsuFont.GetFont(size: 15, weight: FontWeight.SemiBold, italics: true) + }, + new OsuSpriteText + { + Text = new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)), + Padding = new MarginPadding { Top = 3 }, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular, italics: true) + }, + }; + } } } diff --git a/osu.Game/Overlays/Profile/Sections/UnderscoredBeatmapLink.cs b/osu.Game/Overlays/Profile/Sections/UnderscoredBeatmapLink.cs deleted file mode 100644 index ddad99355c..0000000000 --- a/osu.Game/Overlays/Profile/Sections/UnderscoredBeatmapLink.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Beatmaps; - -namespace osu.Game.Overlays.Profile.Sections -{ - public class UnderscoredBeatmapLink : UnderscoredLinkContainer - { - private readonly BeatmapInfo beatmap; - - public UnderscoredBeatmapLink(BeatmapInfo beatmap) - { - this.beatmap = beatmap; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - ClickAction = () => - { - var beatmapId = beatmap.OnlineBeatmapID; - - if (beatmapId.HasValue) - Game?.ShowBeatmap(beatmapId.Value); - }; - } - } -} diff --git a/osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs b/osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs deleted file mode 100644 index 09a23d2fe3..0000000000 --- a/osu.Game/Overlays/Profile/Sections/UnderscoredLinkContainer.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; -using osu.Game.Graphics.Sprites; -using System; -using System.Collections.Generic; - -namespace osu.Game.Overlays.Profile.Sections -{ - public abstract class UnderscoredLinkContainer : Container - { - private const int duration = 200; - private readonly Container underscore; - private readonly FillFlowContainer textContent; - - protected OsuGame Game; - - protected Action ClickAction; - - public IReadOnlyList Text - { - get => textContent.Children; - set - { - textContent.Clear(); - textContent.AddRange(value); - } - } - - protected UnderscoredLinkContainer() - { - AutoSizeAxes = Axes.Both; - Child = new Container - { - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - underscore = new Container - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - Height = 1, - Alpha = 0, - AlwaysPresent = true, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - } - }, - textContent = new FillFlowContainer - { - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - }, - }, - }; - } - - [BackgroundDependencyLoader(true)] - private void load(OsuGame game) - { - Game = game; - } - - protected override bool OnHover(HoverEvent e) - { - underscore.FadeIn(duration, Easing.OutQuint); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - underscore.FadeOut(duration, Easing.OutQuint); - base.OnHoverLost(e); - } - - protected override bool OnClick(ClickEvent e) - { - ClickAction?.Invoke(); - return base.OnClick(e); - } - } -} diff --git a/osu.Game/Overlays/Profile/Sections/UnderscoredUserLink.cs b/osu.Game/Overlays/Profile/Sections/UnderscoredUserLink.cs deleted file mode 100644 index 9b3e8ccdf6..0000000000 --- a/osu.Game/Overlays/Profile/Sections/UnderscoredUserLink.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Overlays.Profile.Sections -{ - public class UnderscoredUserLink : UnderscoredLinkContainer - { - private readonly long userId; - - public UnderscoredUserLink(long userId) - { - this.userId = userId; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - ClickAction = () => Game?.ShowUser(userId); - } - } -} From 111541fe7add521e40a6e9d90cd92f16f449d050 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 15 Jul 2019 13:31:57 +0300 Subject: [PATCH 1754/2854] Use limit in requests --- osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs | 6 ++++-- .../Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs | 6 ++++-- .../Online/API/Requests/GetUserRecentActivitiesRequest.cs | 6 ++++-- osu.Game/Online/API/Requests/GetUserScoresRequest.cs | 6 ++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index 45d751f00e..57005181ca 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -11,17 +11,19 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; private readonly int offset; + private readonly int limit; private readonly BeatmapSetType type; - public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0) + public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0, int limit = 6) { this.userId = userId; this.offset = offset; + this.limit = limit; this.type = type; } // ReSharper disable once ImpureMethodCallOnReadonlyValueField - protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}?offset={offset}"; + protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}?offset={offset}&limit={limit}"; } public enum BeatmapSetType diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index 40e52bdaf6..fccb27a86c 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -10,13 +10,15 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; private readonly int offset; + private readonly int limit; - public GetUserMostPlayedBeatmapsRequest(long userId, int offset = 0) + public GetUserMostPlayedBeatmapsRequest(long userId, int offset = 0, int limit = 5) { this.userId = userId; this.offset = offset; + this.limit = limit; } - protected override string Target => $@"users/{userId}/beatmapsets/most_played?offset={offset}"; + protected override string Target => $@"users/{userId}/beatmapsets/most_played?offset={offset}&limit={limit}"; } } diff --git a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs index 9f80180e70..d066636911 100644 --- a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs @@ -10,14 +10,16 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; private readonly int offset; + private readonly int limit; - public GetUserRecentActivitiesRequest(long userId, int offset = 0) + public GetUserRecentActivitiesRequest(long userId, int offset = 0, int limit = 5) { this.userId = userId; this.offset = offset; + this.limit = limit; } - protected override string Target => $"users/{userId}/recent_activity?offset={offset}"; + protected override string Target => $"users/{userId}/recent_activity?offset={offset}&limit={limit}"; } public enum RecentActivityType diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs index 48a43bbbad..e81686b9fe 100644 --- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs @@ -11,16 +11,18 @@ namespace osu.Game.Online.API.Requests private readonly long userId; private readonly ScoreType type; private readonly int offset; + private readonly int limit; - public GetUserScoresRequest(long userId, ScoreType type, int offset = 0) + public GetUserScoresRequest(long userId, ScoreType type, int offset = 0, int limit = 5) { this.userId = userId; this.type = type; this.offset = offset; + this.offset = limit; } // ReSharper disable once ImpureMethodCallOnReadonlyValueField - protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLowerInvariant()}?offset={offset}"; + protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLowerInvariant()}?offset={offset}&limit={limit}"; } public enum ScoreType From 9458bca58f954a6b80094b7940013b8dcdd5fc22 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 15 Jul 2019 13:37:25 +0300 Subject: [PATCH 1755/2854] Update usage of requests --- osu.Game/Online/API/Requests/GetUserScoresRequest.cs | 2 +- .../Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs | 2 +- .../Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs | 2 +- .../Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs | 2 +- .../Profile/Sections/Recent/PaginatedRecentActivityContainer.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs index e81686b9fe..0e2f7ec417 100644 --- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs @@ -18,7 +18,7 @@ namespace osu.Game.Online.API.Requests this.userId = userId; this.type = type; this.offset = offset; - this.offset = limit; + this.limit = limit; } // ReSharper disable once ImpureMethodCallOnReadonlyValueField diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index b6b0e605d7..22f8e6c6b0 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps protected override void ShowMore() { - request = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); + request = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage, ItemsPerPage); request.Success += sets => Schedule(() => { MoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0); diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index 6085b0bc05..92b0304a68 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected override void ShowMore() { - request = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); + request = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage, ItemsPerPage); request.Success += beatmaps => Schedule(() => { MoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0); diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index a149cfa12e..5bb362e21c 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks protected override void ShowMore() { - request = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); + request = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage, ItemsPerPage); request.Success += scores => Schedule(() => { foreach (var s in scores) diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index b72aec7a44..215c96a423 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent protected override void ShowMore() { - request = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); + request = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage, ItemsPerPage); request.Success += activities => Schedule(() => { MoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0); From 83ffb1d5420e8f5f28316583a84e5f87a40d3cc7 Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 15 Jul 2019 14:15:03 -0700 Subject: [PATCH 1756/2854] Remove unnecessary transforms on top score user section --- .../Overlays/BeatmapSet/Scores/TopScoreUserSection.cs | 4 ++-- osu.Game/Users/Drawables/UpdateableAvatar.cs | 8 +------- osu.Game/Users/Drawables/UpdateableFlag.cs | 8 +------- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index a15d3c5fd1..ffc39e5af2 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores }, } }, - avatar = new UpdateableAvatar(hideImmediately: true) + avatar = new UpdateableAvatar { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -99,7 +99,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Origin = Anchor.CentreLeft, Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold) }, - flag = new UpdateableFlag(hideImmediately: true) + flag = new UpdateableFlag { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs index a49f2d079b..795b90ba11 100644 --- a/osu.Game/Users/Drawables/UpdateableAvatar.cs +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -5,7 +5,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Transforms; namespace osu.Game.Users.Drawables { @@ -38,8 +37,6 @@ namespace osu.Game.Users.Drawables set => base.EdgeEffect = value; } - protected override bool TransformImmediately { get; } - /// /// Whether to show a default guest representation on null user (as opposed to nothing). /// @@ -50,14 +47,11 @@ namespace osu.Game.Users.Drawables /// public readonly BindableBool OpenOnClick = new BindableBool(true); - public UpdateableAvatar(User user = null, bool hideImmediately = false) + public UpdateableAvatar(User user = null) { - TransformImmediately = hideImmediately; User = user; } - protected override TransformSequence ApplyHideTransforms(Drawable drawable) => TransformImmediately ? drawable?.FadeOut() : base.ApplyHideTransforms(drawable); - protected override Drawable CreateDrawable(User user) { if (user == null && !ShowGuestOnNull) diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs index 78d1a8de20..abc16b2390 100644 --- a/osu.Game/Users/Drawables/UpdateableFlag.cs +++ b/osu.Game/Users/Drawables/UpdateableFlag.cs @@ -3,7 +3,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Transforms; namespace osu.Game.Users.Drawables { @@ -15,21 +14,16 @@ namespace osu.Game.Users.Drawables set => Model = value; } - protected override bool TransformImmediately { get; } - /// /// Whether to show a place holder on null country. /// public bool ShowPlaceholderOnNull = true; - public UpdateableFlag(Country country = null, bool hideImmediately = false) + public UpdateableFlag(Country country = null) { - TransformImmediately = hideImmediately; Country = country; } - protected override TransformSequence ApplyHideTransforms(Drawable drawable) => TransformImmediately ? drawable?.FadeOut() : base.ApplyHideTransforms(drawable); - protected override Drawable CreateDrawable(Country country) { if (country == null && !ShowPlaceholderOnNull) From ed203cb0ffc3d22659e322976da02b2855864c68 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 16 Jul 2019 13:45:59 +0900 Subject: [PATCH 1757/2854] Delay intial hitobject updates --- .../Objects/Drawables/DrawableOsuHitObject.cs | 3 ++ .../Objects/Drawables/DrawableHitObject.cs | 42 ++++++++++++++----- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index f372cb65ce..4533e08a2b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -29,6 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables ShakeDuration = 30, RelativeSizeAxes = Axes.Both }); + Alpha = 0; } @@ -38,6 +39,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void ClearInternal(bool disposeChildren = true) => shakeContainer.Clear(disposeChildren); protected override bool RemoveInternal(Drawable drawable) => shakeContainer.Remove(drawable); + protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt; + protected sealed override void UpdateState(ArmedState state) { double transformTime = HitObject.StartTime - HitObject.TimePreempt; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 1f6ca4dd73..76233eabf2 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -179,6 +179,38 @@ namespace osu.Game.Rulesets.Objects.Drawables UpdateResult(false); } + private double? lifetimeStart; + + public override double LifetimeStart + { + get => lifetimeStart ?? (HitObject.StartTime - InitialLifetimeOffset); + set + { + base.LifetimeStart = value; + lifetimeStart = value; + } + } + + /// + /// A safe offset prior to the start time of at which this may begin displaying contents. + /// By default, s are assumed to display their contents within 10 seconds prior to the start time of . + /// + /// + /// This is only used as an optimisation to delay the initial update of this and may be tuned more aggressively if required. + /// A more accurate should be set inside for an state. + /// + protected virtual double InitialLifetimeOffset => 10000; + + /// + /// Will called at least once after the of this has been passed. + /// + internal void OnLifetimeEnd() + { + foreach (var nested in NestedHitObjects) + nested.OnLifetimeEnd(); + UpdateResult(false); + } + protected virtual void AddNested(DrawableHitObject h) { h.OnNewResult += (d, r) => OnNewResult?.Invoke(d, r); @@ -223,16 +255,6 @@ namespace osu.Game.Rulesets.Objects.Drawables OnNewResult?.Invoke(this, Result); } - /// - /// Will called at least once after the of this has been passed. - /// - internal void OnLifetimeEnd() - { - foreach (var nested in NestedHitObjects) - nested.OnLifetimeEnd(); - UpdateResult(false); - } - /// /// Processes this , checking if a scoring result has occurred. /// From e789bb37c8a57aaca7bc67e7b8b3b4dc1bbebb3d Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 16 Jul 2019 14:55:41 +0900 Subject: [PATCH 1758/2854] Ignore shift-delete in SearchTextBox --- .../Graphics/UserInterface/SearchTextBox.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index 7023711aaa..cd801a3fc9 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; using osu.Framework.Input.Events; using osuTK; using osuTK.Input; @@ -15,6 +16,8 @@ namespace osu.Game.Graphics.UserInterface public override bool HandleLeftRightArrows => false; + private InputManager inputManager; + public SearchTextBox() { Height = 35; @@ -33,6 +36,21 @@ namespace osu.Game.Graphics.UserInterface PlaceholderText = "type to search"; } + protected override void LoadComplete() + { + inputManager = GetContainingInputManager(); + base.LoadComplete(); + } + + protected override bool HandleAction(PlatformAction action) + { + // Allow shift-delete to be handled locally + if (inputManager.CurrentState.Keyboard.ShiftPressed && action.ActionType == PlatformActionType.CharNext && action.ActionMethod == PlatformActionMethod.Delete) + return false; + + return base.HandleAction(action); + } + protected override bool OnKeyDown(KeyDownEvent e) { if (!e.ControlPressed && !e.ShiftPressed) From 7634e3cc813c43d317c518dda349878bd94b7013 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Jul 2019 14:57:11 +0900 Subject: [PATCH 1759/2854] Fix song select iterating over all beatmaps in database unnnecessarily --- osu.Game/Screens/Select/SongSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index b3c3925a26..260867b1ea 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -253,7 +253,7 @@ namespace osu.Game.Screens.Select Schedule(() => { // if we have no beatmaps but osu-stable is found, let's prompt the user to import. - if (!beatmaps.GetAllUsableBeatmapSets().Any() && beatmaps.StableInstallationAvailable) + if (!beatmaps.GetAllUsableBeatmapSetsEnumerable().Any() && beatmaps.StableInstallationAvailable) dialogOverlay.Push(new ImportFromStablePopup(() => { Task.Run(beatmaps.ImportFromStableAsync).ContinueWith(_ => scores.ImportFromStableAsync(), TaskContinuationOptions.OnlyOnRanToCompletion); From b95a5983385a349174404d3f71957ec22462abc7 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 16 Jul 2019 15:12:01 +0900 Subject: [PATCH 1760/2854] don't check for shift --- osu.Game/Graphics/UserInterface/SearchTextBox.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index cd801a3fc9..c1020e6340 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -16,8 +16,6 @@ namespace osu.Game.Graphics.UserInterface public override bool HandleLeftRightArrows => false; - private InputManager inputManager; - public SearchTextBox() { Height = 35; @@ -36,16 +34,10 @@ namespace osu.Game.Graphics.UserInterface PlaceholderText = "type to search"; } - protected override void LoadComplete() - { - inputManager = GetContainingInputManager(); - base.LoadComplete(); - } - protected override bool HandleAction(PlatformAction action) { - // Allow shift-delete to be handled locally - if (inputManager.CurrentState.Keyboard.ShiftPressed && action.ActionType == PlatformActionType.CharNext && action.ActionMethod == PlatformActionMethod.Delete) + // Allow delete to be handled locally + if (action.ActionType == PlatformActionType.CharNext && action.ActionMethod == PlatformActionMethod.Delete) return false; return base.HandleAction(action); From b0415dc30abb7aec345c535988097be14446271b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 16 Jul 2019 17:33:14 +0900 Subject: [PATCH 1761/2854] Remove cursortrail drawnode allocs --- .../UI/Cursor/CursorTrail.cs | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index b986076593..6a70728c50 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private readonly TrailPart[] parts = new TrailPart[max_sprites]; private Vector2 size; - private readonly VertexBatch vertexBatch = new QuadBatch(max_sprites, 1); + private readonly CustomBatch vertexBatch = new CustomBatch(max_sprites, 1); public TrailDrawNode(CursorTrail source) : base(source) @@ -197,20 +197,14 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor for (int i = 0; i < parts.Length; ++i) { Vector2 pos = parts[i].Position; - float localTime = parts[i].Time; + vertexBatch.DrawTime = parts[i].Time; DrawQuad( texture, new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y), DrawColourInfo.Colour, null, - v => vertexBatch.Add(new TexturedTrailVertex - { - Position = v.Position, - TexturePosition = v.TexturePosition, - Time = localTime + 1, - Colour = v.Colour, - })); + vertexBatch.AddAction); } shader.Unbind(); @@ -222,6 +216,27 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Dispose(); } + + private class CustomBatch : QuadBatch + { + public new readonly Action AddAction; + + public float DrawTime; + + public CustomBatch(int size, int maxBuffers) + : base(size, maxBuffers) + { + AddAction = add; + } + + private void add(TexturedVertex2D vertex) => Add(new TexturedTrailVertex + { + Position = vertex.Position, + TexturePosition = vertex.TexturePosition, + Time = DrawTime + 1, + Colour = vertex.Colour, + }); + } } [StructLayout(LayoutKind.Sequential)] From 62b867018d555d6c034ce00991768bb35bcc2338 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 16 Jul 2019 17:50:03 +0900 Subject: [PATCH 1762/2854] Refactor a bit --- .../UI/Cursor/CursorTrail.cs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 6a70728c50..05eb0ffdbf 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private readonly TrailPart[] parts = new TrailPart[max_sprites]; private Vector2 size; - private readonly CustomBatch vertexBatch = new CustomBatch(max_sprites, 1); + private readonly TrailBatch vertexBatch = new TrailBatch(max_sprites, 1); public TrailDrawNode(CursorTrail source) : base(source) @@ -196,9 +196,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor for (int i = 0; i < parts.Length; ++i) { - Vector2 pos = parts[i].Position; vertexBatch.DrawTime = parts[i].Time; + Vector2 pos = parts[i].Position; + DrawQuad( texture, new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y), @@ -217,25 +218,23 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Dispose(); } - private class CustomBatch : QuadBatch + // Todo: This shouldn't exist, but is currently used to reduce allocations by caching variable-capturing closures. + private class TrailBatch : QuadBatch { public new readonly Action AddAction; - public float DrawTime; - public CustomBatch(int size, int maxBuffers) + public TrailBatch(int size, int maxBuffers) : base(size, maxBuffers) { - AddAction = add; + AddAction = v => Add(new TexturedTrailVertex + { + Position = v.Position, + TexturePosition = v.TexturePosition, + Time = DrawTime + 1, + Colour = v.Colour, + }); } - - private void add(TexturedVertex2D vertex) => Add(new TexturedTrailVertex - { - Position = vertex.Position, - TexturePosition = vertex.TexturePosition, - Time = DrawTime + 1, - Colour = vertex.Colour, - }); } } From 7c5a227fc53c4e750e19ff630719e15101d22e4b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Jul 2019 14:46:25 +0900 Subject: [PATCH 1763/2854] Fix crashes when presenting replays --- osu.Game/Screens/Select/FooterButtonMods.cs | 16 ++++++++++------ osu.Game/Screens/Select/SongSelect.cs | 11 ++++++++--- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/FooterButtonMods.cs b/osu.Game/Screens/Select/FooterButtonMods.cs index c96c5022c0..fce4d1b2e2 100644 --- a/osu.Game/Screens/Select/FooterButtonMods.cs +++ b/osu.Game/Screens/Select/FooterButtonMods.cs @@ -9,18 +9,25 @@ using osu.Game.Rulesets.Mods; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osuTK; using osuTK.Input; namespace osu.Game.Screens.Select { - public class FooterButtonMods : FooterButton + public class FooterButtonMods : FooterButton, IHasCurrentValue> { - public FooterButtonMods(Bindable> mods) + public Bindable> Current { - FooterModDisplay modDisplay; + get => modDisplay.Current; + set => modDisplay.Current = value; + } + private readonly FooterModDisplay modDisplay; + + public FooterButtonMods() + { Add(new Container { Anchor = Anchor.CentreLeft, @@ -33,9 +40,6 @@ namespace osu.Game.Screens.Select AutoSizeAxes = Axes.Both, Margin = new MarginPadding { Left = 70 } }); - - if (mods != null) - modDisplay.Current = mods; } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 260867b1ea..20dbcf2693 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -221,11 +221,9 @@ namespace osu.Game.Screens.Select [BackgroundDependencyLoader(true)] private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, ScoreManager scores) { - mods.BindTo(Mods); - if (Footer != null) { - Footer.AddButton(new FooterButtonMods(mods), ModSelect); + Footer.AddButton(new FooterButtonMods { Current = mods }, ModSelect); Footer.AddButton(new FooterButtonRandom { Action = triggerRandom }); Footer.AddButton(new FooterButtonOptions(), BeatmapOptions); @@ -263,6 +261,13 @@ namespace osu.Game.Screens.Select } } + protected override void LoadComplete() + { + base.LoadComplete(); + + mods.BindTo(Mods); + } + private DependencyContainer dependencies; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) From a9286fee07fc2c814062586a49ee116fb719334b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 16 Jul 2019 18:19:13 +0900 Subject: [PATCH 1764/2854] Recycle slider paths when the parenting slider dies --- .../Objects/Drawables/DrawableSlider.cs | 6 ++++++ .../Objects/Drawables/Pieces/SliderBody.cs | 19 ++++++++++++++++++- .../Objects/Drawables/DrawableHitObject.cs | 8 ++++---- osu.Game/Rulesets/UI/HitObjectContainer.cs | 10 ++++++++-- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 05cb42d853..4d67c9ae34 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -156,6 +156,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } + public override void OnKilled() + { + base.OnKilled(); + Body.RecyclePath(); + } + protected override void SkinChanged(ISkinSource skin, bool allowFallback) { base.SkinChanged(skin, allowFallback); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 33b3667c4f..97c7c9cec5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -13,7 +13,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public const float DEFAULT_BORDER_SIZE = 1; - private readonly SliderPath path; + private SliderPath path; + protected Path Path => path; public float PathRadius @@ -77,6 +78,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces InternalChild = path = new SliderPath(); } + /// + /// Initialises a new , releasing all resources retained by the old one. + /// + public void RecyclePath() + { + InternalChild = path = new SliderPath + { + Position = path.Position, + PathRadius = path.PathRadius, + AccentColour = path.AccentColour, + BorderColour = path.BorderColour, + BorderSize = path.BorderSize, + Vertices = path.Vertices + }; + } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos); /// diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 76233eabf2..e61fac679e 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -7,7 +7,6 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; -using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osu.Game.Audio; using osu.Game.Graphics; @@ -202,12 +201,13 @@ namespace osu.Game.Rulesets.Objects.Drawables protected virtual double InitialLifetimeOffset => 10000; /// - /// Will called at least once after the of this has been passed. + /// Will be called at least once after this has become not alive. /// - internal void OnLifetimeEnd() + public virtual void OnKilled() { foreach (var nested in NestedHitObjects) - nested.OnLifetimeEnd(); + nested.OnKilled(); + UpdateResult(false); } diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index 2f3a384e95..aa942e70df 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -34,8 +34,14 @@ namespace osu.Game.Rulesets.UI protected override void OnChildLifetimeBoundaryCrossed(LifetimeBoundaryCrossedEvent e) { - if (e.Kind == LifetimeBoundaryKind.End && e.Direction == LifetimeBoundaryCrossingDirection.Forward && e.Child is DrawableHitObject hitObject) - hitObject.OnLifetimeEnd(); + if (!(e.Child is DrawableHitObject hitObject)) + return; + + if (e.Kind == LifetimeBoundaryKind.End && e.Direction == LifetimeBoundaryCrossingDirection.Forward + || e.Kind == LifetimeBoundaryKind.Start && e.Direction == LifetimeBoundaryCrossingDirection.Backward) + { + hitObject.OnKilled(); + } } } } From 66036508b607348d1f88daf0845bc5aeffbc2c71 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jul 2019 17:39:04 +0900 Subject: [PATCH 1765/2854] Fix potential crash when displaying leaderbaords --- osu.Game/Online/Leaderboards/Leaderboard.cs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 35f7ba1c1b..18c827707a 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -51,7 +51,6 @@ namespace osu.Game.Online.Leaderboards loading.Hide(); - // schedule because we may not be loaded yet (LoadComponentAsync complains). showScoresDelegate?.Cancel(); showScoresCancellationSource?.Cancel(); @@ -61,28 +60,22 @@ namespace osu.Game.Online.Leaderboards // ensure placeholder is hidden when displaying scores PlaceholderState = PlaceholderState.Successful; - scrollFlow = CreateScoreFlow(); - scrollFlow.ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1)); + var sf = CreateScoreFlow(); + sf.ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1)); - if (!IsLoaded) - showScoresDelegate = Schedule(showScores); - else - showScores(); - - void showScores() => LoadComponentAsync(scrollFlow, _ => + // schedule because we may not be loaded yet (LoadComponentAsync complains). + showScoresDelegate = Schedule(() => LoadComponentAsync(sf, _ => { - scrollContainer.Add(scrollFlow); + scrollContainer.Add(scrollFlow = sf); int i = 0; foreach (var s in scrollFlow.Children) - { using (s.BeginDelayedSequence(i++ * 50, true)) s.Show(); - } scrollContainer.ScrollTo(0f, false); - }, (showScoresCancellationSource = new CancellationTokenSource()).Token); + }, (showScoresCancellationSource = new CancellationTokenSource()).Token)); } } From cca472d412dd7d3f9983c9f06fe485eb49f22b99 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Jul 2019 19:19:45 +0900 Subject: [PATCH 1766/2854] Fix direct ruleset selector binding in ctor --- osu.Game/Overlays/Direct/DirectRulesetSelector.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs index fdab9f1b90..2c5ea85c5a 100644 --- a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs +++ b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs @@ -26,8 +26,13 @@ namespace osu.Game.Overlays.Direct TabContainer.Masking = false; TabContainer.Spacing = new Vector2(10, 0); AutoSizeAxes = Axes.Both; + } - Current.DisabledChanged += value => SelectedTab.FadeColour(value ? Color4.DarkGray : Color4.White, 200, Easing.OutQuint); + protected override void LoadComplete() + { + base.LoadComplete(); + + Current.BindDisabledChanged(value => SelectedTab.FadeColour(value ? Color4.DarkGray : Color4.White, 200, Easing.OutQuint)); } protected override TabItem CreateTabItem(RulesetInfo value) => new DirectRulesetTabItem(value); From 9f6ff63634b19d99010b282a66a8df760d113da9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Jul 2019 19:25:41 +0900 Subject: [PATCH 1767/2854] Fix judgement disposals causing huge LOH pressure --- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 9 ++++++++- osu.Game/Skinning/LocalSkinOverrideContainer.cs | 6 +++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 0cbe0cca85..9037faf606 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.UI; using System.Linq; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.UI.Cursor; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.UI { @@ -39,7 +40,13 @@ namespace osu.Game.Rulesets.Osu.UI RelativeSizeAxes = Axes.Both, Depth = 1, }, - HitObjectContainer, + // Todo: This should not exist, but currently helps to reduce LOH allocations due to unbinding skin source events on judgement disposal + // Todo: Remove when hitobjects are properly pooled + new LocalSkinOverrideContainer(null) + { + RelativeSizeAxes = Axes.Both, + Child = HitObjectContainer, + }, approachCircles = new ApproachCircleProxyContainer { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs index 37f4cc28a2..7882e0f31b 100644 --- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs +++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs @@ -34,7 +34,7 @@ namespace osu.Game.Skinning public Drawable GetDrawableComponent(string componentName) { Drawable sourceDrawable; - if (beatmapSkins.Value && (sourceDrawable = skin.GetDrawableComponent(componentName)) != null) + if (beatmapSkins.Value && (sourceDrawable = skin?.GetDrawableComponent(componentName)) != null) return sourceDrawable; return fallbackSource?.GetDrawableComponent(componentName); @@ -43,7 +43,7 @@ namespace osu.Game.Skinning public Texture GetTexture(string componentName) { Texture sourceTexture; - if (beatmapSkins.Value && (sourceTexture = skin.GetTexture(componentName)) != null) + if (beatmapSkins.Value && (sourceTexture = skin?.GetTexture(componentName)) != null) return sourceTexture; return fallbackSource.GetTexture(componentName); @@ -52,7 +52,7 @@ namespace osu.Game.Skinning public SampleChannel GetSample(string sampleName) { SampleChannel sourceChannel; - if (beatmapHitsounds.Value && (sourceChannel = skin.GetSample(sampleName)) != null) + if (beatmapHitsounds.Value && (sourceChannel = skin?.GetSample(sampleName)) != null) return sourceChannel; return fallbackSource?.GetSample(sampleName); From 883c090248b49ec98dd14746304bc2374ad7a4cc Mon Sep 17 00:00:00 2001 From: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com> Date: Wed, 17 Jul 2019 20:02:20 +0900 Subject: [PATCH 1768/2854] Fix disabled state potentially not being set --- osu.Game/Overlays/Direct/DirectRulesetSelector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs index 2c5ea85c5a..106aaa616b 100644 --- a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs +++ b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Direct { base.LoadComplete(); - Current.BindDisabledChanged(value => SelectedTab.FadeColour(value ? Color4.DarkGray : Color4.White, 200, Easing.OutQuint)); + Current.BindDisabledChanged(value => SelectedTab.FadeColour(value ? Color4.DarkGray : Color4.White, 200, Easing.OutQuint), true); } protected override TabItem CreateTabItem(RulesetInfo value) => new DirectRulesetTabItem(value); From 42b1e1772fe591452e3e0778771e42dfbc0ef105 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jul 2019 22:07:11 +0900 Subject: [PATCH 1769/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 98f9bf1a42..31c1c29865 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 436ba90a88..f54a51d5db 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index c24349bcb5..eb6d3ead06 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From cc3492f8f29e7e192b5af10dca9479efb288b299 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jul 2019 00:01:38 +0900 Subject: [PATCH 1770/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 31c1c29865..9aa5e631ad 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f54a51d5db..86a68d2159 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index eb6d3ead06..712effcc39 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From f1423b8cb5717361443594e4311f00a1a6b34c90 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jul 2019 22:38:17 +0900 Subject: [PATCH 1771/2854] Add more brackets --- osu.Game/Rulesets/UI/HitObjectContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index aa942e70df..9485189433 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -37,8 +37,8 @@ namespace osu.Game.Rulesets.UI if (!(e.Child is DrawableHitObject hitObject)) return; - if (e.Kind == LifetimeBoundaryKind.End && e.Direction == LifetimeBoundaryCrossingDirection.Forward - || e.Kind == LifetimeBoundaryKind.Start && e.Direction == LifetimeBoundaryCrossingDirection.Backward) + if ((e.Kind == LifetimeBoundaryKind.End && e.Direction == LifetimeBoundaryCrossingDirection.Forward) + || (e.Kind == LifetimeBoundaryKind.Start && e.Direction == LifetimeBoundaryCrossingDirection.Backward)) { hitObject.OnKilled(); } From a6ddcd78d46a8fc38fa9ae8d37bfe724d58e7610 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jul 2019 01:08:12 +0900 Subject: [PATCH 1772/2854] Fix results screen not showing first tab correctly --- osu.Game/Screens/Ranking/Results.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index f53fb75e1a..cac26b3dbf 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -256,9 +256,12 @@ namespace osu.Game.Screens.Ranking } }; - foreach (var t in CreateResultPages()) - modeChangeButtons.AddItem(t); - modeChangeButtons.Current.Value = modeChangeButtons.Items.FirstOrDefault(); + var pages = CreateResultPages(); + + foreach (var p in pages) + modeChangeButtons.AddItem(p); + + modeChangeButtons.Current.Value = pages.FirstOrDefault(); modeChangeButtons.Current.BindValueChanged(page => { From 1ce2b2eaceabb32ef5c3f1953e6a4cce30387b9e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 18 Jul 2019 13:18:06 +0900 Subject: [PATCH 1773/2854] Set title as default grouping/sorting modes --- osu.Game/Screens/Select/FilterControl.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index bd0b0dd5cd..57fe15fd99 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -120,7 +120,8 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.X, Height = 24, Width = 0.5f, - AutoSort = true + AutoSort = true, + Current = { Value = GroupMode.Title } }, //spriteText = new OsuSpriteText //{ @@ -139,6 +140,7 @@ namespace osu.Game.Screens.Select Width = 0.5f, Height = 24, AutoSort = true, + Current = { Value = SortMode.Title } } } }, From f175f597e76bf717ac528ee25c98dd7cb12bc0ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jul 2019 14:05:57 +0900 Subject: [PATCH 1774/2854] Update follower count API source --- osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs | 2 +- osu.Game/Users/User.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs b/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs index 2e4fd6fe3d..6e1b6e2c7d 100644 --- a/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs @@ -53,6 +53,6 @@ namespace osu.Game.Overlays.Profile.Header.Components User.BindValueChanged(user => updateFollowers(user.NewValue), true); } - private void updateFollowers(User user) => followerText.Text = user?.FollowerCount?.Length > 0 ? user.FollowerCount[0].ToString("#,##0") : "0"; + private void updateFollowers(User user) => followerText.Text = user?.FollowerCount.ToString("#,##0"); } } diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index df41e194b0..34bd4bbaae 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -118,7 +118,7 @@ namespace osu.Game.Users public int PostCount; [JsonProperty(@"follower_count")] - public int[] FollowerCount; + public int FollowerCount; [JsonProperty] private string[] playstyle From ca72409dc269fe0ffd0a1045f17cdcec98e48ab2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 18 Jul 2019 20:16:10 +0900 Subject: [PATCH 1775/2854] More closely match osu-web styling --- .../Historical/DrawableMostPlayedBeatmap.cs | 147 +++++++++++------- 1 file changed, 87 insertions(+), 60 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs index fc457676a8..a6c41cde72 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs @@ -14,34 +14,45 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; using System.Collections.Generic; +using osu.Framework.Graphics.Cursor; namespace osu.Game.Overlays.Profile.Sections.Historical { public class DrawableMostPlayedBeatmap : OsuHoverContainer { - private readonly Box background; private const int cover_width = 100; private const int corner_radius = 10; - private readonly SpriteIcon icon; - private readonly OsuSpriteText playCountText; - private readonly LinkFlowContainer mapper; + + private readonly BeatmapInfo beatmap; + private readonly int playCount; + + private Box background; protected override IEnumerable EffectTargets => new[] { background }; public DrawableMostPlayedBeatmap(BeatmapInfo beatmap, int playCount) { + this.beatmap = beatmap; + this.playCount = playCount; Enabled.Value = true; //manually enabled, because we have no action RelativeSizeAxes = Axes.X; - Height = 60; + AutoSizeAxes = Axes.Y; + Masking = true; CornerRadius = corner_radius; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + IdleColour = colours.GreySeafoam; + HoverColour = colours.GreySeafoamLight; + Children = new Drawable[] { new UpdateableBeatmapSetCover { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, RelativeSizeAxes = Axes.Y, Width = cover_width, BeatmapSet = beatmap.BeatmapSet, @@ -49,69 +60,53 @@ namespace osu.Game.Overlays.Profile.Sections.Historical }, new Container { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Left = cover_width - corner_radius }, Children = new Drawable[] { new Container { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Masking = true, CornerRadius = corner_radius, Children = new Drawable[] { - background = new Box - { - RelativeSizeAxes = Axes.Both, - }, + background = new Box { RelativeSizeAxes = Axes.Both }, new Container { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = 15, Right = 20 }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding(10), Children = new Drawable[] { - new MostPlayedBeatmapMetadataContainer(beatmap) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Bottom = 2 }, - }, - mapper = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 15, weight: FontWeight.Regular)) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.TopLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Margin = new MarginPadding { Top = 2 }, - }.With(d => - { - d.AddText("mapped by "); - d.AddUserLink(beatmap.Metadata.Author); - }), new FillFlowContainer { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, + Direction = FillDirection.Vertical, Children = new Drawable[] { - icon = new SpriteIcon + new MostPlayedBeatmapMetadataContainer(beatmap), + new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular)) { - Icon = FontAwesome.Solid.CaretRight, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Size = new Vector2(20), - }, - playCountText = new OsuSpriteText + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Colour = colours.GreySeafoamLighter + }.With(d => { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Text = playCount.ToString(), - Font = OsuFont.GetFont(size: 30, weight: FontWeight.Regular, fixedWidth: true), - }, + d.AddText("mapped by "); + d.AddUserLink(beatmap.Metadata.Author); + }), } - } + }, + new PlayCountText(playCount) + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight + }, } }, } @@ -121,15 +116,6 @@ namespace osu.Game.Overlays.Profile.Sections.Historical }; } - [BackgroundDependencyLoader] - private void load(OsuColour colors) - { - IdleColour = colors.GreySeafoam; - HoverColour = colors.GreySeafoamLight; - mapper.Colour = colors.GreySeafoamLighter; - icon.Colour = playCountText.Colour = colors.Yellow; - } - private class MostPlayedBeatmapMetadataContainer : BeatmapMetadataContainer { public MostPlayedBeatmapMetadataContainer(BeatmapInfo beatmap) @@ -144,14 +130,55 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Text = new LocalisedString(( $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ", $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] ")), - Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold) + Font = OsuFont.GetFont(weight: FontWeight.Bold) }, new OsuSpriteText { Text = "by " + new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)), - Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular) + Font = OsuFont.GetFont(weight: FontWeight.Regular) }, }; } + + private class PlayCountText : CompositeDrawable, IHasTooltip + { + public string TooltipText => "times played"; + + public PlayCountText(int playCount) + { + AutoSizeAxes = Axes.Both; + + InternalChild = new FillFlowContainer + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(5, 0), + Children = new Drawable[] + { + new SpriteIcon + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(12), + Icon = FontAwesome.Solid.Play, + }, + new OsuSpriteText + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Text = playCount.ToString(), + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular), + }, + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = colours.Yellow; + } + } } } From f87e9b801716136e0ad43d124bc1b312b5b6212f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 18 Jul 2019 20:17:19 +0900 Subject: [PATCH 1776/2854] Remove unnecessary tooltip text --- osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs index 597171cc7f..13b547eed3 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs @@ -19,8 +19,8 @@ namespace osu.Game.Overlays.Profile.Sections protected BeatmapMetadataContainer(BeatmapInfo beatmap) { this.beatmap = beatmap; + AutoSizeAxes = Axes.Both; - TooltipText = $"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title}"; } [BackgroundDependencyLoader(true)] From fa978a47b004085da22d0ebacb31e7df7c8d18d7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 18 Jul 2019 15:08:50 +0300 Subject: [PATCH 1777/2854] Fix loading animation is no longer present --- .../BeatmapSet/Scores/ScoresContainer.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 22d7ea9c97..0adaaa20b2 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -19,7 +19,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores public class ScoresContainer : CompositeDrawable { private const int spacing = 15; - private const int fade_duration = 200; private readonly Box background; private readonly ScoreTable scoreTable; @@ -53,8 +52,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { Schedule(() => { - loading = false; - topScoresContainer.Clear(); if (value?.Scores.Any() != true) @@ -128,13 +125,10 @@ namespace osu.Game.Overlays.BeatmapSet.Scores background.Colour = colours.Gray2; } - private bool loading - { - set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration); - } - private void getScores(BeatmapInfo beatmap) { + loadingAnimation.Show(); + getScoresRequest?.Cancel(); getScoresRequest = null; @@ -142,14 +136,17 @@ namespace osu.Game.Overlays.BeatmapSet.Scores if (beatmap?.OnlineBeatmapID.HasValue != true) { - loading = false; + loadingAnimation.Hide(); return; } getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); - getScoresRequest.Success += scores => Scores = scores; + getScoresRequest.Success += scores => + { + loadingAnimation.Hide(); + Scores = scores; + }; api.Queue(getScoresRequest); - loading = true; } } } From 00f1d1b53cc47c9be1743bf1b69d28b0c89ee659 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Jul 2019 00:53:00 +0900 Subject: [PATCH 1778/2854] Reduce Triangle drawnode overhead by ~90% This was never batching, ever. Pointless memory overhead. --- osu.Game/Graphics/Backgrounds/Triangles.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 29113e0e2f..3502531d13 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -137,11 +137,13 @@ namespace osu.Game.Graphics.Backgrounds } } + protected int AimCount; + private void addTriangles(bool randomY) { - int aimTriangleCount = (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio); + AimCount = (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio); - for (int i = 0; i < aimTriangleCount - parts.Count; i++) + for (int i = 0; i < AimCount - parts.Count; i++) parts.Add(createTriangle(randomY)); } @@ -190,11 +192,12 @@ namespace osu.Game.Graphics.Backgrounds private readonly List parts = new List(); private Vector2 size; - private readonly LinearBatch vertexBatch = new LinearBatch(100 * 3, 10, PrimitiveType.Triangles); + private readonly LinearBatch vertexBatch; public TrianglesDrawNode(Triangles source) : base(source) { + vertexBatch = new LinearBatch(source.AimCount * 3, 2, PrimitiveType.Triangles); } public override void ApplyState() From a23bb3a6b32f48355dbb6f0b837d1835e4b0a14b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Jul 2019 01:14:55 +0900 Subject: [PATCH 1779/2854] Account for headless nulls (but how?) --- osu.Game/Graphics/Backgrounds/Triangles.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 3502531d13..20e92cd2c6 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -249,7 +249,7 @@ namespace osu.Game.Graphics.Backgrounds { base.Dispose(isDisposing); - vertexBatch.Dispose(); + vertexBatch?.Dispose(); } } From 95f36c36c4d00dc53c8b27bd2bb44c167d21b739 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Jul 2019 01:52:53 +0900 Subject: [PATCH 1780/2854] Late-initialize vertex batch for safety --- osu.Game/Graphics/Backgrounds/Triangles.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 20e92cd2c6..4f50abc985 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -192,12 +192,11 @@ namespace osu.Game.Graphics.Backgrounds private readonly List parts = new List(); private Vector2 size; - private readonly LinearBatch vertexBatch; + private LinearBatch vertexBatch; public TrianglesDrawNode(Triangles source) : base(source) { - vertexBatch = new LinearBatch(source.AimCount * 3, 2, PrimitiveType.Triangles); } public override void ApplyState() @@ -214,6 +213,9 @@ namespace osu.Game.Graphics.Backgrounds public override void Draw(Action vertexAction) { + if (vertexBatch == null && Source.AimCount > 0) + vertexBatch = new LinearBatch(Source.AimCount * 3, 2, PrimitiveType.Triangles); + base.Draw(vertexAction); shader.Bind(); From 024f136d820db7791324d988fc4e9d2a20e47676 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Jul 2019 14:24:11 +0900 Subject: [PATCH 1781/2854] Reduce buffer count by one --- osu.Game/Graphics/Backgrounds/Triangles.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 4f50abc985..62151d066b 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -213,11 +213,14 @@ namespace osu.Game.Graphics.Backgrounds public override void Draw(Action vertexAction) { - if (vertexBatch == null && Source.AimCount > 0) - vertexBatch = new LinearBatch(Source.AimCount * 3, 2, PrimitiveType.Triangles); - base.Draw(vertexAction); + if (vertexBatch == null || vertexBatch.Size != Source.AimCount * 6) + { + vertexBatch?.Dispose(); + vertexBatch = new LinearBatch(Source.AimCount * 6, 1, PrimitiveType.Triangles); + } + shader.Bind(); Vector2 localInflationAmount = edge_smoothness * DrawInfo.MatrixInverse.ExtractScale().Xy; From 99ab77b926a5fb3e569e2955eb577e1a2211adfd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 19 Jul 2019 15:33:09 +0900 Subject: [PATCH 1782/2854] Add PaginatedWebRequest to handle request pagination --- .../API/Requests/GetUserBeatmapsRequest.cs | 11 +++---- .../GetUserMostPlayedBeatmapsRequest.cs | 9 ++---- .../GetUserRecentActivitiesRequest.cs | 9 ++---- .../API/Requests/GetUserScoresRequest.cs | 10 ++----- .../API/Requests/PaginatedAPIRequest.cs | 30 +++++++++++++++++++ 5 files changed, 43 insertions(+), 26 deletions(-) create mode 100644 osu.Game/Online/API/Requests/PaginatedAPIRequest.cs diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index 57005181ca..978d0915fb 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -7,23 +7,20 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Online.API.Requests { - public class GetUserBeatmapsRequest : APIRequest> + public class GetUserBeatmapsRequest : PaginatedAPIRequest> { private readonly long userId; - private readonly int offset; - private readonly int limit; + private readonly BeatmapSetType type; public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0, int limit = 6) + : base(offset, limit) { this.userId = userId; - this.offset = offset; - this.limit = limit; this.type = type; } - // ReSharper disable once ImpureMethodCallOnReadonlyValueField - protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}?offset={offset}&limit={limit}"; + protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}"; } public enum BeatmapSetType diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index fccb27a86c..a363d127d8 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -6,19 +6,16 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Online.API.Requests { - public class GetUserMostPlayedBeatmapsRequest : APIRequest> + public class GetUserMostPlayedBeatmapsRequest : PaginatedAPIRequest> { private readonly long userId; - private readonly int offset; - private readonly int limit; public GetUserMostPlayedBeatmapsRequest(long userId, int offset = 0, int limit = 5) + : base(offset, limit) { this.userId = userId; - this.offset = offset; - this.limit = limit; } - protected override string Target => $@"users/{userId}/beatmapsets/most_played?offset={offset}&limit={limit}"; + protected override string Target => $@"users/{userId}/beatmapsets/most_played"; } } diff --git a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs index d066636911..5675e877b5 100644 --- a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs @@ -6,20 +6,17 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Online.API.Requests { - public class GetUserRecentActivitiesRequest : APIRequest> + public class GetUserRecentActivitiesRequest : PaginatedAPIRequest> { private readonly long userId; - private readonly int offset; - private readonly int limit; public GetUserRecentActivitiesRequest(long userId, int offset = 0, int limit = 5) + : base(offset, limit) { this.userId = userId; - this.offset = offset; - this.limit = limit; } - protected override string Target => $"users/{userId}/recent_activity?offset={offset}&limit={limit}"; + protected override string Target => $"users/{userId}/recent_activity"; } public enum RecentActivityType diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs index 0e2f7ec417..03a11b68ac 100644 --- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs @@ -6,23 +6,19 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Online.API.Requests { - public class GetUserScoresRequest : APIRequest> + public class GetUserScoresRequest : PaginatedAPIRequest> { private readonly long userId; private readonly ScoreType type; - private readonly int offset; - private readonly int limit; public GetUserScoresRequest(long userId, ScoreType type, int offset = 0, int limit = 5) + : base(offset, limit) { this.userId = userId; this.type = type; - this.offset = offset; - this.limit = limit; } - // ReSharper disable once ImpureMethodCallOnReadonlyValueField - protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLowerInvariant()}?offset={offset}&limit={limit}"; + protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLowerInvariant()}"; } public enum ScoreType diff --git a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs new file mode 100644 index 0000000000..45360612bd --- /dev/null +++ b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Globalization; +using osu.Framework.IO.Network; + +namespace osu.Game.Online.API.Requests +{ + public abstract class PaginatedAPIRequest : APIRequest + { + private readonly int offset; + private readonly int limit; + + protected PaginatedAPIRequest(int offset, int limit) + { + this.offset = offset; + this.limit = limit; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + + req.AddParameter("offset", offset.ToString(CultureInfo.InvariantCulture)); + req.AddParameter("limit", limit.ToString(CultureInfo.InvariantCulture)); + + return req; + } + } +} From 066bee35350fa0f094a1b08983d426ff5b178f18 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 19 Jul 2019 16:02:33 +0900 Subject: [PATCH 1783/2854] Simplify offset calculation --- .../Online/API/Requests/GetUserBeatmapsRequest.cs | 4 ++-- .../Requests/GetUserMostPlayedBeatmapsRequest.cs | 4 ++-- .../API/Requests/GetUserRecentActivitiesRequest.cs | 4 ++-- .../Online/API/Requests/GetUserScoresRequest.cs | 4 ++-- .../Online/API/Requests/PaginatedAPIRequest.cs | 14 +++++++------- .../Sections/Beatmaps/PaginatedBeatmapContainer.cs | 2 +- .../PaginatedMostPlayedBeatmapContainer.cs | 2 +- .../Sections/Ranks/PaginatedScoreContainer.cs | 2 +- .../Recent/PaginatedRecentActivityContainer.cs | 2 +- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index 978d0915fb..f3384163b8 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -13,8 +13,8 @@ namespace osu.Game.Online.API.Requests private readonly BeatmapSetType type; - public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0, int limit = 6) - : base(offset, limit) + public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int page = 0, int itemsPerPage = 6) + : base(page, itemsPerPage) { this.userId = userId; this.type = type; diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index a363d127d8..9f094e51c4 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserMostPlayedBeatmapsRequest(long userId, int offset = 0, int limit = 5) - : base(offset, limit) + public GetUserMostPlayedBeatmapsRequest(long userId, int page = 0, int itemsPerPage = 5) + : base(page, itemsPerPage) { this.userId = userId; } diff --git a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs index 5675e877b5..4908e5ecc2 100644 --- a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserRecentActivitiesRequest(long userId, int offset = 0, int limit = 5) - : base(offset, limit) + public GetUserRecentActivitiesRequest(long userId, int page = 0, int itemsPerPage = 5) + : base(page, itemsPerPage) { this.userId = userId; } diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs index 03a11b68ac..d41966fe1b 100644 --- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs @@ -11,8 +11,8 @@ namespace osu.Game.Online.API.Requests private readonly long userId; private readonly ScoreType type; - public GetUserScoresRequest(long userId, ScoreType type, int offset = 0, int limit = 5) - : base(offset, limit) + public GetUserScoresRequest(long userId, ScoreType type, int page = 0, int itemsPerPage = 5) + : base(page, itemsPerPage) { this.userId = userId; this.type = type; diff --git a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs index 45360612bd..52e12f04ee 100644 --- a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs +++ b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs @@ -8,21 +8,21 @@ namespace osu.Game.Online.API.Requests { public abstract class PaginatedAPIRequest : APIRequest { - private readonly int offset; - private readonly int limit; + private readonly int page; + private readonly int itemsPerPage; - protected PaginatedAPIRequest(int offset, int limit) + protected PaginatedAPIRequest(int page, int itemsPerPage) { - this.offset = offset; - this.limit = limit; + this.page = page; + this.itemsPerPage = itemsPerPage; } protected override WebRequest CreateWebRequest() { var req = base.CreateWebRequest(); - req.AddParameter("offset", offset.ToString(CultureInfo.InvariantCulture)); - req.AddParameter("limit", limit.ToString(CultureInfo.InvariantCulture)); + req.AddParameter("offset", (page * itemsPerPage).ToString(CultureInfo.InvariantCulture)); + req.AddParameter("limit", itemsPerPage.ToString(CultureInfo.InvariantCulture)); return req; } diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index 22f8e6c6b0..1b6c1c99a6 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps protected override void ShowMore() { - request = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage, ItemsPerPage); + request = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); request.Success += sets => Schedule(() => { MoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0); diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index 92b0304a68..9409cd9aeb 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected override void ShowMore() { - request = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage, ItemsPerPage); + request = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, ItemsPerPage); request.Success += beatmaps => Schedule(() => { MoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0); diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 5bb362e21c..4a9ac6e5c7 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks protected override void ShowMore() { - request = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage, ItemsPerPage); + request = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); request.Success += scores => Schedule(() => { foreach (var s in scores) diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index 215c96a423..f2a778a874 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent protected override void ShowMore() { - request = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage, ItemsPerPage); + request = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++, ItemsPerPage); request.Success += activities => Schedule(() => { MoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0); From eebfdd88650446da481fdfceebbfdb0b1f17c8ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Jul 2019 23:43:25 +0900 Subject: [PATCH 1784/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 9aa5e631ad..b9451fc744 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 86a68d2159..d90b1d36e1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 712effcc39..fa2521a19e 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 5696d794235850fc37578ef475c4b91e2ec74d98 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Jul 2019 23:47:48 +0900 Subject: [PATCH 1785/2854] Use TriangleBatch --- osu.Game/Graphics/Backgrounds/Triangles.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 62151d066b..a2d8f42fac 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -8,7 +8,6 @@ using osuTK.Graphics; using System; using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Textures; -using osuTK.Graphics.ES30; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Primitives; using osu.Framework.Allocation; @@ -192,7 +191,7 @@ namespace osu.Game.Graphics.Backgrounds private readonly List parts = new List(); private Vector2 size; - private LinearBatch vertexBatch; + private TriangleBatch vertexBatch; public TrianglesDrawNode(Triangles source) : base(source) @@ -215,10 +214,10 @@ namespace osu.Game.Graphics.Backgrounds { base.Draw(vertexAction); - if (vertexBatch == null || vertexBatch.Size != Source.AimCount * 6) + if (vertexBatch == null || vertexBatch.Size != Source.AimCount) { vertexBatch?.Dispose(); - vertexBatch = new LinearBatch(Source.AimCount * 6, 1, PrimitiveType.Triangles); + vertexBatch = new TriangleBatch(Source.AimCount, 1); } shader.Bind(); From 2a94b68ecb4cebe004240b7f88a680b6ccd4f54b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 20 Jul 2019 22:50:17 +0900 Subject: [PATCH 1786/2854] Simplify logic --- osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 0adaaa20b2..a6cc2b0500 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -127,19 +127,15 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private void getScores(BeatmapInfo beatmap) { - loadingAnimation.Show(); - getScoresRequest?.Cancel(); getScoresRequest = null; Scores = null; if (beatmap?.OnlineBeatmapID.HasValue != true) - { - loadingAnimation.Hide(); return; - } + loadingAnimation.Show(); getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); getScoresRequest.Success += scores => { From 4c592a5e65a11ad8a3b472f0ab6ba796e1196109 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 20 Jul 2019 22:50:35 +0900 Subject: [PATCH 1787/2854] Fix TriangleDrawNode crash when aimcount is zero --- osu.Game/Graphics/Backgrounds/Triangles.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index a2d8f42fac..79f227fafe 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -214,7 +214,7 @@ namespace osu.Game.Graphics.Backgrounds { base.Draw(vertexAction); - if (vertexBatch == null || vertexBatch.Size != Source.AimCount) + if (vertexBatch == null || vertexBatch.Size != Source.AimCount && Source.AimCount > 0) { vertexBatch?.Dispose(); vertexBatch = new TriangleBatch(Source.AimCount, 1); From 2926932a1acf5adc8da8125ba83b05a7960ebb60 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 20 Jul 2019 21:10:17 +0200 Subject: [PATCH 1788/2854] Add MusicControllerToast used to display current music playback status on OSD --- osu.Game/OsuGame.cs | 6 +-- osu.Game/Overlays/OSD/MusicControllerToast.cs | 13 ++++++ osu.Game/Overlays/OSD/OsdIconToast.cs | 45 ------------------- 3 files changed, 16 insertions(+), 48 deletions(-) create mode 100644 osu.Game/Overlays/OSD/MusicControllerToast.cs delete mode 100644 osu.Game/Overlays/OSD/OsdIconToast.cs diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index bae301a8a6..dde87cd2ed 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -726,21 +726,21 @@ namespace osu.Game if (!musicController.IsLoaded) return true; if (musicController.PlayTrack()) - osd.Display(new Overlays.OSD.OsdIconToast(musicController.IsPlaying ? "Play track" : "Pause track", musicController.IsPlaying ? FontAwesome.Solid.PlayCircle : FontAwesome.Solid.PauseCircle)); + osd.Display(new Overlays.OSD.MusicControllerToast(musicController.IsPlaying ? "Play track" : "Pause track")); return true; case GlobalAction.MusicNext: if (!musicController.IsLoaded) return true; if (musicController.NextTrack()) - osd.Display(new Overlays.OSD.OsdIconToast("Next track", FontAwesome.Solid.FastForward)); + osd.Display(new Overlays.OSD.MusicControllerToast("Next track")); return true; case GlobalAction.MusicPrev: if (!musicController.IsLoaded) return true; if (musicController.PreviousTrack()) - osd.Display(new Overlays.OSD.OsdIconToast("Previous track", FontAwesome.Solid.FastBackward)); + osd.Display(new Overlays.OSD.MusicControllerToast("Previous track")); return true; } diff --git a/osu.Game/Overlays/OSD/MusicControllerToast.cs b/osu.Game/Overlays/OSD/MusicControllerToast.cs new file mode 100644 index 0000000000..d9e0ad2c07 --- /dev/null +++ b/osu.Game/Overlays/OSD/MusicControllerToast.cs @@ -0,0 +1,13 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Overlays.OSD +{ + public class MusicControllerToast : Toast + { + public MusicControllerToast(string value) + : base("Music Playback", value, "") + { + } + } +} diff --git a/osu.Game/Overlays/OSD/OsdIconToast.cs b/osu.Game/Overlays/OSD/OsdIconToast.cs deleted file mode 100644 index 0e2bcd377f..0000000000 --- a/osu.Game/Overlays/OSD/OsdIconToast.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; - -namespace osu.Game.Overlays.OSD -{ - public class OsdIconToast : OsdToast - { - public OsdIconToast(string message, IconUsage icon) - { - Children = new Drawable[] - { - new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Direction = FillDirection.Vertical, - Spacing = new osuTK.Vector2(10), - Children = new Drawable[] - { - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.BottomCentre, - Font = OsuFont.GetFont(size: 24, weight: FontWeight.Light), - Text = message - }, - new SpriteIcon - { - Icon = icon, - Size = new osuTK.Vector2(45), - Anchor = Anchor.Centre, - Origin = Anchor.BottomCentre, - } - } - } - }; - } - } -} From 842417cf42480d7df01a50e23f6e016a720ca9a4 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 21 Jul 2019 03:07:27 +0300 Subject: [PATCH 1789/2854] Check if selected scope requires API --- osu.Game/Online/Leaderboards/Leaderboard.cs | 6 +++++- osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs | 2 ++ osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 18c827707a..5e3f57a19e 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -194,13 +194,17 @@ namespace osu.Game.Online.Leaderboards private APIRequest getScoresRequest; + protected abstract bool IsOnlineScope(); + public void APIStateChanged(IAPIProvider api, APIState state) { switch (state) { case APIState.Online: case APIState.Offline: - UpdateScores(); + if (IsOnlineScope()) + UpdateScores(); + break; } } diff --git a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs index fff713f026..873765c17c 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs @@ -33,6 +33,8 @@ namespace osu.Game.Screens.Multi.Match.Components }, true); } + protected override bool IsOnlineScope() => true; + protected override APIRequest FetchScores(Action> scoresCallback) { if (roomId.Value == null) diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 0f6d4f3188..3c857cc44a 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -79,6 +79,8 @@ namespace osu.Game.Screens.Select.Leaderboards }; } + protected override bool IsOnlineScope() => Scope != BeatmapLeaderboardScope.Local; + protected override APIRequest FetchScores(Action> scoresCallback) { if (Scope == BeatmapLeaderboardScope.Local) From e76b3e2b406d2e744b1794edbd1fe6cca17352d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 21 Jul 2019 10:42:40 +0900 Subject: [PATCH 1790/2854] User property instead of method --- osu.Game/Online/Leaderboards/Leaderboard.cs | 4 ++-- osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs | 2 +- osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 5e3f57a19e..98f15599fc 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -194,7 +194,7 @@ namespace osu.Game.Online.Leaderboards private APIRequest getScoresRequest; - protected abstract bool IsOnlineScope(); + protected abstract bool IsOnlineScope { get; } public void APIStateChanged(IAPIProvider api, APIState state) { @@ -202,7 +202,7 @@ namespace osu.Game.Online.Leaderboards { case APIState.Online: case APIState.Offline: - if (IsOnlineScope()) + if (IsOnlineScope) UpdateScores(); break; diff --git a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs index 873765c17c..ae27e53813 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Multi.Match.Components }, true); } - protected override bool IsOnlineScope() => true; + protected override bool IsOnlineScope => true; protected override APIRequest FetchScores(Action> scoresCallback) { diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 3c857cc44a..cb45c00f66 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -79,7 +79,7 @@ namespace osu.Game.Screens.Select.Leaderboards }; } - protected override bool IsOnlineScope() => Scope != BeatmapLeaderboardScope.Local; + protected override bool IsOnlineScope => Scope != BeatmapLeaderboardScope.Local; protected override APIRequest FetchScores(Action> scoresCallback) { From ed0ef90613bf1038883c4368cb6702da211fe45f Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 21 Jul 2019 12:14:55 +0300 Subject: [PATCH 1791/2854] Separate glowing sprite text into it's own class --- .../Graphics/Sprites/GlowingSpriteText.cs | 110 ++++++++++++++++++ .../Online/Leaderboards/LeaderboardScore.cs | 57 ++------- 2 files changed, 122 insertions(+), 45 deletions(-) create mode 100644 osu.Game/Graphics/Sprites/GlowingSpriteText.cs diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs new file mode 100644 index 0000000000..77187b9ff2 --- /dev/null +++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs @@ -0,0 +1,110 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Graphics.Sprites +{ + public class GlowingSpriteText : Container + { + private readonly BufferedContainer blurContainer; + private readonly OsuSpriteText spriteText, glowingText; + + private string text = string.Empty; + + public string Text + { + get => text; + set + { + text = value; + + spriteText.Text = text; + glowingText.Text = text; + } + } + + private FontUsage font = OsuFont.Default.With(fixedWidth: true); + + public FontUsage Font + { + get => font; + set + { + font = value.With(fixedWidth: true); + + spriteText.Font = font; + glowingText.Font = font; + } + } + + private Vector2 textSize; + + public Vector2 TextSize + { + get => textSize; + set + { + textSize = value; + + spriteText.Size = textSize; + glowingText.Size = textSize; + } + } + + public ColourInfo TextColour + { + get => spriteText.Colour; + set => spriteText.Colour = value; + } + + public ColourInfo GlowColour + { + get => glowingText.Colour; + set => glowingText.Colour = value; + } + + public GlowingSpriteText() + { + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + blurContainer = new BufferedContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + BlurSigma = new Vector2(4), + CacheDrawnFrameBuffer = true, + RelativeSizeAxes = Axes.Both, + Blending = BlendingMode.Additive, + Size = new Vector2(3f), + Children = new[] + { + glowingText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = font, + Text = text, + Shadow = false, + }, + }, + }, + spriteText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = font, + Text = text, + Shadow = false, + }, + }; + } + } +} diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 9840b59805..008f8208eb 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -187,7 +187,13 @@ namespace osu.Game.Online.Leaderboards Spacing = new Vector2(5f, 0f), Children = new Drawable[] { - scoreLabel = new GlowingSpriteText(score.TotalScore.ToString(@"N0"), OsuFont.Numeric.With(size: 23), Color4.White, OsuColour.FromHex(@"83ccfa")), + scoreLabel = new GlowingSpriteText + { + TextColour = Color4.White, + GlowColour = OsuColour.FromHex(@"83ccfa"), + Text = score.TotalScore.ToString(@"N0"), + Font = OsuFont.Numeric.With(size: 23), + }, RankContainer = new Container { Size = new Vector2(40f, 20f), @@ -275,49 +281,6 @@ namespace osu.Game.Online.Leaderboards base.OnHoverLost(e); } - private class GlowingSpriteText : Container - { - public GlowingSpriteText(string text, FontUsage font, Color4 textColour, Color4 glowColour) - { - AutoSizeAxes = Axes.Both; - - Children = new Drawable[] - { - new BufferedContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - BlurSigma = new Vector2(4), - CacheDrawnFrameBuffer = true, - RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, - Size = new Vector2(3f), - Children = new[] - { - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = font.With(fixedWidth: true), - Text = text, - Colour = glowColour, - Shadow = false, - }, - }, - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = font.With(fixedWidth: true), - Text = text, - Colour = textColour, - Shadow = false, - }, - }; - } - } - private class ScoreComponentLabel : Container, IHasTooltip { private const float icon_size = 20; @@ -367,10 +330,14 @@ namespace osu.Game.Online.Leaderboards }, }, }, - new GlowingSpriteText(statistic.Value, OsuFont.GetFont(size: 17, weight: FontWeight.Bold), Color4.White, OsuColour.FromHex(@"83ccfa")) + new GlowingSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, + TextColour = Color4.White, + GlowColour = OsuColour.FromHex(@"83ccfa"), + Text = statistic.Value, + Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold), }, }, }; From 9d7f6abbddb5354839d1afe45d706b806789abef Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 21 Jul 2019 12:18:31 +0300 Subject: [PATCH 1792/2854] Remove unnecessary field --- osu.Game/Graphics/Sprites/GlowingSpriteText.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs index 77187b9ff2..b075dc0476 100644 --- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs @@ -12,7 +12,6 @@ namespace osu.Game.Graphics.Sprites { public class GlowingSpriteText : Container { - private readonly BufferedContainer blurContainer; private readonly OsuSpriteText spriteText, glowingText; private string text = string.Empty; @@ -75,7 +74,7 @@ namespace osu.Game.Graphics.Sprites Children = new Drawable[] { - blurContainer = new BufferedContainer + new BufferedContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 87fb22352c4fea9f2740021e8315f9331e263926 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 21 Jul 2019 12:28:55 +0300 Subject: [PATCH 1793/2854] glowingText -> blurredText --- osu.Game/Graphics/Sprites/GlowingSpriteText.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs index b075dc0476..7082e99600 100644 --- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs @@ -12,7 +12,7 @@ namespace osu.Game.Graphics.Sprites { public class GlowingSpriteText : Container { - private readonly OsuSpriteText spriteText, glowingText; + private readonly OsuSpriteText spriteText, blurredText; private string text = string.Empty; @@ -85,7 +85,7 @@ namespace osu.Game.Graphics.Sprites Size = new Vector2(3f), Children = new[] { - glowingText = new OsuSpriteText + blurredText = new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 57fc5cbda86e6c5ea6565227d66732d0cd8de506 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 21 Jul 2019 12:29:06 +0300 Subject: [PATCH 1794/2854] Fix CI issues --- osu.Game/Graphics/Sprites/GlowingSpriteText.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs index 7082e99600..abc81df7a7 100644 --- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osuTK; -using osuTK.Graphics; namespace osu.Game.Graphics.Sprites { From affd0b28789b0339be71ee68e2265d5cae49f759 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 21 Jul 2019 12:34:52 +0300 Subject: [PATCH 1795/2854] Fix build issues --- osu.Game/Graphics/Sprites/GlowingSpriteText.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs index abc81df7a7..546b8cae58 100644 --- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs @@ -23,7 +23,7 @@ namespace osu.Game.Graphics.Sprites text = value; spriteText.Text = text; - glowingText.Text = text; + blurredText.Text = text; } } @@ -37,7 +37,7 @@ namespace osu.Game.Graphics.Sprites font = value.With(fixedWidth: true); spriteText.Font = font; - glowingText.Font = font; + blurredText.Font = font; } } @@ -51,7 +51,7 @@ namespace osu.Game.Graphics.Sprites textSize = value; spriteText.Size = textSize; - glowingText.Size = textSize; + blurredText.Size = textSize; } } @@ -63,8 +63,8 @@ namespace osu.Game.Graphics.Sprites public ColourInfo GlowColour { - get => glowingText.Colour; - set => glowingText.Colour = value; + get => blurredText.Colour; + set => blurredText.Colour = value; } public GlowingSpriteText() From de8ac9a428b994691b2195ddf7cf3ab74b82e47a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 21 Jul 2019 21:41:07 +0300 Subject: [PATCH 1796/2854] Simple implementation --- osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs | 5 +++++ osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs | 4 ++++ osu.Game/Overlays/BeatmapSet/Header.cs | 2 ++ 3 files changed, 11 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index ea3f0b61b9..df3a45d1cc 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -66,6 +66,11 @@ namespace osu.Game.Beatmaps /// public int FavouriteCount { get; set; } + /// + /// Whether this beatmap set has been favourited by the current user. + /// + public bool HasFavourited { get; set; } + /// /// The availability of this beatmap set. /// diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 200a705500..e5bfde8f8f 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -30,6 +30,9 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"preview_url")] private string preview { get; set; } + [JsonProperty(@"has_favourited")] + private bool hasFavourited { get; set; } + [JsonProperty(@"play_count")] private int playCount { get; set; } @@ -91,6 +94,7 @@ namespace osu.Game.Online.API.Requests.Responses Ranked = ranked, LastUpdated = lastUpdated, Availability = availability, + HasFavourited = hasFavourited, }, Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(), }; diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index b50eac2c1a..dd8a2f18c1 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -246,6 +246,8 @@ namespace osu.Game.Overlays.BeatmapSet onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status; downloadButtonsContainer.FadeIn(transition_duration); + + favouriteButton.Favourited.Value = setInfo.NewValue.OnlineInfo.HasFavourited; favouriteButton.FadeIn(transition_duration); updateDownloadButtons(); From e50b70d6157266cb6448719b033aba2a15eb4401 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jul 2019 15:59:22 +0900 Subject: [PATCH 1797/2854] Centralise osu! circle radius specification --- .../Blueprints/HitCircles/Components/HitCirclePiece.cs | 2 +- .../Blueprints/Sliders/Components/SliderBodyPiece.cs | 3 +-- .../Objects/Drawables/DrawableSlider.cs | 2 +- .../Objects/Drawables/Pieces/CirclePiece.cs | 2 +- .../Objects/Drawables/Pieces/ExplodePiece.cs | 2 +- .../Objects/Drawables/Pieces/FlashPiece.cs | 4 ++-- .../Objects/Drawables/Pieces/RingPiece.cs | 4 ++-- .../Objects/Drawables/Pieces/SliderBall.cs | 10 ++++------ osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 +- 9 files changed, 14 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs index 7f6a60c400..fe11ead94d 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components this.hitCircle = hitCircle; Origin = Anchor.Centre; - Size = new Vector2((float)OsuHitObject.OBJECT_RADIUS * 2); + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); Scale = new Vector2(hitCircle.Scale); CornerRadius = Size.X / 2; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs index 957550a051..f1f55731b6 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs @@ -24,7 +24,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components InternalChild = body = new ManualSliderBody { AccentColour = Color4.Transparent, - PathRadius = slider.Scale * 64 }; } @@ -34,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components body.BorderColour = colours.Yellow; PositionBindable.BindValueChanged(_ => updatePosition(), true); - ScaleBindable.BindValueChanged(scale => body.PathRadius = scale.NewValue * 64, true); + ScaleBindable.BindValueChanged(scale => body.PathRadius = scale.NewValue * OsuHitObject.OBJECT_RADIUS, true); } private void updatePosition() => Position = slider.StackedPosition; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 4d67c9ae34..56b5decd30 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { Body = new SnakingSliderBody(s) { - PathRadius = s.Scale * 64, + PathRadius = s.Scale * OsuHitObject.OBJECT_RADIUS, }, ticks = new Container { RelativeSizeAxes = Axes.Both }, repeatPoints = new Container { RelativeSizeAxes = Axes.Both }, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index 786cac7198..dc0b149140 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public CirclePiece() { - Size = new Vector2((float)OsuHitObject.OBJECT_RADIUS * 2); + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); Masking = true; CornerRadius = Size.X / 2; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs index b960f40578..8ff16f8b84 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public ExplodePiece() { - Size = new Vector2(128); + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); Anchor = Anchor.Centre; Origin = Anchor.Centre; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs index 8e5eb886aa..c22073f56c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public FlashPiece() { - Size = new Vector2(128); + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); Anchor = Anchor.Centre; Origin = Anchor.Centre; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs index 28180a7f71..af733f16e1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public RingPiece() { - Size = new Vector2(128); + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces RelativeSizeAxes = Axes.Both } } - }); + }, confineMode: ConfineMode.NoScaling); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 7d1d77ae96..9ba8ad3474 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -17,8 +17,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class SliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition { - private const float width = 128; - private Color4 accentColour = Color4.Black; public Func GetInitialHitAction; @@ -57,8 +55,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { Origin = Anchor.Centre, Anchor = Anchor.Centre, - Width = width, - Height = width, + Width = OsuHitObject.OBJECT_RADIUS * 2, + Height = OsuHitObject.OBJECT_RADIUS * 2, Alpha = 0, Child = new SkinnableDrawable("Play/osu/sliderfollowcircle", _ => new CircularContainer { @@ -84,8 +82,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Alpha = 1, Child = new Container { - Width = width, - Height = width, + Width = OsuHitObject.OBJECT_RADIUS * 2, + Height = OsuHitObject.OBJECT_RADIUS * 2, // TODO: support skin filename animation (sliderb0, sliderb1...) Child = new SkinnableDrawable("Play/osu/sliderb", _ => new CircularContainer { diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 364c182dd4..d1221fd2d3 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Objects { public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition { - public const double OBJECT_RADIUS = 64; + public const float OBJECT_RADIUS = 64; public double TimePreempt = 600; public double TimeFadeIn = 400; From 74c961bcffcf7030ccae4320e3fb951346a734d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jul 2019 12:10:28 +0900 Subject: [PATCH 1798/2854] Add more flexible skin element confine modes --- .../Drawables/Connections/FollowPoint.cs | 2 +- .../Objects/Drawables/DrawableRepeatPoint.cs | 2 +- .../Objects/Drawables/DrawableSliderTick.cs | 4 +- .../Objects/Drawables/Pieces/NumberPiece.cs | 2 +- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 2 +- .../Gameplay/TestSceneSkinReloadable.cs | 4 +- .../Rulesets/Judgements/DrawableJudgement.cs | 2 +- osu.Game/Skinning/SkinnableDrawable.cs | 44 +++++++++++++------ osu.Game/Skinning/SkinnableSprite.cs | 4 +- osu.Game/Skinning/SkinnableSpriteText.cs | 4 +- 10 files changed, 43 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs index aacf3ee08d..a2a23e9ff7 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections Anchor = Anchor.Centre, Alpha = 0.5f, } - }, restrictSize: false); + }, confineMode: ConfineMode.NoScaling); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index cce6dfe106..cf6f05a604 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { RelativeSizeAxes = Axes.Both, Icon = FontAwesome.Solid.ChevronRight - }, restrictSize: false) + }) }; } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 72b648bfd0..01ef1eb3e0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) { - Size = new Vector2(16) * sliderTick.Scale; + Size = new Vector2(16 * sliderTick.Scale); Origin = Anchor.Centre; InternalChildren = new Drawable[] @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Colour = AccentColour, Alpha = 0.3f, } - }, restrictSize: false) + }) }; } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs index 84034d3ee9..e8dc63abca 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { Font = OsuFont.Numeric.With(size: 40), UseFullGlyphHeight = false, - }, restrictSize: false) + }, confineMode: ConfineMode.NoScaling) { Text = @"1" } diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index 27546fa424..b3492a2b31 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor }, }, } - }, restrictSize: false) + }) { Origin = Anchor.Centre, Anchor = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs index c7a0df6e9f..af03ddc65c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs @@ -92,8 +92,8 @@ namespace osu.Game.Tests.Visual.Gameplay public new Drawable Drawable => base.Drawable; public int SkinChangedCount { get; private set; } - public SkinConsumer(string name, Func defaultImplementation, Func allowFallback = null, bool restrictSize = true) - : base(name, defaultImplementation, allowFallback, restrictSize) + public SkinConsumer(string name, Func defaultImplementation, Func allowFallback = null) + : base(name, defaultImplementation, allowFallback) { } diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 2150726a42..61c2644c6f 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Judgements Font = OsuFont.Numeric.With(size: 12), Colour = judgementColour(Result.Type), Scale = new Vector2(0.85f, 1), - }, restrictSize: false) + }) }; } diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index 995cb15136..a30bdbd2dc 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -9,8 +9,8 @@ namespace osu.Game.Skinning { public class SkinnableDrawable : SkinnableDrawable { - public SkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, bool restrictSize = true) - : base(name, defaultImplementation, allowFallback, restrictSize) + public SkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + : base(name, defaultImplementation, allowFallback, confineMode) { } } @@ -29,7 +29,7 @@ namespace osu.Game.Skinning private readonly string componentName; - private readonly bool restrictSize; + private readonly ConfineMode confineMode; /// /// Create a new skinnable drawable. @@ -37,18 +37,18 @@ namespace osu.Game.Skinning /// The namespace-complete resource name for this skinnable element. /// A function to create the default skin implementation of this element. /// A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present. - /// Whether a user-skin drawable should be limited to the size of our parent. - public SkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, bool restrictSize = true) - : this(name, allowFallback, restrictSize) + /// How (if at all) the should be resize to fit within our own bounds. + public SkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + : this(name, allowFallback, confineMode) { createDefault = defaultImplementation; } - protected SkinnableDrawable(string name, Func allowFallback = null, bool restrictSize = true) + protected SkinnableDrawable(string name, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) : base(allowFallback) { componentName = name; - this.restrictSize = restrictSize; + this.confineMode = confineMode; RelativeSizeAxes = Axes.Both; } @@ -58,7 +58,7 @@ namespace osu.Game.Skinning protected virtual T CreateDefault(string name) => createDefault(name); /// - /// Whether to apply size restrictions (specified via ) to the default implementation. + /// Whether to apply size restrictions (specified via ) to the default implementation. /// protected virtual bool ApplySizeRestrictionsToDefault => false; @@ -76,12 +76,18 @@ namespace osu.Game.Skinning if (Drawable != null) { - if (restrictSize && (!isDefault || ApplySizeRestrictionsToDefault)) + if (confineMode != ConfineMode.NoScaling && (!isDefault || ApplySizeRestrictionsToDefault)) { - Drawable.RelativeSizeAxes = Axes.Both; - Drawable.Size = Vector2.One; - Drawable.Scale = Vector2.One; - Drawable.FillMode = FillMode.Fit; + bool applyScaling = confineMode == ConfineMode.ScaleToFit || + (confineMode == ConfineMode.ScaleDownToFit && (Drawable.DrawSize.X > DrawSize.X || Drawable.DrawSize.Y > DrawSize.Y)); + + if (applyScaling) + { + Drawable.RelativeSizeAxes = Axes.Both; + Drawable.Size = Vector2.One; + Drawable.Scale = Vector2.One; + Drawable.FillMode = FillMode.Fit; + } } Drawable.Origin = Anchor.Centre; @@ -93,4 +99,14 @@ namespace osu.Game.Skinning ClearInternal(); } } + + public enum ConfineMode + { + /// + /// Don't apply any scaling. This allows the user element to be of any size, exceeding specified bounds. + /// + NoScaling, + ScaleDownToFit, + ScaleToFit, + } } diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index ceb1ed0f70..1716ec71de 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -18,8 +18,8 @@ namespace osu.Game.Skinning [Resolved] private TextureStore textures { get; set; } - public SkinnableSprite(string name, Func allowFallback = null, bool restrictSize = true) - : base(name, allowFallback, restrictSize) + public SkinnableSprite(string name, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + : base(name, allowFallback, confineMode) { } diff --git a/osu.Game/Skinning/SkinnableSpriteText.cs b/osu.Game/Skinning/SkinnableSpriteText.cs index 36e646d743..d12a6f74f7 100644 --- a/osu.Game/Skinning/SkinnableSpriteText.cs +++ b/osu.Game/Skinning/SkinnableSpriteText.cs @@ -8,8 +8,8 @@ namespace osu.Game.Skinning { public class SkinnableSpriteText : SkinnableDrawable, IHasText { - public SkinnableSpriteText(string name, Func defaultImplementation, Func allowFallback = null, bool restrictSize = true) - : base(name, defaultImplementation, allowFallback, restrictSize) + public SkinnableSpriteText(string name, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + : base(name, defaultImplementation, allowFallback, confineMode) { } From 9d091c96b81d2bf461890137cc32d87daa7b4972 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jul 2019 17:52:50 +0900 Subject: [PATCH 1799/2854] Use cache to ensure correct DrawSize when deciding scaling --- osu.Game/Skinning/SkinnableDrawable.cs | 32 +++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index a30bdbd2dc..5d1ca98bfd 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Caching; using osu.Framework.Graphics; using osuTK; @@ -55,6 +56,10 @@ namespace osu.Game.Skinning private readonly Func createDefault; + private readonly Cached scaling = new Cached(); + + private bool isDefault; + protected virtual T CreateDefault(string name) => createDefault(name); /// @@ -66,7 +71,7 @@ namespace osu.Game.Skinning { Drawable = skin.GetDrawableComponent(componentName); - bool isDefault = false; + isDefault = false; if (Drawable == null && allowFallback) { @@ -76,7 +81,23 @@ namespace osu.Game.Skinning if (Drawable != null) { - if (confineMode != ConfineMode.NoScaling && (!isDefault || ApplySizeRestrictionsToDefault)) + scaling.Invalidate(); + Drawable.Origin = Anchor.Centre; + Drawable.Anchor = Anchor.Centre; + + InternalChild = Drawable; + } + else + ClearInternal(); + } + + protected override void Update() + { + base.Update(); + + if (!scaling.IsValid) + { + if (Drawable != null && confineMode != ConfineMode.NoScaling && (!isDefault || ApplySizeRestrictionsToDefault)) { bool applyScaling = confineMode == ConfineMode.ScaleToFit || (confineMode == ConfineMode.ScaleDownToFit && (Drawable.DrawSize.X > DrawSize.X || Drawable.DrawSize.Y > DrawSize.Y)); @@ -90,13 +111,8 @@ namespace osu.Game.Skinning } } - Drawable.Origin = Anchor.Centre; - Drawable.Anchor = Anchor.Centre; - - InternalChild = Drawable; + scaling.Validate(); } - else - ClearInternal(); } } From eca63980d2ed7cda53a848af781eb495960a11c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jul 2019 17:53:01 +0900 Subject: [PATCH 1800/2854] Add comprehensive scaling mode tests --- .../Gameplay/TestSceneSkinReloadable.cs | 145 --------- .../Gameplay/TestSceneSkinnableDrawable.cs | 283 ++++++++++++++++++ 2 files changed, 283 insertions(+), 145 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs deleted file mode 100644 index af03ddc65c..0000000000 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using NUnit.Framework; -using osu.Framework.Audio.Sample; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Textures; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Skinning; -using osuTK.Graphics; - -namespace osu.Game.Tests.Visual.Gameplay -{ - public class TestSceneSkinReloadable : OsuTestScene - { - [Test] - public void TestInitialLoad() - { - var secondarySource = new SecondarySource(); - SkinConsumer consumer = null; - - AddStep("setup layout", () => - { - Child = new SkinSourceContainer - { - RelativeSizeAxes = Axes.Both, - Child = new LocalSkinOverrideContainer(secondarySource) - { - RelativeSizeAxes = Axes.Both, - Child = consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true) - } - }; - }); - - AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox); - AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1); - } - - [Test] - public void TestOverride() - { - var secondarySource = new SecondarySource(); - - SkinConsumer consumer = null; - Container target = null; - - AddStep("setup layout", () => - { - Child = new SkinSourceContainer - { - RelativeSizeAxes = Axes.Both, - Child = target = new LocalSkinOverrideContainer(secondarySource) - { - RelativeSizeAxes = Axes.Both, - } - }; - }); - - AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true))); - AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox); - AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1); - } - - private class NamedBox : Container - { - public NamedBox(string name) - { - Children = new Drawable[] - { - new Box - { - Colour = Color4.Black, - RelativeSizeAxes = Axes.Both, - }, - new OsuSpriteText - { - Font = OsuFont.Default.With(size: 40), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = name - } - }; - } - } - - private class SkinConsumer : SkinnableDrawable - { - public new Drawable Drawable => base.Drawable; - public int SkinChangedCount { get; private set; } - - public SkinConsumer(string name, Func defaultImplementation, Func allowFallback = null) - : base(name, defaultImplementation, allowFallback) - { - } - - protected override void SkinChanged(ISkinSource skin, bool allowFallback) - { - base.SkinChanged(skin, allowFallback); - SkinChangedCount++; - } - } - - private class BaseSourceBox : NamedBox - { - public BaseSourceBox() - : base("Base Source") - { - } - } - - private class SecondarySourceBox : NamedBox - { - public SecondarySourceBox() - : base("Secondary Source") - { - } - } - - private class SecondarySource : ISkin - { - public Drawable GetDrawableComponent(string componentName) => new SecondarySourceBox(); - - public Texture GetTexture(string componentName) => throw new NotImplementedException(); - - public SampleChannel GetSample(string sampleName) => throw new NotImplementedException(); - - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); - } - - private class SkinSourceContainer : Container, ISkin - { - public Drawable GetDrawableComponent(string componentName) => new BaseSourceBox(); - - public Texture GetTexture(string componentName) => throw new NotImplementedException(); - - public SampleChannel GetSample(string sampleName) => throw new NotImplementedException(); - - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); - } - } -} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs new file mode 100644 index 0000000000..0b5978e3eb --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs @@ -0,0 +1,283 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Globalization; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Textures; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Skinning; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneSkinnableDrawable : OsuTestScene + { + [Test] + public void TestConfineScaleDown() + { + FillFlowContainer fill = null; + + AddStep("setup layout larger source", () => + { + Child = new LocalSkinOverrideContainer(new SizedSource(50)) + { + RelativeSizeAxes = Axes.Both, + Child = fill = new FillFlowContainer + { + Size = new Vector2(30), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Spacing = new Vector2(10), + Children = new[] + { + new ExposedSkinnableDrawable("default", _ => new DefaultBox(), _ => true), + new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true), + new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.ScaleToFit), + new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.NoScaling) + } + }, + }; + }); + + AddAssert("check sizes", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 30, 30, 30, 50 })); + AddStep("adjust scale", () => fill.Scale = new Vector2(2)); + AddAssert("check sizes unchanged by scale", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 30, 30, 30, 50 })); + } + + [Test] + public void TestConfineScaleUp() + { + FillFlowContainer fill = null; + + AddStep("setup layout larger source", () => + { + Child = new LocalSkinOverrideContainer(new SizedSource(30)) + { + RelativeSizeAxes = Axes.Both, + Child = fill = new FillFlowContainer + { + Size = new Vector2(50), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Spacing = new Vector2(10), + Children = new[] + { + new ExposedSkinnableDrawable("default", _ => new DefaultBox(), _ => true), + new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true), + new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.ScaleToFit), + new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.NoScaling) + } + }, + }; + }); + + AddAssert("check sizes", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 50, 30, 50, 30 })); + AddStep("adjust scale", () => fill.Scale = new Vector2(2)); + AddAssert("check sizes unchanged by scale", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 50, 30, 50, 30 })); + } + + [Test] + public void TestInitialLoad() + { + var secondarySource = new SecondarySource(); + SkinConsumer consumer = null; + + AddStep("setup layout", () => + { + Child = new SkinSourceContainer + { + RelativeSizeAxes = Axes.Both, + Child = new LocalSkinOverrideContainer(secondarySource) + { + RelativeSizeAxes = Axes.Both, + Child = consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true) + } + }; + }); + + AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox); + AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1); + } + + [Test] + public void TestOverride() + { + var secondarySource = new SecondarySource(); + + SkinConsumer consumer = null; + Container target = null; + + AddStep("setup layout", () => + { + Child = new SkinSourceContainer + { + RelativeSizeAxes = Axes.Both, + Child = target = new LocalSkinOverrideContainer(secondarySource) + { + RelativeSizeAxes = Axes.Both, + } + }; + }); + + AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true))); + AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox); + AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1); + } + + private class ExposedSkinnableDrawable : SkinnableDrawable + { + public new Drawable Drawable => base.Drawable; + + public ExposedSkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + : base(name, defaultImplementation, allowFallback, confineMode) + { + } + } + + private class DefaultBox : DrawWidthBox + { + public DefaultBox() + { + RelativeSizeAxes = Axes.Both; + } + } + + private class DrawWidthBox : Container + { + private readonly OsuSpriteText text; + + public DrawWidthBox() + { + Children = new Drawable[] + { + new Box + { + Colour = Color4.Gray, + RelativeSizeAxes = Axes.Both, + }, + text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }; + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + text.Text = DrawWidth.ToString(CultureInfo.InvariantCulture); + } + } + + private class NamedBox : Container + { + public NamedBox(string name) + { + Children = new Drawable[] + { + new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, + }, + new OsuSpriteText + { + Font = OsuFont.Default.With(size: 40), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = name + } + }; + } + } + + private class SkinConsumer : SkinnableDrawable + { + public new Drawable Drawable => base.Drawable; + public int SkinChangedCount { get; private set; } + + public SkinConsumer(string name, Func defaultImplementation, Func allowFallback = null) + : base(name, defaultImplementation, allowFallback) + { + } + + protected override void SkinChanged(ISkinSource skin, bool allowFallback) + { + base.SkinChanged(skin, allowFallback); + SkinChangedCount++; + } + } + + private class BaseSourceBox : NamedBox + { + public BaseSourceBox() + : base("Base Source") + { + } + } + + private class SecondarySourceBox : NamedBox + { + public SecondarySourceBox() + : base("Secondary Source") + { + } + } + + private class SizedSource : ISkin + { + private readonly float size; + + public SizedSource(float size) + { + this.size = size; + } + + public Drawable GetDrawableComponent(string componentName) => + componentName == "available" + ? new DrawWidthBox + { + Colour = Color4.Yellow, + Size = new Vector2(size) + } + : null; + + public Texture GetTexture(string componentName) => throw new NotImplementedException(); + + public SampleChannel GetSample(string sampleName) => throw new NotImplementedException(); + + public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); + } + + private class SecondarySource : ISkin + { + public Drawable GetDrawableComponent(string componentName) => new SecondarySourceBox(); + + public Texture GetTexture(string componentName) => throw new NotImplementedException(); + + public SampleChannel GetSample(string sampleName) => throw new NotImplementedException(); + + public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); + } + + private class SkinSourceContainer : Container, ISkin + { + public Drawable GetDrawableComponent(string componentName) => new BaseSourceBox(); + + public Texture GetTexture(string componentName) => throw new NotImplementedException(); + + public SampleChannel GetSample(string sampleName) => throw new NotImplementedException(); + + public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); + } + } +} From 36c557c752be5bdecb37b9e761c6ad4551e47c12 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jul 2019 18:39:33 +0900 Subject: [PATCH 1801/2854] Add null check and simplify scaling conditional logic via switch --- osu.Game/Skinning/SkinnableDrawable.cs | 33 ++++++++++++++++---------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index 5d1ca98bfd..eb0508b568 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -84,7 +84,6 @@ namespace osu.Game.Skinning scaling.Invalidate(); Drawable.Origin = Anchor.Centre; Drawable.Anchor = Anchor.Centre; - InternalChild = Drawable; } else @@ -97,21 +96,31 @@ namespace osu.Game.Skinning if (!scaling.IsValid) { - if (Drawable != null && confineMode != ConfineMode.NoScaling && (!isDefault || ApplySizeRestrictionsToDefault)) + try { - bool applyScaling = confineMode == ConfineMode.ScaleToFit || - (confineMode == ConfineMode.ScaleDownToFit && (Drawable.DrawSize.X > DrawSize.X || Drawable.DrawSize.Y > DrawSize.Y)); + if (Drawable == null || (isDefault && !ApplySizeRestrictionsToDefault)) return; - if (applyScaling) + switch (confineMode) { - Drawable.RelativeSizeAxes = Axes.Both; - Drawable.Size = Vector2.One; - Drawable.Scale = Vector2.One; - Drawable.FillMode = FillMode.Fit; - } - } + case ConfineMode.NoScaling: + return; - scaling.Validate(); + case ConfineMode.ScaleDownToFit: + if (Drawable.DrawSize.X <= DrawSize.X && Drawable.DrawSize.Y <= DrawSize.Y) + return; + + break; + } + + Drawable.RelativeSizeAxes = Axes.Both; + Drawable.Size = Vector2.One; + Drawable.Scale = Vector2.One; + Drawable.FillMode = FillMode.Fit; + } + finally + { + scaling.Validate(); + } } } } From 8327452fe18b89b80fe374b6e873f97bacedc706 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jul 2019 14:45:25 +0900 Subject: [PATCH 1802/2854] Make AccentColour a bindable --- .../Drawable/DrawableCatchHitObject.cs | 2 +- .../Objects/Drawable/DrawableDroplet.cs | 11 +---- .../Objects/Drawable/DrawableFruit.cs | 46 +++++++++---------- .../TestSceneHoldNoteSelectionBlueprint.cs | 2 +- .../TestSceneNotes.cs | 4 +- .../Objects/Drawables/DrawableHoldNote.cs | 26 ++++------- .../Objects/Drawables/DrawableHoldNoteTick.cs | 17 ++----- .../Objects/Drawables/DrawableNote.cs | 30 +++++------- osu.Game.Rulesets.Mania/UI/Column.cs | 2 +- osu.Game.Rulesets.Mania/UI/HitExplosion.cs | 2 +- .../Objects/Drawables/DrawableHitCircle.cs | 18 +++----- .../Objects/Drawables/DrawableOsuHitObject.cs | 4 +- .../Objects/Drawables/DrawableSlider.cs | 19 +++----- .../Objects/Drawables/DrawableSliderTick.cs | 3 +- .../Objects/Drawables/DrawableHitObject.cs | 5 +- 15 files changed, 75 insertions(+), 116 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs index 5785d9a9ca..2ccb01a3e3 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable base.SkinChanged(skin, allowFallback); if (HitObject is IHasComboInformation combo) - AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; + AccentColour.Value = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; } protected override void UpdateState(ArmedState state) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs index 9cabdc3dd9..059310d671 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawable { @@ -27,16 +26,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable private void load() { AddInternal(pulp = new Pulp { Size = Size }); - } - public override Color4 AccentColour - { - get => base.AccentColour; - set - { - base.AccentColour = value; - pulp.AccentColour = AccentColour; - } + AccentColour.BindValueChanged(colour => { pulp.AccentColour = colour.NewValue; }, true); } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs index 77407def54..ce2daebbf1 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable private void load() { // todo: this should come from the skin. - AccentColour = colourForRepresentation(HitObject.VisualRepresentation); + AccentColour.Value = colourForRepresentation(HitObject.VisualRepresentation); AddRangeInternal(new[] { @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable Hollow = !HitObject.HyperDash, Type = EdgeEffectType.Glow, Radius = 4 * radius_adjust, - Colour = HitObject.HyperDash ? Color4.Red : AccentColour.Darken(1).Opacity(0.6f) + Colour = HitObject.HyperDash ? Color4.Red : AccentColour.Value.Darken(1).Opacity(0.6f) }, Size = new Vector2(Height), Anchor = Anchor.Centre, @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable new Box { AlwaysPresent = true, - Colour = AccentColour, + Colour = AccentColour.Value, Alpha = 0, RelativeSizeAxes = Axes.Both } @@ -115,32 +115,32 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable { new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(small_pulp), Y = -0.34f, }, new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(large_pulp_4), Position = positionAt(0, distance_from_centre_4), }, new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(large_pulp_4), Position = positionAt(90, distance_from_centre_4), }, new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(large_pulp_4), Position = positionAt(180, distance_from_centre_4), }, new Pulp { Size = new Vector2(large_pulp_4), - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Position = positionAt(270, distance_from_centre_4), }, } @@ -154,32 +154,32 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable { new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(small_pulp), Y = -0.3f, }, new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(large_pulp_4), Position = positionAt(45, distance_from_centre_4), }, new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(large_pulp_4), Position = positionAt(135, distance_from_centre_4), }, new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(large_pulp_4), Position = positionAt(225, distance_from_centre_4), }, new Pulp { Size = new Vector2(large_pulp_4), - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Position = positionAt(315, distance_from_centre_4), }, } @@ -193,26 +193,26 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable { new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(small_pulp), Y = -0.33f, }, new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(large_pulp_3), Position = positionAt(60, distance_from_centre_3), }, new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(large_pulp_3), Position = positionAt(180, distance_from_centre_3), }, new Pulp { Size = new Vector2(large_pulp_3), - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Position = positionAt(300, distance_from_centre_3), }, } @@ -226,26 +226,26 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable { new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(small_pulp), Y = -0.25f, }, new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(large_pulp_3), Position = positionAt(0, distance_from_centre_3), }, new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(large_pulp_3), Position = positionAt(120, distance_from_centre_3), }, new Pulp { Size = new Vector2(large_pulp_3), - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Position = positionAt(240, distance_from_centre_3), }, } @@ -259,13 +259,13 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable { new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(small_pulp), Y = -0.3f }, new Pulp { - AccentColour = AccentColour, + AccentColour = AccentColour.Value, Size = new Vector2(large_pulp_4 * 0.8f, large_pulp_4 * 2.5f), Y = 0.05f, }, diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteSelectionBlueprint.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteSelectionBlueprint.cs index 04c5724f93..622d840a0c 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteSelectionBlueprint.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Mania.Tests Child = drawableObject = new DrawableHoldNote(holdNote) { Height = 300, - AccentColour = OsuColour.Gray(0.3f) + AccentColour = { Value = OsuColour.Gray(0.3f) } } }; } diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs index b2613a59d5..031abb08e2 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Tests AutoSizeAxes = Axes.Both, Child = new NoteContainer(direction, $"note {identifier}, scrolling {direction.ToString().ToLowerInvariant()}") { - Child = hitObject = new DrawableNote(note) { AccentColour = Color4.OrangeRed } + Child = hitObject = new DrawableNote(note) { AccentColour = { Value = Color4.OrangeRed } } } }; } @@ -88,7 +88,7 @@ namespace osu.Game.Rulesets.Mania.Tests Child = hitObject = new DrawableHoldNote(note) { RelativeSizeAxes = Axes.Both, - AccentColour = Color4.OrangeRed, + AccentColour = { Value = Color4.OrangeRed }, } } }; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 9368af987d..952c6e128e 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -6,7 +6,6 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; -using osuTK.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Scoring; @@ -36,11 +35,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables /// private bool hasBroken; - private readonly Container tickContainer; - public DrawableHoldNote(HoldNote hitObject) : base(hitObject) { + Container tickContainer; RelativeSizeAxes = Axes.X; AddRangeInternal(new Drawable[] @@ -74,6 +72,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables AddNested(Head); AddNested(Tail); + + AccentColour.BindValueChanged(colour => + { + bodyPiece.AccentColour = colour.NewValue; + Head.AccentColour.Value = colour.NewValue; + Tail.AccentColour.Value = colour.NewValue; + tickContainer.ForEach(t => t.AccentColour.Value = colour.NewValue); + }, true); } protected override void OnDirectionChanged(ValueChangedEvent e) @@ -83,20 +89,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft; } - public override Color4 AccentColour - { - get => base.AccentColour; - set - { - base.AccentColour = value; - - bodyPiece.AccentColour = value; - Head.AccentColour = value; - Tail.AccentColour = value; - tickContainer.ForEach(t => t.AccentColour = value); - } - } - protected override void CheckForResult(bool userTriggered, double timeOffset) { if (Tail.AllJudged) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs index 9a29273282..9b0322a6cd 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs @@ -3,7 +3,6 @@ using System; using osuTK; -using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -23,11 +22,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables /// public Func HoldStartTime; - private readonly Container glowContainer; - public DrawableHoldNoteTick(HoldNoteTick hitObject) : base(hitObject) { + Container glowContainer; + Anchor = Anchor.TopCentre; Origin = Anchor.TopCentre; @@ -53,23 +52,17 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables } } }); - } - public override Color4 AccentColour - { - get => base.AccentColour; - set + AccentColour.BindValueChanged(colour => { - base.AccentColour = value; - glowContainer.EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, Radius = 2f, Roundness = 15f, - Colour = value.Opacity(0.3f) + Colour = colour.NewValue.Opacity(0.3f) }; - } + }, true); } protected override void CheckForResult(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index afd7777861..dccff7f6ac 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -3,7 +3,6 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; -using osuTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Effects; using osu.Framework.Input.Bindings; @@ -30,6 +29,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Masking = true; AddInternal(headPiece = new NotePiece()); + + AccentColour.BindValueChanged(colour => + { + headPiece.AccentColour = colour.NewValue; + + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = colour.NewValue.Lighten(1f).Opacity(0.6f), + Radius = 10, + }; + }, true); } protected override void OnDirectionChanged(ValueChangedEvent e) @@ -39,23 +50,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables headPiece.Anchor = headPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre; } - public override Color4 AccentColour - { - get => base.AccentColour; - set - { - base.AccentColour = value; - headPiece.AccentColour = AccentColour; - - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = AccentColour.Lighten(1f).Opacity(0.6f), - Radius = 10, - }; - } - } - protected override void CheckForResult(bool userTriggered, double timeOffset) { if (!userTriggered) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index c59bed4ea7..91dd236ab1 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Mania.UI /// The DrawableHitObject to add. public override void Add(DrawableHitObject hitObject) { - hitObject.AccentColour = AccentColour; + hitObject.AccentColour.Value = AccentColour; hitObject.OnNewResult += OnNewResult; HitObjectContainer.Add(hitObject); diff --git a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs index 0ec1fc38d2..48470add8b 100644 --- a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Mania.UI EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, - Colour = Interpolation.ValueAt(0.1f, judgedObject.AccentColour, Color4.White, 0, 1), + Colour = Interpolation.ValueAt(0.1f, judgedObject.AccentColour.Value, Color4.White, 0, 1), Radius = 100, }, Child = new Box diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index fef0bfdc2c..a83f1b5e56 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -10,7 +10,6 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osuTK; using osu.Game.Rulesets.Scoring; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -98,19 +97,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables positionBindable.BindTo(HitObject.PositionBindable); stackHeightBindable.BindTo(HitObject.StackHeightBindable); scaleBindable.BindTo(HitObject.ScaleBindable); - } - public override Color4 AccentColour - { - get => base.AccentColour; - set + AccentColour.BindValueChanged(colour => { - base.AccentColour = value; - explode.Colour = AccentColour; - glow.Colour = AccentColour; - circle.Colour = AccentColour; - ApproachCircle.Colour = AccentColour; - } + explode.Colour = colour.NewValue; + glow.Colour = colour.NewValue; + circle.Colour = colour.NewValue; + ApproachCircle.Colour = colour.NewValue; + }, true); } protected override void CheckForResult(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 4533e08a2b..30cf09c9d7 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables base.SkinChanged(skin, allowFallback); if (HitObject is IHasComboInformation combo) - AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; + AccentColour.Value = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; } protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 4d67c9ae34..e06287f527 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -114,20 +114,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables pathBindable.BindTo(slider.PathBindable); pathBindable.BindValueChanged(_ => Body.Refresh()); - } - public override Color4 AccentColour - { - get => base.AccentColour; - set + AccentColour.BindValueChanged(colour => { - base.AccentColour = value; - Body.AccentColour = AccentColour; - Ball.AccentColour = AccentColour; + Body.AccentColour = colour.NewValue; + Ball.AccentColour = colour.NewValue; foreach (var drawableHitObject in NestedHitObjects) - drawableHitObject.AccentColour = AccentColour; - } + drawableHitObject.AccentColour.Value = colour.NewValue; + }, true); } public readonly Bindable Tracking = new Bindable(); @@ -167,9 +162,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables base.SkinChanged(skin, allowFallback); Body.BorderSize = skin.GetValue(s => s.SliderBorderSize) ?? SliderBody.DEFAULT_BORDER_SIZE; - Body.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? AccentColour; + Body.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? AccentColour.Value; Body.BorderColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Color4.White; - Ball.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? AccentColour; + Ball.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? AccentColour.Value; } protected override void CheckForResult(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 72b648bfd0..ec294a1630 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -34,6 +34,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, CornerRadius = Size.X / 2, + Colour = AccentColour.Value, BorderThickness = 2, BorderColour = Color4.White, @@ -41,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Child = new Box { RelativeSizeAxes = Axes.Both, - Colour = AccentColour, + Colour = AccentColour.Value, Alpha = 0.3f, } }, restrictSize: false) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index e61fac679e..1fd6176f4f 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -9,7 +9,6 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics.Primitives; using osu.Game.Audio; -using osu.Game.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; @@ -18,14 +17,14 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Objects.Drawables { - public abstract class DrawableHitObject : SkinReloadableDrawable, IHasAccentColour + public abstract class DrawableHitObject : SkinReloadableDrawable { public readonly HitObject HitObject; /// /// The colour used for various elements of this DrawableHitObject. /// - public virtual Color4 AccentColour { get; set; } = Color4.Gray; + public readonly Bindable AccentColour = new Bindable(Color4.Gray); // Todo: Rulesets should be overriding the resources instead, but we need to figure out where/when to apply overrides first protected virtual string SampleNamespace => null; From a631aac66435e3b6f0e2a3be851096a5c7ec74b4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Jul 2019 15:01:01 +0900 Subject: [PATCH 1803/2854] Fix workingbeatmap's disposal potentially null-refing --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 37aa0024da..949a2aab6f 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -247,7 +247,7 @@ namespace osu.Game.Beatmaps // cancelling the beatmap load is safe for now since the retrieval is a synchronous // operation. if we add an async retrieval method this may need to be reconsidered. - beatmapCancellation.Cancel(); + beatmapCancellation?.Cancel(); total_count.Value--; } From 91f86adb66250b20062ebbe3fbe1d7aede2b1b2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jul 2019 15:05:56 +0900 Subject: [PATCH 1804/2854] Move DrawableHitObject state management to base class --- .../Drawable/DrawableCatchHitObject.cs | 10 --- .../Objects/Drawables/DrawableOsuHitObject.cs | 49 +---------- .../Objects/Drawables/DrawableHitObject.cs | 82 ++++++++++++++++--- 3 files changed, 75 insertions(+), 66 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs index 2ccb01a3e3..f5dd7cd255 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -3,12 +3,10 @@ using System; using osuTK; -using osuTK.Graphics; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawable { @@ -60,14 +58,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss); } - protected override void SkinChanged(ISkinSource skin, bool allowFallback) - { - base.SkinChanged(skin, allowFallback); - - if (HitObject is IHasComboInformation combo) - AccentColour.Value = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; - } - protected override void UpdateState(ArmedState state) { using (BeginAbsoluteSequence(HitObject.StartTime - HitObject.TimePreempt)) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 30cf09c9d7..145be253bc 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -1,15 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; -using osuTK.Graphics; using osu.Game.Graphics.Containers; namespace osu.Game.Rulesets.Osu.Objects.Drawables @@ -39,49 +34,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void ClearInternal(bool disposeChildren = true) => shakeContainer.Clear(disposeChildren); protected override bool RemoveInternal(Drawable drawable) => shakeContainer.Remove(drawable); + protected override bool UseTransformStateManagement => true; + protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt; - protected sealed override void UpdateState(ArmedState state) - { - double transformTime = HitObject.StartTime - HitObject.TimePreempt; - - base.ApplyTransformsAt(transformTime, true); - base.ClearTransformsAfter(transformTime, true); - - using (BeginAbsoluteSequence(transformTime, true)) - { - UpdatePreemptState(); - - var judgementOffset = Math.Min(HitObject.HitWindows.HalfWindowFor(HitResult.Miss), Result?.TimeOffset ?? 0); - - using (BeginDelayedSequence(HitObject.TimePreempt + judgementOffset, true)) - UpdateCurrentState(state); - } - } - - protected override void SkinChanged(ISkinSource skin, bool allowFallback) - { - base.SkinChanged(skin, allowFallback); - - if (HitObject is IHasComboInformation combo) - AccentColour.Value = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; - } - - protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn); - - protected virtual void UpdateCurrentState(ArmedState state) - { - } - - // Todo: At some point we need to move these to DrawableHitObject after ensuring that all other Rulesets apply - // transforms in the same way and don't rely on them not being cleared - public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) - { - } - - public override void ApplyTransformsAt(double time, bool propagateChildren = false) - { - } + protected override void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn); private OsuInputManager osuActionInputManager; internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 1fd6176f4f..06ea82746f 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -79,10 +79,12 @@ namespace osu.Game.Rulesets.Objects.Drawables public override bool RemoveCompletedTransforms => false; protected override bool RequiresChildrenUpdate => true; - public override bool IsPresent => base.IsPresent || (State.Value == ArmedState.Idle && Clock?.CurrentTime >= LifetimeStart); + public override bool IsPresent => base.IsPresent || (state.Value == ArmedState.Idle && Clock?.CurrentTime >= LifetimeStart); public readonly Bindable State = new Bindable(); + private readonly Bindable state = new Bindable(); + protected DrawableHitObject(HitObject hitObject) { HitObject = hitObject; @@ -122,21 +124,81 @@ namespace osu.Game.Rulesets.Objects.Drawables { base.LoadComplete(); - State.ValueChanged += armed => + state.BindValueChanged(armed => { - UpdateState(armed.NewValue); + updateState(armed.NewValue); // apply any custom state overrides ApplyCustomUpdateState?.Invoke(this, armed.NewValue); if (armed.NewValue == ArmedState.Hit) PlaySamples(); - }; - - State.TriggerChange(); + }, true); } - protected abstract void UpdateState(ArmedState state); + protected virtual bool UseTransformStateManagement => false; + + private void updateState(ArmedState state) + { + if (UseTransformStateManagement) + { + double transformTime = HitObject.StartTime - InitialLifetimeOffset; + + base.ApplyTransformsAt(transformTime, true); + base.ClearTransformsAfter(transformTime, true); + + using (BeginAbsoluteSequence(transformTime, true)) + { + UpdatePreemptState(); + + var judgementOffset = Math.Min(HitObject.HitWindows?.HalfWindowFor(HitResult.Miss) ?? double.MaxValue, Result?.TimeOffset ?? 0); + + using (BeginDelayedSequence(InitialLifetimeOffset + judgementOffset, true)) + { + UpdateCurrentState(state); + State.Value = state; + } + } + } + else + { + State.Value = state; + } + + UpdateState(state); + } + + protected override void SkinChanged(ISkinSource skin, bool allowFallback) + { + base.SkinChanged(skin, allowFallback); + + if (HitObject is IHasComboInformation combo) + AccentColour.Value = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; + } + + protected virtual void UpdatePreemptState() + { + } + + protected virtual void UpdateCurrentState(ArmedState state) + { + } + + public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) + { + if (!UseTransformStateManagement) + base.ClearTransformsAfter(time, propagateChildren, targetMember); + } + + public override void ApplyTransformsAt(double time, bool propagateChildren = false) + { + if (!UseTransformStateManagement) + base.ApplyTransformsAt(time, propagateChildren); + } + + protected virtual void UpdateState(ArmedState state) + { + } /// /// Bind to apply a custom state which can override the default implementation. @@ -163,7 +225,7 @@ namespace osu.Game.Rulesets.Objects.Drawables Result.TimeOffset = 0; Result.Type = HitResult.None; - State.Value = ArmedState.Idle; + state.Value = ArmedState.Idle; } } } @@ -243,11 +305,11 @@ namespace osu.Game.Rulesets.Objects.Drawables break; case HitResult.Miss: - State.Value = ArmedState.Miss; + state.Value = ArmedState.Miss; break; default: - State.Value = ArmedState.Hit; + state.Value = ArmedState.Hit; break; } From 6d889c8a37b3f83978fc5020c59857608ff5f5d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jul 2019 15:43:27 +0900 Subject: [PATCH 1805/2854] Revert unintended change --- osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs index af733f16e1..575f2c92c5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces RelativeSizeAxes = Axes.Both } } - }, confineMode: ConfineMode.NoScaling); + }); } } } From be170b412496834986c5d214dd4660b4d26115a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jul 2019 15:33:12 +0900 Subject: [PATCH 1806/2854] Naming and documentation improvements --- .../Objects/Drawables/DrawableHitCircle.cs | 6 +-- .../Objects/Drawables/DrawableOsuHitObject.cs | 2 +- .../Objects/Drawables/DrawableRepeatPoint.cs | 4 +- .../Objects/Drawables/DrawableSlider.cs | 2 +- .../Objects/Drawables/DrawableSliderTick.cs | 4 +- .../Objects/Drawables/DrawableSpinner.cs | 6 +-- .../Objects/Drawables/DrawableHitObject.cs | 39 ++++++++++++------- 7 files changed, 37 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index a83f1b5e56..d3d763daf3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -128,16 +128,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables ApplyResult(r => r.Type = result); } - protected override void UpdatePreemptState() + protected override void UpdateInitialTransforms() { - base.UpdatePreemptState(); + base.UpdateInitialTransforms(); ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt)); ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt); ApproachCircle.Expire(true); } - protected override void UpdateCurrentState(ArmedState state) + protected override void UpdateStateTransforms(ArmedState state) { glow.FadeOut(400); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 145be253bc..8107d366b3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt; - protected override void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn); + protected override void UpdateInitialTransforms() => this.FadeIn(HitObject.TimeFadeIn); private OsuInputManager osuActionInputManager; internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index cce6dfe106..1e2c0ae59f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables ApplyResult(r => r.Type = drawableSlider.Tracking.Value ? HitResult.Great : HitResult.Miss); } - protected override void UpdatePreemptState() + protected override void UpdateInitialTransforms() { animDuration = Math.Min(150, repeatPoint.SpanDuration / 2); @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables ); } - protected override void UpdateCurrentState(ArmedState state) + protected override void UpdateStateTransforms(ArmedState state) { switch (state) { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index e06287f527..d2089c05f3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }); } - protected override void UpdateCurrentState(ArmedState state) + protected override void UpdateStateTransforms(ArmedState state) { Ball.FadeIn(); Ball.ScaleTo(HitObject.Scale); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index ec294a1630..3e128e9f15 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -55,13 +55,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss); } - protected override void UpdatePreemptState() + protected override void UpdateInitialTransforms() { this.FadeOut().FadeIn(ANIM_DURATION); this.ScaleTo(0.5f).ScaleTo(1f, ANIM_DURATION * 4, Easing.OutElasticHalf); } - protected override void UpdateCurrentState(ArmedState state) + protected override void UpdateStateTransforms(ArmedState state) { switch (state) { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 1794da54b7..a0bd301fdb 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -196,9 +196,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables symbol.RotateTo(Disc.Rotation / 2, 500, Easing.OutQuint); } - protected override void UpdatePreemptState() + protected override void UpdateInitialTransforms() { - base.UpdatePreemptState(); + base.UpdateInitialTransforms(); circleContainer.ScaleTo(Spinner.Scale * 0.3f); circleContainer.ScaleTo(Spinner.Scale, HitObject.TimePreempt / 1.4f, Easing.OutQuint); @@ -213,7 +213,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables .ScaleTo(1, 500, Easing.OutQuint); } - protected override void UpdateCurrentState(ArmedState state) + protected override void UpdateStateTransforms(ArmedState state) { var sequence = this.Delay(Spinner.Duration).FadeOut(160); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 06ea82746f..26d51e3809 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -118,8 +118,6 @@ namespace osu.Game.Rulesets.Objects.Drawables } } - protected override void ClearInternal(bool disposeChildren = true) => throw new InvalidOperationException($"Should never clear a {nameof(DrawableHitObject)}"); - protected override void LoadComplete() { base.LoadComplete(); @@ -136,8 +134,19 @@ namespace osu.Game.Rulesets.Objects.Drawables }, true); } + #region State / Transform Management + + /// + /// Enables automatic transform management of this hitobject. Implementation of transforms should be done in and only. Rewinding and removing previous states is done automatically. + /// + /// + /// Going forward, this is the preferred way of implementing s. Previous functionality + /// is offered as a compatibility layer until all rulesets have been migrated across. + /// protected virtual bool UseTransformStateManagement => false; + protected override void ClearInternal(bool disposeChildren = true) => throw new InvalidOperationException($"Should never clear a {nameof(DrawableHitObject)}"); + private void updateState(ArmedState state) { if (UseTransformStateManagement) @@ -149,13 +158,13 @@ namespace osu.Game.Rulesets.Objects.Drawables using (BeginAbsoluteSequence(transformTime, true)) { - UpdatePreemptState(); + UpdateInitialTransforms(); var judgementOffset = Math.Min(HitObject.HitWindows?.HalfWindowFor(HitResult.Miss) ?? double.MaxValue, Result?.TimeOffset ?? 0); using (BeginDelayedSequence(InitialLifetimeOffset + judgementOffset, true)) { - UpdateCurrentState(state); + UpdateStateTransforms(state); State.Value = state; } } @@ -168,19 +177,11 @@ namespace osu.Game.Rulesets.Objects.Drawables UpdateState(state); } - protected override void SkinChanged(ISkinSource skin, bool allowFallback) - { - base.SkinChanged(skin, allowFallback); - - if (HitObject is IHasComboInformation combo) - AccentColour.Value = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; - } - - protected virtual void UpdatePreemptState() + protected virtual void UpdateInitialTransforms() { } - protected virtual void UpdateCurrentState(ArmedState state) + protected virtual void UpdateStateTransforms(ArmedState state) { } @@ -200,6 +201,16 @@ namespace osu.Game.Rulesets.Objects.Drawables { } + #endregion + + protected override void SkinChanged(ISkinSource skin, bool allowFallback) + { + base.SkinChanged(skin, allowFallback); + + if (HitObject is IHasComboInformation combo) + AccentColour.Value = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; + } + /// /// Bind to apply a custom state which can override the default implementation. /// From c3b81bef4ab8e9160e73f0013067082d4239706f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jul 2019 15:55:38 +0900 Subject: [PATCH 1807/2854] Flip default to the preferred method going forward --- .../Objects/Drawable/DrawableCatchHitObject.cs | 3 +++ .../Objects/Drawables/DrawableManiaHitObject.cs | 3 +++ .../Objects/Drawables/DrawableOsuHitObject.cs | 2 -- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs | 1 + .../Objects/Drawables/DrawableTaikoHitObject.cs | 2 ++ osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 6 files changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs index f5dd7cd255..a1279e8443 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -58,8 +58,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss); } + protected override bool UseTransformStateManagement => false; + protected override void UpdateState(ArmedState state) { + // TODO: update to use new state management. using (BeginAbsoluteSequence(HitObject.StartTime - HitObject.TimePreempt)) this.FadeIn(200); diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 0873f753be..db6b53e76d 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -58,8 +58,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables HitObject = hitObject; } + protected override bool UseTransformStateManagement => false; + protected override void UpdateState(ArmedState state) { + // TODO: update to use new state management. switch (state) { case ArmedState.Miss: diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 8107d366b3..579f16e0d4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -34,8 +34,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void ClearInternal(bool disposeChildren = true) => shakeContainer.Clear(disposeChildren); protected override bool RemoveInternal(Drawable drawable) => shakeContainer.Remove(drawable); - protected override bool UseTransformStateManagement => true; - protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt; protected override void UpdateInitialTransforms() => this.FadeIn(HitObject.TimeFadeIn); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 4c8d5d5204..34ae7db984 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -94,6 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void UpdateState(ArmedState state) { + // TODO: update to use new state management. var circlePiece = MainPiece as CirclePiece; circlePiece?.FlashBox.FinishTransforms(); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index bd45b52d7b..b46738c69a 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -121,6 +121,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } + protected override bool UseTransformStateManagement => false; + // Normal and clap samples are handled by the drum protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 26d51e3809..aef163cda7 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// Going forward, this is the preferred way of implementing s. Previous functionality /// is offered as a compatibility layer until all rulesets have been migrated across. /// - protected virtual bool UseTransformStateManagement => false; + protected virtual bool UseTransformStateManagement => true; protected override void ClearInternal(bool disposeChildren = true) => throw new InvalidOperationException($"Should never clear a {nameof(DrawableHitObject)}"); From d4d286c9880a4ed3a08c9245e5f5491d87f5c9f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jul 2019 16:08:38 +0900 Subject: [PATCH 1808/2854] Add full documentation --- .../Objects/Drawables/DrawableHitObject.cs | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index aef163cda7..d10f829dae 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -136,6 +136,11 @@ namespace osu.Game.Rulesets.Objects.Drawables #region State / Transform Management + /// + /// Bind to apply a custom state which can override the default implementation. + /// + public event Action ApplyCustomUpdateState; + /// /// Enables automatic transform management of this hitobject. Implementation of transforms should be done in and only. Rewinding and removing previous states is done automatically. /// @@ -177,26 +182,45 @@ namespace osu.Game.Rulesets.Objects.Drawables UpdateState(state); } + /// + /// Apply (generally fade-in) transforms. + /// The local drawable hierarchy is recursively delayed to for convenience. + /// + /// + /// This is called once before every . This is to ensure a good state in the case + /// the was negative and potentially altered the pre-hit transforms. + /// protected virtual void UpdateInitialTransforms() { } + /// + /// Apply transforms based on the current . Previous states are automatically cleared. + /// + /// The new armed state. protected virtual void UpdateStateTransforms(ArmedState state) { } public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { + // When we are using automatic state menement, parent calls to this should be blocked for safety. if (!UseTransformStateManagement) base.ClearTransformsAfter(time, propagateChildren, targetMember); } public override void ApplyTransformsAt(double time, bool propagateChildren = false) { + // When we are using automatic state menement, parent calls to this should be blocked for safety. if (!UseTransformStateManagement) base.ApplyTransformsAt(time, propagateChildren); } + /// + /// Legacy method to handle state changes. + /// Should generally not be used when is true; use instead. + /// + /// The new armed state. protected virtual void UpdateState(ArmedState state) { } @@ -211,11 +235,6 @@ namespace osu.Game.Rulesets.Objects.Drawables AccentColour.Value = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; } - /// - /// Bind to apply a custom state which can override the default implementation. - /// - public event Action ApplyCustomUpdateState; - /// /// Plays all the hit sounds for this . /// This is invoked automatically when this is hit. @@ -268,6 +287,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// /// This is only used as an optimisation to delay the initial update of this and may be tuned more aggressively if required. + /// It is indirectly used to decide the automatic transform offset provided to . /// A more accurate should be set inside for an state. /// protected virtual double InitialLifetimeOffset => 10000; From 07a0df7c4f8e12f6acd09f068c4d77a369165549 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jul 2019 18:29:04 +0900 Subject: [PATCH 1809/2854] Fix bracket precedence --- osu.Game/Graphics/Backgrounds/Triangles.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 79f227fafe..2b68e8530d 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -214,7 +214,7 @@ namespace osu.Game.Graphics.Backgrounds { base.Draw(vertexAction); - if (vertexBatch == null || vertexBatch.Size != Source.AimCount && Source.AimCount > 0) + if (Source.AimCount > 0 && (vertexBatch == null || vertexBatch.Size != Source.AimCount)) { vertexBatch?.Dispose(); vertexBatch = new TriangleBatch(Source.AimCount, 1); From 3e95cb9145b935ce9eabcb2e91c092fa5053756a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 22 Jul 2019 14:35:18 +0300 Subject: [PATCH 1810/2854] Use bindable and disable button --- .../BeatmapSet/Buttons/FavouriteButton.cs | 16 ++++++++++++---- osu.Game/Overlays/BeatmapSet/Header.cs | 5 ++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs index 7207739646..5266623494 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs @@ -7,6 +7,7 @@ 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.Graphics; using osu.Game.Graphics.Backgrounds; using osuTK; @@ -15,7 +16,8 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { public class FavouriteButton : HeaderButton { - public readonly Bindable Favourited = new Bindable(); + private readonly Bindable favourited = new Bindable(); + public readonly Bindable BeatmapSet = new Bindable(); [BackgroundDependencyLoader] private void load() @@ -54,7 +56,15 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons }, }); - Favourited.ValueChanged += favourited => + BeatmapSet.BindValueChanged(setInfo => + { + if (setInfo.NewValue?.OnlineInfo?.HasFavourited == null) + return; + + favourited.Value = setInfo.NewValue.OnlineInfo.HasFavourited; + }); + + favourited.ValueChanged += favourited => { if (favourited.NewValue) { @@ -67,8 +77,6 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons icon.Icon = FontAwesome.Regular.Heart; } }; - - Action = () => Favourited.Value = !Favourited.Value; } protected override void UpdateAfterChildren() diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index dd8a2f18c1..d5b6fbd8e6 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -161,7 +161,8 @@ namespace osu.Game.Overlays.BeatmapSet Margin = new MarginPadding { Top = 10 }, Children = new Drawable[] { - favouriteButton = new FavouriteButton(), + favouriteButton = new FavouriteButton + { BeatmapSet = { BindTarget = BeatmapSet } }, downloadButtonsContainer = new FillFlowContainer { RelativeSizeAxes = Axes.Both, @@ -246,8 +247,6 @@ namespace osu.Game.Overlays.BeatmapSet onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status; downloadButtonsContainer.FadeIn(transition_duration); - - favouriteButton.Favourited.Value = setInfo.NewValue.OnlineInfo.HasFavourited; favouriteButton.FadeIn(transition_duration); updateDownloadButtons(); From 075ca3d8ea50df15918410798bed9aa2a9272df0 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 22 Jul 2019 14:47:35 +0300 Subject: [PATCH 1811/2854] CI fix --- osu.Game/Overlays/BeatmapSet/Header.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index d5b6fbd8e6..260a989628 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -162,7 +162,9 @@ namespace osu.Game.Overlays.BeatmapSet Children = new Drawable[] { favouriteButton = new FavouriteButton - { BeatmapSet = { BindTarget = BeatmapSet } }, + { + BeatmapSet = { BindTarget = BeatmapSet } + }, downloadButtonsContainer = new FillFlowContainer { RelativeSizeAxes = Axes.Both, From cdf75b409854cbf941c7827906c9a805f851aaf5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jul 2019 22:14:09 +0900 Subject: [PATCH 1812/2854] Public before private --- osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs index 5266623494..11f56bc163 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs @@ -16,9 +16,10 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { public class FavouriteButton : HeaderButton { - private readonly Bindable favourited = new Bindable(); public readonly Bindable BeatmapSet = new Bindable(); + private readonly Bindable favourited = new Bindable(); + [BackgroundDependencyLoader] private void load() { From 764513feea591b2d6e25e378e15913017d525b20 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jul 2019 23:13:48 +0900 Subject: [PATCH 1813/2854] Fix code quality --- .../Select/Details/UserTopScoreContainer.cs | 46 ++++++++++--------- osu.Game/Screens/Select/SongSelect.cs | 6 ++- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs index ba6751475e..cc2d2a3dae 100644 --- a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs @@ -31,30 +31,31 @@ namespace osu.Game.Screens.Select.Details { RelativeSizeAxes = Axes.X; Height = height; - Anchor = Anchor.BottomCentre; - Origin = Anchor.BottomCentre; + + Anchor = Anchor.BottomLeft; + Origin = Anchor.BottomLeft; + Children = new Drawable[] { contentContainer = new Container { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Height = height, - RelativeSizeAxes = Axes.X, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, Children = new Drawable[] { new OsuSpriteText { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, Margin = new MarginPadding { Top = 5 }, Text = @"your personal best".ToUpper(), Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold), }, scoreContainer = new Container { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, } @@ -62,22 +63,25 @@ namespace osu.Game.Screens.Select.Details } }; - Score.BindValueChanged(score => onScoreChanged(score.NewValue)); + Score.BindValueChanged(onScoreChanged); } - private void onScoreChanged(APILegacyUserTopScoreInfo score) + private void onScoreChanged(ValueChangedEvent score) { - if (score != null) + var newScore = score.NewValue; + + if (newScore == null) { - scoreContainer.Clear(); - scoreContainer.Add(new LeaderboardScore(score.Score, score.Position) - { - Action = () => ScoreSelected?.Invoke(score.Score) - }); - Show(); - } - else Hide(); + return; + } + + scoreContainer.Child = new LeaderboardScore(newScore.Score, newScore.Position) + { + Action = () => ScoreSelected?.Invoke(newScore.Score) + }; + + Show(); } protected override void PopIn() diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 209707d8fe..a3b87b5068 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -215,8 +215,10 @@ namespace osu.Game.Screens.Select }); } - BeatmapDetails.Leaderboard.ScoreSelected += s => this.Push(new SoloResults(s)); - BeatmapDetails.TopScore.ScoreSelected += s => this.Push(new SoloResults(s)); + void displayScore(ScoreInfo score) => this.Push(new SoloResults(score)); + + BeatmapDetails.Leaderboard.ScoreSelected += displayScore; + BeatmapDetails.TopScore.ScoreSelected += displayScore; } [BackgroundDependencyLoader(true)] From d83d93ee66fa26b2aabe1678d5c4704f1f74c0d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jul 2019 23:21:07 +0900 Subject: [PATCH 1814/2854] Use asynchronous loading --- .../Select/Details/UserTopScoreContainer.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs index cc2d2a3dae..1535aa3df1 100644 --- a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs @@ -10,6 +10,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; using osu.Game.Scoring; using System; +using System.Threading; namespace osu.Game.Screens.Select.Details { @@ -66,6 +67,8 @@ namespace osu.Game.Screens.Select.Details Score.BindValueChanged(onScoreChanged); } + private CancellationTokenSource loadScoreCancellation; + private void onScoreChanged(ValueChangedEvent score) { var newScore = score.NewValue; @@ -76,12 +79,17 @@ namespace osu.Game.Screens.Select.Details return; } - scoreContainer.Child = new LeaderboardScore(newScore.Score, newScore.Position) + scoreContainer.Clear(); + loadScoreCancellation?.Cancel(); + + LoadComponentAsync(new LeaderboardScore(newScore.Score, newScore.Position) { Action = () => ScoreSelected?.Invoke(newScore.Score) - }; - - Show(); + }, drawableScore => + { + scoreContainer.Child = drawableScore; + Show(); + }, (loadScoreCancellation = new CancellationTokenSource()).Token); } protected override void PopIn() From 95241165cce4724758f7267630b1755c748b1dbc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jul 2019 23:26:11 +0900 Subject: [PATCH 1815/2854] Fix text alignment --- osu.Game/Screens/Select/Details/UserTopScoreContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs index 1535aa3df1..5a224756b9 100644 --- a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; @@ -47,8 +47,8 @@ namespace osu.Game.Screens.Select.Details { new OsuSpriteText { - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, Margin = new MarginPadding { Top = 5 }, Text = @"your personal best".ToUpper(), Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold), From 5a6c8bfec9ff1563856ecd22936dc396e2360d90 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jul 2019 23:28:17 +0900 Subject: [PATCH 1816/2854] Adjust transition to now show janky resize --- .../Select/Details/UserTopScoreContainer.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs index 5a224756b9..8e9df8bbb1 100644 --- a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Select.Details public class UserTopScoreContainer : VisibilityContainer { private const int height = 90; - private const int duration = 300; + private const int duration = 800; private readonly Container contentContainer; private readonly Container scoreContainer; @@ -92,16 +92,8 @@ namespace osu.Game.Screens.Select.Details }, (loadScoreCancellation = new CancellationTokenSource()).Token); } - protected override void PopIn() - { - this.ResizeHeightTo(height, duration, Easing.OutQuint); - contentContainer.FadeIn(duration, Easing.OutQuint); - } + protected override void PopIn() => this.ResizeHeightTo(height, duration / 4f, Easing.OutQuint).OnComplete(_ => contentContainer.FadeIn(duration, Easing.OutQuint)); - protected override void PopOut() - { - this.ResizeHeightTo(0, duration, Easing.OutQuint); - contentContainer.FadeOut(duration, Easing.OutQuint); - } + protected override void PopOut() => contentContainer.FadeOut(duration, Easing.OutQuint).OnComplete(_ => this.ResizeHeightTo(0)); } } From 2f111e6cf4ea5e56147a395c8bff06a3c7508b2c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jul 2019 23:57:26 +0900 Subject: [PATCH 1817/2854] Fix incorrect corner radius --- .../Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs index a6c41cde72..0e9b534ca2 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical public class DrawableMostPlayedBeatmap : OsuHoverContainer { private const int cover_width = 100; - private const int corner_radius = 10; + private const int corner_radius = 6; private readonly BeatmapInfo beatmap; private readonly int playCount; From ee6fed5b33c219cada52a9628301d61767dabf57 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 22 Jul 2019 18:17:49 +0300 Subject: [PATCH 1818/2854] Use fixed height --- .../Sections/Historical/DrawableMostPlayedBeatmap.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs index 0e9b534ca2..0206c4e13b 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs @@ -22,6 +22,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { private const int cover_width = 100; private const int corner_radius = 6; + private const int height = 50; private readonly BeatmapInfo beatmap; private readonly int playCount; @@ -37,7 +38,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Enabled.Value = true; //manually enabled, because we have no action RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; + Height = height; Masking = true; CornerRadius = corner_radius; @@ -60,15 +61,13 @@ namespace osu.Game.Overlays.Profile.Sections.Historical }, new Container { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = cover_width - corner_radius }, Children = new Drawable[] { new Container { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.Both, Masking = true, CornerRadius = corner_radius, Children = new Drawable[] @@ -76,8 +75,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical background = new Box { RelativeSizeAxes = Axes.Both }, new Container { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(10), Children = new Drawable[] { From 94ed03548d7a401bc43be3b9ce602412efb6e662 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 22 Jul 2019 18:34:31 +0300 Subject: [PATCH 1819/2854] Hide top score at every leaderboard change --- osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index ee16123e20..f4a18e3b58 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -86,6 +86,8 @@ namespace osu.Game.Screens.Select.Leaderboards protected override APIRequest FetchScores(Action> scoresCallback) { + TopScore.Value = null; + if (Scope == BeatmapLeaderboardScope.Local) { var scores = scoreManager From 081355e3d158f74cc74ff061455e013ecb09406f Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 22 Jul 2019 23:12:09 +0300 Subject: [PATCH 1820/2854] Use IHasText and simplify properties --- .../Graphics/Sprites/GlowingSpriteText.cs | 44 ++++--------------- 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs index 546b8cae58..6c92d4cd06 100644 --- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs @@ -9,50 +9,26 @@ using osuTK; namespace osu.Game.Graphics.Sprites { - public class GlowingSpriteText : Container + public class GlowingSpriteText : Container, IHasText { private readonly OsuSpriteText spriteText, blurredText; - private string text = string.Empty; - public string Text { - get => text; - set - { - text = value; - - spriteText.Text = text; - blurredText.Text = text; - } + get => spriteText.Text; + set => blurredText.Text = spriteText.Text = value; } - - private FontUsage font = OsuFont.Default.With(fixedWidth: true); - + public FontUsage Font { - get => font; - set - { - font = value.With(fixedWidth: true); - - spriteText.Font = font; - blurredText.Font = font; - } + get => spriteText.Font; + set => blurredText.Font = spriteText.Font = value.With(fixedWidth: true); } - private Vector2 textSize; - public Vector2 TextSize { - get => textSize; - set - { - textSize = value; - - spriteText.Size = textSize; - blurredText.Size = textSize; - } + get => spriteText.Size; + set => blurredText.Size = spriteText.Size = value; } public ColourInfo TextColour @@ -88,8 +64,6 @@ namespace osu.Game.Graphics.Sprites { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Font = font, - Text = text, Shadow = false, }, }, @@ -98,8 +72,6 @@ namespace osu.Game.Graphics.Sprites { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Font = font, - Text = text, Shadow = false, }, }; From 32e9547ce9d264b779f1d90b60bf765c1e41dd1c Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 22 Jul 2019 23:16:54 +0300 Subject: [PATCH 1821/2854] Trim whitespace --- osu.Game/Graphics/Sprites/GlowingSpriteText.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs index 6c92d4cd06..74e387d60e 100644 --- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs @@ -18,7 +18,7 @@ namespace osu.Game.Graphics.Sprites get => spriteText.Text; set => blurredText.Text = spriteText.Text = value; } - + public FontUsage Font { get => spriteText.Font; From ffcc1c62af5fcfed1d9ec7b3b322b4145d724d56 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 22 Jul 2019 23:22:39 +0300 Subject: [PATCH 1822/2854] simplify moving condition --- osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index bf0cd91321..927986159b 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -71,15 +71,7 @@ namespace osu.Game.Overlays.Toolbar // Scheduled to allow the flow layout to be computed before the line position is updated private void moveLineToCurrent() => ScheduleAfterChildren(() => { - foreach (var tabItem in TabContainer) - { - if (tabItem.Value.Equals(Current.Value)) - { - ModeButtonLine.MoveToX(tabItem.DrawPosition.X, !hasInitialPosition ? 0 : 200, Easing.OutQuint); - break; - } - } - + ModeButtonLine.MoveToX(SelectedTab.DrawPosition.X, !hasInitialPosition ? 0 : 200, Easing.OutQuint); hasInitialPosition = true; }); From 76b79f355443cba1b1f60fea02f24fae764e771d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 23 Jul 2019 01:14:45 +0300 Subject: [PATCH 1823/2854] Transform adjustments --- osu.Game/Screens/Select/Details/UserTopScoreContainer.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs index 8e9df8bbb1..c10f4d4fd4 100644 --- a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs @@ -42,7 +42,8 @@ namespace osu.Game.Screens.Select.Details { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + Height = height, Children = new Drawable[] { new OsuSpriteText @@ -94,6 +95,10 @@ namespace osu.Game.Screens.Select.Details protected override void PopIn() => this.ResizeHeightTo(height, duration / 4f, Easing.OutQuint).OnComplete(_ => contentContainer.FadeIn(duration, Easing.OutQuint)); - protected override void PopOut() => contentContainer.FadeOut(duration, Easing.OutQuint).OnComplete(_ => this.ResizeHeightTo(0)); + protected override void PopOut() + { + this.ResizeHeightTo(0); + contentContainer.FadeOut(duration / 4f, Easing.OutQuint); + } } } From d5ee4cbc9cc5d3bae002d6e6357296ebc9be5b1c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Jul 2019 13:11:06 +0900 Subject: [PATCH 1824/2854] Move TouchDevice mod to new "system" category --- osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs | 2 ++ osu.Game/Rulesets/Mods/ModType.cs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs index 571756d056..f0db548e74 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs @@ -11,6 +11,8 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Acronym => "TD"; public override double ScoreMultiplier => 1; + public override ModType Type => ModType.System; + public override bool Ranked => true; } } diff --git a/osu.Game/Rulesets/Mods/ModType.cs b/osu.Game/Rulesets/Mods/ModType.cs index cd649728cf..e3c82e42f5 100644 --- a/osu.Game/Rulesets/Mods/ModType.cs +++ b/osu.Game/Rulesets/Mods/ModType.cs @@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Mods DifficultyIncrease, Conversion, Automation, - Fun + Fun, + System } } From f8feac792c4cc1da31de38e2ebb4b48d74633439 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Jul 2019 13:12:17 +0900 Subject: [PATCH 1825/2854] Return TouchDevice in GetAllMods response --- osu.Game.Rulesets.Osu/OsuRuleset.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index baa4aff413..8df0f77629 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -138,6 +138,12 @@ namespace osu.Game.Rulesets.Osu new MultiMod(new ModWindUp(), new ModWindDown()), }; + case ModType.System: + return new Mod[] + { + new OsuModTouchDevice(), + }; + default: return new Mod[] { }; } From e628e44d8e7a45ea961481dafe8c02a92b965209 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 23 Jul 2019 13:25:03 +0900 Subject: [PATCH 1826/2854] update comment --- osu.Game/Graphics/UserInterface/SearchTextBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index c1020e6340..a3fe9bb8f5 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -36,7 +36,7 @@ namespace osu.Game.Graphics.UserInterface protected override bool HandleAction(PlatformAction action) { - // Allow delete to be handled locally + // Shift-delete is unnecessary for search inputs, so its propagated up the input queue. if (action.ActionType == PlatformActionType.CharNext && action.ActionMethod == PlatformActionMethod.Delete) return false; From 292bd22f92656b972318ce766b4ff6d0440c92f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Jul 2019 13:38:05 +0900 Subject: [PATCH 1827/2854] Allow multiple instances of osu! when running under debug --- osu.Desktop/Program.cs | 45 ++++++++++++++++++++++++------------------ osu.Game/OsuGame.cs | 3 ++- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index cb488fea52..141b2cdbbc 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -29,29 +29,36 @@ namespace osu.Desktop if (!host.IsPrimaryInstance) { - var importer = new ArchiveImportIPCChannel(host); - // Restore the cwd so relative paths given at the command line work correctly - Directory.SetCurrentDirectory(cwd); - - foreach (var file in args) + if (args.Length > 0 && args[0].Contains('.')) // easy way to check for a file import in args { - Console.WriteLine(@"Importing {0}", file); - if (!importer.ImportAsync(Path.GetFullPath(file)).Wait(3000)) - throw new TimeoutException(@"IPC took too long to send"); + var importer = new ArchiveImportIPCChannel(host); + // Restore the cwd so relative paths given at the command line work correctly + Directory.SetCurrentDirectory(cwd); + + foreach (var file in args) + { + Console.WriteLine(@"Importing {0}", file); + if (!importer.ImportAsync(Path.GetFullPath(file)).Wait(3000)) + throw new TimeoutException(@"IPC took too long to send"); + } + + return 0; } + + // we want to allow multiple instances to be started when in debug. + if (!DebugUtils.IsDebugBuild) + return 0; } - else - { - switch (args.FirstOrDefault() ?? string.Empty) - { - default: - host.Run(new OsuGameDesktop(args)); - break; - case "--tournament": - host.Run(new TournamentGame()); - break; - } + switch (args.FirstOrDefault() ?? string.Empty) + { + default: + host.Run(new OsuGameDesktop(args)); + break; + + case "--tournament": + host.Run(new TournamentGame()); + break; } return 0; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2a484fc122..41b67f343a 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -20,6 +20,7 @@ using System.Threading; using System.Threading.Tasks; using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Development; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; @@ -153,7 +154,7 @@ namespace osu.Game { this.frameworkConfig = frameworkConfig; - if (!Host.IsPrimaryInstance) + if (!Host.IsPrimaryInstance && !DebugUtils.IsDebugBuild) { Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error); Environment.Exit(0); From 776757545d15ae536f223afeb8e6ada4d8bab89b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 23 Jul 2019 15:17:02 +0900 Subject: [PATCH 1828/2854] Fix FTB causing flashlight to block vision correctly --- .../Vertices/PositionAndColourVertex.cs | 26 +++++++++++++++++++ osu.Game/Rulesets/Mods/ModFlashlight.cs | 12 ++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Graphics/OpenGL/Vertices/PositionAndColourVertex.cs diff --git a/osu.Game/Graphics/OpenGL/Vertices/PositionAndColourVertex.cs b/osu.Game/Graphics/OpenGL/Vertices/PositionAndColourVertex.cs new file mode 100644 index 0000000000..8714138322 --- /dev/null +++ b/osu.Game/Graphics/OpenGL/Vertices/PositionAndColourVertex.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Runtime.InteropServices; +using osu.Framework.Graphics.OpenGL.Vertices; +using osuTK; +using osuTK.Graphics; +using osuTK.Graphics.ES30; + +namespace osu.Game.Graphics.OpenGL.Vertices +{ + [StructLayout(LayoutKind.Sequential)] + public struct PositionAndColourVertex : IEquatable, IVertex + { + [VertexMember(2, VertexAttribPointerType.Float)] + public Vector2 Position; + + [VertexMember(4, VertexAttribPointerType.Float)] + public Color4 Colour; + + public bool Equals(PositionAndColourVertex other) + => Position.Equals(other.Position) + && Colour.Equals(other.Colour); + } +} diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 405d21c711..cb0c2fafe5 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Batches; using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shaders; @@ -13,6 +14,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps.Timing; using osu.Game.Graphics; +using osu.Game.Graphics.OpenGL.Vertices; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; @@ -153,9 +155,17 @@ namespace osu.Game.Rulesets.Mods private Vector2 flashlightSize; private float flashlightDim; + private readonly VertexBatch quadBatch = new QuadBatch(1, 1); + private readonly Action addAction; + public FlashlightDrawNode(Flashlight source) : base(source) { + addAction = v => quadBatch.Add(new PositionAndColourVertex + { + Position = v.Position, + Colour = v.Colour + }); } public override void ApplyState() @@ -179,7 +189,7 @@ namespace osu.Game.Rulesets.Mods shader.GetUniform("flashlightSize").UpdateValue(ref flashlightSize); shader.GetUniform("flashlightDim").UpdateValue(ref flashlightDim); - DrawQuad(Texture.WhitePixel, screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction); + DrawQuad(Texture.WhitePixel, screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: addAction); shader.Unbind(); } From 4d8e2a78d119c79907da53197d9bd0abc5736889 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 23 Jul 2019 15:31:09 +0900 Subject: [PATCH 1829/2854] update with new framework changes and update comment --- osu.Game/Graphics/UserInterface/SearchTextBox.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index a3fe9bb8f5..4b49174e65 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -34,13 +34,14 @@ namespace osu.Game.Graphics.UserInterface PlaceholderText = "type to search"; } - protected override bool HandleAction(PlatformAction action) + public override bool OnPressed(PlatformAction action) { - // Shift-delete is unnecessary for search inputs, so its propagated up the input queue. + // Shift-delete, used in MacOS for character deletion, is unnecessary here as arrow keys are blocked by HandleLeftRightArrows + // Avoid handling it here to allow other components to potentially consume the shortcut if (action.ActionType == PlatformActionType.CharNext && action.ActionMethod == PlatformActionMethod.Delete) return false; - return base.HandleAction(action); + return base.OnPressed(action); } protected override bool OnKeyDown(KeyDownEvent e) From 5e72ed0d1276dd149d2d69dc6da90f308a047e96 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 23 Jul 2019 15:35:12 +0900 Subject: [PATCH 1830/2854] Fix potential nullref --- osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index 927986159b..2c79f5bc0e 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -71,8 +71,11 @@ namespace osu.Game.Overlays.Toolbar // Scheduled to allow the flow layout to be computed before the line position is updated private void moveLineToCurrent() => ScheduleAfterChildren(() => { - ModeButtonLine.MoveToX(SelectedTab.DrawPosition.X, !hasInitialPosition ? 0 : 200, Easing.OutQuint); - hasInitialPosition = true; + if (SelectedTab != null) + { + ModeButtonLine.MoveToX(SelectedTab.DrawPosition.X, !hasInitialPosition ? 0 : 200, Easing.OutQuint); + hasInitialPosition = true; + } }); public override bool HandleNonPositionalInput => !Current.Disabled && base.HandleNonPositionalInput; From 704fe2d6554f5138d994d5528b10be3831cc4459 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Jul 2019 16:01:05 +0900 Subject: [PATCH 1831/2854] Remove text shadow in chat --- osu.Game/Overlays/Chat/ChatLine.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index e29216dffd..2576b38ec8 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -85,6 +85,7 @@ namespace osu.Game.Overlays.Chat Drawable effectedUsername = username = new OsuSpriteText { + Shadow = false, Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length], Font = OsuFont.GetFont(size: TextSize, weight: FontWeight.Bold, italics: true) }; @@ -133,6 +134,7 @@ namespace osu.Game.Overlays.Chat { timestamp = new OsuSpriteText { + Shadow = false, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Font = OsuFont.GetFont(size: TextSize * 0.75f, weight: FontWeight.SemiBold, fixedWidth: true) @@ -155,6 +157,8 @@ namespace osu.Game.Overlays.Chat { contentFlow = new LinkFlowContainer(t => { + t.Shadow = false; + if (Message.IsAction) { t.Font = OsuFont.GetFont(italics: true); From e81ef4bf339e59307f1cb47a12e9097e580afd47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Jul 2019 16:44:19 +0900 Subject: [PATCH 1832/2854] Rewrite comment --- osu.Game/Graphics/UserInterface/SearchTextBox.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index 4b49174e65..c3efe2ed45 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -36,8 +36,9 @@ namespace osu.Game.Graphics.UserInterface public override bool OnPressed(PlatformAction action) { - // Shift-delete, used in MacOS for character deletion, is unnecessary here as arrow keys are blocked by HandleLeftRightArrows - // Avoid handling it here to allow other components to potentially consume the shortcut + // Shift+delete is handled via PlatformAction on macOS. this is not so useful in the context of a SearchTextBox + // as we do not allow arrow key navigation in the first place (ie. the care should always be at the end of text) + // Avoid handling it here to allow other components to potentially consume the shortcut. if (action.ActionType == PlatformActionType.CharNext && action.ActionMethod == PlatformActionMethod.Delete) return false; From 8220a513102a6128fc6e3c11383b365029b4aa4f Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 23 Jul 2019 17:59:50 +0900 Subject: [PATCH 1833/2854] Make backbutton handle global input last --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 669fd62e45..ff006efdd6 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Bindings; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Input.Bindings { @@ -55,8 +56,11 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed), }; + /// + /// Make sure that the handles global input first, and that handles global input last. + /// protected override IEnumerable KeyBindingInputQueue => - handler == null ? base.KeyBindingInputQueue : base.KeyBindingInputQueue.Prepend(handler); + (handler == null ? base.KeyBindingInputQueue : base.KeyBindingInputQueue.Prepend(handler)).OrderBy(d => d is BackButton); } public enum GlobalAction From b1a9ce85e7fbaaac4ff9962cb71be382d4853a0f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 23 Jul 2019 20:30:47 +0900 Subject: [PATCH 1834/2854] Fix ticks being given an extra colour --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index ec294a1630..dafdcf3b82 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -34,11 +34,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, CornerRadius = Size.X / 2, - Colour = AccentColour.Value, - BorderThickness = 2, BorderColour = Color4.White, - Child = new Box { RelativeSizeAxes = Axes.Both, From 74b09c72fa044d9fc0f21f19f03da216df2bf291 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 23 Jul 2019 21:08:41 +0900 Subject: [PATCH 1835/2854] Refactor state updates to convert State into an IBindable --- .../Objects/Drawables/DrawableHitObject.cs | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index d10f829dae..3253302c71 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -79,12 +79,12 @@ namespace osu.Game.Rulesets.Objects.Drawables public override bool RemoveCompletedTransforms => false; protected override bool RequiresChildrenUpdate => true; - public override bool IsPresent => base.IsPresent || (state.Value == ArmedState.Idle && Clock?.CurrentTime >= LifetimeStart); - - public readonly Bindable State = new Bindable(); + public override bool IsPresent => base.IsPresent || (State.Value == ArmedState.Idle && Clock?.CurrentTime >= LifetimeStart); private readonly Bindable state = new Bindable(); + public IBindable State => state; + protected DrawableHitObject(HitObject hitObject) { HitObject = hitObject; @@ -121,17 +121,7 @@ namespace osu.Game.Rulesets.Objects.Drawables protected override void LoadComplete() { base.LoadComplete(); - - state.BindValueChanged(armed => - { - updateState(armed.NewValue); - - // apply any custom state overrides - ApplyCustomUpdateState?.Invoke(this, armed.NewValue); - - if (armed.NewValue == ArmedState.Hit) - PlaySamples(); - }, true); + updateState(ArmedState.Idle, true); } #region State / Transform Management @@ -152,8 +142,17 @@ namespace osu.Game.Rulesets.Objects.Drawables protected override void ClearInternal(bool disposeChildren = true) => throw new InvalidOperationException($"Should never clear a {nameof(DrawableHitObject)}"); - private void updateState(ArmedState state) + private void updateState(ArmedState newState, bool force = false) { + if (State.Value == newState && !force) + return; + + // apply any custom state overrides + ApplyCustomUpdateState?.Invoke(this, newState); + + if (newState == ArmedState.Hit) + PlaySamples(); + if (UseTransformStateManagement) { double transformTime = HitObject.StartTime - InitialLifetimeOffset; @@ -169,17 +168,15 @@ namespace osu.Game.Rulesets.Objects.Drawables using (BeginDelayedSequence(InitialLifetimeOffset + judgementOffset, true)) { - UpdateStateTransforms(state); - State.Value = state; + UpdateStateTransforms(newState); + state.Value = newState; } } } else - { - State.Value = state; - } + state.Value = newState; - UpdateState(state); + UpdateState(newState); } /// @@ -255,7 +252,8 @@ namespace osu.Game.Rulesets.Objects.Drawables Result.TimeOffset = 0; Result.Type = HitResult.None; - state.Value = ArmedState.Idle; + + updateState(ArmedState.Idle); } } } @@ -336,11 +334,11 @@ namespace osu.Game.Rulesets.Objects.Drawables break; case HitResult.Miss: - state.Value = ArmedState.Miss; + updateState(ArmedState.Miss); break; default: - state.Value = ArmedState.Hit; + updateState(ArmedState.Hit); break; } From 4e7e2d1d5230b76bb25edfbceffb8b51c060f475 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 23 Jul 2019 21:15:55 +0900 Subject: [PATCH 1836/2854] Adjust comments --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 3253302c71..1d9d885527 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -180,7 +180,7 @@ namespace osu.Game.Rulesets.Objects.Drawables } /// - /// Apply (generally fade-in) transforms. + /// Apply (generally fade-in) transforms leading into the start time. /// The local drawable hierarchy is recursively delayed to for convenience. /// /// @@ -201,14 +201,14 @@ namespace osu.Game.Rulesets.Objects.Drawables public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { - // When we are using automatic state menement, parent calls to this should be blocked for safety. + // When we are using automatic state management, parent calls to this should be blocked for safety. if (!UseTransformStateManagement) base.ClearTransformsAfter(time, propagateChildren, targetMember); } public override void ApplyTransformsAt(double time, bool propagateChildren = false) { - // When we are using automatic state menement, parent calls to this should be blocked for safety. + // When we are using automatic state management, parent calls to this should be blocked for safety. if (!UseTransformStateManagement) base.ApplyTransformsAt(time, propagateChildren); } From 2610ee2abb4a63603e4afb1b7c9468a59d43fb1b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Jul 2019 21:39:10 +0900 Subject: [PATCH 1837/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b9451fc744..b24493665e 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d90b1d36e1..c05cc6f9dd 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index fa2521a19e..3b18039600 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 38559685a96ec763291aff3bed423696462b69cf Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 24 Jul 2019 12:47:41 +0900 Subject: [PATCH 1838/2854] proxy backbutton instead --- osu.Game/OsuGame.cs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2a484fc122..4c7fc688ab 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -394,6 +394,16 @@ namespace osu.Game AddRange(new Drawable[] { + backButton = new BackButton + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Action = () => + { + if ((screenStack.CurrentScreen as IOsuScreen)?.AllowBackButton == true) + screenStack.Exit(); + } + }, new VolumeControlReceptor { RelativeSizeAxes = Axes.Both, @@ -403,19 +413,10 @@ namespace osu.Game screenContainer = new ScalingContainer(ScalingMode.ExcludeOverlays) { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + Children = new[] { screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }, - backButton = new BackButton - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Action = () => - { - if ((screenStack.CurrentScreen as IOsuScreen)?.AllowBackButton == true) - screenStack.Exit(); - } - }, + backButton.CreateProxy(), logoContainer = new Container { RelativeSizeAxes = Axes.Both }, } }, From da3dc610baf632279149814ac500f7d47883c4d3 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 24 Jul 2019 12:52:18 +0900 Subject: [PATCH 1839/2854] revert globalaction changes --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index ff006efdd6..669fd62e45 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -7,7 +7,6 @@ using System.Linq; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Bindings; -using osu.Game.Graphics.UserInterface; namespace osu.Game.Input.Bindings { @@ -56,11 +55,8 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed), }; - /// - /// Make sure that the handles global input first, and that handles global input last. - /// protected override IEnumerable KeyBindingInputQueue => - (handler == null ? base.KeyBindingInputQueue : base.KeyBindingInputQueue.Prepend(handler)).OrderBy(d => d is BackButton); + handler == null ? base.KeyBindingInputQueue : base.KeyBindingInputQueue.Prepend(handler); } public enum GlobalAction From 136f3b8b6b1430605ebe10391d9f40503d505a52 Mon Sep 17 00:00:00 2001 From: DTSDAO Date: Wed, 24 Jul 2019 20:49:35 +0800 Subject: [PATCH 1840/2854] Add iOS import osu! files --- osu.iOS/AppDelegate.cs | 19 ++++++++++++++++++- osu.iOS/Info.plist | 13 +++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/osu.iOS/AppDelegate.cs b/osu.iOS/AppDelegate.cs index 058e246ed8..93eca8caa1 100644 --- a/osu.iOS/AppDelegate.cs +++ b/osu.iOS/AppDelegate.cs @@ -1,15 +1,32 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Threading.Tasks; using Foundation; using osu.Framework.iOS; using osu.Game; +using UIKit; namespace osu.iOS { [Register("AppDelegate")] public class AppDelegate : GameAppDelegate { - protected override Framework.Game CreateGame() => new OsuGameIOS(); + private OsuGameIOS IOSGame; + + protected override Framework.Game CreateGame() + { + //Save OsuGameIOS for Import + IOSGame = new OsuGameIOS(); + return IOSGame; + } + + public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation) + { + //Open in Application + Task.Run(() => IOSGame.Import(url.Path)); + + return true; + } } } diff --git a/osu.iOS/Info.plist b/osu.iOS/Info.plist index d7992353cf..f6fc768632 100644 --- a/osu.iOS/Info.plist +++ b/osu.iOS/Info.plist @@ -40,5 +40,18 @@ XSAppIconAssets Assets.xcassets/AppIcon.appiconset + CFBundleDocumentTypes + + + LSHandlerRank + Owner + CFBundleTypeName + public.item + LSItemContentTypes + + public.item + + + From 433f192214da43c206a47296531cadb2c76910f1 Mon Sep 17 00:00:00 2001 From: DTSDAO Date: Wed, 24 Jul 2019 21:51:01 +0800 Subject: [PATCH 1841/2854] Add iOS custom UTIs --- osu.iOS/Info.plist | 56 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/osu.iOS/Info.plist b/osu.iOS/Info.plist index f6fc768632..0775d1522d 100644 --- a/osu.iOS/Info.plist +++ b/osu.iOS/Info.plist @@ -40,16 +40,68 @@ XSAppIconAssets Assets.xcassets/AppIcon.appiconset + UTExportedTypeDeclarations + + + UTTypeConformsTo + + + + UTTypeIdentifier + sh.ppy.osu.items + UTTypeTagSpecification + + + + UTTypeConformsTo + + sh.ppy.osu.items + + UTTypeIdentifier + sh.ppy.osu.osr + UTTypeTagSpecification + + public.filename-extension + osr + + + + UTTypeConformsTo + + sh.ppy.osu.items + + UTTypeIdentifier + sh.ppy.osu.osk + UTTypeTagSpecification + + public.filename-extension + osk + + + + UTTypeConformsTo + + sh.ppy.osu.items + + UTTypeIdentifier + sh.ppy.osu.osz + UTTypeTagSpecification + + public.filename-extension + osz + + + CFBundleDocumentTypes LSHandlerRank Owner CFBundleTypeName - public.item + Supported osu! files LSItemContentTypes - public.item + sh.ppy.osu.items From 78a8a6490e50a15eb8aad5f83675f1c741e6ea30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=BCbner?= Date: Wed, 24 Jul 2019 17:17:29 +0200 Subject: [PATCH 1842/2854] close chat tabs with middle mouse button --- .../Overlays/Chat/Tabs/ChannelSelectorTabItem.cs | 3 +++ osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs index 7386bffb1a..f6533be551 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Online.Chat; @@ -39,5 +40,7 @@ namespace osu.Game.Overlays.Chat.Tabs Name = "+"; } } + + protected override bool OnMouseUp(MouseUpEvent e) => false; } } diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index 7f820e4ff7..859ca30d7f 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -16,6 +16,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; using osuTK; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Overlays.Chat.Tabs { @@ -138,6 +139,17 @@ namespace osu.Game.Overlays.Chat.Tabs updateState(); } + protected override bool OnMouseUp(MouseUpEvent e) + { + if (e.Button == MouseButton.Middle) + { + CloseButton.Action(); + return true; + } + + return false; + } + [BackgroundDependencyLoader] private void load(OsuColour colours) { From bbcc8f072116e52e5105bc241166a4396d3b9af5 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 25 Jul 2019 11:11:20 +0900 Subject: [PATCH 1843/2854] Add new container level and unblock footer --- osu.Game/OsuGame.cs | 33 +++++++++++++++++++------------ osu.Game/Screens/Select/Footer.cs | 4 ++-- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 6fb76c640f..a248da4304 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -395,16 +395,6 @@ namespace osu.Game AddRange(new Drawable[] { - backButton = new BackButton - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Action = () => - { - if ((screenStack.CurrentScreen as IOsuScreen)?.AllowBackButton == true) - screenStack.Exit(); - } - }, new VolumeControlReceptor { RelativeSizeAxes = Axes.Both, @@ -416,9 +406,26 @@ namespace osu.Game RelativeSizeAxes = Axes.Both, Children = new[] { - screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }, - backButton.CreateProxy(), - logoContainer = new Container { RelativeSizeAxes = Axes.Both }, + backButton = new BackButton + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Action = () => + { + if ((screenStack.CurrentScreen as IOsuScreen)?.AllowBackButton == true) + screenStack.Exit(); + } + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new[] + { + screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }, + backButton.CreateProxy(), + logoContainer = new Container { RelativeSizeAxes = Axes.Both }, + } + } } }, overlayContent = new Container { RelativeSizeAxes = Axes.Both }, diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index 0680711f1c..0043acb818 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -104,8 +104,8 @@ namespace osu.Game.Screens.Select updateModeLight(); } - protected override bool OnMouseDown(MouseDownEvent e) => true; + protected override bool OnMouseDown(MouseDownEvent e) => false; - protected override bool OnClick(ClickEvent e) => true; + protected override bool OnClick(ClickEvent e) => false; } } From c16af882993b86a0ef91d7813ba5c551dbced753 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Jul 2019 11:47:27 +0900 Subject: [PATCH 1844/2854] Start exploding animation earlier (don't wait for flash) --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index d3d763daf3..001003155d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -169,6 +169,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables .FadeOut(100); explode.FadeIn(flash_in); + explodeContainer.ScaleTo(1.5f, 400, Easing.OutQuad); using (BeginDelayedSequence(flash_in, true)) { @@ -178,7 +179,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables number.FadeOut(); this.FadeOut(800); - explodeContainer.ScaleTo(1.5f, 400, Easing.OutQuad); } Expire(); From e63c97b306b712db8b2159d8bfd3acef76006e81 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 25 Jul 2019 11:20:24 +0900 Subject: [PATCH 1845/2854] remove unnecessary overrides --- osu.Game/Screens/Select/Footer.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index 0043acb818..a1384f19b4 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -103,9 +103,5 @@ namespace osu.Game.Screens.Select updateModeLight(); } - - protected override bool OnMouseDown(MouseDownEvent e) => false; - - protected override bool OnClick(ClickEvent e) => false; } } From 7275beaf1aedbcb55fce70deaab3f075762ea153 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Jul 2019 18:38:24 +0900 Subject: [PATCH 1846/2854] Remove unnecessary type specification from SkinnableDrawable --- osu.Game/Skinning/SkinnableDrawable.cs | 22 ++++++---------------- osu.Game/Skinning/SkinnableSprite.cs | 5 +++-- osu.Game/Skinning/SkinnableSpriteText.cs | 2 +- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index eb0508b568..0c635a3d2f 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -8,23 +8,13 @@ using osuTK; namespace osu.Game.Skinning { - public class SkinnableDrawable : SkinnableDrawable - { - public SkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) - : base(name, defaultImplementation, allowFallback, confineMode) - { - } - } - /// /// A drawable which can be skinned via an . /// - /// The type of drawable. - public class SkinnableDrawable : SkinReloadableDrawable - where T : Drawable + public class SkinnableDrawable : SkinReloadableDrawable { /// - /// The displayed component. May or may not be a type- member. + /// The displayed component. /// protected Drawable Drawable { get; private set; } @@ -39,7 +29,7 @@ namespace osu.Game.Skinning /// A function to create the default skin implementation of this element. /// A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present. /// How (if at all) the should be resize to fit within our own bounds. - public SkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + public SkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) : this(name, allowFallback, confineMode) { createDefault = defaultImplementation; @@ -54,13 +44,13 @@ namespace osu.Game.Skinning RelativeSizeAxes = Axes.Both; } - private readonly Func createDefault; + private readonly Func createDefault; private readonly Cached scaling = new Cached(); private bool isDefault; - protected virtual T CreateDefault(string name) => createDefault(name); + protected virtual Drawable CreateDefault(string name) => createDefault(name); /// /// Whether to apply size restrictions (specified via ) to the default implementation. diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index 1716ec71de..07ba48d6ae 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; @@ -11,7 +12,7 @@ namespace osu.Game.Skinning /// /// A skinnable element which uses a stable sprite and can therefore share implementation logic. /// - public class SkinnableSprite : SkinnableDrawable + public class SkinnableSprite : SkinnableDrawable { protected override bool ApplySizeRestrictionsToDefault => true; @@ -23,6 +24,6 @@ namespace osu.Game.Skinning { } - protected override Sprite CreateDefault(string name) => new Sprite { Texture = textures.Get(name) }; + protected override Drawable CreateDefault(string name) => new Sprite { Texture = textures.Get(name) }; } } diff --git a/osu.Game/Skinning/SkinnableSpriteText.cs b/osu.Game/Skinning/SkinnableSpriteText.cs index d12a6f74f7..5af6df15e1 100644 --- a/osu.Game/Skinning/SkinnableSpriteText.cs +++ b/osu.Game/Skinning/SkinnableSpriteText.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics.Sprites; namespace osu.Game.Skinning { - public class SkinnableSpriteText : SkinnableDrawable, IHasText + public class SkinnableSpriteText : SkinnableDrawable, IHasText { public SkinnableSpriteText(string name, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) : base(name, defaultImplementation, allowFallback, confineMode) From c989185774497a13ef6cf977fb58052b56722ec3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Jul 2019 19:32:24 +0900 Subject: [PATCH 1847/2854] Fix incorrect approach circle scaling --- .../Objects/Drawables/DrawableHitCircle.cs | 2 +- .../Drawables/Pieces/ApproachCircle.cs | 22 ++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index d3d763daf3..9b8627d8f1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables base.UpdateInitialTransforms(); ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt)); - ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt); + ApproachCircle.ScaleTo(1f, HitObject.TimePreempt); ApproachCircle.Expire(true); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs index 9981585f9e..5813197336 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -24,7 +25,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces [BackgroundDependencyLoader] private void load(TextureStore textures) { - Child = new SkinnableSprite("Play/osu/approachcircle"); + Child = new SkinnableApproachCircle(); + } + + private class SkinnableApproachCircle : SkinnableSprite + { + public SkinnableApproachCircle() + : base("Play/osu/approachcircle") + { + } + + protected override Drawable CreateDefault(string name) + { + var drawable = base.CreateDefault(name); + + // account for the sprite being used for the default approach circle being taken from stable, + // when hitcircles have 5px padding on each size. this should be removed if we update the sprite. + drawable.Scale = new Vector2(128 / 118f); + + return drawable; + } } } } From 6ef3c71e22c430039cdfab297a0095bfc48a20a6 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 25 Jul 2019 11:31:46 +0900 Subject: [PATCH 1848/2854] remove unused using --- osu.Game/Screens/Select/Footer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index a1384f19b4..71641cab5d 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -9,7 +9,6 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; namespace osu.Game.Screens.Select From 69844e6c24e973a74024be87ba654f16e9958221 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Jul 2019 12:18:18 +0900 Subject: [PATCH 1849/2854] Fix beatmap present failing directly after an import --- osu.Game/Screens/Select/BeatmapCarousel.cs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 16354534f4..5069096a44 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -81,7 +81,8 @@ namespace osu.Game.Screens.Select itemsCache.Invalidate(); scrollPositionCache.Invalidate(); - Schedule(() => + // Run on late scheduler want to ensure this runs after all pending UpdateBeatmapSet / RemoveBeatmapSet operations are run. + SchedulerAfterChildren.Add(() => { BeatmapSetsChanged?.Invoke(); BeatmapSetsLoaded = true; @@ -129,19 +130,16 @@ namespace osu.Game.Screens.Select loadBeatmapSets(beatmaps.GetAllUsableBeatmapSetsEnumerable()); } - public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) + public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() => { - Schedule(() => - { - var existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.ID == beatmapSet.ID); + var existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.ID == beatmapSet.ID); - if (existingSet == null) - return; + if (existingSet == null) + return; - root.RemoveChild(existingSet); - itemsCache.Invalidate(); - }); - } + root.RemoveChild(existingSet); + itemsCache.Invalidate(); + }); public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() => { From 2b184658d1d81b7ae6f86cf5b38e963d2b53f2cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jul 2019 20:28:31 +0900 Subject: [PATCH 1850/2854] Adjust follow points by circle size --- .../Objects/Drawables/Connections/FollowPointRenderer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs index 7569626230..a269b87c75 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs @@ -97,13 +97,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections Position = pointStartPosition, Rotation = rotation, Alpha = 0, - Scale = new Vector2(1.5f), + Scale = new Vector2(1.5f * currHitObject.Scale), }); using (fp.BeginAbsoluteSequence(fadeInTime)) { fp.FadeIn(currHitObject.TimeFadeIn); - fp.ScaleTo(1, currHitObject.TimeFadeIn, Easing.Out); + fp.ScaleTo(currHitObject.Scale, currHitObject.TimeFadeIn, Easing.Out); fp.MoveTo(pointEndPosition, currHitObject.TimeFadeIn, Easing.Out); From 28653e871cad59e40d978a088717ee21f3e0614f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jul 2019 15:59:29 +0900 Subject: [PATCH 1851/2854] Give repeat points a size specification --- .../Objects/Drawables/DrawableRepeatPoint.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 2e6e7e03ac..543e5d20d5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables this.repeatPoint = repeatPoint; this.drawableSlider = drawableSlider; - Size = new Vector2(45 * repeatPoint.Scale); + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2 * repeatPoint.Scale); Blending = BlendingMode.Additive; Origin = Anchor.Centre; @@ -36,7 +36,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables new SkinnableDrawable("Play/osu/reversearrow", _ => new SpriteIcon { RelativeSizeAxes = Axes.Both, - Icon = FontAwesome.Solid.ChevronRight + Icon = FontAwesome.Solid.ChevronRight, + Size = new Vector2(0.35f) }) }; } From 5a9d18380cc6844ecc462b727970deba05a2bfac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jul 2019 16:40:40 +0900 Subject: [PATCH 1852/2854] Use scale correctly in DrawableRepeatPoint --- .../Objects/Drawables/DrawableRepeatPoint.cs | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 543e5d20d5..d07b37488c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; @@ -20,28 +22,40 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private double animDuration; + private readonly SkinnableDrawable scaleContainer; + public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider) : base(repeatPoint) { this.repeatPoint = repeatPoint; this.drawableSlider = drawableSlider; - Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2 * repeatPoint.Scale); + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); Blending = BlendingMode.Additive; Origin = Anchor.Centre; - InternalChildren = new Drawable[] + InternalChild = scaleContainer = new SkinnableDrawable("Play/osu/reversearrow", _ => new SpriteIcon { - new SkinnableDrawable("Play/osu/reversearrow", _ => new SpriteIcon - { - RelativeSizeAxes = Axes.Both, - Icon = FontAwesome.Solid.ChevronRight, - Size = new Vector2(0.35f) - }) + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.Solid.ChevronRight, + Size = new Vector2(0.35f) + }) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, }; } + private readonly IBindable scaleBindable = new Bindable(); + + [BackgroundDependencyLoader] + private void load() + { + scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true); + scaleBindable.BindTo(HitObject.ScaleBindable); + } + protected override void CheckForResult(bool userTriggered, double timeOffset) { if (repeatPoint.StartTime <= Time.Current) From 2cb3619b548ead0a1422017d246840843dc8eafc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Jul 2019 13:27:41 +0900 Subject: [PATCH 1853/2854] Allow scaling outside of defined area Caters to skins which show borders outside of the circle for repeats. --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index d07b37488c..f75b62eecf 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables RelativeSizeAxes = Axes.Both, Icon = FontAwesome.Solid.ChevronRight, Size = new Vector2(0.35f) - }) + }, confineMode: ConfineMode.NoScaling) { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 5e153a3dd39364ff742505f5322ef83900015de3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Jul 2019 13:42:29 +0900 Subject: [PATCH 1854/2854] Use scale correctly in DrawableSliderTick --- .../Objects/Drawables/DrawableSliderTick.cs | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index c8062b67b5..653e73ac3f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osuTK; @@ -16,36 +18,49 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public const double ANIM_DURATION = 150; + private const float default_tick_size = 16; + public bool Tracking { get; set; } public override bool DisplayResult => false; + private readonly SkinnableDrawable scaleContainer; + public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) { - Size = new Vector2(16 * sliderTick.Scale); + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); Origin = Anchor.Centre; - InternalChildren = new Drawable[] + InternalChild = scaleContainer = new SkinnableDrawable("Play/osu/sliderscorepoint", _ => new CircularContainer { - new SkinnableDrawable("Play/osu/sliderscorepoint", _ => new Container + Masking = true, + Origin = Anchor.Centre, + Size = new Vector2(default_tick_size), + BorderThickness = default_tick_size / 4, + BorderColour = Color4.White, + Child = new Box { - Masking = true, RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - CornerRadius = Size.X / 2, - BorderThickness = 2, - BorderColour = Color4.White, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = AccentColour.Value, - Alpha = 0.3f, - } - }) + Colour = AccentColour.Value, + Alpha = 0.3f, + } + }) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, }; } + private readonly IBindable scaleBindable = new Bindable(); + + [BackgroundDependencyLoader] + private void load() + { + scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true); + scaleBindable.BindTo(HitObject.ScaleBindable); + } + protected override void CheckForResult(bool userTriggered, double timeOffset) { if (timeOffset >= 0) From a290437286e1c55e34efea34bc5b0327271c71a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jul 2019 12:34:40 +0900 Subject: [PATCH 1855/2854] Fix skin changed events triggering after disposal --- osu.Game/Skinning/SkinReloadableDrawable.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs index c09d5b1f92..440645bee7 100644 --- a/osu.Game/Skinning/SkinReloadableDrawable.cs +++ b/osu.Game/Skinning/SkinReloadableDrawable.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -36,12 +36,14 @@ namespace osu.Game.Skinning skin.SourceChanged += onChange; } - private void onChange() => SkinChanged(skin, allowDefaultFallback); + private void onChange() => + // schedule required to avoid calls after disposed. + Schedule(() => SkinChanged(skin, allowDefaultFallback)); protected override void LoadAsyncComplete() { base.LoadAsyncComplete(); - onChange(); + SkinChanged(skin, allowDefaultFallback); } /// From 9473f6d3e3b83970e4b785629f46080fa55cb440 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Jul 2019 11:45:33 +0900 Subject: [PATCH 1856/2854] Fix incorrect ratios being applied to playfield / skin elements This now matches osu-stable 1:1. --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 3 +-- .../UI/OsuPlayfieldAdjustmentContainer.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 15 +++++++++++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index b3492a2b31..ea81527fa9 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -101,11 +101,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor }, }, } - }) + }, confineMode: ConfineMode.NoScaling) { Origin = Anchor.Centre, Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, } }; diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs index e28ff5f460..8b6b483618 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.UI Anchor = Anchor.Centre; Origin = Anchor.Centre; - Size = new Vector2(0.75f); + Size = new Vector2(0.8f); InternalChild = new Container { diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 513a024a36..7b6cd613ec 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -95,7 +95,6 @@ namespace osu.Game.Skinning public override Texture GetTexture(string componentName) { float ratio = 2; - var texture = Textures.Get($"{componentName}@2x"); if (texture == null) @@ -105,7 +104,19 @@ namespace osu.Game.Skinning } if (texture != null) - texture.ScaleAdjust = ratio / 0.72f; // brings sizing roughly in-line with stable + { + texture.ScaleAdjust = ratio; + + switch (componentName) + { + case "cursormiddle": + case "cursortrail": + case "cursor": + // apply inverse of adjustment in OsuPlayfieldAdjustmentContainer for non-gameplay-scale textures. + texture.ScaleAdjust *= 1.6f; + break; + } + } return texture; } From 46e17646acee682392c3b576b13aa97ca47b2978 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Jul 2019 18:49:52 +0900 Subject: [PATCH 1857/2854] Align slider path size with legacy skins --- .../Objects/Drawables/DrawableSlider.cs | 7 +++---- osu.Game/Skinning/LegacySkin.cs | 17 +++++++++++++++++ osu.Game/Skinning/SkinConfiguration.cs | 2 ++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 4a6bd45007..dc05f3de58 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -48,10 +48,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables InternalChildren = new Drawable[] { - Body = new SnakingSliderBody(s) - { - PathRadius = s.Scale * OsuHitObject.OBJECT_RADIUS, - }, + Body = new SnakingSliderBody(s), ticks = new Container { RelativeSizeAxes = Axes.Both }, repeatPoints = new Container { RelativeSizeAxes = Axes.Both }, Ball = new SliderBall(s, this) @@ -162,6 +159,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables base.SkinChanged(skin, allowFallback); Body.BorderSize = skin.GetValue(s => s.SliderBorderSize) ?? SliderBody.DEFAULT_BORDER_SIZE; + Body.PathRadius = slider.Scale * (skin.GetValue(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS); + Body.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? AccentColour.Value; Body.BorderColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Color4.White; Ball.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? AccentColour.Value; diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 7b6cd613ec..af9a24df42 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -24,6 +24,13 @@ namespace osu.Game.Skinning protected IResourceStore Samples; + /// + /// On osu-stable, hitcircles have 5 pixels of transparent padding on each side to allow for shadows etc. + /// Their hittable area is 128px, but the actual circle portion is 118px. + /// We must account for some gameplay elements such as slider bodies, where this padding is not present. + /// + private const float legacy_circle_radius = 64 - 5; + public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) : this(skin, new LegacySkinResourceStore(skin, storage), audioManager, "skin.ini") { @@ -41,6 +48,16 @@ namespace osu.Game.Skinning Samples = audioManager.GetSampleStore(storage); Textures = new TextureStore(new TextureLoaderStore(storage)); + + bool hasHitCircle = false; + + using (var testStream = storage.GetStream("hitcircle")) + hasHitCircle |= testStream != null; + + if (hasHitCircle) + { + Configuration.SliderPathRadius = legacy_circle_radius; + } } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs index 043622f8ce..93b599f9f6 100644 --- a/osu.Game/Skinning/SkinConfiguration.cs +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -27,6 +27,8 @@ namespace osu.Game.Skinning public float? SliderBorderSize { get; set; } + public float? SliderPathRadius { get; set; } + public bool? CursorExpand { get; set; } = true; } } From 5e8867066c3e395c9b6b646fdd4d539cd09de635 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Jul 2019 14:43:44 +0900 Subject: [PATCH 1858/2854] Correctly handling bindable scale changes --- .../Objects/Drawables/DrawableSlider.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index dc05f3de58..020a64a26b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); scaleBindable.BindValueChanged(scale => { - Body.PathRadius = scale.NewValue * 64; + updatePathRadius(); Ball.Scale = new Vector2(scale.NewValue); }); @@ -154,18 +154,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Body.RecyclePath(); } + private float sliderPathRadius; + protected override void SkinChanged(ISkinSource skin, bool allowFallback) { base.SkinChanged(skin, allowFallback); Body.BorderSize = skin.GetValue(s => s.SliderBorderSize) ?? SliderBody.DEFAULT_BORDER_SIZE; - Body.PathRadius = slider.Scale * (skin.GetValue(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS); + sliderPathRadius = skin.GetValue(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS; + updatePathRadius(); Body.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? AccentColour.Value; Body.BorderColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Color4.White; Ball.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? AccentColour.Value; } + private void updatePathRadius() => Body.PathRadius = slider.Scale * sliderPathRadius; + protected override void CheckForResult(bool userTriggered, double timeOffset) { if (userTriggered || Time.Current < slider.EndTime) From c4bed0e6d29944cb6045267a3a4fdd17cf40bf34 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 25 Jul 2019 15:31:21 +0900 Subject: [PATCH 1859/2854] Resize BeatmapCarousel, update carouselitem logic --- osu.Game/Screens/Select/BeatmapCarousel.cs | 21 +++++++++------- osu.Game/Screens/Select/FilterControl.cs | 6 ----- osu.Game/Screens/Select/SongSelect.cs | 28 +++++++++++++++------- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 16354534f4..938ae1c028 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -108,10 +108,12 @@ namespace osu.Game.Screens.Select root = new CarouselRoot(this); Child = new OsuContextMenuContainer { + Masking = false, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Child = scrollableContent = new Container { + Masking = false, RelativeSizeAxes = Axes.X, } }; @@ -424,7 +426,9 @@ namespace osu.Game.Screens.Select if (!scrollPositionCache.IsValid) updateScrollPosition(); - float drawHeight = DrawHeight; + // The draw positions of individual sets extend beyond the size of the carousel and into its parent + // As a result, this should use the Parent's draw height instead. + float drawHeight = Parent.DrawHeight; // Remove all items that should no longer be on-screen scrollableContent.RemoveAll(p => p.Y < Current - p.DrawHeight || p.Y > Current + drawHeight || !p.IsPresent); @@ -432,9 +436,9 @@ namespace osu.Game.Screens.Select // Find index range of all items that should be on-screen Trace.Assert(Items.Count == yPositions.Count); - int firstIndex = yPositions.BinarySearch(Current - DrawableCarouselItem.MAX_HEIGHT); + int firstIndex = yPositions.BinarySearch(Current - DrawableCarouselItem.MAX_HEIGHT - Parent.Padding.Top); if (firstIndex < 0) firstIndex = ~firstIndex; - int lastIndex = yPositions.BinarySearch(Current + drawHeight); + int lastIndex = yPositions.BinarySearch(Current + drawHeight + Parent.Padding.Bottom); if (lastIndex < 0) lastIndex = ~lastIndex; int notVisibleCount = 0; @@ -637,18 +641,19 @@ namespace osu.Game.Screens.Select /// the current scroll position. /// /// The item to be updated. - /// Half the draw height of the carousel container. - private void updateItem(DrawableCarouselItem p, float halfHeight) + /// Half the draw height of the carousel container's parent. + private void updateItem(DrawableCarouselItem p, float parentHalfHeight) { var height = p.IsPresent ? p.DrawHeight : 0; - float itemDrawY = p.Position.Y - Current + height / 2; - float dist = Math.Abs(1f - itemDrawY / halfHeight); + // The actual Y position of the item needs to be offset by any potential padding set by the container's parent. + float itemDrawY = p.Position.Y - Current + Parent.Padding.Top + height / 2; + float dist = Math.Abs(1f - itemDrawY / parentHalfHeight); // Setting the origin position serves as an additive position on top of potential // local transformation we may want to apply (e.g. when a item gets selected, we // may want to smoothly transform it leftwards.) - p.OriginPosition = new Vector2(-offsetX(dist, halfHeight), 0); + p.OriginPosition = new Vector2(-offsetX(dist, parentHalfHeight), 0); // We are applying a multiplicative alpha (which is internally done by nesting an // additional container and setting that container's alpha) such that we can diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 57fe15fd99..97ba1cce6b 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -187,11 +187,5 @@ namespace osu.Game.Screens.Select } private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria()); - - protected override bool OnMouseDown(MouseDownEvent e) => true; - - protected override bool OnMouseMove(MouseMoveEvent e) => true; - - protected override bool OnClick(ClickEvent e) => true; } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 20dbcf2693..011c62f0eb 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -45,6 +45,7 @@ namespace osu.Game.Screens.Select protected const float BACKGROUND_BLUR = 20; private const float left_area_padding = 20; + private const float filter_control_height = 100; public readonly FilterControl FilterControl; @@ -121,7 +122,7 @@ namespace osu.Game.Screens.Select Size = new Vector2(wedged_container_size.X, 1), Padding = new MarginPadding { - Bottom = 50, + Bottom = Footer.HEIGHT, Top = wedged_container_size.Y + left_area_padding, Left = left_area_padding, Right = left_area_padding * 2, @@ -147,20 +148,29 @@ namespace osu.Game.Screens.Select Width = 0.5f, Children = new Drawable[] { - Carousel = new BeatmapCarousel + new Container { - Masking = false, RelativeSizeAxes = Axes.Both, - Size = new Vector2(1 - wedged_container_size.X, 1), - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - SelectionChanged = updateSelectedBeatmap, - BeatmapSetsChanged = carouselBeatmapsLoaded, + Padding = new MarginPadding + { + Top = filter_control_height, + Bottom = Footer.HEIGHT + }, + Child = Carousel = new BeatmapCarousel + { + Masking = false, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(1 - wedged_container_size.X, 1), + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + SelectionChanged = updateSelectedBeatmap, + BeatmapSetsChanged = carouselBeatmapsLoaded, + }, }, FilterControl = new FilterControl { RelativeSizeAxes = Axes.X, - Height = 100, + Height = filter_control_height, FilterChanged = c => Carousel.Filter(c), Background = { Width = 2 }, Exit = () => From 58feba72a30824e7d5624dbf20bf6a0e2acc0666 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Jul 2019 15:59:44 +0900 Subject: [PATCH 1860/2854] Fix scheduled events not running on previous drawables --- osu.Game/Skinning/SkinReloadableDrawable.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs index 440645bee7..397c6656a5 100644 --- a/osu.Game/Skinning/SkinReloadableDrawable.cs +++ b/osu.Game/Skinning/SkinReloadableDrawable.cs @@ -38,7 +38,8 @@ namespace osu.Game.Skinning private void onChange() => // schedule required to avoid calls after disposed. - Schedule(() => SkinChanged(skin, allowDefaultFallback)); + // note that this has the side-effect of components only performance a skin change when they are alive. + Scheduler.AddOnce(() => SkinChanged(skin, allowDefaultFallback)); protected override void LoadAsyncComplete() { From eb6bda3f0853cf2fa3c11f3f0787e1c1ffeb06d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Jul 2019 19:12:41 +0900 Subject: [PATCH 1861/2854] Cache DrawableHitObject for skinnables to access --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 1d9d885527..c1cb1a7a33 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -17,6 +17,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Objects.Drawables { + [Cached(typeof(DrawableHitObject))] public abstract class DrawableHitObject : SkinReloadableDrawable { public readonly HitObject HitObject; From 859233526d4ad1f3173e0b6d4a5c33425cbfc45b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Jul 2019 18:50:57 +0900 Subject: [PATCH 1862/2854] Move circle visual implementation to new class Allows for more precise skin control over state animations. --- .../Objects/Drawables/DrawableHitCircle.cs | 127 +++++++++--------- .../Objects/Drawables/DrawableOsuHitObject.cs | 2 +- .../Objects/Drawables/Pieces/CirclePiece.cs | 31 +---- .../Drawables/Pieces/MainCirclePiece.cs | 94 +++++++++++++ 4 files changed, 157 insertions(+), 97 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 001003155d..897ab0a17b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -6,33 +6,29 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osuTK; using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach { public ApproachCircle ApproachCircle; - private readonly CirclePiece circle; - private readonly RingPiece ring; - private readonly FlashPiece flash; - private readonly ExplodePiece explode; - private readonly NumberPiece number; - private readonly GlowPiece glow; private readonly IBindable positionBindable = new Bindable(); private readonly IBindable stackHeightBindable = new Bindable(); private readonly IBindable scaleBindable = new Bindable(); - public OsuAction? HitAction => circle.HitAction; - - private readonly Container explodeContainer; + public OsuAction? HitAction => hitArea.HitAction; private readonly Container scaleContainer; + private readonly HitArea hitArea; + public DrawableHitCircle(HitCircle h) : base(h) { @@ -47,44 +43,30 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, Anchor = Anchor.Centre, - Child = explodeContainer = new Container + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Children = new Drawable[] + hitArea = new HitArea { - glow = new GlowPiece(), - circle = new CirclePiece + Hit = () => { - Hit = () => - { - if (AllJudged) - return false; + if (AllJudged) + return false; - UpdateResult(true); - return true; - }, + UpdateResult(true); + return true; }, - number = new NumberPiece - { - Text = (HitObject.IndexInCurrentCombo + 1).ToString(), - }, - ring = new RingPiece(), - flash = new FlashPiece(), - explode = new ExplodePiece(), - ApproachCircle = new ApproachCircle - { - Alpha = 0, - Scale = new Vector2(4), - } + }, + new SkinnableDrawable("Play/Osu/Objects/Drawables/MainCircle", _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)), + ApproachCircle = new ApproachCircle + { + Alpha = 0, + Scale = new Vector2(4), } } }, }; - //may not be so correct - Size = circle.DrawSize; + Size = hitArea.DrawSize; } [BackgroundDependencyLoader] @@ -98,13 +80,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables stackHeightBindable.BindTo(HitObject.StackHeightBindable); scaleBindable.BindTo(HitObject.ScaleBindable); - AccentColour.BindValueChanged(colour => - { - explode.Colour = colour.NewValue; - glow.Colour = colour.NewValue; - circle.Colour = colour.NewValue; - ApproachCircle.Colour = colour.NewValue; - }, true); + AccentColour.BindValueChanged(accent => ApproachCircle.Colour = accent.NewValue, true); } protected override void CheckForResult(bool userTriggered, double timeOffset) @@ -139,8 +115,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void UpdateStateTransforms(ArmedState state) { - glow.FadeOut(400); - switch (state) { case ArmedState.Idle: @@ -148,7 +122,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Expire(true); - circle.HitAction = null; + hitArea.HitAction = null; // override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early. LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.HalfWindowFor(HitResult.Miss); @@ -163,29 +137,50 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables case ArmedState.Hit: ApproachCircle.FadeOut(50); - const double flash_in = 40; - flash.FadeTo(0.8f, flash_in) - .Then() - .FadeOut(100); - - explode.FadeIn(flash_in); - explodeContainer.ScaleTo(1.5f, 400, Easing.OutQuad); - - using (BeginDelayedSequence(flash_in, true)) - { - //after the flash, we can hide some elements that were behind it - ring.FadeOut(); - circle.FadeOut(); - number.FadeOut(); - - this.FadeOut(800); - } - - Expire(); + // todo: temporary / arbitrary + this.Delay(800).Expire(); break; } } public Drawable ProxiedLayer => ApproachCircle; + + private class HitArea : Drawable, IKeyBindingHandler + { + // IsHovered is used + public override bool HandlePositionalInput => true; + + public Func Hit; + + public OsuAction? HitAction; + + public HitArea() + { + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + + public bool OnPressed(OsuAction action) + { + switch (action) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + if (IsHovered && (Hit?.Invoke() ?? false)) + { + HitAction = action; + return true; + } + + break; + } + + return false; + } + + public bool OnReleased(OsuAction action) => false; + } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 579f16e0d4..a89fb8b682 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Drawables; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index dc0b149140..acbf5ba9ae 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -1,24 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.Bindings; using osu.Game.Skinning; using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { - public class CirclePiece : Container, IKeyBindingHandler + public class CirclePiece : CompositeDrawable { - // IsHovered is used - public override bool HandlePositionalInput => true; - - public Func Hit; - - public OsuAction? HitAction; - public CirclePiece() { Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); @@ -30,25 +21,5 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces InternalChild = new SkinnableDrawable("Play/osu/hitcircle", _ => new DefaultCirclePiece()); } - - public bool OnPressed(OsuAction action) - { - switch (action) - { - case OsuAction.LeftButton: - case OsuAction.RightButton: - if (IsHovered && (Hit?.Invoke() ?? false)) - { - HitAction = action; - return true; - } - - break; - } - - return false; - } - - public bool OnReleased(OsuAction action) => false; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs new file mode 100644 index 0000000000..944c93bb6d --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs @@ -0,0 +1,94 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects.Drawables; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class MainCirclePiece : CompositeDrawable + { + private readonly CirclePiece circle; + private readonly RingPiece ring; + private readonly FlashPiece flash; + private readonly ExplodePiece explode; + private readonly NumberPiece number; + private readonly GlowPiece glow; + + public MainCirclePiece(int index) + { + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + InternalChildren = new Drawable[] + { + glow = new GlowPiece(), + circle = new CirclePiece(), + number = new NumberPiece + { + Text = (index + 1).ToString(), + }, + ring = new RingPiece(), + flash = new FlashPiece(), + explode = new ExplodePiece(), + }; + } + + private readonly IBindable state = new Bindable(); + + private readonly Bindable accentColour = new Bindable(); + + [BackgroundDependencyLoader] + private void load(DrawableHitObject drawableObject) + { + state.BindTo(drawableObject.State); + state.BindValueChanged(updateState, true); + + accentColour.BindTo(drawableObject.AccentColour); + accentColour.BindValueChanged(colour => + { + explode.Colour = colour.NewValue; + glow.Colour = colour.NewValue; + circle.Colour = colour.NewValue; + }, true); + } + + private void updateState(ValueChangedEvent state) + { + glow.FadeOut(400); + + switch (state.NewValue) + { + case ArmedState.Hit: + const double flash_in = 40; + const double flash_out = 100; + + flash.FadeTo(0.8f, flash_in) + .Then() + .FadeOut(flash_out); + + explode.FadeIn(flash_in); + this.ScaleTo(1.5f, 400, Easing.OutQuad); + + using (BeginDelayedSequence(flash_in, true)) + { + //after the flash, we can hide some elements that were behind it + ring.FadeOut(); + circle.FadeOut(); + number.FadeOut(); + + this.FadeOut(800); + } + + break; + } + } + } +} From 38e21caa5a50296697d200138c492628b3c3ed15 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jul 2019 12:34:54 +0900 Subject: [PATCH 1863/2854] Add legacy hitcircle --- osu.Game/Skinning/LegacySkin.cs | 75 ++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index af9a24df42..0a811a9514 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -6,15 +6,22 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Game.Database; +using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; using osuTK; +using osuTK.Graphics; namespace osu.Game.Skinning { @@ -36,6 +43,8 @@ namespace osu.Game.Skinning { } + private readonly bool hasHitCircle; + protected LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager, string filename) : base(skin) { @@ -49,8 +58,6 @@ namespace osu.Game.Skinning Samples = audioManager.GetSampleStore(storage); Textures = new TextureStore(new TextureLoaderStore(storage)); - bool hasHitCircle = false; - using (var testStream = storage.GetStream("hitcircle")) hasHitCircle |= testStream != null; @@ -71,6 +78,12 @@ namespace osu.Game.Skinning { switch (componentName) { + case "Play/Osu/Objects/Drawables/MainCircle": + if (!hasHitCircle) + return null; + + return new LegacyMainCirclePiece(); + case "Play/Miss": componentName = "hit0"; break; @@ -243,5 +256,63 @@ namespace osu.Game.Skinning return texture; } } + + public class LegacyMainCirclePiece : CompositeDrawable + { + public LegacyMainCirclePiece() + { + Size = new Vector2(128); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + + private readonly IBindable state = new Bindable(); + + private readonly Bindable accentColour = new Bindable(); + + [BackgroundDependencyLoader] + private void load(DrawableHitObject drawableObject, ISkinSource skin) + { + Sprite mainCircle; + + InternalChildren = new Drawable[] + { + mainCircle = new Sprite + { + Texture = skin.GetTexture("hitcircle"), + Colour = drawableObject.AccentColour.Value + }, + new SkinnableSpriteText("Play/osu/number-text", _ => new OsuSpriteText + { + Font = OsuFont.Numeric.With(size: 40), + UseFullGlyphHeight = false, + }, confineMode: ConfineMode.NoScaling) + { + Text = (((IHasComboInformation)drawableObject.HitObject).IndexInCurrentCombo + 1).ToString() + }, + new Sprite { Texture = skin.GetTexture("hitcircleoverlay") } + }; + + state.BindTo(drawableObject.State); + state.BindValueChanged(updateState, true); + + accentColour.BindTo(drawableObject.AccentColour); + accentColour.BindValueChanged(colour => mainCircle.Colour = colour.NewValue, true); + } + + private void updateState(ValueChangedEvent state) + { + const double legacy_fade_duration = 240; + + switch (state.NewValue) + { + case ArmedState.Hit: + this.FadeOut(legacy_fade_duration, Easing.Out); + this.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + break; + } + } + } } } From c1b01308579e057a9748df8f66583f5fe89b3cc4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Jul 2019 16:17:02 +0900 Subject: [PATCH 1864/2854] Add legacy cursormiddle support --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 133 +++++++++++-------- osu.Game/Skinning/LegacySkin.cs | 39 ++++++ 2 files changed, 115 insertions(+), 57 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index ea81527fa9..37ac972f41 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { public class OsuCursor : SkinReloadableDrawable { + private const float size = 28; + private bool cursorExpand; private Bindable cursorScale; @@ -30,7 +32,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor public OsuCursor() { Origin = Anchor.Centre; - Size = new Vector2(28); + + Size = new Vector2(size); } protected override void SkinChanged(ISkinSource skin, bool allowFallback) @@ -46,62 +49,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, Anchor = Anchor.Centre, - Child = scaleTarget = new SkinnableDrawable("cursor", _ => new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderThickness = Size.X / 6, - BorderColour = Color4.White, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Pink.Opacity(0.5f), - Radius = 5, - }, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true, - }, - new CircularContainer - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderThickness = Size.X / 3, - BorderColour = Color4.White.Opacity(0.5f), - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true, - }, - }, - }, - new CircularContainer - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Scale = new Vector2(0.1f), - Masking = true, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, - }, - }, - }, - } - }, confineMode: ConfineMode.NoScaling) + Child = scaleTarget = new SkinnableDrawable("osu/Game/Rulesets/Osu/UI/Cursor/OsuCursor", _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling) { Origin = Anchor.Centre, Anchor = Anchor.Centre, @@ -144,5 +92,76 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor } public void Contract() => expandTarget.ScaleTo(released_scale, 100, Easing.OutQuad); + + private class DefaultCursor : CompositeDrawable + { + public DefaultCursor() + { + RelativeSizeAxes = Axes.Both; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + InternalChildren = new Drawable[] + { + new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = size / 6, + BorderColour = Color4.White, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Pink.Opacity(0.5f), + Radius = 5, + }, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true, + }, + new CircularContainer + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = size / 3, + BorderColour = Color4.White.Opacity(0.5f), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true, + }, + }, + }, + new CircularContainer + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Scale = new Vector2(0.1f), + Masking = true, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + }, + }, + } + } + }; + } + } } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index af9a24df42..905541a592 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -6,9 +6,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; @@ -71,6 +73,12 @@ namespace osu.Game.Skinning { switch (componentName) { + case "osu/Game/Rulesets/Osu/UI/Cursor/OsuCursor": + if (GetTexture("cursor") != null) + return new LegacyCursor(); + + break; + case "Play/Miss": componentName = "hit0"; break; @@ -243,5 +251,36 @@ namespace osu.Game.Skinning return texture; } } + + public class LegacyCursor : CompositeDrawable + { + public LegacyCursor() + { + Size = new Vector2(50); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + InternalChildren = new Drawable[] + { + new Sprite + { + Texture = skin.GetTexture("cursormiddle"), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new Sprite + { + Texture = skin.GetTexture("cursor"), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }; + } + } } } From bfaf9d5f419aef2cd84031e0e0c00aeffc3acf5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Jul 2019 16:24:10 +0900 Subject: [PATCH 1865/2854] Remove unnecessary special cases --- osu.Game/Skinning/LegacySkin.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 905541a592..48888191e9 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -129,20 +129,8 @@ namespace osu.Game.Skinning } if (texture != null) - { texture.ScaleAdjust = ratio; - switch (componentName) - { - case "cursormiddle": - case "cursortrail": - case "cursor": - // apply inverse of adjustment in OsuPlayfieldAdjustmentContainer for non-gameplay-scale textures. - texture.ScaleAdjust *= 1.6f; - break; - } - } - return texture; } From 6cc6aff66e94de5d3a387639f0aeaac5230a02c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Jul 2019 15:35:51 +0900 Subject: [PATCH 1866/2854] Fix slider ball sizing when legacy skin falls back to default --- .../Objects/Drawables/DrawableSlider.cs | 2 - .../Objects/Drawables/Pieces/SliderBall.cs | 130 ++++++++++-------- 2 files changed, 76 insertions(+), 56 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 020a64a26b..a0626707af 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -115,7 +115,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AccentColour.BindValueChanged(colour => { Body.AccentColour = colour.NewValue; - Ball.AccentColour = colour.NewValue; foreach (var drawableHitObject in NestedHitObjects) drawableHitObject.AccentColour.Value = colour.NewValue; @@ -166,7 +165,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Body.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? AccentColour.Value; Body.BorderColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Color4.White; - Ball.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? AccentColour.Value; } private void updatePathRadius() => Body.PathRadius = slider.Scale * sliderPathRadius; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 9ba8ad3474..119005eb40 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -3,11 +3,14 @@ using System; using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Framework.Input.Events; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osuTK.Graphics; using osu.Game.Skinning; @@ -17,88 +20,44 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class SliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition { - private Color4 accentColour = Color4.Black; - public Func GetInitialHitAction; - /// - /// The colour that is used for the slider ball. - /// - public Color4 AccentColour - { - get => accentColour; - set - { - accentColour = value; - if (drawableBall != null) - drawableBall.Colour = value; - } - } - private readonly Slider slider; public readonly Drawable FollowCircle; - private Drawable drawableBall; private readonly DrawableSlider drawableSlider; public SliderBall(Slider slider, DrawableSlider drawableSlider = null) { this.drawableSlider = drawableSlider; this.slider = slider; - Masking = true; - AutoSizeAxes = Axes.Both; + Blending = BlendingMode.Additive; Origin = Anchor.Centre; + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); + Children = new[] { - FollowCircle = new Container + FollowCircle = new FollowCircleContainer { Origin = Anchor.Centre, Anchor = Anchor.Centre, - Width = OsuHitObject.OBJECT_RADIUS * 2, - Height = OsuHitObject.OBJECT_RADIUS * 2, + RelativeSizeAxes = Axes.Both, Alpha = 0, - Child = new SkinnableDrawable("Play/osu/sliderfollowcircle", _ => new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderThickness = 5, - BorderColour = Color4.Orange, - Blending = BlendingMode.Additive, - Child = new Box - { - Colour = Color4.Orange, - RelativeSizeAxes = Axes.Both, - Alpha = 0.2f, - } - }), + Child = new SkinnableDrawable("Play/osu/sliderfollowcircle", _ => new DefaultFollowCircle()), }, new CircularContainer { Masking = true, - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, Anchor = Anchor.Centre, Alpha = 1, Child = new Container { - Width = OsuHitObject.OBJECT_RADIUS * 2, - Height = OsuHitObject.OBJECT_RADIUS * 2, + RelativeSizeAxes = Axes.Both, // TODO: support skin filename animation (sliderb0, sliderb1...) - Child = new SkinnableDrawable("Play/osu/sliderb", _ => new CircularContainer - { - Masking = true, - RelativeSizeAxes = Axes.Both, - BorderThickness = 10, - BorderColour = Color4.White, - Alpha = 1, - Child = drawableBall = new Box - { - Colour = AccentColour, - RelativeSizeAxes = Axes.Both, - Alpha = 0.4f, - } - }), + Child = new SkinnableDrawable("Play/osu/sliderb", _ => new DefaultSliderBall()), } } }; @@ -191,7 +150,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces // in valid time range Time.Current >= slider.StartTime && Time.Current < slider.EndTime && // in valid position range - lastScreenSpaceMousePosition.HasValue && base.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) && + lastScreenSpaceMousePosition.HasValue && FollowCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) && // valid action (actions?.Any(isValidTrackingAction) ?? false); } @@ -214,5 +173,68 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { Position = slider.CurvePositionAt(completionProgress); } + + private class FollowCircleContainer : Container + { + public override bool HandlePositionalInput => true; + } + + public class DefaultFollowCircle : CompositeDrawable + { + public DefaultFollowCircle() + { + RelativeSizeAxes = Axes.Both; + + InternalChild = new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = 5, + BorderColour = Color4.Orange, + Blending = BlendingMode.Additive, + Child = new Box + { + Colour = Color4.Orange, + RelativeSizeAxes = Axes.Both, + Alpha = 0.2f, + } + }; + } + } + + public class DefaultSliderBall : CompositeDrawable + { + private readonly Bindable accentColour = new Bindable(); + + private Drawable drawableBall; + + [BackgroundDependencyLoader] + private void load(DrawableHitObject drawableObject, ISkinSource skin) + { + RelativeSizeAxes = Axes.Both; + + float radius = skin.GetValue(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS; + + InternalChild = new CircularContainer + { + Masking = true, + RelativeSizeAxes = Axes.Both, + Scale = new Vector2(radius / OsuHitObject.OBJECT_RADIUS), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + BorderThickness = 10, + BorderColour = Color4.White, + Alpha = 1, + Child = drawableBall = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.4f, + } + }; + + accentColour.BindTo(drawableObject.AccentColour); + accentColour.BindValueChanged(colour => drawableBall.Colour = colour.NewValue, true); + } + } } } From 7b82d184bd48dd3130552596ff1115a58986c651 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 25 Jul 2019 11:07:53 +0300 Subject: [PATCH 1867/2854] Add bindable boolean for break times --- osu.Game/Screens/Play/BreakOverlay.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index d390787090..0941b8a452 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -35,6 +37,11 @@ namespace osu.Game.Screens.Play public override bool RemoveCompletedTransforms => false; + /// + /// Whether we are currently in the break time range. + /// + public readonly BindableBool IsBreakTime = new BindableBool(); + private readonly Container remainingTimeAdjustmentBox; private readonly Container remainingTimeBox; private readonly RemainingTimeCounter remainingTimeCounter; @@ -109,6 +116,13 @@ namespace osu.Game.Screens.Play initializeBreaks(); } + protected override void Update() + { + base.Update(); + + IsBreakTime.Value = breaks.Where(b => b.HasEffect).Any(b => Clock.CurrentTime >= b.StartTime && Clock.CurrentTime <= b.EndTime); + } + private void initializeBreaks() { if (!IsLoaded) return; // we need a clock. From 0ea0a10ca45ee73e08efad09e55e98f17b1ee2da Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 25 Jul 2019 11:26:38 +0300 Subject: [PATCH 1868/2854] Expose as IBindable --- osu.Game/Screens/Play/BreakOverlay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 0941b8a452..593b54afb8 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -41,6 +41,9 @@ namespace osu.Game.Screens.Play /// Whether we are currently in the break time range. /// public readonly BindableBool IsBreakTime = new BindableBool(); + public IBindable IsBreakTime => isBreakTime; + + private readonly BindableBool isBreakTime = new BindableBool(); private readonly Container remainingTimeAdjustmentBox; private readonly Container remainingTimeBox; From 69d2f57f4fadc108076365780f13b96b6e8e08cc Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 25 Jul 2019 11:27:01 +0300 Subject: [PATCH 1869/2854] Avoid using LINQ --- osu.Game/Screens/Play/BreakOverlay.cs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 593b54afb8..bebbbc5b68 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -123,7 +122,24 @@ namespace osu.Game.Screens.Play { base.Update(); - IsBreakTime.Value = breaks.Where(b => b.HasEffect).Any(b => Clock.CurrentTime >= b.StartTime && Clock.CurrentTime <= b.EndTime); + updateBreakTimeBindable(); + } + + private void updateBreakTimeBindable() + { + foreach (var b in breaks) + { + if (!b.HasEffect) + continue; + + if (Clock.CurrentTime >= b.StartTime && Clock.CurrentTime <= b.EndTime) + { + isBreakTime.Value = true; + return; + } + } + + isBreakTime.Value = false; } private void initializeBreaks() From 9bd66b6e7a777f9273efd5b6c7734db909c78b2f Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 25 Jul 2019 11:27:32 +0300 Subject: [PATCH 1870/2854] Better xmldoc --- osu.Game/Screens/Play/BreakOverlay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index bebbbc5b68..5958ca77d8 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -37,9 +37,8 @@ namespace osu.Game.Screens.Play public override bool RemoveCompletedTransforms => false; /// - /// Whether we are currently in the break time range. + /// Whether the gameplay is currently in a break. /// - public readonly BindableBool IsBreakTime = new BindableBool(); public IBindable IsBreakTime => isBreakTime; private readonly BindableBool isBreakTime = new BindableBool(); From 97eb5293a87102dbf918bd4b69ca0486937abd03 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 25 Jul 2019 17:32:21 +0900 Subject: [PATCH 1871/2854] Don't depend on parent sizing --- osu.Game/Screens/Select/BeatmapCarousel.cs | 28 +++++++++++----------- osu.Game/Screens/Select/FilterControl.cs | 1 - osu.Game/Screens/Select/Footer.cs | 4 ---- osu.Game/Screens/Select/SongSelect.cs | 21 ++++++++++------ 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 938ae1c028..e4b75a3540 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -426,19 +426,18 @@ namespace osu.Game.Screens.Select if (!scrollPositionCache.IsValid) updateScrollPosition(); - // The draw positions of individual sets extend beyond the size of the carousel and into its parent - // As a result, this should use the Parent's draw height instead. - float drawHeight = Parent.DrawHeight; + // The draw positions of individual sets extend beyond the size of the carousel and into the footer and header. + float visibleHeight = DrawHeight + SongSelect.FOOTER_HEIGHT + SongSelect.FILTER_CONTROL_HEIGHT; // Remove all items that should no longer be on-screen - scrollableContent.RemoveAll(p => p.Y < Current - p.DrawHeight || p.Y > Current + drawHeight || !p.IsPresent); + scrollableContent.RemoveAll(p => p.Y < Current - p.DrawHeight || p.Y > Current + visibleHeight || !p.IsPresent); // Find index range of all items that should be on-screen Trace.Assert(Items.Count == yPositions.Count); - int firstIndex = yPositions.BinarySearch(Current - DrawableCarouselItem.MAX_HEIGHT - Parent.Padding.Top); + int firstIndex = yPositions.BinarySearch(Current - DrawableCarouselItem.MAX_HEIGHT - SongSelect.FILTER_CONTROL_HEIGHT); if (firstIndex < 0) firstIndex = ~firstIndex; - int lastIndex = yPositions.BinarySearch(Current + drawHeight + Parent.Padding.Bottom); + int lastIndex = yPositions.BinarySearch(Current + visibleHeight + SongSelect.FOOTER_HEIGHT); if (lastIndex < 0) lastIndex = ~lastIndex; int notVisibleCount = 0; @@ -490,7 +489,7 @@ namespace osu.Game.Screens.Select // Update externally controlled state of currently visible items // (e.g. x-offset and opacity). - float halfHeight = drawHeight / 2; + float halfHeight = visibleHeight / 2; foreach (DrawableCarouselItem p in scrollableContent.Children) updateItem(p, halfHeight); } @@ -546,7 +545,8 @@ namespace osu.Game.Screens.Select yPositions.Clear(); - float currentY = DrawHeight / 2; + float visibleHeight = DrawHeight + SongSelect.FOOTER_HEIGHT + SongSelect.FILTER_CONTROL_HEIGHT; + float currentY = visibleHeight / 2; DrawableCarouselBeatmapSet lastSet = null; scrollTarget = null; @@ -600,7 +600,7 @@ namespace osu.Game.Screens.Select currentY += d.DrawHeight + 5; } - currentY += DrawHeight / 2; + currentY += visibleHeight / 2; scrollableContent.Height = currentY; if (BeatmapSetsLoaded && (selectedBeatmapSet == null || selectedBeatmap == null || selectedBeatmapSet.State.Value != CarouselItemState.Selected)) @@ -641,19 +641,19 @@ namespace osu.Game.Screens.Select /// the current scroll position. /// /// The item to be updated. - /// Half the draw height of the carousel container's parent. - private void updateItem(DrawableCarouselItem p, float parentHalfHeight) + /// Half the draw height of the carousel container's parent. + private void updateItem(DrawableCarouselItem p, float halfHeight) { var height = p.IsPresent ? p.DrawHeight : 0; // The actual Y position of the item needs to be offset by any potential padding set by the container's parent. - float itemDrawY = p.Position.Y - Current + Parent.Padding.Top + height / 2; - float dist = Math.Abs(1f - itemDrawY / parentHalfHeight); + float itemDrawY = p.Position.Y + SongSelect.FILTER_CONTROL_HEIGHT - Current + height / 2; + float dist = Math.Abs(1f - itemDrawY / halfHeight); // Setting the origin position serves as an additive position on top of potential // local transformation we may want to apply (e.g. when a item gets selected, we // may want to smoothly transform it leftwards.) - p.OriginPosition = new Vector2(-offsetX(dist, parentHalfHeight), 0); + p.OriginPosition = new Vector2(-offsetX(dist, halfHeight), 0); // We are applying a multiplicative alpha (which is internally done by nesting an // additional container and setting that container's alpha) such that we can diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 97ba1cce6b..31b71a21c8 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -14,7 +14,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Select.Filter; using Container = osu.Framework.Graphics.Containers.Container; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; using osu.Game.Configuration; using osu.Game.Rulesets; diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index 0680711f1c..7db3d65a0b 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -62,10 +62,6 @@ namespace osu.Game.Screens.Select public Footer() { - RelativeSizeAxes = Axes.X; - Height = HEIGHT; - Anchor = Anchor.BottomCentre; - Origin = Anchor.BottomCentre; Children = new Drawable[] { new Box diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 011c62f0eb..e5576f78cf 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -43,9 +43,10 @@ namespace osu.Game.Screens.Select { private static readonly Vector2 wedged_container_size = new Vector2(0.5f, 245); + public const float FILTER_CONTROL_HEIGHT = 100; + public const float FOOTER_HEIGHT = 50; protected const float BACKGROUND_BLUR = 20; private const float left_area_padding = 20; - private const float filter_control_height = 100; public readonly FilterControl FilterControl; @@ -122,7 +123,7 @@ namespace osu.Game.Screens.Select Size = new Vector2(wedged_container_size.X, 1), Padding = new MarginPadding { - Bottom = Footer.HEIGHT, + Bottom = FOOTER_HEIGHT, Top = wedged_container_size.Y + left_area_padding, Left = left_area_padding, Right = left_area_padding * 2, @@ -153,8 +154,8 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { - Top = filter_control_height, - Bottom = Footer.HEIGHT + Top = FILTER_CONTROL_HEIGHT, + Bottom = FOOTER_HEIGHT }, Child = Carousel = new BeatmapCarousel { @@ -170,7 +171,7 @@ namespace osu.Game.Screens.Select FilterControl = new FilterControl { RelativeSizeAxes = Axes.X, - Height = filter_control_height, + Height = FILTER_CONTROL_HEIGHT, FilterChanged = c => Carousel.Filter(c), Background = { Width = 2 }, Exit = () => @@ -209,7 +210,7 @@ namespace osu.Game.Screens.Select Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Bottom = Footer.HEIGHT }, + Margin = new MarginPadding { Bottom = FOOTER_HEIGHT }, Children = new Drawable[] { BeatmapOptions = new BeatmapOptionsOverlay(), @@ -221,7 +222,13 @@ namespace osu.Game.Screens.Select } } }, - Footer = new Footer() + Footer = new Footer + { + RelativeSizeAxes = Axes.X, + Height = FOOTER_HEIGHT, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + } }); } From a9f0dda9d7ec20b4a0492f670c02e03be26e4a1b Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 25 Jul 2019 17:36:13 +0900 Subject: [PATCH 1872/2854] Confine positional input --- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index e4b75a3540..b917fc4bb4 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -53,6 +53,8 @@ namespace osu.Game.Screens.Select public override bool HandleNonPositionalInput => AllowSelection; public override bool HandlePositionalInput => AllowSelection; + protected override bool ConfinePositionalInput => true; + /// /// Whether carousel items have completed asynchronously loaded. /// From 172a9ce33a29a5286d23f46fc0b752e795362e12 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 25 Jul 2019 11:40:45 +0300 Subject: [PATCH 1873/2854] Use a for loop instead of foreach avoid allocating an iterator --- osu.Game/Screens/Play/BreakOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 5958ca77d8..918bb1e5c9 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -126,12 +126,12 @@ namespace osu.Game.Screens.Play private void updateBreakTimeBindable() { - foreach (var b in breaks) + for (int i = 0; i < breaks.Count; i++) { - if (!b.HasEffect) + if (!breaks[i].HasEffect) continue; - if (Clock.CurrentTime >= b.StartTime && Clock.CurrentTime <= b.EndTime) + if (Clock.CurrentTime >= breaks[i].StartTime && Clock.CurrentTime <= breaks[i].EndTime) { isBreakTime.Value = true; return; From 5a55433d6c7e2b76e8b7ceabd732374e0573efc1 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 25 Jul 2019 11:53:32 +0300 Subject: [PATCH 1874/2854] Return if breaks are null Fixes a test --- osu.Game/Screens/Play/BreakOverlay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 918bb1e5c9..8b9431a87c 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -126,6 +126,9 @@ namespace osu.Game.Screens.Play private void updateBreakTimeBindable() { + if (breaks == null) + return; + for (int i = 0; i < breaks.Count; i++) { if (!breaks[i].HasEffect) From d4f85af19c9745d49dadfbd77fe529c5248b6f83 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 25 Jul 2019 18:22:56 +0900 Subject: [PATCH 1875/2854] Force snaking slider paths to retain a fixed size --- .../Objects/Drawables/Pieces/SliderBody.cs | 17 +++++++++-------- .../Drawables/Pieces/SnakingSliderBody.cs | 19 ++++++++++++++++++- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 97c7c9cec5..6bc19ee3b5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Lines; @@ -75,22 +76,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces protected SliderBody() { - InternalChild = path = new SliderPath(); + RecyclePath(); } /// /// Initialises a new , releasing all resources retained by the old one. /// - public void RecyclePath() + public virtual void RecyclePath() { InternalChild = path = new SliderPath { - Position = path.Position, - PathRadius = path.PathRadius, - AccentColour = path.AccentColour, - BorderColour = path.BorderColour, - BorderSize = path.BorderSize, - Vertices = path.Vertices + Position = path?.Position ?? Vector2.Zero, + PathRadius = path?.PathRadius ?? 10, + AccentColour = path?.AccentColour ?? Color4.White, + BorderColour = path?.BorderColour ?? Color4.White, + BorderSize = path?.BorderSize ?? DEFAULT_BORDER_SIZE, + Vertices = path?.Vertices ?? Array.Empty() }; } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs index 73b184bffe..a3d3893c8b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Types; using osuTK; @@ -78,9 +79,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces slider.Path.GetPathToProgress(CurrentCurve, 0, 1); SetVertices(CurrentCurve); - // The body is sized to the full path size to avoid excessive autosize computations + // Force the body to be the final path size to avoid excessive autosize computations + Path.AutoSizeAxes = Axes.Both; Size = Path.Size; + updatePathSize(); + snakedPosition = Path.PositionInBoundingBox(Vector2.Zero); snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]); @@ -93,6 +97,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces setRange(lastSnakedStart, lastSnakedEnd); } + public override void RecyclePath() + { + base.RecyclePath(); + updatePathSize(); + } + + private void updatePathSize() + { + // Force the path to its final size to avoid excessive framebuffer resizes + Path.AutoSizeAxes = Axes.None; + Path.Size = Size; + } + private void setRange(double p0, double p1) { if (p0 > p1) From cdda264c491c4cef4b122ece777e04413e57cbc5 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 25 Jul 2019 12:28:21 +0300 Subject: [PATCH 1876/2854] Use global index and move it to find if break time Avoid using iterations --- osu.Game/Screens/Play/BreakOverlay.cs | 38 ++++++++++++++++++--------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 8b9431a87c..0470cdb0d5 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -30,6 +31,8 @@ namespace osu.Game.Screens.Play set { breaks = value; + currentBreakIndex = 0; + initializeBreaks(); } } @@ -41,6 +44,7 @@ namespace osu.Game.Screens.Play /// public IBindable IsBreakTime => isBreakTime; + private int currentBreakIndex; private readonly BindableBool isBreakTime = new BindableBool(); private readonly Container remainingTimeAdjustmentBox; @@ -126,22 +130,30 @@ namespace osu.Game.Screens.Play private void updateBreakTimeBindable() { - if (breaks == null) - return; - - for (int i = 0; i < breaks.Count; i++) + if (breaks == null || !breaks.Any()) { - if (!breaks[i].HasEffect) - continue; - - if (Clock.CurrentTime >= breaks[i].StartTime && Clock.CurrentTime <= breaks[i].EndTime) - { - isBreakTime.Value = true; - return; - } + isBreakTime.Value = false; + return; } - isBreakTime.Value = false; + int indexDirection = Clock.CurrentTime < breaks[currentBreakIndex].StartTime ? -1 : (Clock.CurrentTime > breaks[currentBreakIndex].EndTime ? 1 : 0); + + while (Clock.CurrentTime < breaks[currentBreakIndex].StartTime || Clock.CurrentTime > breaks[currentBreakIndex].EndTime) + { + currentBreakIndex += indexDirection; + + if (currentBreakIndex < 0 || currentBreakIndex >= breaks.Count) + break; + } + + if (currentBreakIndex < 0 || currentBreakIndex >= breaks.Count) + { + isBreakTime.Value = false; + currentBreakIndex = 0; + return; + } + + isBreakTime.Value = true; } private void initializeBreaks() From 5e5101280054709321e3f528312605be91843b28 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Thu, 25 Jul 2019 22:54:05 +0930 Subject: [PATCH 1877/2854] Rewrite updateBreakTimeBindable --- .../Visual/Gameplay/TestSceneBreakOverlay.cs | 26 ++++++++++++++++- osu.Game/Screens/Play/BreakOverlay.cs | 29 +++++++------------ 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs index 3cd1b8307a..ffa822d175 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs @@ -1,8 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using NUnit.Framework; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps.Timing; using osu.Game.Screens.Play; @@ -11,11 +16,30 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneBreakOverlay : OsuTestScene { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(BreakOverlay), + }; + private readonly BreakOverlay breakOverlay; + private readonly IBindable isBreakTimeBindable = new BindableBool(); public TestSceneBreakOverlay() { - Child = breakOverlay = new BreakOverlay(true); + SpriteText breakTimeText; + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + breakTimeText = new SpriteText(), + breakOverlay = new BreakOverlay(true) + } + }; + + isBreakTimeBindable.BindTo(breakOverlay.IsBreakTime); + isBreakTimeBindable.BindValueChanged(e => breakTimeText.Text = $"IsBreakTime: {e.NewValue}", true); AddStep("2s break", () => startBreak(2000)); AddStep("5s break", () => startBreak(5000)); diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 0470cdb0d5..aceacdbab1 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -31,7 +31,7 @@ namespace osu.Game.Screens.Play set { breaks = value; - currentBreakIndex = 0; + nearestBreakIndex = 0; initializeBreaks(); } @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Play ///
public IBindable IsBreakTime => isBreakTime; - private int currentBreakIndex; + private int nearestBreakIndex; private readonly BindableBool isBreakTime = new BindableBool(); private readonly Container remainingTimeAdjustmentBox; @@ -136,24 +136,17 @@ namespace osu.Game.Screens.Play return; } - int indexDirection = Clock.CurrentTime < breaks[currentBreakIndex].StartTime ? -1 : (Clock.CurrentTime > breaks[currentBreakIndex].EndTime ? 1 : 0); + while (nearestBreakIndex < breaks.Count - 1 && Clock.CurrentTime > breaks[nearestBreakIndex].EndTime) + nearestBreakIndex++; - while (Clock.CurrentTime < breaks[currentBreakIndex].StartTime || Clock.CurrentTime > breaks[currentBreakIndex].EndTime) - { - currentBreakIndex += indexDirection; + while (nearestBreakIndex > 0 && Clock.CurrentTime < breaks[nearestBreakIndex].StartTime) + nearestBreakIndex--; - if (currentBreakIndex < 0 || currentBreakIndex >= breaks.Count) - break; - } - - if (currentBreakIndex < 0 || currentBreakIndex >= breaks.Count) - { - isBreakTime.Value = false; - currentBreakIndex = 0; - return; - } - - isBreakTime.Value = true; + // This ensures that IsBreakTime is generally consistent with the overlay's transforms during a break. + // If the overlay never shows (break.HasEffect is false), IsBreakTime should be false. + // We also assume that the overlay's fade out transform is "not break time". + var nearestBreak = breaks[nearestBreakIndex]; + isBreakTime.Value = nearestBreak.HasEffect && Clock.CurrentTime >= nearestBreak.StartTime && Clock.CurrentTime <= nearestBreak.EndTime - fade_duration; } private void initializeBreaks() From 1d6c321e14ff3c820ae2f3578f62e239cacbdd18 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Fri, 26 Jul 2019 08:34:18 +0930 Subject: [PATCH 1878/2854] Ensure we don't ping-pong nearestBreakIndex between breaks --- osu.Game/Screens/Play/BreakOverlay.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index aceacdbab1..50b2c97f59 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -136,11 +136,16 @@ namespace osu.Game.Screens.Play return; } - while (nearestBreakIndex < breaks.Count - 1 && Clock.CurrentTime > breaks[nearestBreakIndex].EndTime) - nearestBreakIndex++; - - while (nearestBreakIndex > 0 && Clock.CurrentTime < breaks[nearestBreakIndex].StartTime) - nearestBreakIndex--; + if (Clock.CurrentTime > breaks[nearestBreakIndex].EndTime) + { + while (nearestBreakIndex < breaks.Count - 1 && Clock.CurrentTime > breaks[nearestBreakIndex].EndTime) + nearestBreakIndex++; + } + else + { + while (nearestBreakIndex > 0 && Clock.CurrentTime < breaks[nearestBreakIndex].StartTime) + nearestBreakIndex--; + } // This ensures that IsBreakTime is generally consistent with the overlay's transforms during a break. // If the overlay never shows (break.HasEffect is false), IsBreakTime should be false. From a08d54eb06b2b2686152b9b04729ba3aabe65fdb Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 26 Jul 2019 03:11:59 +0300 Subject: [PATCH 1879/2854] Remove unnecessary checks --- osu.Game/Screens/Play/BreakOverlay.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 50b2c97f59..aceacdbab1 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -136,16 +136,11 @@ namespace osu.Game.Screens.Play return; } - if (Clock.CurrentTime > breaks[nearestBreakIndex].EndTime) - { - while (nearestBreakIndex < breaks.Count - 1 && Clock.CurrentTime > breaks[nearestBreakIndex].EndTime) - nearestBreakIndex++; - } - else - { - while (nearestBreakIndex > 0 && Clock.CurrentTime < breaks[nearestBreakIndex].StartTime) - nearestBreakIndex--; - } + while (nearestBreakIndex < breaks.Count - 1 && Clock.CurrentTime > breaks[nearestBreakIndex].EndTime) + nearestBreakIndex++; + + while (nearestBreakIndex > 0 && Clock.CurrentTime < breaks[nearestBreakIndex].StartTime) + nearestBreakIndex--; // This ensures that IsBreakTime is generally consistent with the overlay's transforms during a break. // If the overlay never shows (break.HasEffect is false), IsBreakTime should be false. From b4c93b1777646c59507bed626c0c79d2e5f4f82b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 26 Jul 2019 05:11:01 +0300 Subject: [PATCH 1880/2854] Use lookup direction than 2 while loops --- osu.Game/Screens/Play/BreakOverlay.cs | 31 +++++++++++++++++++-------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index aceacdbab1..9091e54159 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -31,7 +31,9 @@ namespace osu.Game.Screens.Play set { breaks = value; - nearestBreakIndex = 0; + + // reset index in case the new breaks list is smaller than last one + currentBreakIndex = 0; initializeBreaks(); } @@ -44,7 +46,7 @@ namespace osu.Game.Screens.Play ///
public IBindable IsBreakTime => isBreakTime; - private int nearestBreakIndex; + private int currentBreakIndex; private readonly BindableBool isBreakTime = new BindableBool(); private readonly Container remainingTimeAdjustmentBox; @@ -133,20 +135,31 @@ namespace osu.Game.Screens.Play if (breaks == null || !breaks.Any()) { isBreakTime.Value = false; + currentBreakIndex = 0; return; } - while (nearestBreakIndex < breaks.Count - 1 && Clock.CurrentTime > breaks[nearestBreakIndex].EndTime) - nearestBreakIndex++; + var lastIndex = currentBreakIndex; + var lookupDirection = Clock.CurrentTime > breaks[lastIndex].EndTime ? 1 : (Clock.CurrentTime < breaks[lastIndex].StartTime ? -1 : 0); - while (nearestBreakIndex > 0 && Clock.CurrentTime < breaks[nearestBreakIndex].StartTime) - nearestBreakIndex--; + while (Clock.CurrentTime < breaks[currentBreakIndex].StartTime || Clock.CurrentTime > breaks[currentBreakIndex].EndTime) + { + currentBreakIndex += lookupDirection; + + // restore index if out of bounds + if (currentBreakIndex < 0 || currentBreakIndex >= breaks.Count) + { + isBreakTime.Value = false; + currentBreakIndex = lastIndex; + return; + } + } // This ensures that IsBreakTime is generally consistent with the overlay's transforms during a break. - // If the overlay never shows (break.HasEffect is false), IsBreakTime should be false. + // If the current break doesn't have effects, IsBreakTime should be false. // We also assume that the overlay's fade out transform is "not break time". - var nearestBreak = breaks[nearestBreakIndex]; - isBreakTime.Value = nearestBreak.HasEffect && Clock.CurrentTime >= nearestBreak.StartTime && Clock.CurrentTime <= nearestBreak.EndTime - fade_duration; + var currentBreak = breaks[currentBreakIndex]; + isBreakTime.Value = currentBreak.HasEffect && Clock.CurrentTime <= currentBreak.EndTime - fade_duration; } private void initializeBreaks() From 44895c4b69743042b1cb0e70e6a8231ce7bc44c7 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 26 Jul 2019 05:41:10 +0300 Subject: [PATCH 1881/2854] Use IReadOnlyList for break periods list --- osu.Game/Screens/Play/BreakOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index d390787090..2b401778a6 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -19,11 +19,11 @@ namespace osu.Game.Screens.Play private const float remaining_time_container_max_size = 0.3f; private const int vertical_margin = 25; - private List breaks; - private readonly Container fadeContainer; - public List Breaks + private IReadOnlyList breaks; + + public IReadOnlyList Breaks { get => breaks; set From c89830f3d89cd1bd2baa0e1731d8f67bf1147b94 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 26 Jul 2019 13:07:28 +0900 Subject: [PATCH 1882/2854] move constants, combine local vars into properties --- osu.Game/Screens/Select/BeatmapCarousel.cs | 22 ++++++++++++---------- osu.Game/Screens/Select/FilterControl.cs | 2 ++ osu.Game/Screens/Select/Footer.cs | 4 ++++ osu.Game/Screens/Select/SongSelect.cs | 22 +++++++--------------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index b917fc4bb4..44aebf73c9 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -26,6 +26,9 @@ namespace osu.Game.Screens.Select { public class BeatmapCarousel : OsuScrollContainer { + private const float bleed_top = FilterControl.HEIGHT; + private const float bleed_bottom = Footer.HEIGHT; + /// /// Triggered when the loaded change and are completely loaded. /// @@ -110,12 +113,10 @@ namespace osu.Game.Screens.Select root = new CarouselRoot(this); Child = new OsuContextMenuContainer { - Masking = false, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Child = scrollableContent = new Container { - Masking = false, RelativeSizeAxes = Axes.X, } }; @@ -342,6 +343,12 @@ namespace osu.Game.Screens.Select public bool AllowSelection = true; + /// + /// The total bounds of what is displayable in the beatmap carousel. + /// + /// + private float visibleHeight => DrawHeight + bleed_bottom + bleed_top; + public void FlushPendingFilterOperations() { if (PendingFilter?.Completed == false) @@ -428,18 +435,15 @@ namespace osu.Game.Screens.Select if (!scrollPositionCache.IsValid) updateScrollPosition(); - // The draw positions of individual sets extend beyond the size of the carousel and into the footer and header. - float visibleHeight = DrawHeight + SongSelect.FOOTER_HEIGHT + SongSelect.FILTER_CONTROL_HEIGHT; - // Remove all items that should no longer be on-screen scrollableContent.RemoveAll(p => p.Y < Current - p.DrawHeight || p.Y > Current + visibleHeight || !p.IsPresent); // Find index range of all items that should be on-screen Trace.Assert(Items.Count == yPositions.Count); - int firstIndex = yPositions.BinarySearch(Current - DrawableCarouselItem.MAX_HEIGHT - SongSelect.FILTER_CONTROL_HEIGHT); + int firstIndex = yPositions.BinarySearch(Current - DrawableCarouselItem.MAX_HEIGHT - bleed_top); if (firstIndex < 0) firstIndex = ~firstIndex; - int lastIndex = yPositions.BinarySearch(Current + visibleHeight + SongSelect.FOOTER_HEIGHT); + int lastIndex = yPositions.BinarySearch(Current + visibleHeight + bleed_bottom); if (lastIndex < 0) lastIndex = ~lastIndex; int notVisibleCount = 0; @@ -547,7 +551,6 @@ namespace osu.Game.Screens.Select yPositions.Clear(); - float visibleHeight = DrawHeight + SongSelect.FOOTER_HEIGHT + SongSelect.FILTER_CONTROL_HEIGHT; float currentY = visibleHeight / 2; DrawableCarouselBeatmapSet lastSet = null; @@ -648,8 +651,7 @@ namespace osu.Game.Screens.Select { var height = p.IsPresent ? p.DrawHeight : 0; - // The actual Y position of the item needs to be offset by any potential padding set by the container's parent. - float itemDrawY = p.Position.Y + SongSelect.FILTER_CONTROL_HEIGHT - Current + height / 2; + float itemDrawY = p.Position.Y + bleed_top - Current + height / 2; float dist = Math.Abs(1f - itemDrawY / halfHeight); // Setting the origin position serves as an additive position on top of potential diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 31b71a21c8..84e8e90f54 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -21,6 +21,8 @@ namespace osu.Game.Screens.Select { public class FilterControl : Container { + public const float HEIGHT = 100; + public Action FilterChanged; private readonly OsuTabControl sortTabs; diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index 7db3d65a0b..0680711f1c 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -62,6 +62,10 @@ namespace osu.Game.Screens.Select public Footer() { + RelativeSizeAxes = Axes.X; + Height = HEIGHT; + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; Children = new Drawable[] { new Box diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index e5576f78cf..73848cc740 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -42,9 +42,7 @@ namespace osu.Game.Screens.Select public abstract class SongSelect : OsuScreen, IKeyBindingHandler { private static readonly Vector2 wedged_container_size = new Vector2(0.5f, 245); - - public const float FILTER_CONTROL_HEIGHT = 100; - public const float FOOTER_HEIGHT = 50; + protected const float BACKGROUND_BLUR = 20; private const float left_area_padding = 20; @@ -123,7 +121,7 @@ namespace osu.Game.Screens.Select Size = new Vector2(wedged_container_size.X, 1), Padding = new MarginPadding { - Bottom = FOOTER_HEIGHT, + Bottom = Footer.HEIGHT, Top = wedged_container_size.Y + left_area_padding, Left = left_area_padding, Right = left_area_padding * 2, @@ -154,8 +152,8 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { - Top = FILTER_CONTROL_HEIGHT, - Bottom = FOOTER_HEIGHT + Top = FilterControl.HEIGHT, + Bottom = Footer.HEIGHT }, Child = Carousel = new BeatmapCarousel { @@ -171,7 +169,7 @@ namespace osu.Game.Screens.Select FilterControl = new FilterControl { RelativeSizeAxes = Axes.X, - Height = FILTER_CONTROL_HEIGHT, + Height = FilterControl.HEIGHT, FilterChanged = c => Carousel.Filter(c), Background = { Width = 2 }, Exit = () => @@ -210,7 +208,7 @@ namespace osu.Game.Screens.Select Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Bottom = FOOTER_HEIGHT }, + Margin = new MarginPadding { Bottom = Footer.HEIGHT }, Children = new Drawable[] { BeatmapOptions = new BeatmapOptionsOverlay(), @@ -222,13 +220,7 @@ namespace osu.Game.Screens.Select } } }, - Footer = new Footer - { - RelativeSizeAxes = Axes.X, - Height = FOOTER_HEIGHT, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - } + Footer = new Footer() }); } From 7ec6ac7b0eb15e88ed18e13889a57ca917e63738 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jul 2019 13:15:36 +0900 Subject: [PATCH 1883/2854] Remove unnecessary override --- osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs index f6533be551..7386bffb1a 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Online.Chat; @@ -40,7 +39,5 @@ namespace osu.Game.Overlays.Chat.Tabs Name = "+"; } } - - protected override bool OnMouseUp(MouseUpEvent e) => false; } } From 56b27db7a412a30547f9f9530fa4ccfee5864e25 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jul 2019 13:17:21 +0900 Subject: [PATCH 1884/2854] Use "Click" instead of "Action" --- osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index 859ca30d7f..2a3dd55c71 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -141,13 +141,15 @@ namespace osu.Game.Overlays.Chat.Tabs protected override bool OnMouseUp(MouseUpEvent e) { - if (e.Button == MouseButton.Middle) + switch (e.Button) { - CloseButton.Action(); - return true; - } + case MouseButton.Middle: + CloseButton.Click(); + return true; - return false; + default: + return false; + } } [BackgroundDependencyLoader] From 8a54dab5d0e28d4aaa372c1f62a8a655629c5a4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jul 2019 13:27:37 +0900 Subject: [PATCH 1885/2854] Tidy up code --- osu.iOS/AppDelegate.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/osu.iOS/AppDelegate.cs b/osu.iOS/AppDelegate.cs index 93eca8caa1..9ef21e014c 100644 --- a/osu.iOS/AppDelegate.cs +++ b/osu.iOS/AppDelegate.cs @@ -12,20 +12,13 @@ namespace osu.iOS [Register("AppDelegate")] public class AppDelegate : GameAppDelegate { - private OsuGameIOS IOSGame; + private OsuGameIOS game; - protected override Framework.Game CreateGame() - { - //Save OsuGameIOS for Import - IOSGame = new OsuGameIOS(); - return IOSGame; - } + protected override Framework.Game CreateGame() => game = new OsuGameIOS(); public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation) { - //Open in Application - Task.Run(() => IOSGame.Import(url.Path)); - + Task.Run(() => game.Import(url.Path)); return true; } } From 53ecb2ae82acf42a4e36ff5283de641aa6442ed6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jul 2019 13:48:29 +0900 Subject: [PATCH 1886/2854] Reduce notification span during beatmap imports --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/OsuGame.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 65efcaa949..166ba5111c 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -386,7 +386,7 @@ namespace osu.Game.Beatmaps beatmap.OnlineBeatmapID = res.OnlineBeatmapID; }; - req.Failure += e => { LogForModel(set, $"Online retrieval failed for {beatmap}", e); }; + req.Failure += e => { LogForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})"); }; // intentionally blocking to limit web request concurrency req.Perform(api); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 41b67f343a..ceaf0c3d5e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -589,7 +589,7 @@ namespace osu.Game { int recentLogCount = 0; - const double debounce = 5000; + const double debounce = 60000; Logger.NewEntry += entry => { From 91fa8a6552c4591ad78b891be086159aa556baf6 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 26 Jul 2019 08:09:18 +0300 Subject: [PATCH 1887/2854] Simplify null and any check --- osu.Game/Screens/Play/BreakOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 9091e54159..86a991c02e 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -132,7 +132,7 @@ namespace osu.Game.Screens.Play private void updateBreakTimeBindable() { - if (breaks == null || !breaks.Any()) + if (breaks?.Any() != true) { isBreakTime.Value = false; currentBreakIndex = 0; From 806d41daf499df87917abaf0c3dd8772ee557e75 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 26 Jul 2019 08:11:13 +0300 Subject: [PATCH 1888/2854] Add function to reset break index --- osu.Game/Screens/Play/BreakOverlay.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 86a991c02e..99361a5bd7 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Play breaks = value; // reset index in case the new breaks list is smaller than last one - currentBreakIndex = 0; + resetBreakIndex(); initializeBreaks(); } @@ -130,12 +130,17 @@ namespace osu.Game.Screens.Play updateBreakTimeBindable(); } + private void resetBreakIndex() + { + isBreakTime.Value = false; + currentBreakIndex = 0; + } + private void updateBreakTimeBindable() { if (breaks?.Any() != true) { - isBreakTime.Value = false; - currentBreakIndex = 0; + resetBreakIndex(); return; } From 3fa6804501a0d417feb4a876160fc83731101140 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 26 Jul 2019 08:12:32 +0300 Subject: [PATCH 1889/2854] Use better loops for moving index Easy to read, suggested by peppy --- osu.Game/Screens/Play/BreakOverlay.cs | 31 +++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 99361a5bd7..f3decb38fb 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -144,19 +144,28 @@ namespace osu.Game.Screens.Play return; } - var lastIndex = currentBreakIndex; - var lookupDirection = Clock.CurrentTime > breaks[lastIndex].EndTime ? 1 : (Clock.CurrentTime < breaks[lastIndex].StartTime ? -1 : 0); + var time = Clock.CurrentTime; - while (Clock.CurrentTime < breaks[currentBreakIndex].StartTime || Clock.CurrentTime > breaks[currentBreakIndex].EndTime) + if (time > breaks[currentBreakIndex].EndTime) { - currentBreakIndex += lookupDirection; - - // restore index if out of bounds - if (currentBreakIndex < 0 || currentBreakIndex >= breaks.Count) + for (int i = currentBreakIndex; i < breaks.Count; i++) { - isBreakTime.Value = false; - currentBreakIndex = lastIndex; - return; + if (time <= breaks[i].EndTime) + { + currentBreakIndex = i; + break; + } + } + } + else if (time < breaks[currentBreakIndex].StartTime) + { + for (int i = currentBreakIndex; i >= 0; i--) + { + if (time >= breaks[i].StartTime) + { + currentBreakIndex = i; + break; + } } } @@ -164,7 +173,7 @@ namespace osu.Game.Screens.Play // If the current break doesn't have effects, IsBreakTime should be false. // We also assume that the overlay's fade out transform is "not break time". var currentBreak = breaks[currentBreakIndex]; - isBreakTime.Value = currentBreak.HasEffect && Clock.CurrentTime <= currentBreak.EndTime - fade_duration; + isBreakTime.Value = currentBreak.HasEffect && time >= currentBreak.StartTime && time <= currentBreak.EndTime - fade_duration; } private void initializeBreaks() From 6fac716ec704f1c07436d57a2979e95f0b504966 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 26 Jul 2019 08:15:46 +0300 Subject: [PATCH 1890/2854] Use container instead of fill flow --- .../Visual/Gameplay/TestSceneBreakOverlay.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs index ffa822d175..85f69bf058 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs @@ -27,13 +27,17 @@ namespace osu.Game.Tests.Visual.Gameplay public TestSceneBreakOverlay() { SpriteText breakTimeText; - Child = new FillFlowContainer + Child = new Container { RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, Children = new Drawable[] { - breakTimeText = new SpriteText(), + breakTimeText = new SpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Margin = new MarginPadding { Bottom = 35 }, + }, breakOverlay = new BreakOverlay(true) } }; From 3b0a48274380ef9616dcc029c0e217f62b477f98 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 25 Jul 2019 23:10:00 -0700 Subject: [PATCH 1891/2854] Fix font weight of leaderboard mod filter --- osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs index 8134cfb42d..d158186899 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs @@ -64,7 +64,7 @@ namespace osu.Game.Graphics.UserInterface Direction = FillDirection.Horizontal, Children = new Drawable[] { - text = new OsuSpriteText { Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold) }, + text = new OsuSpriteText { Font = OsuFont.GetFont(size: 14) }, icon = new SpriteIcon { Size = new Vector2(14), @@ -84,7 +84,11 @@ namespace osu.Game.Graphics.UserInterface } }; - Current.ValueChanged += selected => { icon.Icon = selected.NewValue ? FontAwesome.Regular.CheckCircle : FontAwesome.Regular.Circle; }; + Current.ValueChanged += selected => + { + icon.Icon = selected.NewValue ? FontAwesome.Regular.CheckCircle : FontAwesome.Regular.Circle; + text.Font = text.Font.With(weight: selected.NewValue ? FontWeight.Bold : FontWeight.Medium); + }; } [BackgroundDependencyLoader] From 6765e9f7fa6a6d0dbfb75aa2e012ac18eef3e99c Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 26 Jul 2019 15:13:10 +0900 Subject: [PATCH 1892/2854] Combine into properties and update for framework changes --- osu.Game/Screens/Select/BeatmapCarousel.cs | 32 ++++++++++++++-------- osu.Game/Screens/Select/SongSelect.cs | 2 +- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 44aebf73c9..c8746579b5 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -56,8 +56,6 @@ namespace osu.Game.Screens.Select public override bool HandleNonPositionalInput => AllowSelection; public override bool HandlePositionalInput => AllowSelection; - protected override bool ConfinePositionalInput => true; - /// /// Whether carousel items have completed asynchronously loaded. /// @@ -344,11 +342,24 @@ namespace osu.Game.Screens.Select public bool AllowSelection = true; /// - /// The total bounds of what is displayable in the beatmap carousel. - /// + /// The total height of the displayable portion of the Beatmap Carousel. + /// + /// This is different from , since + /// the beatmap carousel bleeds into the and the + /// /// private float visibleHeight => DrawHeight + bleed_bottom + bleed_top; + /// + /// The position of the lower visible bound with respect to the current scroll position. + /// + private float visibleBottomBound => Current + DrawHeight + bleed_bottom; + + /// + /// The position of the upper visible bound with respect to the current scroll position. + /// + private float visibleUpperBound => Current - bleed_top; + public void FlushPendingFilterOperations() { if (PendingFilter?.Completed == false) @@ -425,6 +436,8 @@ namespace osu.Game.Screens.Select return true; } + protected override bool ReceiveSubTreePositionalInputAt(Vector2 screenSpacePos) => ReceivePositionalInputAt(screenSpacePos); + protected override void Update() { base.Update(); @@ -436,14 +449,14 @@ namespace osu.Game.Screens.Select updateScrollPosition(); // Remove all items that should no longer be on-screen - scrollableContent.RemoveAll(p => p.Y < Current - p.DrawHeight || p.Y > Current + visibleHeight || !p.IsPresent); + scrollableContent.RemoveAll(p => p.Y < visibleUpperBound - p.DrawHeight || p.Y > visibleBottomBound || !p.IsPresent); // Find index range of all items that should be on-screen Trace.Assert(Items.Count == yPositions.Count); - int firstIndex = yPositions.BinarySearch(Current - DrawableCarouselItem.MAX_HEIGHT - bleed_top); + int firstIndex = yPositions.BinarySearch(visibleUpperBound - DrawableCarouselItem.MAX_HEIGHT); if (firstIndex < 0) firstIndex = ~firstIndex; - int lastIndex = yPositions.BinarySearch(Current + visibleHeight + bleed_bottom); + int lastIndex = yPositions.BinarySearch(visibleBottomBound); if (lastIndex < 0) lastIndex = ~lastIndex; int notVisibleCount = 0; @@ -584,7 +597,6 @@ namespace osu.Game.Screens.Select float? setY = null; if (!d.IsLoaded || beatmap.Alpha == 0) // can't use IsPresent due to DrawableCarouselItem override. - // ReSharper disable once PossibleNullReferenceException (resharper broken?) setY = lastSet.Y + lastSet.DrawHeight + 5; if (d.IsLoaded) @@ -649,9 +661,7 @@ namespace osu.Game.Screens.Select /// Half the draw height of the carousel container's parent. private void updateItem(DrawableCarouselItem p, float halfHeight) { - var height = p.IsPresent ? p.DrawHeight : 0; - - float itemDrawY = p.Position.Y + bleed_top - Current + height / 2; + float itemDrawY = p.Position.Y - visibleUpperBound + p.DrawHeight / 2; float dist = Math.Abs(1f - itemDrawY / halfHeight); // Setting the origin position serves as an additive position on top of potential diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 73848cc740..7dd934f91a 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.Select public abstract class SongSelect : OsuScreen, IKeyBindingHandler { private static readonly Vector2 wedged_container_size = new Vector2(0.5f, 245); - + protected const float BACKGROUND_BLUR = 20; private const float left_area_padding = 20; From 5a94a223147b4e774452154b41ed2c4794d30615 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 26 Jul 2019 09:17:39 +0300 Subject: [PATCH 1893/2854] Add a quick check if we're not in a break with current index --- osu.Game/Screens/Play/BreakOverlay.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index f3decb38fb..8f07b90b30 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -146,6 +146,16 @@ namespace osu.Game.Screens.Play var time = Clock.CurrentTime; + if (currentBreakIndex < breaks.Count - 1) + { + // Quick check if we're not in a break with our current index. + if (time > breaks[currentBreakIndex].EndTime && time < breaks[currentBreakIndex + 1].StartTime) + { + isBreakTime.Value = false; + return; + } + } + if (time > breaks[currentBreakIndex].EndTime) { for (int i = currentBreakIndex; i < breaks.Count; i++) From 0b6cfec21cd0463617158abddc1c65f67c73cc75 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 25 Jul 2019 23:20:56 -0700 Subject: [PATCH 1894/2854] Hide leaderboard mod filter when on details tab --- osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs b/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs index f66cd2b29a..f632a7f979 100644 --- a/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs +++ b/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs @@ -28,6 +28,8 @@ namespace osu.Game.Screens.Select private void invokeOnFilter() { OnFilter?.Invoke(tabs.Current.Value, modsCheckbox.Current.Value); + + modsCheckbox.FadeTo(tabs.Current.Value == BeatmapDetailTab.Details ? 0 : 1, 200, Easing.OutQuint); } [BackgroundDependencyLoader] From 0f6c6c7de0b981ab517e35b3f1a1f2c9aae31315 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 26 Jul 2019 15:22:29 +0900 Subject: [PATCH 1895/2854] consolidate halfheight as well --- osu.Game/Screens/Select/BeatmapCarousel.cs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index c8746579b5..cbe54cce1a 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -342,13 +342,13 @@ namespace osu.Game.Screens.Select public bool AllowSelection = true; /// - /// The total height of the displayable portion of the Beatmap Carousel. + /// Half the height of the visible content. /// - /// This is different from , since + /// This is different from the height of , since /// the beatmap carousel bleeds into the and the /// /// - private float visibleHeight => DrawHeight + bleed_bottom + bleed_top; + private float visibleHalfHeight => (DrawHeight + bleed_bottom + bleed_top) / 2; /// /// The position of the lower visible bound with respect to the current scroll position. @@ -508,9 +508,8 @@ namespace osu.Game.Screens.Select // Update externally controlled state of currently visible items // (e.g. x-offset and opacity). - float halfHeight = visibleHeight / 2; foreach (DrawableCarouselItem p in scrollableContent.Children) - updateItem(p, halfHeight); + updateItem(p); } protected override void Dispose(bool isDisposing) @@ -564,7 +563,7 @@ namespace osu.Game.Screens.Select yPositions.Clear(); - float currentY = visibleHeight / 2; + float currentY = visibleHalfHeight; DrawableCarouselBeatmapSet lastSet = null; scrollTarget = null; @@ -617,7 +616,7 @@ namespace osu.Game.Screens.Select currentY += d.DrawHeight + 5; } - currentY += visibleHeight / 2; + currentY += visibleHalfHeight; scrollableContent.Height = currentY; if (BeatmapSetsLoaded && (selectedBeatmapSet == null || selectedBeatmap == null || selectedBeatmapSet.State.Value != CarouselItemState.Selected)) @@ -658,16 +657,15 @@ namespace osu.Game.Screens.Select /// the current scroll position. /// /// The item to be updated. - /// Half the draw height of the carousel container's parent. - private void updateItem(DrawableCarouselItem p, float halfHeight) + private void updateItem(DrawableCarouselItem p) { float itemDrawY = p.Position.Y - visibleUpperBound + p.DrawHeight / 2; - float dist = Math.Abs(1f - itemDrawY / halfHeight); + float dist = Math.Abs(1f - itemDrawY / visibleHalfHeight); // Setting the origin position serves as an additive position on top of potential // local transformation we may want to apply (e.g. when a item gets selected, we // may want to smoothly transform it leftwards.) - p.OriginPosition = new Vector2(-offsetX(dist, halfHeight), 0); + p.OriginPosition = new Vector2(-offsetX(dist, visibleHalfHeight), 0); // We are applying a multiplicative alpha (which is internally done by nesting an // additional container and setting that container's alpha) such that we can From 4c9e8527d86b57c65774b89c7db6ae552c9ae64b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 26 Jul 2019 09:24:53 +0300 Subject: [PATCH 1896/2854] Modify global index directly in the for loop Moves the global index to a near break if not in a break yet --- osu.Game/Screens/Play/BreakOverlay.cs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 8f07b90b30..005cdd36d7 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -158,25 +158,15 @@ namespace osu.Game.Screens.Play if (time > breaks[currentBreakIndex].EndTime) { - for (int i = currentBreakIndex; i < breaks.Count; i++) - { - if (time <= breaks[i].EndTime) - { - currentBreakIndex = i; + for (; currentBreakIndex < breaks.Count; currentBreakIndex++) + if (time <= breaks[currentBreakIndex].EndTime) break; - } - } } else if (time < breaks[currentBreakIndex].StartTime) { - for (int i = currentBreakIndex; i >= 0; i--) - { - if (time >= breaks[i].StartTime) - { - currentBreakIndex = i; + for (; currentBreakIndex >= 0; currentBreakIndex--) + if (time >= breaks[currentBreakIndex].StartTime) break; - } - } } // This ensures that IsBreakTime is generally consistent with the overlay's transforms during a break. From 7fa419a38b0e47e9fb85b946e1a32042cb07f033 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jul 2019 15:49:21 +0900 Subject: [PATCH 1897/2854] Fix file layout order --- .../Select/BeatmapDetailAreaTabControl.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs b/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs index f632a7f979..ea466c2370 100644 --- a/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs +++ b/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs @@ -25,24 +25,6 @@ namespace osu.Game.Screens.Select private Bindable selectedTab; - private void invokeOnFilter() - { - OnFilter?.Invoke(tabs.Current.Value, modsCheckbox.Current.Value); - - modsCheckbox.FadeTo(tabs.Current.Value == BeatmapDetailTab.Details ? 0 : 1, 200, Easing.OutQuint); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colour, OsuConfigManager config) - { - modsCheckbox.AccentColour = tabs.AccentColour = colour.YellowLight; - - selectedTab = config.GetBindable(OsuSetting.BeatmapDetailTab); - - tabs.Current.BindTo(selectedTab); - tabs.Current.TriggerChange(); - } - public BeatmapDetailAreaTabControl() { Height = HEIGHT; @@ -74,6 +56,24 @@ namespace osu.Game.Screens.Select tabs.Current.ValueChanged += _ => invokeOnFilter(); modsCheckbox.Current.ValueChanged += _ => invokeOnFilter(); } + + [BackgroundDependencyLoader] + private void load(OsuColour colour, OsuConfigManager config) + { + modsCheckbox.AccentColour = tabs.AccentColour = colour.YellowLight; + + selectedTab = config.GetBindable(OsuSetting.BeatmapDetailTab); + + tabs.Current.BindTo(selectedTab); + tabs.Current.TriggerChange(); + } + + private void invokeOnFilter() + { + OnFilter?.Invoke(tabs.Current.Value, modsCheckbox.Current.Value); + + modsCheckbox.FadeTo(tabs.Current.Value == BeatmapDetailTab.Details ? 0 : 1, 200, Easing.OutQuint); + } } public enum BeatmapDetailTab From 4d49aad1538c5de6b245d1a1eb72a39b9f847059 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jul 2019 15:51:51 +0900 Subject: [PATCH 1898/2854] Start not visible --- osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs b/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs index ea466c2370..7f82d3cc12 100644 --- a/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs +++ b/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs @@ -50,6 +50,7 @@ namespace osu.Game.Screens.Select Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, Text = @"Mods", + Alpha = 0, }, }; From 9d080ec249bbb2237cd649ca168ad25f16ede974 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jul 2019 17:18:56 +0900 Subject: [PATCH 1899/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b24493665e..385d630823 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@
- + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c05cc6f9dd..71de5ac17f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 3b18039600..95ae4021e8 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 9ef858806b9d8cde669446abb7695efa042b9993 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jul 2019 17:48:19 +0900 Subject: [PATCH 1900/2854] Fix existing usage of Path --- osu.Game/Graphics/UserInterface/LineGraph.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/LineGraph.cs b/osu.Game/Graphics/UserInterface/LineGraph.cs index 757a9a349c..714e953816 100644 --- a/osu.Game/Graphics/UserInterface/LineGraph.cs +++ b/osu.Game/Graphics/UserInterface/LineGraph.cs @@ -76,7 +76,12 @@ namespace osu.Game.Graphics.UserInterface { Masking = true, RelativeSizeAxes = Axes.Both, - Child = path = new SmoothPath { RelativeSizeAxes = Axes.Both, PathRadius = 1 } + Child = path = new SmoothPath + { + AutoSizeAxes = Axes.None, + RelativeSizeAxes = Axes.Both, + PathRadius = 1 + } }); } From 5317a8fa0f3cf0e537aacf3be0cde9c0c79bed08 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jul 2019 17:50:49 +0900 Subject: [PATCH 1901/2854] Update framework again --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 385d630823..0dd3c98116 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 71de5ac17f..9f405d1099 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 95ae4021e8..4e8ce18c6f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 6e09d857fd3d2ed0b18737e6565836fb6ea5bdf7 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 26 Jul 2019 19:00:07 +0900 Subject: [PATCH 1902/2854] Fix ValueChanged events being called out of order --- osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 5606328575..5c98c72b75 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -53,7 +53,13 @@ namespace osu.Game.Graphics.Containers samplePopIn = audio.Samples.Get(@"UI/overlay-pop-in"); samplePopOut = audio.Samples.Get(@"UI/overlay-pop-out"); + } + protected override void LoadComplete() + { + base.LoadComplete(); + + // This must be added after the base LoadComplete. The overlay may need to be hidden immediately if its disabled. State.ValueChanged += onStateChanged; } From 1b0f7b0459c28ff4684b580e2104b69978ddea58 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Fri, 26 Jul 2019 19:05:55 +0900 Subject: [PATCH 1903/2854] more detailed explanation --- osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 5c98c72b75..158f96b46b 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -59,7 +59,8 @@ namespace osu.Game.Graphics.Containers { base.LoadComplete(); - // This must be added after the base LoadComplete. The overlay may need to be hidden immediately if its disabled. + // This must be added after the base LoadComplete. The overlay may need to be hidden immediately if its disabled, + // but the overlay doesn't get shown until after the stateChanged function from VisibilityContainer gets called. State.ValueChanged += onStateChanged; } From 699e366306c20050c91c5f639439a1a1afbeaaea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jul 2019 20:29:57 +0900 Subject: [PATCH 1904/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 0dd3c98116..6744590f0d 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9f405d1099..0b2baa982b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 4e8ce18c6f..55d1afa645 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From ba4045a761e790097d5015d6dd174bf4fdc8ba3a Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 27 Jul 2019 00:22:40 +0300 Subject: [PATCH 1905/2854] Fix transforming mods not working properly Hidden, Grow, Deflate, etc.. --- .../Rulesets/Objects/Drawables/DrawableHitObject.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 1d9d885527..181ae37a8b 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -147,12 +147,6 @@ namespace osu.Game.Rulesets.Objects.Drawables if (State.Value == newState && !force) return; - // apply any custom state overrides - ApplyCustomUpdateState?.Invoke(this, newState); - - if (newState == ArmedState.Hit) - PlaySamples(); - if (UseTransformStateManagement) { double transformTime = HitObject.StartTime - InitialLifetimeOffset; @@ -177,6 +171,12 @@ namespace osu.Game.Rulesets.Objects.Drawables state.Value = newState; UpdateState(newState); + + // apply any custom state overrides + ApplyCustomUpdateState?.Invoke(this, newState); + + if (newState == ArmedState.Hit) + PlaySamples(); } /// From 3571cb96b0b923405beeccfd4f306f55f3be7b6a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 27 Jul 2019 12:56:55 +0900 Subject: [PATCH 1906/2854] Fix broken merge --- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 40079f01e4..cf88b697d9 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -434,7 +434,7 @@ namespace osu.Game.Screens.Select return true; } - protected override bool ReceiveSubTreePositionalInputAt(Vector2 screenSpacePos) => ReceivePositionalInputAt(screenSpacePos); + protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => ReceivePositionalInputAt(screenSpacePos); protected override void Update() { From 56dbd94d167d76ca82ba64afa5ae1859880ce4d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 27 Jul 2019 19:46:46 +0900 Subject: [PATCH 1907/2854] Adjust tournament map pool layout to allow for larger pools --- osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 46d4cfa98c..d32c0d6156 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -47,8 +47,8 @@ namespace osu.Game.Tournament.Screens.MapPool mapFlows = new FillFlowContainer> { Y = 100, - Spacing = new Vector2(10, 20), - Padding = new MarginPadding(50), + Spacing = new Vector2(10, 10), + Padding = new MarginPadding(25), Direction = FillDirection.Vertical, RelativeSizeAxes = Axes.Both, }, @@ -218,7 +218,7 @@ namespace osu.Game.Tournament.Screens.MapPool { mapFlows.Add(currentFlow = new FillFlowContainer { - Spacing = new Vector2(10, 20), + Spacing = new Vector2(10, 5), Direction = FillDirection.Full, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y From 46f17885c6378aaaeb19e7cd0043629cefa494ff Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 27 Jul 2019 15:51:14 +0300 Subject: [PATCH 1908/2854] Rewrite break overlay test --- .../Visual/Gameplay/TestSceneBreakOverlay.cs | 180 +++++++++++------- 1 file changed, 107 insertions(+), 73 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs index 85f69bf058..bbebd62b26 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs @@ -3,11 +3,10 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; -using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Timing; using osu.Game.Beatmaps.Timing; using osu.Game.Screens.Play; @@ -21,96 +20,131 @@ namespace osu.Game.Tests.Visual.Gameplay typeof(BreakOverlay), }; - private readonly BreakOverlay breakOverlay; - private readonly IBindable isBreakTimeBindable = new BindableBool(); + private readonly BreakOverlay breakOverlay, manualBreakOverlay; + private readonly ManualClock manualClock = new ManualClock(); + + private readonly IReadOnlyList testBreaks = new List + { + new BreakPeriod + { + StartTime = 1000, + EndTime = 5000, + }, + new BreakPeriod + { + StartTime = 6000, + EndTime = 13500, + }, + }; public TestSceneBreakOverlay() { - SpriteText breakTimeText; - Child = new Container + Add(breakOverlay = new BreakOverlay(true)); + Add(manualBreakOverlay = new BreakOverlay(true) { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - breakTimeText = new SpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Margin = new MarginPadding { Bottom = 35 }, - }, - breakOverlay = new BreakOverlay(true) - } - }; - - isBreakTimeBindable.BindTo(breakOverlay.IsBreakTime); - isBreakTimeBindable.BindValueChanged(e => breakTimeText.Text = $"IsBreakTime: {e.NewValue}", true); - - AddStep("2s break", () => startBreak(2000)); - AddStep("5s break", () => startBreak(5000)); - AddStep("10s break", () => startBreak(10000)); - AddStep("15s break", () => startBreak(15000)); - AddStep("2s, 2s", startMultipleBreaks); - AddStep("0.5s, 0.7s, 1s, 2s", startAnotherMultipleBreaks); + Alpha = 0, + Clock = new FramedClock(manualClock), + }); } - private void startBreak(double duration) + [Test] + public void TestShowBreaks() { - breakOverlay.Breaks = new List + loadClockStep(false); + + addShowBreakStep(2); + addShowBreakStep(5); + addShowBreakStep(15); + } + + [Test] + public void TestNoEffectsBreak() + { + var shortBreak = new BreakPeriod { EndTime = 500 }; + + loadClockStep(true); + AddStep("start short break", () => manualBreakOverlay.Breaks = new[] { shortBreak }); + + seekBreakStep("seek back to 0", 0, false); + addBreakSeeks(shortBreak, false); + } + + [Test] + public void TestMultipleBreaks() + { + loadClockStep(true); + AddStep("start multiple breaks", () => manualBreakOverlay.Breaks = testBreaks); + + seekBreakStep("seek back to 0", 0, false); + foreach (var b in testBreaks) + addBreakSeeks(b, false); + } + + [Test] + public void TestRewindBreaks() + { + loadClockStep(true); + AddStep("start multiple breaks in rewind", () => manualBreakOverlay.Breaks = testBreaks); + + seekBreakStep("seek back to 0", 0, false); + foreach (var b in testBreaks.Reverse()) + addBreakSeeks(b, true); + } + + [Test] + public void TestSkipBreaks() + { + loadClockStep(true); + AddStep("start multiple breaks with skipping", () => manualBreakOverlay.Breaks = testBreaks); + + var b = testBreaks.Last(); + seekBreakStep("seek back to 0", 0, false); + addBreakSeeks(b, false); + } + + private void addShowBreakStep(double seconds) + { + AddStep($"show '{seconds}s' break", () => breakOverlay.Breaks = new List { new BreakPeriod { StartTime = Clock.CurrentTime, - EndTime = Clock.CurrentTime + duration, + EndTime = Clock.CurrentTime + seconds * 1000, } - }; + }); } - private void startMultipleBreaks() + private void loadClockStep(bool loadManual) { - double currentTime = Clock.CurrentTime; - - breakOverlay.Breaks = new List + AddStep($"load {(loadManual ? "manual" : "normal")} clock", () => { - new BreakPeriod - { - StartTime = currentTime, - EndTime = currentTime + 2000, - }, - new BreakPeriod - { - StartTime = currentTime + 4000, - EndTime = currentTime + 6000, - } - }; + breakOverlay.FadeTo(loadManual ? 0 : 1); + manualBreakOverlay.FadeTo(loadManual ? 1 : 0); + }); } - private void startAnotherMultipleBreaks() + private void addBreakSeeks(BreakPeriod b, bool isReversed) { - double currentTime = Clock.CurrentTime; - - breakOverlay.Breaks = new List + if (isReversed) { - new BreakPeriod // Duration is less than 650 - too short to appear - { - StartTime = currentTime, - EndTime = currentTime + 500, - }, - new BreakPeriod - { - StartTime = currentTime + 1500, - EndTime = currentTime + 2200, - }, - new BreakPeriod - { - StartTime = currentTime + 3200, - EndTime = currentTime + 4200, - }, - new BreakPeriod - { - StartTime = currentTime + 5200, - EndTime = currentTime + 7200, - } - }; + seekBreakStep("seek to break after end", b.EndTime + 500, false); + seekBreakStep("seek to break end", b.EndTime, false); + seekBreakStep("seek to break middle", b.StartTime + b.Duration / 2, b.HasEffect); + seekBreakStep("seek to break start", b.StartTime, b.HasEffect); + } + else + { + seekBreakStep("seek to break start", b.StartTime, b.HasEffect); + seekBreakStep("seek to break middle", b.StartTime + b.Duration / 2, b.HasEffect); + seekBreakStep("seek to break end", b.EndTime, false); + seekBreakStep("seek to break after end", b.EndTime + 500, false); + } + } + + private void seekBreakStep(string seekStepDescription, double time, bool onBreak) + { + AddStep(seekStepDescription, () => manualClock.CurrentTime = time); + AddAssert($"is{(!onBreak ? " not " : " ")}break time", () => manualBreakOverlay.IsBreakTime.Value == onBreak); } } } From 6c580ac9d5c4456bc91a1f8a51f4d92457167669 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 27 Jul 2019 15:52:01 +0300 Subject: [PATCH 1909/2854] Use while loops instead --- osu.Game/Screens/Play/BreakOverlay.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index f96c176104..532109d14b 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -158,15 +158,13 @@ namespace osu.Game.Screens.Play if (time > breaks[currentBreakIndex].EndTime) { - for (; currentBreakIndex < breaks.Count; currentBreakIndex++) - if (time <= breaks[currentBreakIndex].EndTime) - break; + while (time > breaks[currentBreakIndex].EndTime && currentBreakIndex < breaks.Count - 1) + currentBreakIndex++; } else if (time < breaks[currentBreakIndex].StartTime) { - for (; currentBreakIndex >= 0; currentBreakIndex--) - if (time >= breaks[currentBreakIndex].StartTime) - break; + while (time < breaks[currentBreakIndex].StartTime && currentBreakIndex > 0) + currentBreakIndex--; } // This ensures that IsBreakTime is generally consistent with the overlay's transforms during a break. From 95b568eb4646c45b2169899d599114f86c350865 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 27 Jul 2019 15:52:30 +0300 Subject: [PATCH 1910/2854] Remove unnecessary condition --- osu.Game/Screens/Play/BreakOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 532109d14b..7e87c44259 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -161,7 +161,7 @@ namespace osu.Game.Screens.Play while (time > breaks[currentBreakIndex].EndTime && currentBreakIndex < breaks.Count - 1) currentBreakIndex++; } - else if (time < breaks[currentBreakIndex].StartTime) + else { while (time < breaks[currentBreakIndex].StartTime && currentBreakIndex > 0) currentBreakIndex--; From edf6453e04dfe28e092ceebe3f058e573f374ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=BCbner?= Date: Sat, 27 Jul 2019 18:56:37 +0200 Subject: [PATCH 1911/2854] truncate long usernames in private chat --- osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs index 9e87bae864..4f93beee17 100644 --- a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs @@ -65,6 +65,12 @@ namespace osu.Game.Overlays.Chat.Tabs Text.X = ChatOverlay.TAB_AREA_HEIGHT; TextBold.X = ChatOverlay.TAB_AREA_HEIGHT; + + Text.Width = 100f; + TextBold.Width = 100f; + + Text.Truncate = true; + TextBold.Truncate = true; } protected override bool ShowCloseOnHover => false; From 4d49b38277ec35df3723eac519e6cba80f681e1a Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 28 Jul 2019 05:21:27 +0300 Subject: [PATCH 1912/2854] Add a function for loading a list of breaks --- .../Visual/Gameplay/TestSceneBreakOverlay.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs index bbebd62b26..c238f42b60 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs @@ -63,9 +63,8 @@ namespace osu.Game.Tests.Visual.Gameplay var shortBreak = new BreakPeriod { EndTime = 500 }; loadClockStep(true); - AddStep("start short break", () => manualBreakOverlay.Breaks = new[] { shortBreak }); + loadBreaksStep("short break", new[] { shortBreak }); - seekBreakStep("seek back to 0", 0, false); addBreakSeeks(shortBreak, false); } @@ -73,9 +72,8 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestMultipleBreaks() { loadClockStep(true); - AddStep("start multiple breaks", () => manualBreakOverlay.Breaks = testBreaks); + loadBreaksStep("multiple breaks", testBreaks); - seekBreakStep("seek back to 0", 0, false); foreach (var b in testBreaks) addBreakSeeks(b, false); } @@ -84,9 +82,8 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestRewindBreaks() { loadClockStep(true); - AddStep("start multiple breaks in rewind", () => manualBreakOverlay.Breaks = testBreaks); + loadBreaksStep("multiple breaks", testBreaks); - seekBreakStep("seek back to 0", 0, false); foreach (var b in testBreaks.Reverse()) addBreakSeeks(b, true); } @@ -95,10 +92,9 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestSkipBreaks() { loadClockStep(true); - AddStep("start multiple breaks with skipping", () => manualBreakOverlay.Breaks = testBreaks); + loadBreaksStep("multiple breaks", testBreaks); var b = testBreaks.Last(); - seekBreakStep("seek back to 0", 0, false); addBreakSeeks(b, false); } @@ -123,6 +119,12 @@ namespace osu.Game.Tests.Visual.Gameplay }); } + private void loadBreaksStep(string breakDescription, IReadOnlyList breaks) + { + AddStep($"load {breakDescription}", () => manualBreakOverlay.Breaks = breaks); + seekBreakStep("seek back to 0", 0, false); + } + private void addBreakSeeks(BreakPeriod b, bool isReversed) { if (isReversed) From 4143bafd67ac14e61b98855797a96bec2fac4647 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 28 Jul 2019 05:22:09 +0300 Subject: [PATCH 1913/2854] More simplifies --- osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs index c238f42b60..d2db92c855 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay }; private readonly BreakOverlay breakOverlay, manualBreakOverlay; - private readonly ManualClock manualClock = new ManualClock(); + private readonly ManualClock manualClock; private readonly IReadOnlyList testBreaks = new List { @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Gameplay Add(manualBreakOverlay = new BreakOverlay(true) { Alpha = 0, - Clock = new FramedClock(manualClock), + Clock = new FramedClock(manualClock = new ManualClock()), }); } @@ -94,8 +94,7 @@ namespace osu.Game.Tests.Visual.Gameplay loadClockStep(true); loadBreaksStep("multiple breaks", testBreaks); - var b = testBreaks.Last(); - addBreakSeeks(b, false); + addBreakSeeks(testBreaks.Last(), false); } private void addShowBreakStep(double seconds) From bd2fce4bb7997dde45486bd0ff2cbecbcdce6826 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Sun, 28 Jul 2019 13:45:54 +0900 Subject: [PATCH 1914/2854] don't use extra container --- osu.Game/OsuGame.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e71344738b..e5099f3c2f 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -416,16 +416,9 @@ namespace osu.Game screenStack.Exit(); } }, - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new[] - { - screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }, - backButton.CreateProxy(), - logoContainer = new Container { RelativeSizeAxes = Axes.Both }, - } - } + screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }, + backButton.CreateProxy(), + logoContainer = new Container { RelativeSizeAxes = Axes.Both }, } }, overlayContent = new Container { RelativeSizeAxes = Axes.Both }, From 1dd3a6630082b0e0b88b43be089f471b54d19aec Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 28 Jul 2019 09:16:19 +0300 Subject: [PATCH 1915/2854] Remove unnecessary index resets --- osu.Game/Screens/Play/BreakOverlay.cs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 7e87c44259..726c825c84 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -33,7 +33,8 @@ namespace osu.Game.Screens.Play breaks = value; // reset index in case the new breaks list is smaller than last one - resetBreakIndex(); + isBreakTime.Value = false; + currentBreakIndex = 0; initializeBreaks(); } @@ -126,23 +127,13 @@ namespace osu.Game.Screens.Play protected override void Update() { base.Update(); - updateBreakTimeBindable(); } - private void resetBreakIndex() - { - isBreakTime.Value = false; - currentBreakIndex = 0; - } - private void updateBreakTimeBindable() { if (breaks?.Any() != true) - { - resetBreakIndex(); return; - } var time = Clock.CurrentTime; From 5bf0277fd401b5d668bd8939ba7a41264a1ca6c9 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 28 Jul 2019 09:17:13 +0300 Subject: [PATCH 1916/2854] Remove unnecessary quick check Not saving for anything --- osu.Game/Screens/Play/BreakOverlay.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 726c825c84..e3e4014eb3 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -137,16 +137,6 @@ namespace osu.Game.Screens.Play var time = Clock.CurrentTime; - if (currentBreakIndex < breaks.Count - 1) - { - // Quick check if we're not in a break with our current index. - if (time > breaks[currentBreakIndex].EndTime && time < breaks[currentBreakIndex + 1].StartTime) - { - isBreakTime.Value = false; - return; - } - } - if (time > breaks[currentBreakIndex].EndTime) { while (time > breaks[currentBreakIndex].EndTime && currentBreakIndex < breaks.Count - 1) From 37c32659429787f33f3a78005142d5803feb1ac8 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 28 Jul 2019 09:21:54 +0300 Subject: [PATCH 1917/2854] Manually call the update function on retrieving IsBreakTime value --- .../Visual/Gameplay/TestSceneBreakOverlay.cs | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs index d2db92c855..890c575eb6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Graphics; +using osu.Framework.Bindables; using osu.Framework.Timing; using osu.Game.Beatmaps.Timing; using osu.Game.Screens.Play; @@ -20,8 +20,9 @@ namespace osu.Game.Tests.Visual.Gameplay typeof(BreakOverlay), }; - private readonly BreakOverlay breakOverlay, manualBreakOverlay; private readonly ManualClock manualClock; + private readonly BreakOverlay breakOverlay; + private readonly TestBreakOverlay manualBreakOverlay; private readonly IReadOnlyList testBreaks = new List { @@ -40,7 +41,7 @@ namespace osu.Game.Tests.Visual.Gameplay public TestSceneBreakOverlay() { Add(breakOverlay = new BreakOverlay(true)); - Add(manualBreakOverlay = new BreakOverlay(true) + Add(manualBreakOverlay = new TestBreakOverlay(true) { Alpha = 0, Clock = new FramedClock(manualClock = new ManualClock()), @@ -147,5 +148,24 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep(seekStepDescription, () => manualClock.CurrentTime = time); AddAssert($"is{(!onBreak ? " not " : " ")}break time", () => manualBreakOverlay.IsBreakTime.Value == onBreak); } + + private class TestBreakOverlay : BreakOverlay + { + public new IBindable IsBreakTime + { + get + { + // Manually call the update function as it might take up to 2 frames for an automatic update to happen + Update(); + + return base.IsBreakTime; + } + } + + public TestBreakOverlay(bool letterboxing) + : base(letterboxing) + { + } + } } } From c9e45f8cdc2e5b26e6d099e21bf2e7d731fa2dab Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 28 Jul 2019 09:27:02 +0300 Subject: [PATCH 1918/2854] Assign clocks instead of creating 2 separate overlays --- .../Visual/Gameplay/TestSceneBreakOverlay.cs | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs index 890c575eb6..eaae647dbc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs @@ -20,9 +20,7 @@ namespace osu.Game.Tests.Visual.Gameplay typeof(BreakOverlay), }; - private readonly ManualClock manualClock; - private readonly BreakOverlay breakOverlay; - private readonly TestBreakOverlay manualBreakOverlay; + private readonly TestBreakOverlay breakOverlay; private readonly IReadOnlyList testBreaks = new List { @@ -40,12 +38,7 @@ namespace osu.Game.Tests.Visual.Gameplay public TestSceneBreakOverlay() { - Add(breakOverlay = new BreakOverlay(true)); - Add(manualBreakOverlay = new TestBreakOverlay(true) - { - Alpha = 0, - Clock = new FramedClock(manualClock = new ManualClock()), - }); + Add(breakOverlay = new TestBreakOverlay(true)); } [Test] @@ -112,16 +105,12 @@ namespace osu.Game.Tests.Visual.Gameplay private void loadClockStep(bool loadManual) { - AddStep($"load {(loadManual ? "manual" : "normal")} clock", () => - { - breakOverlay.FadeTo(loadManual ? 0 : 1); - manualBreakOverlay.FadeTo(loadManual ? 1 : 0); - }); + AddStep($"load {(loadManual ? "manual" : "normal")} clock", () => breakOverlay.SwitchClock(loadManual)); } private void loadBreaksStep(string breakDescription, IReadOnlyList breaks) { - AddStep($"load {breakDescription}", () => manualBreakOverlay.Breaks = breaks); + AddStep($"load {breakDescription}", () => breakOverlay.Breaks = breaks); seekBreakStep("seek back to 0", 0, false); } @@ -145,12 +134,22 @@ namespace osu.Game.Tests.Visual.Gameplay private void seekBreakStep(string seekStepDescription, double time, bool onBreak) { - AddStep(seekStepDescription, () => manualClock.CurrentTime = time); - AddAssert($"is{(!onBreak ? " not " : " ")}break time", () => manualBreakOverlay.IsBreakTime.Value == onBreak); + AddStep(seekStepDescription, () => breakOverlay.ManualClockTime = time); + AddAssert($"is{(!onBreak ? " not " : " ")}break time", () => breakOverlay.IsBreakTime.Value == onBreak); } private class TestBreakOverlay : BreakOverlay { + private readonly FramedClock framedManualClock; + private readonly ManualClock manualClock; + private IFrameBasedClock normalClock; + + public double ManualClockTime + { + get => manualClock.CurrentTime; + set => manualClock.CurrentTime = value; + } + public new IBindable IsBreakTime { get @@ -165,6 +164,15 @@ namespace osu.Game.Tests.Visual.Gameplay public TestBreakOverlay(bool letterboxing) : base(letterboxing) { + framedManualClock = new FramedClock(manualClock = new ManualClock()); + } + + public void SwitchClock(bool setManual) => Clock = setManual ? framedManualClock : normalClock; + + protected override void LoadComplete() + { + base.LoadComplete(); + normalClock = Clock; } } } From c6d4ce0f8a8bb0787744bc2d337231f8bcdb3f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=BCbner?= Date: Sun, 28 Jul 2019 12:14:06 +0200 Subject: [PATCH 1919/2854] revert truncation in derived class --- osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs index 4f93beee17..9e87bae864 100644 --- a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs @@ -65,12 +65,6 @@ namespace osu.Game.Overlays.Chat.Tabs Text.X = ChatOverlay.TAB_AREA_HEIGHT; TextBold.X = ChatOverlay.TAB_AREA_HEIGHT; - - Text.Width = 100f; - TextBold.Width = 100f; - - Text.Truncate = true; - TextBold.Truncate = true; } protected override bool ShowCloseOnHover => false; From df8d4d896639867418b5ad5199170a1931e6cdfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=BCbner?= Date: Sun, 28 Jul 2019 12:16:32 +0200 Subject: [PATCH 1920/2854] add truncation to base class --- osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 2 ++ osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index 2a3dd55c71..7007b0183d 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -102,6 +102,8 @@ namespace osu.Game.Overlays.Chat.Tabs Anchor = Anchor.CentreLeft, Text = value.ToString(), Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold) + Width = 115f, + Truncate = true, }, CloseButton = new TabCloseButton { diff --git a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs index 9e87bae864..194e62e52a 100644 --- a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs @@ -65,6 +65,8 @@ namespace osu.Game.Overlays.Chat.Tabs Text.X = ChatOverlay.TAB_AREA_HEIGHT; TextBold.X = ChatOverlay.TAB_AREA_HEIGHT; + + Text.Width = 100f; } protected override bool ShowCloseOnHover => false; From f7b9ddb48ce4dffa56129344a1591e623e502597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=BCbner?= Date: Sun, 28 Jul 2019 12:40:21 +0200 Subject: [PATCH 1921/2854] combine Text and TextBold --- .../Chat/Tabs/ChannelSelectorTabItem.cs | 3 ++- osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 23 +++++++------------ .../Chat/Tabs/PrivateChannelTabItem.cs | 1 - 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs index 7386bffb1a..ba4f046b66 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs @@ -13,6 +13,8 @@ namespace osu.Game.Overlays.Chat.Tabs public override bool IsSwitchable => false; + protected override bool IsBoldWhenActive => false; + public ChannelSelectorTabItem() : base(new ChannelSelectorTabChannel()) { @@ -22,7 +24,6 @@ namespace osu.Game.Overlays.Chat.Tabs Icon.Alpha = 0; Text.Font = Text.Font.With(size: 45); - TextBold.Font = Text.Font.With(size: 45); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index 7007b0183d..ea7875ac63 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -29,7 +29,6 @@ namespace osu.Game.Overlays.Chat.Tabs public override bool IsRemovable => !Pinned; protected readonly SpriteText Text; - protected readonly SpriteText TextBold; protected readonly ClickableContainer CloseButton; private readonly Box box; private readonly Box highlightBox; @@ -92,16 +91,7 @@ namespace osu.Game.Overlays.Chat.Tabs Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, Text = value.ToString(), - Font = OsuFont.GetFont(size: 18) - }, - TextBold = new OsuSpriteText - { - Alpha = 0, - Margin = new MarginPadding(5), - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Text = value.ToString(), - Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold) + Font = OsuFont.GetFont(size: 18), Width = 115f, Truncate = true, }, @@ -125,6 +115,8 @@ namespace osu.Game.Overlays.Chat.Tabs protected virtual bool ShowCloseOnHover => true; + protected virtual bool IsBoldWhenActive => true; + protected override bool OnHover(HoverEvent e) { if (IsRemovable && ShowCloseOnHover) @@ -205,8 +197,10 @@ namespace osu.Game.Overlays.Chat.Tabs box.FadeColour(BackgroundActive, TRANSITION_LENGTH, Easing.OutQuint); highlightBox.FadeIn(TRANSITION_LENGTH, Easing.OutQuint); - Text.FadeOut(TRANSITION_LENGTH, Easing.OutQuint); - TextBold.FadeIn(TRANSITION_LENGTH, Easing.OutQuint); + if (IsBoldWhenActive) + { + Text.Font = Text.Font.With(weight: FontWeight.Bold); + } } protected virtual void FadeInactive() @@ -218,8 +212,7 @@ namespace osu.Game.Overlays.Chat.Tabs box.FadeColour(BackgroundInactive, TRANSITION_LENGTH, Easing.OutQuint); highlightBox.FadeOut(TRANSITION_LENGTH, Easing.OutQuint); - Text.FadeIn(TRANSITION_LENGTH, Easing.OutQuint); - TextBold.FadeOut(TRANSITION_LENGTH, Easing.OutQuint); + Text.Font = Text.Font.With(weight: FontWeight.Medium); } protected override void OnActivated() => updateState(); diff --git a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs index 194e62e52a..97f695c73a 100644 --- a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs @@ -64,7 +64,6 @@ namespace osu.Game.Overlays.Chat.Tabs avatar.OnLoadComplete += d => d.FadeInFromZero(300, Easing.OutQuint); Text.X = ChatOverlay.TAB_AREA_HEIGHT; - TextBold.X = ChatOverlay.TAB_AREA_HEIGHT; Text.Width = 100f; } From 088c04a20fa32b8c42ab7bf4ecac1acb363c0d81 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 28 Jul 2019 15:32:29 +0900 Subject: [PATCH 1922/2854] Revert "Fix BackButton handling escape before all other elements (#5440)" This reverts commit 17a6563f4c5d0c7e3a4dfac5469c65866de311aa. --- osu.Game/OsuGame.cs | 5 ++--- osu.Game/Screens/Select/Footer.cs | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e5099f3c2f..ceaf0c3d5e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -404,8 +404,9 @@ namespace osu.Game screenContainer = new ScalingContainer(ScalingMode.ExcludeOverlays) { RelativeSizeAxes = Axes.Both, - Children = new[] + Children = new Drawable[] { + screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }, backButton = new BackButton { Anchor = Anchor.BottomLeft, @@ -416,8 +417,6 @@ namespace osu.Game screenStack.Exit(); } }, - screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }, - backButton.CreateProxy(), logoContainer = new Container { RelativeSizeAxes = Axes.Both }, } }, diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index 71641cab5d..0680711f1c 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -9,6 +9,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; namespace osu.Game.Screens.Select @@ -102,5 +103,9 @@ namespace osu.Game.Screens.Select updateModeLight(); } + + protected override bool OnMouseDown(MouseDownEvent e) => true; + + protected override bool OnClick(ClickEvent e) => true; } } From 07f905d21c7944d1969b3435fedc703746c8d98a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Jul 2019 01:00:41 +0900 Subject: [PATCH 1923/2854] Tidy up code and fix explode animations not playing correctly --- osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs | 89 ++++++++++------------ 1 file changed, 40 insertions(+), 49 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs index 102275043c..18267a2e0e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs @@ -1,95 +1,86 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; -using OpenTK; +using osuTK; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModSpinIn : Mod, IApplicableToDrawableHitObjects + public class OsuModSpinIn : Mod, IApplicableToDrawableHitObjects, IReadFromConfig { public override string Name => "Spin In"; - public override string ShortenedName => "SI"; - public override FontAwesome Icon => FontAwesome.fa_rotate_right; + public override string Acronym => "SI"; + public override IconUsage Icon => FontAwesome.Solid.Undo; public override ModType Type => ModType.Fun; public override string Description => "Circles spin in. No approach circles."; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden) }; private const int rotate_offset = 360; - private const float rotate_starting_width = 2.0f; + private const float rotate_starting_width = 2; + private Bindable increaseFirstObjectVisibility = new Bindable(); + + public void ReadFromConfig(OsuConfigManager config) + { + increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); + } public void ApplyToDrawableHitObjects(IEnumerable drawables) { - foreach (var drawable in drawables) + foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0)) { - // Need to add custom update in order to disable fade - drawable.ApplyCustomUpdateState += ApplyZoomState; + switch (drawable) + { + case DrawableSpinner _: + continue; + + default: + drawable.ApplyCustomUpdateState += applyZoomState; + break; + } } } - protected void ApplyZoomState(DrawableHitObject drawable, ArmedState state) + private void applyZoomState(DrawableHitObject drawable, ArmedState state) { - if (!(drawable is DrawableOsuHitObject)) return; - if (state != ArmedState.Idle) return; - var h = (OsuHitObject)drawable.HitObject; - var appearTime = h.StartTime - h.TimePreempt + 1; - var moveDuration = h.TimePreempt - 1; - switch (drawable) { case DrawableHitCircle circle: - // Disable Fade - circle.Transforms - .Where(t => t.TargetMember == "Alpha") - .ForEach(t => circle.RemoveTransform(t)); - - using (circle.BeginAbsoluteSequence(appearTime, true)) - { - var origScale = drawable.Scale; - var origRotate = circle.Rotation; - - circle - .RotateTo(origRotate+rotate_offset) - .RotateTo(origRotate, moveDuration, Easing.InOutSine) - .ScaleTo(origScale * new Vector2(rotate_starting_width, 0)) - .ScaleTo(origScale, moveDuration, Easing.InOutSine) - .FadeTo(1); - } - - using (circle.ApproachCircle.BeginAbsoluteSequence(appearTime, true)) + using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) { circle.ApproachCircle.Hide(); + + circle.RotateTo(rotate_offset).Then().RotateTo(0, h.TimePreempt, Easing.InOutSine); + circle.ScaleTo(new Vector2(rotate_starting_width, 0)).Then().ScaleTo(1, h.TimePreempt, Easing.InOutSine); + + // bypass fade in. + if (state == ArmedState.Idle) + circle.FadeIn(); } break; case DrawableSlider slider: - // Disable fade - slider.Transforms - .Where(t => t.TargetMember == "Alpha") - .ForEach(t => slider.RemoveTransform(t)); - - using (slider.BeginAbsoluteSequence(appearTime, true)) + using (slider.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) { - var origScale = slider.Scale; + slider.ScaleTo(0).Then().ScaleTo(1, h.TimePreempt, Easing.InOutSine); - slider - .ScaleTo(0) - .ScaleTo(origScale, moveDuration, Easing.InOutSine) - .FadeTo(1); + // bypass fade in. + if (state == ArmedState.Idle) + slider.FadeIn(); } break; From 3e74079d0278a3876b41497acb4206b2d312fa06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Jul 2019 01:15:54 +0900 Subject: [PATCH 1924/2854] Add incompatibility with scale tween mods --- osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs | 4 +++- osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs index 18267a2e0e..62b5ecfd58 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs @@ -24,7 +24,9 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override string Description => "Circles spin in. No approach circles."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden) }; + + // todo: this mod should be able to be compatible with hidden with a bit of further implementation. + public override Type[] IncompatibleMods => new[] { typeof(OsuModeObjectScaleTween), typeof(OsuModHidden) }; private const int rotate_offset = 360; private const float rotate_starting_width = 2; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs b/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs index ad6a15718a..e926ade41b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Bindables; @@ -28,6 +29,8 @@ namespace osu.Game.Rulesets.Osu.Mods private Bindable increaseFirstObjectVisibility = new Bindable(); + public override Type[] IncompatibleMods => new[] { typeof(OsuModSpinIn) }; + public void ReadFromConfig(OsuConfigManager config) { increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); @@ -64,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Mods case DrawableSlider _: case DrawableHitCircle _: { - using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) drawable.ScaleTo(StartScale).Then().ScaleTo(EndScale, h.TimePreempt, Easing.OutSine); break; } @@ -75,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Mods { case DrawableHitCircle circle: // we don't want to see the approach circle - using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) circle.ApproachCircle.Hide(); break; } From de8f5028714007f6b0051793f60436555c29cb40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Jul 2019 02:46:33 +0900 Subject: [PATCH 1925/2854] Add test --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 4d3992ce13..9196513a55 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Online private TestChatOverlay chatOverlay; private ChannelManager channelManager; - private readonly Channel channel1 = new Channel(new User()) { Name = "test1" }; + private readonly Channel channel1 = new Channel(new User()) { Name = "test really long username" }; private readonly Channel channel2 = new Channel(new User()) { Name = "test2" }; [SetUp] From 663f34d3d8643221feee276eb6923cc95a5b90a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Jul 2019 02:47:26 +0900 Subject: [PATCH 1926/2854] Remove width specifications --- .../Overlays/Chat/Tabs/ChannelSelectorTabItem.cs | 1 + osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 12 ++++++++++-- osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs | 6 ++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs index ba4f046b66..d5d9a6c2ce 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs @@ -24,6 +24,7 @@ namespace osu.Game.Overlays.Chat.Tabs Icon.Alpha = 0; Text.Font = Text.Font.With(size: 45); + Text.Truncate = false; } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index ea7875ac63..3de321e127 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -87,12 +87,16 @@ namespace osu.Game.Overlays.Chat.Tabs }, Text = new OsuSpriteText { - Margin = new MarginPadding(5), Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, Text = value.ToString(), Font = OsuFont.GetFont(size: 18), - Width = 115f, + Padding = new MarginPadding(5) + { + Left = LeftTextPadding, + Right = RightTextPadding, + }, + RelativeSizeAxes = Axes.X, Truncate = true, }, CloseButton = new TabCloseButton @@ -111,6 +115,10 @@ namespace osu.Game.Overlays.Chat.Tabs }; } + protected virtual float LeftTextPadding => 5; + + protected virtual float RightTextPadding => IsRemovable ? 40 : 5; + protected virtual IconUsage DisplayIcon => FontAwesome.Solid.Hashtag; protected virtual bool ShowCloseOnHover => true; diff --git a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs index 97f695c73a..1413b8fe78 100644 --- a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs @@ -62,12 +62,10 @@ namespace osu.Game.Overlays.Chat.Tabs }); avatar.OnLoadComplete += d => d.FadeInFromZero(300, Easing.OutQuint); - - Text.X = ChatOverlay.TAB_AREA_HEIGHT; - - Text.Width = 100f; } + protected override float LeftTextPadding => base.LeftTextPadding + ChatOverlay.TAB_AREA_HEIGHT; + protected override bool ShowCloseOnHover => false; protected override void FadeActive() From 316b11d08b0647041c08aa5e839cb5adf1ccc11b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=BCbner?= Date: Sun, 28 Jul 2019 20:36:21 +0200 Subject: [PATCH 1927/2854] use single line if-statement --- osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index 3de321e127..266e68f17e 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -205,10 +205,7 @@ namespace osu.Game.Overlays.Chat.Tabs box.FadeColour(BackgroundActive, TRANSITION_LENGTH, Easing.OutQuint); highlightBox.FadeIn(TRANSITION_LENGTH, Easing.OutQuint); - if (IsBoldWhenActive) - { - Text.Font = Text.Font.With(weight: FontWeight.Bold); - } + if (IsBoldWhenActive) Text.Font = Text.Font.With(weight: FontWeight.Bold); } protected virtual void FadeInactive() From f4effd12c301ecece374df443cc08bd0e2bf2958 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 28 Jul 2019 21:04:55 +0200 Subject: [PATCH 1928/2854] make legacy skins animatable --- osu.Game/Skinning/LegacySkin.cs | 38 +++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 513a024a36..24c6c9e4ec 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; +using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; @@ -52,22 +53,29 @@ namespace osu.Game.Skinning public override Drawable GetDrawableComponent(string componentName) { + bool animatable = false; + (bool looping, double frametime) animationData = (false, 1000 / 60d); + switch (componentName) { case "Play/Miss": componentName = "hit0"; + animatable = true; break; case "Play/Meh": componentName = "hit50"; + animatable = true; break; case "Play/Good": componentName = "hit100"; + animatable = true; break; case "Play/Great": componentName = "hit300"; + animatable = true; break; case "Play/osu/number-text": @@ -81,15 +89,31 @@ namespace osu.Game.Skinning }; } - // temporary allowance is given for skins the fact that stable handles non-animatable items such as hitcircles (incorrectly) - // by (incorrectly) displaying the first frame of animation rather than the non-animated version. - // users have used this to "hide" certain elements like hit300. - var texture = GetTexture($"{componentName}-0") ?? GetTexture(componentName); + var texture = GetTexture($"{componentName}-0"); - if (texture == null) - return null; + if (texture != null && animatable) + { + var animation = new TextureAnimation { DefaultFrameLength = animationData.frametime }; - return new Sprite { Texture = texture }; + for (int i = 1; texture != null; i++) + { + animation.AddFrame(texture); + texture = GetTexture($"{componentName}-{i}"); + } + + animation.Repeat = animationData.looping; + + return animation; + } + else + { + texture = GetTexture(componentName); + + if (texture == null) + return null; + + return new Sprite { Texture = texture }; + } } public override Texture GetTexture(string componentName) From 26fc782de9cb40dc36960b6c7764fa0c024a83ce Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 29 Jul 2019 10:33:34 +0900 Subject: [PATCH 1929/2854] Don't exit if screenstack is null --- osu.Game/Screens/Multi/Multiplayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 9ffd620e55..90806bab6e 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -212,7 +212,7 @@ namespace osu.Game.Screens.Multi public override bool OnExiting(IScreen next) { - if (!(screenStack.CurrentScreen is LoungeSubScreen)) + if (screenStack.CurrentScreen != null && !(screenStack.CurrentScreen is LoungeSubScreen)) { screenStack.Exit(); return true; From 4b5fb84888dd7afed64bdafc497031675182489d Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 29 Jul 2019 11:02:44 +0900 Subject: [PATCH 1930/2854] Rewrite comment --- osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 158f96b46b..c33e980a9a 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -59,8 +59,8 @@ namespace osu.Game.Graphics.Containers { base.LoadComplete(); - // This must be added after the base LoadComplete. The overlay may need to be hidden immediately if its disabled, - // but the overlay doesn't get shown until after the stateChanged function from VisibilityContainer gets called. + // This must be added after the base LoadComplete, since onStateChanged contains logic that + // must be run after other state change logic has been completed. State.ValueChanged += onStateChanged; } From c14c3ba8ec28f4e927b7865de73cff615f9b942e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Jul 2019 16:55:19 +0900 Subject: [PATCH 1931/2854] Move database isolation logic to OsuTestScene for easier reuse --- .../Background/TestSceneUserDimContainer.cs | 17 ++------------ .../SongSelect/TestScenePlaySongSelect.cs | 23 ++----------------- osu.Game/Tests/Visual/OsuTestScene.cs | 15 ++++++++++++ 3 files changed, 19 insertions(+), 36 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index dc4ceed59e..f114559114 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -17,7 +17,6 @@ using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Configuration; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -51,26 +50,14 @@ namespace osu.Game.Tests.Visual.Background private DummySongSelect songSelect; private TestPlayerLoader playerLoader; private TestPlayer player; - private DatabaseContextFactory factory; private BeatmapManager manager; private RulesetStore rulesets; [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { - factory = new DatabaseContextFactory(LocalStorage); - factory.ResetDatabase(); - - using (var usage = factory.Get()) - usage.Migrate(); - - factory.ResetDatabase(); - - using (var usage = factory.Get()) - usage.Migrate(); - - Dependencies.Cache(rulesets = new RulesetStore(factory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); Dependencies.Cache(new OsuConfigManager(LocalStorage)); manager.Import(TestResources.GetTestBeatmapForImport()).Wait(); diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index f3255814f2..680250a226 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -15,7 +15,6 @@ using osu.Framework.MathUtils; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Beatmaps; -using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; @@ -35,7 +34,6 @@ namespace osu.Game.Tests.Visual.SongSelect private RulesetStore rulesets; private WorkingBeatmap defaultBeatmap; - private DatabaseContextFactory factory; public override IReadOnlyList RequiredTypes => new[] { @@ -74,28 +72,11 @@ namespace osu.Game.Tests.Visual.SongSelect private TestSongSelect songSelect; - protected override void Dispose(bool isDisposing) - { - factory.ResetDatabase(); - base.Dispose(isDisposing); - } - [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { - factory = new DatabaseContextFactory(LocalStorage); - factory.ResetDatabase(); - - using (var usage = factory.Get()) - usage.Migrate(); - - factory.ResetDatabase(); - - using (var usage = factory.Get()) - usage.Migrate(); - - Dependencies.Cache(rulesets = new RulesetStore(factory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default)); + Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default)); Beatmap.SetDefault(); } diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 9b3c15aa91..27d72f3950 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -15,6 +15,7 @@ using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Beatmaps; +using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Tests.Beatmaps; @@ -43,6 +44,9 @@ namespace osu.Game.Tests.Visual private readonly Lazy localStorage; protected Storage LocalStorage => localStorage.Value; + private readonly Lazy contextFactory; + protected DatabaseContextFactory ContextFactory => contextFactory.Value; + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { // This is the earliest we can get OsuGameBase, which is used by the dummy working beatmap to find textures @@ -59,6 +63,14 @@ namespace osu.Game.Tests.Visual protected OsuTestScene() { localStorage = new Lazy(() => new NativeStorage($"{GetType().Name}-{Guid.NewGuid()}")); + contextFactory = new Lazy(() => + { + var factory = new DatabaseContextFactory(LocalStorage); + factory.ResetDatabase(); + using (var usage = factory.Get()) + usage.Migrate(); + return factory; + }); } [Resolved] @@ -85,6 +97,9 @@ namespace osu.Game.Tests.Visual if (beatmap?.Value.TrackLoaded == true) beatmap.Value.Track.Stop(); + if (contextFactory.IsValueCreated) + contextFactory.Value.ResetDatabase(); + if (localStorage.IsValueCreated) { try From cb17007fa740c67e665dbc0cd447412e5e81b78b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Jul 2019 16:55:39 +0900 Subject: [PATCH 1932/2854] Fix zero-length hash models incorrectly creating a unique hash --- osu.Game/Database/ArchiveModelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index ed65bdc069..efb76deff8 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -253,7 +253,7 @@ namespace osu.Game.Database using (Stream s = reader.GetStream(file)) s.CopyTo(hashable); - return hashable.ComputeSHA2Hash(); + return hashable.Length > 0 ? hashable.ComputeSHA2Hash() : null; } /// From c514cbe2b78c9c4846b125d8b9f48924b1e7f176 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jul 2019 19:29:06 +0900 Subject: [PATCH 1933/2854] Add basic skinning test --- .../default-skin/approachcircle@2x.png | Bin 0 -> 18164 bytes .../Resources/default-skin/hit300k@2x.png | Bin 0 -> 29098 bytes .../Resources/default-skin/hitcircle@2x.png | Bin 0 -> 7768 bytes .../default-skin/hitcircleoverlay@2x.png | Bin 0 -> 45901 bytes .../Resources/metrics-skin/hitcircle@2x.png | Bin 0 -> 13140 bytes .../metrics-skin/hitcircleoverlay@2x.png | Bin 0 -> 38217 bytes .../special-skin/approachcircle@2x.png | Bin 0 -> 31796 bytes .../Resources/special-skin/hitcircle@2x.png | Bin 0 -> 263521 bytes .../special-skin/hitcircleoverlay@2x.png | Bin 0 -> 262013 bytes .../SkinnableTestScene.cs | 66 ++++++++++++++++++ .../TestSceneHitCircle.cs | 44 ++++++------ osu.Game/Skinning/SkinManager.cs | 6 +- .../Tests/Visual}/OsuGridTestScene.cs | 2 +- osu.iOS.props | 2 +- 14 files changed, 92 insertions(+), 28 deletions(-) create mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/approachcircle@2x.png create mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300k@2x.png create mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hitcircle@2x.png create mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hitcircleoverlay@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hitcircle@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hitcircleoverlay@2x.png create mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/special-skin/approachcircle@2x.png create mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircle@2x.png create mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircleoverlay@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs rename {osu.Game.Tests/Visual/UserInterface => osu.Game/Tests/Visual}/OsuGridTestScene.cs (97%) diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/approachcircle@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/approachcircle@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..db2f4a5730b80c3488c618fe825e322742e4007c GIT binary patch literal 18164 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
xUBq)wpYuSo%L4;iD|W&d3&2irtb$J#-rmG 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 tq+89yAYBs>kLVeUC5rTls%z@vF}1DU#4;$aN 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 R|M{Pbx-w?>O~E?q{{RqUO*sGn literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hitcircleoverlay@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hitcircleoverlay@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4233d9bb6e6aa88c7da5c1998439789cc5f9c7b2 GIT binary patch literal 38217 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/approachcircle@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/approachcircle@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..5d28b3f7cb5b7d682686c9a93a994ae506f68f2f GIT binary patch literal 31796 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircle@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircle@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..b4eb5221ffd0c2791d079ebfc6bafcf0ac7a31ba GIT binary patch literal 263521 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

ga#N){uvTxT7 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. Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.IO; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.IO.Stores; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public abstract class SkinnableTestScene : OsuGridTestScene + { + private static Skin getSkinFromResources(SkinManager skins, string name) + { + using (var storage = new DllResourceStore("osu.Game.Rulesets.Osu.Tests.dll")) + { + var tempName = Path.GetTempFileName(); + + File.Delete(tempName); + Directory.CreateDirectory(tempName); + + var files = storage.GetAvailableResources().Where(f => f.StartsWith($"Resources/{name}")); + + foreach (var file in files) + using (var stream = storage.GetStream(file)) + using (var newFile = File.Create(Path.Combine(tempName, Path.GetFileName(file)))) + stream.CopyTo(newFile); + + return skins.GetSkin(skins.Import(tempName).Result); + } + } + + private Skin metricsSkin; + private Skin defaultSkin; + private Skin specialSkin; + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + var skins = new SkinManager(LocalStorage, ContextFactory, null, audio); + + metricsSkin = getSkinFromResources(skins, "metrics_skin"); + defaultSkin = getSkinFromResources(skins, "default_skin"); + specialSkin = getSkinFromResources(skins, "special_skin"); + } + + public void SetContents(Func creationFunction) + { + Cell(0).Child = new LocalSkinOverrideContainer(null) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction()); + Cell(1).Child = new LocalSkinOverrideContainer(metricsSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction()); + Cell(2).Child = new LocalSkinOverrideContainer(defaultSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction()); + Cell(3).Child = new LocalSkinOverrideContainer(specialSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction()); + } + + protected SkinnableTestScene() + : base(2, 2) + { + } + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs index d44a0cd841..8692744c0d 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs @@ -7,7 +7,6 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Tests.Visual; using osuTK; using System.Collections.Generic; using System; @@ -19,37 +18,32 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Tests { [TestFixture] - public class TestSceneHitCircle : OsuTestScene + public class TestSceneHitCircle : SkinnableTestScene { public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableHitCircle) }; - private readonly Container content; - protected override Container Content => content; - private int depthIndex; public TestSceneHitCircle() { - base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); - - AddStep("Miss Big Single", () => testSingle(2)); - AddStep("Miss Medium Single", () => testSingle(5)); - AddStep("Miss Small Single", () => testSingle(7)); - AddStep("Hit Big Single", () => testSingle(2, true)); - AddStep("Hit Medium Single", () => testSingle(5, true)); - AddStep("Hit Small Single", () => testSingle(7, true)); - AddStep("Miss Big Stream", () => testStream(2)); - AddStep("Miss Medium Stream", () => testStream(5)); - AddStep("Miss Small Stream", () => testStream(7)); - AddStep("Hit Big Stream", () => testStream(2, true)); - AddStep("Hit Medium Stream", () => testStream(5, true)); - AddStep("Hit Small Stream", () => testStream(7, true)); + AddStep("Miss Big Single", () => SetContents(() => testSingle(2))); + AddStep("Miss Medium Single", () => SetContents(() => testSingle(5))); + AddStep("Miss Small Single", () => SetContents(() => testSingle(7))); + AddStep("Hit Big Single", () => SetContents(() => testSingle(2, true))); + AddStep("Hit Medium Single", () => SetContents(() => testSingle(5, true))); + AddStep("Hit Small Single", () => SetContents(() => testSingle(7, true))); + AddStep("Miss Big Stream", () => SetContents(() => testStream(2))); + AddStep("Miss Medium Stream", () => SetContents(() => testStream(5))); + AddStep("Miss Small Stream", () => SetContents(() => testStream(7))); + AddStep("Hit Big Stream", () => SetContents(() => testStream(2, true))); + AddStep("Hit Medium Stream", () => SetContents(() => testStream(5, true))); + AddStep("Hit Small Stream", () => SetContents(() => testStream(7, true))); } - private void testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null) + private Drawable testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null) { positionOffset = positionOffset ?? Vector2.Zero; @@ -70,18 +64,22 @@ namespace osu.Game.Rulesets.Osu.Tests foreach (var mod in Mods.Value.OfType()) mod.ApplyToDrawableHitObjects(new[] { drawable }); - Add(drawable); + return drawable; } - private void testStream(float circleSize, bool auto = false) + private Drawable testStream(float circleSize, bool auto = false) { + var container = new Container { RelativeSizeAxes = Axes.Both }; + Vector2 pos = new Vector2(-250, 0); for (int i = 0; i <= 1000; i += 100) { - testSingle(circleSize, auto, i, pos); + container.Add(testSingle(circleSize, auto, i, pos)); pos.X += 50; } + + return container; } protected class TestDrawableHitCircle : DrawableHitCircle diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 70abfac501..19997e8844 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -45,7 +45,7 @@ namespace osu.Game.Skinning CurrentSkinInfo.Value = SkinInfo.Default; }; - CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = getSkin(skin.NewValue); + CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue); CurrentSkin.ValueChanged += skin => { if (skin.NewValue.SkinInfo != CurrentSkinInfo.Value) @@ -80,7 +80,7 @@ namespace osu.Game.Skinning { await base.Populate(model, archive, cancellationToken); - Skin reference = getSkin(model); + Skin reference = GetSkin(model); if (!string.IsNullOrEmpty(reference.Configuration.SkinInfo.Name)) { @@ -99,7 +99,7 @@ namespace osu.Game.Skinning ///

0xJlZ?;ZV^q_mZ9Q!A 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircleoverlay@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircleoverlay@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..69b601e006d36419bf3392ec832097b3035b8676 GIT binary patch literal 262013 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

/// The skin to lookup. /// A instance correlating to the provided . - private Skin getSkin(SkinInfo skinInfo) + public Skin GetSkin(SkinInfo skinInfo) { if (skinInfo == SkinInfo.Default) return new DefaultSkin(); diff --git a/osu.Game.Tests/Visual/UserInterface/OsuGridTestScene.cs b/osu.Game/Tests/Visual/OsuGridTestScene.cs similarity index 97% rename from osu.Game.Tests/Visual/UserInterface/OsuGridTestScene.cs rename to osu.Game/Tests/Visual/OsuGridTestScene.cs index 096ac951de..c09f4d6218 100644 --- a/osu.Game.Tests/Visual/UserInterface/OsuGridTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGridTestScene.cs @@ -5,7 +5,7 @@ using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -namespace osu.Game.Tests.Visual.UserInterface +namespace osu.Game.Tests.Visual { /// /// An abstract test case which exposes small cells arranged in a grid. diff --git a/osu.iOS.props b/osu.iOS.props index 55d1afa645..e87e759492 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -35,7 +35,7 @@ prompt 4 - iPhone Distribution + iPhone Developer true true Entitlements.plist From 466297df552500a5d789aa8e846469474332b729 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Jul 2019 17:05:13 +0900 Subject: [PATCH 1934/2854] Fix shaking test --- osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs | 12 +++++++----- osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs | 15 +++++++-------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs index 8692744c0d..84a7bfc53e 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs @@ -55,11 +55,7 @@ namespace osu.Game.Rulesets.Osu.Tests circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize }); - var drawable = new TestDrawableHitCircle(circle, auto) - { - Anchor = Anchor.Centre, - Depth = depthIndex++ - }; + var drawable = CreateDrawableHitCircle(circle, auto); foreach (var mod in Mods.Value.OfType()) mod.ApplyToDrawableHitObjects(new[] { drawable }); @@ -67,6 +63,12 @@ namespace osu.Game.Rulesets.Osu.Tests return drawable; } + protected virtual TestDrawableHitCircle CreateDrawableHitCircle(HitCircle circle, bool auto) => new TestDrawableHitCircle(circle, auto) + { + Anchor = Anchor.Centre, + Depth = depthIndex++ + }; + private Drawable testStream(float circleSize, bool auto = false) { var container = new Container { RelativeSizeAxes = Axes.Both }; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs index 3d8afd66f4..84a73c7cfc 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs @@ -1,23 +1,22 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics; using osu.Framework.MathUtils; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Tests { public class TestSceneShaking : TestSceneHitCircle { - public override void Add(Drawable drawable) + protected override TestDrawableHitCircle CreateDrawableHitCircle(HitCircle circle, bool auto) { - base.Add(drawable); + var drawableHitObject = base.CreateDrawableHitCircle(circle, auto); - if (drawable is TestDrawableHitCircle hitObject) - { - Scheduler.AddDelayed(() => hitObject.TriggerJudgement(), - hitObject.HitObject.StartTime - (hitObject.HitObject.HitWindows.HalfWindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current); - } + Scheduler.AddDelayed(() => drawableHitObject.TriggerJudgement(), + drawableHitObject.HitObject.StartTime - (drawableHitObject.HitObject.HitWindows.HalfWindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current); + + return drawableHitObject; } } } From e579bce18afab63f9c8e63a0bd32418a12378b9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Jul 2019 18:14:06 +0900 Subject: [PATCH 1935/2854] Don't report delta patch failures to sentry --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index 78a1e680ec..fa41c061b5 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -27,6 +27,8 @@ namespace osu.Desktop.Updater public Task PrepareUpdateAsync() => UpdateManager.RestartAppWhenExited(); + private static readonly Logger logger = Logger.GetLogger("updater"); + [BackgroundDependencyLoader] private void load(NotificationOverlay notification, OsuGameBase game) { @@ -77,7 +79,7 @@ namespace osu.Desktop.Updater { if (useDeltaPatching) { - Logger.Error(e, @"delta patching failed!"); + logger.Add(@"delta patching failed; will attempt full download!"); //could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959) //try again without deltas. @@ -163,16 +165,11 @@ namespace osu.Desktop.Updater { public LogLevel Level { get; set; } = LogLevel.Info; - private Logger logger; - public void Write(string message, LogLevel logLevel) { if (logLevel < Level) return; - if (logger == null) - logger = Logger.GetLogger("updater"); - logger.Add(message); } From 98813222afbeadbf823af00724fa23a4a9a55bbd Mon Sep 17 00:00:00 2001 From: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com> Date: Mon, 29 Jul 2019 18:35:22 +0900 Subject: [PATCH 1936/2854] Adjust comment --- osu.Game/Skinning/SkinReloadableDrawable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs index 397c6656a5..4bbdeafba5 100644 --- a/osu.Game/Skinning/SkinReloadableDrawable.cs +++ b/osu.Game/Skinning/SkinReloadableDrawable.cs @@ -38,7 +38,7 @@ namespace osu.Game.Skinning private void onChange() => // schedule required to avoid calls after disposed. - // note that this has the side-effect of components only performance a skin change when they are alive. + // note that this has the side-effect of components only performing a skin change when they are alive. Scheduler.AddOnce(() => SkinChanged(skin, allowDefaultFallback)); protected override void LoadAsyncComplete() From ac01e9fbebbe271c368361aa31e29193e4140e1a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Jul 2019 18:36:07 +0900 Subject: [PATCH 1937/2854] Fix legacy scores with no online ID being imported with a non-null ID --- osu.Game/Scoring/Legacy/LegacyScoreParser.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs index 0fdbd56c92..2e4b4b3a9a 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs @@ -80,6 +80,9 @@ namespace osu.Game.Scoring.Legacy else if (version >= 20121008) scoreInfo.OnlineScoreID = sr.ReadInt32(); + if (scoreInfo.OnlineScoreID <= 0) + scoreInfo.OnlineScoreID = null; + if (compressedReplay?.Length > 0) { using (var replayInStream = new MemoryStream(compressedReplay)) From 396892da1aa4bd2aeff96155d477189db3b1cb26 Mon Sep 17 00:00:00 2001 From: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com> Date: Mon, 29 Jul 2019 18:43:05 +0900 Subject: [PATCH 1938/2854] Describe how the ratio came to be --- osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs index 8b6b483618..f7888f8022 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs @@ -18,6 +18,7 @@ namespace osu.Game.Rulesets.Osu.UI Anchor = Anchor.Centre; Origin = Anchor.Centre; + // Calculated from osu!stable as 512 (default gamefield size) / 640 (default window size) Size = new Vector2(0.8f); InternalChild = new Container From 0635680db52db1d6063c09dbdf88fa459a07b64f Mon Sep 17 00:00:00 2001 From: jorolf Date: Mon, 29 Jul 2019 11:49:59 +0200 Subject: [PATCH 1939/2854] fix some code quality --- osu.Game/Skinning/LegacySkin.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 24c6c9e4ec..c9c64c96f2 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -54,28 +54,33 @@ namespace osu.Game.Skinning public override Drawable GetDrawableComponent(string componentName) { bool animatable = false; - (bool looping, double frametime) animationData = (false, 1000 / 60d); + bool looping = true; + const double frametime = 1000 / 60d; switch (componentName) { case "Play/Miss": componentName = "hit0"; animatable = true; + looping = false; break; case "Play/Meh": componentName = "hit50"; animatable = true; + looping = false; break; case "Play/Good": componentName = "hit100"; animatable = true; + looping = false; break; case "Play/Great": componentName = "hit300"; animatable = true; + looping = false; break; case "Play/osu/number-text": @@ -93,7 +98,7 @@ namespace osu.Game.Skinning if (texture != null && animatable) { - var animation = new TextureAnimation { DefaultFrameLength = animationData.frametime }; + var animation = new TextureAnimation { DefaultFrameLength = frametime }; for (int i = 1; texture != null; i++) { @@ -101,7 +106,9 @@ namespace osu.Game.Skinning texture = GetTexture($"{componentName}-{i}"); } - animation.Repeat = animationData.looping; + // This comment can be removed once we have components which are looping + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + animation.Repeat = looping; return animation; } @@ -109,10 +116,7 @@ namespace osu.Game.Skinning { texture = GetTexture(componentName); - if (texture == null) - return null; - - return new Sprite { Texture = texture }; + return texture == null ? null : new Sprite { Texture = texture }; } } From c01461b9511c553c8e0c5b54e63f538cd739f78d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 29 Jul 2019 19:12:41 +0900 Subject: [PATCH 1940/2854] Recalculate path size when path radius changes --- .../Objects/Drawables/Pieces/SliderBody.cs | 2 +- .../Objects/Drawables/Pieces/SnakingSliderBody.cs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 6bc19ee3b5..24a437c20e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces protected Path Path => path; - public float PathRadius + public virtual float PathRadius { get => path.PathRadius; set => path.PathRadius = value; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs index a3d3893c8b..0590ca1d96 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs @@ -24,6 +24,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public double? SnakedStart { get; private set; } public double? SnakedEnd { get; private set; } + public override float PathRadius + { + get => base.PathRadius; + set + { + if (base.PathRadius == value) + return; + + base.PathRadius = value; + + Refresh(); + } + } + public override Vector2 PathOffset => snakedPathOffset; /// From b4bcdb1c7013f2013c147a794111d2d96c0dc384 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Jul 2019 19:44:58 +0900 Subject: [PATCH 1941/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 6744590f0d..2c5e54d4f5 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0b2baa982b..472f6e9d65 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 55d1afa645..0a19eac2b5 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From f21a2f7e5e4ced31256d8bdd0ef9d64b24426e15 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Jul 2019 21:49:12 +0900 Subject: [PATCH 1942/2854] Fix version overlay displaying briefly before it should --- osu.Desktop/OsuGameDesktop.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 18f0cd1f80..cd39ca0699 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -52,11 +52,7 @@ namespace osu.Desktop if (!noVersionOverlay) { - LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, v => - { - Add(v); - v.Show(); - }); + LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, Add); if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows) Add(new SquirrelUpdateManager()); From ece5a9622e53160e337b699c3d7cd2c164c29cb5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 06:31:45 +0900 Subject: [PATCH 1943/2854] Fix scores without an online ID not always presenting correctly --- osu.Game/OsuGame.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 9358a412d9..e71dd67bf2 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -265,7 +265,16 @@ namespace osu.Game { // The given ScoreInfo may have missing properties if it was retrieved from online data. Re-retrieve it from the database // to ensure all the required data for presenting a replay are present. - var databasedScoreInfo = ScoreManager.Query(s => s.OnlineScoreID == score.OnlineScoreID); + var databasedScoreInfo = score.OnlineScoreID != null + ? ScoreManager.Query(s => s.OnlineScoreID == score.OnlineScoreID) + : ScoreManager.Query(s => s.Hash == score.Hash); + + if (databasedScoreInfo == null) + { + Logger.Log("The requested score could not be found locally.", LoggingTarget.Information); + return; + } + var databasedScore = ScoreManager.GetScore(databasedScoreInfo); if (databasedScore.Replay == null) From cec26a270ecf65453e808f5c6b5d5634c5e59ade Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 07:03:59 +0900 Subject: [PATCH 1944/2854] Fix using right mouse button to drag at song select seeking incorrectly with many beatmaps loaded Closes #5195 --- .../Graphics/Containers/OsuScrollContainer.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs index 53092ddc9e..8fc8dec9fd 100644 --- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs +++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs @@ -27,11 +27,12 @@ namespace osu.Game.Graphics.Containers private bool shouldPerformRightMouseScroll(MouseButtonEvent e) => RightMouseScrollbar && e.Button == MouseButton.Right; - private void scrollToRelative(float value) => ScrollTo(Clamp((value - Scrollbar.DrawSize[ScrollDim] / 2) / Scrollbar.Size[ScrollDim]), true, DistanceDecayOnRightMouseScrollbar); + private void scrollFromMouseEvent(MouseEvent e) => + ScrollTo(Clamp(ToLocalSpace(e.ScreenSpaceMousePosition)[ScrollDim] / DrawSize[ScrollDim]) * Content.DrawSize[ScrollDim], true, DistanceDecayOnRightMouseScrollbar); - private bool mouseScrollBarDragging; + private bool rightMouseDragging; - protected override bool IsDragging => base.IsDragging || mouseScrollBarDragging; + protected override bool IsDragging => base.IsDragging || rightMouseDragging; public OsuScrollContainer(Direction scrollDirection = Direction.Vertical) : base(scrollDirection) @@ -42,7 +43,7 @@ namespace osu.Game.Graphics.Containers { if (shouldPerformRightMouseScroll(e)) { - scrollToRelative(e.MousePosition[ScrollDim]); + scrollFromMouseEvent(e); return true; } @@ -51,9 +52,9 @@ namespace osu.Game.Graphics.Containers protected override bool OnDrag(DragEvent e) { - if (mouseScrollBarDragging) + if (rightMouseDragging) { - scrollToRelative(e.MousePosition[ScrollDim]); + scrollFromMouseEvent(e); return true; } @@ -64,7 +65,7 @@ namespace osu.Game.Graphics.Containers { if (shouldPerformRightMouseScroll(e)) { - mouseScrollBarDragging = true; + rightMouseDragging = true; return true; } @@ -73,9 +74,9 @@ namespace osu.Game.Graphics.Containers protected override bool OnDragEnd(DragEndEvent e) { - if (mouseScrollBarDragging) + if (rightMouseDragging) { - mouseScrollBarDragging = false; + rightMouseDragging = false; return true; } From 62f69581652254283a82b75d4e16382b2aa40bde Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 07:23:25 +0900 Subject: [PATCH 1945/2854] Fix tournament tests not running --- .../TestSceneDrawableTournamentMatch.cs | 3 +- osu.Game.Tournament.Tests/LadderTestScene.cs | 5 ++-- .../Screens/TestSceneGameplayScreen.cs | 3 +- .../Screens/TestSceneScheduleScreen.cs | 3 +- .../Screens/TestSceneShowcaseScreen.cs | 3 +- .../TestSceneTournamentSceneManager.cs | 3 +- .../TournamentTestScene.cs | 28 +++++++++++++++++++ 7 files changed, 36 insertions(+), 12 deletions(-) create mode 100644 osu.Game.Tournament.Tests/TournamentTestScene.cs diff --git a/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentMatch.cs b/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentMatch.cs index f329623703..e65b708fea 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentMatch.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentMatch.cs @@ -5,14 +5,13 @@ using System; using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Tests.Visual; using osu.Game.Tournament.Components; using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.Ladder.Components; namespace osu.Game.Tournament.Tests.Components { - public class TestSceneDrawableTournamentMatch : OsuTestScene + public class TestSceneDrawableTournamentMatch : TournamentTestScene { public override IReadOnlyList RequiredTypes => new[] { diff --git a/osu.Game.Tournament.Tests/LadderTestScene.cs b/osu.Game.Tournament.Tests/LadderTestScene.cs index b49341d0d1..dae0721023 100644 --- a/osu.Game.Tournament.Tests/LadderTestScene.cs +++ b/osu.Game.Tournament.Tests/LadderTestScene.cs @@ -1,13 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using NUnit.Framework; using osu.Framework.Allocation; -using osu.Game.Tests.Visual; using osu.Game.Tournament.Models; namespace osu.Game.Tournament.Tests { - public abstract class LadderTestScene : OsuTestScene + [TestFixture] + public abstract class LadderTestScene : TournamentTestScene { [Resolved] protected LadderInfo Ladder { get; private set; } diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs index 201736f38a..9de00818a5 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs @@ -2,13 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Game.Tests.Visual; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Gameplay; namespace osu.Game.Tournament.Tests.Screens { - public class TestSceneGameplayScreen : OsuTestScene + public class TestSceneGameplayScreen : TournamentTestScene { [Cached] private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay(); diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs index f3e65919eb..2277302e98 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs @@ -2,12 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Game.Tests.Visual; using osu.Game.Tournament.Screens.Schedule; namespace osu.Game.Tournament.Tests.Screens { - public class TestSceneScheduleScreen : OsuTestScene + public class TestSceneScheduleScreen : TournamentTestScene { [BackgroundDependencyLoader] private void load() diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneShowcaseScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneShowcaseScreen.cs index edf1477b06..8c43e25416 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneShowcaseScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneShowcaseScreen.cs @@ -2,12 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Game.Tests.Visual; using osu.Game.Tournament.Screens.Showcase; namespace osu.Game.Tournament.Tests.Screens { - public class TestSceneShowcaseScreen : OsuTestScene + public class TestSceneShowcaseScreen : TournamentTestScene { [BackgroundDependencyLoader] private void load() diff --git a/osu.Game.Tournament.Tests/TestSceneTournamentSceneManager.cs b/osu.Game.Tournament.Tests/TestSceneTournamentSceneManager.cs index 378614343a..4d134ce4af 100644 --- a/osu.Game.Tournament.Tests/TestSceneTournamentSceneManager.cs +++ b/osu.Game.Tournament.Tests/TestSceneTournamentSceneManager.cs @@ -3,11 +3,10 @@ using osu.Framework.Allocation; using osu.Framework.Platform; -using osu.Game.Tests.Visual; namespace osu.Game.Tournament.Tests { - public class TestSceneTournamentSceneManager : OsuTestScene + public class TestSceneTournamentSceneManager : TournamentTestScene { [BackgroundDependencyLoader] private void load(Storage storage) diff --git a/osu.Game.Tournament.Tests/TournamentTestScene.cs b/osu.Game.Tournament.Tests/TournamentTestScene.cs new file mode 100644 index 0000000000..18ac3230da --- /dev/null +++ b/osu.Game.Tournament.Tests/TournamentTestScene.cs @@ -0,0 +1,28 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Testing; +using osu.Game.Tests.Visual; + +namespace osu.Game.Tournament.Tests +{ + public abstract class TournamentTestScene : OsuTestScene + { + protected override ITestSceneTestRunner CreateRunner() => new TournamentTestSceneTestRunner(); + + public class TournamentTestSceneTestRunner : TournamentGameBase, ITestSceneTestRunner + { + private TestSceneTestRunner.TestRunner runner; + + protected override void LoadAsyncComplete() + { + // this has to be run here rather than LoadComplete because + // TestScene.cs is checking the IsLoaded state (on another thread) and expects + // the runner to be loaded at that point. + Add(runner = new TestSceneTestRunner.TestRunner()); + } + + public void RunTestBlocking(TestScene test) => runner.RunTestBlocking(test); + } + } +} From 1d1372d639066b0086f50750e08dbda679727bbb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 07:42:19 +0900 Subject: [PATCH 1946/2854] Remove forceful resizing --- osu.Game.Tournament/TournamentGameBase.cs | 70 ++++++++++++++++------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 06fb52da77..dbfa70704b 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -9,15 +9,20 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Game.Beatmaps; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Models; +using osuTK.Graphics; using osuTK.Input; namespace osu.Game.Tournament @@ -35,6 +40,8 @@ namespace osu.Game.Tournament private Bindable windowSize; private FileBasedIPC ipc; + private Drawable heightWarning; + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { return dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); @@ -53,6 +60,12 @@ namespace osu.Game.Tournament this.storage = storage; windowSize = frameworkConfig.GetBindable(FrameworkSetting.WindowedSize); + windowSize.BindValueChanged(size => ScheduleAfterChildren(() => + { + var minWidth = (int)(size.NewValue.Height / 9f * 16 + 400); + + heightWarning.Alpha = size.NewValue.Width < minWidth ? 1 : 0; + }), true); readBracket(); @@ -61,16 +74,43 @@ namespace osu.Game.Tournament dependencies.CacheAs(ipc = new FileBasedIPC()); Add(ipc); - Add(new OsuButton + AddRange(new[] { - Text = "Save Changes", - Width = 140, - Height = 50, - Depth = float.MinValue, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Padding = new MarginPadding(10), - Action = SaveChanges, + new OsuButton + { + Text = "Save Changes", + Width = 140, + Height = 50, + Depth = float.MinValue, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Padding = new MarginPadding(10), + Action = SaveChanges, + }, + heightWarning = new Container + { + Masking = true, + CornerRadius = 5, + Depth = float.MinValue, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Red, + RelativeSizeAxes = Axes.Both, + }, + new SpriteText + { + Text = "Please make the window wider", + Font = OsuFont.Default.With(weight: "bold"), + Colour = Color4.White, + Padding = new MarginPadding(20) + } + } + }, }); } @@ -195,18 +235,6 @@ namespace osu.Game.Tournament base.LoadComplete(); } - protected override void Update() - { - base.Update(); - var minWidth = (int)(windowSize.Value.Height / 9f * 16 + 400); - - if (windowSize.Value.Width < minWidth) - { - // todo: can be removed after ppy/osu-framework#1975 - windowSize.Value = Host.Window.ClientSize = new Size(minWidth, windowSize.Value.Height); - } - } - protected virtual void SaveChanges() { foreach (var r in ladder.Rounds) From a16c0f2aa0b3ac2962d8921d8145334d69e00a84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 12:44:08 +0900 Subject: [PATCH 1947/2854] Don't report stable storage msising to sentry --- osu.Desktop/OsuGameDesktop.cs | 4 ++-- osu.Game/Utils/RavenLogger.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 18f0cd1f80..4d68148a36 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -38,9 +38,9 @@ namespace osu.Desktop if (Host is DesktopGameHost desktopHost) return new StableStorage(desktopHost); } - catch (Exception e) + catch (Exception) { - Logger.Error(e, "Error while searching for stable install"); + Logger.Log("Could not find a stable install", LoggingTarget.Runtime, LogLevel.Important); } return null; diff --git a/osu.Game/Utils/RavenLogger.cs b/osu.Game/Utils/RavenLogger.cs index 7f4faa60ae..0a6f40a0a6 100644 --- a/osu.Game/Utils/RavenLogger.cs +++ b/osu.Game/Utils/RavenLogger.cs @@ -54,7 +54,7 @@ namespace osu.Game.Utils } lastException = exception; - queuePendingTask(raven.CaptureAsync(new SentryEvent(exception))); + queuePendingTask(raven.CaptureAsync(new SentryEvent(exception) { Message = entry.Message })); } else raven.AddTrail(new Breadcrumb(entry.Target.ToString(), BreadcrumbType.Navigation) { Message = entry.Message }); From 370a81d78a9963e3c2109cfe73b7a954fdf3b818 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 13:30:26 +0900 Subject: [PATCH 1948/2854] Don't forward timeout exceptions to sentry --- osu.Game/Utils/RavenLogger.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Utils/RavenLogger.cs b/osu.Game/Utils/RavenLogger.cs index 7f4faa60ae..80786d2c72 100644 --- a/osu.Game/Utils/RavenLogger.cs +++ b/osu.Game/Utils/RavenLogger.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Net; using System.Threading.Tasks; using osu.Framework.Logging; using SharpRaven; @@ -46,6 +47,16 @@ namespace osu.Game.Utils return; } + if (exception is WebException we) + { + switch (we.Status) + { + // more statuses may need to be blocked as we come across them. + case WebExceptionStatus.Timeout: + return; + } + } + // since we let unhandled exceptions go ignored at times, we want to ensure they don't get submitted on subsequent reports. if (lastException != null && lastException.Message == exception.Message && exception.StackTrace.StartsWith(lastException.StackTrace)) From 6f91a21f00d9f4acce39109953c8f384b2d60da9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 13:31:57 +0900 Subject: [PATCH 1949/2854] Use switch --- osu.Game/Utils/RavenLogger.cs | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/osu.Game/Utils/RavenLogger.cs b/osu.Game/Utils/RavenLogger.cs index 80786d2c72..2a396d35cf 100644 --- a/osu.Game/Utils/RavenLogger.cs +++ b/osu.Game/Utils/RavenLogger.cs @@ -37,24 +37,27 @@ namespace osu.Game.Utils if (exception != null) { - if (exception is IOException ioe) + switch (exception) { - // disk full exceptions, see https://stackoverflow.com/a/9294382 - const int hr_error_handle_disk_full = unchecked((int)0x80070027); - const int hr_error_disk_full = unchecked((int)0x80070070); + case IOException ioe: + // disk full exceptions, see https://stackoverflow.com/a/9294382 + const int hr_error_handle_disk_full = unchecked((int)0x80070027); + const int hr_error_disk_full = unchecked((int)0x80070070); - if (ioe.HResult == hr_error_handle_disk_full || ioe.HResult == hr_error_disk_full) - return; - } - - if (exception is WebException we) - { - switch (we.Status) - { - // more statuses may need to be blocked as we come across them. - case WebExceptionStatus.Timeout: + if (ioe.HResult == hr_error_handle_disk_full || ioe.HResult == hr_error_disk_full) return; - } + + break; + + case WebException we: + switch (we.Status) + { + // more statuses may need to be blocked as we come across them. + case WebExceptionStatus.Timeout: + return; + } + + break; } // since we let unhandled exceptions go ignored at times, we want to ensure they don't get submitted on subsequent reports. From 195609816674f0428a281d12e4c0757cba08169a Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 30 Jul 2019 13:58:08 +0900 Subject: [PATCH 1950/2854] Add a fallback for humanizer localization failure --- osu.Game/Graphics/DrawableDate.cs | 4 +-- .../BeatmapSet/Scores/TopScoreUserSection.cs | 4 +-- osu.Game/Utils/HumanizerUtils.cs | 36 +++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Utils/HumanizerUtils.cs diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs index 125c994c92..533f02af7b 100644 --- a/osu.Game/Graphics/DrawableDate.cs +++ b/osu.Game/Graphics/DrawableDate.cs @@ -2,11 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; -using Humanizer; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Game.Graphics.Sprites; +using osu.Game.Utils; namespace osu.Game.Graphics { @@ -71,7 +71,7 @@ namespace osu.Game.Graphics Scheduler.AddDelayed(updateTimeWithReschedule, timeUntilNextUpdate); } - protected virtual string Format() => Date.Humanize(); + protected virtual string Format() => HumanizerUtils.Humanize(Date); private void updateTime() => Text = Format(); diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index ffc39e5af2..38a909411a 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using Humanizer; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -14,6 +13,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Online.Leaderboards; using osu.Game.Scoring; using osu.Game.Users.Drawables; +using osu.Game.Utils; using osuTK; using osuTK.Graphics; @@ -132,7 +132,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { avatar.User = value.User; flag.Country = value.User.Country; - date.Text = $@"achieved {value.Date.Humanize()}"; + date.Text = $@"achieved {HumanizerUtils.Humanize(value.Date)}"; usernameText.Clear(); usernameText.AddUserLink(value.User); diff --git a/osu.Game/Utils/HumanizerUtils.cs b/osu.Game/Utils/HumanizerUtils.cs new file mode 100644 index 0000000000..398c76a09f --- /dev/null +++ b/osu.Game/Utils/HumanizerUtils.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Globalization; +using Humanizer; + +namespace osu.Game.Utils +{ + public static class HumanizerUtils + { + /// + /// Humanizes a string using the system culture, then falls back if one cannot be found. + /// + /// A localization lookup failure will throw an exception of type + /// + /// + /// The time to humanize. + /// A humanized string of the given time. + public static string Humanize(DateTimeOffset dateTimeOffset) + { + string offset; + + try + { + offset = dateTimeOffset.Humanize(); + } + catch (ArgumentException) + { + offset = dateTimeOffset.Humanize(culture: new CultureInfo("en-US")); + } + + return offset; + } + } +} From 20b611486350e69582f7a5a126463f4ad1e78db7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 17:52:06 +0900 Subject: [PATCH 1951/2854] Reduce method complexity --- osu.Game/Utils/RavenLogger.cs | 54 +++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/osu.Game/Utils/RavenLogger.cs b/osu.Game/Utils/RavenLogger.cs index 2a396d35cf..6aaf55e045 100644 --- a/osu.Game/Utils/RavenLogger.cs +++ b/osu.Game/Utils/RavenLogger.cs @@ -37,35 +37,13 @@ namespace osu.Game.Utils if (exception != null) { - switch (exception) - { - case IOException ioe: - // disk full exceptions, see https://stackoverflow.com/a/9294382 - const int hr_error_handle_disk_full = unchecked((int)0x80070027); - const int hr_error_disk_full = unchecked((int)0x80070070); - - if (ioe.HResult == hr_error_handle_disk_full || ioe.HResult == hr_error_disk_full) - return; - - break; - - case WebException we: - switch (we.Status) - { - // more statuses may need to be blocked as we come across them. - case WebExceptionStatus.Timeout: - return; - } - - break; - } + if (!shouldSubmitException(exception)) + return; // since we let unhandled exceptions go ignored at times, we want to ensure they don't get submitted on subsequent reports. if (lastException != null && lastException.Message == exception.Message && exception.StackTrace.StartsWith(lastException.StackTrace)) - { return; - } lastException = exception; queuePendingTask(raven.CaptureAsync(new SentryEvent(exception))); @@ -75,6 +53,34 @@ namespace osu.Game.Utils }; } + private bool shouldSubmitException(Exception exception) + { + switch (exception) + { + case IOException ioe: + // disk full exceptions, see https://stackoverflow.com/a/9294382 + const int hr_error_handle_disk_full = unchecked((int)0x80070027); + const int hr_error_disk_full = unchecked((int)0x80070070); + + if (ioe.HResult == hr_error_handle_disk_full || ioe.HResult == hr_error_disk_full) + return false; + + break; + + case WebException we: + switch (we.Status) + { + // more statuses may need to be blocked as we come across them. + case WebExceptionStatus.Timeout: + return false; + } + + break; + } + + return true; + } + private void queuePendingTask(Task task) { lock (tasks) tasks.Add(task); From e8e5b2742d545d1344769afe965073aaeae00e3f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 18:31:57 +0900 Subject: [PATCH 1952/2854] Fix manual clock usage --- .../Visual/Gameplay/TestSceneBreakOverlay.cs | 65 +++++++++---------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs index eaae647dbc..ed5861b47d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Bindables; using osu.Framework.Timing; using osu.Game.Beatmaps.Timing; using osu.Game.Screens.Play; @@ -44,7 +43,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestShowBreaks() { - loadClockStep(false); + setClock(false); addShowBreakStep(2); addShowBreakStep(5); @@ -56,7 +55,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var shortBreak = new BreakPeriod { EndTime = 500 }; - loadClockStep(true); + setClock(true); loadBreaksStep("short break", new[] { shortBreak }); addBreakSeeks(shortBreak, false); @@ -65,7 +64,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestMultipleBreaks() { - loadClockStep(true); + setClock(true); loadBreaksStep("multiple breaks", testBreaks); foreach (var b in testBreaks) @@ -75,7 +74,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestRewindBreaks() { - loadClockStep(true); + setClock(true); loadBreaksStep("multiple breaks", testBreaks); foreach (var b in testBreaks.Reverse()) @@ -85,7 +84,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestSkipBreaks() { - loadClockStep(true); + setClock(true); loadBreaksStep("multiple breaks", testBreaks); addBreakSeeks(testBreaks.Last(), false); @@ -103,46 +102,50 @@ namespace osu.Game.Tests.Visual.Gameplay }); } - private void loadClockStep(bool loadManual) + private void setClock(bool useManual) { - AddStep($"load {(loadManual ? "manual" : "normal")} clock", () => breakOverlay.SwitchClock(loadManual)); + AddStep($"set {(useManual ? "manual" : "realtime")} clock", () => breakOverlay.SwitchClock(useManual)); } private void loadBreaksStep(string breakDescription, IReadOnlyList breaks) { AddStep($"load {breakDescription}", () => breakOverlay.Breaks = breaks); - seekBreakStep("seek back to 0", 0, false); + seekAndAssertBreak("seek back to 0", 0, false); } private void addBreakSeeks(BreakPeriod b, bool isReversed) { if (isReversed) { - seekBreakStep("seek to break after end", b.EndTime + 500, false); - seekBreakStep("seek to break end", b.EndTime, false); - seekBreakStep("seek to break middle", b.StartTime + b.Duration / 2, b.HasEffect); - seekBreakStep("seek to break start", b.StartTime, b.HasEffect); + seekAndAssertBreak("seek to break after end", b.EndTime + 500, false); + seekAndAssertBreak("seek to break end", b.EndTime, false); + seekAndAssertBreak("seek to break middle", b.StartTime + b.Duration / 2, b.HasEffect); + seekAndAssertBreak("seek to break start", b.StartTime, b.HasEffect); } else { - seekBreakStep("seek to break start", b.StartTime, b.HasEffect); - seekBreakStep("seek to break middle", b.StartTime + b.Duration / 2, b.HasEffect); - seekBreakStep("seek to break end", b.EndTime, false); - seekBreakStep("seek to break after end", b.EndTime + 500, false); + seekAndAssertBreak("seek to break start", b.StartTime, b.HasEffect); + seekAndAssertBreak("seek to break middle", b.StartTime + b.Duration / 2, b.HasEffect); + seekAndAssertBreak("seek to break end", b.EndTime, false); + seekAndAssertBreak("seek to break after end", b.EndTime + 500, false); } } - private void seekBreakStep(string seekStepDescription, double time, bool onBreak) + private void seekAndAssertBreak(string seekStepDescription, double time, bool shouldBeBreak) { AddStep(seekStepDescription, () => breakOverlay.ManualClockTime = time); - AddAssert($"is{(!onBreak ? " not " : " ")}break time", () => breakOverlay.IsBreakTime.Value == onBreak); + AddAssert($"is{(!shouldBeBreak ? " not" : string.Empty)} break time", () => + { + breakOverlay.ProgressTime(); + return breakOverlay.IsBreakTime.Value == shouldBeBreak; + }); } private class TestBreakOverlay : BreakOverlay { private readonly FramedClock framedManualClock; private readonly ManualClock manualClock; - private IFrameBasedClock normalClock; + private IFrameBasedClock originalClock; public double ManualClockTime { @@ -150,29 +153,25 @@ namespace osu.Game.Tests.Visual.Gameplay set => manualClock.CurrentTime = value; } - public new IBindable IsBreakTime - { - get - { - // Manually call the update function as it might take up to 2 frames for an automatic update to happen - Update(); - - return base.IsBreakTime; - } - } - public TestBreakOverlay(bool letterboxing) : base(letterboxing) { framedManualClock = new FramedClock(manualClock = new ManualClock()); + ProcessCustomClock = false; } - public void SwitchClock(bool setManual) => Clock = setManual ? framedManualClock : normalClock; + public void ProgressTime() + { + framedManualClock.ProcessFrame(); + Update(); + } + + public void SwitchClock(bool setManual) => Clock = setManual ? framedManualClock : originalClock; protected override void LoadComplete() { base.LoadComplete(); - normalClock = Clock; + originalClock = Clock; } } } From eaaef61bc2ba264d1faf63ce16c69e292389f876 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 18:59:17 +0900 Subject: [PATCH 1953/2854] Revert unintended change --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index 897dc36dea..0a19eac2b5 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -35,7 +35,7 @@ prompt 4 - iPhone Developer + iPhone Distribution true true Entitlements.plist From 1222536f7ac5380b1d217b5f7d69c77bf9e7a1fb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 30 Jul 2019 19:10:21 +0900 Subject: [PATCH 1954/2854] Rename resource string --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index 37ac972f41..eb1977a13d 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, Anchor = Anchor.Centre, - Child = scaleTarget = new SkinnableDrawable("osu/Game/Rulesets/Osu/UI/Cursor/OsuCursor", _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling) + Child = scaleTarget = new SkinnableDrawable("Play/osu/cursor", _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling) { Origin = Anchor.Centre, Anchor = Anchor.Centre, diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 48888191e9..a567f85a02 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -73,7 +73,7 @@ namespace osu.Game.Skinning { switch (componentName) { - case "osu/Game/Rulesets/Osu/UI/Cursor/OsuCursor": + case "Play/osu/cursor": if (GetTexture("cursor") != null) return new LegacyCursor(); From de20d026727094eef78e75d36c239cd4ddb1fff6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 19:11:54 +0900 Subject: [PATCH 1955/2854] Make special skin more generic --- .../special-skin/approachcircle@2x.png | Bin 31796 -> 26840 bytes .../Resources/special-skin/hitcircle@2x.png | Bin 263521 -> 245645 bytes .../special-skin/hitcircleoverlay@2x.png | Bin 262013 -> 247101 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/approachcircle@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/approachcircle@2x.png index 5d28b3f7cb5b7d682686c9a93a994ae506f68f2f..0a6ec6535c3b20c63fa9e47fa03beac92c64bee9 100755 GIT binary patch delta 24199 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 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircle@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircle@2x.png index b4eb5221ffd0c2791d079ebfc6bafcf0ac7a31ba..919d8f405ca7f40c921e71b91e7a81655db1f94b 100755 GIT binary patch delta 17705 zcma&NX*`tQA3uD}HG>&s-$Fu$Y#|ZZCP|ho*|!jdtd*2?t|19!Nwy-iNY=4rDa#Cr zWEtO-Jtk{qnXzwU=0Cstestfj`@#KSo?P=f*E#2Nea`!{yx-?{GEWMFt2zn>M&5oF z1A8W^A$eB&l$xgc8EtJP$A65tFET(?WL;j<9+6=n$}sZ z2LUXP0$56DXC;cv5Fo!!7E@krnXS}pz=-rF=?w-5vcOzI&nz>+YW!1CF z|IhX1NZp4kbN}x@$&(h>yl)2I4GHzjkqU>4G)_jqjbMAL0bMKXXo zeJTEV9yjl3R7}jp#^7J~hPE9KVV}a58qO^t1}i7HT;*`gr=#a!cb6JI52K1)8;>3`3R6}9lpW&cKmHe2-V4c91z^AZ@YyTf(?rHsVW^6DlX=h%xE-_+Hi{1^0P808uE4)eA|mOL#5(BQZ?V+FWH zhCEqtZfa>F6usdUqjK3g3se0}01FQXr~wTOJso^J6=XnL&8<(vz;M?(wPv>M*|9Y? zBRoDsUvMajERjd6TZmOJ7O_L~ETccBmLUELq&z(@7l(F|!`{}JAlmvB;NC@A&=?~)o@mc*E&=X(vnA2-Ku{+4Cepe*vqZ0k^5G4DSRI1 zDOs~h?`_js7qj?q%=P``!Lfvw#ieNOi-}SlX=Crc6_v*&RqzwNjLFBP zBM+Rfv)F1YX*{>KWvT2$EB3lY$Y3^#^P278zB1<;7z1w>=ue2t!Dg&M1D8w zU6P5TAg{&o!KUB17Ked;>MZ(tKeRi@y6r8Ec$SvNVl%9_9c<7N)VM=10^BcL>1E}N zk}+xUMUoj@WS@I9j+e+6Ocx##xV~wuO}2uqA0{)7mb3Z?Rg92e1{Xn%IZu1o=Sp?X zB(j{L;eXhH;M6eCB+u-29#nMhLgxPRSM%8a;MF7VTto9C^$rRg_M^y+i@HVOrpyLW% zmi=y8r}qmM(*HUJxJ5={5#kR2Z5v{te;wXgj1TzS2G@cb7ryLZ+TtD8 z*U^KG!7?~L-ckR4IUom#`do+Kkt~^@U|IuO4AWgS>Q1T*z1{FB)x=EjmJDzy;p^li)vI6Y=BQ4|f zz0G|&nH&#d|`#!^9~boNQF>ce>!XV3YXahYM>E4_mP;m)L za~jb%b~}sFYb_DqjcM&;IGNicbge8YB^>`^R6dm?D#T6wO?z?tBYdE(s~o~zu{V+*jt zZ+}$S!vnUrkcse!{BKh4708?JI;6ty6IExHdl-}NjEc%Ig0TwX1&qWdryjP>Nh16n z4$<%&$ed5(Jxzo9wkwvbU=ZzdSND_IG*zW5F-CzZbX>%G?s#2Vy}<7luL6MzVY#D| zCkD0_Ydr&h)xYZCPe6F)oPZ2lED?-0+SMc*SU=*oLBsrRSI)tVv>rS8Z=d^QxF(c!UbT=@qZo# z_Q`(r*Tb?eKYAW2N($`cOE!9~E2aC2Q}$V8#x z$fynbW9Fd0%daAS?Ju{)-Mo7<9!y{W_GRz*a0Y3NF0!%GqTx

bF2s0)VHd_aN%?wwRSD#3e> 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 delta 35772 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 delta 34252 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 Date: Tue, 30 Jul 2019 19:28:02 +0900 Subject: [PATCH 1956/2854] Add maximum height to skin dropdown --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 5d7542ca2b..35be930a2e 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -107,6 +107,8 @@ namespace osu.Game.Overlays.Settings.Sections private class SkinDropdownControl : DropdownControl { protected override string GenerateItemText(SkinInfo item) => item.ToString(); + + protected override DropdownMenu CreateMenu() => base.CreateMenu().With(m => m.MaxHeight = 200); } } } From e6e315e07bd952625a01bdd9c1578224d41d3dd0 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 30 Jul 2019 13:29:41 +0300 Subject: [PATCH 1957/2854] Expose current break index --- .../Visual/Gameplay/TestSceneBreakOverlay.cs | 2 ++ osu.Game/Screens/Play/BreakOverlay.cs | 17 +++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs index ed5861b47d..bb89c85e93 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs @@ -147,6 +147,8 @@ namespace osu.Game.Tests.Visual.Gameplay private readonly ManualClock manualClock; private IFrameBasedClock originalClock; + public new int CurrentBreakIndex => base.CurrentBreakIndex; + public double ManualClockTime { get => manualClock.CurrentTime; diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index e3e4014eb3..93267e5f2a 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Play // reset index in case the new breaks list is smaller than last one isBreakTime.Value = false; - currentBreakIndex = 0; + CurrentBreakIndex = 0; initializeBreaks(); } @@ -47,7 +47,8 @@ namespace osu.Game.Screens.Play ///

public IBindable IsBreakTime => isBreakTime; - private int currentBreakIndex; + protected int CurrentBreakIndex; + private readonly BindableBool isBreakTime = new BindableBool(); private readonly Container remainingTimeAdjustmentBox; @@ -137,21 +138,21 @@ namespace osu.Game.Screens.Play var time = Clock.CurrentTime; - if (time > breaks[currentBreakIndex].EndTime) + if (time > breaks[CurrentBreakIndex].EndTime) { - while (time > breaks[currentBreakIndex].EndTime && currentBreakIndex < breaks.Count - 1) - currentBreakIndex++; + while (time > breaks[CurrentBreakIndex].EndTime && CurrentBreakIndex < breaks.Count - 1) + CurrentBreakIndex++; } else { - while (time < breaks[currentBreakIndex].StartTime && currentBreakIndex > 0) - currentBreakIndex--; + while (time < breaks[CurrentBreakIndex].StartTime && CurrentBreakIndex > 0) + CurrentBreakIndex--; } // This ensures that IsBreakTime is generally consistent with the overlay's transforms during a break. // If the current break doesn't have effects, IsBreakTime should be false. // We also assume that the overlay's fade out transform is "not break time". - var currentBreak = breaks[currentBreakIndex]; + var currentBreak = breaks[CurrentBreakIndex]; isBreakTime.Value = currentBreak.HasEffect && time >= currentBreak.StartTime && time <= currentBreak.EndTime - fade_duration; } From f2ab259c21aefc7900f983b1cffc087229f0a349 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 30 Jul 2019 13:30:26 +0300 Subject: [PATCH 1958/2854] Add an assert if current break index has skipped a break --- osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs index bb89c85e93..879e15c548 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs @@ -87,7 +87,12 @@ namespace osu.Game.Tests.Visual.Gameplay setClock(true); loadBreaksStep("multiple breaks", testBreaks); - addBreakSeeks(testBreaks.Last(), false); + seekAndAssertBreak("seek to break start", testBreaks[1].StartTime, true); + AddAssert("is skipped to break #2", () => breakOverlay.CurrentBreakIndex == 1); + + seekAndAssertBreak("seek to break middle", testBreaks[1].StartTime + testBreaks[1].Duration / 2, true); + seekAndAssertBreak("seek to break end", testBreaks[1].EndTime, false); + seekAndAssertBreak("seek to break after end", testBreaks[1].EndTime + 500, false); } private void addShowBreakStep(double seconds) From 333049e71220dedc33bda9ec84e02b6a506fd38e Mon Sep 17 00:00:00 2001 From: DTSDAO Date: Tue, 30 Jul 2019 19:32:53 +0800 Subject: [PATCH 1959/2854] Fix ITMS-90737 --- osu.iOS/Info.plist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.iOS/Info.plist b/osu.iOS/Info.plist index 0775d1522d..4fbc67e27b 100644 --- a/osu.iOS/Info.plist +++ b/osu.iOS/Info.plist @@ -14,6 +14,8 @@ 0.1.0 LSRequiresIPhoneOS + LSSupportsOpeningDocumentsInPlace + MinimumOSVersion 10.0 UIDeviceFamily From 6ded53b3a91285e8f968c2a1772af347e16841f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 21:53:28 +0900 Subject: [PATCH 1960/2854] Reorder class --- .../SkinnableTestScene.cs | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs b/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs index ba31f15b3c..a2c058193b 100644 --- a/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs +++ b/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs @@ -16,30 +16,15 @@ namespace osu.Game.Rulesets.Osu.Tests { public abstract class SkinnableTestScene : OsuGridTestScene { - private static Skin getSkinFromResources(SkinManager skins, string name) - { - using (var storage = new DllResourceStore("osu.Game.Rulesets.Osu.Tests.dll")) - { - var tempName = Path.GetTempFileName(); - - File.Delete(tempName); - Directory.CreateDirectory(tempName); - - var files = storage.GetAvailableResources().Where(f => f.StartsWith($"Resources/{name}")); - - foreach (var file in files) - using (var stream = storage.GetStream(file)) - using (var newFile = File.Create(Path.Combine(tempName, Path.GetFileName(file)))) - stream.CopyTo(newFile); - - return skins.GetSkin(skins.Import(tempName).Result); - } - } - private Skin metricsSkin; private Skin defaultSkin; private Skin specialSkin; + protected SkinnableTestScene() + : base(2, 2) + { + } + [BackgroundDependencyLoader] private void load(AudioManager audio) { @@ -58,9 +43,24 @@ namespace osu.Game.Rulesets.Osu.Tests Cell(3).Child = new LocalSkinOverrideContainer(specialSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction()); } - protected SkinnableTestScene() - : base(2, 2) + private static Skin getSkinFromResources(SkinManager skins, string name) { + using (var storage = new DllResourceStore("osu.Game.Rulesets.Osu.Tests.dll")) + { + var tempName = Path.GetTempFileName(); + + File.Delete(tempName); + Directory.CreateDirectory(tempName); + + var files = storage.GetAvailableResources().Where(f => f.StartsWith($"Resources/{name}")); + + foreach (var file in files) + using (var stream = storage.GetStream(file)) + using (var newFile = File.Create(Path.Combine(tempName, Path.GetFileName(file)))) + stream.CopyTo(newFile); + + return skins.GetSkin(skins.Import(tempName).Result); + } } } } From e849e68e9984741b2dbe59a17e7445f123b61fac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 22:24:03 +0900 Subject: [PATCH 1961/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 2c5e54d4f5..e64102c401 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 472f6e9d65..1f91ce1cd8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 0a19eac2b5..66f398a927 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 307a6c10953eb0cd8c830694b2c0a3d8654b08ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 22:38:29 +0900 Subject: [PATCH 1962/2854] Remove DefaultCirclePiece --- .../Objects/Drawables/Pieces/CirclePiece.cs | 24 +++++++++++-- .../Drawables/Pieces/DefaultCirclePiece.cs | 35 ------------------- osu.Game/Skinning/LegacySkin.cs | 6 ++-- 3 files changed, 25 insertions(+), 40 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultCirclePiece.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index acbf5ba9ae..c92937ef09 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Skinning; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces @@ -18,8 +20,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Anchor = Anchor.Centre; Origin = Anchor.Centre; + } - InternalChild = new SkinnableDrawable("Play/osu/hitcircle", _ => new DefaultCirclePiece()); + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + InternalChildren = new Drawable[] + { + new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = textures.Get(@"Play/osu/disc"), + }, + new TrianglesPiece + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingMode.Additive, + Alpha = 0.5f, + } + }; } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultCirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultCirclePiece.cs deleted file mode 100644 index 047ff943ff..0000000000 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultCirclePiece.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class DefaultCirclePiece : Container - { - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - RelativeSizeAxes = Axes.Both; - Children = new Drawable[] - { - new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Texture = textures.Get(@"Play/osu/disc"), - }, - new TrianglesPiece - { - RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, - Alpha = 0.5f, - } - }; - } - } -} diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 0a811a9514..dffcd00cc3 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -274,11 +274,11 @@ namespace osu.Game.Skinning [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject, ISkinSource skin) { - Sprite mainCircle; + Sprite hitCircleSprite; InternalChildren = new Drawable[] { - mainCircle = new Sprite + hitCircleSprite = new Sprite { Texture = skin.GetTexture("hitcircle"), Colour = drawableObject.AccentColour.Value @@ -298,7 +298,7 @@ namespace osu.Game.Skinning state.BindValueChanged(updateState, true); accentColour.BindTo(drawableObject.AccentColour); - accentColour.BindValueChanged(colour => mainCircle.Colour = colour.NewValue, true); + accentColour.BindValueChanged(colour => hitCircleSprite.Colour = colour.NewValue, true); } private void updateState(ValueChangedEvent state) From e6bd02d2767f6b7dbb265542dbf2d2369b15e9fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 22:41:20 +0900 Subject: [PATCH 1963/2854] Simplify namespace definition --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index df944fd9a6..ca124e9214 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables return true; }, }, - new SkinnableDrawable("Play/Osu/Objects/Drawables/MainCircle", _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)), + new SkinnableDrawable("Play/osu/hitcircle", _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)), ApproachCircle = new ApproachCircle { Alpha = 0, diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index dffcd00cc3..fff7fd082e 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -78,7 +78,7 @@ namespace osu.Game.Skinning { switch (componentName) { - case "Play/Osu/Objects/Drawables/MainCircle": + case "Play/osu/hitcircle": if (!hasHitCircle) return null; From fb1f77bd0490ebaf7c921654d3b67792b47ea6d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 23:06:18 +0900 Subject: [PATCH 1964/2854] Move implementation and colour logic to legacy implementation --- .../Objects/Drawables/Pieces/SliderBall.cs | 13 +++---------- osu.Game/Skinning/LegacySkin.cs | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 119005eb40..8b72b23ca3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -57,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { RelativeSizeAxes = Axes.Both, // TODO: support skin filename animation (sliderb0, sliderb1...) - Child = new SkinnableDrawable("Play/osu/sliderb", _ => new DefaultSliderBall()), + Child = new SkinnableDrawable("Play/osu/sliderball", _ => new DefaultSliderBall()), } } }; @@ -204,10 +203,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public class DefaultSliderBall : CompositeDrawable { - private readonly Bindable accentColour = new Bindable(); - - private Drawable drawableBall; - [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject, ISkinSource skin) { @@ -225,15 +220,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces BorderThickness = 10, BorderColour = Color4.White, Alpha = 1, - Child = drawableBall = new Box + Child = new Box { RelativeSizeAxes = Axes.Both, + Colour = Color4.White, Alpha = 0.4f, } }; - - accentColour.BindTo(drawableObject.AccentColour); - accentColour.BindValueChanged(colour => drawableBall.Colour = colour.NewValue, true); } } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index af9a24df42..84fa5ff97b 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; @@ -15,6 +16,7 @@ using osu.Framework.IO.Stores; using osu.Game.Database; using osu.Game.Graphics.Sprites; using osuTK; +using osuTK.Graphics; namespace osu.Game.Skinning { @@ -71,6 +73,12 @@ namespace osu.Game.Skinning { switch (componentName) { + case "Play/osu/sliderball": + if (GetTexture("sliderb") != null) + return new LegacySliderBall(); + + break; + case "Play/Miss": componentName = "hit0"; break; @@ -109,6 +117,16 @@ namespace osu.Game.Skinning return new Sprite { Texture = texture }; } + public class LegacySliderBall : Sprite + { + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + Texture = skin.GetTexture("sliderb"); + Colour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White; + } + } + public override Texture GetTexture(string componentName) { float ratio = 2; From 6d279dba5ce40957bb8df20845c2409203f02d24 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 23:07:41 +0900 Subject: [PATCH 1965/2854] Fix centering being incorrect for some skins --- osu.Game/Skinning/LegacySkin.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index fff7fd082e..c381c6293b 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -281,7 +281,9 @@ namespace osu.Game.Skinning hitCircleSprite = new Sprite { Texture = skin.GetTexture("hitcircle"), - Colour = drawableObject.AccentColour.Value + Colour = drawableObject.AccentColour.Value, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, }, new SkinnableSpriteText("Play/osu/number-text", _ => new OsuSpriteText { @@ -291,7 +293,12 @@ namespace osu.Game.Skinning { Text = (((IHasComboInformation)drawableObject.HitObject).IndexInCurrentCombo + 1).ToString() }, - new Sprite { Texture = skin.GetTexture("hitcircleoverlay") } + new Sprite + { + Texture = skin.GetTexture("hitcircleoverlay"), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } }; state.BindTo(drawableObject.State); From d12de7dbfa0127ffac637e9736a54fc496d29bd9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 23:20:35 +0900 Subject: [PATCH 1966/2854] Add temporary scale hack to make cursors correct size --- osu.Game/Skinning/LegacySkin.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index a567f85a02..bd08de03d5 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -255,13 +255,13 @@ namespace osu.Game.Skinning { InternalChildren = new Drawable[] { - new Sprite + new NonPlayfieldSprite { Texture = skin.GetTexture("cursormiddle"), Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - new Sprite + new NonPlayfieldSprite { Texture = skin.GetTexture("cursor"), Anchor = Anchor.Centre, @@ -270,5 +270,18 @@ namespace osu.Game.Skinning }; } } + + private class NonPlayfieldSprite : Sprite + { + public override Texture Texture + { + get => base.Texture; + set + { + value.ScaleAdjust *= 2f; + base.Texture = value; + } + } + } } } From 21a8f566c3b34b78bbfd3be635a06ea5d169d93f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 23:24:28 +0900 Subject: [PATCH 1967/2854] Trim whitespace --- osu.Game/Skinning/LegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index f01dc7f8dd..b6d9b014fd 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -87,7 +87,7 @@ namespace osu.Game.Skinning case "Play/osu/hitcircle": if (hasHitCircle) return new LegacyMainCirclePiece(); - + return null; case "Play/Miss": From 4c6cccb3a338294dd20b69615cddeb82ba77301b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 23:31:21 +0900 Subject: [PATCH 1968/2854] Update settings in line with framework changes --- .../Settings/Sections/Debug/GeneralSettings.cs | 5 ----- .../Debug/{GCSettings.cs => MemorySettings.cs} | 12 ++++++------ osu.Game/Overlays/Settings/Sections/DebugSection.cs | 2 +- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 2 +- 4 files changed, 8 insertions(+), 13 deletions(-) rename osu.Game/Overlays/Settings/Sections/Debug/{GCSettings.cs => MemorySettings.cs} (63%) diff --git a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs index f063898a9f..7eec971b62 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs @@ -27,11 +27,6 @@ namespace osu.Game.Overlays.Settings.Sections.Debug Bindable = frameworkConfig.GetBindable(FrameworkSetting.PerformanceLogging) }, new SettingsCheckbox - { - LabelText = "Bypass caching (slow)", - Bindable = config.GetBindable(DebugSetting.BypassCaching) - }, - new SettingsCheckbox { LabelText = "Bypass front-to-back render pass", Bindable = config.GetBindable(DebugSetting.BypassFrontToBackPass) diff --git a/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/MemorySettings.cs similarity index 63% rename from osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs rename to osu.Game/Overlays/Settings/Sections/Debug/MemorySettings.cs index 6897b42f4f..db64c9a8ac 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/MemorySettings.cs @@ -1,26 +1,26 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Platform; namespace osu.Game.Overlays.Settings.Sections.Debug { - public class GCSettings : SettingsSubsection + public class MemorySettings : SettingsSubsection { - protected override string Header => "Garbage Collector"; + protected override string Header => "Memory"; [BackgroundDependencyLoader] - private void load(FrameworkDebugConfigManager config) + private void load(FrameworkDebugConfigManager config, GameHost host) { Children = new Drawable[] { new SettingsButton { - Text = "Force garbage collection", - Action = GC.Collect + Text = "Clear all caches", + Action = host.Collect }, }; } diff --git a/osu.Game/Overlays/Settings/Sections/DebugSection.cs b/osu.Game/Overlays/Settings/Sections/DebugSection.cs index 0149cab802..f62de0b243 100644 --- a/osu.Game/Overlays/Settings/Sections/DebugSection.cs +++ b/osu.Game/Overlays/Settings/Sections/DebugSection.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Settings.Sections Children = new Drawable[] { new GeneralSettings(), - new GCSettings(), + new MemorySettings(), }; } } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 2551ffe2fc..a9e4eaa9b3 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -141,6 +141,7 @@ namespace osu.Game.Screens.Select private readonly RulesetInfo ruleset; public BufferedWedgeInfo(WorkingBeatmap beatmap, RulesetInfo userRuleset) + : base(pixelSnapping: true) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; @@ -152,7 +153,6 @@ namespace osu.Game.Screens.Select var beatmapInfo = beatmap.BeatmapInfo; var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); - PixelSnapping = true; CacheDrawnFrameBuffer = true; RelativeSizeAxes = Axes.Both; From f6b6fa963393ed2e3130a96e737b33605fc91fcd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 23:44:47 +0900 Subject: [PATCH 1969/2854] Add disappeared null check --- osu.Game/Skinning/LegacySkin.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index d6709df646..906a81a007 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -365,7 +365,8 @@ namespace osu.Game.Skinning get => base.Texture; set { - value.ScaleAdjust *= 2f; + if (value != null) + value.ScaleAdjust *= 2f; base.Texture = value; } } From 9335ebeb5592d10a820bdcb5b3edc719294f7593 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jul 2019 23:45:55 +0900 Subject: [PATCH 1970/2854] Fix missing anchor/origin specification --- osu.Game/Skinning/LegacySkin.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 906a81a007..19f04c9b7a 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -291,6 +291,8 @@ namespace osu.Game.Skinning new NonPlayfieldSprite { Texture = skin.GetTexture("cursor"), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, } }; } From 6bfac9f8e42546565c34151b0d30d0c5b501bd74 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 31 Jul 2019 17:50:13 +0900 Subject: [PATCH 1971/2854] Remove protected ctor --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 47ce28db4c..b3d7bfb91f 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -217,10 +217,6 @@ namespace osu.Game.Rulesets.Scoring private double baseScore; private double bonusScore; - protected ScoreProcessor() - { - } - public ScoreProcessor(DrawableRuleset drawableRuleset) { Debug.Assert(base_portion + combo_portion == 1.0); From e57663b39cdf2ba57984f54c6dd54a751ac225e7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 31 Jul 2019 17:55:22 +0900 Subject: [PATCH 1972/2854] Apply mod score multipliers --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index b3d7bfb91f..2e863f7edb 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -217,6 +217,8 @@ namespace osu.Game.Rulesets.Scoring private double baseScore; private double bonusScore; + private double scoreMultiplier = 1; + public ScoreProcessor(DrawableRuleset drawableRuleset) { Debug.Assert(base_portion + combo_portion == 1.0); @@ -235,6 +237,15 @@ namespace osu.Game.Rulesets.Scoring } Mode.ValueChanged += _ => updateScore(); + Mods.ValueChanged += mods => + { + scoreMultiplier = 1; + + foreach (var m in mods.NewValue) + scoreMultiplier *= m.ScoreMultiplier; + + updateScore(); + }; } ///
@@ -384,7 +395,7 @@ namespace osu.Game.Rulesets.Scoring if (rollingMaxBaseScore != 0) Accuracy.Value = baseScore / rollingMaxBaseScore; - TotalScore.Value = getScore(Mode.Value); + TotalScore.Value = getScore(Mode.Value) * scoreMultiplier; } private double getScore(ScoringMode mode) From 3af1aaeabe8571674c01f721e01dd0628b89edbe Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 31 Jul 2019 15:19:02 +0300 Subject: [PATCH 1973/2854] Unsubscribe from Completed event on old beatmap --- osu.Game/OsuGame.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e71dd67bf2..431e6c91f9 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -307,8 +307,11 @@ namespace osu.Game if (nextBeatmap?.Track != null) nextBeatmap.Track.Completed += currentTrackCompleted; - beatmap.OldValue?.Dispose(); + var oldBeatmap = beatmap.OldValue; + if (oldBeatmap?.Track != null) + oldBeatmap.Track.Completed -= currentTrackCompleted; + oldBeatmap?.Dispose(); nextBeatmap?.LoadBeatmapAsync(); } From 0f1dd8a46e4d5e43e0c1e138886d790a84539cea Mon Sep 17 00:00:00 2001 From: DTSDAO Date: Wed, 31 Jul 2019 21:44:57 +0800 Subject: [PATCH 1974/2854] Undo #5533 for causing crashes --- osu.iOS/Info.plist | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.iOS/Info.plist b/osu.iOS/Info.plist index 4fbc67e27b..0775d1522d 100644 --- a/osu.iOS/Info.plist +++ b/osu.iOS/Info.plist @@ -14,8 +14,6 @@ 0.1.0 LSRequiresIPhoneOS - LSSupportsOpeningDocumentsInPlace - MinimumOSVersion 10.0 UIDeviceFamily From 958e3fb68bffcd8bbad6f178c585f13bf849faa2 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 31 Jul 2019 22:42:23 +0300 Subject: [PATCH 1975/2854] Add a property for acquiring online API access --- osu.Game/Tests/Visual/OsuTestScene.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 27d72f3950..dd68ed93e6 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -16,6 +16,7 @@ using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Tests.Beatmaps; @@ -47,6 +48,12 @@ namespace osu.Game.Tests.Visual private readonly Lazy contextFactory; protected DatabaseContextFactory ContextFactory => contextFactory.Value; + /// + /// Whether this test scene requires API access + /// Setting this will cache an actual . + /// + protected virtual bool RequiresAPIAccess => false; + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { // This is the earliest we can get OsuGameBase, which is used by the dummy working beatmap to find textures @@ -57,7 +64,17 @@ namespace osu.Game.Tests.Visual Default = working }; - return Dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + Dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + + if (!RequiresAPIAccess) + { + var dummyAPI = new DummyAPIAccess(); + + Dependencies.CacheAs(dummyAPI); + Add(dummyAPI); + } + + return Dependencies; } protected OsuTestScene() From 034345f1bd5dd28dcd9f5b91bdaca3a6fead35d2 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 31 Jul 2019 22:43:05 +0300 Subject: [PATCH 1976/2854] Resolve API for dummy-caching tests --- osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs | 7 +------ .../Online/TestSceneAccountCreationOverlay.cs | 14 ++++++++------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs b/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs index f2718b8e80..13116de320 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs @@ -10,14 +10,9 @@ namespace osu.Game.Tests.Visual.Menus { public class TestSceneDisclaimer : ScreenTestScene { - [Cached(typeof(IAPIProvider))] - private readonly DummyAPIAccess api = new DummyAPIAccess(); - [BackgroundDependencyLoader] - private void load() + private void load(IAPIProvider api) { - Add(api); - AddStep("load disclaimer", () => LoadScreen(new Disclaimer())); AddStep("toggle support", () => diff --git a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs index 35449f5687..66ab1fe18a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs @@ -25,17 +25,14 @@ namespace osu.Game.Tests.Visual.Online typeof(AccountCreationScreen), }; - [Cached(typeof(IAPIProvider))] - private DummyAPIAccess api = new DummyAPIAccess(); + private readonly Container userPanelArea; public TestSceneAccountCreationOverlay() { - Container userPanelArea; AccountCreationOverlay accountCreation; Children = new Drawable[] { - api, accountCreation = new AccountCreationOverlay(), userPanelArea = new Container { @@ -46,11 +43,16 @@ namespace osu.Game.Tests.Visual.Online }, }; + AddStep("show", () => accountCreation.Show()); + } + + [BackgroundDependencyLoader] + private void load(IAPIProvider api) + { api.Logout(); api.LocalUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true); - AddStep("show", () => accountCreation.Show()); - AddStep("logout", () => api.Logout()); + AddStep("logout", api.Logout); } } } From 849ed0c69df35d11c0b2c6e133e943143bb19e01 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 31 Jul 2019 22:44:44 +0300 Subject: [PATCH 1977/2854] Acquire api access for online tests --- .../Multiplayer/TestSceneMatchLeaderboard.cs | 7 +++---- .../Visual/Multiplayer/TestSceneMultiScreen.cs | 2 ++ .../Visual/Online/TestSceneBeatmapSetOverlay.cs | 2 ++ .../Visual/Online/TestSceneChangelogOverlay.cs | 2 ++ .../Visual/Online/TestSceneDirectOverlay.cs | 2 ++ .../Visual/Online/TestSceneHistoricalSection.cs | 17 +++++++++-------- .../Visual/Online/TestSceneSocialOverlay.cs | 2 ++ .../Visual/Online/TestSceneUserProfileHeader.cs | 2 ++ .../Online/TestSceneUserProfileOverlay.cs | 2 ++ .../Visual/Online/TestSceneUserRanks.cs | 2 ++ ...estSceneUpdateableBeatmapBackgroundSprite.cs | 2 ++ 11 files changed, 30 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs index fa3c392b2e..723e5fc03d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs @@ -14,6 +14,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMatchLeaderboard : MultiplayerTestScene { + protected override bool RequiresAPIAccess => true; + public TestSceneMatchLeaderboard() { Room.RoomID.Value = 3; @@ -27,11 +29,8 @@ namespace osu.Game.Tests.Visual.Multiplayer }); } - [Resolved] - private IAPIProvider api { get; set; } - [BackgroundDependencyLoader] - private void load() + private void load(IAPIProvider api) { var req = new GetRoomScoresRequest(); req.Success += v => { }; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs index 069e133c2b..b646433846 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs @@ -12,6 +12,8 @@ namespace osu.Game.Tests.Visual.Multiplayer [TestFixture] public class TestSceneMultiScreen : ScreenTestScene { + protected override bool RequiresAPIAccess => true; + public override IReadOnlyList RequiredTypes => new[] { typeof(Screens.Multi.Multiplayer), diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index daee419b52..edb232359f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -42,6 +42,8 @@ namespace osu.Game.Tests.Visual.Online typeof(BeatmapAvailability), }; + protected override bool RequiresAPIAccess => true; + private RulesetInfo taikoRuleset; private RulesetInfo maniaRuleset; diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index cf8bac7642..324291c9d7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -27,6 +27,8 @@ namespace osu.Game.Tests.Visual.Online typeof(Comments), }; + protected override bool RequiresAPIAccess => true; + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs index 75c2a2a6a1..14ae975806 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs @@ -13,6 +13,8 @@ namespace osu.Game.Tests.Visual.Online { private DirectOverlay direct; + protected override bool RequiresAPIAccess => true; + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs index 838347800f..c98f98c23d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs @@ -17,14 +17,15 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneHistoricalSection : OsuTestScene { - public override IReadOnlyList RequiredTypes => - new[] - { - typeof(HistoricalSection), - typeof(PaginatedMostPlayedBeatmapContainer), - typeof(DrawableMostPlayedBeatmap), - typeof(DrawableProfileRow) - }; + protected override bool RequiresAPIAccess => true; + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(HistoricalSection), + typeof(PaginatedMostPlayedBeatmapContainer), + typeof(DrawableMostPlayedBeatmap), + typeof(DrawableProfileRow) + }; public TestSceneHistoricalSection() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs index 5cb96c7ed2..806b36e855 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs @@ -13,6 +13,8 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneSocialOverlay : OsuTestScene { + protected override bool RequiresAPIAccess => true; + public override IReadOnlyList RequiredTypes => new[] { typeof(UserPanel), diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs index 2285c9b799..555d5334d8 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs @@ -17,6 +17,8 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneUserProfileHeader : OsuTestScene { + protected override bool RequiresAPIAccess => true; + public override IReadOnlyList RequiredTypes => new[] { typeof(ProfileHeader), diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs index c2376aa153..39ba0ea3da 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs @@ -19,6 +19,8 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneUserProfileOverlay : OsuTestScene { + protected override bool RequiresAPIAccess => true; + private readonly TestUserProfileOverlay profile; [Resolved] diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs index 9f0a8c769a..d777f9766a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs @@ -18,6 +18,8 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneUserRanks : OsuTestScene { + protected override bool RequiresAPIAccess => true; + public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableProfileScore), typeof(RanksSection) }; public TestSceneUserRanks() diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs index 9cdfcb6cc4..fdc50be3fa 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs @@ -20,6 +20,8 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneUpdateableBeatmapBackgroundSprite : OsuTestScene { + protected override bool RequiresAPIAccess => true; + private BeatmapSetInfo testBeatmap; private IAPIProvider api; private RulesetStore rulesets; From 7b95741dab566f5ef85ae01d1b169dbd3e9f18a0 Mon Sep 17 00:00:00 2001 From: jorolf Date: Wed, 31 Jul 2019 21:55:56 +0200 Subject: [PATCH 1978/2854] fix crash and add some tests - still missing special skin textures --- .../Resources/default-skin/hit0@2x.png | Bin 0 -> 16112 bytes .../Resources/default-skin/hit100@2x.png | Bin 0 -> 31228 bytes .../Resources/default-skin/hit100k@2x.png | Bin 0 -> 21318 bytes .../Resources/default-skin/hit300@2x.png | Bin 0 -> 36873 bytes .../Resources/default-skin/hit300g@2x.png | Bin 0 -> 39840 bytes .../Resources/default-skin/hit50@2x.png | Bin 0 -> 26015 bytes .../Resources/metrics-skin/hit0@2x.PNG | Bin 0 -> 9492 bytes .../Resources/metrics-skin/hit100@2x.PNG | Bin 0 -> 8371 bytes .../Resources/metrics-skin/hit300@2x.PNG | Bin 0 -> 9589 bytes .../Resources/metrics-skin/hit50@2x.PNG | Bin 0 -> 9299 bytes .../TestSceneDrawableJudgement.cs | 39 ++++++++++++++++++ osu.Game/Skinning/LegacySkin.cs | 2 +- 12 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit0@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit100@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit100k@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300g@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit50@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit0@2x.PNG create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit100@2x.PNG create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit300@2x.PNG create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit50@2x.PNG create mode 100644 osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit0@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit0@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bdb2bcbc41085fef9d4bd351bdeae81f0da331f4 GIT binary patch literal 16112 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit100@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit100@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7db8eb312403e739d8dc61b280f930443ad0d81c GIT binary patch literal 31228 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2c7c07852ff6e84edc2164b4487aaaa6cb4d8fe4 GIT binary patch literal 36873 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%0DE1j{G{fQ$Fey0U z2R#5S0}4Y2sll>g2l-GBpks0;f3-|n88Lztw=KzCtYfelY{<&lVA7umPTvbY&)D%? zp2tC!ci}6ArU*~P2wv-%?{u@{IlxQ>C-=}%SBBG2x0{wmp{bM9t{>o@L1#NNi{^id5$h2K%|ahW&;i8Dm>f|(9-A85fm*X zL3PD+EAZrC5I#nw(C%3v7NXjb3SBkS5gzP~Muz9%7O@NC@Et$Jolt0SS*q}8(g7Kl zjo%b4OdE_}>b1uuQDYf)XznHMnkJZpy#peWvYQ!dx=9G?>#r5@w3f zpg6!&lFb)7$Vd|yn-?H2up3Mt!GTUdfvI?IhjmFAv_SAUxMU&~f+1!x^G+CiZr(8Q zN)v~fFzGx>Vhu0tSk_>mh#Z~hgDn&+hTU)Kb)z@4| zYM2Vw^>EA|z2>fvpwL`v=$jq0-)PeDJ7o%wr!?52c|3*~Q+!d^$I0r-RJ39Oy&z`s zd23lL4}pOQkmAz|56_J`&tC1T5J}L&uuEz!^DQK@40eg0V#CZ5TQ_A7&1+i*tGWbB zgFBi84Oa+N?7>Lp)B4!A`{(WaADL^;*|BvMI@1D63$*XVb%D&K&A|P%2ZA4Z(5PN5 zZvs7}gEUYJBk={lVPBXP&0gprn|B*Zrr<{V6TEPIVEljaBA7|vTCsNc7iMe`w^$8$ zkZ5HgUM43mY2!3Qytry5n+wXofKawKh--t@HINjb2PE5f;V?!Hr_QsS%U9r}okz$% z^)6nXkU9@Wae@&6n_?QHC>5H{O|zc0LkveLSSwC|+um}i%Par|uQcKmuZqF$0&$uv zpw?%_SzS;^1e~f@+B9oIqkv#)`i@S4`*Nv$fvvb|!y9j|td5siv7o(!4ta$L1j3V~ z?GFgivZz$6yTX_@)GV5a7ori61sc_AAwK3NKzJ4sFaOXu5L4@-i;@wzL}QA@q1b5E zXd&FJvhiNk7h1QhK`-;b&~Y?_e^(`jeO`^56Nd?86B*Oo6t z8<9JDn%rv;3}(sVQ6QI4cBD-l#sPCDXMx%^$L@JIuTG;_7iGg$MpIuICf&%Cli5z_-b3N%+nH)n$KZKBP=qI@q9y*630kO zB~slRZS**S6D+a0wXgl)4j6>kLQ^=|DK-sl_@UG(~L zan@yCaM#CK8>zECrbKoHzToX}8hI0&?s!~s>RP249GM6V?{XiZQ33N2 zYZN0GQ@TQhqRpZD{!n5bHaz(LwUXdMmk1eawQA%Z~gKzg8gatMcZispd; zEbz?T5Kq~VY4Xo9XlA-irm>lyJp@I1TB3QG1J5_U52eL9L~M9J8a|1$&01mth8@Dn ztl`t_!c{NlOl`A#rON>fVOr(JjiyrjW^0u|7P9c0og% zZ^DY(W&&c#L#&96dfg0;wMyw!M4%ifD|XbjrN`-jXJjTQKmbFtUFw1NxY)4m$+!^W zMZNL61}1EHo|d*?MhS=+OPW3?-fzRYG5Lh^=y9K=QyZ%aUahJYb^k8UXA@iY2v5c6 z`JegB2-{|baqAsLBaN(gtt=XTV!gAp`ErT?QBJz-z6U z$stZd3e+=gGHRKBV1$QSgoJ7Zr^->h2#s}Y5QnA$WCS1Ad+$LjbDwq%_~M#jErXLX zi_tdC;qOJpJG$+n?peLxeJ?nDYONy#Uol zFip!KvDRF_xC2J;TE>+1OVCMJ{63NB%Gnlb#3B<$7Ttn1IAGYlMvnmkPw? zp}}gqF&Z6m_Tf>vBt7n;onw9Q2edxNYB(xtuC}ZK1Pkb<$XJY(nlN<0X&GaNw8PiX z0)~6>kB-9eYm6n2`!-Y*bUiZKcrsx` z{V`c$n774vQE*b{x@f6)1ci2w{4ut!2_%%2$Tx99yIqDH;^xNP#p<+T%?bk4uOm6c z%{7J(!Vf3~=hEPhHP2A5PDFD~&3@;8-n#>D{-6kYhSKnxVIV-PfbMa7e$lOW)#?w> zk}H?(CdW6(a|ID)q!1BzC9c~ixNf>|FpOs0NDP{2Lbx_dg4h>eKLBF_#u5{Ao#or> z&=sOdAs$0qk^sc_d(FLsb&vM#L7}+~477y5Wt4Q-<(y;l{kop8nx0`n8Ram=B03$` zkq<@$k;kVBV}-FdE?4R=2drw|Wmf&WaHZ`!1Hoa=D!noLr<5*Vv{b(HLn z241mxL2N1pd-w^!1Z%|{Vc~lf#2RLl!`^uUQyFRX#?{doIK80?P$G@geT~3jZO5nT zorMgoZw1HxKr@7YD^5o-V9DzU4lGTZPHFI2zbb__oEvp}N-+#^KBCZwf2-i+i}8l= zc#-@4GZdhqCK@u$=v6evr^OOg##xZ$){Zrac-2)QO|i={ZcPyw5@9M>zN2n+7|4j( zxznogan!3}rGplcj-=t2FU=Z3FXtv9m=n5CM~s?68!1TyAg3MCV~XrFrX%I?Tc7wV|@u zg7d6;*ZUZ~jMsg%>Iqvw(L^gc&LmjWTaRxuFW^995@={{o*$ydWd|_N1-ImcWR_0trf9)EU zsAC*|K!hhQoyR-)#O_;|Xd)9KR0>53^t|768-#Onb*voFLZQLg&bf{TqxnQzhaYK$ zNYCDWA0mHD5M!^?vgZCZjj6v~^XI|P`}H^Ssd`%YU|oSXG-8wleNE2Q##8-+;lC~mw8bRpJG zn9_bjd7j01N7U;Orwmc+_PN^(-tH8k9!T11*6nrZ{uB08XzKL6LEQ?(4Jr?@m5bl~ z@0k{7+ar5I4!`WQ(IM9qqS_YnmOgy@N3Or?mEzX9&x#0d(5R+E(erGbm8z$8pR7B7 zDQA0VnjGCOL_|iyFkagXC0BFpcfF?dWN5ZI?U~2#9%7^5tFrsMjusxqD(9eEtWt7cvG&$w3rQOl%JHpr-)gb*n{ul|ml@l4`d(NnHzxu9Nhjr_7hB*8BwQk)WGtTnR^Z$Nl z!P$K5nMC!*0TX$8$fsu{FVqdDI&(JqOI$8 z$iY^6)En5J!s~b6hS__bAnprc-N0#)E`s&Y8;9GsVSR!)hsCJJk4 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300g@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300g@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1ce746e3a494f4446cc451f5924240f4c8eecb4b GIT binary patch literal 39840 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%0DP1I3KX^ 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 eY4UfV&i_BJT8gC9ZPy+E0000_|)(jNwS@P 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit50@2x.PNG b/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit50@2x.PNG new file mode 100644 index 0000000000000000000000000000000000000000..f64feded0cc2fc38672c081da38fe214d2546df3 GIT binary patch literal 9299 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. Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public class TestSceneDrawableJudgement : SkinnableTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(DrawableJudgement), + typeof(DrawableOsuJudgement) + }; + + public TestSceneDrawableJudgement() + { + foreach (HitResult result in Enum.GetValues(typeof(HitResult))) + { + JudgementResult judgement = new JudgementResult(null) + { + Type = result, + }; + + AddStep("Show " + result.GetDescription(), () => SetContents(() => new DrawableOsuJudgement(judgement, null) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + })); + } + } + } +} diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 5357a75e00..3426ddaf16 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -140,7 +140,7 @@ namespace osu.Game.Skinning if (texture != null && animatable) { - var animation = new TextureAnimation { DefaultFrameLength = frametime }; + var animation = new TextureAnimation { DefaultFrameLength = frametime, AutoSizeAxes = Axes.None }; for (int i = 1; texture != null; i++) { From 852079d43852a255b7d238f1691c3214d5221830 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Thu, 1 Aug 2019 01:35:42 +0300 Subject: [PATCH 1979/2854] Remove redundant ScreenshotManager.Update override --- osu.Game/Graphics/ScreenshotManager.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 5ad5e5569a..524a4742c0 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -92,7 +92,8 @@ namespace osu.Game.Graphics using (var image = await host.TakeScreenshotAsync()) { - Interlocked.Decrement(ref screenShotTasks); + if (Interlocked.Decrement(ref screenShotTasks) == 0 && cursorVisibility.Value == false) + cursorVisibility.Value = true; var fileName = getFileName(); if (fileName == null) return; @@ -125,14 +126,6 @@ namespace osu.Game.Graphics } }); - protected override void Update() - { - base.Update(); - - if (cursorVisibility.Value == false && Interlocked.CompareExchange(ref screenShotTasks, 0, 0) == 0) - cursorVisibility.Value = true; - } - private string getFileName() { var dt = DateTime.Now; From 8a64ab03847c9ebdb7c2d9d5081e306410f9ee1b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Aug 2019 12:35:17 +0900 Subject: [PATCH 1980/2854] Remove generics from IApplicableToBeatmap --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 3 +-- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 3 +-- .../Mods/ManiaModMirror.cs | 4 +-- .../Mods/ManiaModRandom.cs | 4 +-- osu.Game.Rulesets.Osu/OsuRuleset.cs | 3 +-- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 3 +-- .../Rulesets/Mods/IApplicableToBeatmap.cs | 13 ++++------ osu.Game/Rulesets/Mods/ModTimeRamp.cs | 26 +++++++------------ osu.Game/Rulesets/Mods/ModWindDown.cs | 6 ++--- osu.Game/Rulesets/Mods/ModWindUp.cs | 6 ++--- osu.Game/Rulesets/UI/DrawableRuleset.cs | 2 +- 11 files changed, 28 insertions(+), 45 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index ea9f225cc1..6f1a7873ec 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -11,7 +11,6 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; @@ -108,7 +107,7 @@ namespace osu.Game.Rulesets.Catch case ModType.Fun: return new Mod[] { - new MultiMod(new ModWindUp(), new ModWindDown()) + new MultiMod(new ModWindUp(), new ModWindDown()) }; default: diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index d83033f9c6..8966b5058f 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Game.Graphics; -using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; @@ -154,7 +153,7 @@ namespace osu.Game.Rulesets.Mania case ModType.Fun: return new Mod[] { - new MultiMod(new ModWindUp(), new ModWindDown()) + new MultiMod(new ModWindUp(), new ModWindDown()) }; default: diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index 17f4098420..485595cea9 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Mania.Beatmaps; namespace osu.Game.Rulesets.Mania.Mods { - public class ManiaModMirror : Mod, IApplicableToBeatmap + public class ManiaModMirror : Mod, IApplicableToBeatmap { public override string Name => "Mirror"; public override string Acronym => "MR"; @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Mods public override double ScoreMultiplier => 1; public override bool Ranked => true; - public void ApplyToBeatmap(Beatmap beatmap) + public void ApplyToBeatmap(IBeatmap beatmap) { var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns; diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs index ba16140644..9275371a61 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs @@ -13,7 +13,7 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Mania.Mods { - public class ManiaModRandom : Mod, IApplicableToBeatmap + public class ManiaModRandom : Mod, IApplicableToBeatmap { public override string Name => "Random"; public override string Acronym => "RD"; @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Mods public override string Description => @"Shuffle around the keys!"; public override double ScoreMultiplier => 1; - public void ApplyToBeatmap(Beatmap beatmap) + public void ApplyToBeatmap(IBeatmap beatmap) { var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns; var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList(); diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 7f45fbe1dd..d50d4f401c 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -14,7 +14,6 @@ using osu.Game.Overlays.Settings; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; @@ -136,7 +135,7 @@ namespace osu.Game.Rulesets.Osu new OsuModWiggle(), new OsuModSpinIn(), new MultiMod(new OsuModGrow(), new OsuModDeflate()), - new MultiMod(new ModWindUp(), new ModWindDown()), + new MultiMod(new ModWindUp(), new ModWindDown()), }; case ModType.System: diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index a67004e9c7..83356b77c2 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Replays.Types; -using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Replays; using osu.Game.Beatmaps.Legacy; using osu.Game.Rulesets.Difficulty; @@ -107,7 +106,7 @@ namespace osu.Game.Rulesets.Taiko case ModType.Fun: return new Mod[] { - new MultiMod(new ModWindUp(), new ModWindDown()) + new MultiMod(new ModWindUp(), new ModWindDown()) }; default: diff --git a/osu.Game/Rulesets/Mods/IApplicableToBeatmap.cs b/osu.Game/Rulesets/Mods/IApplicableToBeatmap.cs index a634976b3c..cff669bf53 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToBeatmap.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToBeatmap.cs @@ -2,21 +2,18 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mods { /// - /// Interface for a that applies changes to a - /// after conversion and post-processing has completed. + /// Interface for a that applies changes to a after conversion and post-processing has completed. /// - public interface IApplicableToBeatmap : IApplicableMod - where TObject : HitObject + public interface IApplicableToBeatmap : IApplicableMod { /// - /// Applies this to a . + /// Applies this to an . /// - /// The to apply to. - void ApplyToBeatmap(Beatmap beatmap); + /// The to apply to. + void ApplyToBeatmap(IBeatmap beatmap); } } diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index a5f96087c0..9edf57ad00 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -13,27 +13,21 @@ using osuTK; namespace osu.Game.Rulesets.Mods { - public abstract class ModTimeRamp : Mod + public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToClock, IApplicableToBeatmap { - public override Type[] IncompatibleMods => new[] { typeof(ModTimeAdjust) }; - - protected abstract double FinalRateAdjustment { get; } - } - - public abstract class ModTimeRamp : ModTimeRamp, IUpdatableByPlayfield, IApplicableToClock, IApplicableToBeatmap - where T : HitObject - { - private double finalRateTime; - - private double beginRampTime; - - private IAdjustableClock clock; - /// /// The point in the beatmap at which the final ramping rate should be reached. /// private const double final_rate_progress = 0.75f; + public override Type[] IncompatibleMods => new[] { typeof(ModTimeAdjust) }; + + protected abstract double FinalRateAdjustment { get; } + + private double finalRateTime; + private double beginRampTime; + private IAdjustableClock clock; + public virtual void ApplyToClock(IAdjustableClock clock) { this.clock = clock; @@ -44,7 +38,7 @@ namespace osu.Game.Rulesets.Mods applyAdjustment(1); } - public virtual void ApplyToBeatmap(Beatmap beatmap) + public virtual void ApplyToBeatmap(IBeatmap beatmap) { HitObject lastObject = beatmap.HitObjects.LastOrDefault(); diff --git a/osu.Game/Rulesets/Mods/ModWindDown.cs b/osu.Game/Rulesets/Mods/ModWindDown.cs index 5d71c8950b..b2e3abb59d 100644 --- a/osu.Game/Rulesets/Mods/ModWindDown.cs +++ b/osu.Game/Rulesets/Mods/ModWindDown.cs @@ -4,12 +4,10 @@ using System; using System.Linq; using osu.Framework.Graphics.Sprites; -using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mods { - public class ModWindDown : ModTimeRamp - where T : HitObject + public class ModWindDown : ModTimeRamp { public override string Name => "Wind Down"; public override string Acronym => "WD"; @@ -19,6 +17,6 @@ namespace osu.Game.Rulesets.Mods protected override double FinalRateAdjustment => -0.25; - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindUp)).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindUp)).ToArray(); } } diff --git a/osu.Game/Rulesets/Mods/ModWindUp.cs b/osu.Game/Rulesets/Mods/ModWindUp.cs index aae85cec19..8df35a1de2 100644 --- a/osu.Game/Rulesets/Mods/ModWindUp.cs +++ b/osu.Game/Rulesets/Mods/ModWindUp.cs @@ -4,12 +4,10 @@ using System; using System.Linq; using osu.Framework.Graphics.Sprites; -using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mods { - public class ModWindUp : ModTimeRamp - where T : HitObject + public class ModWindUp : ModTimeRamp { public override string Name => "Wind Up"; public override string Acronym => "WU"; @@ -19,6 +17,6 @@ namespace osu.Game.Rulesets.Mods protected override double FinalRateAdjustment => 0.5; - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindDown)).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindDown)).ToArray(); } } diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 52fba9cab3..faa6b98c86 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -278,7 +278,7 @@ namespace osu.Game.Rulesets.UI if (mods == null) return; - foreach (var mod in mods.OfType>()) + foreach (var mod in mods.OfType()) mod.ApplyToBeatmap(Beatmap); } From bc80fa11bbeee2b39174ec1b68132cc3a6468bfe Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Aug 2019 12:41:46 +0900 Subject: [PATCH 1981/2854] Mode IApplicableToBeatmap application to WorkingBeatmap --- osu.Game/Beatmaps/WorkingBeatmap.cs | 3 +++ osu.Game/Rulesets/UI/DrawableRuleset.cs | 15 --------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 949a2aab6f..baf921ddfc 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -141,6 +141,9 @@ namespace osu.Game.Beatmaps processor?.PostProcess(); + foreach (var mod in mods.OfType()) + mod.ApplyToBeatmap(Beatmap); + return converted; } diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index faa6b98c86..ac81fdc719 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -109,8 +109,6 @@ namespace osu.Game.Rulesets.UI Beatmap = (Beatmap)workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods); - applyBeatmapMods(mods); - KeyBindingInputManager = CreateInputManager(); playfield = new Lazy(CreatePlayfield); @@ -269,19 +267,6 @@ namespace osu.Game.Rulesets.UI public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(this); - /// - /// Applies the active mods to the Beatmap. - /// - /// - private void applyBeatmapMods(IReadOnlyList mods) - { - if (mods == null) - return; - - foreach (var mod in mods.OfType()) - mod.ApplyToBeatmap(Beatmap); - } - /// /// Applies the active mods to this DrawableRuleset. /// From 0108700793069bd524b390e50e44ea7b00869247 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 31 Jul 2019 19:48:50 +0900 Subject: [PATCH 1982/2854] Make beatmap conversion test use WorkingBeatmap --- .../ManiaBeatmapConversionTest.cs | 39 ++++++++-- osu.Game/Beatmaps/WorkingBeatmap.cs | 10 ++- .../Tests/Beatmaps/BeatmapConversionTest.cs | 72 ++++++++++++++----- 3 files changed, 94 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs index 6b95975059..51c7ba029a 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs @@ -35,11 +35,37 @@ namespace osu.Game.Rulesets.Mania.Tests }; } - protected override ManiaConvertMapping CreateConvertMapping() => new ManiaConvertMapping(Converter); + private readonly Dictionary rngSnapshots = new Dictionary(); + + protected override void OnConversionGenerated(HitObject original, IEnumerable result, IBeatmapConverter beatmapConverter) + { + base.OnConversionGenerated(original, result, beatmapConverter); + + rngSnapshots[original] = new RngSnapshot(beatmapConverter); + } + + protected override ManiaConvertMapping CreateConvertMapping(HitObject source) => new ManiaConvertMapping(rngSnapshots[source]); protected override Ruleset CreateRuleset() => new ManiaRuleset(); } + public class RngSnapshot + { + public readonly uint RandomW; + public readonly uint RandomX; + public readonly uint RandomY; + public readonly uint RandomZ; + + public RngSnapshot(IBeatmapConverter converter) + { + var maniaConverter = (ManiaBeatmapConverter)converter; + RandomW = maniaConverter.Random.W; + RandomX = maniaConverter.Random.X; + RandomY = maniaConverter.Random.Y; + RandomZ = maniaConverter.Random.Z; + } + } + public class ManiaConvertMapping : ConvertMapping, IEquatable { public uint RandomW; @@ -51,13 +77,12 @@ namespace osu.Game.Rulesets.Mania.Tests { } - public ManiaConvertMapping(IBeatmapConverter converter) + public ManiaConvertMapping(RngSnapshot snapshot) { - var maniaConverter = (ManiaBeatmapConverter)converter; - RandomW = maniaConverter.Random.W; - RandomX = maniaConverter.Random.X; - RandomY = maniaConverter.Random.Y; - RandomZ = maniaConverter.Random.Z; + RandomW = snapshot.RandomW; + RandomX = snapshot.RandomX; + RandomY = snapshot.RandomY; + RandomZ = snapshot.RandomZ; } public bool Equals(ManiaConvertMapping other) => other != null && RandomW == other.RandomW && RandomX == other.RandomX && RandomY == other.RandomY && RandomZ == other.RandomZ; diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index baf921ddfc..6b3a21a2c1 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -89,6 +89,14 @@ namespace osu.Game.Beatmaps return path; } + /// + /// Creates a to convert a for a specified . + /// + /// The to be converted. + /// The for which should be converted. + /// The applicable . + protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap); + /// /// Constructs a playable from using the applicable converters for a specific . /// @@ -104,7 +112,7 @@ namespace osu.Game.Beatmaps { var rulesetInstance = ruleset.CreateInstance(); - IBeatmapConverter converter = rulesetInstance.CreateBeatmapConverter(Beatmap); + IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance); // Check if the beatmap can be converted if (!converter.CanConvert) diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 6a5e17eb38..1f2d457624 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -4,13 +4,16 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using Newtonsoft.Json; using NUnit.Framework; -using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; namespace osu.Game.Tests.Beatmaps @@ -25,8 +28,6 @@ namespace osu.Game.Tests.Beatmaps protected abstract string ResourceAssembly { get; } - protected IBeatmapConverter Converter { get; private set; } - protected void Test(string name) { var ourResult = convert(name); @@ -98,26 +99,33 @@ namespace osu.Game.Tests.Beatmaps var rulesetInstance = CreateRuleset(); beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo(); - Converter = rulesetInstance.CreateBeatmapConverter(beatmap); + var converterResult = new Dictionary>(); - var result = new ConvertResult(); - - Converter.ObjectConverted += (orig, converted) => + var working = new ConversionWorkingBeatmap(beatmap) { - converted.ForEach(h => h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty)); - - var mapping = CreateConvertMapping(); - mapping.StartTime = orig.StartTime; - - foreach (var obj in converted) - mapping.Objects.AddRange(CreateConvertValue(obj)); - result.Mappings.Add(mapping); + ConversionGenerated = (o, r, c) => + { + converterResult[o] = r; + OnConversionGenerated(o, r, c); + } }; - IBeatmap convertedBeatmap = Converter.Convert(); - rulesetInstance.CreateBeatmapProcessor(convertedBeatmap)?.PostProcess(); + working.GetPlayableBeatmap(rulesetInstance.RulesetInfo, Array.Empty()); - return result; + return new ConvertResult + { + Mappings = converterResult.Select(r => + { + var mapping = CreateConvertMapping(r.Key); + mapping.StartTime = r.Key.StartTime; + mapping.Objects.AddRange(r.Value.SelectMany(CreateConvertValue)); + return mapping; + }).ToList() + }; + } + + protected virtual void OnConversionGenerated(HitObject original, IEnumerable result, IBeatmapConverter beatmapConverter) + { } private ConvertResult read(string name) @@ -154,7 +162,7 @@ namespace osu.Game.Tests.Beatmaps /// This should be used to validate the integrity of the conversion process after a conversion has occurred. /// /// - protected virtual TConvertMapping CreateConvertMapping() => new TConvertMapping(); + protected virtual TConvertMapping CreateConvertMapping(HitObject source) => new TConvertMapping(); /// public ResumeOverlay ResumeOverlay { get; protected set; } + /// + /// Returns first available provided by a . + /// + [CanBeNull] + public HitWindows FirstAvailableHitWindows + { + get + { + foreach (var h in Objects) + { + if (h.HitWindows != null) + return h.HitWindows; + + foreach (var n in h.NestedHitObjects) + if (n.HitWindows != null) + return n.HitWindows; + } + + return null; + } + } + protected virtual ResumeOverlay CreateResumeOverlay() => null; /// diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index eac45f9214..eee7235a6e 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -259,9 +258,7 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20, Right = 10 }, }; - protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay( - scoreProcessor, - drawableRuleset.Objects.Concat(drawableRuleset.Objects.SelectMany(h => h.NestedHitObjects)).FirstOrDefault(h => h.HitWindows != null)?.HitWindows); + protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset.FirstAvailableHitWindows); protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); From d1cdf49dd51a6d5b66f006abb2afc5417e04c3e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 14:21:54 +0900 Subject: [PATCH 2549/2854] Revert SkinnableSprite lookups to old behaviour --- osu.Game.Rulesets.Catch/CatchSkinComponent.cs | 2 +- osu.Game.Rulesets.Catch/CatchSkinComponents.cs | 1 - osu.Game.Rulesets.Catch/UI/CatcherSprite.cs | 2 +- .../Objects/Drawables/Pieces/ApproachCircle.cs | 2 +- osu.Game/Skinning/SkinnableSprite.cs | 16 ++++++++++++++-- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchSkinComponent.cs b/osu.Game.Rulesets.Catch/CatchSkinComponent.cs index 0a3e43dcfc..8bf53e53e3 100644 --- a/osu.Game.Rulesets.Catch/CatchSkinComponent.cs +++ b/osu.Game.Rulesets.Catch/CatchSkinComponent.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Catch { } - protected override string RulesetPrefix => CatchRuleset.SHORT_NAME; + protected override string RulesetPrefix => "catch"; // todo: use CatchRuleset.SHORT_NAME; protected override string ComponentName => Component.ToString().ToLower(); } diff --git a/osu.Game.Rulesets.Catch/CatchSkinComponents.cs b/osu.Game.Rulesets.Catch/CatchSkinComponents.cs index c03fe42af7..7e482d4045 100644 --- a/osu.Game.Rulesets.Catch/CatchSkinComponents.cs +++ b/osu.Game.Rulesets.Catch/CatchSkinComponents.cs @@ -5,6 +5,5 @@ namespace osu.Game.Rulesets.Catch { public enum CatchSkinComponents { - Catcher } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs index 1c2fe3517a..e3c6c93d01 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.UI [BackgroundDependencyLoader] private void load() { - InternalChild = new SkinnableSprite(new CatchSkinComponent(CatchSkinComponents.Catcher)) + InternalChild = new SkinnableSprite("Gameplay/catch/fruit-catcher-idle") { RelativeSizeAxes = Axes.Both, Anchor = Anchor.TopCentre, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs index c17c276205..1b474f265c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private class SkinnableApproachCircle : SkinnableSprite { public SkinnableApproachCircle() - : base(new OsuSkinComponent(OsuSkinComponents.ApproachCircle)) + : base("Gameplay/osu/approachcircle") { } diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index 0081aef520..4b78493e97 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -19,11 +19,23 @@ namespace osu.Game.Skinning [Resolved] private TextureStore textures { get; set; } - public SkinnableSprite(ISkinComponent component, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) - : base(component, allowFallback, confineMode) + public SkinnableSprite(string textureName, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + : base(new SpriteComponent(textureName), allowFallback, confineMode) { } protected override Drawable CreateDefault(ISkinComponent component) => new Sprite { Texture = textures.Get(component.LookupName) }; + + private class SpriteComponent : ISkinComponent + { + private readonly string textureName; + + public SpriteComponent(string textureName) + { + this.textureName = textureName; + } + + public string LookupName => textureName; + } } } From bebc3309ced768307cac8398cebf804055dbf79c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 17:57:34 +0900 Subject: [PATCH 2550/2854] Refactor skin configuration to be infinitely extensible --- .../TestSceneCatcher.cs | 4 +- .../TestSceneSkinFallbacks.cs | 2 + .../Objects/Drawables/DrawableSlider.cs | 9 ++-- .../Objects/Drawables/Pieces/SliderBall.cs | 3 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- .../Skinning/LegacySliderBall.cs | 2 +- ...acySkin.cs => OsuLegacySkinTransformer.cs} | 51 +++++++++--------- .../Skinning/OsuSkinColour.cs | 12 +++++ .../Skinning/OsuSkinConfiguration.cs | 14 +++++ osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 3 +- .../Gameplay/TestSceneSkinnableDrawable.cs | 10 ++-- .../Objects/Drawables/DrawableHitObject.cs | 6 ++- osu.Game/Screens/Menu/LogoVisualisation.cs | 2 +- osu.Game/Screens/Menu/MenuSideFlashes.cs | 2 +- osu.Game/Skinning/DefaultSkin.cs | 5 +- osu.Game/Skinning/DefaultSkinConfiguration.cs | 4 -- osu.Game/Skinning/GameplaySkinComponent.cs | 2 +- osu.Game/Skinning/GlobalSkinColour.cs | 10 ++++ osu.Game/Skinning/GlobalSkinConfiguration.cs | 10 ++++ osu.Game/Skinning/ISkin.cs | 4 +- osu.Game/Skinning/LegacySkin.cs | 43 +++++++++++++++ osu.Game/Skinning/LegacySkinDecoder.cs | 54 +++++++------------ osu.Game/Skinning/Skin.cs | 8 +-- osu.Game/Skinning/SkinConfigManager.cs | 16 ++++++ osu.Game/Skinning/SkinConfiguration.cs | 10 +--- osu.Game/Skinning/SkinCustomColourLookup.cs | 15 ++++++ osu.Game/Skinning/SkinManager.cs | 4 +- osu.Game/Skinning/SkinProvidingContainer.cs | 14 +++-- 28 files changed, 214 insertions(+), 107 deletions(-) rename osu.Game.Rulesets.Osu/Skinning/{OsuLegacySkin.cs => OsuLegacySkinTransformer.cs} (70%) create mode 100644 osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs create mode 100644 osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs create mode 100644 osu.Game/Skinning/GlobalSkinColour.cs create mode 100644 osu.Game/Skinning/GlobalSkinConfiguration.cs create mode 100644 osu.Game/Skinning/SkinConfigManager.cs create mode 100644 osu.Game/Skinning/SkinCustomColourLookup.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index c89cd95f36..6a4294a178 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osuTK.Graphics; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics.Textures; using osu.Game.Audio; @@ -99,8 +100,7 @@ namespace osu.Game.Rulesets.Catch.Tests public Texture GetTexture(string componentName) => throw new NotImplementedException(); - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => - throw new NotImplementedException(); + public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); } } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs index fe73e7c861..02c65db6ad 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; @@ -135,6 +136,7 @@ namespace osu.Game.Rulesets.Osu.Tests public SampleChannel GetSample(ISampleInfo sampleInfo) => null; public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => default; + public IBindable GetConfig(TLookup lookup) => null; public event Action SourceChanged; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 1749ea1f60..00c953c393 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -12,6 +12,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Configuration; +using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Scoring; using osuTK.Graphics; using osu.Game.Skinning; @@ -166,12 +167,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.SkinChanged(skin, allowFallback); - Body.BorderSize = skin.GetValue(s => s.SliderBorderSize) ?? SliderBody.DEFAULT_BORDER_SIZE; - sliderPathRadius = skin.GetValue(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS; + Body.BorderSize = skin.GetConfig(OsuSkinConfiguration.SliderBorderSize)?.Value ?? SliderBody.DEFAULT_BORDER_SIZE; + sliderPathRadius = skin.GetConfig(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS; updatePathRadius(); - Body.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? AccentColour.Value; - Body.BorderColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Color4.White; + Body.AccentColour = skin.GetConfig(OsuSkinColour.SliderTrackOverride)?.Value ?? AccentColour.Value; + Body.BorderColour = skin.GetConfig(OsuSkinColour.SliderBorder)?.Value ?? Color4.White; } private void updatePathRadius() => Body.PathRadius = slider.Scale * sliderPathRadius; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 7c871c6ccd..ef7b077480 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -11,6 +11,7 @@ using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Skinning; using osuTK.Graphics; using osu.Game.Skinning; using osuTK; @@ -218,7 +219,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { RelativeSizeAxes = Axes.Both; - float radius = skin.GetValue(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS; + float radius = skin.GetConfig(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS; InternalChild = new CircularContainer { diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 27899ab56e..ceb9ed9343 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this); - public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkin(source); + public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkinTransformer(source); public override int? LegacyID => 0; diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs index ec838c596d..81c02199d0 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Skinning [BackgroundDependencyLoader] private void load(ISkinSource skin, DrawableHitObject drawableObject) { - animationContent.Colour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White; + animationContent.Colour = skin.GetConfig(OsuSkinColour.SliderBall)?.Value ?? Color4.White; InternalChildren = new[] { diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs similarity index 70% rename from osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs rename to osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index e3e302b81c..284259705a 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -3,21 +3,19 @@ using System; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Audio; using osu.Game.Skinning; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning { - public class OsuLegacySkin : ISkin + public class OsuLegacySkinTransformer : ISkin { private readonly ISkin source; - private Lazy configuration; - private Lazy hasHitCircle; /// @@ -27,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Skinning /// private const float legacy_circle_radius = 64 - 5; - public OsuLegacySkin(ISkinSource source) + public OsuLegacySkinTransformer(ISkinSource source) { this.source = source; @@ -37,21 +35,6 @@ namespace osu.Game.Rulesets.Osu.Skinning private void sourceChanged() { - // these need to be lazy in order to ensure they aren't called before the dependencies have been loaded into our source. - configuration = new Lazy(() => - { - var config = new SkinConfiguration(); - if (hasHitCircle.Value) - config.SliderPathRadius = legacy_circle_radius; - - // defaults should only be applied for non-beatmap skins (which are parsed via this constructor). - config.CustomColours["SliderBall"] = - source.GetValue(s => s.CustomColours.TryGetValue("SliderBall", out var val) ? val : (Color4?)null) - ?? new Color4(2, 170, 255, 255); - - return config; - }); - hasHitCircle = new Lazy(() => source.GetTexture("hitcircle") != null); } @@ -96,8 +79,8 @@ namespace osu.Game.Rulesets.Osu.Skinning return null; case OsuSkinComponents.HitCircleText: - string font = GetValue(config => config.HitCircleFont); - var overlap = GetValue(config => config.HitCircleOverlap); + var font = GetConfig(OsuSkinConfiguration.HitCircleFont)?.Value ?? "default"; + var overlap = GetConfig(OsuSkinConfiguration.HitCircleOverlap)?.Value ?? 0; return !hasFont(font) ? null @@ -116,13 +99,27 @@ namespace osu.Game.Rulesets.Osu.Skinning public SampleChannel GetSample(ISampleInfo sample) => source.GetSample(sample); - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration + public IBindable GetConfig(TLookup lookup) { - TValue val; - if (configuration.Value is TConfiguration conf && (val = query.Invoke(conf)) != null) - return val; + switch (lookup) + { + case OsuSkinColour colour: + return source.GetConfig(new SkinCustomColourLookup(colour)); - return source.GetValue(query); + case OsuSkinConfiguration osuLookup: + switch (osuLookup) + { + case OsuSkinConfiguration.SliderPathRadius: + if (hasHitCircle.Value) + return new BindableFloat(legacy_circle_radius) as Bindable; + + break; + } + + break; + } + + return source.GetConfig(lookup); } private bool hasFont(string fontName) => source.GetTexture($"{fontName}-0") != null; diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs new file mode 100644 index 0000000000..4e6d3ef0e4 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs @@ -0,0 +1,12 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Osu.Skinning +{ + public enum OsuSkinColour + { + SliderTrackOverride, + SliderBorder, + SliderBall + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs new file mode 100644 index 0000000000..a6b87150ae --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Osu.Skinning +{ + public enum OsuSkinConfiguration + { + HitCircleFont, + HitCircleOverlap, + SliderBorderSize, + SliderPathRadius, + CursorExpand, + } +} diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index 869c27dcac..ac641ecfbc 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -38,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor protected override void SkinChanged(ISkinSource skin, bool allowFallback) { - cursorExpand = skin.GetValue(s => s.CursorExpand ?? true); + cursorExpand = skin.GetConfig(OsuSkinConfiguration.CursorExpand)?.Value ?? true; } [BackgroundDependencyLoader] diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs index ee5552c6e0..91ee16cab7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Linq; using NUnit.Framework; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -137,7 +138,8 @@ namespace osu.Game.Tests.Visual.Gameplay { public new Drawable Drawable => base.Drawable; - public ExposedSkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + public ExposedSkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, + ConfineMode confineMode = ConfineMode.ScaleDownToFit) : base(new TestSkinComponent(name), defaultImplementation, allowFallback, confineMode) { } @@ -256,7 +258,7 @@ namespace osu.Game.Tests.Visual.Gameplay public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); + public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); } private class SecondarySource : ISkin @@ -267,7 +269,7 @@ namespace osu.Game.Tests.Visual.Gameplay public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); + public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); } private class SkinSourceContainer : Container, ISkin @@ -278,7 +280,7 @@ namespace osu.Game.Tests.Visual.Gameplay public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); + public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); } private class TestSkinComponent : ISkinComponent diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 1a224b2cea..a6d0aad880 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -241,7 +241,11 @@ namespace osu.Game.Rulesets.Objects.Drawables base.SkinChanged(skin, allowFallback); if (HitObject is IHasComboInformation combo) - AccentColour.Value = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; + { + var comboColours = skin.GetConfig>(GlobalSkinConfiguration.ComboColours)?.Value; + + AccentColour.Value = comboColours?.Count > 0 ? comboColours[combo.ComboIndex % comboColours.Count] : Color4.White; + } } /// diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 6984959e9c..59ab6ad265 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -122,7 +122,7 @@ namespace osu.Game.Screens.Menu Color4 defaultColour = Color4.White.Opacity(0.2f); if (user.Value?.IsSupporter ?? false) - AccentColour = skin.Value.GetValue(s => s.CustomColours.ContainsKey("MenuGlow") ? s.CustomColours["MenuGlow"] : (Color4?)null) ?? defaultColour; + AccentColour = skin.Value.GetConfig(GlobalSkinColour.MenuGlow)?.Value ?? defaultColour; else AccentColour = defaultColour; } diff --git a/osu.Game/Screens/Menu/MenuSideFlashes.cs b/osu.Game/Screens/Menu/MenuSideFlashes.cs index 393964561c..55a6a33e89 100644 --- a/osu.Game/Screens/Menu/MenuSideFlashes.cs +++ b/osu.Game/Screens/Menu/MenuSideFlashes.cs @@ -112,7 +112,7 @@ namespace osu.Game.Screens.Menu Color4 baseColour = colours.Blue; if (user.Value?.IsSupporter ?? false) - baseColour = skin.Value.GetValue(s => s.CustomColours.ContainsKey("MenuGlow") ? s.CustomColours["MenuGlow"] : (Color4?)null) ?? baseColour; + baseColour = skin.Value.GetConfig(GlobalSkinColour.MenuGlow)?.Value ?? baseColour; // linear colour looks better in this case, so let's use it for now. Color4 gradientDark = baseColour.Opacity(0).ToLinear(); diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 9eda5d597a..4dee70a47f 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -1,7 +1,8 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Audio; @@ -21,5 +22,7 @@ namespace osu.Game.Skinning public override Texture GetTexture(string componentName) => null; public override SampleChannel GetSample(ISampleInfo sampleInfo) => null; + + public override IBindable GetConfig(TLookup lookup) => null; } } diff --git a/osu.Game/Skinning/DefaultSkinConfiguration.cs b/osu.Game/Skinning/DefaultSkinConfiguration.cs index 722b35f102..f52fac6077 100644 --- a/osu.Game/Skinning/DefaultSkinConfiguration.cs +++ b/osu.Game/Skinning/DefaultSkinConfiguration.cs @@ -12,8 +12,6 @@ namespace osu.Game.Skinning { public DefaultSkinConfiguration() { - HitCircleFont = "default"; - ComboColours.AddRange(new[] { new Color4(17, 136, 170, 255), @@ -21,8 +19,6 @@ namespace osu.Game.Skinning new Color4(204, 102, 0, 255), new Color4(121, 9, 13, 255) }); - - CursorExpand = true; } } } diff --git a/osu.Game/Skinning/GameplaySkinComponent.cs b/osu.Game/Skinning/GameplaySkinComponent.cs index 8695b3d720..2aa380fa90 100644 --- a/osu.Game/Skinning/GameplaySkinComponent.cs +++ b/osu.Game/Skinning/GameplaySkinComponent.cs @@ -5,7 +5,7 @@ using System.Linq; namespace osu.Game.Skinning { - public class GameplaySkinComponent : ISkinComponent where T : struct + public class GameplaySkinComponent : ISkinComponent { public readonly T Component; diff --git a/osu.Game/Skinning/GlobalSkinColour.cs b/osu.Game/Skinning/GlobalSkinColour.cs new file mode 100644 index 0000000000..d039be98ce --- /dev/null +++ b/osu.Game/Skinning/GlobalSkinColour.cs @@ -0,0 +1,10 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Skinning +{ + public enum GlobalSkinColour + { + MenuGlow + } +} diff --git a/osu.Game/Skinning/GlobalSkinConfiguration.cs b/osu.Game/Skinning/GlobalSkinConfiguration.cs new file mode 100644 index 0000000000..66dc9a9395 --- /dev/null +++ b/osu.Game/Skinning/GlobalSkinConfiguration.cs @@ -0,0 +1,10 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Skinning +{ + public enum GlobalSkinConfiguration + { + ComboColours + } +} diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index bc1ae634c9..841ff3d357 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.cs @@ -1,8 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Audio; @@ -20,6 +20,6 @@ namespace osu.Game.Skinning SampleChannel GetSample(ISampleInfo sampleInfo); - TValue GetValue(Func query) where TConfiguration : SkinConfiguration; + IBindable GetConfig(TLookup lookup); } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 535471f455..53f7c54003 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -1,10 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.IO; using System.Linq; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; @@ -48,6 +50,47 @@ namespace osu.Game.Skinning Samples?.Dispose(); } + public override IBindable GetConfig(TLookup lookup) + { + switch (lookup) + { + case GlobalSkinConfiguration global: + switch (global) + { + case GlobalSkinConfiguration.ComboColours: + return new Bindable>(Configuration.ComboColours) as IBindable; + } + + break; + + case GlobalSkinColour colour: + return getCustomColour(colour.ToString()) as IBindable; + + case SkinCustomColourLookup customColour: + return getCustomColour(customColour.Lookup.ToString()) as IBindable; + + default: + try + { + if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString(), out var val)) + { + var bindable = new Bindable(); + bindable.Parse(val); + return bindable; + } + } + catch + { + } + + break; + } + + return null; + } + + private IBindable getCustomColour(string lookup) => Configuration.CustomColours.TryGetValue(lookup, out var col) ? new Bindable(col) : null; + public override Drawable GetDrawableComponent(ISkinComponent component) { switch (component) diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index 0160755eed..1912c4cd05 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -14,47 +14,31 @@ namespace osu.Game.Skinning protected override void ParseLine(DefaultSkinConfiguration skin, Section section, string line) { - line = StripComments(line); - - var pair = SplitKeyVal(line); - - switch (section) + if (section != Section.Colours) { - case Section.General: - switch (pair.Key) - { - case @"Name": - skin.SkinInfo.Name = pair.Value; - break; + line = StripComments(line); - case @"Author": - skin.SkinInfo.Creator = pair.Value; - break; + var pair = SplitKeyVal(line); - case @"CursorExpand": - skin.CursorExpand = pair.Value != "0"; - break; + switch (section) + { + case Section.General: + switch (pair.Key) + { + case @"Name": + skin.SkinInfo.Name = pair.Value; + return; - case @"SliderBorderSize": - skin.SliderBorderSize = Parsing.ParseFloat(pair.Value); - break; - } + case @"Author": + skin.SkinInfo.Creator = pair.Value; + return; + } - break; + break; + } - case Section.Fonts: - switch (pair.Key) - { - case "HitCirclePrefix": - skin.HitCircleFont = pair.Value; - break; - - case "HitCircleOverlap": - skin.HitCircleOverlap = int.Parse(pair.Value); - break; - } - - break; + if (!string.IsNullOrEmpty(pair.Key)) + skin.ConfigDictionary[$"{section}/{pair.Key}"] = pair.Value; } base.ParseLine(skin, section, line); diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 299f257e57..fa4aebd8a5 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -1,8 +1,9 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Audio; @@ -13,7 +14,7 @@ namespace osu.Game.Skinning { public readonly SkinInfo SkinInfo; - public virtual SkinConfiguration Configuration { get; protected set; } + public SkinConfiguration Configuration { get; protected set; } public abstract Drawable GetDrawableComponent(ISkinComponent componentName); @@ -21,8 +22,7 @@ namespace osu.Game.Skinning public abstract Texture GetTexture(string componentName); - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration - => Configuration is TConfiguration conf ? query.Invoke(conf) : default; + public abstract IBindable GetConfig(TLookup lookup); protected Skin(SkinInfo skin) { diff --git a/osu.Game/Skinning/SkinConfigManager.cs b/osu.Game/Skinning/SkinConfigManager.cs new file mode 100644 index 0000000000..896444d1d2 --- /dev/null +++ b/osu.Game/Skinning/SkinConfigManager.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Configuration; + +namespace osu.Game.Skinning +{ + public class SkinConfigManager : ConfigManager where T : struct + { + protected override void PerformLoad() + { + } + + protected override bool PerformSave() => false; + } +} diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs index d585c58ef1..54aac86e3c 100644 --- a/osu.Game/Skinning/SkinConfiguration.cs +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -18,14 +18,6 @@ namespace osu.Game.Skinning public Dictionary CustomColours { get; set; } = new Dictionary(); - public string HitCircleFont { get; set; } - - public int HitCircleOverlap { get; set; } - - public float? SliderBorderSize { get; set; } - - public float? SliderPathRadius { get; set; } - - public bool? CursorExpand { get; set; } + public readonly Dictionary ConfigDictionary = new Dictionary(); } } diff --git a/osu.Game/Skinning/SkinCustomColourLookup.cs b/osu.Game/Skinning/SkinCustomColourLookup.cs new file mode 100644 index 0000000000..b8e5ac9b53 --- /dev/null +++ b/osu.Game/Skinning/SkinCustomColourLookup.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Skinning +{ + public class SkinCustomColourLookup + { + public readonly object Lookup; + + public SkinCustomColourLookup(object lookup) + { + Lookup = lookup; + } + } +} diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index a55a128dff..aa3b3981c2 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -131,6 +131,6 @@ namespace osu.Game.Skinning public SampleChannel GetSample(ISampleInfo sampleInfo) => CurrentSkin.Value.GetSample(sampleInfo); - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => CurrentSkin.Value.GetValue(query); + public IBindable GetConfig(TLookup lookup) => CurrentSkin.Value.GetConfig(lookup); } } diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index 85a80655ea..ef7f5f381b 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; @@ -64,13 +65,16 @@ namespace osu.Game.Skinning return fallbackSource?.GetSample(sampleInfo); } - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration + public IBindable GetConfig(TLookup lookup) { - TValue val; - if (AllowConfigurationLookup && skin != null && (val = skin.GetValue(query)) != null) - return val; + if (AllowConfigurationLookup && skin != null) + { + var bindable = skin.GetConfig(lookup); + if (bindable != null) + return bindable; + } - return fallbackSource == null ? default : fallbackSource.GetValue(query); + return fallbackSource?.GetConfig(lookup); } protected virtual void TriggerSourceChanged() => SourceChanged?.Invoke(); From 097012dc95c05e829864d451b35be143844cdc7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 18:05:16 +0900 Subject: [PATCH 2551/2854] Move slider ball colouring to DefaultLegacySkin for now --- osu.Game/Skinning/DefaultLegacySkin.cs | 2 ++ osu.Game/Skinning/LegacySkin.cs | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index b35c9c7b97..98f158c725 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -3,6 +3,7 @@ using osu.Framework.Audio; using osu.Framework.IO.Stores; +using osuTK.Graphics; namespace osu.Game.Skinning { @@ -11,6 +12,7 @@ namespace osu.Game.Skinning public DefaultLegacySkin(IResourceStore storage, AudioManager audioManager) : base(Info, storage, audioManager, string.Empty) { + Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); } public static SkinInfo Info { get; } = new SkinInfo diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 53f7c54003..5f0afae075 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -25,8 +25,6 @@ namespace osu.Game.Skinning public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) : this(skin, new LegacySkinResourceStore(skin, storage), audioManager, "skin.ini") { - // defaults should only be applied for non-beatmap skins (which are parsed via this constructor). - if (!Configuration.CustomColours.ContainsKey("SliderBall")) Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); } protected LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager, string filename) From 352fd3efdaa1fcddd2a19bc87287134b02ebed97 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2019 09:18:24 +0000 Subject: [PATCH 2552/2854] Bump ppy.osu.Game.Resources from 2019.903.0 to 2019.903.1 Bumps [ppy.osu.Game.Resources](https://github.com/ppy/osu-resources) from 2019.903.0 to 2019.903.1. - [Release notes](https://github.com/ppy/osu-resources/releases) - [Commits](https://github.com/ppy/osu-resources/compare/2019.903.0...2019.903.1) Signed-off-by: dependabot-preview[bot] --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 743508baf8..96706f2bdc 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -60,7 +60,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 03207dfdf7..ca69bb2295 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,7 +14,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index ec76ceaf95..86a2a40940 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -117,7 +117,7 @@ - + From 2f74ef513140bfcd0e8b69c16a4d0731f87b5790 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 18:21:57 +0900 Subject: [PATCH 2553/2854] Add test for changing of a source --- .../Gameplay/TestSceneSkinnableDrawable.cs | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs index ee5552c6e0..80015099cf 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs @@ -5,6 +5,7 @@ using System; using System.Globalization; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -133,6 +134,48 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1); } + [Test] + public void TestSwitchOff() + { + SkinConsumer consumer = null; + SwitchableSkinProvidingContainer target = null; + + AddStep("setup layout", () => + { + Child = new SkinSourceContainer + { + RelativeSizeAxes = Axes.Both, + Child = target = new SwitchableSkinProvidingContainer(new SecondarySource()) + { + RelativeSizeAxes = Axes.Both, + } + }; + }); + + AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true))); + AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox); + AddStep("disable", () => target.Disable()); + AddAssert("consumer using base source", () => consumer.Drawable is BaseSourceBox); + } + + private class SwitchableSkinProvidingContainer : SkinProvidingContainer + { + private bool allow = true; + + protected override bool AllowDrawableLookup(ISkinComponent component) => allow; + + public void Disable() + { + allow = false; + TriggerSourceChanged(); + } + + public SwitchableSkinProvidingContainer(ISkin skin) + : base(skin) + { + } + } + private class ExposedSkinnableDrawable : SkinnableDrawable { public new Drawable Drawable => base.Drawable; @@ -270,7 +313,8 @@ namespace osu.Game.Tests.Visual.Gameplay public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); } - private class SkinSourceContainer : Container, ISkin + [Cached(typeof(ISkinSource))] + private class SkinSourceContainer : Container, ISkinSource { public Drawable GetDrawableComponent(ISkinComponent componentName) => new BaseSourceBox(); @@ -279,6 +323,8 @@ namespace osu.Game.Tests.Visual.Gameplay public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); + + public event Action SourceChanged; } private class TestSkinComponent : ISkinComponent From 002de80c30b2f65eb842e8089382ac74cf2e1964 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 18:30:22 +0900 Subject: [PATCH 2554/2854] Add xmldoc to ISkin --- osu.Game/Skinning/ISkin.cs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index 841ff3d357..cb2a379b8e 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using JetBrains.Annotations; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -14,12 +15,36 @@ namespace osu.Game.Skinning /// public interface ISkin { + /// + /// Retrieve a component implementation. + /// + /// The requested component. + /// A drawable representation for the requested component, or null if unavailable. + [CanBeNull] Drawable GetDrawableComponent(ISkinComponent component); + /// + /// Retrieve a . + /// + /// The requested texture. + /// A matching texture, or null if unavailable. + [CanBeNull] Texture GetTexture(string componentName); + /// + /// Retrieve a . + /// + /// The requested sample. + /// A matching sample channel, or null if unavailable. + [CanBeNull] SampleChannel GetSample(ISampleInfo sampleInfo); + /// + /// Retrieve a configuration value. + /// + /// The requested configuration value. + /// A matching value boxed in an , or null if unavailable. + [CanBeNull] IBindable GetConfig(TLookup lookup); } } From f58ca823986f79491946fd4fe3394ec9fbd4afa1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 18:56:01 +0900 Subject: [PATCH 2555/2854] Don't include section for now --- osu.Game/Skinning/LegacySkinDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index 1912c4cd05..e97664e75e 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -38,7 +38,7 @@ namespace osu.Game.Skinning } if (!string.IsNullOrEmpty(pair.Key)) - skin.ConfigDictionary[$"{section}/{pair.Key}"] = pair.Value; + skin.ConfigDictionary[pair.Key] = pair.Value; } base.ParseLine(skin, section, line); From 343af28ed578cc247ba842c9983d72e4dce138a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 18:59:11 +0900 Subject: [PATCH 2556/2854] Add extra legacy skin parsing tests --- osu.Game.Tests/Resources/skin.ini | 1 + osu.Game.Tests/Skins/LegacySkinDecoderTest.cs | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/osu.Game.Tests/Resources/skin.ini b/osu.Game.Tests/Resources/skin.ini index 0e5737b4ea..7f7f0b32a6 100644 --- a/osu.Game.Tests/Resources/skin.ini +++ b/osu.Game.Tests/Resources/skin.ini @@ -1,5 +1,6 @@ [General] Name: test skin +TestLookup: TestValue [Colours] Combo1 : 142,199,255 diff --git a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs index 24ef9e4535..8bd846518b 100644 --- a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs +++ b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs @@ -41,5 +41,20 @@ namespace osu.Game.Tests.Skins Assert.AreEqual(expectedColors[i], comboColors[i]); } } + + [Test] + public void TestDecodeGeneral() + { + var decoder = new LegacySkinDecoder(); + + using (var resStream = TestResources.OpenResource("skin.ini")) + using (var stream = new StreamReader(resStream)) + { + var config = decoder.Decode(stream); + + Assert.AreEqual("test skin", config.SkinInfo.Name); + Assert.AreEqual("TestValue", config.ConfigDictionary["TestLookup"]); + } + } } } From 299d528654f823689e14ffc6fb56c9f2db55ae5f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 19:20:23 +0900 Subject: [PATCH 2557/2854] Simplify implementation --- osu.Game/Graphics/UserInterface/HoverClickSounds.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs index e64b9259f1..38e6a82bab 100644 --- a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs @@ -36,11 +36,9 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnMouseUp(MouseUpEvent e) { - bool shouldPlayEffect = buttons.Contains(e.Button); - // examine the button pressed first for short-circuiting - // in most usages it is more likely that another button was pressed than that the cursor left the drawable bounds - if (shouldPlayEffect && Contains(e.ScreenSpaceMousePosition)) + if (buttons.Contains(e.Button) && Contains(e.ScreenSpaceMousePosition)) sampleClick?.Play(); + return base.OnMouseUp(e); } From e98059267d4348a92e4d16fe6b75d9ebe45e3912 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 19:21:24 +0900 Subject: [PATCH 2558/2854] Improve xmldoc --- osu.Game/Graphics/UserInterface/HoverClickSounds.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs index 38e6a82bab..1fb73efa65 100644 --- a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs @@ -21,12 +21,12 @@ namespace osu.Game.Graphics.UserInterface private readonly MouseButton[] buttons; /// - /// Creates an instance that adds sounds on hover and on click for any of the buttons specified. + /// a container which plays sounds on hover and click for any specified s. /// /// Set of click samples to play. /// /// Array of button codes which should trigger the click sound. - /// If this optional parameter is omitted or set to null, the click sound will only be added on left click. + /// If this optional parameter is omitted or set to null, the click sound will only be played on left click. /// public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal, MouseButton[] buttons = null) : base(sampleSet) From 4b2cb8854e06735d859568ba1b22e4ceb3a71541 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 19:28:10 +0900 Subject: [PATCH 2559/2854] Fix storyboard samples not stopping on exit --- osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs index b04f1d4518..f3f8308964 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs @@ -64,5 +64,11 @@ namespace osu.Game.Storyboards.Drawables LifetimeEnd = sampleInfo.StartTime; } } + + protected override void Dispose(bool isDisposing) + { + channel?.Stop(); + base.Dispose(isDisposing); + } } } From f8c1afa539e47e6ea8ee5bef22f3c4df35a4a053 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 20:17:39 +0900 Subject: [PATCH 2560/2854] Fix two more cases of judgements appearing on hit error display when they shouldn't --- osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs | 3 +++ osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs index 63713541b4..5bd480c0ff 100644 --- a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs @@ -5,6 +5,7 @@ using System; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; namespace osu.Game.Rulesets.Osu.Objects @@ -28,5 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects } public override Judgement CreateJudgement() => new OsuJudgement(); + + protected override HitWindows CreateHitWindows() => null; } } diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs index 4f2af64161..c53a88337e 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs @@ -23,5 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects } public override Judgement CreateJudgement() => new OsuSliderTailJudgement(); + + protected override HitWindows CreateHitWindows() => null; } } From a8f16503e2faa8dcfe98d1f28297e32149f2cd01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 3 Sep 2019 23:18:39 +0200 Subject: [PATCH 2561/2854] Add backslash escaping to new link format For users to be able to add square brackets inside of links using the new format, the regular expression used for parsing those links contained a balancing group, which can be used for matching pairs of tokens (in this case, opening and closing brackets, in that order). However, this means that users could not post links with unmatched brackets inside of them (ie. ones that contain single brackets, or a closing bracket and then an opening one). Allow for escaping opening and closing brackets using the backslash character. The change substitutes this old fragment of the regex in the display text group: [^\[\]]* // any character other than closing/opening bracket for this one: (((?<=\\)[\[\]])|[^\[\]])* The second pattern in the alternative remains the same; the first one performs the escaping, as follows: ( (?<=\\) // positive lookbehind expression: // this match will succeed, if the next expression // is preceded by a single backslash [\[\]] // either an opening or closing brace ) Since the entire display group is matched, unfortunately the lookbehind expression does not actually strip the backslashes, so they are manually stripped in handleMatches. As demonstrated in the unit tests attached, this also allows balanced brackets to be mixed with escaped ones. --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 36 +++++++++++++++++++ .../Visual/Online/TestSceneChatLink.cs | 1 + osu.Game/Online/Chat/MessageFormatter.cs | 11 ++++-- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index 0d6ed67767..1de6280531 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -131,6 +131,42 @@ namespace osu.Game.Tests.Chat Assert.AreEqual(11, result.Links[0].Length); } + [Test] + public void TestNewFormatLinkWithEscapedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh nasty link with escaped brackets: \\] and \\[]" }); + + Assert.AreEqual("This is a nasty link with escaped brackets: ] and [", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(41, result.Links[0].Length); + } + + [Test] + public void TestNewFormatLinkWithBackslashesInside() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh link \\ with \\ backslashes \\]" }); + + Assert.AreEqual("This is a link \\ with \\ backslashes \\", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(27, result.Links[0].Length); + } + + [Test] + public void TestNewFormatLinkWithEscapedAndBalancedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh [link [with \\] too many brackets \\[ ]]]" }); + + Assert.AreEqual("This is a [link [with ] too many brackets [ ]]", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(36, result.Links[0].Length); + } + [Test] public void TestMarkdownFormatLink() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index c18e0e3064..056ccafe79 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -127,6 +127,7 @@ namespace osu.Game.Tests.Visual.Online addMessageWithChecks("is now playing [https://osu.ppy.sh/b/252238 IMAGE -MATERIAL- ]", 1, true, expectedActions: LinkAction.OpenBeatmap); addMessageWithChecks("Let's (try)[https://osu.ppy.sh/home] [https://osu.ppy.sh/b/252238 multiple links] https://osu.ppy.sh/home", 3, expectedActions: new[] { LinkAction.External, LinkAction.OpenBeatmap, LinkAction.External }); + addMessageWithChecks("[https://osu.ppy.sh/home New link format with escaped [and \\[ paired] braces]", expectedActions: LinkAction.External); // note that there's 0 links here (they get removed if a channel is not found) addMessageWithChecks("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present)."); addMessageWithChecks("I am important!", 0, false, true); diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index db26945ef3..a9fffc196c 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text.RegularExpressions; namespace osu.Game.Online.Chat @@ -16,7 +17,7 @@ namespace osu.Game.Online.Chat private static readonly Regex old_link_regex = new Regex(@"\(([^\)]*)\)\[([a-z]+://[^ ]+)\]"); // [https://osu.ppy.sh/b/1234 Beatmap [Hard] (poop)] -> Beatmap [hard] (poop) (https://osu.ppy.sh/b/1234) - private static readonly Regex new_link_regex = new Regex(@"\[([a-z]+://[^ ]+) ([^\[\]]*(((?\[)[^\[\]]*)+((?\])[^\[\]]*)+)*(?(open)(?!)))\]"); + private static readonly Regex new_link_regex = new Regex(@"\[([a-z]+://[^ ]+) ((((?<=\\)[\[\]])|[^\[\]])*(((?\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]"); // [test](https://osu.ppy.sh/b/1234) -> test (https://osu.ppy.sh/b/1234) aka correct markdown format private static readonly Regex markdown_link_regex = new Regex(@"\[([^\]]*)\]\(([a-z]+://[^ ]+)\)"); @@ -48,7 +49,7 @@ namespace osu.Game.Online.Chat // Unicode emojis private static readonly Regex emoji_regex = new Regex(@"(\uD83D[\uDC00-\uDE4F])"); - private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null) + private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null, char[] escapeChars = null) { int captureOffset = 0; @@ -68,6 +69,10 @@ namespace osu.Game.Online.Chat if (displayText.Length == 0 || linkText.Length == 0) continue; + // Remove backslash escapes in front of the characters provided in escapeChars + if (escapeChars != null) + displayText = escapeChars.Aggregate(displayText, (current, c) => current.Replace($"\\{c}", c.ToString())); + // Check for encapsulated links if (result.Links.Find(l => (l.Index <= index && l.Index + l.Length >= index + m.Length) || (index <= l.Index && index + m.Length >= l.Index + l.Length)) == null) { @@ -183,7 +188,7 @@ namespace osu.Game.Online.Chat var result = new MessageFormatterResult(toFormat); // handle the [link display] format - handleMatches(new_link_regex, "{2}", "{1}", result, startIndex); + handleMatches(new_link_regex, "{2}", "{1}", result, startIndex, escapeChars: new[] { '[', ']' }); // handle the standard markdown []() format handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex); From 24d4f0372c851280eb017553101712ad86731133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 3 Sep 2019 23:56:07 +0200 Subject: [PATCH 2562/2854] Refactor link parsing regexes to use named groups For the sake of readability, consistency and to make further changes easier, introduce named groups (?) and (?) to all link parsing regexes which have parts containing the desired link text and (optionally) URL. The introduction of the named groups additionally simplifies handleMatches() and makes all calls to it consistent. --- osu.Game/Online/Chat/MessageFormatter.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index a9fffc196c..23b5cdc0a6 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -11,16 +11,16 @@ namespace osu.Game.Online.Chat public static class MessageFormatter { // [[Performance Points]] -> wiki:Performance Points (https://osu.ppy.sh/wiki/Performance_Points) - private static readonly Regex wiki_regex = new Regex(@"\[\[([^\]]+)\]\]"); + private static readonly Regex wiki_regex = new Regex(@"\[\[(?[^\]]+)\]\]"); // (test)[https://osu.ppy.sh/b/1234] -> test (https://osu.ppy.sh/b/1234) - private static readonly Regex old_link_regex = new Regex(@"\(([^\)]*)\)\[([a-z]+://[^ ]+)\]"); + private static readonly Regex old_link_regex = new Regex(@"\((?[^\)]*)\)\[(?[a-z]+://[^ ]+)\]"); // [https://osu.ppy.sh/b/1234 Beatmap [Hard] (poop)] -> Beatmap [hard] (poop) (https://osu.ppy.sh/b/1234) - private static readonly Regex new_link_regex = new Regex(@"\[([a-z]+://[^ ]+) ((((?<=\\)[\[\]])|[^\[\]])*(((?\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]"); + private static readonly Regex new_link_regex = new Regex(@"\[(?[a-z]+://[^ ]+) (?(((?<=\\)[\[\]])|[^\[\]])*(((?\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]"); // [test](https://osu.ppy.sh/b/1234) -> test (https://osu.ppy.sh/b/1234) aka correct markdown format - private static readonly Regex markdown_link_regex = new Regex(@"\[([^\]]*)\]\(([a-z]+://[^ ]+)\)"); + private static readonly Regex markdown_link_regex = new Regex(@"\[(?[^\]]*)\]\((?[a-z]+://[^ ]+)\)"); // advanced, RFC-compatible regular expression that matches any possible URL, *but* allows certain invalid characters that are widely used // This is in the format (, [optional]): @@ -59,13 +59,13 @@ namespace osu.Game.Online.Chat var displayText = string.Format(display, m.Groups[0], - m.Groups.Count > 1 ? m.Groups[1].Value : "", - m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim(); + m.Groups["text"].Value, + m.Groups["url"].Value).Trim(); var linkText = string.Format(link, m.Groups[0], - m.Groups.Count > 1 ? m.Groups[1].Value : "", - m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim(); + m.Groups["text"].Value, + m.Groups["url"].Value).Trim(); if (displayText.Length == 0 || linkText.Length == 0) continue; @@ -188,7 +188,7 @@ namespace osu.Game.Online.Chat var result = new MessageFormatterResult(toFormat); // handle the [link display] format - handleMatches(new_link_regex, "{2}", "{1}", result, startIndex, escapeChars: new[] { '[', ']' }); + handleMatches(new_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '[', ']' }); // handle the standard markdown []() format handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex); From f04add6d9edda7bbdf080adc7e6959ee16e6f222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 4 Sep 2019 00:01:26 +0200 Subject: [PATCH 2563/2854] Add bracket handling to Markdown link format Allow users to put both balanced brackets, as well as unbalanced escaped ones, in Markdown link text. The implementation is the exact same as in the case of new format links. For completion's sake, tests also included. --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 47 +++++++++++++++++++ .../Visual/Online/TestSceneChatLink.cs | 1 + osu.Game/Online/Chat/MessageFormatter.cs | 4 +- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index 1de6280531..198267d78a 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -179,6 +179,53 @@ namespace osu.Game.Tests.Chat Assert.AreEqual(11, result.Links[0].Length); } + [Test] + public void TestMarkdownFormatLinkWithBalancedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [tricky [one]](https://osu.ppy.sh)!" }); + + Assert.AreEqual("This is a tricky [one]!", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(12, result.Links[0].Length); + } + + [Test] + public void TestMarkdownFormatLinkWithEscapedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is [another loose bracket \\]](https://osu.ppy.sh)." }); + + Assert.AreEqual("This is another loose bracket ].", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(8, result.Links[0].Index); + Assert.AreEqual(23, result.Links[0].Length); + } + + [Test] + public void TestMarkdownFormatWithBackslashes() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This link [should end with a backslash \\](https://osu.ppy.sh)." }); + Assert.AreEqual("This link should end with a backslash \\.", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(29, result.Links[0].Length); + } + + [Test] + public void TestMarkdownFormatLinkWithEscapedAndBalancedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [\\]super\\[\\[ tricky [one]](https://osu.ppy.sh)!" }); + + Assert.AreEqual("This is a ]super[[ tricky [one]!", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(21, result.Links[0].Length); + } + [Test] public void TestChannelLink() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index 056ccafe79..61c7d3f5b6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -128,6 +128,7 @@ namespace osu.Game.Tests.Visual.Online addMessageWithChecks("Let's (try)[https://osu.ppy.sh/home] [https://osu.ppy.sh/b/252238 multiple links] https://osu.ppy.sh/home", 3, expectedActions: new[] { LinkAction.External, LinkAction.OpenBeatmap, LinkAction.External }); addMessageWithChecks("[https://osu.ppy.sh/home New link format with escaped [and \\[ paired] braces]", expectedActions: LinkAction.External); + addMessageWithChecks("[Markdown link format with escaped [and \\[ paired] braces](https://osu.ppy.sh/home)", expectedActions: LinkAction.External); // note that there's 0 links here (they get removed if a channel is not found) addMessageWithChecks("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present)."); addMessageWithChecks("I am important!", 0, false, true); diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 23b5cdc0a6..e40bb05381 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -20,7 +20,7 @@ namespace osu.Game.Online.Chat private static readonly Regex new_link_regex = new Regex(@"\[(?[a-z]+://[^ ]+) (?(((?<=\\)[\[\]])|[^\[\]])*(((?\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]"); // [test](https://osu.ppy.sh/b/1234) -> test (https://osu.ppy.sh/b/1234) aka correct markdown format - private static readonly Regex markdown_link_regex = new Regex(@"\[(?[^\]]*)\]\((?[a-z]+://[^ ]+)\)"); + private static readonly Regex markdown_link_regex = new Regex(@"\[(?(((?<=\\)[\[\]])|[^\[\]])*(((?\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]\((?[a-z]+://[^ ]+)\)"); // advanced, RFC-compatible regular expression that matches any possible URL, *but* allows certain invalid characters that are widely used // This is in the format (, [optional]): @@ -191,7 +191,7 @@ namespace osu.Game.Online.Chat handleMatches(new_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '[', ']' }); // handle the standard markdown []() format - handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex); + handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '[', ']' }); // handle the ()[] link format handleMatches(old_link_regex, "{1}", "{2}", result, startIndex); From 08350a1acaf4c3bb70176e384c845fc83735d497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 4 Sep 2019 00:17:52 +0200 Subject: [PATCH 2564/2854] Add parenthesis handling to old link format Allow users to put both balanced round parentheses, as well as unbalanced escaped ones, in old style link text. The implementation is the same as for Markdown and new style links, except for swapping all instances of \[\] to \(\) for obvious reasons (different type of parenthesis requiring escaping). Tests also included. --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 47 +++++++++++++++++++ .../Visual/Online/TestSceneChatLink.cs | 5 +- osu.Game/Online/Chat/MessageFormatter.cs | 4 +- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index 198267d78a..9b4a90e9a9 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -119,6 +119,53 @@ namespace osu.Game.Tests.Chat Assert.AreEqual(11, result.Links[0].Length); } + [Test] + public void TestOldFormatLinkWithBalancedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a (tricky (one))[https://osu.ppy.sh]!" }); + + Assert.AreEqual("This is a tricky (one)!", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(12, result.Links[0].Length); + } + + [Test] + public void TestOldFormatLinkWithEscapedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is (another loose bracket \\))[https://osu.ppy.sh]." }); + + Assert.AreEqual("This is another loose bracket ).", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(8, result.Links[0].Index); + Assert.AreEqual(23, result.Links[0].Length); + } + + [Test] + public void TestOldFormatWithBackslashes() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This link (should end with a backslash \\)[https://osu.ppy.sh]." }); + Assert.AreEqual("This link should end with a backslash \\.", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(29, result.Links[0].Length); + } + + [Test] + public void TestOldFormatLinkWithEscapedAndBalancedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a (\\)super\\(\\( tricky (one))[https://osu.ppy.sh]!" }); + + Assert.AreEqual("This is a )super(( tricky (one)!", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(21, result.Links[0].Length); + } + [Test] public void TestNewFormatLink() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index 61c7d3f5b6..a1c77e2db0 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -127,8 +127,9 @@ namespace osu.Game.Tests.Visual.Online addMessageWithChecks("is now playing [https://osu.ppy.sh/b/252238 IMAGE -MATERIAL- ]", 1, true, expectedActions: LinkAction.OpenBeatmap); addMessageWithChecks("Let's (try)[https://osu.ppy.sh/home] [https://osu.ppy.sh/b/252238 multiple links] https://osu.ppy.sh/home", 3, expectedActions: new[] { LinkAction.External, LinkAction.OpenBeatmap, LinkAction.External }); - addMessageWithChecks("[https://osu.ppy.sh/home New link format with escaped [and \\[ paired] braces]", expectedActions: LinkAction.External); - addMessageWithChecks("[Markdown link format with escaped [and \\[ paired] braces](https://osu.ppy.sh/home)", expectedActions: LinkAction.External); + addMessageWithChecks("[https://osu.ppy.sh/home New link format with escaped [and \\[ paired] braces]", 1, expectedActions: LinkAction.External); + addMessageWithChecks("[Markdown link format with escaped [and \\[ paired] braces](https://osu.ppy.sh/home)", 1, expectedActions: LinkAction.External); + addMessageWithChecks("(Old link format with escaped (and \\( paired) parentheses)[https://osu.ppy.sh/home] and [[also a rogue wiki link]]", 2, expectedActions: new[] { LinkAction.External, LinkAction.External }); // note that there's 0 links here (they get removed if a channel is not found) addMessageWithChecks("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present)."); addMessageWithChecks("I am important!", 0, false, true); diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index e40bb05381..24d17612ee 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -14,7 +14,7 @@ namespace osu.Game.Online.Chat private static readonly Regex wiki_regex = new Regex(@"\[\[(?[^\]]+)\]\]"); // (test)[https://osu.ppy.sh/b/1234] -> test (https://osu.ppy.sh/b/1234) - private static readonly Regex old_link_regex = new Regex(@"\((?[^\)]*)\)\[(?[a-z]+://[^ ]+)\]"); + private static readonly Regex old_link_regex = new Regex(@"\((?(((?<=\\)[\(\)])|[^\(\)])*(((?\()(((?<=\\)[\(\)])|[^\(\)])*)+((?\))(((?<=\\)[\(\)])|[^\(\)])*)+)*(?(open)(?!)))\)\[(?[a-z]+://[^ ]+)\]"); // [https://osu.ppy.sh/b/1234 Beatmap [Hard] (poop)] -> Beatmap [hard] (poop) (https://osu.ppy.sh/b/1234) private static readonly Regex new_link_regex = new Regex(@"\[(?[a-z]+://[^ ]+) (?(((?<=\\)[\[\]])|[^\[\]])*(((?\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]"); @@ -194,7 +194,7 @@ namespace osu.Game.Online.Chat handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '[', ']' }); // handle the ()[] link format - handleMatches(old_link_regex, "{1}", "{2}", result, startIndex); + handleMatches(old_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '(', ')' }); // handle wiki links handleMatches(wiki_regex, "{1}", "https://osu.ppy.sh/wiki/{1}", result, startIndex); From 9ec16bc2b260fd35a7d0b2722432cb5636c6f4f2 Mon Sep 17 00:00:00 2001 From: Joehu Date: Tue, 3 Sep 2019 16:56:45 -0700 Subject: [PATCH 2565/2854] Add test for initial button hover --- .../Gameplay/TestSceneGameplayMenuOverlay.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs index 0d8a84fa51..ef7345bda7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs @@ -63,6 +63,7 @@ namespace osu.Game.Tests.Visual.Gameplay testKeyDownWrapping(); testHideResets(); + testInitialButtonHover(); testMouseSelectionAfterKeySelection(); testKeySelectionAfterMouseSelection(); @@ -159,6 +160,25 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected.Value)); } + /// + /// Tests that entering menu with cursor initially on button selects it. + /// + private void testInitialButtonHover() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + var firstButton = pauseOverlay.Buttons.First(); + + AddStep("Hover first button", () => InputManager.MoveMouseTo(firstButton)); + + AddStep("Hide overlay", () => pauseOverlay.Hide()); + AddStep("Show overlay", () => pauseOverlay.Show()); + + AddAssert("First button selected", () => firstButton.Selected.Value); + + AddStep("Hide overlay", () => pauseOverlay.Hide()); + } + /// /// Tests that hovering a button that was previously selected with the keyboard correctly selects the new button and deselects the previous button. /// From 5c10a228771ba0f3bb5459ff80b5d6f74a8e586f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 10:34:56 +0900 Subject: [PATCH 2566/2854] Update tests to use [Test] attributes --- .../Gameplay/TestSceneGameplayMenuOverlay.cs | 57 +++++++++---------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs index ef7345bda7..8b2fbe8aae 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs @@ -3,8 +3,8 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -16,7 +16,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay { - [Description("player pause/fail screens")] + [System.ComponentModel.Description("player pause/fail screens")] public class TestSceneGameplayMenuOverlay : ManualInputManagerTestScene { public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseOverlay) }; @@ -55,29 +55,13 @@ namespace osu.Game.Tests.Visual.Gameplay retryCount++; pauseOverlay.Retries = failOverlay.Retries = retryCount; }); - - testEnterWithoutSelection(); - testKeyUpFromInitial(); - testKeyDownFromInitial(); - testKeyUpWrapping(); - testKeyDownWrapping(); - - testHideResets(); - testInitialButtonHover(); - - testMouseSelectionAfterKeySelection(); - testKeySelectionAfterMouseSelection(); - - testMouseDeselectionResets(); - - testClickSelection(); - testEnterKeySelection(); } /// /// Tests that pressing enter after an overlay shows doesn't trigger an event because a selection hasn't occurred. /// - private void testEnterWithoutSelection() + [Test] + public void TestEnterWithoutSelection() { AddStep("Show overlay", () => pauseOverlay.Show()); @@ -90,7 +74,8 @@ namespace osu.Game.Tests.Visual.Gameplay /// /// Tests that pressing the up arrow from the initial state selects the last button. /// - private void testKeyUpFromInitial() + [Test] + public void TestKeyUpFromInitial() { AddStep("Show overlay", () => pauseOverlay.Show()); @@ -103,7 +88,8 @@ namespace osu.Game.Tests.Visual.Gameplay /// /// Tests that pressing the down arrow from the initial state selects the first button. /// - private void testKeyDownFromInitial() + [Test] + public void TestKeyDownFromInitial() { AddStep("Show overlay", () => pauseOverlay.Show()); @@ -116,7 +102,8 @@ namespace osu.Game.Tests.Visual.Gameplay /// /// Tests that pressing the up arrow repeatedly causes the selected button to wrap correctly. /// - private void testKeyUpWrapping() + [Test] + public void TestKeyUpWrapping() { AddStep("Show overlay", () => failOverlay.Show()); @@ -133,7 +120,8 @@ namespace osu.Game.Tests.Visual.Gameplay /// /// Tests that pressing the down arrow repeatedly causes the selected button to wrap correctly. /// - private void testKeyDownWrapping() + [Test] + public void TestKeyDownWrapping() { AddStep("Show overlay", () => failOverlay.Show()); @@ -150,7 +138,8 @@ namespace osu.Game.Tests.Visual.Gameplay /// /// Test that hiding the overlay after hovering a button will reset the overlay to the initial state with no buttons selected. /// - private void testHideResets() + [Test] + public void TestHideResets() { AddStep("Show overlay", () => failOverlay.Show()); @@ -163,7 +152,8 @@ namespace osu.Game.Tests.Visual.Gameplay /// /// Tests that entering menu with cursor initially on button selects it. /// - private void testInitialButtonHover() + [Test] + public void TestInitialButtonHover() { AddStep("Show overlay", () => pauseOverlay.Show()); @@ -182,7 +172,8 @@ namespace osu.Game.Tests.Visual.Gameplay /// /// Tests that hovering a button that was previously selected with the keyboard correctly selects the new button and deselects the previous button. /// - private void testMouseSelectionAfterKeySelection() + [Test] + public void TestMouseSelectionAfterKeySelection() { AddStep("Show overlay", () => pauseOverlay.Show()); @@ -199,7 +190,8 @@ namespace osu.Game.Tests.Visual.Gameplay /// /// Tests that pressing a key after selecting a button with a hover event correctly selects a new button and deselects the previous button. /// - private void testKeySelectionAfterMouseSelection() + [Test] + public void TestKeySelectionAfterMouseSelection() { AddStep("Show overlay", () => { @@ -220,7 +212,8 @@ namespace osu.Game.Tests.Visual.Gameplay /// /// Tests that deselecting with the mouse by losing hover will reset the overlay to the initial state. /// - private void testMouseDeselectionResets() + [Test] + public void TestMouseDeselectionResets() { AddStep("Show overlay", () => pauseOverlay.Show()); @@ -237,7 +230,8 @@ namespace osu.Game.Tests.Visual.Gameplay /// /// Tests that clicking on a button correctly causes a click event for that button. /// - private void testClickSelection() + [Test] + public void TestClickSelection() { AddStep("Show overlay", () => pauseOverlay.Show()); @@ -260,7 +254,8 @@ namespace osu.Game.Tests.Visual.Gameplay /// /// Tests that pressing the enter key with a button selected correctly causes a click event for that button. /// - private void testEnterKeySelection() + [Test] + public void TestEnterKeySelection() { AddStep("Show overlay", () => pauseOverlay.Show()); From 40c61894effd9afcc1dc2db5393f4b37058c1076 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 10:44:24 +0900 Subject: [PATCH 2567/2854] Update some case sensitive resources lookups in-line with resources --- osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs | 2 +- osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index c89cd95f36..e96c7d8f92 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Catch.Tests { switch (component.LookupName) { - case "Gameplay/Catch/fruit-catcher-idle": + case "Gameplay/catch/fruit-catcher-idle": return new CatcherCustomSkin(); } diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index 9766da9a24..5234ae1f69 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -132,10 +132,10 @@ namespace osu.Game.Rulesets.Taiko.UI [BackgroundDependencyLoader] private void load(TextureStore textures, OsuColour colours) { - rim.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-outer"); - rimHit.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-outer-hit"); - centre.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-inner"); - centreHit.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-inner-hit"); + rim.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-outer"); + rimHit.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-outer-hit"); + centre.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-inner"); + centreHit.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-inner-hit"); rimHit.Colour = colours.Blue; centreHit.Colour = colours.Pink; From 7cbcc7b9069178bc07c38bdf8802fbf3eaf7333b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 11:36:09 +0900 Subject: [PATCH 2568/2854] Further test refactors --- .../Gameplay/TestSceneGameplayMenuOverlay.cs | 121 ++++++++---------- 1 file changed, 54 insertions(+), 67 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs index 8b2fbe8aae..cc275009ba 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Logging; +using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Screens.Play; using osuTK; @@ -29,32 +30,43 @@ namespace osu.Game.Tests.Visual.Gameplay [BackgroundDependencyLoader] private void load(OsuGameBase game) { - Child = globalActionContainer = new GlobalActionContainer(game) - { - Children = new Drawable[] - { - pauseOverlay = new PauseOverlay - { - OnResume = () => Logger.Log(@"Resume"), - OnRetry = () => Logger.Log(@"Retry"), - OnQuit = () => Logger.Log(@"Quit"), - }, - failOverlay = new FailOverlay + Child = globalActionContainer = new GlobalActionContainer(game); + } - { - OnRetry = () => Logger.Log(@"Retry"), - OnQuit = () => Logger.Log(@"Quit"), - } + [SetUp] + public void SetUp() => Schedule(() => + { + globalActionContainer.Children = new Drawable[] + { + pauseOverlay = new PauseOverlay + { + OnResume = () => Logger.Log(@"Resume"), + OnRetry = () => Logger.Log(@"Retry"), + OnQuit = () => Logger.Log(@"Quit"), + }, + failOverlay = new FailOverlay + + { + OnRetry = () => Logger.Log(@"Retry"), + OnQuit = () => Logger.Log(@"Quit"), } }; + InputManager.MoveMouseTo(Vector2.Zero); + }); + + [Test] + public void TestAdjustRetryCount() + { + showOverlay(); + var retryCount = 0; - AddStep("Add retry", () => + AddRepeatStep("Add retry", () => { retryCount++; pauseOverlay.Retries = failOverlay.Retries = retryCount; - }); + }, 10); } /// @@ -63,12 +75,10 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestEnterWithoutSelection() { - AddStep("Show overlay", () => pauseOverlay.Show()); + showOverlay(); AddStep("Press select", () => press(GlobalAction.Select)); AddAssert("Overlay still open", () => pauseOverlay.State.Value == Visibility.Visible); - - AddStep("Hide overlay", () => pauseOverlay.Hide()); } /// @@ -77,12 +87,10 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestKeyUpFromInitial() { - AddStep("Show overlay", () => pauseOverlay.Show()); + showOverlay(); AddStep("Up arrow", () => press(Key.Up)); AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected.Value); - - AddStep("Hide overlay", () => pauseOverlay.Hide()); } /// @@ -91,12 +99,10 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestKeyDownFromInitial() { - AddStep("Show overlay", () => pauseOverlay.Show()); + showOverlay(); AddStep("Down arrow", () => press(Key.Down)); - AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected.Value); - - AddStep("Hide overlay", () => pauseOverlay.Hide()); + AddAssert("First button selected", () => getButton(0).Selected.Value); } /// @@ -113,8 +119,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("First button selected", () => failOverlay.Buttons.First().Selected.Value); AddStep("Up arrow", () => press(Key.Up)); AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected.Value); - - AddStep("Hide overlay", () => failOverlay.Hide()); } /// @@ -131,8 +135,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected.Value); AddStep("Down arrow", () => press(Key.Down)); AddAssert("First button selected", () => failOverlay.Buttons.First().Selected.Value); - - AddStep("Hide overlay", () => failOverlay.Hide()); } /// @@ -155,18 +157,14 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestInitialButtonHover() { - AddStep("Show overlay", () => pauseOverlay.Show()); + showOverlay(); - var firstButton = pauseOverlay.Buttons.First(); - - AddStep("Hover first button", () => InputManager.MoveMouseTo(firstButton)); + AddStep("Hover first button", () => InputManager.MoveMouseTo(getButton(0))); AddStep("Hide overlay", () => pauseOverlay.Hide()); - AddStep("Show overlay", () => pauseOverlay.Show()); + showOverlay(); - AddAssert("First button selected", () => firstButton.Selected.Value); - - AddStep("Hide overlay", () => pauseOverlay.Hide()); + AddAssert("First button selected", () => getButton(0).Selected.Value); } /// @@ -175,16 +173,12 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestMouseSelectionAfterKeySelection() { - AddStep("Show overlay", () => pauseOverlay.Show()); - - var secondButton = pauseOverlay.Buttons.Skip(1).First(); + showOverlay(); AddStep("Down arrow", () => press(Key.Down)); - AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton)); - AddAssert("First button not selected", () => !pauseOverlay.Buttons.First().Selected.Value); - AddAssert("Second button selected", () => secondButton.Selected.Value); - - AddStep("Hide overlay", () => pauseOverlay.Hide()); + AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1))); + AddAssert("First button not selected", () => !getButton(0).Selected.Value); + AddAssert("Second button selected", () => getButton(1).Selected.Value); } /// @@ -196,17 +190,12 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Show overlay", () => { pauseOverlay.Show(); - InputManager.MoveMouseTo(Vector2.Zero); }); - var secondButton = pauseOverlay.Buttons.Skip(1).First(); - - AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton)); + AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1))); AddStep("Up arrow", () => press(Key.Up)); - AddAssert("Second button not selected", () => !secondButton.Selected.Value); - AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected.Value); - - AddStep("Hide overlay", () => pauseOverlay.Hide()); + AddAssert("Second button not selected", () => !getButton(1).Selected.Value); + AddAssert("First button selected", () => getButton(0).Selected.Value); } /// @@ -215,16 +204,12 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestMouseDeselectionResets() { - AddStep("Show overlay", () => pauseOverlay.Show()); + showOverlay(); - var secondButton = pauseOverlay.Buttons.Skip(1).First(); - - AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton)); + AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1))); AddStep("Unhover second button", () => InputManager.MoveMouseTo(Vector2.Zero)); AddStep("Down arrow", () => press(Key.Down)); - AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected.Value); // Initial state condition - - AddStep("Hide overlay", () => pauseOverlay.Hide()); + AddAssert("First button selected", () => getButton(0).Selected.Value); // Initial state condition } /// @@ -233,9 +218,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestClickSelection() { - AddStep("Show overlay", () => pauseOverlay.Show()); - - var retryButton = pauseOverlay.Buttons.Skip(1).First(); + showOverlay(); bool triggered = false; AddStep("Click retry button", () => @@ -243,7 +226,7 @@ namespace osu.Game.Tests.Visual.Gameplay var lastAction = pauseOverlay.OnRetry; pauseOverlay.OnRetry = () => triggered = true; - retryButton.Click(); + getButton(1).Click(); pauseOverlay.OnRetry = lastAction; }); @@ -257,7 +240,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestEnterKeySelection() { - AddStep("Show overlay", () => pauseOverlay.Show()); + showOverlay(); AddStep("Select second button", () => { @@ -287,6 +270,10 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("Overlay is closed", () => pauseOverlay.State.Value == Visibility.Hidden); } + private void showOverlay() => AddStep("Show overlay", () => pauseOverlay.Show()); + + private DialogButton getButton(int index) => pauseOverlay.Buttons.Skip(index).First(); + private void press(Key key) { InputManager.PressKey(key); From 4c563232d627b17a6f1c31350b156673c7befb7d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 11:37:02 +0900 Subject: [PATCH 2569/2854] HoverClickSounds should handle click event instead of MouseUp --- osu.Game/Graphics/UserInterface/HoverClickSounds.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs index 1fb73efa65..4f678b7218 100644 --- a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs @@ -34,12 +34,12 @@ namespace osu.Game.Graphics.UserInterface this.buttons = buttons ?? new[] { MouseButton.Left }; } - protected override bool OnMouseUp(MouseUpEvent e) + protected override bool OnClick(ClickEvent e) { if (buttons.Contains(e.Button) && Contains(e.ScreenSpaceMousePosition)) sampleClick?.Play(); - return base.OnMouseUp(e); + return base.OnClick(e); } [BackgroundDependencyLoader] From 5efd455ce4821e48919eb462531325db12d54f65 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Sep 2019 12:47:10 +0900 Subject: [PATCH 2570/2854] Fix taiko sample namespace --- .../Objects/Drawables/DrawableTaikoHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 5424ccb4de..423f65b2d3 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables // Normal and clap samples are handled by the drum protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP); - protected override string SampleNamespace => "Taiko"; + protected override string SampleNamespace => "taiko"; protected virtual TaikoPiece CreateMainPiece() => new CirclePiece(); From 024aa4dd7b3ea19e04cd59cf8e66a26f74a62cc2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Sep 2019 13:05:05 +0900 Subject: [PATCH 2571/2854] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 96706f2bdc..90d1854c39 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -60,7 +60,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ca69bb2295..7d106f0484 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,7 +14,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 86a2a40940..8390a2229b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -117,7 +117,7 @@ - + From 9edfe6800f7a7157b3a0b8902a2a9ad57b599814 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2019 04:20:46 +0000 Subject: [PATCH 2572/2854] Bump ppy.osu.Game.Resources from 2019.903.1 to 2019.904.0 Bumps [ppy.osu.Game.Resources](https://github.com/ppy/osu-resources) from 2019.903.1 to 2019.904.0. - [Release notes](https://github.com/ppy/osu-resources/releases) - [Commits](https://github.com/ppy/osu-resources/compare/2019.903.1...2019.904.0) Signed-off-by: dependabot-preview[bot] --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 96706f2bdc..90d1854c39 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -60,7 +60,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ca69bb2295..7d106f0484 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,7 +14,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 86a2a40940..8390a2229b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -117,7 +117,7 @@ - + From 04c2c33c64bbc908324510672c822681bf40667b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 13:29:55 +0900 Subject: [PATCH 2573/2854] Allow LegacySkin to be constructed with all nulls --- osu.Game/Skinning/LegacySkin.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 5f0afae075..7bdb980eaf 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -30,14 +30,14 @@ namespace osu.Game.Skinning protected LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager, string filename) : base(skin) { - Stream stream = storage.GetStream(filename); + Stream stream = storage?.GetStream(filename); if (stream != null) using (StreamReader reader = new StreamReader(stream)) Configuration = new LegacySkinDecoder().Decode(reader); else Configuration = new DefaultSkinConfiguration(); - Samples = audioManager.GetSampleStore(storage); + Samples = audioManager?.GetSampleStore(storage); Textures = new TextureStore(new TextureLoaderStore(storage)); } @@ -72,6 +72,10 @@ namespace osu.Game.Skinning { if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString(), out var val)) { + // special case for handling skins which use 1 or 0 to signify a boolean state. + if (typeof(TValue) == typeof(bool)) + val = val == "1" ? "true" : "false"; + var bindable = new Bindable(); bindable.Parse(val); return bindable; From f655cd451681e8fc64e0f6c8faebcdc1c0ab08ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 13:30:46 +0900 Subject: [PATCH 2574/2854] Fix parsing of null configuration elements --- osu.Game/Skinning/LegacySkin.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 7bdb980eaf..94e2a49908 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -77,7 +77,8 @@ namespace osu.Game.Skinning val = val == "1" ? "true" : "false"; var bindable = new Bindable(); - bindable.Parse(val); + if (val != null) + bindable.Parse(val); return bindable; } } From fb3d050209bb8b72c02435d5225b8f7318afd98f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 13:36:50 +0900 Subject: [PATCH 2575/2854] Add comprehensive configuration lookup tests --- .../Skins/SkinConfigurationLookupTest.cs | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 osu.Game.Tests/Skins/SkinConfigurationLookupTest.cs diff --git a/osu.Game.Tests/Skins/SkinConfigurationLookupTest.cs b/osu.Game.Tests/Skins/SkinConfigurationLookupTest.cs new file mode 100644 index 0000000000..1344d20d9f --- /dev/null +++ b/osu.Game.Tests/Skins/SkinConfigurationLookupTest.cs @@ -0,0 +1,137 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Textures; +using osu.Game.Audio; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; +using osuTK.Graphics; + +namespace osu.Game.Tests.Skins +{ + [TestFixture] + public class TestSceneSkinConfigurationLookup : OsuTestScene + { + private LegacySkin source1; + private LegacySkin source2; + private SkinRequester requester; + + [SetUp] + public void SetUp() => Schedule(() => + { + Add(new SkinProvidingContainer(source1 = new SkinSource()) + .WithChild(new SkinProvidingContainer(source2 = new SkinSource()) + .WithChild(requester = new SkinRequester()))); + }); + + [Test] + public void TestBasicLookup() + { + AddStep("Add config values", () => + { + source1.Configuration.ConfigDictionary["Lookup"] = "source1"; + source2.Configuration.ConfigDictionary["Lookup"] = "source2"; + }); + + AddAssert("Check lookup finds source2", () => requester.GetConfig("Lookup")?.Value == "source2"); + } + + [Test] + public void TestParsingLookup() + { + AddStep("Add config values", () => + { + source1.Configuration.ConfigDictionary["FloatTest"] = "1.1"; + source2.Configuration.ConfigDictionary["BoolTest"] = "1"; + }); + + AddAssert("Check float parse lookup", () => requester.GetConfig("FloatTest")?.Value == 1.1f); + AddAssert("Check bool parse lookup", () => requester.GetConfig("BoolTest")?.Value == true); + } + + [Test] + public void TestEnumLookup() + { + AddStep("Add config values", () => { source1.Configuration.ConfigDictionary["Test"] = "Test2"; }); + + AddAssert("Check float parse lookup", () => requester.GetConfig(LookupType.Test)?.Value == ValueType.Test2); + } + + [Test] + public void TestLookupFailure() + { + AddAssert("Check lookup failure", () => requester.GetConfig("Lookup") == null); + } + + [Test] + public void TestLookupNull() + { + AddStep("Add config values", () => { source1.Configuration.ConfigDictionary["Lookup"] = null; }); + + AddAssert("Check lookup null", () => + { + var bindable = requester.GetConfig("Lookup"); + return bindable != null && bindable.Value == null; + }); + } + + [Test] + public void TestColourLookup() + { + AddStep("Add config colour", () => { source1.Configuration.CustomColours["Lookup"] = Color4.Red; }); + AddAssert("Check colour lookup", () => requester.GetConfig(new SkinCustomColourLookup("Lookup"))?.Value == Color4.Red); + } + + [Test] + public void TestGlobalLookup() + { + AddAssert("Check combo colours", () => requester.GetConfig>(GlobalSkinConfiguration.ComboColours)?.Value?.Count > 0); + } + + public enum LookupType + { + Test + } + + public enum ValueType + { + Test1, + Test2, + Test3 + } + + public class SkinSource : LegacySkin + { + public SkinSource() + : base(new SkinInfo(), null, null, string.Empty) + { + } + } + + public class SkinRequester : Drawable, ISkin + { + private ISkinSource skin; + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + this.skin = skin; + } + + public Drawable GetDrawableComponent(ISkinComponent component) => skin.GetDrawableComponent(component); + + public Texture GetTexture(string componentName) => skin.GetTexture(componentName); + + public SampleChannel GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo); + + public IBindable GetConfig(TLookup lookup) => skin.GetConfig(lookup); + } + } +} From 8d48cc3533ef470fba9045e8206763caac0a7820 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 13:40:36 +0900 Subject: [PATCH 2576/2854] Fix filename --- ...igurationLookupTest.cs => TestSceneSkinConfigurationLookup.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename osu.Game.Tests/Skins/{SkinConfigurationLookupTest.cs => TestSceneSkinConfigurationLookup.cs} (100%) diff --git a/osu.Game.Tests/Skins/SkinConfigurationLookupTest.cs b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs similarity index 100% rename from osu.Game.Tests/Skins/SkinConfigurationLookupTest.cs rename to osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs From 69b9d70a35c2c1f42214d16f083b6e73f43ddc4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 15:56:18 +0900 Subject: [PATCH 2577/2854] Add minimal configuration to support nuget package creation --- osu.Game/osu.Game.csproj | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7d106f0484..3147ca749f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -6,6 +6,17 @@ AnyCPU true + + osu! + ppy.osu.Game + ppy Pty Ltd + https://github.com/ppy/osu/blob/master/LICENCE.md + https://github.com/ppy/osu + https://github.com/ppy/osu + Automated release. + Copyright (c) 2019 ppy Pty Ltd + osu game + @@ -15,7 +26,7 @@ - + From 8ea82123e4afc36ac897735d24743a17b259d25b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 15:59:09 +0900 Subject: [PATCH 2578/2854] Fix nullref on test disposal --- osu.Game/Skinning/LegacySkin.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 94e2a49908..cd2ad2d61c 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; @@ -18,8 +19,10 @@ namespace osu.Game.Skinning { public class LegacySkin : Skin { + [CanBeNull] protected TextureStore Textures; + [CanBeNull] protected IResourceStore Samples; public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) @@ -37,8 +40,11 @@ namespace osu.Game.Skinning else Configuration = new DefaultSkinConfiguration(); - Samples = audioManager?.GetSampleStore(storage); - Textures = new TextureStore(new TextureLoaderStore(storage)); + if (storage != null) + { + Samples = audioManager?.GetSampleStore(storage); + Textures = new TextureStore(new TextureLoaderStore(storage)); + } } protected override void Dispose(bool isDisposing) @@ -125,12 +131,12 @@ namespace osu.Game.Skinning componentName = getFallbackName(componentName); float ratio = 2; - var texture = Textures.Get($"{componentName}@2x"); + var texture = Textures?.Get($"{componentName}@2x"); if (texture == null) { ratio = 1; - texture = Textures.Get(componentName); + texture = Textures?.Get(componentName); } if (texture != null) @@ -143,7 +149,7 @@ namespace osu.Game.Skinning { foreach (var lookup in sampleInfo.LookupNames) { - var sample = Samples.Get(getFallbackName(lookup)); + var sample = Samples?.Get(getFallbackName(lookup)); if (sample != null) return sample; @@ -151,7 +157,7 @@ namespace osu.Game.Skinning if (sampleInfo is HitSampleInfo hsi) // Try fallback to non-bank samples. - return Samples.Get(hsi.Name); + return Samples?.Get(hsi.Name); return null; } From 07f662071d5c7dc31424e692fb9e438587cff662 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Sep 2019 18:14:55 +0900 Subject: [PATCH 2579/2854] Remove judgementOccurred --- .../Objects/Drawables/DrawableHitObject.cs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 1a224b2cea..f7efa625a5 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -76,8 +76,6 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public JudgementResult Result { get; private set; } - private bool judgementOccurred; - public override bool RemoveWhenNotAlive => false; public override bool RemoveCompletedTransforms => false; protected override bool RequiresChildrenUpdate => true; @@ -342,8 +340,6 @@ namespace osu.Game.Rulesets.Objects.Drawables if (!Result.HasResult) throw new InvalidOperationException($"{GetType().ReadableName()} applied a {nameof(JudgementResult)} but did not update {nameof(JudgementResult.Type)}."); - judgementOccurred = true; - // Ensure that the judgement is given a valid time offset, because this may not get set by the caller var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; Result.TimeOffset = Time.Current - endTime; @@ -376,21 +372,13 @@ namespace osu.Game.Rulesets.Objects.Drawables if (Time.Elapsed < 0) return false; - judgementOccurred = false; - - if (AllJudged) + if (Judged) return false; - foreach (var d in NestedHitObjects) - judgementOccurred |= d.UpdateResult(userTriggered); - - if (judgementOccurred || Judged) - return judgementOccurred; - var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; CheckForResult(userTriggered, Time.Current - endTime); - return judgementOccurred; + return Judged; } /// From a87a1e60314352fc3bd241935d5116f9e07cc075 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Sep 2019 19:38:12 +0900 Subject: [PATCH 2580/2854] Don't redraw certain buffered containers on scale change --- osu.Game/Graphics/Backgrounds/Background.cs | 3 ++- osu.Game/Overlays/NowPlayingOverlay.cs | 5 ++++- osu.Game/Screens/Play/SquareGraph.cs | 1 + osu.Game/Screens/Select/BeatmapInfoWedge.cs | 2 ++ .../Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 1 + 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index d13475189d..0f923c3a28 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -57,8 +57,9 @@ namespace osu.Game.Graphics.Backgrounds AddInternal(bufferedContainer = new BufferedContainer { - CacheDrawnFrameBuffer = true, RelativeSizeAxes = Axes.Both, + CacheDrawnFrameBuffer = true, + RedrawOnScale = false, Child = Sprite }); } diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index a3243a655e..c8361c6114 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -346,10 +346,13 @@ namespace osu.Game.Overlays public Background(WorkingBeatmap beatmap = null) { this.beatmap = beatmap; - CacheDrawnFrameBuffer = true; + Depth = float.MaxValue; RelativeSizeAxes = Axes.Both; + CacheDrawnFrameBuffer = true; + RedrawOnScale = false; + Children = new Drawable[] { sprite = new Sprite diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 9c56725c4e..05f6128ac2 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -103,6 +103,7 @@ namespace osu.Game.Screens.Play var newColumns = new BufferedContainer { CacheDrawnFrameBuffer = true, + RedrawOnScale = false, RelativeSizeAxes = Axes.Both, }; diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 5f6307e3b4..65ecd7b812 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -154,6 +154,8 @@ namespace osu.Game.Screens.Select var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); CacheDrawnFrameBuffer = true; + RedrawOnScale = false; + RelativeSizeAxes = Axes.Both; titleBinding = localisation.GetLocalisedString(new LocalisedString((metadata.TitleUnicode, metadata.Title))); diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 97b6a78804..699e01bca7 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -146,6 +146,7 @@ namespace osu.Game.Screens.Select.Carousel public PanelBackground(WorkingBeatmap working) { CacheDrawnFrameBuffer = true; + RedrawOnScale = false; Children = new Drawable[] { From b80a8296cd591db9dd34b8746fdbb85570694be2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 20:27:27 +0900 Subject: [PATCH 2581/2854] Fix unavailable rulesets crashing at song select --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 2 +- osu.Game/Rulesets/RulesetInfo.cs | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 81f517dd86..8014631eca 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -84,7 +84,7 @@ namespace osu.Game.Beatmaps.Drawables Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) - Icon = ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } + Icon = ruleset?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } } }; } diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index d9cff86265..c982ef7be1 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; using Newtonsoft.Json; namespace osu.Game.Rulesets @@ -20,7 +21,13 @@ namespace osu.Game.Rulesets [JsonIgnore] public bool Available { get; set; } - public virtual Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this); + [CanBeNull] + public virtual Ruleset CreateInstance() + { + if (!Available) return null; + + return (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this); + } public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo; From c59a2bf9bb6bce70906c6fd3b3e99417ff314249 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 20:28:04 +0900 Subject: [PATCH 2582/2854] Fix tests crashing if a ruleset doesn't provide a NoFail mod --- osu.Game/Tests/Visual/PlayerTestScene.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index ccd996098c..2c5a51ca02 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -50,7 +50,11 @@ namespace osu.Game.Tests.Visual Beatmap.Value = CreateWorkingBeatmap(beatmap); if (!AllowFail) - Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) }; + { + var noFailMod = ruleset.GetAllMods().FirstOrDefault(m => m is ModNoFail); + if (noFailMod != null) + Mods.Value = new[] { noFailMod }; + } if (Autoplay) { From 6197c7fd31d3edcbae5a07c1b06d7157b3eb25b6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 20:28:21 +0900 Subject: [PATCH 2583/2854] Add automatic resource mapping for rulesets to their own dll --- osu.Game/Rulesets/Ruleset.cs | 3 +++ osu.Game/Rulesets/UI/DrawableRuleset.cs | 20 ++++++++++++++++++++ osu.Game/Skinning/SkinnableSound.cs | 14 ++++++++------ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index b63292757d..197c089f71 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; +using osu.Framework.IO.Stores; using osu.Game.Beatmaps; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Edit; @@ -83,6 +84,8 @@ namespace osu.Game.Rulesets public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.QuestionCircle }; + public virtual IResourceStore CreateReourceStore() => new NamespacedResourceStore(new DllResourceStore(GetType().Assembly.Location), @"Resources"); + public abstract string Description { get; } public virtual RulesetSettingsSubsection CreateSettings() => null; diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index a32407d180..562b2c4667 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -14,10 +14,14 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using JetBrains.Annotations; +using osu.Framework.Audio; +using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.Input.Events; +using osu.Framework.IO.Stores; using osu.Game.Configuration; using osu.Game.Graphics.Cursor; using osu.Game.Input.Handlers; @@ -51,6 +55,10 @@ namespace osu.Game.Rulesets.UI private readonly Lazy playfield; + private TextureStore textureStore; + + private ISampleStore sampleStore; + /// /// The playfield. /// @@ -142,6 +150,18 @@ namespace osu.Game.Rulesets.UI { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + var resources = Ruleset.CreateReourceStore(); + + if (resources != null) + { + textureStore = new TextureStore(new TextureLoaderStore(new NamespacedResourceStore(resources, "Textures"))); + textureStore.AddStore(dependencies.Get()); + dependencies.Cache(textureStore); + + sampleStore = dependencies.Get().GetSampleStore(new NamespacedResourceStore(resources, "Samples")); + dependencies.CacheAs(sampleStore); + } + onScreenDisplay = dependencies.Get(); Config = dependencies.Get().GetConfigFor(Ruleset); diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 3d0219ed93..bdf8be773b 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Audio; @@ -20,7 +21,7 @@ namespace osu.Game.Skinning private SampleChannel[] channels; - private AudioManager audio; + private ISampleStore samples; public SkinnableSound(IEnumerable hitSamples) { @@ -33,9 +34,9 @@ namespace osu.Game.Skinning } [BackgroundDependencyLoader] - private void load(AudioManager audio) + private void load(ISampleStore samples) { - this.audio = audio; + this.samples = samples; } private bool looping; @@ -81,7 +82,7 @@ namespace osu.Game.Skinning if (ch == null && allowFallback) foreach (var lookup in s.LookupNames) - if ((ch = audio.Samples.Get($"Gameplay/{lookup}")) != null) + if ((ch = samples.Get($"Gameplay/{lookup}")) != null) break; if (ch != null) @@ -102,8 +103,9 @@ namespace osu.Game.Skinning { base.Dispose(isDisposing); - foreach (var c in channels) - c.Dispose(); + if (channels != null) + foreach (var c in channels) + c.Dispose(); } } } From 8e8f33ec7b5fb0a24f279692c76ba3d214997bc2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 21:35:59 +0900 Subject: [PATCH 2584/2854] Remove null hinting for now --- osu.Game/Rulesets/RulesetInfo.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index c982ef7be1..6a69fd8dd0 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using JetBrains.Annotations; using Newtonsoft.Json; namespace osu.Game.Rulesets @@ -21,7 +20,6 @@ namespace osu.Game.Rulesets [JsonIgnore] public bool Available { get; set; } - [CanBeNull] public virtual Ruleset CreateInstance() { if (!Available) return null; From f9fa5988e69a679a7a344baa0a1855703d7f7caf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 23:16:56 +0900 Subject: [PATCH 2585/2854] Add basic nuget deploy support --- appveyor.yml | 2 -- appveyor_deploy.yml | 10 ++++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 appveyor_deploy.yml diff --git a/appveyor.yml b/appveyor.yml index 4dcaa7b45e..be1727e7d7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,5 @@ clone_depth: 1 version: '{branch}-{build}' image: Previous Visual Studio 2017 test: off -install: - - cmd: git submodule update --init --recursive --depth=5 build_script: - cmd: PowerShell -Version 2.0 .\build.ps1 diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml new file mode 100644 index 0000000000..d36298175b --- /dev/null +++ b/appveyor_deploy.yml @@ -0,0 +1,10 @@ +clone_depth: 1 +version: '{build}' +image: Previous Visual Studio 2017 +test: off +skip_non_tags: true +build_script: + - cmd: PowerShell -Version 2.0 .\build.ps1 +deploy: + - provider: Environment + name: nuget From 50de4d1a3a4569da2532c52b3b74371f28f46ae7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 01:11:53 +0900 Subject: [PATCH 2586/2854] Remove PrivateAssets changes for now --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3147ca749f..f2a605e7a7 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + From afac512a1bfd09d115fc60b9dcfc5d8c0b7e776e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 02:12:17 +0900 Subject: [PATCH 2587/2854] Fix databased config save performance Adds proper save debounce logic. Closes #5991. --- .../Configuration/DatabasedConfigManager.cs | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs index d5cdd7e4bc..6aa89cdd69 100644 --- a/osu.Game/Configuration/DatabasedConfigManager.cs +++ b/osu.Game/Configuration/DatabasedConfigManager.cs @@ -16,11 +16,11 @@ namespace osu.Game.Configuration private readonly int? variant; - private readonly List databasedSettings; + private List databasedSettings; private readonly RulesetInfo ruleset; - private readonly bool legacySettingsExist; + private bool legacySettingsExist; protected DatabasedConfigManager(SettingsStore settings, RulesetInfo ruleset = null, int? variant = null) { @@ -28,21 +28,34 @@ namespace osu.Game.Configuration this.ruleset = ruleset; this.variant = variant; - databasedSettings = settings.Query(ruleset?.ID, variant); - legacySettingsExist = databasedSettings.Any(s => int.TryParse(s.Key, out var _)); + Load(); InitialiseDefaults(); } protected override void PerformLoad() { + databasedSettings = settings.Query(ruleset?.ID, variant); + legacySettingsExist = databasedSettings.Any(s => int.TryParse(s.Key, out var _)); } protected override bool PerformSave() { + lock (dirtySettings) + { + if (dirtySettings.Count > 0) + { + foreach (var setting in dirtySettings) + settings.Update(setting); + dirtySettings.Clear(); + } + } + return true; } + private readonly List dirtySettings = new List(); + protected override void AddBindable(T lookup, Bindable bindable) { base.AddBindable(lookup, bindable); @@ -80,7 +93,9 @@ namespace osu.Game.Configuration bindable.ValueChanged += b => { setting.Value = b.NewValue; - settings.Update(setting); + lock (dirtySettings) + if (!dirtySettings.Contains(setting)) + dirtySettings.Add(setting); }; } } From a1c580f27ecae7f58bc6fbf6b58de8b6cef9b885 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 5 Sep 2019 05:56:21 +0300 Subject: [PATCH 2588/2854] Create "none selected" placeholder state --- osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs | 1 + osu.Game/Online/Leaderboards/Leaderboard.cs | 4 ++++ osu.Game/Online/Leaderboards/PlaceholderState.cs | 1 + .../Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 8 +++++++- 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs index 8e358a77db..186f27a8b2 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs @@ -42,6 +42,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter)); AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn)); AddStep(@"Unavailable", () => leaderboard.SetRetrievalState(PlaceholderState.Unavailable)); + AddStep(@"None selected", () => leaderboard.SetRetrievalState(PlaceholderState.NoneSelected)); foreach (BeatmapSetOnlineStatus status in Enum.GetValues(typeof(BeatmapSetOnlineStatus))) AddStep($"{status} beatmap", () => showBeatmapWithStatus(status)); } diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 98f15599fc..147556b78b 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -133,6 +133,10 @@ namespace osu.Game.Online.Leaderboards }); break; + case PlaceholderState.NoneSelected: + replacePlaceholder(new MessagePlaceholder(@"Please select a beatmap!")); + break; + case PlaceholderState.Unavailable: replacePlaceholder(new MessagePlaceholder(@"Leaderboards are not available for this beatmap!")); break; diff --git a/osu.Game/Online/Leaderboards/PlaceholderState.cs b/osu.Game/Online/Leaderboards/PlaceholderState.cs index 930e1df484..297241fa73 100644 --- a/osu.Game/Online/Leaderboards/PlaceholderState.cs +++ b/osu.Game/Online/Leaderboards/PlaceholderState.cs @@ -9,6 +9,7 @@ namespace osu.Game.Online.Leaderboards Retrieving, NetworkFailure, Unavailable, + NoneSelected, NoScores, NotLoggedIn, NotSupporter, diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index cb45c00f66..33f040755e 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -83,6 +83,12 @@ namespace osu.Game.Screens.Select.Leaderboards protected override APIRequest FetchScores(Action> scoresCallback) { + if (Beatmap == null) + { + PlaceholderState = PlaceholderState.NoneSelected; + return null; + } + if (Scope == BeatmapLeaderboardScope.Local) { var scores = scoreManager @@ -113,7 +119,7 @@ namespace osu.Game.Screens.Select.Leaderboards return null; } - if (Beatmap?.OnlineBeatmapID == null || Beatmap?.Status <= BeatmapSetOnlineStatus.Pending) + if (Beatmap.OnlineBeatmapID == null || Beatmap?.Status <= BeatmapSetOnlineStatus.Pending) { PlaceholderState = PlaceholderState.Unavailable; return null; From 1b0123a60cb22e89f009b2befbc024960dee706f Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 5 Sep 2019 05:56:52 +0300 Subject: [PATCH 2589/2854] Set beatmap of leaderboard to null if NoBeatmapsAvailable is selected --- osu.Game/Screens/Select/BeatmapDetailArea.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs index b66a2ffe0f..bf8fc8cf07 100644 --- a/osu.Game/Screens/Select/BeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs @@ -27,8 +27,8 @@ namespace osu.Game.Screens.Select set { beatmap = value; - Leaderboard.Beatmap = beatmap?.BeatmapInfo; Details.Beatmap = beatmap?.BeatmapInfo; + Leaderboard.Beatmap = beatmap is NoBeatmapsAvailableWorkingBeatmap ? null : beatmap?.BeatmapInfo; } } From d40129aabe9a8947add0a28b6a51fa85b7017365 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Sep 2019 13:36:37 +0900 Subject: [PATCH 2590/2854] Remove unnecessary count check --- osu.Game/Configuration/DatabasedConfigManager.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs index 6aa89cdd69..0046f4fa7f 100644 --- a/osu.Game/Configuration/DatabasedConfigManager.cs +++ b/osu.Game/Configuration/DatabasedConfigManager.cs @@ -43,12 +43,9 @@ namespace osu.Game.Configuration { lock (dirtySettings) { - if (dirtySettings.Count > 0) - { - foreach (var setting in dirtySettings) - settings.Update(setting); - dirtySettings.Clear(); - } + foreach (var setting in dirtySettings) + settings.Update(setting); + dirtySettings.Clear(); } return true; From 070a005294668614deaf2779450eed7b1e90051e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Sep 2019 13:37:11 +0900 Subject: [PATCH 2591/2854] Add braces to lock() Personal preference, I want to be sure that everything is wrapped correctly. --- osu.Game/Configuration/DatabasedConfigManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs index 0046f4fa7f..02382cfd2b 100644 --- a/osu.Game/Configuration/DatabasedConfigManager.cs +++ b/osu.Game/Configuration/DatabasedConfigManager.cs @@ -90,9 +90,12 @@ namespace osu.Game.Configuration bindable.ValueChanged += b => { setting.Value = b.NewValue; + lock (dirtySettings) + { if (!dirtySettings.Contains(setting)) dirtySettings.Add(setting); + } }; } } From 2e6af84ca839d5e22f6fdab8fe790c0fd80894c4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Sep 2019 14:39:02 +0900 Subject: [PATCH 2592/2854] Don't redraw leaderboard scores --- osu.Game/Graphics/Sprites/GlowingSpriteText.cs | 9 ++++++++- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs index 24816deeb5..892b27da75 100644 --- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs @@ -12,6 +12,7 @@ namespace osu.Game.Graphics.Sprites public class GlowingSpriteText : Container, IHasText { private readonly OsuSpriteText spriteText, blurredText; + private readonly BufferedContainer buffer; public string Text { @@ -43,13 +44,19 @@ namespace osu.Game.Graphics.Sprites set => blurredText.Colour = value; } + public bool RedrawOnScale + { + get => buffer.RedrawOnScale; + set => buffer.RedrawOnScale = value; + } + public GlowingSpriteText() { AutoSizeAxes = Axes.Both; Children = new Drawable[] { - new BufferedContainer + buffer = new BufferedContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 008f8208eb..e29748060e 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -193,6 +193,7 @@ namespace osu.Game.Online.Leaderboards GlowColour = OsuColour.FromHex(@"83ccfa"), Text = score.TotalScore.ToString(@"N0"), Font = OsuFont.Numeric.With(size: 23), + RedrawOnScale = false, }, RankContainer = new Container { @@ -338,6 +339,7 @@ namespace osu.Game.Online.Leaderboards GlowColour = OsuColour.FromHex(@"83ccfa"), Text = statistic.Value, Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold), + RedrawOnScale = false }, }, }; From 99579255ade422b7985b80c2dce5034735d8038c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Sep 2019 14:41:02 +0900 Subject: [PATCH 2593/2854] Force glowing sprite text to never redraw --- osu.Game/Graphics/Sprites/GlowingSpriteText.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs index 892b27da75..12688da9df 100644 --- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs @@ -12,7 +12,6 @@ namespace osu.Game.Graphics.Sprites public class GlowingSpriteText : Container, IHasText { private readonly OsuSpriteText spriteText, blurredText; - private readonly BufferedContainer buffer; public string Text { @@ -44,24 +43,19 @@ namespace osu.Game.Graphics.Sprites set => blurredText.Colour = value; } - public bool RedrawOnScale - { - get => buffer.RedrawOnScale; - set => buffer.RedrawOnScale = value; - } - public GlowingSpriteText() { AutoSizeAxes = Axes.Both; Children = new Drawable[] { - buffer = new BufferedContainer + new BufferedContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, BlurSigma = new Vector2(4), CacheDrawnFrameBuffer = true, + RedrawOnScale = false, RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, Size = new Vector2(3f), From a1d7291ffa8623eeb91bebb947bba1423e0ee1ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 16:31:10 +0900 Subject: [PATCH 2594/2854] Fix pause menu keyboard navigation being affected by initial cursor hover --- .../Visual/Gameplay/TestSceneGameplayMenuOverlay.cs | 9 +++++++-- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs index cc275009ba..c1635ffc83 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs @@ -17,7 +17,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay { - [System.ComponentModel.Description("player pause/fail screens")] + [Description("player pause/fail screens")] public class TestSceneGameplayMenuOverlay : ManualInputManagerTestScene { public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseOverlay) }; @@ -152,7 +152,8 @@ namespace osu.Game.Tests.Visual.Gameplay } /// - /// Tests that entering menu with cursor initially on button selects it. + /// Tests that entering menu with cursor initially on button doesn't selects it immediately. + /// This is to allow for stable keyboard navigation. /// [Test] public void TestInitialButtonHover() @@ -164,6 +165,10 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Hide overlay", () => pauseOverlay.Hide()); showOverlay(); + AddAssert("First button not selected", () => !getButton(0).Selected.Value); + + AddStep("Move slightly", () => InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(1))); + AddAssert("First button selected", () => getButton(0).Selected.Value); } diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index f93d5d8b02..c5202fa792 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -304,6 +304,9 @@ namespace osu.Game.Screens.Play private class Button : DialogButton { + // required to ensure keyboard navigation always starts from an extremity (unless the cursor is moved) + protected override bool OnHover(HoverEvent e) => true; + protected override bool OnMouseMove(MouseMoveEvent e) { Selected.Value = true; From 55c6feab6408d37f69c26706eb981906d5e61bc9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 16:33:14 +0900 Subject: [PATCH 2595/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 90d1854c39..adc340a734 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -61,6 +61,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7d106f0484..cb30eee33a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 8390a2229b..88d181454f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -118,8 +118,8 @@ - - + + From 174f8ddb31b75b9a4e925c05871ea22f37a97fe0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 16:37:47 +0900 Subject: [PATCH 2596/2854] Remove incorrect usages --- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index e29748060e..008f8208eb 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -193,7 +193,6 @@ namespace osu.Game.Online.Leaderboards GlowColour = OsuColour.FromHex(@"83ccfa"), Text = score.TotalScore.ToString(@"N0"), Font = OsuFont.Numeric.With(size: 23), - RedrawOnScale = false, }, RankContainer = new Container { @@ -339,7 +338,6 @@ namespace osu.Game.Online.Leaderboards GlowColour = OsuColour.FromHex(@"83ccfa"), Text = statistic.Value, Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold), - RedrawOnScale = false }, }, }; From bda21998c4de0a87d57ed019a6c77b0c86925223 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Sep 2019 16:39:58 +0900 Subject: [PATCH 2597/2854] Add helper method to make direct casts be used --- .../Skinning/OsuLegacySkinTransformer.cs | 2 +- .../Skins/TestSceneSkinConfigurationLookup.cs | 19 +++++++++++++++++++ osu.Game/Skinning/LegacySkin.cs | 6 +++--- osu.Game/Skinning/SkinUtils.cs | 18 ++++++++++++++++++ 4 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Skinning/SkinUtils.cs diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 284259705a..5957b81d7e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Skinning { case OsuSkinConfiguration.SliderPathRadius: if (hasHitCircle.Value) - return new BindableFloat(legacy_circle_radius) as Bindable; + return SkinUtils.As(new BindableFloat(legacy_circle_radius)); break; } diff --git a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs index 1344d20d9f..71df038311 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs @@ -95,6 +95,25 @@ namespace osu.Game.Tests.Skins AddAssert("Check combo colours", () => requester.GetConfig>(GlobalSkinConfiguration.ComboColours)?.Value?.Count > 0); } + [Test] + public void TestWrongColourType() + { + AddStep("Add config colour", () => { source1.Configuration.CustomColours["Lookup"] = Color4.Red; }); + + AddAssert("perform incorrect lookup", () => + { + try + { + requester.GetConfig(new SkinCustomColourLookup("Lookup")); + return false; + } + catch + { + return true; + } + }); + } + public enum LookupType { Test diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index cd2ad2d61c..0b1076be01 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -62,16 +62,16 @@ namespace osu.Game.Skinning switch (global) { case GlobalSkinConfiguration.ComboColours: - return new Bindable>(Configuration.ComboColours) as IBindable; + return SkinUtils.As(new Bindable>(Configuration.ComboColours)); } break; case GlobalSkinColour colour: - return getCustomColour(colour.ToString()) as IBindable; + return SkinUtils.As(getCustomColour(colour.ToString())); case SkinCustomColourLookup customColour: - return getCustomColour(customColour.Lookup.ToString()) as IBindable; + return SkinUtils.As(getCustomColour(customColour.Lookup.ToString())); default: try diff --git a/osu.Game/Skinning/SkinUtils.cs b/osu.Game/Skinning/SkinUtils.cs new file mode 100644 index 0000000000..18059bc4bf --- /dev/null +++ b/osu.Game/Skinning/SkinUtils.cs @@ -0,0 +1,18 @@ +using osu.Framework.Bindables; + +namespace osu.Game.Skinning +{ + /// + /// Contains helper methods to assist in implementing s. + /// + public static class SkinUtils + { + /// + /// Converts an to a . Used for returning configuration values of specific types. + /// + /// The value. + /// The type of value , and the type of the resulting bindable. + /// The resulting bindable. + public static Bindable As(object value) => (Bindable)value; + } +} From 696802e6743088e1d450ad822f940afe348c4f1e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 16:52:53 +0900 Subject: [PATCH 2598/2854] Don't use in music player for now --- osu.Game/Overlays/NowPlayingOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index c8361c6114..cf42c8005a 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -351,7 +351,6 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both; CacheDrawnFrameBuffer = true; - RedrawOnScale = false; Children = new Drawable[] { From 8e204ba2e90182dd3f691d94fa58ac526b91232e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Sep 2019 16:55:24 +0900 Subject: [PATCH 2599/2854] Refactor tests --- .../Skins/TestSceneSkinConfigurationLookup.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs index 71df038311..bbcc4140a9 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs @@ -44,24 +44,24 @@ namespace osu.Game.Tests.Skins } [Test] - public void TestParsingLookup() + public void TestFloatLookup() { - AddStep("Add config values", () => - { - source1.Configuration.ConfigDictionary["FloatTest"] = "1.1"; - source2.Configuration.ConfigDictionary["BoolTest"] = "1"; - }); - + AddStep("Add config values", () => source1.Configuration.ConfigDictionary["FloatTest"] = "1.1"); AddAssert("Check float parse lookup", () => requester.GetConfig("FloatTest")?.Value == 1.1f); + } + + [Test] + public void TestBoolLookup() + { + AddStep("Add config values", () => source1.Configuration.ConfigDictionary["BoolTest"] = "1"); AddAssert("Check bool parse lookup", () => requester.GetConfig("BoolTest")?.Value == true); } [Test] public void TestEnumLookup() { - AddStep("Add config values", () => { source1.Configuration.ConfigDictionary["Test"] = "Test2"; }); - - AddAssert("Check float parse lookup", () => requester.GetConfig(LookupType.Test)?.Value == ValueType.Test2); + AddStep("Add config values", () => source1.Configuration.ConfigDictionary["Test"] = "Test2"); + AddAssert("Check enum parse lookup", () => requester.GetConfig(LookupType.Test)?.Value == ValueType.Test2); } [Test] @@ -73,7 +73,7 @@ namespace osu.Game.Tests.Skins [Test] public void TestLookupNull() { - AddStep("Add config values", () => { source1.Configuration.ConfigDictionary["Lookup"] = null; }); + AddStep("Add config values", () => source1.Configuration.ConfigDictionary["Lookup"] = null); AddAssert("Check lookup null", () => { @@ -85,7 +85,7 @@ namespace osu.Game.Tests.Skins [Test] public void TestColourLookup() { - AddStep("Add config colour", () => { source1.Configuration.CustomColours["Lookup"] = Color4.Red; }); + AddStep("Add config colour", () => source1.Configuration.CustomColours["Lookup"] = Color4.Red); AddAssert("Check colour lookup", () => requester.GetConfig(new SkinCustomColourLookup("Lookup"))?.Value == Color4.Red); } @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Skins [Test] public void TestWrongColourType() { - AddStep("Add config colour", () => { source1.Configuration.CustomColours["Lookup"] = Color4.Red; }); + AddStep("Add config colour", () => source1.Configuration.CustomColours["Lookup"] = Color4.Red); AddAssert("perform incorrect lookup", () => { From 90985b6af65a6008b55fd968d523e7c9ebc40d05 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Sep 2019 17:00:43 +0900 Subject: [PATCH 2600/2854] Add missing license header --- osu.Game/Skinning/SkinUtils.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Skinning/SkinUtils.cs b/osu.Game/Skinning/SkinUtils.cs index 18059bc4bf..e3bc5e28b8 100644 --- a/osu.Game/Skinning/SkinUtils.cs +++ b/osu.Game/Skinning/SkinUtils.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using osu.Framework.Bindables; namespace osu.Game.Skinning From 0a6c42972c8896ef0943133b93661895cb129841 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 23:01:35 +0900 Subject: [PATCH 2601/2854] Add back missing sample fallback to default skin --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 8 ++- osu.Game/Rulesets/UI/FallbackSampleStore.cs | 64 +++++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Rulesets/UI/FallbackSampleStore.cs diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 562b2c4667..2a8f64c42e 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.UI private TextureStore textureStore; - private ISampleStore sampleStore; + private ISampleStore localSampleStore; /// /// The playfield. @@ -158,8 +158,8 @@ namespace osu.Game.Rulesets.UI textureStore.AddStore(dependencies.Get()); dependencies.Cache(textureStore); - sampleStore = dependencies.Get().GetSampleStore(new NamespacedResourceStore(resources, "Samples")); - dependencies.CacheAs(sampleStore); + localSampleStore = dependencies.Get().GetSampleStore(new NamespacedResourceStore(resources, "Samples")); + dependencies.CacheAs(new FallbackSampleStore(localSampleStore, dependencies.Get())); } onScreenDisplay = dependencies.Get(); @@ -334,6 +334,8 @@ namespace osu.Game.Rulesets.UI { base.Dispose(isDisposing); + localSampleStore?.Dispose(); + if (Config != null) { onScreenDisplay?.StopTracking(this, Config); diff --git a/osu.Game/Rulesets/UI/FallbackSampleStore.cs b/osu.Game/Rulesets/UI/FallbackSampleStore.cs new file mode 100644 index 0000000000..64e273b72e --- /dev/null +++ b/osu.Game/Rulesets/UI/FallbackSampleStore.cs @@ -0,0 +1,64 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Audio.Track; +using osu.Framework.Bindables; + +namespace osu.Game.Rulesets.UI +{ + public class FallbackSampleStore : ISampleStore + { + private readonly ISampleStore primary; + private readonly ISampleStore secondary; + + public FallbackSampleStore(ISampleStore primary, ISampleStore secondary) + { + this.primary = primary; + this.secondary = secondary; + } + + public void Dispose() + { + primary.Dispose(); + secondary.Dispose(); + } + + public SampleChannel Get(string name) => primary.Get(name) ?? secondary.Get(name); + + public Task GetAsync(string name) => primary.GetAsync(name) ?? secondary.GetAsync(name); + + public Stream GetStream(string name) => primary.GetStream(name) ?? secondary.GetStream(name); + + public IEnumerable GetAvailableResources() => primary.GetAvailableResources().Concat(secondary.GetAvailableResources()); + + public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) + { + primary.AddAdjustment(type, adjustBindable); + secondary.AddAdjustment(type, adjustBindable); + } + + public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) + { + primary.RemoveAdjustment(type, adjustBindable); + primary.RemoveAdjustment(type, adjustBindable); + } + + public BindableDouble Volume => primary.Volume; + + public BindableDouble Balance => primary.Balance; + + public BindableDouble Frequency => primary.Frequency; + + public int PlaybackConcurrency + { + get => primary.PlaybackConcurrency; + set => primary.PlaybackConcurrency = value; + } + } +} From 60c2d113b80eed1630d0ccdc951a3b1fa954b9e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 23:07:03 +0900 Subject: [PATCH 2602/2854] Fix typo --- osu.Game/Rulesets/UI/FallbackSampleStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/FallbackSampleStore.cs b/osu.Game/Rulesets/UI/FallbackSampleStore.cs index 64e273b72e..cdefae458a 100644 --- a/osu.Game/Rulesets/UI/FallbackSampleStore.cs +++ b/osu.Game/Rulesets/UI/FallbackSampleStore.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.UI public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) { primary.RemoveAdjustment(type, adjustBindable); - primary.RemoveAdjustment(type, adjustBindable); + secondary.RemoveAdjustment(type, adjustBindable); } public BindableDouble Volume => primary.Volume; From a0aeccf2322d06c782c9fc15d5eda86d1b15df6b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 23:24:13 +0900 Subject: [PATCH 2603/2854] Fix fallback to default combo colours not working --- osu.Game/Skinning/DefaultSkin.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 4dee70a47f..c0d6bb34e0 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -1,11 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Audio; +using osuTK.Graphics; namespace osu.Game.Skinning { @@ -23,6 +25,21 @@ namespace osu.Game.Skinning public override SampleChannel GetSample(ISampleInfo sampleInfo) => null; - public override IBindable GetConfig(TLookup lookup) => null; + public override IBindable GetConfig(TLookup lookup) + { + switch (lookup) + { + case GlobalSkinConfiguration global: + switch (global) + { + case GlobalSkinConfiguration.ComboColours: + return SkinUtils.As(new Bindable>(Configuration.ComboColours)); + } + + break; + } + + return null; + } } } From dafe9da851437098d15f6104ce720d7eba2fc351 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 00:22:35 +0900 Subject: [PATCH 2604/2854] Dispose config managers ahead of time to avoid database errors --- osu.Game/Rulesets/RulesetConfigCache.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Rulesets/RulesetConfigCache.cs b/osu.Game/Rulesets/RulesetConfigCache.cs index 8c9e3c94e2..abaf7a96f2 100644 --- a/osu.Game/Rulesets/RulesetConfigCache.cs +++ b/osu.Game/Rulesets/RulesetConfigCache.cs @@ -36,5 +36,13 @@ namespace osu.Game.Rulesets return configCache.GetOrAdd(ruleset.RulesetInfo.ID.Value, _ => ruleset.CreateConfig(settingsStore)); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + foreach (var c in configCache.Values) + (c as IDisposable)?.Dispose(); + } } } From 5b094f8e1de30a7473e9b29c9b7933696f77c226 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 01:13:58 +0900 Subject: [PATCH 2605/2854] Actually register the RulesetConfigCache as a component --- osu.Game/OsuGameBase.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index de8f316b06..d6b8ad3e67 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -207,6 +207,7 @@ namespace osu.Game FileStore.Cleanup(); AddInternal(API); + AddInternal(RulesetConfigCache); GlobalActionContainer globalBinding; From d6cdde552daa84e04a76e6bf1c1d61872002c0bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 01:14:37 +0900 Subject: [PATCH 2606/2854] Add comment explaining dispose logic --- osu.Game/Rulesets/RulesetConfigCache.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/RulesetConfigCache.cs b/osu.Game/Rulesets/RulesetConfigCache.cs index abaf7a96f2..cdcd2666cf 100644 --- a/osu.Game/Rulesets/RulesetConfigCache.cs +++ b/osu.Game/Rulesets/RulesetConfigCache.cs @@ -41,6 +41,7 @@ namespace osu.Game.Rulesets { base.Dispose(isDisposing); + // ensures any potential database operations are finalised before game destruction. foreach (var c in configCache.Values) (c as IDisposable)?.Dispose(); } From 50985d1b1d952b7d922c0a4ccd3936218061cc26 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 10:43:19 +0900 Subject: [PATCH 2607/2854] Fix disposal logic --- osu.Game/Rulesets/UI/FallbackSampleStore.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/UI/FallbackSampleStore.cs b/osu.Game/Rulesets/UI/FallbackSampleStore.cs index cdefae458a..f9e1c85e38 100644 --- a/osu.Game/Rulesets/UI/FallbackSampleStore.cs +++ b/osu.Game/Rulesets/UI/FallbackSampleStore.cs @@ -23,12 +23,6 @@ namespace osu.Game.Rulesets.UI this.secondary = secondary; } - public void Dispose() - { - primary.Dispose(); - secondary.Dispose(); - } - public SampleChannel Get(string name) => primary.Get(name) ?? secondary.Get(name); public Task GetAsync(string name) => primary.GetAsync(name) ?? secondary.GetAsync(name); @@ -58,7 +52,15 @@ namespace osu.Game.Rulesets.UI public int PlaybackConcurrency { get => primary.PlaybackConcurrency; - set => primary.PlaybackConcurrency = value; + set + { + primary.PlaybackConcurrency = value; + secondary.PlaybackConcurrency = value; + } + } + + public void Dispose() + { } } } From 43aed7fea7c1be8da0e906f5f18d6dafc17f6716 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2019 02:58:58 +0000 Subject: [PATCH 2608/2854] Bump Humanizer from 2.6.2 to 2.7.2 Bumps [Humanizer](https://github.com/Humanizr/Humanizer) from 2.6.2 to 2.7.2. - [Release notes](https://github.com/Humanizr/Humanizer/releases) - [Changelog](https://github.com/Humanizr/Humanizer/blob/master/release_notes.md) - [Commits](https://github.com/Humanizr/Humanizer/compare/v2.6.2...v2.7.2) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8e4ce03e1a..5f2aad24dc 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -21,7 +21,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 88d181454f..5027a4ef8c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -113,7 +113,7 @@ - + From 7d1f5310d20a866a9b8c9283eb3a2a1bc6903840 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 12:03:29 +0900 Subject: [PATCH 2609/2854] Don't implement anything --- osu.Game/Rulesets/UI/FallbackSampleStore.cs | 30 +++++++-------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/osu.Game/Rulesets/UI/FallbackSampleStore.cs b/osu.Game/Rulesets/UI/FallbackSampleStore.cs index f9e1c85e38..f1df8bf359 100644 --- a/osu.Game/Rulesets/UI/FallbackSampleStore.cs +++ b/osu.Game/Rulesets/UI/FallbackSampleStore.cs @@ -1,9 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading.Tasks; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -29,34 +29,22 @@ namespace osu.Game.Rulesets.UI public Stream GetStream(string name) => primary.GetStream(name) ?? secondary.GetStream(name); - public IEnumerable GetAvailableResources() => primary.GetAvailableResources().Concat(secondary.GetAvailableResources()); + public IEnumerable GetAvailableResources() => throw new NotImplementedException(); - public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) - { - primary.AddAdjustment(type, adjustBindable); - secondary.AddAdjustment(type, adjustBindable); - } + public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); - public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) - { - primary.RemoveAdjustment(type, adjustBindable); - secondary.RemoveAdjustment(type, adjustBindable); - } + public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); - public BindableDouble Volume => primary.Volume; + public BindableDouble Volume => throw new NotImplementedException(); - public BindableDouble Balance => primary.Balance; + public BindableDouble Balance => throw new NotImplementedException(); - public BindableDouble Frequency => primary.Frequency; + public BindableDouble Frequency => throw new NotImplementedException(); public int PlaybackConcurrency { - get => primary.PlaybackConcurrency; - set - { - primary.PlaybackConcurrency = value; - secondary.PlaybackConcurrency = value; - } + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); } public void Dispose() From bf6f803e691edbc776896433beb8f8c4f0f2f3ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 12:12:27 +0900 Subject: [PATCH 2610/2854] Nest temporary class --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 49 +++++++++++++++++++ osu.Game/Rulesets/UI/FallbackSampleStore.cs | 54 --------------------- 2 files changed, 49 insertions(+), 54 deletions(-) delete mode 100644 osu.Game/Rulesets/UI/FallbackSampleStore.cs diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 2a8f64c42e..a34bb6e8ea 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -11,10 +11,13 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; +using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics.Cursor; @@ -465,4 +468,50 @@ namespace osu.Game.Rulesets.UI { } } + + /// + /// A sample store which adds a fallback source. + /// + /// + /// This is a temporary implementation to workaround ISampleStore limitations. + /// + public class FallbackSampleStore : ISampleStore + { + private readonly ISampleStore primary; + private readonly ISampleStore secondary; + + public FallbackSampleStore(ISampleStore primary, ISampleStore secondary) + { + this.primary = primary; + this.secondary = secondary; + } + + public SampleChannel Get(string name) => primary.Get(name) ?? secondary.Get(name); + + public Task GetAsync(string name) => primary.GetAsync(name) ?? secondary.GetAsync(name); + + public Stream GetStream(string name) => primary.GetStream(name) ?? secondary.GetStream(name); + + public IEnumerable GetAvailableResources() => throw new NotImplementedException(); + + public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); + + public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); + + public BindableDouble Volume => throw new NotImplementedException(); + + public BindableDouble Balance => throw new NotImplementedException(); + + public BindableDouble Frequency => throw new NotImplementedException(); + + public int PlaybackConcurrency + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } + + public void Dispose() + { + } + } } diff --git a/osu.Game/Rulesets/UI/FallbackSampleStore.cs b/osu.Game/Rulesets/UI/FallbackSampleStore.cs deleted file mode 100644 index f1df8bf359..0000000000 --- a/osu.Game/Rulesets/UI/FallbackSampleStore.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Audio.Track; -using osu.Framework.Bindables; - -namespace osu.Game.Rulesets.UI -{ - public class FallbackSampleStore : ISampleStore - { - private readonly ISampleStore primary; - private readonly ISampleStore secondary; - - public FallbackSampleStore(ISampleStore primary, ISampleStore secondary) - { - this.primary = primary; - this.secondary = secondary; - } - - public SampleChannel Get(string name) => primary.Get(name) ?? secondary.Get(name); - - public Task GetAsync(string name) => primary.GetAsync(name) ?? secondary.GetAsync(name); - - public Stream GetStream(string name) => primary.GetStream(name) ?? secondary.GetStream(name); - - public IEnumerable GetAvailableResources() => throw new NotImplementedException(); - - public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); - - public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); - - public BindableDouble Volume => throw new NotImplementedException(); - - public BindableDouble Balance => throw new NotImplementedException(); - - public BindableDouble Frequency => throw new NotImplementedException(); - - public int PlaybackConcurrency - { - get => throw new NotImplementedException(); - set => throw new NotImplementedException(); - } - - public void Dispose() - { - } - } -} From f4f95197616cce29716fb31f1f36d3cb03bde0b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 12:16:20 +0900 Subject: [PATCH 2611/2854] Add todo comment --- osu.Game/Skinning/DefaultSkin.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index c0d6bb34e0..529c1afca5 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -29,6 +29,8 @@ namespace osu.Game.Skinning { switch (lookup) { + // todo: this code is pulled from LegacySkin and should not exist. + // will likely change based on how databased storage of skin configuration goes. case GlobalSkinConfiguration global: switch (global) { From f925e781a9ac8a1a770d0e2fc09d0b421b54af47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 15:24:00 +0900 Subject: [PATCH 2612/2854] Refactor HitWindows for legibility --- .../Objects/CatchHitObject.cs | 1 + .../{Objects => Scoring}/CatchHitWindows.cs | 3 +- .../Scoring/CatchScoreProcessor.cs | 1 - .../Difficulty/ManiaDifficultyCalculator.cs | 6 +- osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 2 +- .../Objects/HoldNoteTick.cs | 2 +- .../Objects/ManiaHitObject.cs | 2 + .../Objects/ManiaHitWindows.cs | 35 ----- .../Scoring/ManiaHitWindows.cs | 11 ++ .../Scoring/ManiaScoreProcessor.cs | 1 - .../TestSceneShaking.cs | 2 +- .../Difficulty/OsuDifficultyCalculator.cs | 5 +- .../Objects/Drawables/DrawableHitCircle.cs | 6 +- .../Objects/Drawables/DrawableRepeatPoint.cs | 2 +- .../Objects/Drawables/DrawableSliderTick.cs | 2 +- .../Objects/Drawables/DrawableSpinner.cs | 2 +- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 + .../Objects/OsuHitWindows.cs | 29 ---- osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 1 + .../Objects/SliderTailCircle.cs | 1 + osu.Game.Rulesets.Osu/Objects/SliderTick.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 2 +- .../Replays/OsuAutoGenerator.cs | 20 +-- .../Scoring/OsuHitWindows.cs | 20 +++ .../TestSceneTaikoPlayfield.cs | 2 +- .../Difficulty/TaikoDifficultyCalculator.cs | 5 +- .../Objects/Drawables/DrawableHit.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 2 +- .../Objects/DrumRollTick.cs | 2 +- .../Objects/StrongHitObject.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/Swell.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/SwellTick.cs | 2 +- .../Objects/TaikoHitObject.cs | 2 + .../Objects/TaikoHitWindows.cs | 41 ------ .../Scoring/TaikoHitWindows.cs | 19 +++ .../Scoring/TaikoScoreProcessor.cs | 1 - .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +- .../Gameplay/TestSceneBarHitErrorMeter.cs | 20 +-- .../Objects/Drawables/DrawableHitObject.cs | 2 +- osu.Game/Rulesets/Objects/HitObject.cs | 1 + .../Objects/Legacy/Mania/ConvertHit.cs | 1 + .../Objects/Legacy/Mania/ConvertHold.cs | 1 + .../Objects/Legacy/Mania/ConvertSlider.cs | 1 + .../Objects/Legacy/Mania/ConvertSpinner.cs | 1 + .../Rulesets/Objects/Legacy/Osu/ConvertHit.cs | 1 + .../Objects/Legacy/Osu/ConvertSlider.cs | 1 + .../Objects/Legacy/Osu/ConvertSpinner.cs | 1 + .../Objects/Legacy/Taiko/ConvertHit.cs | 2 + .../Objects/Legacy/Taiko/ConvertSlider.cs | 2 + .../Objects/Legacy/Taiko/ConvertSpinner.cs | 1 + osu.Game/Rulesets/Scoring/HitResult.cs | 4 + .../{Objects => Scoring}/HitWindows.cs | 131 +++++++++++++----- osu.Game/Scoring/ScoreInfo.cs | 2 +- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 1 - .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 3 +- .../Play/HUD/HitErrorMeters/HitErrorMeter.cs | 2 +- 57 files changed, 225 insertions(+), 199 deletions(-) rename osu.Game.Rulesets.Catch/{Objects => Scoring}/CatchHitWindows.cs (87%) delete mode 100644 osu.Game.Rulesets.Mania/Objects/ManiaHitWindows.cs create mode 100644 osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs delete mode 100644 osu.Game.Rulesets.Osu/Objects/OsuHitWindows.cs create mode 100644 osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs delete mode 100644 osu.Game.Rulesets.Taiko/Objects/TaikoHitWindows.cs create mode 100644 osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs rename osu.Game/Rulesets/{Objects => Scoring}/HitWindows.cs (56%) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 19a1b59752..a25d9cb67e 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -6,6 +6,7 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Catch.Objects { diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitWindows.cs b/osu.Game.Rulesets.Catch/Scoring/CatchHitWindows.cs similarity index 87% rename from osu.Game.Rulesets.Catch/Objects/CatchHitWindows.cs rename to osu.Game.Rulesets.Catch/Scoring/CatchHitWindows.cs index 837662f5fe..ff793a372e 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitWindows.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchHitWindows.cs @@ -1,10 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; -namespace osu.Game.Rulesets.Catch.Objects +namespace osu.Game.Rulesets.Catch.Scoring { public class CatchHitWindows : HitWindows { diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 99b22b2d56..18785d65ea 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -4,7 +4,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index d945abdb04..37cba1fd3c 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -11,9 +11,9 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Difficulty.Preprocessing; using osu.Game.Rulesets.Mania.Difficulty.Skills; using osu.Game.Rulesets.Mania.Mods; -using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Difficulty { @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty StarRating = difficultyValue(skills) * star_scaling_factor, Mods = mods, // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future - GreatHitWindow = (int)(hitWindows.Great / 2) / clockRate, + GreatHitWindow = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate, Skills = skills }; } diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index d28d04b3c1..0c82cf7bbc 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -5,8 +5,8 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Objects { diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs index 6bb21633b6..d0125f8793 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs @@ -3,7 +3,7 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Judgements; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Objects { diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs index 70720a926b..995e1516cb 100644 --- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs @@ -3,7 +3,9 @@ using osu.Framework.Bindables; using osu.Game.Rulesets.Mania.Objects.Types; +using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Objects { diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitWindows.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitWindows.cs deleted file mode 100644 index 5f2ceab48b..0000000000 --- a/osu.Game.Rulesets.Mania/Objects/ManiaHitWindows.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Mania.Objects -{ - public class ManiaHitWindows : HitWindows - { - private static readonly IReadOnlyDictionary base_ranges = new Dictionary - { - { HitResult.Perfect, (44.8, 38.8, 27.8) }, - { HitResult.Great, (128, 98, 68) }, - { HitResult.Good, (194, 164, 134) }, - { HitResult.Ok, (254, 224, 194) }, - { HitResult.Meh, (302, 272, 242) }, - { HitResult.Miss, (376, 346, 316) }, - }; - - public override bool IsHitResultAllowed(HitResult result) => true; - - public override void SetDifficulty(double difficulty) - { - Perfect = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Perfect]); - Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]); - Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]); - Ok = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Ok]); - Meh = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Meh]); - Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]); - } - } -} diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs new file mode 100644 index 0000000000..549f0f9214 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mania.Scoring +{ + public class ManiaHitWindows : HitWindows + { + } +} diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index 5caf08fb1e..49894a644c 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -4,7 +4,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs index 585fdb9cb4..863d0eda09 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Tests Debug.Assert(drawableHitObject.HitObject.HitWindows != null); - double delay = drawableHitObject.HitObject.StartTime - (drawableHitObject.HitObject.HitWindows.HalfWindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current; + double delay = drawableHitObject.HitObject.StartTime - (drawableHitObject.HitObject.HitWindows.WindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current; Scheduler.AddDelayed(() => drawableHitObject.TriggerJudgement(), delay); return drawableHitObject; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 61e9f60cdd..b0d261a1cc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -9,11 +9,12 @@ using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Difficulty.Skills; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Difficulty { @@ -39,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future - double hitWindowGreat = (int)(hitWindows.Great / 2) / clockRate; + double hitWindowGreat = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate; double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; int maxCombo = beatmap.HitObjects.Count; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 3decc4e51f..985dcbca86 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -10,8 +10,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; -using osuTK; using osu.Game.Rulesets.Scoring; +using osuTK; using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables @@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (result == HitResult.None) { - Shake(Math.Abs(timeOffset) - HitObject.HitWindows.HalfWindowFor(HitResult.Miss)); + Shake(Math.Abs(timeOffset) - HitObject.HitWindows.WindowFor(HitResult.Miss)); return; } @@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables hitArea.HitAction = null; // override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early. - LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.HalfWindowFor(HitResult.Miss); + LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.WindowFor(HitResult.Miss); break; case ArmedState.Miss: diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 50187781f6..00a943a67f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -9,8 +9,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; using osu.Game.Rulesets.Objects.Drawables; -using osuTK; using osu.Game.Rulesets.Scoring; +using osuTK; using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index c5fa5f0af5..ba931976a8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -8,9 +8,9 @@ using osu.Game.Rulesets.Objects.Drawables; using osuTK; using osuTK.Graphics; using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index a0bd301fdb..49aaa2aaea 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -13,8 +13,8 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; -using osu.Game.Screens.Ranking; using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Ranking; namespace osu.Game.Rulesets.Osu.Objects.Drawables { diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index b52bfcd181..2cf877b000 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -7,6 +7,8 @@ using osu.Game.Rulesets.Objects; using osuTK; using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitWindows.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitWindows.cs deleted file mode 100644 index add8fd53c7..0000000000 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitWindows.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Osu.Objects -{ - public class OsuHitWindows : HitWindows - { - private static readonly IReadOnlyDictionary base_ranges = new Dictionary - { - { HitResult.Great, (160, 100, 40) }, - { HitResult.Good, (280, 200, 120) }, - { HitResult.Meh, (400, 300, 200) }, - { HitResult.Miss, (400, 400, 400) }, - }; - - public override void SetDifficulty(double difficulty) - { - Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]); - Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]); - Meh = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Meh]); - Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]); - } - } -} diff --git a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs index 5bd480c0ff..a794e57c9e 100644 --- a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs @@ -5,8 +5,8 @@ using System; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 93231844bb..2805494021 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -13,6 +13,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs index c53a88337e..7e540a577b 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs @@ -5,6 +5,7 @@ using osu.Framework.Bindables; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs index 60e9084ed3..af7cf5b144 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs @@ -4,8 +4,8 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index 69c779a182..2e7b763966 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -6,8 +6,8 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index e5fa571d4d..24320b6579 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -10,9 +10,9 @@ using System.Diagnostics; using System.Linq; using osu.Framework.Graphics; using osu.Game.Replays; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Replays @@ -118,29 +118,29 @@ namespace osu.Game.Rulesets.Osu.Replays Debug.Assert(hitWindows != null); // Make the cursor stay at a hitObject as long as possible (mainly for autopilot). - if (h.StartTime - hitWindows.HalfWindowFor(HitResult.Miss) > endTime + hitWindows.HalfWindowFor(HitResult.Meh) + 50) + if (h.StartTime - hitWindows.WindowFor(HitResult.Miss) > endTime + hitWindows.WindowFor(HitResult.Meh) + 50) { if (!(prev is Spinner) && h.StartTime - endTime < 1000) - AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.WindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(h is Spinner)) - AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.HalfWindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.WindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } - else if (h.StartTime - hitWindows.HalfWindowFor(HitResult.Meh) > endTime + hitWindows.HalfWindowFor(HitResult.Meh) + 50) + else if (h.StartTime - hitWindows.WindowFor(HitResult.Meh) > endTime + hitWindows.WindowFor(HitResult.Meh) + 50) { if (!(prev is Spinner) && h.StartTime - endTime < 1000) - AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.WindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(h is Spinner)) - AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.WindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } - else if (h.StartTime - hitWindows.HalfWindowFor(HitResult.Good) > endTime + hitWindows.HalfWindowFor(HitResult.Good) + 50) + else if (h.StartTime - hitWindows.WindowFor(HitResult.Good) > endTime + hitWindows.WindowFor(HitResult.Good) + 50) { if (!(prev is Spinner) && h.StartTime - endTime < 1000) - AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.HalfWindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.WindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(h is Spinner)) - AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.HalfWindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.WindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } } diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs new file mode 100644 index 0000000000..8f258c9e3d --- /dev/null +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Scoring +{ + public class OsuHitWindows : HitWindows + { + private static readonly DifficultyRange[] osu_ranges = + { + new DifficultyRange(HitResult.Great, 80, 50, 20), + new DifficultyRange(HitResult.Good, 140, 100, 60), + new DifficultyRange(HitResult.Meh, 200, 150, 100), + new DifficultyRange(HitResult.Miss, 200, 200, 200), + }; + + protected override DifficultyRange[] GetRanges() => osu_ranges; + } +} diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs index 6fd16c213b..3aa461e779 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs @@ -14,13 +14,13 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.UI; using osu.Game.Tests.Visual; using osuTK; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.Tests { diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index fc93bccb94..32d49ea39c 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -8,11 +8,12 @@ using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Difficulty.Skills; using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Scoring; namespace osu.Game.Rulesets.Taiko.Difficulty { @@ -38,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty StarRating = skills.Single().DifficultyValue() * star_scaling_factor, Mods = mods, // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future - GreatHitWindow = (int)(hitWindows.Great / 2) / clockRate, + GreatHitWindow = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate, MaxCombo = beatmap.HitObjects.Count(h => h is Hit), Skills = skills }; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 0942b37f58..676ecd5a0b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables validActionPressed = false; UnproxyContent(); - this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire(); + this.Delay(HitObject.HitWindows.WindowFor(HitResult.Miss)).Expire(); break; case ArmedState.Miss: diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 3ed52f21f0..4e02c76a8b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -6,7 +6,7 @@ using System; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs index 39e2b45e24..c466ca7c8a 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects diff --git a/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs index 830e640242..d660149528 100644 --- a/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs index e7812841bf..f96c033dce 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs @@ -4,7 +4,7 @@ using System; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects diff --git a/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs index 049fa7de5f..68212e8f12 100644 --- a/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index 3592d73004..6f4fbd0651 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -4,7 +4,9 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; +using osu.Game.Rulesets.Taiko.Scoring; namespace osu.Game.Rulesets.Taiko.Objects { diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitWindows.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitWindows.cs deleted file mode 100644 index f232919cbf..0000000000 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitWindows.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Taiko.Objects -{ - public class TaikoHitWindows : HitWindows - { - private static readonly IReadOnlyDictionary base_ranges = new Dictionary - { - { HitResult.Great, (100, 70, 40) }, - { HitResult.Good, (240, 160, 100) }, - { HitResult.Miss, (270, 190, 140) }, - }; - - public override bool IsHitResultAllowed(HitResult result) - { - switch (result) - { - case HitResult.Great: - case HitResult.Good: - case HitResult.Miss: - return true; - - default: - return false; - } - } - - public override void SetDifficulty(double difficulty) - { - Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]); - Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]); - Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]); - } - } -} diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs new file mode 100644 index 0000000000..77d59f9638 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Taiko.Scoring +{ + public class TaikoHitWindows : HitWindows + { + private static readonly DifficultyRange[] taiko_ranges = + { + new DifficultyRange(HitResult.Great, 50, 35, 20), + new DifficultyRange(HitResult.Good, 120, 80, 50), + new DifficultyRange(HitResult.Miss, 135, 95, 70), + }; + + protected override DifficultyRange[] GetRanges() => taiko_ranges; + } +} diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index 68ddf2db19..75a27ff639 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -3,7 +3,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.UI; diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index a725c58462..4859abbb8e 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -9,8 +9,8 @@ using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.IO.Serialization; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; using osu.Game.Tests.Resources; using osuTK; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs index e9c15dab9b..a934d22b5d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs @@ -3,18 +3,18 @@ using NUnit.Framework; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Catch.Objects; using System; using System.Collections.Generic; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Scoring; using osu.Framework.MathUtils; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Catch.Scoring; +using osu.Game.Rulesets.Mania.Scoring; +using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Scoring; using osu.Game.Screens.Play.HUD.HitErrorMeters; namespace osu.Game.Tests.Visual.Gameplay @@ -36,8 +36,8 @@ namespace osu.Game.Tests.Visual.Gameplay AddRepeatStep("New random judgement", () => newJudgement(), 40); - AddRepeatStep("New max negative", () => newJudgement(-hitWindows.HalfWindowFor(HitResult.Meh)), 20); - AddRepeatStep("New max positive", () => newJudgement(hitWindows.HalfWindowFor(HitResult.Meh)), 20); + AddRepeatStep("New max negative", () => newJudgement(-hitWindows.WindowFor(HitResult.Meh)), 20); + AddRepeatStep("New max positive", () => newJudgement(hitWindows.WindowFor(HitResult.Meh)), 20); AddStep("New fixed judgement (50ms)", () => newJudgement(50)); } @@ -85,9 +85,9 @@ namespace osu.Game.Tests.Visual.Gameplay AutoSizeAxes = Axes.Both, Children = new[] { - new SpriteText { Text = $@"Great: {hitWindows?.Great}" }, - new SpriteText { Text = $@"Good: {hitWindows?.Good}" }, - new SpriteText { Text = $@"Meh: {hitWindows?.Meh}" }, + new SpriteText { Text = $@"Great: {hitWindows?.WindowFor(HitResult.Great)}" }, + new SpriteText { Text = $@"Good: {hitWindows?.WindowFor(HitResult.Good)}" }, + new SpriteText { Text = $@"Meh: {hitWindows?.WindowFor(HitResult.Meh)}" }, } }); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index a6d0aad880..5dfab5ef77 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Objects.Drawables { UpdateInitialTransforms(); - var judgementOffset = Math.Min(HitObject.HitWindows?.HalfWindowFor(HitResult.Miss) ?? double.MaxValue, Result?.TimeOffset ?? 0); + var judgementOffset = Math.Min(HitObject.HitWindows?.WindowFor(HitResult.Miss) ?? double.MaxValue, Result?.TimeOffset ?? 0); using (BeginDelayedSequence(InitialLifetimeOffset + judgementOffset, true)) { diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 5e029139d9..96297ab44f 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -9,6 +9,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Objects { diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs index 06fde576d2..609bdd571a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Objects.Legacy.Mania { diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs index 096c07f7d2..350ee3185d 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Objects.Legacy.Mania { diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs index 226d91bb86..e372fbd273 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Objects.Legacy.Mania { diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs index eb20fa67f1..067377d300 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Objects.Legacy.Mania { diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs index 84b66a4c26..c9851a0074 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; using osuTK; namespace osu.Game.Rulesets.Objects.Legacy.Osu diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs index c850feb189..1c1180702b 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; using osuTK; namespace osu.Game.Rulesets.Objects.Legacy.Osu diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs index e5a8884aa2..bc94ea1803 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; using osuTK; namespace osu.Game.Rulesets.Objects.Legacy.Osu diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs index 5cecc2a59f..709345170f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.Scoring; + namespace osu.Game.Rulesets.Objects.Legacy.Taiko { /// diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs index 5cedc6e2e5..c173b3e11a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.Scoring; + namespace osu.Game.Rulesets.Objects.Legacy.Taiko { /// diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs index ca9fdd53ed..9a35ad2776 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Objects.Legacy.Taiko { diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index 2376f12e9e..7ba88d3df8 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -16,6 +16,10 @@ namespace osu.Game.Rulesets.Scoring /// /// Indicates that the object has been judged as a miss. /// + /// + /// This miss window should determine how early a hit can be before it is considered for judgement (as opposed to being ignored as + /// "too far in the future). It should also define when a forced miss should be triggered (as a result of no user input in time). + /// [Description(@"Miss")] Miss, diff --git a/osu.Game/Rulesets/Objects/HitWindows.cs b/osu.Game/Rulesets/Scoring/HitWindows.cs similarity index 56% rename from osu.Game/Rulesets/Objects/HitWindows.cs rename to osu.Game/Rulesets/Scoring/HitWindows.cs index e88af67c7c..beba62044a 100644 --- a/osu.Game/Rulesets/Objects/HitWindows.cs +++ b/osu.Game/Rulesets/Scoring/HitWindows.cs @@ -4,51 +4,61 @@ using System; using System.Collections.Generic; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Objects; -namespace osu.Game.Rulesets.Objects +namespace osu.Game.Rulesets.Scoring { + /// + /// A structure containing timing data for hit window based gameplay. + /// public class HitWindows { - private static readonly IReadOnlyDictionary base_ranges = new Dictionary + private static readonly DifficultyRange[] base_ranges = { - { HitResult.Perfect, (44.8, 38.8, 27.8) }, - { HitResult.Great, (128, 98, 68) }, - { HitResult.Good, (194, 164, 134) }, - { HitResult.Ok, (254, 224, 194) }, - { HitResult.Meh, (302, 272, 242) }, - { HitResult.Miss, (376, 346, 316) }, + new DifficultyRange(HitResult.Perfect, 22.4D, 19.4D, 13.9D), + new DifficultyRange(HitResult.Great, 64, 49, 34), + new DifficultyRange(HitResult.Good, 97, 82, 67), + new DifficultyRange(HitResult.Ok, 127, 112, 97), + new DifficultyRange(HitResult.Meh, 151, 136, 121), + new DifficultyRange(HitResult.Miss, 188, 173, 158), }; /// /// Hit window for a result. /// - public double Perfect { get; protected set; } + private double perfect; /// /// Hit window for a result. /// - public double Great { get; protected set; } + /// + /// Note that this value includes both the early and late region. + /// + private double great; /// /// Hit window for a result. /// - public double Good { get; protected set; } + private double good; /// /// Hit window for an result. /// - public double Ok { get; protected set; } + private double ok; /// /// Hit window for a result. /// - public double Meh { get; protected set; } + private double meh; /// /// Hit window for a result. /// - public double Miss { get; protected set; } + /// + /// This miss window should determine how early a hit can be before it is considered for judgement (as opposed to being ignored as + /// "too far in the future). It should also define when a forced miss should be triggered (as a result of no user input in time). + /// + private double miss; /// /// Retrieves the with the largest hit window that produces a successful hit. @@ -66,15 +76,15 @@ namespace osu.Game.Rulesets.Objects } /// - /// Retrieves a mapping of s to their half window timing for all allowed s. + /// Retrieves a mapping of s to their timing windows for all allowed s. /// /// - public IEnumerable<(HitResult result, double length)> GetAllAvailableHalfWindows() + public IEnumerable<(HitResult result, double length)> GetAllAvailableWindows() { for (var result = HitResult.Meh; result <= HitResult.Perfect; ++result) { if (IsHitResultAllowed(result)) - yield return (result, HalfWindowFor(result)); + yield return (result, WindowFor(result)); } } @@ -100,14 +110,39 @@ namespace osu.Game.Rulesets.Objects /// Sets hit windows with values that correspond to a difficulty parameter. /// /// The parameter. - public virtual void SetDifficulty(double difficulty) + public void SetDifficulty(double difficulty) { - Perfect = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Perfect]); - Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]); - Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]); - Ok = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Ok]); - Meh = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Meh]); - Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]); + foreach (var range in GetRanges()) + { + var value = BeatmapDifficulty.DifficultyRange(difficulty, (range.Min, range.Average, range.Max)); + + switch (range.Result) + { + case HitResult.Miss: + miss = value; + break; + + case HitResult.Meh: + meh = value; + break; + + case HitResult.Ok: + ok = value; + break; + + case HitResult.Good: + good = value; + break; + + case HitResult.Great: + great = value; + break; + + case HitResult.Perfect: + perfect = value; + break; + } + } } /// @@ -121,7 +156,7 @@ namespace osu.Game.Rulesets.Objects for (var result = HitResult.Perfect; result >= HitResult.Miss; --result) { - if (IsHitResultAllowed(result) && timeOffset <= HalfWindowFor(result)) + if (IsHitResultAllowed(result) && timeOffset <= WindowFor(result)) return result; } @@ -129,32 +164,32 @@ namespace osu.Game.Rulesets.Objects } /// - /// Retrieves half the hit window for a . - /// This is useful if the hit window for one half of the hittable range of a is required. + /// Retrieves the hit window for a . + /// This is the number of +/- milliseconds allowed for the requested result (so the actual hittable range is double this). /// /// The expected . /// One half of the hit window for . - public double HalfWindowFor(HitResult result) + public double WindowFor(HitResult result) { switch (result) { case HitResult.Perfect: - return Perfect / 2; + return perfect; case HitResult.Great: - return Great / 2; + return great; case HitResult.Good: - return Good / 2; + return good; case HitResult.Ok: - return Ok / 2; + return ok; case HitResult.Meh: - return Meh / 2; + return meh; case HitResult.Miss: - return Miss / 2; + return miss; default: throw new ArgumentException(nameof(result)); @@ -167,6 +202,30 @@ namespace osu.Game.Rulesets.Objects /// /// The time offset. /// Whether the can be hit at any point in the future from this time offset. - public bool CanBeHit(double timeOffset) => timeOffset <= HalfWindowFor(LowestSuccessfulHitResult()); + public bool CanBeHit(double timeOffset) => timeOffset <= WindowFor(LowestSuccessfulHitResult()); + + /// + /// Retrieve a valid list of s representing hit windows. + /// Defaults are provided but can be overridden to customise for a ruleset. + /// + protected virtual DifficultyRange[] GetRanges() => base_ranges; + } + + public struct DifficultyRange + { + public readonly HitResult Result; + + public double Min; + public double Average; + public double Max; + + public DifficultyRange(HitResult result, double min, double average, double max) + { + Result = result; + + Min = min; + Average = average; + Max = max; + } } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 266725a739..d3c37bd4f4 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -11,8 +11,8 @@ using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -using osu.Game.Users; using osu.Game.Rulesets.Scoring; +using osu.Game.Users; namespace osu.Game.Scoring { diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index adda94d629..920d11c910 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Configuration; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD.HitErrorMeters; diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 594dd64e52..03a0f23fb6 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osuTK; using osuTK.Graphics; @@ -146,7 +145,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters private void createColourBars(OsuColour colours) { - var windows = HitWindows.GetAllAvailableHalfWindows().ToArray(); + var windows = HitWindows.GetAllAvailableWindows().ToArray(); maxHitWindow = windows.First().length; diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index da1d9fff0d..dee25048ed 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -3,7 +3,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play.HUD.HitErrorMeters { From 6cdc87bd29cfade223c0fe44fe7c0808a8634c80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 15:37:20 +0900 Subject: [PATCH 2613/2854] Fix allowed results omissions --- osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs | 14 ++++++++++++++ osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs | 13 +++++++++++++ 2 files changed, 27 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs index 8f258c9e3d..a6491bb3f3 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs @@ -15,6 +15,20 @@ namespace osu.Game.Rulesets.Osu.Scoring new DifficultyRange(HitResult.Miss, 200, 200, 200), }; + public override bool IsHitResultAllowed(HitResult result) + { + switch (result) + { + case HitResult.Great: + case HitResult.Good: + case HitResult.Meh: + case HitResult.Miss: + return true; + } + + return false; + } + protected override DifficultyRange[] GetRanges() => osu_ranges; } } diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs index 77d59f9638..9d273392ff 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs @@ -14,6 +14,19 @@ namespace osu.Game.Rulesets.Taiko.Scoring new DifficultyRange(HitResult.Miss, 135, 95, 70), }; + public override bool IsHitResultAllowed(HitResult result) + { + switch (result) + { + case HitResult.Great: + case HitResult.Good: + case HitResult.Miss: + return true; + } + + return false; + } + protected override DifficultyRange[] GetRanges() => taiko_ranges; } } From 4031f51745ae15add836cbeb2d5c9b6116ac37d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 15:37:30 +0900 Subject: [PATCH 2614/2854] More permissive IsHitResultAllow by default --- osu.Game/Rulesets/Scoring/HitWindows.cs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/HitWindows.cs b/osu.Game/Rulesets/Scoring/HitWindows.cs index beba62044a..6d8107cf9c 100644 --- a/osu.Game/Rulesets/Scoring/HitWindows.cs +++ b/osu.Game/Rulesets/Scoring/HitWindows.cs @@ -93,18 +93,7 @@ namespace osu.Game.Rulesets.Scoring /// /// The result type to check. /// Whether the can be achieved. - public virtual bool IsHitResultAllowed(HitResult result) - { - switch (result) - { - case HitResult.Perfect: - case HitResult.Ok: - return false; - - default: - return true; - } - } + public virtual bool IsHitResultAllowed(HitResult result) => true; /// /// Sets hit windows with values that correspond to a difficulty parameter. From 985375d1c64daec3290130d80957ea980ee8b836 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 15:47:01 +0900 Subject: [PATCH 2615/2854] Remove private field xmldoc --- osu.Game/Rulesets/Scoring/HitWindows.cs | 30 ------------------------- 1 file changed, 30 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/HitWindows.cs b/osu.Game/Rulesets/Scoring/HitWindows.cs index 6d8107cf9c..efc4cd9f5c 100644 --- a/osu.Game/Rulesets/Scoring/HitWindows.cs +++ b/osu.Game/Rulesets/Scoring/HitWindows.cs @@ -23,41 +23,11 @@ namespace osu.Game.Rulesets.Scoring new DifficultyRange(HitResult.Miss, 188, 173, 158), }; - /// - /// Hit window for a result. - /// private double perfect; - - /// - /// Hit window for a result. - /// - /// - /// Note that this value includes both the early and late region. - /// private double great; - - /// - /// Hit window for a result. - /// private double good; - - /// - /// Hit window for an result. - /// private double ok; - - /// - /// Hit window for a result. - /// private double meh; - - /// - /// Hit window for a result. - /// - /// - /// This miss window should determine how early a hit can be before it is considered for judgement (as opposed to being ignored as - /// "too far in the future). It should also define when a forced miss should be triggered (as a result of no user input in time). - /// private double miss; /// From b89fb5cdf7ca5e5b1ac8932ed6b3e79fb7a8bedd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 16:51:30 +0900 Subject: [PATCH 2616/2854] Fix failing test --- .../Visual/Gameplay/TestSceneFailJudgement.cs | 10 +--------- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 9 +++++++-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs index bb0901524f..d57ec44f39 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs @@ -26,15 +26,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddUntilStep("wait for fail", () => Player.HasFailed); AddUntilStep("wait for multiple judged objects", () => ((FailPlayer)Player).DrawableRuleset.Playfield.AllHitObjects.Count(h => h.AllJudged) > 1); - AddAssert("total judgements == 1", () => - { - int count = 0; - - foreach (var stat in (HitResult[])Enum.GetValues(typeof(HitResult))) - count += ((FailPlayer)Player).ScoreProcessor.GetStatistic(stat); - - return count == 1; - }); + AddAssert("total judgements == 1", () => ((FailPlayer)Player).ScoreProcessor.JudgedHits == 1); } private class FailPlayer : ReplayPlayer diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 9f2c79df33..e4f20c27b4 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -92,6 +92,11 @@ namespace osu.Game.Rulesets.Scoring /// public virtual bool HasCompleted => false; + /// + /// The total number of judged s at the current point in time. + /// + public int JudgedHits { get; protected set; } + /// /// Whether this ScoreProcessor has already triggered the failed state. /// @@ -142,6 +147,8 @@ namespace osu.Game.Rulesets.Scoring Rank.Value = ScoreRank.X; HighestCombo.Value = 0; + JudgedHits = 0; + HasFailed = false; } @@ -208,7 +215,6 @@ namespace osu.Game.Rulesets.Scoring public sealed override bool HasCompleted => JudgedHits == MaxHits; protected int MaxHits { get; private set; } - protected int JudgedHits { get; private set; } private double maxHighestCombo; @@ -441,7 +447,6 @@ namespace osu.Game.Rulesets.Scoring base.Reset(storeResults); - JudgedHits = 0; baseScore = 0; rollingMaxBaseScore = 0; bonusScore = 0; From 88d0756107f6e2c0a1955f64a68de6553d680631 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 17:17:30 +0900 Subject: [PATCH 2617/2854] Allow global actions to propagate through pause screen --- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index c5202fa792..f54d638584 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -313,5 +313,22 @@ namespace osu.Game.Screens.Play return base.OnMouseMove(e); } } + + [Resolved] + private GlobalActionContainer globalAction { get; set; } + + protected override bool Handle(UIEvent e) + { + switch (e) + { + case ScrollEvent _: + if (ReceivePositionalInputAt(e.ScreenSpaceMousePosition)) + return globalAction.TriggerEvent(e); + + break; + } + + return base.Handle(e); + } } } From 95baae5088de6333deebece25b66c42869793a7a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 18:27:54 +0900 Subject: [PATCH 2618/2854] Fix dragging from inside to outside an overlay incorrectly hiding --- .../Containers/OsuFocusedOverlayContainer.cs | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 9c948d6f90..a4121acfca 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -62,21 +62,31 @@ namespace osu.Game.Graphics.Containers protected override bool OnClick(ClickEvent e) { - closeIfOutside(e); + if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)) + Hide(); return base.OnClick(e); } - protected override bool OnDragEnd(DragEndEvent e) - { - closeIfOutside(e); - return base.OnDragEnd(e); - } + private bool closeOnDragEnd; - private void closeIfOutside(MouseEvent e) + protected override bool OnDragStart(DragStartEvent e) { if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)) + closeOnDragEnd = true; + + return base.OnDragStart(e); + } + + protected override bool OnDragEnd(DragEndEvent e) + { + if (closeOnDragEnd) + { Hide(); + closeOnDragEnd = false; + } + + return base.OnDragEnd(e); } public virtual bool OnPressed(GlobalAction action) From 374479f837b3e56e527b6682d9d7234f78ea4adf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 19:00:49 +0900 Subject: [PATCH 2619/2854] Add truncatino of long usernames in chat --- .../Online/TestSceneChatLineTruncation.cs | 108 ++++++++++++++++++ osu.Game/Overlays/Chat/ChatLine.cs | 10 +- 2 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs new file mode 100644 index 0000000000..888e55ab0a --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs @@ -0,0 +1,108 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Containers; +using osu.Game.Online.Chat; +using osu.Game.Overlays.Chat; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Online +{ + [TestFixture] + public class TestSceneChatLineTruncation : OsuTestScene + { + private readonly TestChatLineContainer textContainer; + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ChatLine), + typeof(Message), + typeof(LinkFlowContainer), + typeof(MessageFormatter) + }; + + public TestSceneChatLineTruncation() + { + Add(textContainer = new TestChatLineContainer + { + Padding = new MarginPadding { Left = 20, Right = 20 }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + }); + } + + [BackgroundDependencyLoader] + private void load() + { + testFormatting(); + } + + private void clear() => AddStep("clear messages", textContainer.Clear); + + private void addMessageWithChecks(string text, bool isAction = false, bool isImportant = false, string username = null) + { + int index = textContainer.Count + 1; + var newLine = new ChatLine(new DummyMessage(text, isAction, isImportant, index, username)); + textContainer.Add(newLine); + } + + private void testFormatting() + { + for (int a = 0; a < 25; a++) + addMessageWithChecks($"Wide {a} character username.", username: new string('w', a)); + addMessageWithChecks("Short name with spaces.", username: "sho rt name"); + addMessageWithChecks("Long name with spaces.", username: "long name with s p a c e s"); + } + + private class DummyMessage : Message + { + private static long messageCounter; + + internal static readonly User TEST_SENDER_BACKGROUND = new User + { + Username = @"i-am-important", + Id = 42, + Colour = "#250cc9", + }; + + internal static readonly User TEST_SENDER = new User + { + Username = @"Somebody", + Id = 1, + }; + + public new DateTimeOffset Timestamp = DateTimeOffset.Now; + + public DummyMessage(string text, bool isAction = false, bool isImportant = false, int number = 0, string username = null) + : base(messageCounter++) + { + Content = text; + IsAction = isAction; + Sender = new User + { + Username = username ?? $"user {number}", + Id = number, + Colour = isImportant ? "#250cc9" : null, + }; + } + } + + private class TestChatLineContainer : FillFlowContainer + { + protected override int Compare(Drawable x, Drawable y) + { + var xC = (ChatLine)x; + var yC = (ChatLine)y; + + return xC.Message.CompareTo(yC.Message); + } + } + } +} diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 2576b38ec8..a07b6472a3 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -31,6 +31,8 @@ namespace osu.Game.Overlays.Chat protected virtual float MessagePadding => default_message_padding; + private const float timestamp_padding = 70; + private const float default_horizontal_padding = 15; protected virtual float HorizontalPadding => default_horizontal_padding; @@ -87,7 +89,10 @@ namespace osu.Game.Overlays.Chat { Shadow = false, Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length], - Font = OsuFont.GetFont(size: TextSize, weight: FontWeight.Bold, italics: true) + Truncate = true, + EllipsisString = ".. :", + Font = OsuFont.GetFont(size: TextSize, weight: FontWeight.Bold, italics: true), + RelativeSizeAxes = Axes.Both, }; if (hasBackground) @@ -141,7 +146,8 @@ namespace osu.Game.Overlays.Chat }, new MessageSender(message.Sender) { - AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = timestamp_padding }, + RelativeSizeAxes = Axes.Both, Origin = Anchor.TopRight, Anchor = Anchor.TopRight, Child = effectedUsername, From c6b8f2db77313f29bdfa106a042a9ba211b009fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 19:05:50 +0900 Subject: [PATCH 2620/2854] Update historic licence header --- osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs | 4 ++-- osu.Game/Overlays/Chat/ChatLine.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs index 888e55ab0a..4773e84a5e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index a07b6472a3..d812e007a0 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Chat protected virtual float MessagePadding => default_message_padding; - private const float timestamp_padding = 70; + private const float timestamp_padding = 70; private const float default_horizontal_padding = 15; From 7f2d14416a16d7b849e7c9be682bd4f84493c024 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 7 Sep 2019 14:44:44 +0900 Subject: [PATCH 2621/2854] Reset DrawableHitObject lifetimes on state change --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index db87d4b4f2..e3390c8cf0 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -153,6 +153,9 @@ namespace osu.Game.Rulesets.Objects.Drawables if (UseTransformStateManagement) { + lifetimeStart = null; + LifetimeEnd = double.MaxValue; + double transformTime = HitObject.StartTime - InitialLifetimeOffset; base.ApplyTransformsAt(transformTime, true); From 55b2bc1ed5b4466ef0a21cf3faadacb76b6dc951 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 7 Sep 2019 18:03:04 +0300 Subject: [PATCH 2622/2854] Set Health default value to 1 --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index e4f20c27b4..f350eef146 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Scoring /// /// The current health. /// - public readonly BindableDouble Health = new BindableDouble { MinValue = 0, MaxValue = 1 }; + public readonly BindableDouble Health = new BindableDouble(1) { MinValue = 0, MaxValue = 1 }; /// /// The current combo. From c397ab62821a91ccca252b2e627f90f9c89bdc1e Mon Sep 17 00:00:00 2001 From: miterosan Date: Sat, 7 Sep 2019 17:04:13 +0200 Subject: [PATCH 2623/2854] Fix Android Builds. --- osu.Android.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index adc340a734..03c6889a69 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -14,7 +14,7 @@ v9.0 false - + True portable False @@ -30,12 +30,12 @@ armeabi-v7a;x86;arm64-v8a true - + false None True prompt - true + true false SdkOnly False From fdd36874371909a1dd4391dbf828bb015cf441cc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 8 Sep 2019 00:09:24 +0900 Subject: [PATCH 2624/2854] Fix catcher additive sprites staying on screen during rewind --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index ceda643335..592a45c865 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -197,6 +197,7 @@ namespace osu.Game.Rulesets.Catch.UI additive.Anchor = Anchor; additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly. + additive.LifetimeStart = Clock.CurrentTime; additive.Position = Position; additive.Scale = Scale; additive.Colour = HyperDashing ? Color4.Red : Color4.White; From ec7a50b75f946453c76d351e181162136a7d88b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 8 Sep 2019 00:10:31 +0900 Subject: [PATCH 2625/2854] Fix already caught osu!catch objects not correctly disappearing --- .../Objects/Drawable/DrawableCatchHitObject.cs | 4 ++++ osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 13 ++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs index 00734810b3..ce90319846 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -50,6 +50,10 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable public Func CheckPosition; + public bool IsOnPlate; + + public override bool RemoveWhenNotAlive => IsOnPlate; + protected override void CheckForResult(bool userTriggered, double timeOffset) { if (CheckPosition == null) return; diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 592a45c865..330f6e6b24 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -69,6 +69,7 @@ namespace osu.Game.Rulesets.Catch.UI caughtFruit.RelativePositionAxes = Axes.None; caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0); + caughtFruit.IsOnPlate = true; caughtFruit.Anchor = Anchor.TopCentre; caughtFruit.Origin = Anchor.Centre; @@ -384,6 +385,12 @@ namespace osu.Game.Rulesets.Catch.UI X = hyperDashTargetPosition; SetHyperDashState(); } + + if (Clock.ElapsedFrameTime < 0) + { + AdditiveTarget.RemoveAll(d => Clock.CurrentTime < d.LifetimeStart); + caughtFruit.RemoveAll(d => d.HitObject.StartTime > Clock.CurrentTime); + } } /// @@ -407,7 +414,7 @@ namespace osu.Game.Rulesets.Catch.UI f.MoveToY(f.Y + 75, 750, Easing.InSine); f.FadeOut(750); - f.Expire(); + f.Expire(true); } } @@ -437,11 +444,11 @@ namespace osu.Game.Rulesets.Catch.UI ExplodingFruitTarget.Add(fruit); } + fruit.ClearTransforms(); fruit.MoveToY(fruit.Y - 50, 250, Easing.OutSine).Then().MoveToY(fruit.Y + 50, 500, Easing.InSine); fruit.MoveToX(fruit.X + originalX * 6, 1000); fruit.FadeOut(750); - - fruit.Expire(); + fruit.Expire(true); } } } From be803fa9217d3cc5ecc808d32a0c603e7278ad5b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 7 Sep 2019 18:15:49 +0300 Subject: [PATCH 2626/2854] Reset score processor before starting the simulation --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index f350eef146..4ca9ddd183 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -269,6 +269,8 @@ namespace osu.Game.Rulesets.Scoring /// The to simulate. protected virtual void SimulateAutoplay(Beatmap beatmap) { + Reset(false); + foreach (var obj in beatmap.HitObjects) simulate(obj); From 6581e51d6af714cae1763a1f28d79013f9756a25 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sat, 7 Sep 2019 18:03:33 +0200 Subject: [PATCH 2627/2854] Add a default for the configuration into the android props. This allows building even if no configuration is specified. --- osu.Android.props | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Android.props b/osu.Android.props index 03c6889a69..4962064853 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -1,5 +1,6 @@ + Debug bin\$(Configuration) 4 2.0 From 3435e2a8d3f2e7ab5ef9c3cf0090c72dbe0d0f21 Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Sun, 8 Sep 2019 13:36:58 +0800 Subject: [PATCH 2628/2854] open login on enter main menu --- osu.Game/Screens/Menu/MainMenu.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 499b5089f6..160ff95632 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -11,6 +11,7 @@ using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Charts; @@ -44,6 +45,12 @@ namespace osu.Game.Screens.Menu [Resolved(canBeNull: true)] private MusicController music { get; set; } + [Resolved(canBeNull: true)] + private LoginOverlay login { get; set; } + + [Resolved] + private IAPIProvider api { get; set; } + private BackgroundScreenDefault background; protected override BackgroundScreen CreateBackground() => background; @@ -128,6 +135,9 @@ namespace osu.Game.Screens.Menu track.Seek(metadata.PreviewTime != -1 ? metadata.PreviewTime : 0.4f * track.Length); track.Start(); } + + if (api?.State == APIState.Offline) + login?.ToggleVisibility(); } Beatmap.ValueChanged += beatmap_ValueChanged; From a67a2899a9d763b94f8b3e689fbeb6a5bc323cde Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Sun, 8 Sep 2019 16:18:15 +0800 Subject: [PATCH 2629/2854] move api state check to it's own clause --- osu.Game/Screens/Menu/MainMenu.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 160ff95632..10b2f827af 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -135,11 +135,11 @@ namespace osu.Game.Screens.Menu track.Seek(metadata.PreviewTime != -1 ? metadata.PreviewTime : 0.4f * track.Length); track.Start(); } - - if (api?.State == APIState.Offline) - login?.ToggleVisibility(); } + if (last is IntroScreen && api?.State == APIState.Offline) + login?.ToggleVisibility(); + Beatmap.ValueChanged += beatmap_ValueChanged; } From d790656b7ed9feee90fd419c56e85797aa3f9e3a Mon Sep 17 00:00:00 2001 From: miterosan Date: Sun, 8 Sep 2019 15:33:16 +0200 Subject: [PATCH 2630/2854] Revert shortening the condition --- osu.Android.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 4962064853..a84b877a98 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -15,7 +15,7 @@ v9.0 false - + True portable False @@ -31,7 +31,7 @@ armeabi-v7a;x86;arm64-v8a true - + false None True From 8862cdab9c719a093d47c49dd9be546fc655f8eb Mon Sep 17 00:00:00 2001 From: miterosan Date: Sun, 8 Sep 2019 15:33:51 +0200 Subject: [PATCH 2631/2854] Also set the default for Platform --- osu.Android.props | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Android.props b/osu.Android.props index a84b877a98..896b10133d 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -1,6 +1,7 @@ Debug + AnyCPU bin\$(Configuration) 4 2.0 From 9951011e6c5d469f149ee9b0d08405f0afb6a250 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sun, 8 Sep 2019 16:27:25 +0200 Subject: [PATCH 2632/2854] Remove not needed androidnativelibrary itemgroup --- osu.Android.props | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Android.props b/osu.Android.props index adc340a734..51bfdca064 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -49,7 +49,6 @@ osu.licenseheader - From be4f0cc2dd19c8adfced41dd82731c22a951a523 Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Mon, 9 Sep 2019 06:14:49 +0800 Subject: [PATCH 2633/2854] remove null conditional --- osu.Game/Screens/Menu/MainMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 10b2f827af..276c653345 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -137,7 +137,7 @@ namespace osu.Game.Screens.Menu } } - if (last is IntroScreen && api?.State == APIState.Offline) + if (last is IntroScreen && api.State == APIState.Offline) login?.ToggleVisibility(); Beatmap.ValueChanged += beatmap_ValueChanged; From eeebd517f3e5cafc0245e60668407d1bb131f5e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Sep 2019 12:08:59 +0900 Subject: [PATCH 2634/2854] Use MaxWidth specification --- osu.Game/Overlays/Chat/ChatLine.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index d812e007a0..4c37d626c0 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Chat protected virtual float MessagePadding => default_message_padding; - private const float timestamp_padding = 70; + private const float timestamp_padding = 65; private const float default_horizontal_padding = 15; @@ -92,7 +92,9 @@ namespace osu.Game.Overlays.Chat Truncate = true, EllipsisString = ".. :", Font = OsuFont.GetFont(size: TextSize, weight: FontWeight.Bold, italics: true), - RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + MaxWidth = default_message_padding - timestamp_padding }; if (hasBackground) From 04a4f9c9a347f45549233517479a35bf4f864f85 Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Mon, 9 Sep 2019 11:26:51 +0800 Subject: [PATCH 2635/2854] use IsLoggedIn and remove useless clause --- osu.Game/Screens/Menu/MainMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 276c653345..79a3993874 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -137,7 +137,7 @@ namespace osu.Game.Screens.Menu } } - if (last is IntroScreen && api.State == APIState.Offline) + if (!api.IsLoggedIn) login?.ToggleVisibility(); Beatmap.ValueChanged += beatmap_ValueChanged; From 7adfae37843adc5a225244bb76a7b433a8c9db7a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Sep 2019 12:35:15 +0900 Subject: [PATCH 2636/2854] Reorder CursorTrail members --- .../UI/Cursor/CursorTrail.cs | 51 ++++++++----------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 05eb0ffdbf..a50c3a2fea 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -22,28 +22,14 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { internal class CursorTrail : Drawable, IRequireHighFrequencyMousePosition { - private int currentIndex; - - private IShader shader; - private Texture texture; - - private Vector2 size => texture.Size * Scale; - - private double timeOffset; - - private float time; - - public override bool IsPresent => true; - private const int max_sprites = 2048; private readonly TrailPart[] parts = new TrailPart[max_sprites]; - - private Vector2? lastPosition; - - private readonly InputResampler resampler = new InputResampler(); - - protected override DrawNode CreateDrawNode() => new TrailDrawNode(this); + private int currentIndex; + private IShader shader; + private Texture texture; + private double timeOffset; + private float time; public CursorTrail() { @@ -60,8 +46,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor } } - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - [BackgroundDependencyLoader] private void load(ShaderManager shaders, TextureStore textures) { @@ -76,6 +60,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor resetTime(); } + public override bool IsPresent => true; + protected override void Update() { base.Update(); @@ -101,6 +87,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor timeOffset = Time.Current; } + private Vector2 size => texture.Size * Scale; + + private Vector2? lastPosition; + private readonly InputResampler resampler = new InputResampler(); + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + protected override bool OnMouseMove(MouseMoveEvent e) { Vector2 pos = e.ScreenSpaceMousePosition; @@ -127,21 +120,19 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor for (float d = interval; d < distance; d += interval) { lastPosition = pos1 + direction * d; - addPosition(lastPosition.Value); + + parts[currentIndex].Position = lastPosition.Value; + parts[currentIndex].Time = time; + ++parts[currentIndex].InvalidationID; + + currentIndex = (currentIndex + 1) % max_sprites; } } return base.OnMouseMove(e); } - private void addPosition(Vector2 pos) - { - parts[currentIndex].Position = pos; - parts[currentIndex].Time = time; - ++parts[currentIndex].InvalidationID; - - currentIndex = (currentIndex + 1) % max_sprites; - } + protected override DrawNode CreateDrawNode() => new TrailDrawNode(this); private struct TrailPart { From af09ed1b7fe648930f4bc084e2dd00b7307cf1e1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Sep 2019 12:48:34 +0900 Subject: [PATCH 2637/2854] Make cursor test scene more automated --- .../TestSceneGameplayCursor.cs | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index ebb6cd3a5a..f4bc172d9c 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -6,7 +6,9 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Testing.Input; using osu.Game.Rulesets.Osu.UI.Cursor; +using osuTK; namespace osu.Game.Rulesets.Osu.Tests { @@ -18,11 +20,35 @@ namespace osu.Game.Rulesets.Osu.Tests [BackgroundDependencyLoader] private void load() { - SetContents(() => new OsuCursorContainer + SetContents(() => new MovingCursorInputManager { - RelativeSizeAxes = Axes.Both, - Masking = true, + Child = new OsuCursorContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + } }); } + + private class MovingCursorInputManager : ManualInputManager + { + public MovingCursorInputManager() + { + UseParentInput = false; + } + + protected override void Update() + { + base.Update(); + + const double spin_duration = 5000; + double currentTime = Time.Current; + + double angle = (currentTime % spin_duration) / spin_duration * 2 * Math.PI; + Vector2 rPos = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)); + + MoveMouseTo(ToScreenSpace(DrawSize / 2 + DrawSize / 3 * rPos)); + } + } } } From 74440dcfdcc35fa847ecafe1d577f618a7d2b34b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Sep 2019 13:01:40 +0900 Subject: [PATCH 2638/2854] Make the cursors click every so often --- .../TestSceneGameplayCursor.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index f4bc172d9c..f50b935477 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Tests { SetContents(() => new MovingCursorInputManager { - Child = new OsuCursorContainer + Child = new ClickingCursorContainer { RelativeSizeAxes = Axes.Both, Masking = true, @@ -30,6 +30,21 @@ namespace osu.Game.Rulesets.Osu.Tests }); } + private class ClickingCursorContainer : OsuCursorContainer + { + protected override void Update() + { + base.Update(); + + double currentTime = Time.Current; + + if (((int)(currentTime / 1000)) % 2 == 0) + OnPressed(OsuAction.LeftButton); + else + OnReleased(OsuAction.LeftButton); + } + } + private class MovingCursorInputManager : ManualInputManager { public MovingCursorInputManager() From 07fce8397bdd3bb4cae902a65196ad5cd01301fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Sep 2019 14:24:17 +0900 Subject: [PATCH 2639/2854] Move reset call to ctor --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 4ca9ddd183..18c2a2ca01 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -233,6 +233,8 @@ namespace osu.Game.Rulesets.Scoring drawableRuleset.OnRevertResult += revertResult; ApplyBeatmap(drawableRuleset.Beatmap); + + Reset(false); SimulateAutoplay(drawableRuleset.Beatmap); Reset(true); @@ -269,8 +271,6 @@ namespace osu.Game.Rulesets.Scoring /// The to simulate. protected virtual void SimulateAutoplay(Beatmap beatmap) { - Reset(false); - foreach (var obj in beatmap.HitObjects) simulate(obj); From c2353cbdfa7193941784799fd8d9bd473d542c27 Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Mon, 9 Sep 2019 13:30:48 +0800 Subject: [PATCH 2640/2854] move logic to logo action --- osu.Game/Screens/Menu/MainMenu.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 79a3993874..e85d59fc72 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -39,6 +39,8 @@ namespace osu.Game.Screens.Menu private ButtonSystem buttons; + private bool loginPrompted = false; + [Resolved] private GameHost host { get; set; } @@ -137,9 +139,6 @@ namespace osu.Game.Screens.Menu } } - if (!api.IsLoggedIn) - login?.ToggleVisibility(); - Beatmap.ValueChanged += beatmap_ValueChanged; } @@ -152,6 +151,16 @@ namespace osu.Game.Screens.Menu logo.FadeColour(Color4.White, 100, Easing.OutQuint); logo.FadeIn(100, Easing.OutQuint); + logo.Action += () => + { + if (!api.IsLoggedIn && !loginPrompted) + login?.ToggleVisibility(); + + loginPrompted = true; + + return true; + }; + if (resuming) { buttons.State = ButtonSystemState.TopLevel; From ff49c4ae98622e5a05d8d8e3b122a674d1b28ccb Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Mon, 9 Sep 2019 13:50:14 +0800 Subject: [PATCH 2641/2854] remove redundancies --- osu.Game/Screens/Menu/MainMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index e85d59fc72..4c3566b3e9 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Menu private ButtonSystem buttons; - private bool loginPrompted = false; + private bool loginPrompted; [Resolved] private GameHost host { get; set; } From 5b692915be98de0c3d17399f96b2dddcb4b472bf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Sep 2019 17:03:14 +0900 Subject: [PATCH 2642/2854] Add required type --- osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index f50b935477..aa170eae1e 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -15,7 +15,11 @@ namespace osu.Game.Rulesets.Osu.Tests [TestFixture] public class TestSceneGameplayCursor : SkinnableTestScene { - public override IReadOnlyList RequiredTypes => new[] { typeof(CursorTrail) }; + public override IReadOnlyList RequiredTypes => new[] + { + typeof(OsuCursorContainer), + typeof(CursorTrail) + }; [BackgroundDependencyLoader] private void load() From 81bb8d9bc4d2f606bc2f4b4211a33b0a0345c906 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Sep 2019 17:05:03 +0900 Subject: [PATCH 2643/2854] Make SkinnabbleTestScene use stored classic skin --- .../default-skin/approachcircle@2x.png | Bin 18164 -> 0 bytes .../Resources/default-skin/cursor@2x.png | Bin 28063 -> 0 bytes .../Resources/default-skin/cursormiddle@2x.png | Bin 7676 -> 0 bytes .../Resources/default-skin/hit0@2x.png | Bin 16112 -> 0 bytes .../Resources/default-skin/hit100@2x.png | Bin 31228 -> 0 bytes .../Resources/default-skin/hit100k@2x.png | Bin 21318 -> 0 bytes .../Resources/default-skin/hit300@2x.png | Bin 36873 -> 0 bytes .../Resources/default-skin/hit300g@2x.png | Bin 39840 -> 0 bytes .../Resources/default-skin/hit300k@2x.png | Bin 29098 -> 0 bytes .../Resources/default-skin/hit50@2x.png | Bin 26015 -> 0 bytes .../Resources/default-skin/hitcircle@2x.png | Bin 7768 -> 0 bytes .../default-skin/hitcircleoverlay@2x.png | Bin 45901 -> 0 bytes .../Resources/default-skin/sliderb-nd@2x.png | Bin 14258 -> 0 bytes .../Resources/default-skin/sliderb-spec@2x.png | Bin 13141 -> 0 bytes .../Resources/default-skin/sliderb0@2x.png | Bin 17053 -> 0 bytes .../Resources/default-skin/sliderb1@2x.png | Bin 17792 -> 0 bytes .../Resources/default-skin/sliderb2@2x.png | Bin 18268 -> 0 bytes .../Resources/default-skin/sliderb3@2x.png | Bin 18182 -> 0 bytes .../Resources/default-skin/sliderb4@2x.png | Bin 18062 -> 0 bytes .../Resources/default-skin/sliderb5@2x.png | Bin 16895 -> 0 bytes .../Resources/default-skin/sliderb6@2x.png | Bin 16702 -> 0 bytes .../Resources/default-skin/sliderb7@2x.png | Bin 17139 -> 0 bytes .../Resources/default-skin/sliderb8@2x.png | Bin 17084 -> 0 bytes .../Resources/default-skin/sliderb9@2x.png | Bin 17067 -> 0 bytes .../SkinnableTestScene.cs | 4 ++-- 25 files changed, 2 insertions(+), 2 deletions(-) delete mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/approachcircle@2x.png delete mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursor@2x.png delete mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursormiddle@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit0@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit100@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit100k@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300g@2x.png delete mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300k@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit50@2x.png delete mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hitcircle@2x.png delete mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hitcircleoverlay@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-nd@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-spec@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb0@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb1@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb2@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb3@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb4@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb5@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb6@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb7@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb8@2x.png delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb9@2x.png diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/approachcircle@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/approachcircle@2x.png deleted file mode 100755 index db2f4a5730b80c3488c618fe825e322742e4007c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18164 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
xUBq)wpYuSo%L4;iD|W&d3&2irtb$J#-rmG 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 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursormiddle@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursormiddle@2x.png deleted file mode 100755 index ebf59c18ba9f62390f49be4bed696a997a51f05c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7676 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 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit100@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit100@2x.png deleted file mode 100644 index 7db8eb312403e739d8dc61b280f930443ad0d81c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31228 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 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300@2x.png deleted file mode 100644 index 2c7c07852ff6e84edc2164b4487aaaa6cb4d8fe4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36873 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%0DE1j{G{fQ$Fey0U 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 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300g@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300g@2x.png deleted file mode 100644 index 1ce746e3a494f4446cc451f5924240f4c8eecb4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39840 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%0DP1I3KX^ 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 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-spec@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-spec@2x.png deleted file mode 100644 index 76fd9ab16807c2ef61dd2aa0db47c78262db03b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13141 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 O@*I3?M71L9&Hn(Rv4%VV diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb1@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb1@2x.png deleted file mode 100644 index e99f076947aa7ca1614fe825839dd3d7b8dcc303..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17792 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 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb2@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb2@2x.png deleted file mode 100644 index cd36a0ae169e1d876012402b2b7aa34a13a179d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18268 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 SqgKcO0000l1{&5@md}*`-!Dp9 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 Y0Z!VEjc(%0RR91007*qoM6N<$f_m)to&W#< diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb4@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb4@2x.png deleted file mode 100644 index a5b19887d64f0040da387029545dea15d85669c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18062 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 Hu0mjf{m45K diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb5@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb5@2x.png deleted file mode 100644 index 4bb01f0e88229c6433c6363d7f7c725776372683..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16895 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

{E0 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 E0O3MVi~s-t diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb6@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb6@2x.png deleted file mode 100644 index 859e0aa4c1f0d548eda3fe4c6c02f7fc4af1831f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16702 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 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb8@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb8@2x.png deleted file mode 100644 index fcdf4ed4a4a76808dbfe41e54a45d4433dc1320b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17084 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 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb9@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb9@2x.png deleted file mode 100644 index c990cf0fe6784265e2419070a27459fa6f7b2fa0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17067 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, "Resources/metrics_skin"), audio, true); - defaultSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore(dllStore, "Resources/default_skin"), audio, false); + defaultSkin = skinManager.GetSkin(DefaultLegacySkin.Info); specialSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore(dllStore, "Resources/special_skin"), audio, true); } From 55afcc1e04ef4d48569cf7247195a32a15d0ec72 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Sep 2019 17:53:51 +0900 Subject: [PATCH 2644/2854] Add skin component for the legacy cursor trail --- osu.Game.Rulesets.Osu/OsuSkinComponents.cs | 1 + .../Skinning/LegacyCursorTrail.cs | 24 +++++++++++++++++++ .../Skinning/OsuLegacySkinTransformer.cs | 6 +++++ .../UI/Cursor/CursorTrail.cs | 22 +++++++++++++---- .../UI/Cursor/OsuCursorContainer.cs | 20 ++++++++++++---- 5 files changed, 63 insertions(+), 10 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs index 5971f053c2..8dd48eace0 100644 --- a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs +++ b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs @@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Osu HitCircle, FollowPoint, Cursor, + CursorTrail, SliderScorePoint, ApproachCircle, ReverseArrow, diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs new file mode 100644 index 0000000000..74746faa44 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Osu.UI.Cursor; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Osu.Skinning +{ + public class LegacyCursorTrail : CursorTrail + { + public LegacyCursorTrail() + { + Blending = BlendingParameters.Additive; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + Texture = skin.GetTexture("cursortrail"); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 5957b81d7e..b5475f50ef 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -78,6 +78,12 @@ namespace osu.Game.Rulesets.Osu.Skinning return null; + case OsuSkinComponents.CursorTrail: + if (source.GetTexture("cursortrail") != null) + return new LegacyCursorTrail(); + + return null; + case OsuSkinComponents.HitCircleText: var font = GetConfig(OsuSkinConfiguration.HitCircleFont)?.Value ?? "default"; var overlap = GetConfig(OsuSkinConfiguration.HitCircleOverlap)?.Value ?? 0; diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index a50c3a2fea..acb7fb8251 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -20,14 +20,28 @@ using osuTK.Graphics.ES30; namespace osu.Game.Rulesets.Osu.UI.Cursor { - internal class CursorTrail : Drawable, IRequireHighFrequencyMousePosition + public class CursorTrail : Drawable, IRequireHighFrequencyMousePosition { private const int max_sprites = 2048; + private Texture texture = Texture.WhitePixel; + + public Texture Texture + { + get => texture; + set + { + if (texture == value) + return; + + texture = value; + Invalidate(Invalidation.DrawNode); + } + } + private readonly TrailPart[] parts = new TrailPart[max_sprites]; private int currentIndex; private IShader shader; - private Texture texture; private double timeOffset; private float time; @@ -47,11 +61,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor } [BackgroundDependencyLoader] - private void load(ShaderManager shaders, TextureStore textures) + private void load(ShaderManager shaders) { shader = shaders.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE); - texture = textures.Get(@"Cursor/cursortrail"); - Scale = new Vector2(1 / texture.ScaleAdjust); } protected override void LoadComplete() diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index 893c7875fa..5d68d200ff 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -6,9 +6,12 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Textures; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.UI; +using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Osu.UI.Cursor { @@ -22,17 +25,14 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private readonly Bindable showTrail = new Bindable(true); - private readonly CursorTrail cursorTrail; + private readonly Drawable cursorTrail; public OsuCursorContainer() { InternalChild = fadeContainer = new Container { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - cursorTrail = new CursorTrail { Depth = 1 } - } + Child = cursorTrail = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail()) }; } @@ -98,5 +98,15 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint); ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint); } + + private class DefaultCursorTrail : CursorTrail + { + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + Texture = textures.Get(@"Cursor/cursortrail"); + Scale = new Vector2(1 / Texture.ScaleAdjust); + } + } } } From 2d636ce1472a9e19fa15698a012ac1b60b775f59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Sep 2019 17:54:53 +0900 Subject: [PATCH 2645/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 896b10133d..f76297c197 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5f2aad24dc..791d2fe285 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 5027a4ef8c..0560c45edf 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -118,8 +118,8 @@ - - + + From 195f101799d4ef63af86fdda28df71b6a5d7b52d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Sep 2019 19:00:42 +0900 Subject: [PATCH 2646/2854] Move complex property below ctor --- .../UI/Cursor/CursorTrail.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index acb7fb8251..8164816f9f 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -24,21 +24,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { private const int max_sprites = 2048; - private Texture texture = Texture.WhitePixel; - - public Texture Texture - { - get => texture; - set - { - if (texture == value) - return; - - texture = value; - Invalidate(Invalidation.DrawNode); - } - } - private readonly TrailPart[] parts = new TrailPart[max_sprites]; private int currentIndex; private IShader shader; @@ -72,6 +57,21 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor resetTime(); } + private Texture texture = Texture.WhitePixel; + + public Texture Texture + { + get => texture; + set + { + if (texture == value) + return; + + texture = value; + Invalidate(Invalidation.DrawNode); + } + } + public override bool IsPresent => true; protected override void Update() From 1d225ba81e627128d14e16eb2aef9c34304d6f3d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Sep 2019 19:02:10 +0900 Subject: [PATCH 2647/2854] Add FadeDuration to control cursor trail fade --- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 8164816f9f..91bc3278bf 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -72,6 +72,11 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor } } + ///

+ /// The amount of time to fade the cursor trail pieces. + /// + protected virtual double FadeDuration => 300; + public override bool IsPresent => true; protected override void Update() @@ -82,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor const int fade_clock_reset_threshold = 1000000; - time = (float)(Time.Current - timeOffset) / 300f; + time = (float)((Time.Current - timeOffset) / FadeDuration); if (time > fade_clock_reset_threshold) resetTime(); } From 3b1b7910bbf50447b82b8d5ba7bce555255ad0a1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Sep 2019 19:22:27 +0900 Subject: [PATCH 2648/2854] Add toggle for cursor trail interpolation --- .../UI/Cursor/CursorTrail.cs | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 91bc3278bf..0998b5d604 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -106,6 +106,11 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private Vector2 size => texture.Size * Scale; + /// + /// Whether to interpolate mouse movements and add trail pieces at intermediate points. + /// + protected virtual bool InterpolateMovements => true; + private Vector2? lastPosition; private readonly InputResampler resampler = new InputResampler(); @@ -126,29 +131,41 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { Trace.Assert(lastPosition.HasValue); - // ReSharper disable once PossibleInvalidOperationException - Vector2 pos1 = lastPosition.Value; - Vector2 diff = pos2 - pos1; - float distance = diff.Length; - Vector2 direction = diff / distance; - - float interval = size.X / 2 * 0.9f; - - for (float d = interval; d < distance; d += interval) + if (InterpolateMovements) { - lastPosition = pos1 + direction * d; + // ReSharper disable once PossibleInvalidOperationException + Vector2 pos1 = lastPosition.Value; + Vector2 diff = pos2 - pos1; + float distance = diff.Length; + Vector2 direction = diff / distance; - parts[currentIndex].Position = lastPosition.Value; - parts[currentIndex].Time = time; - ++parts[currentIndex].InvalidationID; + float interval = size.X / 2 * 0.9f; - currentIndex = (currentIndex + 1) % max_sprites; + for (float d = interval; d < distance; d += interval) + { + lastPosition = pos1 + direction * d; + addPart(lastPosition.Value); + } + } + else + { + lastPosition = pos2; + addPart(lastPosition.Value); } } return base.OnMouseMove(e); } + private void addPart(Vector2 screenSpacePosition) + { + parts[currentIndex].Position = screenSpacePosition; + parts[currentIndex].Time = time; + ++parts[currentIndex].InvalidationID; + + currentIndex = (currentIndex + 1) % max_sprites; + } + protected override DrawNode CreateDrawNode() => new TrailDrawNode(this); private struct TrailPart From 292d50aacfed96eb19e83b6775515ef74b933c74 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Sep 2019 19:22:44 +0900 Subject: [PATCH 2649/2854] Don't confine the cursor trail --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index 5d68d200ff..a944ff88c6 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor InternalChild = fadeContainer = new Container { RelativeSizeAxes = Axes.Both, - Child = cursorTrail = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail()) + Child = cursorTrail = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling) }; } From a200485fbd0db1b2a4b429bb15620c8174cc4a9f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Sep 2019 19:23:02 +0900 Subject: [PATCH 2650/2854] Implement disjoint (old style) cursor trails --- .../Skinning/LegacyCursorTrail.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs index 74746faa44..d2018ea720 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Skinning; @@ -10,6 +11,11 @@ namespace osu.Game.Rulesets.Osu.Skinning { public class LegacyCursorTrail : CursorTrail { + private const double disjoint_trail_time_separation = 1000 / 60.0; + + private bool disjointTrail; + private double lastTrailTime; + public LegacyCursorTrail() { Blending = BlendingParameters.Additive; @@ -19,6 +25,31 @@ namespace osu.Game.Rulesets.Osu.Skinning private void load(ISkinSource skin) { Texture = skin.GetTexture("cursortrail"); + disjointTrail = skin.GetTexture("cursormiddle") == null; + + if (disjointTrail && Texture != null) + { + // stable "magic ratio". see OsuPlayfieldAdjustmentContainer for full explanation. + Texture.ScaleAdjust *= 1.6f; + } + } + + protected override double FadeDuration => disjointTrail ? 150 : 500; + + protected override bool InterpolateMovements => !disjointTrail; + + protected override bool OnMouseMove(MouseMoveEvent e) + { + if (!disjointTrail) + return base.OnMouseMove(e); + + if (Time.Current - lastTrailTime >= disjoint_trail_time_separation) + { + lastTrailTime = Time.Current; + return base.OnMouseMove(e); + } + + return false; } } } From e3b972187e7bfa966b0efc1d29adabcdb9875088 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Sep 2019 19:30:31 +0900 Subject: [PATCH 2651/2854] Fix incorrect cursor trail size + scale --- .../UI/Cursor/CursorTrail.cs | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 0998b5d604..7975982aec 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; using osu.Framework.Allocation; +using osu.Framework.Caching; using osu.Framework.Graphics; using osu.Framework.Graphics.Batches; using osu.Framework.Graphics.OpenGL.Vertices; @@ -72,6 +73,20 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor } } + private readonly Cached partSizeCache = new Cached(); + + private Vector2 partSize => partSizeCache.IsValid + ? partSizeCache.Value + : (partSizeCache.Value = new Vector2(Texture.DisplayWidth, Texture.DisplayHeight) * DrawInfo.Matrix.ExtractScale().Xy); + + public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) + { + if ((invalidation & (Invalidation.DrawInfo | Invalidation.RequiredParentSizeToFit | Invalidation.Presence)) > 0) + partSizeCache.Invalidate(); + + return base.Invalidate(invalidation, source, shallPropagate); + } + /// /// The amount of time to fade the cursor trail pieces. /// @@ -104,8 +119,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor timeOffset = Time.Current; } - private Vector2 size => texture.Size * Scale; - /// /// Whether to interpolate mouse movements and add trail pieces at intermediate points. /// @@ -139,7 +152,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor float distance = diff.Length; Vector2 direction = diff / distance; - float interval = size.X / 2 * 0.9f; + float interval = partSize.X / 2 * 0.9f; for (float d = interval; d < distance; d += interval) { @@ -202,7 +215,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor shader = Source.shader; texture = Source.texture; - size = Source.size; + size = Source.partSize; time = Source.time; for (int i = 0; i < Source.parts.Length; ++i) From 0790e9e377582117b70688865efa2abcfba2859b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2019 11:35:26 +0000 Subject: [PATCH 2652/2854] Bump ppy.osu.Framework.iOS from 2019.905.0 to 2019.909.0 Bumps [ppy.osu.Framework.iOS](https://github.com/ppy/osu-framework) from 2019.905.0 to 2019.909.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.905.0...2019.909.0) Signed-off-by: dependabot-preview[bot] --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index 5027a4ef8c..5645862bf7 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -119,7 +119,7 @@ - + From 7a7c3d21a1a158691daaf39005268e5acdd3b26d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2019 11:36:58 +0000 Subject: [PATCH 2653/2854] Bump ppy.osu.Framework from 2019.905.0 to 2019.909.0 Bumps [ppy.osu.Framework](https://github.com/ppy/osu-framework) from 2019.905.0 to 2019.909.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.905.0...2019.909.0) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5f2aad24dc..791d2fe285 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 5027a4ef8c..77756dfd87 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -118,7 +118,7 @@ - + From 3b4750ab9e54033fed4a358041e9b15124feb643 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2019 11:37:09 +0000 Subject: [PATCH 2654/2854] Bump ppy.osu.Framework.Android from 2019.905.0 to 2019.909.0 Bumps [ppy.osu.Framework.Android](https://github.com/ppy/osu-framework) from 2019.905.0 to 2019.909.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.905.0...2019.909.0) Signed-off-by: dependabot-preview[bot] --- osu.Android.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android.props b/osu.Android.props index 896b10133d..f76297c197 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + From 0ec642d8261d347ab5b4520f44259747185fb400 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Sep 2019 01:06:37 +0900 Subject: [PATCH 2655/2854] Show instead of toggle --- osu.Game/Screens/Menu/MainMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 4c3566b3e9..65df98551d 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -154,7 +154,7 @@ namespace osu.Game.Screens.Menu logo.Action += () => { if (!api.IsLoggedIn && !loginPrompted) - login?.ToggleVisibility(); + login?.Show(); loginPrompted = true; From f398f134e171e8284c6b8b3f5b66b10df813ffbe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Sep 2019 01:12:30 +0900 Subject: [PATCH 2656/2854] Remove unnecessary bool storage Also delay show slightly for better user experience. --- osu.Game/Screens/Menu/MainMenu.cs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 65df98551d..978a1bffcd 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -39,8 +39,6 @@ namespace osu.Game.Screens.Menu private ButtonSystem buttons; - private bool loginPrompted; - [Resolved] private GameHost host { get; set; } @@ -151,16 +149,6 @@ namespace osu.Game.Screens.Menu logo.FadeColour(Color4.White, 100, Easing.OutQuint); logo.FadeIn(100, Easing.OutQuint); - logo.Action += () => - { - if (!api.IsLoggedIn && !loginPrompted) - login?.Show(); - - loginPrompted = true; - - return true; - }; - if (resuming) { buttons.State = ButtonSystemState.TopLevel; @@ -170,6 +158,14 @@ namespace osu.Game.Screens.Menu sideFlashes.Delay(FADE_IN_DURATION).FadeIn(64, Easing.InQuint); } + else if (!api.IsLoggedIn) + { + logo.Action += () => + { + Scheduler.AddDelayed(() => login?.Show(), 500); + return true; + }; + } } protected override void LogoSuspending(OsuLogo logo) From 7eb20da820128defcfd3c9a3738f5ee5b3f3b861 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Sep 2019 01:17:58 +0900 Subject: [PATCH 2657/2854] Add back local bool (required due to action limitations) --- osu.Game/Screens/Menu/MainMenu.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 978a1bffcd..18a3de7f37 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -140,6 +140,8 @@ namespace osu.Game.Screens.Menu Beatmap.ValueChanged += beatmap_ValueChanged; } + private bool loginDisplayed = false; + protected override void LogoArriving(OsuLogo logo, bool resuming) { base.LogoArriving(logo, resuming); @@ -160,11 +162,18 @@ namespace osu.Game.Screens.Menu } else if (!api.IsLoggedIn) { - logo.Action += () => + logo.Action += displayLogin; + } + + bool displayLogin() + { + if (!loginDisplayed) { Scheduler.AddDelayed(() => login?.Show(), 500); - return true; - }; + loginDisplayed = true; + } + + return true; } } From 98e384129c578742fb88408d7b00c2a69cd223a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Sep 2019 01:34:48 +0900 Subject: [PATCH 2658/2854] Remove redundant initialisation --- osu.Game/Screens/Menu/MainMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 18a3de7f37..a006877082 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -140,7 +140,7 @@ namespace osu.Game.Screens.Menu Beatmap.ValueChanged += beatmap_ValueChanged; } - private bool loginDisplayed = false; + private bool loginDisplayed; protected override void LogoArriving(OsuLogo logo, bool resuming) { From 22fabef344c41920820a662bfc37009675c12207 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 9 Sep 2019 19:52:31 +0300 Subject: [PATCH 2659/2854] Use TestWorkingBeatmap in BeatmapDetailsArea tests --- .../SongSelect/TestSceneBeatmapDetailArea.cs | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs index 7b97a27732..ed9e01a67e 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Screens.Select; +using osu.Game.Tests.Beatmaps; using osuTK; namespace osu.Game.Tests.Visual.SongSelect @@ -30,45 +31,44 @@ namespace osu.Game.Tests.Visual.SongSelect Size = new Vector2(550f, 450f), }); - AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) + AddStep("all metrics", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap + { + BeatmapInfo = { - BeatmapSetInfo = + BeatmapSet = new BeatmapSetInfo { Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() } }, - BeatmapInfo = + Version = "All Metrics", + Metadata = new BeatmapMetadata { - Version = "All Metrics", - Metadata = new BeatmapMetadata - { - Source = "osu!lazer", - Tags = "this beatmap has all the metrics", - }, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 7, - DrainRate = 1, - OverallDifficulty = 5.7f, - ApproachRate = 3.5f, - }, - StarDifficulty = 5.3f, - Metrics = new BeatmapMetrics - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, - } + Source = "osu!lazer", + Tags = "this beatmap has all the metrics", + }, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 7, + DrainRate = 1, + OverallDifficulty = 5.7f, + ApproachRate = 3.5f, + }, + StarDifficulty = 5.3f, + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), + }, } - ); + })); - AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) + AddStep("all except source", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap { - BeatmapSetInfo = - { - Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() } - }, BeatmapInfo = { + BeatmapSet = new BeatmapSetInfo + { + Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() } + }, Version = "All Metrics", Metadata = new BeatmapMetadata { @@ -88,16 +88,16 @@ namespace osu.Game.Tests.Visual.SongSelect Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, } - }); + })); - AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) + AddStep("ratings", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap { - BeatmapSetInfo = - { - Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() } - }, BeatmapInfo = { + BeatmapSet = new BeatmapSetInfo + { + Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() } + }, Version = "Only Ratings", Metadata = new BeatmapMetadata { @@ -113,9 +113,9 @@ namespace osu.Game.Tests.Visual.SongSelect }, StarDifficulty = 4.8f } - }); + })); - AddStep("fails+retries", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) + AddStep("fails+retries", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap { BeatmapInfo = { @@ -139,9 +139,9 @@ namespace osu.Game.Tests.Visual.SongSelect Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, } - }); + })); - AddStep("null metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) + AddStep("null metrics", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap { BeatmapInfo = { @@ -160,7 +160,7 @@ namespace osu.Game.Tests.Visual.SongSelect }, StarDifficulty = 1.97f, } - }); + })); AddStep("null beatmap", () => detailsArea.Beatmap = null); } From b77550625c86360b5c98618eb61dec642c4824dd Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 9 Sep 2019 20:04:04 +0300 Subject: [PATCH 2660/2854] Check if DummyWorkingBeatmap is selected instead --- osu.Game/Screens/Select/BeatmapDetailArea.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs index bf8fc8cf07..5348de68d6 100644 --- a/osu.Game/Screens/Select/BeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Select { beatmap = value; Details.Beatmap = beatmap?.BeatmapInfo; - Leaderboard.Beatmap = beatmap is NoBeatmapsAvailableWorkingBeatmap ? null : beatmap?.BeatmapInfo; + Leaderboard.Beatmap = beatmap is DummyWorkingBeatmap ? null : beatmap?.BeatmapInfo; } } From 65869c7ebba728b9727d53fcf39caf11c68c9d67 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 10 Sep 2019 04:04:37 +0300 Subject: [PATCH 2661/2854] Refactor LeaderboardScopeSelector for more extensibility --- .../UserInterface/GradientLineTabControl.cs | 129 ++++++++++++++++++ .../BeatmapSet/LeaderboardScopeSelector.cs | 93 +------------ 2 files changed, 131 insertions(+), 91 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/GradientLineTabControl.cs diff --git a/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs new file mode 100644 index 0000000000..f4c43b0222 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs @@ -0,0 +1,129 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osuTK; +using osu.Framework.Graphics.Shapes; +using osuTK.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Input.Events; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; + +namespace osu.Game.Graphics.UserInterface +{ + public class GradientLineTabControl : PageTabControl + { + protected override Dropdown CreateDropdown() => null; + + protected override TabItem CreateTabItem(TModel value) => new ScopeSelectorTabItem(value); + + protected Color4 LineColour + { + get => line.MainColour.Value; + set => line.MainColour.Value = value; + } + + private readonly GradientLine line; + + public GradientLineTabControl() + { + RelativeSizeAxes = Axes.X; + + AddInternal(line = new GradientLine + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }); + } + + protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(20, 0), + }; + + private class ScopeSelectorTabItem : PageTabItem + { + public ScopeSelectorTabItem(TModel value) + : base(value) + { + Text.Font = OsuFont.GetFont(size: 16); + } + + protected override bool OnHover(HoverEvent e) + { + Text.FadeColour(AccentColour); + + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + + Text.FadeColour(Color4.White); + } + } + + private class GradientLine : GridContainer + { + public readonly Bindable MainColour = new Bindable(); + + private readonly Box left; + private readonly Box middle; + private readonly Box right; + + public GradientLine() + { + RelativeSizeAxes = Axes.X; + Size = new Vector2(0.8f, 1.5f); + + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(mode: GridSizeMode.Relative, size: 0.4f), + new Dimension(), + }; + + Content = new[] + { + new Drawable[] + { + left = new Box + { + RelativeSizeAxes = Axes.Both, + }, + middle = new Box + { + RelativeSizeAxes = Axes.Both, + }, + right = new Box + { + RelativeSizeAxes = Axes.Both, + }, + } + }; + } + + protected override void LoadComplete() + { + MainColour.BindValueChanged(onColourChanged, true); + base.LoadComplete(); + } + + private void onColourChanged(ValueChangedEvent colour) + { + left.Colour = ColourInfo.GradientHorizontal(colour.NewValue.Opacity(0), colour.NewValue); + middle.Colour = colour.NewValue; + right.Colour = ColourInfo.GradientHorizontal(colour.NewValue, colour.NewValue.Opacity(0)); + } + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs index dcd58db427..bdcd5c21b9 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs @@ -1,119 +1,30 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics.UserInterface; using osu.Game.Screens.Select.Leaderboards; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osuTK; using osu.Game.Graphics.UserInterface; -using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Framework.Allocation; using osuTK.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Input.Events; namespace osu.Game.Overlays.BeatmapSet { - public class LeaderboardScopeSelector : PageTabControl + public class LeaderboardScopeSelector : GradientLineTabControl { protected override bool AddEnumEntriesAutomatically => false; - protected override Dropdown CreateDropdown() => null; - - protected override TabItem CreateTabItem(BeatmapLeaderboardScope value) => new ScopeSelectorTabItem(value); - public LeaderboardScopeSelector() { - RelativeSizeAxes = Axes.X; - AddItem(BeatmapLeaderboardScope.Global); AddItem(BeatmapLeaderboardScope.Country); AddItem(BeatmapLeaderboardScope.Friend); - - AddInternal(new GradientLine - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - }); } [BackgroundDependencyLoader] private void load(OsuColour colours) { AccentColour = colours.Blue; - } - - protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(20, 0), - }; - - private class ScopeSelectorTabItem : PageTabItem - { - public ScopeSelectorTabItem(BeatmapLeaderboardScope value) - : base(value) - { - Text.Font = OsuFont.GetFont(size: 16); - } - - protected override bool OnHover(HoverEvent e) - { - Text.FadeColour(AccentColour); - - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - base.OnHoverLost(e); - - Text.FadeColour(Color4.White); - } - } - - private class GradientLine : GridContainer - { - public GradientLine() - { - RelativeSizeAxes = Axes.X; - Size = new Vector2(0.8f, 1.5f); - - ColumnDimensions = new[] - { - new Dimension(), - new Dimension(mode: GridSizeMode.Relative, size: 0.4f), - new Dimension(), - }; - - Content = new[] - { - new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(Color4.Transparent, Color4.Gray), - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Gray, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(Color4.Gray, Color4.Transparent), - }, - } - }; - } + LineColour = Color4.Gray; } } } From 03bd7ca8e72a88d2551887b742281374bec2ee14 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 10 Sep 2019 04:20:32 +0300 Subject: [PATCH 2662/2854] Implement RankingsScopeSelector --- .../Online/TestSceneRankingsScopeSelector.cs | 53 +++++++++++++++++++ .../UserInterface/GradientLineTabControl.cs | 26 --------- .../BeatmapSet/LeaderboardScopeSelector.cs | 28 ++++++++++ osu.Game/Overlays/RankingsScopeSelector.cs | 26 +++++++++ 4 files changed, 107 insertions(+), 26 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs create mode 100644 osu.Game/Overlays/RankingsScopeSelector.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs new file mode 100644 index 0000000000..1488addb09 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs @@ -0,0 +1,53 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Bindables; +using osu.Game.Overlays; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Framework.Extensions.Color4Extensions; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneRankingsScopeSelector : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(RankingsScopeSelector), + }; + + private readonly Box background; + + public TestSceneRankingsScopeSelector() + { + Bindable scope = new Bindable(); + + Add(background = new Box + { + RelativeSizeAxes = Axes.Both + }); + + Add(new RankingsScopeSelector + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Current = { BindTarget = scope } + }); + + AddStep(@"Select country", () => scope.Value = RankingsScope.Country); + AddStep(@"Select performance", () => scope.Value = RankingsScope.Performance); + AddStep(@"Select score", () => scope.Value = RankingsScope.Score); + AddStep(@"Select spotlights", () => scope.Value = RankingsScope.Spotlights); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.Yellow.Opacity(50); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs index f4c43b0222..7cd8d2c5bd 100644 --- a/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs +++ b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs @@ -8,7 +8,6 @@ using osuTK; using osu.Framework.Graphics.Shapes; using osuTK.Graphics; using osu.Framework.Graphics.Colour; -using osu.Framework.Input.Events; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -18,8 +17,6 @@ namespace osu.Game.Graphics.UserInterface { protected override Dropdown CreateDropdown() => null; - protected override TabItem CreateTabItem(TModel value) => new ScopeSelectorTabItem(value); - protected Color4 LineColour { get => line.MainColour.Value; @@ -49,29 +46,6 @@ namespace osu.Game.Graphics.UserInterface Spacing = new Vector2(20, 0), }; - private class ScopeSelectorTabItem : PageTabItem - { - public ScopeSelectorTabItem(TModel value) - : base(value) - { - Text.Font = OsuFont.GetFont(size: 16); - } - - protected override bool OnHover(HoverEvent e) - { - Text.FadeColour(AccentColour); - - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - base.OnHoverLost(e); - - Text.FadeColour(Color4.White); - } - } - private class GradientLine : GridContainer { public readonly Bindable MainColour = new Bindable(); diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs index bdcd5c21b9..e2a725ec46 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs @@ -6,6 +6,9 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Graphics; using osu.Framework.Allocation; using osuTK.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Framework.Graphics; namespace osu.Game.Overlays.BeatmapSet { @@ -13,6 +16,8 @@ namespace osu.Game.Overlays.BeatmapSet { protected override bool AddEnumEntriesAutomatically => false; + protected override TabItem CreateTabItem(BeatmapLeaderboardScope value) => new ScopeSelectorTabItem(value); + public LeaderboardScopeSelector() { AddItem(BeatmapLeaderboardScope.Global); @@ -26,5 +31,28 @@ namespace osu.Game.Overlays.BeatmapSet AccentColour = colours.Blue; LineColour = Color4.Gray; } + + private class ScopeSelectorTabItem : PageTabItem + { + public ScopeSelectorTabItem(BeatmapLeaderboardScope value) + : base(value) + { + Text.Font = OsuFont.GetFont(size: 16); + } + + protected override bool OnHover(HoverEvent e) + { + Text.FadeColour(AccentColour); + + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + + Text.FadeColour(Color4.White); + } + } } } diff --git a/osu.Game/Overlays/RankingsScopeSelector.cs b/osu.Game/Overlays/RankingsScopeSelector.cs new file mode 100644 index 0000000000..5935876ec9 --- /dev/null +++ b/osu.Game/Overlays/RankingsScopeSelector.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Graphics.UserInterface; +using osu.Framework.Allocation; +using osuTK.Graphics; + +namespace osu.Game.Overlays +{ + public class RankingsScopeSelector : GradientLineTabControl + { + [BackgroundDependencyLoader] + private void load() + { + AccentColour = LineColour = Color4.Black; + } + } + + public enum RankingsScope + { + Performance, + Spotlights, + Score, + Country + } +} From f505a3ff1d48efea15967e3c322f6203fb8bf1cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Sep 2019 11:44:11 +0900 Subject: [PATCH 2663/2854] Mark AutoGeneration tests as headless --- osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs index 20ac5eaa39..f260357df5 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs @@ -3,6 +3,7 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Testing; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Replays; @@ -12,6 +13,7 @@ using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Mania.Tests { [TestFixture] + [HeadlessTest] public class TestSceneAutoGeneration : OsuTestScene { [Test] From af3bb5a2cdf7084cc14a988a0610e636438ad4fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Sep 2019 13:29:50 +0900 Subject: [PATCH 2664/2854] Centralise and share bar line generation code between rulesets --- .../TestSceneStage.cs | 4 +- osu.Game.Rulesets.Mania/Objects/BarLine.cs | 21 ------- .../Objects/Drawables/DrawableBarLine.cs | 9 ++- .../UI/DrawableManiaRuleset.cs | 33 +---------- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 1 + osu.Game.Rulesets.Mania/UI/ManiaStage.cs | 1 + osu.Game.Rulesets.Taiko/Objects/BarLine.cs | 9 --- .../Objects/Drawables/DrawableBarLine.cs | 3 +- .../Objects/Drawables/DrawableBarLineMajor.cs | 1 + .../UI/DrawableTaikoRuleset.cs | 47 +-------------- osu.Game/Rulesets/Objects/BarLine.cs | 16 +++++ osu.Game/Rulesets/Objects/BarLineGenerator.cs | 58 +++++++++++++++++++ 12 files changed, 89 insertions(+), 114 deletions(-) delete mode 100644 osu.Game.Rulesets.Mania/Objects/BarLine.cs delete mode 100644 osu.Game.Rulesets.Taiko/Objects/BarLine.cs create mode 100644 osu.Game/Rulesets/Objects/BarLine.cs create mode 100644 osu.Game/Rulesets/Objects/BarLineGenerator.cs diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs index 395e6daf0a..e7fd601abe 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs @@ -15,6 +15,7 @@ using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; using osuTK; @@ -114,8 +115,7 @@ namespace osu.Game.Rulesets.Mania.Tests var obj = new BarLine { StartTime = Time.Current + 2000, - ControlPoint = new TimingControlPoint(), - BeatIndex = major ? 0 : 1 + Major = major, }; obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); diff --git a/osu.Game.Rulesets.Mania/Objects/BarLine.cs b/osu.Game.Rulesets.Mania/Objects/BarLine.cs deleted file mode 100644 index 4c644a8f09..0000000000 --- a/osu.Game.Rulesets.Mania/Objects/BarLine.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Beatmaps.ControlPoints; - -namespace osu.Game.Rulesets.Mania.Objects -{ - public class BarLine : ManiaHitObject - { - /// - /// The control point which this bar line is part of. - /// - public TimingControlPoint ControlPoint; - - /// - /// The index of the beat which this bar line represents within the control point. - /// This is a "major" bar line if % == 0. - /// - public int BeatIndex; - } -} diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs index e9c352c97e..862af8d15b 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs @@ -4,6 +4,7 @@ using osuTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osuTK.Graphics; @@ -13,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables /// Visualises a . Although this derives DrawableManiaHitObject, /// this does not handle input/sound like a normal hit object. ///
- public class DrawableBarLine : DrawableManiaHitObject + public class DrawableBarLine : DrawableHitObject { /// /// Height of major bar line triangles. @@ -40,9 +41,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Colour = new Color4(255, 204, 33, 255), }); - bool isMajor = barLine.BeatIndex % (int)barLine.ControlPoint.TimeSignature == 0; - - if (isMajor) + if (barLine.Major) { AddInternal(new EquilateralTriangle { @@ -65,7 +64,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables }); } - if (!isMajor && barLine.BeatIndex % 2 == 1) + if (!barLine.Major) Alpha = 0.2f; } diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index f26526fe70..29863fba2e 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -2,14 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Input; -using osu.Framework.MathUtils; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Input.Handlers; using osu.Game.Replays; using osu.Game.Rulesets.Mania.Beatmaps; @@ -19,8 +16,8 @@ using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; @@ -45,33 +42,7 @@ namespace osu.Game.Rulesets.Mania.UI public DrawableManiaRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { - // Generate the bar lines - double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; - - var timingPoints = Beatmap.ControlPointInfo.TimingPoints; - var barLines = new List(); - - for (int i = 0; i < timingPoints.Count; i++) - { - TimingControlPoint point = timingPoints[i]; - - // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object - double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature; - - int index = 0; - - for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++) - { - barLines.Add(new BarLine - { - StartTime = t, - ControlPoint = point, - BeatIndex = index - }); - } - } - - BarLines = barLines; + BarLines = new BarLineGenerator(Beatmap).BarLines; } [BackgroundDependencyLoader] diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 5ab07416a6..12faa499ad 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; using osuTK; diff --git a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs index a28de7ea58..98a4b7d0b6 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; diff --git a/osu.Game.Rulesets.Taiko/Objects/BarLine.cs b/osu.Game.Rulesets.Taiko/Objects/BarLine.cs deleted file mode 100644 index a07012fd71..0000000000 --- a/osu.Game.Rulesets.Taiko/Objects/BarLine.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Rulesets.Taiko.Objects -{ - public class BarLine : TaikoHitObject - { - } -} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs index bf89f7e15b..1a5a797f28 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Objects; using osuTK; using osu.Game.Rulesets.Objects.Drawables; @@ -11,7 +12,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// /// A line that scrolls alongside hit objects in the playfield and visualises control points. /// - public class DrawableBarLine : DrawableHitObject + public class DrawableBarLine : DrawableHitObject { /// /// The width of the line tracker. diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs index 4d3a1a3f8a..f5b75a781b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osuTK; using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index b03bea578e..5caa9e4626 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -5,19 +5,18 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Taiko.Replays; -using System.Linq; using osu.Framework.Input; using osu.Game.Configuration; using osu.Game.Input.Handlers; using osu.Game.Replays; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI.Scrolling; namespace osu.Game.Rulesets.Taiko.UI @@ -38,49 +37,7 @@ namespace osu.Game.Rulesets.Taiko.UI [BackgroundDependencyLoader] private void load() { - loadBarLines(); - } - - private void loadBarLines() - { - TaikoHitObject lastObject = Beatmap.HitObjects[Beatmap.HitObjects.Count - 1]; - double lastHitTime = 1 + ((lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime); - - var timingPoints = Beatmap.ControlPointInfo.TimingPoints.ToList(); - - if (timingPoints.Count == 0) - return; - - int currentIndex = 0; - int currentBeat = 0; - double time = timingPoints[currentIndex].Time; - - while (time <= lastHitTime) - { - int nextIndex = currentIndex + 1; - - if (nextIndex < timingPoints.Count && time > timingPoints[nextIndex].Time) - { - currentIndex = nextIndex; - time = timingPoints[currentIndex].Time; - currentBeat = 0; - } - - var currentPoint = timingPoints[currentIndex]; - - var barLine = new BarLine - { - StartTime = time, - }; - - barLine.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty); - - bool isMajor = currentBeat % (int)currentPoint.TimeSignature == 0; - Playfield.Add(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine)); - - time += currentPoint.BeatLength * (int)currentPoint.TimeSignature; - currentBeat++; - } + new BarLineGenerator(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar))); } public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this); diff --git a/osu.Game/Rulesets/Objects/BarLine.cs b/osu.Game/Rulesets/Objects/BarLine.cs new file mode 100644 index 0000000000..a5c716e127 --- /dev/null +++ b/osu.Game/Rulesets/Objects/BarLine.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Objects +{ + /// + /// A hit object representing the end of a bar. + /// + public class BarLine : HitObject + { + /// + /// Whether this barline is a prominent beat (based on time signature of beatmap). + /// + public bool Major; + } +} diff --git a/osu.Game/Rulesets/Objects/BarLineGenerator.cs b/osu.Game/Rulesets/Objects/BarLineGenerator.cs new file mode 100644 index 0000000000..ce571d7b17 --- /dev/null +++ b/osu.Game/Rulesets/Objects/BarLineGenerator.cs @@ -0,0 +1,58 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Objects +{ + public class BarLineGenerator + { + /// + /// The generated bar lines. + /// + public readonly List BarLines = new List(); + + /// + /// Constructs and generates bar lines for provided beatmap. + /// + /// The beatmap to generate bar lines for. + public BarLineGenerator(IBeatmap beatmap) + { + if (beatmap.HitObjects.Count == 0) + return; + + HitObject lastObject = beatmap.HitObjects.Last(); + double lastHitTime = 1 + ((lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime); + + var timingPoints = beatmap.ControlPointInfo.TimingPoints; + + if (timingPoints.Count == 0) + return; + + for (int i = 0; i < timingPoints.Count; i++) + { + TimingControlPoint currentTimingPoint = timingPoints[i]; + int currentBeat = 0; + + // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object + double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - currentTimingPoint.BeatLength : lastHitTime + currentTimingPoint.BeatLength * (int)currentTimingPoint.TimeSignature; + + double barLength = currentTimingPoint.BeatLength * (int)currentTimingPoint.TimeSignature; + + for (double t = currentTimingPoint.Time; Precision.DefinitelyBigger(endTime, t); t += barLength, currentBeat++) + { + BarLines.Add(new BarLine + { + StartTime = t, + Major = currentBeat % (int)currentTimingPoint.TimeSignature == 0 + }); + } + } + } + } +} From ef90914f581311f17bc689dea4cf83b7f96e01de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Sep 2019 15:27:40 +0900 Subject: [PATCH 2665/2854] Fix mania notes test scene not visually displaying --- osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs index 031abb08e2..8dae5e6d84 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -11,6 +11,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; @@ -40,6 +41,7 @@ namespace osu.Game.Rulesets.Mania.Tests { Child = new FillFlowContainer { + Clock = new FramedClock(new ManualClock()), Anchor = Anchor.Centre, Origin = Anchor.Centre, AutoSizeAxes = Axes.Both, @@ -62,7 +64,7 @@ namespace osu.Game.Rulesets.Mania.Tests private Drawable createNoteDisplay(ScrollingDirection direction, int identifier, out DrawableNote hitObject) { - var note = new Note { StartTime = 999999999 }; + var note = new Note { StartTime = 0 }; note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); return new ScrollingTestContainer(direction) @@ -77,7 +79,7 @@ namespace osu.Game.Rulesets.Mania.Tests private Drawable createHoldNoteDisplay(ScrollingDirection direction, int identifier, out DrawableHoldNote hitObject) { - var note = new HoldNote { StartTime = 999999999, Duration = 5000 }; + var note = new HoldNote { StartTime = 0, Duration = 5000 }; note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); return new ScrollingTestContainer(direction) @@ -133,7 +135,7 @@ namespace osu.Game.Rulesets.Mania.Tests Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.Both, Width = 1.25f, - Colour = Color4.Black.Opacity(0.5f) + Colour = Color4.Green.Opacity(0.5f) }, content = new Container { RelativeSizeAxes = Axes.Both } } From 01fd08cba92f1e7c96878147259b1c2b6411abab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Sep 2019 17:11:16 +0900 Subject: [PATCH 2666/2854] Fix broken positioning of effected usernames --- osu.Game/Overlays/Chat/ChatLine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 4c37d626c0..d125da8e92 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -148,8 +148,8 @@ namespace osu.Game.Overlays.Chat }, new MessageSender(message.Sender) { + AutoSizeAxes = Axes.Both, Padding = new MarginPadding { Left = timestamp_padding }, - RelativeSizeAxes = Axes.Both, Origin = Anchor.TopRight, Anchor = Anchor.TopRight, Child = effectedUsername, From 717a287d692e207e08231eec334f29076ad82b0c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Sep 2019 17:11:26 +0900 Subject: [PATCH 2667/2854] Use real ellipsis character --- osu.Game/Overlays/Chat/ChatLine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index d125da8e92..7596231a3d 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -90,7 +90,7 @@ namespace osu.Game.Overlays.Chat Shadow = false, Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length], Truncate = true, - EllipsisString = ".. :", + EllipsisString = "… :", Font = OsuFont.GetFont(size: TextSize, weight: FontWeight.Bold, italics: true), Anchor = Anchor.TopRight, Origin = Anchor.TopRight, From 36d3736e1dde496c2a1446f31a176dcf7d5a90f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Sep 2019 18:06:24 +0900 Subject: [PATCH 2668/2854] Fix hitcircle font prefix not being read for legacy skins --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 2 +- osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 5957b81d7e..6b6a08ed21 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Skinning return null; case OsuSkinComponents.HitCircleText: - var font = GetConfig(OsuSkinConfiguration.HitCircleFont)?.Value ?? "default"; + var font = GetConfig(OsuSkinConfiguration.HitCirclePrefix)?.Value ?? "default"; var overlap = GetConfig(OsuSkinConfiguration.HitCircleOverlap)?.Value ?? 0; return !hasFont(font) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs index a6b87150ae..e7b686d27d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs @@ -5,7 +5,7 @@ namespace osu.Game.Rulesets.Osu.Skinning { public enum OsuSkinConfiguration { - HitCircleFont, + HitCirclePrefix, HitCircleOverlap, SliderBorderSize, SliderPathRadius, From 1969c5b89bccc3788a295d14ecc8e83412b58da7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 10 Sep 2019 16:36:05 +0300 Subject: [PATCH 2669/2854] Apply suggetsted changes --- .../Online/TestSceneRankingsScopeSelector.cs | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs index 1488addb09..93a00e1d06 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs @@ -9,7 +9,6 @@ using osu.Game.Overlays; using osu.Framework.Graphics.Shapes; using osu.Framework.Allocation; using osu.Game.Graphics; -using osu.Framework.Extensions.Color4Extensions; namespace osu.Game.Tests.Visual.Online { @@ -24,18 +23,20 @@ namespace osu.Game.Tests.Visual.Online public TestSceneRankingsScopeSelector() { - Bindable scope = new Bindable(); + var scope = new Bindable(); - Add(background = new Box + AddRange(new Drawable[] { - RelativeSizeAxes = Axes.Both - }); - - Add(new RankingsScopeSelector - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Current = { BindTarget = scope } + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new RankingsScopeSelector + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Current = { BindTarget = scope } + } }); AddStep(@"Select country", () => scope.Value = RankingsScope.Country); @@ -47,7 +48,7 @@ namespace osu.Game.Tests.Visual.Online [BackgroundDependencyLoader] private void load(OsuColour colours) { - background.Colour = colours.Yellow.Opacity(50); + background.Colour = colours.GreySeafoam; } } } From e682ca4fd9a17cf90ac083ec5ff8faf317606b59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Sep 2019 12:51:54 +0900 Subject: [PATCH 2670/2854] Adjust osu!mania scroll speed defaults to be more sane --- .../Configuration/ManiaRulesetConfigManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs index b591f9da22..f5412dcfc5 100644 --- a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Configuration { base.InitialiseDefaults(); - Set(ManiaRulesetSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0); + Set(ManiaRulesetSetting.ScrollTime, 1500.0, 50.0, 5000.0, 50.0); Set(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down); } From 70d39e9be47d7fae0250c0acc3c9fdf27e0f8ced Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Sep 2019 13:28:36 +0900 Subject: [PATCH 2671/2854] Always apply stable's magic ratio --- osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs index d2018ea720..1885c76fcc 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Skinning Texture = skin.GetTexture("cursortrail"); disjointTrail = skin.GetTexture("cursormiddle") == null; - if (disjointTrail && Texture != null) + if (Texture != null) { // stable "magic ratio". see OsuPlayfieldAdjustmentContainer for full explanation. Texture.ScaleAdjust *= 1.6f; From 6c00d3936ae556af3184f587fffdf85afab58b61 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Sep 2019 13:28:46 +0900 Subject: [PATCH 2672/2854] Reduce interval between cursor trail parts --- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 7975982aec..b32dfd483f 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -152,7 +152,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor float distance = diff.Length; Vector2 direction = diff / distance; - float interval = partSize.X / 2 * 0.9f; + float interval = partSize.X / 2.5f; for (float d = interval; d < distance; d += interval) { From 562280ced02ed9c570a7b56a8a6e4cd9dbd95507 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Sep 2019 13:30:07 +0900 Subject: [PATCH 2673/2854] Add cursor trail test scene --- .../TestSceneCursorTrail.cs | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs new file mode 100644 index 0000000000..dae75e5a9d --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs @@ -0,0 +1,120 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Textures; +using osu.Framework.Testing.Input; +using osu.Game.Audio; +using osu.Game.Rulesets.Osu.Skinning; +using osu.Game.Rulesets.Osu.UI.Cursor; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public class TestSceneCursorTrail : OsuTestScene + { + [Test] + public void TestSmoothCursorTrail() + { + createTest(() => new CursorTrail()); + } + + [Test] + public void TestLegacySmoothCursorTrail() + { + createTest(() => new LegacySkinContainer(false) + { + Child = new LegacyCursorTrail() + }); + } + + [Test] + public void TestLegacyDisjointCursorTrail() + { + createTest(() => new LegacySkinContainer(true) + { + Child = new LegacyCursorTrail() + }); + } + + private void createTest(Func createContent) => AddStep("create trail", () => + { + Clear(); + + Add(new Container + { + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.8f), + Child = new MovingCursorInputManager { Child = createContent?.Invoke() } + }); + }); + + [Cached(typeof(ISkinSource))] + private class LegacySkinContainer : Container, ISkinSource + { + private readonly bool disjoint; + + public LegacySkinContainer(bool disjoint) + { + this.disjoint = disjoint; + + RelativeSizeAxes = Axes.Both; + } + + public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotImplementedException(); + + public Texture GetTexture(string componentName) + { + switch (componentName) + { + case "cursortrail": + var tex = new Texture(Texture.WhitePixel.TextureGL); + + if (disjoint) + tex.ScaleAdjust = 1 / 25f; + return tex; + + case "cursormiddle": + return disjoint ? null : Texture.WhitePixel; + } + + return null; + } + + public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); + + public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); + + public event Action SourceChanged; + } + + private class MovingCursorInputManager : ManualInputManager + { + public MovingCursorInputManager() + { + UseParentInput = false; + } + + protected override void Update() + { + base.Update(); + + const double spin_duration = 1000; + double currentTime = Time.Current; + + double angle = (currentTime % spin_duration) / spin_duration * 2 * Math.PI; + Vector2 rPos = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)); + + MoveMouseTo(ToScreenSpace(DrawSize / 2 + DrawSize / 3 * rPos)); + } + } + } +} From 6760e239a119b74661a9efc0b6bce63615d6fdcd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Sep 2019 13:39:21 +0900 Subject: [PATCH 2674/2854] Fix osu! hitcircle font textures being incorrectly sized --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 6 +++--- osu.Game/Skinning/LegacySpriteText.cs | 7 +------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 6b6a08ed21..c978d95302 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -86,9 +86,9 @@ namespace osu.Game.Rulesets.Osu.Skinning ? null : new LegacySpriteText(source, font) { - // Spacing value was reverse-engineered from the ratio of the rendered sprite size in the visual inspector vs the actual texture size - Scale = new Vector2(0.96f), - Spacing = new Vector2(-overlap * 0.89f, 0) + // stable applies a blanket 0.8x scale to hitcircle fonts + Scale = new Vector2(0.8f), + Spacing = new Vector2(-overlap, 0) }; } diff --git a/osu.Game/Skinning/LegacySpriteText.cs b/osu.Game/Skinning/LegacySpriteText.cs index dbcec019d6..773a9dc5c6 100644 --- a/osu.Game/Skinning/LegacySpriteText.cs +++ b/osu.Game/Skinning/LegacySpriteText.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using osu.Framework.Graphics.Sprites; using osu.Framework.Text; -using osu.Game.Graphics; using osu.Game.Graphics.Sprites; namespace osu.Game.Skinning @@ -18,7 +17,7 @@ namespace osu.Game.Skinning Shadow = false; UseFullGlyphHeight = false; - Font = new FontUsage(font, OsuFont.DEFAULT_FONT_SIZE); + Font = new FontUsage(font, 1); glyphStore = new LegacyGlyphStore(skin); } @@ -37,10 +36,6 @@ namespace osu.Game.Skinning { var texture = skin.GetTexture($"{fontName}-{character}"); - if (texture != null) - // Approximate value that brings character sizing roughly in-line with stable - texture.ScaleAdjust *= 18; - if (texture == null) return null; From e408efff4984be7d737c86a76221d9c5771c229d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Sep 2019 13:40:53 +0900 Subject: [PATCH 2675/2854] Add scaling to the test --- osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs index dae75e5a9d..685a51d208 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs @@ -24,7 +24,15 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestSmoothCursorTrail() { - createTest(() => new CursorTrail()); + Container scalingContainer = null; + + createTest(() => scalingContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Child = new CursorTrail() + }); + + AddStep("set large scale", () => scalingContainer.Scale = new Vector2(10)); } [Test] From 96efc91b51a9b46a0045d246e7c2cc26e86fc6b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Sep 2019 14:57:42 +0900 Subject: [PATCH 2676/2854] Fix follow points not displaying on some skins --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 82181945a4..479c250eab 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -45,6 +45,9 @@ namespace osu.Game.Rulesets.Osu.Skinning switch (osuComponent.Component) { + case OsuSkinComponents.FollowPoint: + return this.GetAnimation(component.LookupName, true, false); + case OsuSkinComponents.SliderFollowCircle: return this.GetAnimation("sliderfollowcircle", true, true); From 95828b07efecaf83ef25127043f71ebb77e6c92f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 11 Sep 2019 10:40:58 +0300 Subject: [PATCH 2677/2854] Implement HeaderFlag component for rankings overlay --- .../Online/TestSceneRankingsHeaderFlag.cs | 55 +++++++++++++++ osu.Game/Overlays/Rankings/HeaderFlag.cs | 68 +++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs create mode 100644 osu.Game/Overlays/Rankings/HeaderFlag.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs new file mode 100644 index 0000000000..d31e3011b8 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs @@ -0,0 +1,55 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Overlays.Rankings; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneRankingsHeaderFlag : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(HeaderFlag), + }; + + public TestSceneRankingsHeaderFlag() + { + HeaderFlag flag; + SpriteText text; + + AddRange(new Drawable[] + { + flag = new HeaderFlag + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(30, 20), + Country = new Country + { + FlagName = "BY", + FullName = "Belarus" + } + }, + text = new SpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "Invoked", + Font = OsuFont.GetFont(size: 30), + Alpha = 0, + } + }); + + flag.Action += () => text.FadeIn().Then().FadeOut(1000, Easing.OutQuint); + + AddStep("Trigger click", () => flag.Click()); + } + } +} diff --git a/osu.Game/Overlays/Rankings/HeaderFlag.cs b/osu.Game/Overlays/Rankings/HeaderFlag.cs new file mode 100644 index 0000000000..5b00e3e487 --- /dev/null +++ b/osu.Game/Overlays/Rankings/HeaderFlag.cs @@ -0,0 +1,68 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Users.Drawables; +using osuTK.Graphics; +using osuTK; +using osu.Framework.Input.Events; +using osu.Framework.Extensions.Color4Extensions; +using System; + +namespace osu.Game.Overlays.Rankings +{ + public class HeaderFlag : UpdateableFlag + { + private const int duration = 200; + + public Action Action; + + private readonly Container hoverContainer; + + public HeaderFlag() + { + AddInternal(hoverContainer = new Container + { + Alpha = 0, + Depth = -1, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(15), + Icon = FontAwesome.Solid.Times, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(100), + } + } + }); + } + + protected override bool OnHover(HoverEvent e) + { + hoverContainer.FadeIn(duration, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + hoverContainer.FadeOut(duration, Easing.OutQuint); + } + + protected override bool OnClick(ClickEvent e) + { + Action?.Invoke(); + return base.OnClick(e); + } + } +} From d610c903716356593f9b774c5e768412583e13c5 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 11 Sep 2019 10:43:51 +0300 Subject: [PATCH 2678/2854] Add more tests --- .../Online/TestSceneRankingsHeaderFlag.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs index d31e3011b8..c9531e1016 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs @@ -24,6 +24,18 @@ namespace osu.Game.Tests.Visual.Online HeaderFlag flag; SpriteText text; + var countryA = new Country + { + FlagName = "BY", + FullName = "Belarus" + }; + + var countryB = new Country + { + FlagName = "US", + FullName = "United States" + }; + AddRange(new Drawable[] { flag = new HeaderFlag @@ -31,11 +43,7 @@ namespace osu.Game.Tests.Visual.Online Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(30, 20), - Country = new Country - { - FlagName = "BY", - FullName = "Belarus" - } + Country = countryA, }, text = new SpriteText { @@ -50,6 +58,8 @@ namespace osu.Game.Tests.Visual.Online flag.Action += () => text.FadeIn().Then().FadeOut(1000, Easing.OutQuint); AddStep("Trigger click", () => flag.Click()); + AddStep("Change to country 2", () => flag.Country = countryB); + AddStep("Change to country 1", () => flag.Country = countryA); } } } From 1d1da1bc13c7f1c613aa8bba35c8f5b90a819b15 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 11 Sep 2019 11:26:09 +0300 Subject: [PATCH 2679/2854] Visual improvements --- osu.Game/Overlays/Rankings/HeaderFlag.cs | 34 +++++++++--------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/osu.Game/Overlays/Rankings/HeaderFlag.cs b/osu.Game/Overlays/Rankings/HeaderFlag.cs index 5b00e3e487..8bd4bdf13f 100644 --- a/osu.Game/Overlays/Rankings/HeaderFlag.cs +++ b/osu.Game/Overlays/Rankings/HeaderFlag.cs @@ -20,49 +20,39 @@ namespace osu.Game.Overlays.Rankings public Action Action; - private readonly Container hoverContainer; + private readonly SpriteIcon hoverIcon; public HeaderFlag() { - AddInternal(hoverContainer = new Container + AddInternal(hoverIcon = new SpriteIcon { - Alpha = 0, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Depth = -1, - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(15), - Icon = FontAwesome.Solid.Times, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(100), - } - } + Alpha = 0, + Size = new Vector2(10), + Icon = FontAwesome.Solid.Times, }); } protected override bool OnHover(HoverEvent e) { - hoverContainer.FadeIn(duration, Easing.OutQuint); + hoverIcon.FadeIn(duration, Easing.OutQuint); + this.FadeColour(Color4.Gray, duration, Easing.OutQuint); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { base.OnHoverLost(e); - hoverContainer.FadeOut(duration, Easing.OutQuint); + hoverIcon.FadeOut(duration, Easing.OutQuint); + this.FadeColour(Color4.White, duration, Easing.OutQuint); } protected override bool OnClick(ClickEvent e) { Action?.Invoke(); - return base.OnClick(e); + return true; } } } From 825a34ecd3180d421aa6f6f8827df3a8269f5f70 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Sep 2019 17:34:03 +0900 Subject: [PATCH 2680/2854] Early return to avoid other potential fail cases --- .../Containers/OsuFocusedOverlayContainer.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 5c2efbc354..08164dbf3e 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -106,26 +106,26 @@ namespace osu.Game.Graphics.Containers protected override void UpdateState(ValueChangedEvent state) { - base.UpdateState(state); - switch (state.NewValue) { case Visibility.Visible: - if (OverlayActivationMode.Value != OverlayActivation.Disabled) + if (OverlayActivationMode.Value == OverlayActivation.Disabled) { - if (PlaySamplesOnStateChange) samplePopIn?.Play(); - if (BlockScreenWideMouse && DimMainContent) osuGame?.AddBlockingOverlay(this); + State.Value = Visibility.Hidden; + return; } - else - Hide(); + if (PlaySamplesOnStateChange) samplePopIn?.Play(); + if (BlockScreenWideMouse && DimMainContent) game?.AddBlockingOverlay(this); break; case Visibility.Hidden: if (PlaySamplesOnStateChange) samplePopOut?.Play(); - if (BlockScreenWideMouse) osuGame?.RemoveBlockingOverlay(this); + if (BlockScreenWideMouse) game?.RemoveBlockingOverlay(this); break; } + + base.UpdateState(state); } protected override void PopOut() From 2c09efa23b577bc5e15c3a4efea6558a490f7d62 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Sep 2019 17:34:28 +0900 Subject: [PATCH 2681/2854] Handle changes to OverlayActivationMode --- .../Containers/OsuFocusedOverlayContainer.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 08164dbf3e..0ce095d44f 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -32,7 +32,7 @@ namespace osu.Game.Graphics.Containers protected virtual bool DimMainContent => true; [Resolved(CanBeNull = true)] - private OsuGame osuGame { get; set; } + private OsuGame game { get; set; } [Resolved] private PreviewTrackManager previewTrackManager { get; set; } @@ -42,8 +42,14 @@ namespace osu.Game.Graphics.Containers [BackgroundDependencyLoader(true)] private void load(AudioManager audio) { - if (osuGame != null) - OverlayActivationMode.BindTo(osuGame.OverlayActivationMode); + OverlayActivationMode.ValueChanged += mode => + { + if (mode.NewValue == OverlayActivation.Disabled) + State.Value = Visibility.Hidden; + }; + + if (game != null) + OverlayActivationMode.BindTo(game.OverlayActivationMode); samplePopIn = audio.Samples.Get(@"UI/overlay-pop-in"); samplePopOut = audio.Samples.Get(@"UI/overlay-pop-out"); @@ -137,7 +143,7 @@ namespace osu.Game.Graphics.Containers protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - osuGame?.RemoveBlockingOverlay(this); + game?.RemoveBlockingOverlay(this); } } } From 660c678cdcbe74b8a6b6fe55555a60b2b778f820 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 11 Sep 2019 11:40:51 +0300 Subject: [PATCH 2682/2854] Remove unused using directives --- osu.Game/Overlays/Rankings/HeaderFlag.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Overlays/Rankings/HeaderFlag.cs b/osu.Game/Overlays/Rankings/HeaderFlag.cs index 8bd4bdf13f..6f641c74a5 100644 --- a/osu.Game/Overlays/Rankings/HeaderFlag.cs +++ b/osu.Game/Overlays/Rankings/HeaderFlag.cs @@ -2,14 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Users.Drawables; using osuTK.Graphics; using osuTK; using osu.Framework.Input.Events; -using osu.Framework.Extensions.Color4Extensions; using System; namespace osu.Game.Overlays.Rankings From 41ad44791bfc2918af739f05273e244e180b7f20 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 11 Sep 2019 11:58:18 +0300 Subject: [PATCH 2683/2854] Move RankingsScopeSelector to another namespace --- osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs | 2 +- osu.Game/Overlays/{ => Rankings}/RankingsScopeSelector.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename osu.Game/Overlays/{ => Rankings}/RankingsScopeSelector.cs (94%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs index 93a00e1d06..2081a6c0cb 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs @@ -5,10 +5,10 @@ using System; using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Bindables; -using osu.Game.Overlays; using osu.Framework.Graphics.Shapes; using osu.Framework.Allocation; using osu.Game.Graphics; +using osu.Game.Overlays.Rankings; namespace osu.Game.Tests.Visual.Online { diff --git a/osu.Game/Overlays/RankingsScopeSelector.cs b/osu.Game/Overlays/Rankings/RankingsScopeSelector.cs similarity index 94% rename from osu.Game/Overlays/RankingsScopeSelector.cs rename to osu.Game/Overlays/Rankings/RankingsScopeSelector.cs index 5935876ec9..2095bcc61c 100644 --- a/osu.Game/Overlays/RankingsScopeSelector.cs +++ b/osu.Game/Overlays/Rankings/RankingsScopeSelector.cs @@ -5,7 +5,7 @@ using osu.Game.Graphics.UserInterface; using osu.Framework.Allocation; using osuTK.Graphics; -namespace osu.Game.Overlays +namespace osu.Game.Overlays.Rankings { public class RankingsScopeSelector : GradientLineTabControl { From da6ba20fc80a7cb25badd75fcc0dfd23a2b4f0bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Sep 2019 18:15:03 +0900 Subject: [PATCH 2684/2854] Reduce glow on notes --- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 2cd81104a3..5aeaba717c 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Diagnostics; @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, - Colour = colour.NewValue.Lighten(1f).Opacity(0.6f), + Colour = colour.NewValue.Lighten(1f).Opacity(0.2f), Radius = 10, }; }, true); From 44d90a4e860fecb7891d295fe929fdc8dbc8d6f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Sep 2019 18:16:14 +0900 Subject: [PATCH 2685/2854] Increase note height --- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs | 4 +++- .../Objects/Drawables/Pieces/NotePiece.cs | 7 +++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 5aeaba717c..31221c05ee 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Diagnostics; @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables /// public class DrawableNote : DrawableManiaHitObject, IKeyBindingHandler { + public const float CORNER_RADIUS = NotePiece.NOTE_HEIGHT / 2; + private readonly NotePiece headPiece; public DrawableNote(Note hitObject) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs index bb33693783..4521af7dfb 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs @@ -18,8 +18,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces /// internal class NotePiece : Container, IHasAccentColour { - public const float NOTE_HEIGHT = 10; - private const float head_colour_height = 6; + public const float NOTE_HEIGHT = 12; private readonly IBindable direction = new Bindable(); @@ -39,8 +38,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces colouredBox = new Box { RelativeSizeAxes = Axes.X, - Height = head_colour_height, - Alpha = 0.2f + Height = NOTE_HEIGHT / 2, + Alpha = 0.1f } }; } From 8f6bc6fd5c8171152f308c937c2e3dff48894b97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Sep 2019 18:12:43 +0900 Subject: [PATCH 2686/2854] Make osu!mania hit explosions more explodey --- .../TestSceneHitExplosion.cs | 84 ++++++++++++ osu.Game.Rulesets.Mania/UI/Column.cs | 9 +- osu.Game.Rulesets.Mania/UI/HitExplosion.cs | 123 +++++++++++++----- 3 files changed, 183 insertions(+), 33 deletions(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs new file mode 100644 index 0000000000..12159ca239 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs @@ -0,0 +1,84 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Tests.Visual; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Mania.Tests +{ + [TestFixture] + public class TestSceneHitExplosion : OsuTestScene + { + private ScrollingTestContainer scrolling; + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(DrawableNote), + typeof(DrawableManiaHitObject), + }; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Child = scrolling = new ScrollingTestContainer(ScrollingDirection.Down) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.Y, + Y = -0.25f, + Size = new Vector2(Column.COLUMN_WIDTH, NotePiece.NOTE_HEIGHT), + }; + + int runcount = 0; + + AddRepeatStep("explode", () => + { + runcount++; + + if (runcount % 15 > 12) + return; + + scrolling.AddRange(new Drawable[] + { + new HitExplosion((runcount / 15) % 2 == 0 ? new Color4(94, 0, 57, 255) : new Color4(6, 84, 0, 255), runcount % 6 != 0) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }); + }, 100); + } + + private class TestNote : DrawableNote + { + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + if (!userTriggered) + { + // force success + ApplyResult(r => r.Type = HitResult.Great); + } + else + base.CheckForResult(userTriggered, timeOffset); + } + + public TestNote(Note hitObject) + : base(hitObject) + { + AccentColour.Value = Color4.Pink; + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 91dd236ab1..fa14a0a293 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Linq; @@ -90,6 +90,8 @@ namespace osu.Game.Rulesets.Mania.UI Bottom = dir.NewValue == ScrollingDirection.Down ? ManiaStage.HIT_TARGET_POSITION : 0, }; + explosionContainer.Y = dir.NewValue == ScrollingDirection.Down ? -NotePiece.NOTE_HEIGHT : 0; + keyArea.Anchor = keyArea.Origin = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft; }, true); } @@ -163,9 +165,10 @@ namespace osu.Game.Rulesets.Mania.UI if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements.Value) return; - explosionContainer.Add(new HitExplosion(judgedObject) + explosionContainer.Add(new HitExplosion(judgedObject.AccentColour.Value, judgedObject is DrawableHoldNoteTick) { - Anchor = Direction.Value == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre + Anchor = Direction.Value == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre, + Origin = Direction.Value == ScrollingDirection.Up ? Anchor.BottomCentre : Anchor.TopCentre, }); } diff --git a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs index 48470add8b..21726206f1 100644 --- a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs @@ -1,16 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osuTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Shapes; using osu.Framework.MathUtils; -using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; -using osu.Game.Rulesets.Objects.Drawables; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.UI { @@ -18,51 +16,116 @@ namespace osu.Game.Rulesets.Mania.UI { public override bool RemoveWhenNotAlive => true; - private readonly CircularContainer circle; + private readonly CircularContainer largeFaint; + private readonly CircularContainer mainGlow1; + private readonly CircularContainer mainGlow2; + private readonly CircularContainer mainGlow3; - public HitExplosion(DrawableHitObject judgedObject) + public HitExplosion(Color4 objectColour, bool isSmall = false) { - bool isTick = judgedObject is DrawableHoldNoteTick; - - Origin = Anchor.Centre; - RelativeSizeAxes = Axes.X; - Y = NotePiece.NOTE_HEIGHT / 2; Height = NotePiece.NOTE_HEIGHT; // scale roughly in-line with visual appearance of notes - Scale = new Vector2(isTick ? 0.4f : 0.8f); + Scale = new Vector2(1f, 0.6f); - InternalChild = circle = new CircularContainer + if (isSmall) + Scale *= 0.5f; + + const float angle_variangle = 15; // should be less than 45 + + const float roundness = 80; + + const float opacity = 1; + + const float initial_height = 10; + + var colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1); + + InternalChildren = new Drawable[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - // we want our size to be very small so the glow dominates it. - Size = new Vector2(0.1f), - EdgeEffect = new EdgeEffectParameters + largeFaint = new CircularContainer { - Type = EdgeEffectType.Glow, - Colour = Interpolation.ValueAt(0.1f, judgedObject.AccentColour.Value, Color4.White, 0, 1), - Radius = 100, - }, - Child = new Box - { - Alpha = 0, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - AlwaysPresent = true + Masking = true, + // we want our size to be very small so the glow dominates it. + Size = new Vector2(0.8f), + Blending = BlendingParameters.Additive, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = Interpolation.ValueAt(0.1f, objectColour, Color4.White, 0, 1).Opacity(0.3f), + Roundness = 160, + Radius = 200, + }, + }, + mainGlow1 = new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Blending = BlendingParameters.Additive, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = Interpolation.ValueAt(0.6f, objectColour, Color4.White, 0, 1), + Roundness = 20, + Radius = 50, + }, + }, + mainGlow2 = new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Size = new Vector2(0.01f, initial_height), + Blending = BlendingParameters.Additive, + Rotation = RNG.NextSingle(-angle_variangle, angle_variangle), + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = colour, + Roundness = roundness, + Radius = 40, + }, + }, + mainGlow3 = new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Size = new Vector2(0.01f, initial_height), + Blending = BlendingParameters.Additive, + Rotation = RNG.NextSingle(-angle_variangle, angle_variangle), + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = colour, + Roundness = roundness, + Radius = 40, + }, } }; } protected override void LoadComplete() { + const double duration = 200; + base.LoadComplete(); - circle.ResizeTo(circle.Size * new Vector2(4, 20), 1000, Easing.OutQuint); - this.FadeIn(16).Then().FadeOut(500, Easing.OutQuint); + largeFaint + .ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint) + .FadeOut(duration * 2); + mainGlow1.ScaleTo(1.4f, duration, Easing.OutQuint); + + this.FadeOut(duration, Easing.Out); Expire(true); } } From 6bfdadb22fb1dcaaf172e5694a955c812c4bd030 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Sep 2019 18:20:41 +0900 Subject: [PATCH 2687/2854] Increase column width --- osu.Game.Rulesets.Mania/UI/Column.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 91dd236ab1..0caee025b6 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -11,6 +11,8 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Rulesets.UI.Scrolling; using osuTK; @@ -19,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.UI { public class Column : ScrollingPlayfield, IKeyBindingHandler, IHasAccentColour { - private const float column_width = 45; + public const float COLUMN_WIDTH = 80; private const float special_column_width = 70; /// @@ -41,10 +43,7 @@ namespace osu.Game.Rulesets.Mania.UI Index = index; RelativeSizeAxes = Axes.Y; - Width = column_width; - - Masking = true; - CornerRadius = 5; + Width = COLUMN_WIDTH; background = new ColumnBackground { RelativeSizeAxes = Axes.Both }; @@ -67,7 +66,7 @@ namespace osu.Game.Rulesets.Mania.UI explosionContainer = new Container { Name = "Hit explosions", - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, } } }, @@ -108,7 +107,7 @@ namespace osu.Game.Rulesets.Mania.UI isSpecial = value; - Width = isSpecial ? special_column_width : column_width; + Width = isSpecial ? special_column_width : COLUMN_WIDTH; } } From c7186efd5339a954eaf67682c01fb184e669b455 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Sep 2019 18:21:29 +0900 Subject: [PATCH 2688/2854] Reduce opacity of judgement area --- osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs index a0d713067d..386bcbb724 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; +using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osuTK.Graphics; @@ -17,7 +18,6 @@ namespace osu.Game.Rulesets.Mania.UI.Components { public class ColumnHitObjectArea : CompositeDrawable, IHasAccentColour { - private const float hit_target_height = 10; private const float hit_target_bar_height = 2; private readonly IBindable direction = new Bindable(); @@ -32,7 +32,8 @@ namespace osu.Game.Rulesets.Mania.UI.Components hitTargetBar = new Box { RelativeSizeAxes = Axes.X, - Height = hit_target_height, + Height = NotePiece.NOTE_HEIGHT, + Alpha = 0.6f, Colour = Color4.Black }, hitTargetLine = new Container From b9e71d26b285922d7c0d0a7e3f2d0bcc60628220 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Sep 2019 18:21:39 +0900 Subject: [PATCH 2689/2854] Dim column backgrounds further --- osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs index 5ee78aa496..57241da564 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs @@ -35,7 +35,6 @@ namespace osu.Game.Rulesets.Mania.UI.Components { Name = "Background", RelativeSizeAxes = Axes.Both, - Alpha = 0.3f }, backgroundOverlay = new Box { @@ -82,7 +81,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components if (!IsLoaded) return; - background.Colour = AccentColour; + background.Colour = AccentColour.Darken(5); var brightPoint = AccentColour.Opacity(0.6f); var dimPoint = AccentColour.Opacity(0); From 06618b6d02e7f358d1929cf5bf19b1e6f22c269a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Sep 2019 18:45:47 +0900 Subject: [PATCH 2690/2854] Fix osu!mania minor barline alpha not being respected --- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs index 862af8d15b..be21610525 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs @@ -68,6 +68,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Alpha = 0.2f; } + protected override void UpdateInitialTransforms() + { + } + protected override void UpdateStateTransforms(ArmedState state) { } From 039b5ec958fcac03296e31791721b2e786c17de7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Sep 2019 18:47:25 +0900 Subject: [PATCH 2691/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index f76297c197..45c162a30e 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 791d2fe285..df8b11e653 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 0560c45edf..7c31744a14 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -118,8 +118,8 @@ - - + + From be66c0e9127982166e393e697daa9d4c2d5db938 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Sep 2019 19:06:31 +0900 Subject: [PATCH 2692/2854] Fix potential of toggle between load and LoadComplete --- .../Graphics/Containers/OsuFocusedOverlayContainer.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 0ce095d44f..a1df973b60 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -41,6 +41,12 @@ namespace osu.Game.Graphics.Containers [BackgroundDependencyLoader(true)] private void load(AudioManager audio) + { + samplePopIn = audio.Samples.Get(@"UI/overlay-pop-in"); + samplePopOut = audio.Samples.Get(@"UI/overlay-pop-out"); + } + + protected override void LoadComplete() { OverlayActivationMode.ValueChanged += mode => { @@ -51,8 +57,7 @@ namespace osu.Game.Graphics.Containers if (game != null) OverlayActivationMode.BindTo(game.OverlayActivationMode); - samplePopIn = audio.Samples.Get(@"UI/overlay-pop-in"); - samplePopOut = audio.Samples.Get(@"UI/overlay-pop-out"); + base.LoadComplete(); } /// From 55a071e8ba9bfe68ba237daa81a1024a43366d92 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Sep 2019 19:12:55 +0900 Subject: [PATCH 2693/2854] Use BindValueChanged --- .../Graphics/Containers/OsuFocusedOverlayContainer.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index a1df973b60..2e8910213b 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -48,14 +48,14 @@ namespace osu.Game.Graphics.Containers protected override void LoadComplete() { - OverlayActivationMode.ValueChanged += mode => + if (game != null) + OverlayActivationMode.BindTo(game.OverlayActivationMode); + + OverlayActivationMode.BindValueChanged(mode => { if (mode.NewValue == OverlayActivation.Disabled) State.Value = Visibility.Hidden; - }; - - if (game != null) - OverlayActivationMode.BindTo(game.OverlayActivationMode); + }, true); base.LoadComplete(); } From dbfbd1262fd1caffd5d657d1bbf6ef24d2b66997 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 11 Sep 2019 23:39:22 +0300 Subject: [PATCH 2694/2854] Implement HeaderTitle component for RankingsOverlay --- .../Online/TestSceneRankingsHeaderTitle.cs | 60 ++++++++++ osu.Game/Overlays/Rankings/HeaderTitle.cs | 105 ++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs create mode 100644 osu.Game/Overlays/Rankings/HeaderTitle.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs new file mode 100644 index 0000000000..0f16b2592f --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs @@ -0,0 +1,60 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Overlays.Rankings; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneRankingsHeaderTitle : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(HeaderFlag), + typeof(HeaderTitle), + }; + + public TestSceneRankingsHeaderTitle() + { + var countryBindable = new Bindable(); + var scope = new Bindable(); + + Add(new HeaderTitle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Country = { BindTarget = countryBindable }, + Scope = { BindTarget = scope }, + }); + + var countryA = new Country + { + FlagName = "BY", + FullName = "Belarus" + }; + + var countryB = new Country + { + FlagName = "US", + FullName = "United States" + }; + + AddStep("Set country", () => countryBindable.Value = countryA); + AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance); + AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score); + AddAssert("Check country is Null", () => countryBindable.Value == null); + + AddStep("Set country 1", () => countryBindable.Value = countryA); + AddStep("Set country 2", () => countryBindable.Value = countryB); + AddStep("Set null country", () => countryBindable.Value = null); + AddStep("Set scope to Performance", () => scope.Value = RankingsScope.Performance); + AddStep("Set scope to Spotlights", () => scope.Value = RankingsScope.Spotlights); + AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score); + AddStep("Set scope to Country", () => scope.Value = RankingsScope.Country); + } + } +} diff --git a/osu.Game/Overlays/Rankings/HeaderTitle.cs b/osu.Game/Overlays/Rankings/HeaderTitle.cs new file mode 100644 index 0000000000..3f1feb10b8 --- /dev/null +++ b/osu.Game/Overlays/Rankings/HeaderTitle.cs @@ -0,0 +1,105 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Users; +using osu.Framework.Graphics; +using osuTK; +using osu.Game.Graphics; +using osu.Framework.Allocation; + +namespace osu.Game.Overlays.Rankings +{ + public class HeaderTitle : CompositeDrawable + { + private const int spacing = 10; + private const int flag_margin = 5; + private const int text_size = 40; + + public readonly Bindable Scope = new Bindable(); + public readonly Bindable Country = new Bindable(); + + private readonly SpriteText scopeText; + private readonly Container flagPlaceholder; + private readonly HeaderFlag flag; + + public HeaderTitle() + { + AutoSizeAxes = Axes.Both; + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(spacing, 0), + Children = new Drawable[] + { + flagPlaceholder = new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Bottom = flag_margin }, + Child = flag = new HeaderFlag + { + Size = new Vector2(30, 20), + }, + }, + scopeText = new SpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Light) + }, + new SpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Light), + Text = @"Ranking" + } + } + }; + + flag.Action += () => Country.Value = null; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + scopeText.Colour = colours.Lime; + } + + protected override void LoadComplete() + { + Scope.BindValueChanged(scope => onScopeChanged(scope.NewValue), true); + Country.BindValueChanged(onCountryChanged, true); + base.LoadComplete(); + } + + private void onScopeChanged(RankingsScope scope) + { + scopeText.Text = scope.ToString(); + + if (scope != RankingsScope.Performance) + Country.Value = null; + } + + private void onCountryChanged(ValueChangedEvent country) + { + if (country.NewValue == null) + { + flagPlaceholder.Hide(); + return; + } + + Scope.Value = RankingsScope.Performance; + + if (country.OldValue == null) + flagPlaceholder.Show(); + + flag.Country = country.NewValue; + } + } +} From e0bf579b18eaddd62a85c9333a2375403b2d2e14 Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 11 Sep 2019 15:35:47 -0700 Subject: [PATCH 2695/2854] Properly fix dialog overlay playing double samples on show/hide --- .../Containers/OsuFocusedOverlayContainer.cs | 6 ++---- osu.Game/Overlays/Dialog/PopupDialog.cs | 20 +------------------ osu.Game/Overlays/DialogOverlay.cs | 16 +++++++++++++-- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 2e8910213b..b117d71006 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -21,8 +21,6 @@ namespace osu.Game.Graphics.Containers private SampleChannel samplePopIn; private SampleChannel samplePopOut; - protected virtual bool PlaySamplesOnStateChange => true; - protected override bool BlockNonPositionalInput => true; /// @@ -126,12 +124,12 @@ namespace osu.Game.Graphics.Containers return; } - if (PlaySamplesOnStateChange) samplePopIn?.Play(); + samplePopIn?.Play(); if (BlockScreenWideMouse && DimMainContent) game?.AddBlockingOverlay(this); break; case Visibility.Hidden: - if (PlaySamplesOnStateChange) samplePopOut?.Play(); + samplePopOut?.Play(); if (BlockScreenWideMouse) game?.RemoveBlockingOverlay(this); break; } diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 1022edfe81..5c0ddb47b1 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -13,20 +13,17 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; -using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; using osuTK.Input; namespace osu.Game.Overlays.Dialog { - public class PopupDialog : OsuFocusedOverlayContainer + public class PopupDialog : VisibilityContainer { public static readonly float ENTER_DURATION = 500; public static readonly float EXIT_DURATION = 200; - protected override bool BlockPositionalInput => false; - private readonly Vector2 ringSize = new Vector2(100f); private readonly Vector2 ringMinifiedSize = new Vector2(20f); private readonly Vector2 buttonsEnterSpacing = new Vector2(0f, 50f); @@ -202,18 +199,6 @@ namespace osu.Game.Overlays.Dialog }; } - public override bool OnPressed(GlobalAction action) - { - switch (action) - { - case GlobalAction.Select: - Buttons.OfType().FirstOrDefault()?.Click(); - return true; - } - - return base.OnPressed(action); - } - protected override bool OnKeyDown(KeyDownEvent e) { if (e.Repeat) return false; @@ -238,8 +223,6 @@ namespace osu.Game.Overlays.Dialog protected override void PopIn() { - base.PopIn(); - actionInvoked = false; // Reset various animations but only if the dialog animation fully completed @@ -263,7 +246,6 @@ namespace osu.Game.Overlays.Dialog // This is presumed to always be a sane default "cancel" action. buttonsContainer.Last().Click(); - base.PopOut(); content.FadeOut(EXIT_DURATION, Easing.InSine); } diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index aaae7bcf5c..0d3c96c984 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -5,6 +5,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Overlays.Dialog; using osu.Game.Graphics.Containers; +using osu.Game.Input.Bindings; +using System.Linq; namespace osu.Game.Overlays { @@ -41,8 +43,6 @@ namespace osu.Game.Overlays Show(); } - protected override bool PlaySamplesOnStateChange => false; - protected override bool BlockNonPositionalInput => true; private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v) @@ -74,5 +74,17 @@ namespace osu.Game.Overlays this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine); } + + public override bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.Select: + currentDialog.Buttons.OfType().FirstOrDefault()?.Click(); + return true; + } + + return base.OnPressed(action); + } } } From 77ac186cf803e02861197032cfda700383933667 Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 11 Sep 2019 16:08:01 -0700 Subject: [PATCH 2696/2854] Add spacing to mod icons on leaderboards --- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 1 + osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs | 1 + .../Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 008f8208eb..0b84cfc28a 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -215,6 +215,7 @@ namespace osu.Game.Online.Leaderboards Origin = Anchor.BottomRight, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, + Spacing = new Vector2(1), ChildrenEnumerable = score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) }) }, }, diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 347522fb48..58f5f02956 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -171,6 +171,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { Direction = FillDirection.Horizontal, AutoSizeAxes = Axes.Both, + Spacing = new Vector2(1), ChildrenEnumerable = score.Mods.Select(m => new ModIcon(m) { AutoSizeAxes = Axes.Both, diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs index 6761d0f710..b9664d7c2f 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs @@ -172,7 +172,8 @@ namespace osu.Game.Overlays.BeatmapSet.Scores : this(new FillFlowContainer { AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal + Direction = FillDirection.Horizontal, + Spacing = new Vector2(1), }) { } From c3c2efe35ca966df9cbde9925417fedbe0000637 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 12 Sep 2019 05:03:59 +0300 Subject: [PATCH 2697/2854] Add ability to override text in PageTabItem --- osu.Game/Graphics/UserInterface/PageTabControl.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/PageTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs index a0d3745180..ddcb626701 100644 --- a/osu.Game/Graphics/UserInterface/PageTabControl.cs +++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs @@ -63,7 +63,7 @@ namespace osu.Game.Graphics.UserInterface Margin = new MarginPadding { Top = 8, Bottom = 8 }, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, - Text = (value as Enum)?.GetDescription() ?? value.ToString(), + Text = CreateText(), Font = OsuFont.GetFont(size: 14) }, box = new Box @@ -81,6 +81,8 @@ namespace osu.Game.Graphics.UserInterface Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Exo, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true); } + protected virtual string CreateText() => (Value as Enum)?.GetDescription() ?? Value.ToString(); + protected override bool OnHover(HoverEvent e) { if (!Active.Value) From 581508b8e75f51560398b06095d27e860afd89c1 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 12 Sep 2019 05:06:51 +0300 Subject: [PATCH 2698/2854] Implement RankingsRulesetSelector --- .../TestSceneRankingsRulesetSelector.cs | 42 ++++++++++++++ .../Rankings/RankingsRulesetSelector.cs | 56 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneRankingsRulesetSelector.cs create mode 100644 osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsRulesetSelector.cs new file mode 100644 index 0000000000..8ad10c6a63 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsRulesetSelector.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Game.Overlays.Rankings; +using osu.Framework.Graphics; +using osu.Game.Rulesets; +using osu.Framework.Bindables; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Taiko; +using osu.Game.Rulesets.Catch; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneRankingsRulesetSelector : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(RankingsRulesetSelector), + }; + + public TestSceneRankingsRulesetSelector() + { + RankingsRulesetSelector selector; + var current = new Bindable(); + + Add(selector = new RankingsRulesetSelector + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Current = { BindTarget = current } + }); + + AddStep("Select osu!", () => current.Value = new OsuRuleset().RulesetInfo); + AddStep("Select mania", () => current.Value = new ManiaRuleset().RulesetInfo); + AddStep("Select taiko", () => current.Value = new TaikoRuleset().RulesetInfo); + AddStep("Select catch", () => current.Value = new CatchRuleset().RulesetInfo); + } + } +} diff --git a/osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs b/osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs new file mode 100644 index 0000000000..f1666507d1 --- /dev/null +++ b/osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs @@ -0,0 +1,56 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets; +using osuTK; +using System.Linq; + +namespace osu.Game.Overlays.Rankings +{ + public class RankingsRulesetSelector : PageTabControl + { + protected override TabItem CreateTabItem(RulesetInfo value) => new RankingsTabItem(value); + + protected override Dropdown CreateDropdown() => null; + + public RankingsRulesetSelector() + { + AutoSizeAxes = Axes.X; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, RulesetStore Rulesets) + { + foreach (var r in Rulesets.AvailableRulesets) + AddItem(r); + + AccentColour = colours.Lime; + + SelectTab(TabContainer.FirstOrDefault()); + } + + protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(20, 0), + }; + + private class RankingsTabItem : PageTabItem + { + public RankingsTabItem(RulesetInfo value) + : base(value) + { + } + + protected override string CreateText() => $"{Value.Name}"; + } + } +} From 4bfb681db6f816fd623e0ef407e86c41980c1c5a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 12 Sep 2019 05:16:56 +0300 Subject: [PATCH 2699/2854] CI fixes --- .../Visual/Online/TestSceneRankingsRulesetSelector.cs | 3 +-- osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsRulesetSelector.cs index 8ad10c6a63..84515bd3a4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsRulesetSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsRulesetSelector.cs @@ -23,10 +23,9 @@ namespace osu.Game.Tests.Visual.Online public TestSceneRankingsRulesetSelector() { - RankingsRulesetSelector selector; var current = new Bindable(); - Add(selector = new RankingsRulesetSelector + Add(new RankingsRulesetSelector { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs b/osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs index f1666507d1..3d25e3995a 100644 --- a/osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs +++ b/osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs @@ -25,9 +25,9 @@ namespace osu.Game.Overlays.Rankings } [BackgroundDependencyLoader] - private void load(OsuColour colours, RulesetStore Rulesets) + private void load(OsuColour colours, RulesetStore rulesets) { - foreach (var r in Rulesets.AvailableRulesets) + foreach (var r in rulesets.AvailableRulesets) AddItem(r); AccentColour = colours.Lime; From b657e31f93b2b9fce626d0f151f6d36a565fd809 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 12 Sep 2019 05:26:10 +0300 Subject: [PATCH 2700/2854] Merge dependent changes --- .../Online/TestSceneRankingsHeaderFlag.cs | 65 +++++++++++ .../Online/TestSceneRankingsHeaderTitle.cs | 60 ++++++++++ .../TestSceneRankingsRulesetSelector.cs | 41 +++++++ .../Online/TestSceneRankingsScopeSelector.cs | 54 +++++++++ .../UserInterface/GradientLineTabControl.cs | 103 +++++++++++++++++ .../Graphics/UserInterface/PageTabControl.cs | 4 +- .../BeatmapSet/LeaderboardScopeSelector.cs | 69 +----------- osu.Game/Overlays/Rankings/HeaderFlag.cs | 55 +++++++++ osu.Game/Overlays/Rankings/HeaderTitle.cs | 105 ++++++++++++++++++ .../Rankings/RankingsRulesetSelector.cs | 56 ++++++++++ .../Rankings/RankingsScopeSelector.cs | 26 +++++ 11 files changed, 572 insertions(+), 66 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs create mode 100644 osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs create mode 100644 osu.Game.Tests/Visual/Online/TestSceneRankingsRulesetSelector.cs create mode 100644 osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs create mode 100644 osu.Game/Graphics/UserInterface/GradientLineTabControl.cs create mode 100644 osu.Game/Overlays/Rankings/HeaderFlag.cs create mode 100644 osu.Game/Overlays/Rankings/HeaderTitle.cs create mode 100644 osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs create mode 100644 osu.Game/Overlays/Rankings/RankingsScopeSelector.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs new file mode 100644 index 0000000000..c9531e1016 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs @@ -0,0 +1,65 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Overlays.Rankings; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneRankingsHeaderFlag : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(HeaderFlag), + }; + + public TestSceneRankingsHeaderFlag() + { + HeaderFlag flag; + SpriteText text; + + var countryA = new Country + { + FlagName = "BY", + FullName = "Belarus" + }; + + var countryB = new Country + { + FlagName = "US", + FullName = "United States" + }; + + AddRange(new Drawable[] + { + flag = new HeaderFlag + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(30, 20), + Country = countryA, + }, + text = new SpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "Invoked", + Font = OsuFont.GetFont(size: 30), + Alpha = 0, + } + }); + + flag.Action += () => text.FadeIn().Then().FadeOut(1000, Easing.OutQuint); + + AddStep("Trigger click", () => flag.Click()); + AddStep("Change to country 2", () => flag.Country = countryB); + AddStep("Change to country 1", () => flag.Country = countryA); + } + } +} diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs new file mode 100644 index 0000000000..0f16b2592f --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs @@ -0,0 +1,60 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Overlays.Rankings; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneRankingsHeaderTitle : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(HeaderFlag), + typeof(HeaderTitle), + }; + + public TestSceneRankingsHeaderTitle() + { + var countryBindable = new Bindable(); + var scope = new Bindable(); + + Add(new HeaderTitle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Country = { BindTarget = countryBindable }, + Scope = { BindTarget = scope }, + }); + + var countryA = new Country + { + FlagName = "BY", + FullName = "Belarus" + }; + + var countryB = new Country + { + FlagName = "US", + FullName = "United States" + }; + + AddStep("Set country", () => countryBindable.Value = countryA); + AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance); + AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score); + AddAssert("Check country is Null", () => countryBindable.Value == null); + + AddStep("Set country 1", () => countryBindable.Value = countryA); + AddStep("Set country 2", () => countryBindable.Value = countryB); + AddStep("Set null country", () => countryBindable.Value = null); + AddStep("Set scope to Performance", () => scope.Value = RankingsScope.Performance); + AddStep("Set scope to Spotlights", () => scope.Value = RankingsScope.Spotlights); + AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score); + AddStep("Set scope to Country", () => scope.Value = RankingsScope.Country); + } + } +} diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsRulesetSelector.cs new file mode 100644 index 0000000000..84515bd3a4 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsRulesetSelector.cs @@ -0,0 +1,41 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Game.Overlays.Rankings; +using osu.Framework.Graphics; +using osu.Game.Rulesets; +using osu.Framework.Bindables; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Taiko; +using osu.Game.Rulesets.Catch; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneRankingsRulesetSelector : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(RankingsRulesetSelector), + }; + + public TestSceneRankingsRulesetSelector() + { + var current = new Bindable(); + + Add(new RankingsRulesetSelector + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Current = { BindTarget = current } + }); + + AddStep("Select osu!", () => current.Value = new OsuRuleset().RulesetInfo); + AddStep("Select mania", () => current.Value = new ManiaRuleset().RulesetInfo); + AddStep("Select taiko", () => current.Value = new TaikoRuleset().RulesetInfo); + AddStep("Select catch", () => current.Value = new CatchRuleset().RulesetInfo); + } + } +} diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs new file mode 100644 index 0000000000..2081a6c0cb --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs @@ -0,0 +1,54 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Bindables; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Overlays.Rankings; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneRankingsScopeSelector : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(RankingsScopeSelector), + }; + + private readonly Box background; + + public TestSceneRankingsScopeSelector() + { + var scope = new Bindable(); + + AddRange(new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new RankingsScopeSelector + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Current = { BindTarget = scope } + } + }); + + AddStep(@"Select country", () => scope.Value = RankingsScope.Country); + AddStep(@"Select performance", () => scope.Value = RankingsScope.Performance); + AddStep(@"Select score", () => scope.Value = RankingsScope.Score); + AddStep(@"Select spotlights", () => scope.Value = RankingsScope.Spotlights); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.GreySeafoam; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs new file mode 100644 index 0000000000..7cd8d2c5bd --- /dev/null +++ b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs @@ -0,0 +1,103 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osuTK; +using osu.Framework.Graphics.Shapes; +using osuTK.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; + +namespace osu.Game.Graphics.UserInterface +{ + public class GradientLineTabControl : PageTabControl + { + protected override Dropdown CreateDropdown() => null; + + protected Color4 LineColour + { + get => line.MainColour.Value; + set => line.MainColour.Value = value; + } + + private readonly GradientLine line; + + public GradientLineTabControl() + { + RelativeSizeAxes = Axes.X; + + AddInternal(line = new GradientLine + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }); + } + + protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(20, 0), + }; + + private class GradientLine : GridContainer + { + public readonly Bindable MainColour = new Bindable(); + + private readonly Box left; + private readonly Box middle; + private readonly Box right; + + public GradientLine() + { + RelativeSizeAxes = Axes.X; + Size = new Vector2(0.8f, 1.5f); + + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(mode: GridSizeMode.Relative, size: 0.4f), + new Dimension(), + }; + + Content = new[] + { + new Drawable[] + { + left = new Box + { + RelativeSizeAxes = Axes.Both, + }, + middle = new Box + { + RelativeSizeAxes = Axes.Both, + }, + right = new Box + { + RelativeSizeAxes = Axes.Both, + }, + } + }; + } + + protected override void LoadComplete() + { + MainColour.BindValueChanged(onColourChanged, true); + base.LoadComplete(); + } + + private void onColourChanged(ValueChangedEvent colour) + { + left.Colour = ColourInfo.GradientHorizontal(colour.NewValue.Opacity(0), colour.NewValue); + middle.Colour = colour.NewValue; + right.Colour = ColourInfo.GradientHorizontal(colour.NewValue, colour.NewValue.Opacity(0)); + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/PageTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs index a0d3745180..ddcb626701 100644 --- a/osu.Game/Graphics/UserInterface/PageTabControl.cs +++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs @@ -63,7 +63,7 @@ namespace osu.Game.Graphics.UserInterface Margin = new MarginPadding { Top = 8, Bottom = 8 }, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, - Text = (value as Enum)?.GetDescription() ?? value.ToString(), + Text = CreateText(), Font = OsuFont.GetFont(size: 14) }, box = new Box @@ -81,6 +81,8 @@ namespace osu.Game.Graphics.UserInterface Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Exo, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true); } + protected virtual string CreateText() => (Value as Enum)?.GetDescription() ?? Value.ToString(); + protected override bool OnHover(HoverEvent e) { if (!Active.Value) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs index dcd58db427..e2a725ec46 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs @@ -1,60 +1,37 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics.UserInterface; using osu.Game.Screens.Select.Leaderboards; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osuTK; using osu.Game.Graphics.UserInterface; -using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Framework.Allocation; using osuTK.Graphics; -using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; +using osu.Framework.Graphics; namespace osu.Game.Overlays.BeatmapSet { - public class LeaderboardScopeSelector : PageTabControl + public class LeaderboardScopeSelector : GradientLineTabControl { protected override bool AddEnumEntriesAutomatically => false; - protected override Dropdown CreateDropdown() => null; - protected override TabItem CreateTabItem(BeatmapLeaderboardScope value) => new ScopeSelectorTabItem(value); public LeaderboardScopeSelector() { - RelativeSizeAxes = Axes.X; - AddItem(BeatmapLeaderboardScope.Global); AddItem(BeatmapLeaderboardScope.Country); AddItem(BeatmapLeaderboardScope.Friend); - - AddInternal(new GradientLine - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - }); } [BackgroundDependencyLoader] private void load(OsuColour colours) { AccentColour = colours.Blue; + LineColour = Color4.Gray; } - protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(20, 0), - }; - private class ScopeSelectorTabItem : PageTabItem { public ScopeSelectorTabItem(BeatmapLeaderboardScope value) @@ -77,43 +54,5 @@ namespace osu.Game.Overlays.BeatmapSet Text.FadeColour(Color4.White); } } - - private class GradientLine : GridContainer - { - public GradientLine() - { - RelativeSizeAxes = Axes.X; - Size = new Vector2(0.8f, 1.5f); - - ColumnDimensions = new[] - { - new Dimension(), - new Dimension(mode: GridSizeMode.Relative, size: 0.4f), - new Dimension(), - }; - - Content = new[] - { - new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(Color4.Transparent, Color4.Gray), - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Gray, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(Color4.Gray, Color4.Transparent), - }, - } - }; - } - } } } diff --git a/osu.Game/Overlays/Rankings/HeaderFlag.cs b/osu.Game/Overlays/Rankings/HeaderFlag.cs new file mode 100644 index 0000000000..6f641c74a5 --- /dev/null +++ b/osu.Game/Overlays/Rankings/HeaderFlag.cs @@ -0,0 +1,55 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Users.Drawables; +using osuTK.Graphics; +using osuTK; +using osu.Framework.Input.Events; +using System; + +namespace osu.Game.Overlays.Rankings +{ + public class HeaderFlag : UpdateableFlag + { + private const int duration = 200; + + public Action Action; + + private readonly SpriteIcon hoverIcon; + + public HeaderFlag() + { + AddInternal(hoverIcon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Depth = -1, + Alpha = 0, + Size = new Vector2(10), + Icon = FontAwesome.Solid.Times, + }); + } + + protected override bool OnHover(HoverEvent e) + { + hoverIcon.FadeIn(duration, Easing.OutQuint); + this.FadeColour(Color4.Gray, duration, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + hoverIcon.FadeOut(duration, Easing.OutQuint); + this.FadeColour(Color4.White, duration, Easing.OutQuint); + } + + protected override bool OnClick(ClickEvent e) + { + Action?.Invoke(); + return true; + } + } +} diff --git a/osu.Game/Overlays/Rankings/HeaderTitle.cs b/osu.Game/Overlays/Rankings/HeaderTitle.cs new file mode 100644 index 0000000000..3f1feb10b8 --- /dev/null +++ b/osu.Game/Overlays/Rankings/HeaderTitle.cs @@ -0,0 +1,105 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Users; +using osu.Framework.Graphics; +using osuTK; +using osu.Game.Graphics; +using osu.Framework.Allocation; + +namespace osu.Game.Overlays.Rankings +{ + public class HeaderTitle : CompositeDrawable + { + private const int spacing = 10; + private const int flag_margin = 5; + private const int text_size = 40; + + public readonly Bindable Scope = new Bindable(); + public readonly Bindable Country = new Bindable(); + + private readonly SpriteText scopeText; + private readonly Container flagPlaceholder; + private readonly HeaderFlag flag; + + public HeaderTitle() + { + AutoSizeAxes = Axes.Both; + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(spacing, 0), + Children = new Drawable[] + { + flagPlaceholder = new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Bottom = flag_margin }, + Child = flag = new HeaderFlag + { + Size = new Vector2(30, 20), + }, + }, + scopeText = new SpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Light) + }, + new SpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Light), + Text = @"Ranking" + } + } + }; + + flag.Action += () => Country.Value = null; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + scopeText.Colour = colours.Lime; + } + + protected override void LoadComplete() + { + Scope.BindValueChanged(scope => onScopeChanged(scope.NewValue), true); + Country.BindValueChanged(onCountryChanged, true); + base.LoadComplete(); + } + + private void onScopeChanged(RankingsScope scope) + { + scopeText.Text = scope.ToString(); + + if (scope != RankingsScope.Performance) + Country.Value = null; + } + + private void onCountryChanged(ValueChangedEvent country) + { + if (country.NewValue == null) + { + flagPlaceholder.Hide(); + return; + } + + Scope.Value = RankingsScope.Performance; + + if (country.OldValue == null) + flagPlaceholder.Show(); + + flag.Country = country.NewValue; + } + } +} diff --git a/osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs b/osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs new file mode 100644 index 0000000000..3d25e3995a --- /dev/null +++ b/osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs @@ -0,0 +1,56 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets; +using osuTK; +using System.Linq; + +namespace osu.Game.Overlays.Rankings +{ + public class RankingsRulesetSelector : PageTabControl + { + protected override TabItem CreateTabItem(RulesetInfo value) => new RankingsTabItem(value); + + protected override Dropdown CreateDropdown() => null; + + public RankingsRulesetSelector() + { + AutoSizeAxes = Axes.X; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, RulesetStore rulesets) + { + foreach (var r in rulesets.AvailableRulesets) + AddItem(r); + + AccentColour = colours.Lime; + + SelectTab(TabContainer.FirstOrDefault()); + } + + protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(20, 0), + }; + + private class RankingsTabItem : PageTabItem + { + public RankingsTabItem(RulesetInfo value) + : base(value) + { + } + + protected override string CreateText() => $"{Value.Name}"; + } + } +} diff --git a/osu.Game/Overlays/Rankings/RankingsScopeSelector.cs b/osu.Game/Overlays/Rankings/RankingsScopeSelector.cs new file mode 100644 index 0000000000..2095bcc61c --- /dev/null +++ b/osu.Game/Overlays/Rankings/RankingsScopeSelector.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Graphics.UserInterface; +using osu.Framework.Allocation; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Rankings +{ + public class RankingsScopeSelector : GradientLineTabControl + { + [BackgroundDependencyLoader] + private void load() + { + AccentColour = LineColour = Color4.Black; + } + } + + public enum RankingsScope + { + Performance, + Spotlights, + Score, + Country + } +} From 0c6c8fdcd0e97e74979ae9d248241050f0bf5149 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 12 Sep 2019 05:53:18 +0300 Subject: [PATCH 2701/2854] Implement RankingsHeader component --- .../Visual/Online/TestSceneRankingsHeader.cs | 52 ++++++++++++ osu.Game/Overlays/Rankings/RankingsHeader.cs | 84 +++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs create mode 100644 osu.Game/Overlays/Rankings/RankingsHeader.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs new file mode 100644 index 0000000000..81534e7d44 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs @@ -0,0 +1,52 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Overlays.Rankings; +using osu.Game.Rulesets; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneRankingsHeader : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(HeaderFlag), + typeof(HeaderTitle), + typeof(RankingsRulesetSelector), + typeof(RankingsScopeSelector), + typeof(RankingsHeader), + }; + + public TestSceneRankingsHeader() + { + var countryBindable = new Bindable(); + var ruleset = new Bindable(); + var scope = new Bindable(); + + Add(new RankingsHeader + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scope = { BindTarget = scope }, + Country = { BindTarget = countryBindable }, + Ruleset = { BindTarget = ruleset }, + }); + + var country = new Country + { + FlagName = "BY", + FullName = "Belarus" + }; + + AddStep("Set country", () => countryBindable.Value = country); + AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance); + AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score); + AddAssert("Check country is Null", () => countryBindable.Value == null); + } + } +} diff --git a/osu.Game/Overlays/Rankings/RankingsHeader.cs b/osu.Game/Overlays/Rankings/RankingsHeader.cs new file mode 100644 index 0000000000..7fdc2ab9c8 --- /dev/null +++ b/osu.Game/Overlays/Rankings/RankingsHeader.cs @@ -0,0 +1,84 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Framework.Bindables; +using osu.Game.Rulesets; +using osu.Game.Users; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; + +namespace osu.Game.Overlays.Rankings +{ + public class RankingsHeader : CompositeDrawable + { + private const int content_height = 250; + + public readonly Bindable Scope = new Bindable(); + public readonly Bindable Ruleset = new Bindable(); + public readonly Bindable Country = new Bindable(); + + public RankingsHeader() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + AddInternal(new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new RankingsRulesetSelector + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Current = { BindTarget = Ruleset } + }, + new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + Height = content_height, + Children = new Drawable[] + { + new HeaderBackground(), + new RankingsScopeSelector + { + Margin = new MarginPadding { Top = 10 }, + Current = { BindTarget = Scope } + }, + new HeaderTitle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scope = { BindTarget = Scope }, + Country = { BindTarget = Country }, + } + } + } + } + }); + } + + public class HeaderBackground : Sprite + { + public HeaderBackground() + { + RelativeSizeAxes = Axes.Both; + FillMode = FillMode.Fill; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + Texture = textures.Get(@"Headers/rankings"); + } + } + } +} From acdd26422dc4c006f88b2eb0028113622b23a4ad Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 12 Sep 2019 06:36:17 +0300 Subject: [PATCH 2702/2854] Implement Spotlights logic --- .../Visual/Online/TestSceneRankingsHeader.cs | 18 +++++ osu.Game/Overlays/Rankings/RankingsHeader.cs | 74 +++++++++++++++++-- osu.Game/Overlays/Rankings/Spotlight.cs | 18 +++++ 3 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Overlays/Rankings/Spotlight.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs index 81534e7d44..e8ed94b59c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs @@ -35,6 +35,24 @@ namespace osu.Game.Tests.Visual.Online Scope = { BindTarget = scope }, Country = { BindTarget = countryBindable }, Ruleset = { BindTarget = ruleset }, + Spotlights = new[] + { + new Spotlight + { + Id = 1, + Text = "Spotlight 1" + }, + new Spotlight + { + Id = 2, + Text = "Spotlight 2" + }, + new Spotlight + { + Id = 3, + Text = "Spotlight 4" + } + } }); var country = new Country diff --git a/osu.Game/Overlays/Rankings/RankingsHeader.cs b/osu.Game/Overlays/Rankings/RankingsHeader.cs index 7fdc2ab9c8..6d55e92502 100644 --- a/osu.Game/Overlays/Rankings/RankingsHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsHeader.cs @@ -4,23 +4,38 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using osu.Framework.Allocation; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; using osu.Framework.Bindables; using osu.Game.Rulesets; using osu.Game.Users; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osuTK; +using osu.Game.Graphics.UserInterface; +using System.Collections.Generic; namespace osu.Game.Overlays.Rankings { public class RankingsHeader : CompositeDrawable { private const int content_height = 250; + private const int dropdown_height = 50; + private const int spacing = 20; + private const int title_offset = 30; + private const int duration = 200; + + public IEnumerable Spotlights + { + get => dropdown.Items; + set => dropdown.Items = value; + } public readonly Bindable Scope = new Bindable(); public readonly Bindable Ruleset = new Bindable(); public readonly Bindable Country = new Bindable(); + public readonly Bindable Spotlight = new Bindable(); + + private readonly Container dropdownPlaceholder; + private readonly OsuDropdown dropdown; public RankingsHeader() { @@ -47,29 +62,72 @@ namespace osu.Game.Overlays.Rankings Height = content_height, Children = new Drawable[] { - new HeaderBackground(), + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Child = new HeaderBackground(), + }, new RankingsScopeSelector { Margin = new MarginPadding { Top = 10 }, Current = { BindTarget = Scope } }, - new HeaderTitle + new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Scope = { BindTarget = Scope }, - Country = { BindTarget = Country }, - } + Y = title_offset, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, spacing), + Children = new Drawable[] + { + new HeaderTitle + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Scope = { BindTarget = Scope }, + Country = { BindTarget = Country }, + }, + dropdownPlaceholder = new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + Height = dropdown_height, + Width = 0.8f, + AlwaysPresent = true, + Child = dropdown = new OsuDropdown + { + RelativeSizeAxes = Axes.X, + Current = { BindTarget = Spotlight }, + } + } + } + }, } } } }); } - public class HeaderBackground : Sprite + protected override void LoadComplete() + { + Scope.BindValueChanged(scope => onScopeChanged(scope.NewValue), true); + base.LoadComplete(); + } + + private void onScopeChanged(RankingsScope scope) => + dropdownPlaceholder.FadeTo(scope == RankingsScope.Spotlights ? 1 : 0, duration, Easing.OutQuint); + + private class HeaderBackground : Sprite { public HeaderBackground() { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; RelativeSizeAxes = Axes.Both; FillMode = FillMode.Fill; } diff --git a/osu.Game/Overlays/Rankings/Spotlight.cs b/osu.Game/Overlays/Rankings/Spotlight.cs new file mode 100644 index 0000000000..e956b4f449 --- /dev/null +++ b/osu.Game/Overlays/Rankings/Spotlight.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Newtonsoft.Json; + +namespace osu.Game.Overlays.Rankings +{ + public class Spotlight + { + [JsonProperty("id")] + public int Id; + + [JsonProperty("text")] + public string Text; + + public override string ToString() => Text; + } +} From b1c0b080ecced55aecae5aa4f28078418d8ecbd7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Sep 2019 13:52:27 +0900 Subject: [PATCH 2703/2854] Fix bad hit explosion anchoring --- osu.Game.Rulesets.Mania/UI/Column.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index fa14a0a293..8021660f77 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -11,6 +11,8 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Rulesets.UI.Scrolling; using osuTK; @@ -90,7 +92,11 @@ namespace osu.Game.Rulesets.Mania.UI Bottom = dir.NewValue == ScrollingDirection.Down ? ManiaStage.HIT_TARGET_POSITION : 0, }; - explosionContainer.Y = dir.NewValue == ScrollingDirection.Down ? -NotePiece.NOTE_HEIGHT : 0; + explosionContainer.Padding = new MarginPadding + { + Top = dir.NewValue == ScrollingDirection.Up ? NotePiece.NOTE_HEIGHT / 2 : 0, + Bottom = dir.NewValue == ScrollingDirection.Down ? NotePiece.NOTE_HEIGHT / 2 : 0 + }; keyArea.Anchor = keyArea.Origin = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft; }, true); @@ -168,7 +174,7 @@ namespace osu.Game.Rulesets.Mania.UI explosionContainer.Add(new HitExplosion(judgedObject.AccentColour.Value, judgedObject is DrawableHoldNoteTick) { Anchor = Direction.Value == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre, - Origin = Direction.Value == ScrollingDirection.Up ? Anchor.BottomCentre : Anchor.TopCentre, + Origin = Anchor.Centre }); } From bbf80f63aa920b3a86b51142cd2b54d782fe5182 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Sep 2019 13:53:05 +0900 Subject: [PATCH 2704/2854] Publicly expose column width constant --- osu.Game.Rulesets.Mania/UI/Column.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 8021660f77..910342a3b0 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.UI { public class Column : ScrollingPlayfield, IKeyBindingHandler, IHasAccentColour { - private const float column_width = 45; + public const float COLUMN_WIDTH = 45; private const float special_column_width = 70; /// @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.UI Index = index; RelativeSizeAxes = Axes.Y; - Width = column_width; + Width = COLUMN_WIDTH; Masking = true; CornerRadius = 5; @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Mania.UI isSpecial = value; - Width = isSpecial ? special_column_width : column_width; + Width = isSpecial ? special_column_width : COLUMN_WIDTH; } } From f9c969788a758f913001047dca99aff00a853de8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Sep 2019 13:56:23 +0900 Subject: [PATCH 2705/2854] Fix keys not reaching full brightness as soon as they should --- osu.Game/Screens/Play/KeyCounter.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index 88a62ac8d4..ad5fcfcf24 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -130,15 +130,17 @@ namespace osu.Game.Screens.Play private void updateGlowSprite(bool show) { + double remainingFadeTime = FadeTime * (1 - glowSprite.Alpha); + if (show) { - glowSprite.FadeIn(FadeTime); - textLayer.FadeColour(KeyDownTextColor, FadeTime); + glowSprite.FadeIn(remainingFadeTime); + textLayer.FadeColour(KeyDownTextColor, remainingFadeTime); } else { - glowSprite.FadeOut(FadeTime); - textLayer.FadeColour(KeyUpTextColor, FadeTime); + glowSprite.FadeOut(remainingFadeTime); + textLayer.FadeColour(KeyUpTextColor, remainingFadeTime); } } From b941f12688eb2bb309e8d37cd3fc5f29beb582a1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Sep 2019 14:09:21 +0900 Subject: [PATCH 2706/2854] Cleanup --- .../TestSceneHitExplosion.cs | 22 ------------------- osu.Game.Rulesets.Mania/UI/HitExplosion.cs | 8 ++----- 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs index 12159ca239..26a1b1b1ec 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs @@ -5,11 +5,9 @@ using System; using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; -using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; using osuTK; @@ -60,25 +58,5 @@ namespace osu.Game.Rulesets.Mania.Tests }); }, 100); } - - private class TestNote : DrawableNote - { - protected override void CheckForResult(bool userTriggered, double timeOffset) - { - if (!userTriggered) - { - // force success - ApplyResult(r => r.Type = HitResult.Great); - } - else - base.CheckForResult(userTriggered, timeOffset); - } - - public TestNote(Note hitObject) - : base(hitObject) - { - AccentColour.Value = Color4.Pink; - } - } } } diff --git a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs index 21726206f1..ccbff226a9 100644 --- a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs @@ -18,8 +18,6 @@ namespace osu.Game.Rulesets.Mania.UI private readonly CircularContainer largeFaint; private readonly CircularContainer mainGlow1; - private readonly CircularContainer mainGlow2; - private readonly CircularContainer mainGlow3; public HitExplosion(Color4 objectColour, bool isSmall = false) { @@ -36,8 +34,6 @@ namespace osu.Game.Rulesets.Mania.UI const float roundness = 80; - const float opacity = 1; - const float initial_height = 10; var colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1); @@ -76,7 +72,7 @@ namespace osu.Game.Rulesets.Mania.UI Radius = 50, }, }, - mainGlow2 = new CircularContainer + new CircularContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -93,7 +89,7 @@ namespace osu.Game.Rulesets.Mania.UI Radius = 40, }, }, - mainGlow3 = new CircularContainer + new CircularContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 158737e001e46bdfd4f8ed85a48ab53ba81f70a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Sep 2019 14:27:29 +0900 Subject: [PATCH 2707/2854] Remove FadeTime customisation Also adjusts fade transitions to feel better, especially in fast forward scenarios. --- .../Visual/Gameplay/TestSceneKeyCounter.cs | 1 - osu.Game/Screens/Play/HUDOverlay.cs | 1 - osu.Game/Screens/Play/KeyCounter.cs | 14 +++++------ osu.Game/Screens/Play/KeyCounterDisplay.cs | 25 ++----------------- 4 files changed, 9 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index 18088a9a5b..4643b82792 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -44,7 +44,6 @@ namespace osu.Game.Tests.Visual.Gameplay Key key = (Key)((int)Key.A + RNG.Next(26)); kc.Add(new KeyCounterKeyboard(key)); }); - AddSliderStep("Fade time", 0, 200, 50, v => kc.FadeTime = v); Key testKey = ((KeyCounterKeyboard)kc.Children.First()).Key; double time1 = 0; diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index eee7235a6e..0f9edf5606 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -231,7 +231,6 @@ namespace osu.Game.Screens.Play protected virtual KeyCounterDisplay CreateKeyCounter() => new KeyCounterDisplay { - FadeTime = 50, Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, Margin = new MarginPadding(10), diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index ad5fcfcf24..1930369299 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -65,7 +65,7 @@ namespace osu.Game.Screens.Play //further: change default values here and in KeyCounterCollection if needed, instead of passing them in every constructor public Color4 KeyDownTextColor { get; set; } = Color4.DarkGray; public Color4 KeyUpTextColor { get; set; } = Color4.White; - public int FadeTime { get; set; } + public double FadeTime { get; set; } protected KeyCounter(string name) { @@ -130,17 +130,17 @@ namespace osu.Game.Screens.Play private void updateGlowSprite(bool show) { - double remainingFadeTime = FadeTime * (1 - glowSprite.Alpha); - if (show) { - glowSprite.FadeIn(remainingFadeTime); - textLayer.FadeColour(KeyDownTextColor, remainingFadeTime); + double remainingFadeTime = FadeTime * (1 - glowSprite.Alpha); + glowSprite.FadeIn(remainingFadeTime, Easing.OutQuint); + textLayer.FadeColour(KeyDownTextColor, remainingFadeTime, Easing.OutQuint); } else { - glowSprite.FadeOut(remainingFadeTime); - textLayer.FadeColour(KeyUpTextColor, remainingFadeTime); + double remainingFadeTime = 8 * FadeTime * glowSprite.Alpha; + glowSprite.FadeOut(remainingFadeTime, Easing.OutQuint); + textLayer.FadeColour(KeyUpTextColor, remainingFadeTime, Easing.OutQuint); } } diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/KeyCounterDisplay.cs index d5967f5899..6b8fa5c75b 100644 --- a/osu.Game/Screens/Play/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/KeyCounterDisplay.cs @@ -17,6 +17,7 @@ namespace osu.Game.Screens.Play public class KeyCounterDisplay : FillFlowContainer { private const int duration = 100; + private const double key_fade_time = 80; public readonly Bindable Visible = new Bindable(true); private readonly Bindable configVisibility = new Bindable(); @@ -33,17 +34,11 @@ namespace osu.Game.Screens.Play base.Add(key); key.IsCounting = IsCounting; - key.FadeTime = FadeTime; + key.FadeTime = key_fade_time; key.KeyDownTextColor = KeyDownTextColor; key.KeyUpTextColor = KeyUpTextColor; } - public void ResetCount() - { - foreach (var counter in Children) - counter.ResetCount(); - } - [BackgroundDependencyLoader] private void load(OsuConfigManager config) { @@ -68,22 +63,6 @@ namespace osu.Game.Screens.Play } } - private int fadeTime; - - public int FadeTime - { - get => fadeTime; - set - { - if (value != fadeTime) - { - fadeTime = value; - foreach (var child in Children) - child.FadeTime = value; - } - } - } - private Color4 keyDownTextColor = Color4.DarkGray; public Color4 KeyDownTextColor From cb0cf6e2c5e14e37d37716a79cec9b02b89d2aae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Sep 2019 14:27:52 +0900 Subject: [PATCH 2708/2854] Remove reset functions --- osu.Game/Screens/Play/KeyCounter.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index 1930369299..ad0858184e 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -144,12 +144,6 @@ namespace osu.Game.Screens.Play } } - public void ResetCount() - { - CountPresses = 0; - states.Clear(); - } - protected override void Update() { base.Update(); From 0cdf125c1e8f64fc5397fc1701e42cb21d1adaf2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Sep 2019 15:41:53 +0900 Subject: [PATCH 2709/2854] Handle key counter rewinding in a better way Use ElapsedFrameTime rather than storing state data --- .../Visual/Gameplay/TestSceneKeyCounter.cs | 40 ++------------- osu.Game/Rulesets/UI/RulesetInputManager.cs | 4 +- osu.Game/Screens/Play/KeyCounter.cs | 49 ++++++------------- osu.Game/Screens/Play/KeyCounterAction.cs | 22 ++++++--- osu.Game/Screens/Play/KeyCounterDisplay.cs | 5 -- osu.Game/Screens/Play/KeyCounterKeyboard.cs | 7 ++- osu.Game/Screens/Play/KeyCounterMouse.cs | 7 ++- 7 files changed, 50 insertions(+), 84 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index 4643b82792..6783a36ac3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -7,7 +7,6 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.MathUtils; -using osu.Framework.Timing; using osu.Game.Screens.Play; using osuTK.Input; @@ -25,14 +24,15 @@ namespace osu.Game.Tests.Visual.Gameplay public TestSceneKeyCounter() { - KeyCounterKeyboard rewindTestKeyCounterKeyboard; + KeyCounterKeyboard testCounter; + KeyCounterDisplay kc = new KeyCounterDisplay { Origin = Anchor.Centre, Anchor = Anchor.Centre, Children = new KeyCounter[] { - rewindTestKeyCounterKeyboard = new KeyCounterKeyboard(Key.X), + testCounter = new KeyCounterKeyboard(Key.X), new KeyCounterKeyboard(Key.X), new KeyCounterMouse(MouseButton.Left), new KeyCounterMouse(MouseButton.Right), @@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual.Gameplay InputManager.ReleaseKey(testKey); }); - AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 1); + AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 1); AddStep($"Press {testKey} key", () => { @@ -63,39 +63,9 @@ namespace osu.Game.Tests.Visual.Gameplay time1 = Clock.CurrentTime; }); - AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 2); - - IFrameBasedClock oldClock = null; - - AddStep($"Rewind {testKey} counter once", () => - { - oldClock = rewindTestKeyCounterKeyboard.Clock; - rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(time1 - 10)); - }); - - AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 1); - - AddStep($"Rewind {testKey} counter to zero", () => rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(0))); - - AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 0); - - AddStep("Restore clock", () => rewindTestKeyCounterKeyboard.Clock = oldClock); + AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 2); Add(kc); } - - private class FixedClock : IClock - { - private readonly double time; - - public FixedClock(double time) - { - this.time = time; - } - - public double CurrentTime => time; - public double Rate => 1; - public bool IsRunning => false; - } } } diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index e25c3bd0e7..98e27240d3 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -137,9 +137,9 @@ namespace osu.Game.Rulesets.UI { } - public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action)); + public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action, Clock.ElapsedFrameTime > 0)); - public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action)); + public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action, Clock.ElapsedFrameTime > 0)); } #endregion diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index ad0858184e..1daf89d8f9 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -22,9 +20,6 @@ namespace osu.Game.Screens.Play private Container textLayer; private SpriteText countSpriteText; - private readonly List states = new List(); - private KeyCounterState currentState; - public bool IsCounting { get; set; } = true; private int countPresses; @@ -52,16 +47,26 @@ namespace osu.Game.Screens.Play { isLit = value; updateGlowSprite(value); - - if (value && IsCounting) - { - CountPresses++; - saveState(); - } } } } + public void Increment() + { + if (!IsCounting) + return; + + CountPresses++; + } + + public void Decrement() + { + if (!IsCounting) + return; + + CountPresses--; + } + //further: change default values here and in KeyCounterCollection if needed, instead of passing them in every constructor public Color4 KeyDownTextColor { get; set; } = Color4.DarkGray; public Color4 KeyUpTextColor { get; set; } = Color4.White; @@ -143,27 +148,5 @@ namespace osu.Game.Screens.Play textLayer.FadeColour(KeyUpTextColor, remainingFadeTime, Easing.OutQuint); } } - - protected override void Update() - { - base.Update(); - - if (currentState?.Time > Clock.CurrentTime) - restoreStateTo(Clock.CurrentTime); - } - - private void saveState() - { - if (currentState == null || currentState.Time < Clock.CurrentTime) - states.Add(currentState = new KeyCounterState(Clock.CurrentTime, CountPresses)); - } - - private void restoreStateTo(double time) - { - states.RemoveAll(state => state.Time > time); - - currentState = states.LastOrDefault(); - CountPresses = currentState?.Count ?? 0; - } } } diff --git a/osu.Game/Screens/Play/KeyCounterAction.cs b/osu.Game/Screens/Play/KeyCounterAction.cs index 8deac653ad..f60ad7aa5a 100644 --- a/osu.Game/Screens/Play/KeyCounterAction.cs +++ b/osu.Game/Screens/Play/KeyCounterAction.cs @@ -1,11 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Input.Bindings; - namespace osu.Game.Screens.Play { - public class KeyCounterAction : KeyCounter, IKeyBindingHandler + public class KeyCounterAction : KeyCounter where T : struct { public T Action { get; } @@ -16,15 +14,25 @@ namespace osu.Game.Screens.Play Action = action; } - public bool OnPressed(T action) + public bool OnPressed(T action, bool forwards) { - if (action.Equals(Action)) IsLit = true; + if (!action.Equals(Action)) + return false; + + IsLit = true; + if (forwards) + Increment(); return false; } - public bool OnReleased(T action) + public bool OnReleased(T action, bool forwards) { - if (action.Equals(Action)) IsLit = false; + if (!action.Equals(Action)) + return false; + + IsLit = false; + if (!forwards) + Decrement(); return false; } } diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/KeyCounterDisplay.cs index 6b8fa5c75b..1edb95ca46 100644 --- a/osu.Game/Screens/Play/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/KeyCounterDisplay.cs @@ -102,11 +102,6 @@ namespace osu.Game.Screens.Play private Receptor receptor; - public Receptor GetReceptor() - { - return receptor ?? (receptor = new Receptor(this)); - } - public void SetReceptor(Receptor receptor) { if (this.receptor != null) diff --git a/osu.Game/Screens/Play/KeyCounterKeyboard.cs b/osu.Game/Screens/Play/KeyCounterKeyboard.cs index d9b6dca79d..29188b6b59 100644 --- a/osu.Game/Screens/Play/KeyCounterKeyboard.cs +++ b/osu.Game/Screens/Play/KeyCounterKeyboard.cs @@ -18,7 +18,12 @@ namespace osu.Game.Screens.Play protected override bool OnKeyDown(KeyDownEvent e) { - if (e.Key == Key) IsLit = true; + if (e.Key == Key) + { + IsLit = true; + Increment(); + } + return base.OnKeyDown(e); } diff --git a/osu.Game/Screens/Play/KeyCounterMouse.cs b/osu.Game/Screens/Play/KeyCounterMouse.cs index 95fa58e5c0..828441de6e 100644 --- a/osu.Game/Screens/Play/KeyCounterMouse.cs +++ b/osu.Game/Screens/Play/KeyCounterMouse.cs @@ -36,7 +36,12 @@ namespace osu.Game.Screens.Play protected override bool OnMouseDown(MouseDownEvent e) { - if (e.Button == Button) IsLit = true; + if (e.Button == Button) + { + IsLit = true; + Increment(); + } + return base.OnMouseDown(e); } From 831d04f339402020b0467bd38a7b940ab8031638 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Sep 2019 15:48:07 +0900 Subject: [PATCH 2710/2854] Don't use gameplay clock in KeyCounter --- osu.Game/Screens/Play/KeyCounter.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index 1daf89d8f9..f4109a63d0 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -78,11 +78,8 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader(true)] - private void load(TextureStore textures, GameplayClock clock) + private void load(TextureStore textures) { - if (clock != null) - Clock = clock; - Children = new Drawable[] { buttonSprite = new Sprite From 09a0c9f4d2fa8224651ff91d05881fe31f118ac1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Sep 2019 18:10:50 +0900 Subject: [PATCH 2711/2854] Add key counter rewind tests --- osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs | 9 ++++++++- .../Visual/Gameplay/TestSceneGameplayRewinding.cs | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs index 452ac859de..e2b620ea98 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs @@ -21,7 +21,12 @@ namespace osu.Game.Tests.Visual.Gameplay protected override void AddCheckSteps() { AddUntilStep("score above zero", () => ((ScoreAccessiblePlayer)Player).ScoreProcessor.TotalScore.Value > 0); - AddUntilStep("key counter counted keys", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 0)); + AddUntilStep("key counter counted keys", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 5)); + AddStep("rewind", () => + { + ((ScoreAccessiblePlayer)Player).GameplayClockContainer.Seek(0); + }); + AddUntilStep("key counter counted no", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0)); } private class ScoreAccessiblePlayer : TestPlayer @@ -29,6 +34,8 @@ namespace osu.Game.Tests.Visual.Gameplay public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; public new HUDOverlay HUDOverlay => base.HUDOverlay; + public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer; + public ScoreAccessiblePlayer() : base(false, false) { diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index 237fee1594..ffc025a942 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -14,6 +14,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using osuTK; @@ -47,9 +48,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for track to start running", () => track.IsRunning); addSeekStep(3000); AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged)); + AddUntilStep("key counter counted keys", () => player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses >= 7)); AddStep("clear results", () => player.AppliedResults.Clear()); addSeekStep(0); AddAssert("none judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged)); + AddUntilStep("key counters reset", () => player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0)); AddAssert("no results triggered", () => player.AppliedResults.Count == 0); } @@ -90,6 +93,10 @@ namespace osu.Game.Tests.Visual.Gameplay { public readonly List AppliedResults = new List(); + public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; + + public new HUDOverlay HUDOverlay => base.HUDOverlay; + public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer; public new DrawableRuleset DrawableRuleset => base.DrawableRuleset; From acdfeef1dc44fcacad192926163e71ced4adc1e9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Sep 2019 18:33:46 +0900 Subject: [PATCH 2712/2854] Improve how osu!catch stores and replays key actions --- .../Replays/CatchAutoGenerator.cs | 29 ++++++++++------ .../Replays/CatchFramedReplayInputHandler.cs | 15 ++------- .../Replays/CatchReplayFrame.cs | 33 ++++++++++++++++--- .../Replays/ManiaReplayFrame.cs | 2 +- .../Replays/OsuReplayFrame.cs | 8 ++--- .../Replays/TaikoReplayFrame.cs | 10 +++--- .../Replays/Types/IConvertibleReplayFrame.cs | 5 +-- osu.Game/Scoring/Legacy/LegacyScoreParser.cs | 15 ++++++--- 8 files changed, 74 insertions(+), 43 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index 8dd00756f2..4ea1f22006 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -27,6 +27,8 @@ namespace osu.Game.Rulesets.Catch.Replays protected Replay Replay; + private CatchReplayFrame currentFrame; + public override Replay Generate() { // todo: add support for HT DT @@ -36,7 +38,7 @@ namespace osu.Game.Rulesets.Catch.Replays double lastTime = 0; // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled - Replay.Frames.Add(new CatchReplayFrame(-100000, lastPosition)); + addFrame(-100000, lastPosition); void moveToNext(CatchHitObject h) { @@ -58,18 +60,18 @@ namespace osu.Game.Rulesets.Catch.Replays { //we are already in the correct range. lastTime = h.StartTime; - Replay.Frames.Add(new CatchReplayFrame(h.StartTime, lastPosition)); + addFrame(h.StartTime, lastPosition); return; } if (impossibleJump) { - Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); + addFrame(h.StartTime, h.X); } else if (h.HyperDash) { - Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition)); - Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); + addFrame(h.StartTime - timeAvailable, lastPosition); + addFrame(h.StartTime, h.X); } else if (dashRequired) { @@ -81,16 +83,16 @@ namespace osu.Game.Rulesets.Catch.Replays float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable); //dash movement - Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, true)); - Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition)); - Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); + addFrame(h.StartTime - timeAvailable + 1, lastPosition, true); + addFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition); + addFrame(h.StartTime, h.X); } else { double timeBefore = positionChange / movement_speed; - Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition)); - Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); + addFrame(h.StartTime - timeBefore, lastPosition); + addFrame(h.StartTime, h.X); } lastTime = h.StartTime; @@ -122,5 +124,12 @@ namespace osu.Game.Rulesets.Catch.Replays return Replay; } + + private void addFrame(double time, float? position = null, bool dashing = false) + { + var last = currentFrame; + currentFrame = new CatchReplayFrame(time, position, dashing, last); + Replay.Frames.Add(currentFrame); + } } } diff --git a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs index 103aa6c3f1..22532bc9ec 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using osu.Framework.Input.StateChanges; using osu.Framework.MathUtils; using osu.Game.Replays; @@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Replays { } - protected override bool IsImportant(CatchReplayFrame frame) => frame.Position > 0; + protected override bool IsImportant(CatchReplayFrame frame) => frame.Actions.Any(); protected float? Position { @@ -38,21 +39,11 @@ namespace osu.Game.Rulesets.Catch.Replays { if (!Position.HasValue) return new List(); - var actions = new List(); - - if (CurrentFrame.Dashing) - actions.Add(CatchAction.Dash); - - if (Position.Value > CurrentFrame.Position) - actions.Add(CatchAction.MoveRight); - else if (Position.Value < CurrentFrame.Position) - actions.Add(CatchAction.MoveLeft); - return new List { new CatchReplayState { - PressedActions = actions, + PressedActions = CurrentFrame?.Actions ?? new List(), CatcherX = Position.Value }, }; diff --git a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs index 1e88b35c3b..19637f321b 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Replays.Legacy; using osu.Game.Rulesets.Catch.UI; @@ -11,6 +12,8 @@ namespace osu.Game.Rulesets.Catch.Replays { public class CatchReplayFrame : ReplayFrame, IConvertibleReplayFrame { + public List Actions = new List(); + public float Position; public bool Dashing; @@ -18,17 +21,39 @@ namespace osu.Game.Rulesets.Catch.Replays { } - public CatchReplayFrame(double time, float? position = null, bool dashing = false) + public CatchReplayFrame(double time, float? position = null, bool dashing = false, CatchReplayFrame lastFrame = null) : base(time) { Position = position ?? -1; Dashing = dashing; + + if (Dashing) + Actions.Add(CatchAction.Dash); + + if (lastFrame != null) + { + if (Position > lastFrame.Position) + Actions.Add(CatchAction.MoveRight); + else if (Position < lastFrame.Position) + Actions.Add(CatchAction.MoveLeft); + } } - public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap) + public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, LegacyReplayFrame lastFrame = null) { - Position = legacyFrame.Position.X / CatchPlayfield.BASE_WIDTH; - Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1; + Position = currentFrame.Position.X / CatchPlayfield.BASE_WIDTH; + Dashing = currentFrame.ButtonState == ReplayButtonState.Left1; + + if (Dashing) + Actions.Add(CatchAction.Dash); + + if (lastFrame != null) + { + if (currentFrame.Position.X > lastFrame.Position.X) + Actions.Add(CatchAction.MoveRight); + else if (currentFrame.Position.X < lastFrame.Position.X) + Actions.Add(CatchAction.MoveLeft); + } } } } diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index f7277d3669..b2901f46c0 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Mania.Replays Actions.AddRange(actions); } - public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap) + public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap, LegacyReplayFrame lastFrame = null) { // We don't need to fully convert, just create the converter var converter = new ManiaBeatmapConverter(beatmap); diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs index 4d90fcadd5..441b69ef2d 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs @@ -26,11 +26,11 @@ namespace osu.Game.Rulesets.Osu.Replays Actions.AddRange(actions); } - public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap) + public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, LegacyReplayFrame lastFrame = null) { - Position = legacyFrame.Position; - if (legacyFrame.MouseLeft) Actions.Add(OsuAction.LeftButton); - if (legacyFrame.MouseRight) Actions.Add(OsuAction.RightButton); + Position = currentFrame.Position; + if (currentFrame.MouseLeft) Actions.Add(OsuAction.LeftButton); + if (currentFrame.MouseRight) Actions.Add(OsuAction.RightButton); } } } diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs index 5203415e90..6e43892777 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs @@ -23,12 +23,12 @@ namespace osu.Game.Rulesets.Taiko.Replays Actions.AddRange(actions); } - public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap) + public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, LegacyReplayFrame lastFrame = null) { - if (legacyFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim); - if (legacyFrame.MouseRight2) Actions.Add(TaikoAction.RightRim); - if (legacyFrame.MouseLeft1) Actions.Add(TaikoAction.LeftCentre); - if (legacyFrame.MouseLeft2) Actions.Add(TaikoAction.RightCentre); + if (currentFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim); + if (currentFrame.MouseRight2) Actions.Add(TaikoAction.RightRim); + if (currentFrame.MouseLeft1) Actions.Add(TaikoAction.LeftCentre); + if (currentFrame.MouseLeft2) Actions.Add(TaikoAction.RightCentre); } } } diff --git a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs index 7ecdc0715b..8fcdec6630 100644 --- a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs +++ b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs @@ -14,8 +14,9 @@ namespace osu.Game.Rulesets.Replays.Types /// /// Populates this using values from a . /// - /// The to extract values from. + /// The to extract values from. /// The beatmap. - void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap); + /// The last , used to fill in missing delta information. May be null. + void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, LegacyReplayFrame lastFrame = null); } } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs index 2e4b4b3a9a..5a90daa045 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs @@ -218,6 +218,7 @@ namespace osu.Game.Scoring.Legacy private void readLegacyReplay(Replay replay, StreamReader reader) { float lastTime = 0; + LegacyReplayFrame currentFrame = null; foreach (var l in reader.ReadToEnd().Split(',')) { @@ -240,23 +241,27 @@ namespace osu.Game.Scoring.Legacy if (diff < 0) continue; - replay.Frames.Add(convertFrame(new LegacyReplayFrame(lastTime, + var lastFrame = currentFrame; + + currentFrame = new LegacyReplayFrame(lastTime, Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE), Parsing.ParseFloat(split[2], Parsing.MAX_COORDINATE_VALUE), - (ReplayButtonState)Parsing.ParseInt(split[3])))); + (ReplayButtonState)Parsing.ParseInt(split[3])); + + replay.Frames.Add(convertFrame(currentFrame, lastFrame)); } } - private ReplayFrame convertFrame(LegacyReplayFrame legacyFrame) + private ReplayFrame convertFrame(LegacyReplayFrame currentFrame, LegacyReplayFrame lastFrame) { var convertible = currentRuleset.CreateConvertibleReplayFrame(); if (convertible == null) throw new InvalidOperationException($"Legacy replay cannot be converted for the ruleset: {currentRuleset.Description}"); - convertible.ConvertFrom(legacyFrame, currentBeatmap); + convertible.ConvertFrom(currentFrame, currentBeatmap, lastFrame); var frame = (ReplayFrame)convertible; - frame.Time = legacyFrame.Time; + frame.Time = currentFrame.Time; return frame; } From 68feedbd159b677879e77260b7f2b1f40e789185 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Sep 2019 18:46:42 +0900 Subject: [PATCH 2713/2854] Fix unreported CI issue --- osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index 6783a36ac3..ad747e88e1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -46,7 +46,6 @@ namespace osu.Game.Tests.Visual.Gameplay }); Key testKey = ((KeyCounterKeyboard)kc.Children.First()).Key; - double time1 = 0; AddStep($"Press {testKey} key", () => { @@ -60,7 +59,6 @@ namespace osu.Game.Tests.Visual.Gameplay { InputManager.PressKey(testKey); InputManager.ReleaseKey(testKey); - time1 = Clock.CurrentTime; }); AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 2); From f21e47d6d2a513451dff67eeaa0bf1f78426f69a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Sep 2019 19:29:08 +0900 Subject: [PATCH 2714/2854] Move expire to DrawableHitObject --- .../Drawable/DrawableCatchHitObject.cs | 4 +-- .../Drawables/DrawableManiaHitObject.cs | 4 +-- .../Objects/Drawables/DrawableHitCircle.cs | 26 +++++++++++++++---- .../Objects/Drawables/DrawableOsuHitObject.cs | 8 ++++++ .../Objects/Drawables/DrawableSlider.cs | 4 +-- .../Objects/Drawables/DrawableSpinner.cs | 6 ----- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 8 +----- .../Objects/Drawables/DrawableDrumRoll.cs | 2 +- .../Objects/Drawables/DrawableDrumRollTick.cs | 2 +- .../Objects/Drawables/DrawableHit.cs | 8 ++---- .../Objects/Drawables/DrawableSwell.cs | 2 -- .../Objects/Drawables/DrawableHitObject.cs | 5 +++- 12 files changed, 43 insertions(+), 36 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs index 00734810b3..51deae6e85 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -71,11 +71,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable switch (state) { case ArmedState.Miss: - this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out).Expire(); + this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out); break; case ArmedState.Hit: - this.FadeOut().Expire(); + this.FadeOut(); break; } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index e5b114ca81..5bfa07bd14 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -51,11 +51,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables switch (state) { case ArmedState.Miss: - this.FadeOut(150, Easing.In).Expire(); + this.FadeOut(150, Easing.In); break; case ArmedState.Hit: - this.FadeOut(150, Easing.OutQuint).Expire(); + this.FadeOut(150, Easing.OutQuint); break; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 985dcbca86..85fd68efdd 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -86,6 +86,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AccentColour.BindValueChanged(accent => ApproachCircle.Colour = accent.NewValue, true); } + public override double LifetimeStart + { + get => base.LifetimeStart; + set + { + base.LifetimeStart = value; + ApproachCircle.LifetimeStart = value; + } + } + + public override double LifetimeEnd + { + get => base.LifetimeEnd; + set + { + base.LifetimeEnd = value; + ApproachCircle.LifetimeEnd = value; + } + } + protected override void CheckForResult(bool userTriggered, double timeOffset) { Debug.Assert(HitObject.HitWindows != null); @@ -132,22 +152,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Expire(true); hitArea.HitAction = null; - - // override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early. - LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.WindowFor(HitResult.Miss); break; case ArmedState.Miss: ApproachCircle.FadeOut(50); this.FadeOut(100); - Expire(); break; case ArmedState.Hit: ApproachCircle.FadeOut(50); // todo: temporary / arbitrary - this.Delay(800).Expire(); + this.Delay(800).FadeOut(); break; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index fcd42314fc..8a7e5117f9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -41,6 +41,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength); + protected override void LoadComplete() + { + base.LoadComplete(); + + // Manually set to reduce the number of future alive objects to a bare minimum. + LifetimeStart = HitObject.StartTime - HitObject.TimePreempt; + } + protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement); } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 00c953c393..65f1d5e15f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -219,10 +219,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables break; } - this.FadeOut(fade_out_time, Easing.OutQuint).Expire(); + this.FadeOut(fade_out_time, Easing.OutQuint); } - - Expire(true); } public Drawable ProxiedLayer => HeadCircle.ApproachCircle; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 49aaa2aaea..b1185ddba8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -219,10 +219,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables switch (state) { - case ArmedState.Idle: - Expire(true); - break; - case ArmedState.Hit: sequence.ScaleTo(Scale * 1.2f, 320, Easing.Out); break; @@ -231,8 +227,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables sequence.ScaleTo(Scale * 0.8f, 320, Easing.In); break; } - - Expire(); } } } diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index ea7eee8bb8..df12ebc514 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -70,13 +70,7 @@ namespace osu.Game.Rulesets.Osu.UI base.Add(h); } - private void addApproachCircleProxy(Drawable d) - { - var proxy = d.CreateProxy(); - proxy.LifetimeStart = d.LifetimeStart; - proxy.LifetimeEnd = d.LifetimeEnd; - approachCircles.Add(proxy); - } + private void addApproachCircleProxy(Drawable d) => approachCircles.Add(d.CreateProxy()); public override void PostProcess() { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index f4407a7b54..8e16a21199 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { case ArmedState.Hit: case ArmedState.Miss: - this.Delay(HitObject.Duration).FadeOut(100).Expire(); + this.Delay(HitObject.Duration).FadeOut(100); break; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index cef9a53deb..25b6141a0e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables switch (state) { case ArmedState.Hit: - this.ScaleTo(0, 100, Easing.OutQuint).Expire(); + this.ScaleTo(0, 100, Easing.OutQuint); break; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 676ecd5a0b..4b25ff0ecc 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -105,12 +105,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables validActionPressed = false; UnproxyContent(); - this.Delay(HitObject.HitWindows.WindowFor(HitResult.Miss)).Expire(); break; case ArmedState.Miss: - this.FadeOut(100) - .Expire(); + this.FadeOut(100); break; case ArmedState.Hit: @@ -129,9 +127,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables .Then() .MoveToY(gravity_travel_height * 2, gravity_time * 2, Easing.In); - this.FadeOut(800) - .Expire(); - + this.FadeOut(800); break; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 094ad1230f..07af7fe7e0 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -208,8 +208,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { this.FadeOut(transition_duration, Easing.Out); bodyContainer.ScaleTo(1.4f, transition_duration); - - Expire(); } break; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index e3390c8cf0..90c49a0144 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -153,7 +153,6 @@ namespace osu.Game.Rulesets.Objects.Drawables if (UseTransformStateManagement) { - lifetimeStart = null; LifetimeEnd = double.MaxValue; double transformTime = HitObject.StartTime - InitialLifetimeOffset; @@ -173,6 +172,9 @@ namespace osu.Game.Rulesets.Objects.Drawables state.Value = newState; } } + + if (state.Value != ArmedState.Idle && LifetimeEnd == double.MaxValue) + Expire(); } else state.Value = newState; @@ -203,6 +205,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// Apply transforms based on the current . Previous states are automatically cleared. + /// In the case of a non-idle , and if was not set during this call, will be invoked. /// /// The new armed state. protected virtual void UpdateStateTransforms(ArmedState state) From b81b162ee12a96b0668222ee57ac477567851a0a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Sep 2019 19:30:27 +0900 Subject: [PATCH 2715/2854] Update InitialLifetimeOffset comment --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 90c49a0144..00b57f7249 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -314,7 +314,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// This is only used as an optimisation to delay the initial update of this and may be tuned more aggressively if required. /// It is indirectly used to decide the automatic transform offset provided to . - /// A more accurate should be set inside for an state. + /// A more accurate should be set for further optimisation (in , for example). /// protected virtual double InitialLifetimeOffset => 10000; From cafb5105bc4b4997bc0c75259c9c1ce587d0c768 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 12 Sep 2019 16:44:15 +0300 Subject: [PATCH 2716/2854] Rename HeaderFlag to DismissableFlag --- ...aderFlag.cs => TestSceneRankingsDismissableFlag.cs} | 10 +++++----- .../Rankings/{HeaderFlag.cs => DismissableFlag.cs} | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) rename osu.Game.Tests/Visual/Online/{TestSceneRankingsHeaderFlag.cs => TestSceneRankingsDismissableFlag.cs} (88%) rename osu.Game/Overlays/Rankings/{HeaderFlag.cs => DismissableFlag.cs} (94%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsDismissableFlag.cs similarity index 88% rename from osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs rename to osu.Game.Tests/Visual/Online/TestSceneRankingsDismissableFlag.cs index c9531e1016..db6afa9bf3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsDismissableFlag.cs @@ -12,16 +12,16 @@ using osuTK; namespace osu.Game.Tests.Visual.Online { - public class TestSceneRankingsHeaderFlag : OsuTestScene + public class TestSceneRankingsDismissableFlag : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { - typeof(HeaderFlag), + typeof(DismissableFlag), }; - public TestSceneRankingsHeaderFlag() + public TestSceneRankingsDismissableFlag() { - HeaderFlag flag; + DismissableFlag flag; SpriteText text; var countryA = new Country @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Online AddRange(new Drawable[] { - flag = new HeaderFlag + flag = new DismissableFlag { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Overlays/Rankings/HeaderFlag.cs b/osu.Game/Overlays/Rankings/DismissableFlag.cs similarity index 94% rename from osu.Game/Overlays/Rankings/HeaderFlag.cs rename to osu.Game/Overlays/Rankings/DismissableFlag.cs index 6f641c74a5..7a55b0bba6 100644 --- a/osu.Game/Overlays/Rankings/HeaderFlag.cs +++ b/osu.Game/Overlays/Rankings/DismissableFlag.cs @@ -11,7 +11,7 @@ using System; namespace osu.Game.Overlays.Rankings { - public class HeaderFlag : UpdateableFlag + public class DismissableFlag : UpdateableFlag { private const int duration = 200; @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Rankings private readonly SpriteIcon hoverIcon; - public HeaderFlag() + public DismissableFlag() { AddInternal(hoverIcon = new SpriteIcon { From b17d097a39d7edd1d15f6d2744029d47c7f2b08d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 12 Sep 2019 17:17:57 +0300 Subject: [PATCH 2717/2854] Simplify colour usage in GradientLine --- .../UserInterface/GradientLineTabControl.cs | 38 +++++-------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs index 7cd8d2c5bd..3523876fca 100644 --- a/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs +++ b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs @@ -8,7 +8,6 @@ using osuTK; using osu.Framework.Graphics.Shapes; using osuTK.Graphics; using osu.Framework.Graphics.Colour; -using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; namespace osu.Game.Graphics.UserInterface @@ -19,8 +18,8 @@ namespace osu.Game.Graphics.UserInterface protected Color4 LineColour { - get => line.MainColour.Value; - set => line.MainColour.Value = value; + get => line.Colour; + set => line.Colour = value; } private readonly GradientLine line; @@ -48,12 +47,6 @@ namespace osu.Game.Graphics.UserInterface private class GradientLine : GridContainer { - public readonly Bindable MainColour = new Bindable(); - - private readonly Box left; - private readonly Box middle; - private readonly Box right; - public GradientLine() { RelativeSizeAxes = Axes.X; @@ -70,34 +63,23 @@ namespace osu.Game.Graphics.UserInterface { new Drawable[] { - left = new Box + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0), Color4.White) + }, + new Box { RelativeSizeAxes = Axes.Both, }, - middle = new Box - { - RelativeSizeAxes = Axes.Both, - }, - right = new Box + new Box { RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.White.Opacity(0)) }, } }; } - - protected override void LoadComplete() - { - MainColour.BindValueChanged(onColourChanged, true); - base.LoadComplete(); - } - - private void onColourChanged(ValueChangedEvent colour) - { - left.Colour = ColourInfo.GradientHorizontal(colour.NewValue.Opacity(0), colour.NewValue); - middle.Colour = colour.NewValue; - right.Colour = ColourInfo.GradientHorizontal(colour.NewValue, colour.NewValue.Opacity(0)); - } } } } From 7ee01ee3233a5a3d7eee3b1f41a018d923567e35 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 12 Sep 2019 18:11:48 +0300 Subject: [PATCH 2718/2854] Use assignment instead of binding --- osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs index 2081a6c0cb..178016c648 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Online { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Current = { BindTarget = scope } + Current = scope } }); From 99fc13b4d85a17e4dcf5c65012c857892674e36c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 12 Sep 2019 19:34:58 +0300 Subject: [PATCH 2719/2854] Update usage of the DismissableFlag --- osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs | 2 +- osu.Game/Overlays/Rankings/HeaderTitle.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs index 0f16b2592f..849ca2defc 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Online { public override IReadOnlyList RequiredTypes => new[] { - typeof(HeaderFlag), + typeof(DismissableFlag), typeof(HeaderTitle), }; diff --git a/osu.Game/Overlays/Rankings/HeaderTitle.cs b/osu.Game/Overlays/Rankings/HeaderTitle.cs index 3f1feb10b8..a00c6c6dcd 100644 --- a/osu.Game/Overlays/Rankings/HeaderTitle.cs +++ b/osu.Game/Overlays/Rankings/HeaderTitle.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Rankings private readonly SpriteText scopeText; private readonly Container flagPlaceholder; - private readonly HeaderFlag flag; + private readonly DismissableFlag flag; public HeaderTitle() { @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Rankings Origin = Anchor.BottomLeft, AutoSizeAxes = Axes.Both, Margin = new MarginPadding { Bottom = flag_margin }, - Child = flag = new HeaderFlag + Child = flag = new DismissableFlag { Size = new Vector2(30, 20), }, From 2a8fa2f5936543aa1c77baed96836a26d9c8ef98 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 12 Sep 2019 14:01:12 -0700 Subject: [PATCH 2720/2854] Refactor modsContainer on profile scores --- .../Sections/Ranks/DrawableProfileScore.cs | 9 ++++---- .../Sections/Ranks/ScoreModsContainer.cs | 21 ------------------- 2 files changed, 5 insertions(+), 25 deletions(-) delete mode 100644 osu.Game/Overlays/Profile/Sections/Ranks/ScoreModsContainer.cs diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index e54ce44ca2..6362d3dfb0 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs @@ -12,12 +12,13 @@ using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Beatmaps; using osu.Framework.Localisation; +using osu.Framework.Graphics.Containers; namespace osu.Game.Overlays.Profile.Sections.Ranks { public abstract class DrawableProfileScore : DrawableProfileRow { - private readonly ScoreModsContainer modsContainer; + private readonly FillFlowContainer modsContainer; protected readonly ScoreInfo Score; protected DrawableProfileScore(ScoreInfo score) @@ -28,12 +29,12 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Height = 60; Children = new Drawable[] { - modsContainer = new ScoreModsContainer + modsContainer = new FillFlowContainer { - AutoSizeAxes = Axes.Y, + AutoSizeAxes = Axes.Both, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - Width = 60, + Spacing = new Vector2(1), Margin = new MarginPadding { Right = 160 } } }; diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/ScoreModsContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/ScoreModsContainer.cs deleted file mode 100644 index 1ce04effa8..0000000000 --- a/osu.Game/Overlays/Profile/Sections/Ranks/ScoreModsContainer.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osuTK; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.UI; -using System.Collections.Generic; -using System.Linq; - -namespace osu.Game.Overlays.Profile.Sections.Ranks -{ - public class ScoreModsContainer : FlowContainer - { - protected override IEnumerable ComputeLayoutPositions() - { - int count = FlowingChildren.Count(); - for (int i = 0; i < count; i++) - yield return new Vector2(DrawWidth * i * (count == 1 ? 0 : 1f / (count - 1)), 0); - } - } -} From b917f29cfe6cccf9158332edc694572cbb7c302b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 13:59:06 +0900 Subject: [PATCH 2721/2854] Make GradientLineTabControl abstract --- osu.Game/Graphics/UserInterface/GradientLineTabControl.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs index 3523876fca..a9bbda4194 100644 --- a/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs +++ b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs @@ -12,10 +12,8 @@ using osu.Framework.Extensions.Color4Extensions; namespace osu.Game.Graphics.UserInterface { - public class GradientLineTabControl : PageTabControl + public abstract class GradientLineTabControl : PageTabControl { - protected override Dropdown CreateDropdown() => null; - protected Color4 LineColour { get => line.Colour; @@ -24,7 +22,7 @@ namespace osu.Game.Graphics.UserInterface private readonly GradientLine line; - public GradientLineTabControl() + protected GradientLineTabControl() { RelativeSizeAxes = Axes.X; @@ -35,6 +33,8 @@ namespace osu.Game.Graphics.UserInterface }); } + protected override Dropdown CreateDropdown() => null; + protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer { Anchor = Anchor.BottomCentre, From 0e679fb468a4ee27dc0b57bd6aefbcfbce67ccfc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 14:06:19 +0900 Subject: [PATCH 2722/2854] Use colour constant rather than opacity helper function --- osu.Game/Graphics/UserInterface/GradientLineTabControl.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs index a9bbda4194..4fd4a2adbd 100644 --- a/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs +++ b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs @@ -8,7 +8,6 @@ using osuTK; using osu.Framework.Graphics.Shapes; using osuTK.Graphics; using osu.Framework.Graphics.Colour; -using osu.Framework.Extensions.Color4Extensions; namespace osu.Game.Graphics.UserInterface { @@ -66,7 +65,7 @@ namespace osu.Game.Graphics.UserInterface new Box { RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0), Color4.White) + Colour = ColourInfo.GradientHorizontal(Color4.Transparent, Color4.White) }, new Box { @@ -75,7 +74,7 @@ namespace osu.Game.Graphics.UserInterface new Box { RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.White.Opacity(0)) + Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.Transparent) }, } }; From 44947aa9edece7d0c140aa8660aa6c342a28b54f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 15:27:29 +0900 Subject: [PATCH 2723/2854] Make PopupDialog abstract --- .../UserInterface/TestSceneDialogOverlay.cs | 8 +++++-- .../UserInterface/TestScenePopupDialog.cs | 23 ++++++++++++------- osu.Game/Overlays/Dialog/PopupDialog.cs | 2 +- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs index a6ff3462d4..cc4a57fb83 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.UserInterface Add(overlay = new DialogOverlay()); - AddStep("dialog #1", () => overlay.Push(new PopupDialog + AddStep("dialog #1", () => overlay.Push(new TestPopupDialog { Icon = FontAwesome.Regular.TrashAlt, HeaderText = @"Confirm deletion of", @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.UserInterface }, })); - AddStep("dialog #2", () => overlay.Push(new PopupDialog + AddStep("dialog #2", () => overlay.Push(new TestPopupDialog { Icon = FontAwesome.Solid.Cog, HeaderText = @"What do you want to do with", @@ -71,5 +71,9 @@ namespace osu.Game.Tests.Visual.UserInterface }, })); } + + private class TestPopupDialog : PopupDialog + { + } } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs index 9ddd8f4038..3d39bb7003 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs @@ -13,13 +13,22 @@ namespace osu.Game.Tests.Visual.UserInterface { public TestScenePopupDialog() { - var popup = new PopupDialog + Add(new TestPopupDialog { RelativeSizeAxes = Axes.Both, State = { Value = Framework.Graphics.Containers.Visibility.Visible }, - Icon = FontAwesome.Solid.AssistiveListeningSystems, - HeaderText = @"This is a test popup", - BodyText = "I can say lots of stuff and even wrap my words!", + }); + } + + private class TestPopupDialog : PopupDialog + { + public TestPopupDialog() + { + Icon = FontAwesome.Solid.AssistiveListeningSystems; + + HeaderText = @"This is a test popup"; + BodyText = "I can say lots of stuff and even wrap my words!"; + Buttons = new PopupDialogButton[] { new PopupDialogCancelButton @@ -30,10 +39,8 @@ namespace osu.Game.Tests.Visual.UserInterface { Text = @"You're a fake!", }, - } - }; - - Add(popup); + }; + } } } } diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 5c0ddb47b1..37674a5dcb 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -19,7 +19,7 @@ using osuTK.Input; namespace osu.Game.Overlays.Dialog { - public class PopupDialog : VisibilityContainer + public abstract class PopupDialog : VisibilityContainer { public static readonly float ENTER_DURATION = 500; public static readonly float EXIT_DURATION = 200; From dc8c7a50414caa25b9304f7cc99a1a9d45ebea44 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 15:27:42 +0900 Subject: [PATCH 2724/2854] Add null check for safety --- osu.Game/Overlays/DialogOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 0d3c96c984..6aaeff8554 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -80,7 +80,7 @@ namespace osu.Game.Overlays switch (action) { case GlobalAction.Select: - currentDialog.Buttons.OfType().FirstOrDefault()?.Click(); + currentDialog?.Buttons.OfType().FirstOrDefault()?.Click(); return true; } From c66e96370531ae6baf9c1779cbfa20eecb045ba4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 15:42:36 +0900 Subject: [PATCH 2725/2854] Make constructor private --- osu.Game/Overlays/Dialog/PopupDialog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 37674a5dcb..cff887865a 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -87,7 +87,7 @@ namespace osu.Game.Overlays.Dialog } } - public PopupDialog() + protected PopupDialog() { RelativeSizeAxes = Axes.Both; From cf2f841b4d00da43aa1c4dd496d34df0a80a0746 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Sep 2019 15:41:53 +0900 Subject: [PATCH 2726/2854] Fix player not correctly exiting after an unpause --- .../Visual/Gameplay/TestScenePause.cs | 10 ++++++++++ osu.Game/Screens/Play/Player.cs | 19 +++++++++++-------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index 5808a78056..50583e43c4 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -160,6 +160,15 @@ namespace osu.Game.Tests.Visual.Gameplay exitAndConfirm(); } + [Test] + public void TestRestartAfterResume() + { + pauseAndConfirm(); + resumeAndConfirm(); + restart(); + confirmExited(); + } + private void pauseAndConfirm() { pause(); @@ -198,6 +207,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("player exited", () => !Player.IsCurrentScreen()); } + private void restart() => AddStep("restart", () => Player.Restart()); private void pause() => AddStep("pause", () => Player.Pause()); private void resume() => AddStep("resume", () => Player.Resume()); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 3f1603eabe..3fd0f0260c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -502,15 +502,18 @@ namespace osu.Game.Screens.Play return true; } - if (pauseCooldownActive && !GameplayClockContainer.IsPaused.Value) - // still want to block if we are within the cooldown period and not already paused. - return true; - - if (HasFailed && ValidForResume && !FailOverlay.IsPresent) - // ValidForResume is false when restarting + // ValidForResume is false when restarting + if (ValidForResume) { - failAnimation.FinishTransforms(true); - return true; + if (pauseCooldownActive && !GameplayClockContainer.IsPaused.Value) + // still want to block if we are within the cooldown period and not already paused. + return true; + + if (HasFailed && !FailOverlay.IsPresent) + { + failAnimation.FinishTransforms(true); + return true; + } } GameplayClockContainer.ResetLocalAdjustments(); From 7818ecd71c1eab60b1c0f7f17b4dcc79f5357ad9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 16:03:44 +0900 Subject: [PATCH 2727/2854] Forward ValueChangedEvent instead --- osu.Game/Overlays/Rankings/HeaderTitle.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Rankings/HeaderTitle.cs b/osu.Game/Overlays/Rankings/HeaderTitle.cs index a00c6c6dcd..ed9dc99a79 100644 --- a/osu.Game/Overlays/Rankings/HeaderTitle.cs +++ b/osu.Game/Overlays/Rankings/HeaderTitle.cs @@ -73,16 +73,16 @@ namespace osu.Game.Overlays.Rankings protected override void LoadComplete() { - Scope.BindValueChanged(scope => onScopeChanged(scope.NewValue), true); + Scope.BindValueChanged(onScopeChanged, true); Country.BindValueChanged(onCountryChanged, true); base.LoadComplete(); } - private void onScopeChanged(RankingsScope scope) + private void onScopeChanged(ValueChangedEvent scope) { - scopeText.Text = scope.ToString(); + scopeText.Text = scope.NewValue.ToString(); - if (scope != RankingsScope.Performance) + if (scope.NewValue != RankingsScope.Performance) Country.Value = null; } From 78e7be919f7dc05f40b806a07d2e8847cb2275f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 16:25:25 +0900 Subject: [PATCH 2728/2854] Remove unnecessary container --- osu.Game/Overlays/Rankings/HeaderTitle.cs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Rankings/HeaderTitle.cs b/osu.Game/Overlays/Rankings/HeaderTitle.cs index ed9dc99a79..efaf4225d5 100644 --- a/osu.Game/Overlays/Rankings/HeaderTitle.cs +++ b/osu.Game/Overlays/Rankings/HeaderTitle.cs @@ -22,7 +22,6 @@ namespace osu.Game.Overlays.Rankings public readonly Bindable Country = new Bindable(); private readonly SpriteText scopeText; - private readonly Container flagPlaceholder; private readonly DismissableFlag flag; public HeaderTitle() @@ -35,16 +34,12 @@ namespace osu.Game.Overlays.Rankings Spacing = new Vector2(spacing, 0), Children = new Drawable[] { - flagPlaceholder = new Container + flag = new DismissableFlag { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - AutoSizeAxes = Axes.Both, Margin = new MarginPadding { Bottom = flag_margin }, - Child = flag = new DismissableFlag - { - Size = new Vector2(30, 20), - }, + Size = new Vector2(30, 20), }, scopeText = new SpriteText { @@ -90,15 +85,13 @@ namespace osu.Game.Overlays.Rankings { if (country.NewValue == null) { - flagPlaceholder.Hide(); + flag.Hide(); return; } Scope.Value = RankingsScope.Performance; - if (country.OldValue == null) - flagPlaceholder.Show(); - + flag.Show(); flag.Country = country.NewValue; } } From 51f17ccb1b8caec176e2b712066cd76caf453c98 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 13 Sep 2019 10:48:02 +0300 Subject: [PATCH 2729/2854] Remove test duplicate --- .../Online/TestSceneRankingsHeaderFlag.cs | 65 ------------------- 1 file changed, 65 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs deleted file mode 100644 index 17f6f8417b..0000000000 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderFlag.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using osu.Game.Overlays.Rankings; -using osu.Game.Users; -using osuTK; - -namespace osu.Game.Tests.Visual.Online -{ - public class TestSceneRankingsHeaderFlag : OsuTestScene - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DismissableFlag), - }; - - public TestSceneRankingsHeaderFlag() - { - DismissableFlag flag; - SpriteText text; - - var countryA = new Country - { - FlagName = "BY", - FullName = "Belarus" - }; - - var countryB = new Country - { - FlagName = "US", - FullName = "United States" - }; - - AddRange(new Drawable[] - { - flag = new DismissableFlag - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(30, 20), - Country = countryA, - }, - text = new SpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = "Invoked", - Font = OsuFont.GetFont(size: 30), - Alpha = 0, - } - }); - - flag.Action += () => text.FadeIn().Then().FadeOut(1000, Easing.OutQuint); - - AddStep("Trigger click", () => flag.Click()); - AddStep("Change to country 2", () => flag.Country = countryB); - AddStep("Change to country 1", () => flag.Country = countryA); - } - } -} From c9ae4336f944b779becfcf7f880cef8293a19815 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 13 Sep 2019 10:50:26 +0300 Subject: [PATCH 2730/2854] Fix RankingsScope test --- osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs index fef9c3a7b1..3693d6b5b4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Online { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Current = { BindTarget = scope } + Current = scope, } }); From 6867b3c23214a8a6f47d547cf6377cdf4d13919b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 13 Sep 2019 10:56:21 +0300 Subject: [PATCH 2731/2854] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 45c162a30e..4167d07698 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -62,7 +62,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index df8b11e653..5703293caf 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -25,7 +25,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 7c31744a14..683dccf3ae 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -117,7 +117,7 @@ - + From 9a9654dbd1021614d95969231a4d09523699b663 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 13 Sep 2019 10:59:09 +0300 Subject: [PATCH 2732/2854] Fix the Test Scene --- osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs index e8ed94b59c..0ceb5f21d3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs @@ -15,7 +15,7 @@ namespace osu.Game.Tests.Visual.Online { public override IReadOnlyList RequiredTypes => new[] { - typeof(HeaderFlag), + typeof(DismissableFlag), typeof(HeaderTitle), typeof(RankingsRulesetSelector), typeof(RankingsScopeSelector), @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Online new Spotlight { Id = 3, - Text = "Spotlight 4" + Text = "Spotlight 3" } } }); From 7cb79dd7605f37e9c5fdd67ec98cda660e4a46b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 17:15:33 +0900 Subject: [PATCH 2733/2854] Fix incorrect DI usage of IAPIProvider in many tests --- .../Visual/Menus/TestSceneDisclaimer.cs | 11 ++++---- .../Multiplayer/TestSceneMatchLeaderboard.cs | 2 +- .../Multiplayer/TestSceneMultiScreen.cs | 2 +- .../Online/TestSceneAccountCreationOverlay.cs | 14 +++++++---- .../Online/TestSceneBeatmapSetOverlay.cs | 2 +- .../Online/TestSceneChangelogOverlay.cs | 2 +- .../Visual/Online/TestSceneDirectOverlay.cs | 2 +- .../Online/TestSceneHistoricalSection.cs | 2 +- .../Visual/Online/TestSceneSocialOverlay.cs | 2 +- .../Online/TestSceneUserProfileHeader.cs | 2 +- .../Online/TestSceneUserProfileOverlay.cs | 2 +- .../Visual/Online/TestSceneUserRanks.cs | 2 +- ...tSceneUpdateableBeatmapBackgroundSprite.cs | 3 +-- osu.Game/Tests/Visual/OsuTestScene.cs | 25 ++++++++++++++----- 14 files changed, 44 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs b/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs index 13116de320..681bf1b40b 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Game.Online.API; using osu.Game.Screens.Menu; using osu.Game.Users; @@ -11,17 +10,17 @@ namespace osu.Game.Tests.Visual.Menus public class TestSceneDisclaimer : ScreenTestScene { [BackgroundDependencyLoader] - private void load(IAPIProvider api) + private void load() { AddStep("load disclaimer", () => LoadScreen(new Disclaimer())); AddStep("toggle support", () => { - api.LocalUser.Value = new User + API.LocalUser.Value = new User { - Username = api.LocalUser.Value.Username, - Id = api.LocalUser.Value.Id, - IsSupporter = !api.LocalUser.Value.IsSupporter, + Username = API.LocalUser.Value.Username, + Id = API.LocalUser.Value.Id, + IsSupporter = !API.LocalUser.Value.IsSupporter, }; }); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs index 723e5fc03d..7ba1782a28 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMatchLeaderboard : MultiplayerTestScene { - protected override bool RequiresAPIAccess => true; + protected override bool UseOnlineAPI => true; public TestSceneMatchLeaderboard() { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs index b646433846..dfe61a4dda 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs @@ -12,7 +12,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [TestFixture] public class TestSceneMultiScreen : ScreenTestScene { - protected override bool RequiresAPIAccess => true; + protected override bool UseOnlineAPI => true; public override IReadOnlyList RequiredTypes => new[] { diff --git a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs index 66ab1fe18a..31eab7f74e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs @@ -4,9 +4,9 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Overlays.AccountCreation; using osu.Game.Users; @@ -27,6 +27,8 @@ namespace osu.Game.Tests.Visual.Online private readonly Container userPanelArea; + private Bindable localUser; + public TestSceneAccountCreationOverlay() { AccountCreationOverlay accountCreation; @@ -47,12 +49,14 @@ namespace osu.Game.Tests.Visual.Online } [BackgroundDependencyLoader] - private void load(IAPIProvider api) + private void load() { - api.Logout(); - api.LocalUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true); + API.Logout(); - AddStep("logout", api.Logout); + localUser = API.LocalUser.GetBoundCopy(); + localUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true); + + AddStep("logout", API.Logout); } } } diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 5068064a1f..9f03d947b9 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Online typeof(BeatmapAvailability), }; - protected override bool RequiresAPIAccess => true; + protected override bool UseOnlineAPI => true; private RulesetInfo taikoRuleset; private RulesetInfo maniaRuleset; diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index 324291c9d7..f555c276f4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Online typeof(Comments), }; - protected override bool RequiresAPIAccess => true; + protected override bool UseOnlineAPI => true; protected override void LoadComplete() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs index 14ae975806..d9873ea243 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs @@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual.Online { private DirectOverlay direct; - protected override bool RequiresAPIAccess => true; + protected override bool UseOnlineAPI => true; protected override void LoadComplete() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs index c98f98c23d..d3b037f499 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneHistoricalSection : OsuTestScene { - protected override bool RequiresAPIAccess => true; + protected override bool UseOnlineAPI => true; public override IReadOnlyList RequiredTypes => new[] { diff --git a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs index 806b36e855..dbd7544b38 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs @@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneSocialOverlay : OsuTestScene { - protected override bool RequiresAPIAccess => true; + protected override bool UseOnlineAPI => true; public override IReadOnlyList RequiredTypes => new[] { diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs index 555d5334d8..63b8acb234 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneUserProfileHeader : OsuTestScene { - protected override bool RequiresAPIAccess => true; + protected override bool UseOnlineAPI => true; public override IReadOnlyList RequiredTypes => new[] { diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs index 42c8ffbf0a..93e6607ac5 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneUserProfileOverlay : OsuTestScene { - protected override bool RequiresAPIAccess => true; + protected override bool UseOnlineAPI => true; private readonly TestUserProfileOverlay profile; diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs index d777f9766a..2951f6b63e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneUserRanks : OsuTestScene { - protected override bool RequiresAPIAccess => true; + protected override bool UseOnlineAPI => true; public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableProfileScore), typeof(RanksSection) }; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs index fdc50be3fa..d3359fd824 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneUpdateableBeatmapBackgroundSprite : OsuTestScene { - protected override bool RequiresAPIAccess => true; + protected override bool UseOnlineAPI => true; private BeatmapSetInfo testBeatmap; private IAPIProvider api; @@ -32,7 +32,6 @@ namespace osu.Game.Tests.Visual.UserInterface [BackgroundDependencyLoader] private void load(OsuGameBase osu, IAPIProvider api, RulesetStore rulesets) { - this.api = api; this.rulesets = rulesets; testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu).Result; diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index dd68ed93e6..5a7fbd31e2 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -46,13 +46,27 @@ namespace osu.Game.Tests.Visual protected Storage LocalStorage => localStorage.Value; private readonly Lazy contextFactory; + + protected IAPIProvider API + { + get + { + if (UseOnlineAPI) + throw new Exception("Using the OsuTestScene dummy API is not supported when UseOnlineAPI is true"); + + return dummyAPI; + } + } + + private DummyAPIAccess dummyAPI; + protected DatabaseContextFactory ContextFactory => contextFactory.Value; /// - /// Whether this test scene requires API access - /// Setting this will cache an actual . + /// Whether this test scene requires real-world API access. + /// If true, this will bypass the local and use the provided one. /// - protected virtual bool RequiresAPIAccess => false; + protected virtual bool UseOnlineAPI => false; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { @@ -66,10 +80,9 @@ namespace osu.Game.Tests.Visual Dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - if (!RequiresAPIAccess) + if (!UseOnlineAPI) { - var dummyAPI = new DummyAPIAccess(); - + dummyAPI = new DummyAPIAccess(); Dependencies.CacheAs(dummyAPI); Add(dummyAPI); } From 0cc21c9c747979052603576c25be1ec0bd502034 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 17:21:47 +0900 Subject: [PATCH 2734/2854] Fix changelog overlay potentially adding children after disposal --- osu.Game/Overlays/ChangelogOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 7755c8a6a6..dfe3669813 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -170,7 +170,7 @@ namespace osu.Game.Overlays var tcs = new TaskCompletionSource(); var req = new GetChangelogRequest(); - req.Success += res => + req.Success += res => Schedule(() => { // remap streams to builds to ensure model equality res.Builds.ForEach(b => b.UpdateStream = res.Streams.Find(s => s.Id == b.UpdateStream.Id)); @@ -182,7 +182,7 @@ namespace osu.Game.Overlays header.Streams.Populate(res.Streams); tcs.SetResult(true); - }; + }); req.Failure += _ => initialFetchTask = null; req.Perform(API); From d681f43e29c2c45fa50c762c47955668e9872e21 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2019 08:38:02 +0000 Subject: [PATCH 2735/2854] Bump ppy.osu.Game.Resources from 2019.904.0 to 2019.913.0 Bumps [ppy.osu.Game.Resources](https://github.com/ppy/osu-resources) from 2019.904.0 to 2019.913.0. - [Release notes](https://github.com/ppy/osu-resources/releases) - [Commits](https://github.com/ppy/osu-resources/compare/2019.904.0...2019.913.0) Signed-off-by: dependabot-preview[bot] --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 45c162a30e..4167d07698 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -62,7 +62,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index df8b11e653..5703293caf 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -25,7 +25,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 7c31744a14..683dccf3ae 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -117,7 +117,7 @@ - + From a7c59098ce51be6eb3828d9029f57bfe51e02688 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 17:38:04 +0900 Subject: [PATCH 2736/2854] Fix missing assignment --- .../UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs index d3359fd824..198cc70e01 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs @@ -32,6 +32,7 @@ namespace osu.Game.Tests.Visual.UserInterface [BackgroundDependencyLoader] private void load(OsuGameBase osu, IAPIProvider api, RulesetStore rulesets) { + this.api = api; this.rulesets = rulesets; testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu).Result; From 1e4f3507ed9c0529eda45ab530fe2430aa85cf07 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 18:07:52 +0900 Subject: [PATCH 2737/2854] Fix layout not matching web --- osu.Game/Overlays/Rankings/RankingsHeader.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Rankings/RankingsHeader.cs b/osu.Game/Overlays/Rankings/RankingsHeader.cs index 6d55e92502..18a0599036 100644 --- a/osu.Game/Overlays/Rankings/RankingsHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsHeader.cs @@ -20,7 +20,6 @@ namespace osu.Game.Overlays.Rankings private const int content_height = 250; private const int dropdown_height = 50; private const int spacing = 20; - private const int title_offset = 30; private const int duration = 200; public IEnumerable Spotlights @@ -68,27 +67,25 @@ namespace osu.Game.Overlays.Rankings Masking = true, Child = new HeaderBackground(), }, - new RankingsScopeSelector - { - Margin = new MarginPadding { Top = 10 }, - Current = { BindTarget = Scope } - }, new FillFlowContainer { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Y = title_offset, AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Direction = FillDirection.Vertical, Spacing = new Vector2(0, spacing), Children = new Drawable[] { + new RankingsScopeSelector + { + Margin = new MarginPadding { Top = 10 }, + Current = { BindTarget = Scope } + }, new HeaderTitle { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Scope = { BindTarget = Scope }, + Margin = new MarginPadding { Top = 10 }, Country = { BindTarget = Country }, }, dropdownPlaceholder = new Container From 031f0ee1e7251308e1830ad86dcca94f69ee7656 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 18:09:15 +0900 Subject: [PATCH 2738/2854] Consume ValueChanged and inline some pointless constants --- osu.Game/Overlays/Rankings/RankingsHeader.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Rankings/RankingsHeader.cs b/osu.Game/Overlays/Rankings/RankingsHeader.cs index 18a0599036..4e502fb7fe 100644 --- a/osu.Game/Overlays/Rankings/RankingsHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsHeader.cs @@ -19,8 +19,6 @@ namespace osu.Game.Overlays.Rankings { private const int content_height = 250; private const int dropdown_height = 50; - private const int spacing = 20; - private const int duration = 200; public IEnumerable Spotlights { @@ -72,7 +70,7 @@ namespace osu.Game.Overlays.Rankings AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Direction = FillDirection.Vertical, - Spacing = new Vector2(0, spacing), + Spacing = new Vector2(0, 20), Children = new Drawable[] { new RankingsScopeSelector @@ -112,12 +110,12 @@ namespace osu.Game.Overlays.Rankings protected override void LoadComplete() { - Scope.BindValueChanged(scope => onScopeChanged(scope.NewValue), true); + Scope.BindValueChanged(onScopeChanged, true); base.LoadComplete(); } - private void onScopeChanged(RankingsScope scope) => - dropdownPlaceholder.FadeTo(scope == RankingsScope.Spotlights ? 1 : 0, duration, Easing.OutQuint); + private void onScopeChanged(ValueChangedEvent scope) => + dropdownPlaceholder.FadeTo(scope.NewValue == RankingsScope.Spotlights ? 1 : 0, 200, Easing.OutQuint); private class HeaderBackground : Sprite { From 614e68cdf960cd15667f4b2597012320239750a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 18:10:52 +0900 Subject: [PATCH 2739/2854] Remove redundant BindTarget usage --- osu.Game/Overlays/Rankings/RankingsHeader.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Rankings/RankingsHeader.cs b/osu.Game/Overlays/Rankings/RankingsHeader.cs index 4e502fb7fe..fbf3097f4f 100644 --- a/osu.Game/Overlays/Rankings/RankingsHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsHeader.cs @@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Rankings { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Current = { BindTarget = Ruleset } + Current = Ruleset }, new Container { @@ -76,14 +76,14 @@ namespace osu.Game.Overlays.Rankings new RankingsScopeSelector { Margin = new MarginPadding { Top = 10 }, - Current = { BindTarget = Scope } + Current = Scope }, new HeaderTitle { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Scope = { BindTarget = Scope }, Margin = new MarginPadding { Top = 10 }, + Scope = { BindTarget = Scope }, Country = { BindTarget = Country }, }, dropdownPlaceholder = new Container @@ -97,7 +97,7 @@ namespace osu.Game.Overlays.Rankings Child = dropdown = new OsuDropdown { RelativeSizeAxes = Axes.X, - Current = { BindTarget = Spotlight }, + Current = Spotlight, } } } From 5c2c055614d943f1cc46d8b8f1faf8f0bb17daa9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Sep 2019 18:49:21 +0900 Subject: [PATCH 2740/2854] Set lifetime on initial state update --- .../Objects/Drawables/DrawableHitCircle.cs | 2 ++ .../Objects/Drawables/DrawableOsuHitObject.cs | 13 +++++++++---- .../Objects/Drawables/DrawableRepeatPoint.cs | 2 ++ .../Objects/Drawables/DrawableSlider.cs | 2 ++ .../Objects/Drawables/DrawableSliderTick.cs | 2 ++ .../Objects/Drawables/DrawableSpinner.cs | 2 ++ 6 files changed, 19 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 85fd68efdd..83646c561d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -142,6 +142,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void UpdateStateTransforms(ArmedState state) { + base.UpdateStateTransforms(state); + Debug.Assert(HitObject.HitWindows != null); switch (state) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 8a7e5117f9..c46343c73c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -41,12 +41,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength); - protected override void LoadComplete() + protected override void UpdateStateTransforms(ArmedState state) { - base.LoadComplete(); + base.UpdateStateTransforms(state); - // Manually set to reduce the number of future alive objects to a bare minimum. - LifetimeStart = HitObject.StartTime - HitObject.TimePreempt; + switch (state) + { + case ArmedState.Idle: + // Manually set to reduce the number of future alive objects to a bare minimum. + LifetimeStart = HitObject.StartTime - HitObject.TimePreempt; + break; + } } protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 00a943a67f..84d2a4af9b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -74,6 +74,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void UpdateStateTransforms(ArmedState state) { + base.UpdateStateTransforms(state); + switch (state) { case ArmedState.Idle: diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 65f1d5e15f..08b43b0345 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -202,6 +202,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void UpdateStateTransforms(ArmedState state) { + base.UpdateStateTransforms(state); + Ball.FadeIn(); Ball.ScaleTo(HitObject.Scale); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index ba931976a8..9d4d9958a1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -75,6 +75,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void UpdateStateTransforms(ArmedState state) { + base.UpdateStateTransforms(state); + switch (state) { case ArmedState.Idle: diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index b1185ddba8..d1b9ee6cb4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -215,6 +215,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void UpdateStateTransforms(ArmedState state) { + base.UpdateStateTransforms(state); + var sequence = this.Delay(Spinner.Duration).FadeOut(160); switch (state) From a6420def9909b6e375c57f6f43e1b9f05edb401f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 19:29:49 +0900 Subject: [PATCH 2741/2854] Make hyperdash test automatic --- osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs index a603d96201..7b8c699f2c 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs @@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Catch.Tests { } + protected override bool Autoplay => true; + [Test] public void TestHyperDash() { From c13950fbbf801d92a8c62aa4075ef51e2dadc323 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 13 Sep 2019 13:43:21 +0300 Subject: [PATCH 2742/2854] Remove custom db additions --- osu.Game/Migrations/20171019041408_InitialCreate.cs | 1 - osu.Game/Migrations/20181007180454_StandardizePaths.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game/Migrations/20171019041408_InitialCreate.cs b/osu.Game/Migrations/20171019041408_InitialCreate.cs index 3349998873..9b6881f98c 100644 --- a/osu.Game/Migrations/20171019041408_InitialCreate.cs +++ b/osu.Game/Migrations/20171019041408_InitialCreate.cs @@ -35,7 +35,6 @@ namespace osu.Game.Migrations AudioFile = table.Column(type: "TEXT", nullable: true), Author = table.Column(type: "TEXT", nullable: true), BackgroundFile = table.Column(type: "TEXT", nullable: true), - VideoFile = table.Column(type: "TEXT", nullable: true), PreviewTime = table.Column(type: "INTEGER", nullable: false), Source = table.Column(type: "TEXT", nullable: true), Tags = table.Column(type: "TEXT", nullable: true), diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.cs index c106b839e2..274b8030a9 100644 --- a/osu.Game/Migrations/20181007180454_StandardizePaths.cs +++ b/osu.Game/Migrations/20181007180454_StandardizePaths.cs @@ -15,7 +15,6 @@ namespace osu.Game.Migrations migrationBuilder.Sql($"UPDATE `BeatmapInfo` SET `Path` = REPLACE(`Path`, '{windowsStyle}', '{standardized}')"); migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `AudioFile` = REPLACE(`AudioFile`, '{windowsStyle}', '{standardized}')"); migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `BackgroundFile` = REPLACE(`BackgroundFile`, '{windowsStyle}', '{standardized}')"); - migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `VideoFile` = REPLACE(`VideoFile`, '{windowsStyle}', '{standardized}')"); migrationBuilder.Sql($"UPDATE `BeatmapSetFileInfo` SET `Filename` = REPLACE(`Filename`, '{windowsStyle}', '{standardized}')"); migrationBuilder.Sql($"UPDATE `SkinFileInfo` SET `Filename` = REPLACE(`Filename`, '{windowsStyle}', '{standardized}')"); } From 9e742839ac360f81f45c3db28c7587b1d4cd4002 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 13 Sep 2019 13:57:55 +0300 Subject: [PATCH 2743/2854] Use correct database migration --- ...20190913104727_AddBeatmapVideo.Designer.cs | 506 ++++++++++++++++++ .../20190913104727_AddBeatmapVideo.cs | 22 + .../Migrations/OsuDbContextModelSnapshot.cs | 4 +- 3 files changed, 531 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Migrations/20190913104727_AddBeatmapVideo.Designer.cs create mode 100644 osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs diff --git a/osu.Game/Migrations/20190913104727_AddBeatmapVideo.Designer.cs b/osu.Game/Migrations/20190913104727_AddBeatmapVideo.Designer.cs new file mode 100644 index 0000000000..826233a2b0 --- /dev/null +++ b/osu.Game/Migrations/20190913104727_AddBeatmapVideo.Designer.cs @@ -0,0 +1,506 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20190913104727_AddBeatmapVideo")] + partial class AddBeatmapVideo + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BPM"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("Length"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("Status"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.Property("VideoFile"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.Property("Status"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Key") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("SkinInfoID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("SkinInfoID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("ScoreInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("ScoreInfoID"); + + b.ToTable("ScoreFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Accuracy") + .HasColumnType("DECIMAL(1,4)"); + + b.Property("BeatmapInfoID"); + + b.Property("Combo"); + + b.Property("Date"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MaxCombo"); + + b.Property("ModsJson") + .HasColumnName("Mods"); + + b.Property("OnlineScoreID"); + + b.Property("PP"); + + b.Property("Rank"); + + b.Property("RulesetID"); + + b.Property("StatisticsJson") + .HasColumnName("Statistics"); + + b.Property("TotalScore"); + + b.Property("UserID") + .HasColumnName("UserID"); + + b.Property("UserString") + .HasColumnName("User"); + + b.HasKey("ID"); + + b.HasIndex("BeatmapInfoID"); + + b.HasIndex("OnlineScoreID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("ScoreInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Settings") + .HasForeignKey("SkinInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Scoring.ScoreInfo") + .WithMany("Files") + .HasForeignKey("ScoreInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") + .WithMany("Scores") + .HasForeignKey("BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs b/osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs new file mode 100644 index 0000000000..9ed0943acd --- /dev/null +++ b/osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class AddBeatmapVideo : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "VideoFile", + table: "BeatmapMetadata", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "VideoFile", + table: "BeatmapMetadata"); + } + } +} diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index 761dca2801..a6d9d1f3cb 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -14,7 +14,7 @@ namespace osu.Game.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.2.4-servicing-10062"); + .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => { @@ -139,6 +139,8 @@ namespace osu.Game.Migrations b.Property("TitleUnicode"); + b.Property("VideoFile"); + b.HasKey("ID"); b.ToTable("BeatmapMetadata"); From 744085fa549766b74d2bcb9f78e8a841225fe936 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 20:25:08 +0900 Subject: [PATCH 2744/2854] Fix exploding fruit not getting correct lifetime --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 330f6e6b24..40d2f64f6a 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -74,6 +74,7 @@ namespace osu.Game.Rulesets.Catch.UI caughtFruit.Anchor = Anchor.TopCentre; caughtFruit.Origin = Anchor.Centre; caughtFruit.Scale *= 0.7f; + caughtFruit.LifetimeStart = caughtFruit.HitObject.StartTime; caughtFruit.LifetimeEnd = double.MaxValue; MovableCatcher.Add(caughtFruit); @@ -414,7 +415,10 @@ namespace osu.Game.Rulesets.Catch.UI f.MoveToY(f.Y + 75, 750, Easing.InSine); f.FadeOut(750); - f.Expire(true); + + // todo: this shouldn't exist once DrawableHitObject's ClearTransformsAfter overrides are repaired. + f.LifetimeStart = Time.Current; + f.Expire(); } } @@ -448,7 +452,10 @@ namespace osu.Game.Rulesets.Catch.UI fruit.MoveToY(fruit.Y - 50, 250, Easing.OutSine).Then().MoveToY(fruit.Y + 50, 500, Easing.InSine); fruit.MoveToX(fruit.X + originalX * 6, 1000); fruit.FadeOut(750); - fruit.Expire(true); + + // todo: this shouldn't exist once DrawableHitObject's ClearTransformsAfter overrides are repaired. + fruit.LifetimeStart = Time.Current; + fruit.Expire(); } } } From d385c35955aa89f604c606e8d7051761d5808374 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 21:55:45 +0900 Subject: [PATCH 2745/2854] Apply suggestions from code review Co-Authored-By: Salman Ahmed --- osu.Game/Tests/Visual/OsuTestScene.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 5a7fbd31e2..b382fdb3c0 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual get { if (UseOnlineAPI) - throw new Exception("Using the OsuTestScene dummy API is not supported when UseOnlineAPI is true"); + throw new Exception($"Using the {nameof(OsuTestScene)} dummy API is not supported when {nameof(UseOnlineAPI)} is true"); return dummyAPI; } @@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual /// /// Whether this test scene requires real-world API access. - /// If true, this will bypass the local and use the provided one. + /// If true, this will bypass the local and use the provided one. /// protected virtual bool UseOnlineAPI => false; From 2379b665e3e81cee3eb91bef8bcebc1a0a66c61b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 22:15:11 +0900 Subject: [PATCH 2746/2854] Use InvalidOperationException --- osu.Game/Tests/Visual/OsuTestScene.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index b382fdb3c0..2b8baab57c 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual get { if (UseOnlineAPI) - throw new Exception($"Using the {nameof(OsuTestScene)} dummy API is not supported when {nameof(UseOnlineAPI)} is true"); + throw new InvalidOperationException($"Using the {nameof(OsuTestScene)} dummy API is not supported when {nameof(UseOnlineAPI)} is true"); return dummyAPI; } From 82561aa44a30dd0986e79b402168c709218a6527 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 22:44:40 +0900 Subject: [PATCH 2747/2854] Fix catcher additive sprite rewinding and remove unnecessary update code --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 40d2f64f6a..56c8b33e02 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -199,7 +199,6 @@ namespace osu.Game.Rulesets.Catch.UI additive.Anchor = Anchor; additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly. - additive.LifetimeStart = Clock.CurrentTime; additive.Position = Position; additive.Scale = Scale; additive.Colour = HyperDashing ? Color4.Red : Color4.White; @@ -208,7 +207,8 @@ namespace osu.Game.Rulesets.Catch.UI AdditiveTarget.Add(additive); - additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire(); + additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); + additive.Expire(true); Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50); } @@ -303,6 +303,7 @@ namespace osu.Game.Rulesets.Catch.UI { this.FadeColour(Color4.White, hyper_dash_transition_length, Easing.OutQuint); this.FadeTo(1, hyper_dash_transition_length, Easing.OutQuint); + Trail &= Dashing; } } else @@ -386,12 +387,6 @@ namespace osu.Game.Rulesets.Catch.UI X = hyperDashTargetPosition; SetHyperDashState(); } - - if (Clock.ElapsedFrameTime < 0) - { - AdditiveTarget.RemoveAll(d => Clock.CurrentTime < d.LifetimeStart); - caughtFruit.RemoveAll(d => d.HitObject.StartTime > Clock.CurrentTime); - } } /// From 624e5644a4f582d66df2b9ecdc7c2216f5a29394 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Sep 2019 23:05:47 +0900 Subject: [PATCH 2748/2854] Change osu!catch key trigger to occur on frame before positional change --- .../Replays/CatchReplayFrame.cs | 15 ++++++++------- .../Replays/ManiaReplayFrame.cs | 2 +- osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs | 2 +- .../Replays/TaikoReplayFrame.cs | 2 +- .../Replays/Types/IConvertibleReplayFrame.cs | 4 ++-- osu.Game/Scoring/Legacy/LegacyScoreParser.cs | 12 +++++------- 6 files changed, 18 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs index 19637f321b..b41a5e0612 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs @@ -33,13 +33,13 @@ namespace osu.Game.Rulesets.Catch.Replays if (lastFrame != null) { if (Position > lastFrame.Position) - Actions.Add(CatchAction.MoveRight); + lastFrame.Actions.Add(CatchAction.MoveRight); else if (Position < lastFrame.Position) - Actions.Add(CatchAction.MoveLeft); + lastFrame.Actions.Add(CatchAction.MoveLeft); } } - public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, LegacyReplayFrame lastFrame = null) + public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null) { Position = currentFrame.Position.X / CatchPlayfield.BASE_WIDTH; Dashing = currentFrame.ButtonState == ReplayButtonState.Left1; @@ -47,11 +47,12 @@ namespace osu.Game.Rulesets.Catch.Replays if (Dashing) Actions.Add(CatchAction.Dash); - if (lastFrame != null) + // this probably needs some cross-checking with osu-stable to ensure it is actually correct. + if (lastFrame is CatchReplayFrame lastCatchFrame) { - if (currentFrame.Position.X > lastFrame.Position.X) - Actions.Add(CatchAction.MoveRight); - else if (currentFrame.Position.X < lastFrame.Position.X) + if (Position > lastCatchFrame.Position) + lastCatchFrame.Actions.Add(CatchAction.MoveRight); + else if (Position < lastCatchFrame.Position) Actions.Add(CatchAction.MoveLeft); } } diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index b2901f46c0..70ba5cd938 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Mania.Replays Actions.AddRange(actions); } - public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap, LegacyReplayFrame lastFrame = null) + public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame lastFrame = null) { // We don't need to fully convert, just create the converter var converter = new ManiaBeatmapConverter(beatmap); diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs index 441b69ef2d..e6c6db5e61 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Replays Actions.AddRange(actions); } - public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, LegacyReplayFrame lastFrame = null) + public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null) { Position = currentFrame.Position; if (currentFrame.MouseLeft) Actions.Add(OsuAction.LeftButton); diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs index 6e43892777..c5ebefc397 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Replays Actions.AddRange(actions); } - public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, LegacyReplayFrame lastFrame = null) + public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null) { if (currentFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim); if (currentFrame.MouseRight2) Actions.Add(TaikoAction.RightRim); diff --git a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs index 8fcdec6630..c2947c0aca 100644 --- a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs +++ b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Replays.Types /// /// The to extract values from. /// The beatmap. - /// The last , used to fill in missing delta information. May be null. - void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, LegacyReplayFrame lastFrame = null); + /// The last post-conversion , used to fill in missing delta information. May be null. + void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null); } } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs index 5a90daa045..2cdd0c4b5e 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs @@ -218,7 +218,7 @@ namespace osu.Game.Scoring.Legacy private void readLegacyReplay(Replay replay, StreamReader reader) { float lastTime = 0; - LegacyReplayFrame currentFrame = null; + ReplayFrame currentFrame = null; foreach (var l in reader.ReadToEnd().Split(',')) { @@ -241,18 +241,16 @@ namespace osu.Game.Scoring.Legacy if (diff < 0) continue; - var lastFrame = currentFrame; - - currentFrame = new LegacyReplayFrame(lastTime, + currentFrame = convertFrame(new LegacyReplayFrame(lastTime, Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE), Parsing.ParseFloat(split[2], Parsing.MAX_COORDINATE_VALUE), - (ReplayButtonState)Parsing.ParseInt(split[3])); + (ReplayButtonState)Parsing.ParseInt(split[3])), currentFrame); - replay.Frames.Add(convertFrame(currentFrame, lastFrame)); + replay.Frames.Add(currentFrame); } } - private ReplayFrame convertFrame(LegacyReplayFrame currentFrame, LegacyReplayFrame lastFrame) + private ReplayFrame convertFrame(LegacyReplayFrame currentFrame, ReplayFrame lastFrame) { var convertible = currentRuleset.CreateConvertibleReplayFrame(); if (convertible == null) From 65aa7b20169e6a92444713fa010ce65582a591c1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 14 Sep 2019 00:07:06 +0900 Subject: [PATCH 2749/2854] Recreate beatmap video on each consumption Should not be shared over multiple usages --- osu.Game/Beatmaps/WorkingBeatmap.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index bf3fa90d7b..3fc33e9f52 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -44,7 +44,6 @@ namespace osu.Game.Beatmaps track = new RecyclableLazy(() => GetTrack() ?? GetVirtualTrack()); background = new RecyclableLazy(GetBackground, BackgroundStillValid); - video = new RecyclableLazy(GetVideo); waveform = new RecyclableLazy(GetWaveform); storyboard = new RecyclableLazy(GetStoryboard); skin = new RecyclableLazy(GetSkin); @@ -188,11 +187,9 @@ namespace osu.Game.Beatmaps protected abstract Texture GetBackground(); private readonly RecyclableLazy background; - public bool VideoLoaded => video.IsResultAvailable; - public VideoSprite Video => video.Value; + public VideoSprite Video => GetVideo(); protected abstract VideoSprite GetVideo(); - private readonly RecyclableLazy video; public bool TrackLoaded => track.IsResultAvailable; public Track Track => track.Value; From 9a94405b3a195d18ddcfaf2e1a01a683bff7bd52 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Sep 2019 06:05:09 +0300 Subject: [PATCH 2750/2854] Fix video playback is stretched on client resize --- osu.Game/Screens/Play/DimmableVideo.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/DimmableVideo.cs b/osu.Game/Screens/Play/DimmableVideo.cs index 3e6b95d2cc..4981c3457f 100644 --- a/osu.Game/Screens/Play/DimmableVideo.cs +++ b/osu.Game/Screens/Play/DimmableVideo.cs @@ -59,8 +59,12 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both; Masking = true; - AddInternal(video); video.RelativeSizeAxes = Axes.Both; + video.FillMode = FillMode.Fill; + video.Anchor = Anchor.Centre; + video.Origin = Anchor.Centre; + + AddInternal(video); } [BackgroundDependencyLoader] From 8ad782a82d71b03d6f17bec85b35bbacf6e30102 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Sep 2019 06:16:25 +0300 Subject: [PATCH 2751/2854] Fix RankingsHeader dropdown can be clickable when not visible --- osu.Game/Overlays/Rankings/RankingsHeader.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Rankings/RankingsHeader.cs b/osu.Game/Overlays/Rankings/RankingsHeader.cs index fbf3097f4f..13c1f0fe40 100644 --- a/osu.Game/Overlays/Rankings/RankingsHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsHeader.cs @@ -31,7 +31,6 @@ namespace osu.Game.Overlays.Rankings public readonly Bindable Country = new Bindable(); public readonly Bindable Spotlight = new Bindable(); - private readonly Container dropdownPlaceholder; private readonly OsuDropdown dropdown; public RankingsHeader() @@ -86,14 +85,13 @@ namespace osu.Game.Overlays.Rankings Scope = { BindTarget = Scope }, Country = { BindTarget = Country }, }, - dropdownPlaceholder = new Container + new Container { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.X, Height = dropdown_height, Width = 0.8f, - AlwaysPresent = true, Child = dropdown = new OsuDropdown { RelativeSizeAxes = Axes.X, @@ -115,7 +113,7 @@ namespace osu.Game.Overlays.Rankings } private void onScopeChanged(ValueChangedEvent scope) => - dropdownPlaceholder.FadeTo(scope.NewValue == RankingsScope.Spotlights ? 1 : 0, 200, Easing.OutQuint); + dropdown.FadeTo(scope.NewValue == RankingsScope.Spotlights ? 1 : 0, 200, Easing.OutQuint); private class HeaderBackground : Sprite { From a36c80868247ff5a7fe1c180378e34e95732a905 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Sep 2019 06:28:59 +0300 Subject: [PATCH 2752/2854] Use Fit FillFode --- osu.Game/Screens/Play/DimmableVideo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/DimmableVideo.cs b/osu.Game/Screens/Play/DimmableVideo.cs index 4981c3457f..628871b164 100644 --- a/osu.Game/Screens/Play/DimmableVideo.cs +++ b/osu.Game/Screens/Play/DimmableVideo.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Play Masking = true; video.RelativeSizeAxes = Axes.Both; - video.FillMode = FillMode.Fill; + video.FillMode = FillMode.Fit; video.Anchor = Anchor.Centre; video.Origin = Anchor.Centre; From 9febeb1f3def1c30e07c825785f5c2557bbdcb26 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Sep 2019 06:32:00 +0300 Subject: [PATCH 2753/2854] Add black background --- osu.Game/Screens/Play/DimmableVideo.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/DimmableVideo.cs b/osu.Game/Screens/Play/DimmableVideo.cs index 628871b164..4d6c10d69d 100644 --- a/osu.Game/Screens/Play/DimmableVideo.cs +++ b/osu.Game/Screens/Play/DimmableVideo.cs @@ -4,8 +4,10 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Video; using osu.Game.Graphics.Containers; +using osuTK.Graphics; namespace osu.Game.Screens.Play { @@ -64,7 +66,15 @@ namespace osu.Game.Screens.Play video.Anchor = Anchor.Centre; video.Origin = Anchor.Centre; - AddInternal(video); + AddRangeInternal(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + video, + }); } [BackgroundDependencyLoader] From 2783ae62ef7b409b5b6a5d9f4f3867d1553d2bf6 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Sep 2019 06:34:57 +0300 Subject: [PATCH 2754/2854] Remove useless container --- osu.Game/Overlays/Rankings/RankingsHeader.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Rankings/RankingsHeader.cs b/osu.Game/Overlays/Rankings/RankingsHeader.cs index 13c1f0fe40..6aa3e75df9 100644 --- a/osu.Game/Overlays/Rankings/RankingsHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsHeader.cs @@ -18,7 +18,6 @@ namespace osu.Game.Overlays.Rankings public class RankingsHeader : CompositeDrawable { private const int content_height = 250; - private const int dropdown_height = 50; public IEnumerable Spotlights { @@ -85,18 +84,13 @@ namespace osu.Game.Overlays.Rankings Scope = { BindTarget = Scope }, Country = { BindTarget = Country }, }, - new Container + dropdown = new OsuDropdown { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.X, - Height = dropdown_height, Width = 0.8f, - Child = dropdown = new OsuDropdown - { - RelativeSizeAxes = Axes.X, - Current = Spotlight, - } + Current = Spotlight, } } }, From 8456861b8d58f5b4bb7695d81aaee02a952717d8 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sat, 14 Sep 2019 17:08:56 +0300 Subject: [PATCH 2755/2854] Wait for cursor hiding using ManualResetEventSlim --- osu.Game/Graphics/ScreenshotManager.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index f532302de2..02d928ec66 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -83,11 +83,19 @@ namespace osu.Game.Graphics const int frames_to_wait = 3; int framesWaited = 0; - ScheduledDelegate waitDelegate = host.DrawThread.Scheduler.AddDelayed(() => framesWaited++, 0, true); - while (framesWaited < frames_to_wait) - Thread.Sleep(10); - waitDelegate.Cancel(); + using (var framesWaitedEvent = new ManualResetEventSlim(false)) + { + ScheduledDelegate waitDelegate = host.DrawThread.Scheduler.AddDelayed(() => + { + if (framesWaited++ < frames_to_wait) + // ReSharper disable once AccessToDisposedClosure + framesWaitedEvent.Set(); + }, 10, true); + + framesWaitedEvent.Wait(); + waitDelegate.Cancel(); + } } using (var image = await host.TakeScreenshotAsync()) From babd34470ea38ca7b38cc80fbbb6a00f8ed7bfaf Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 15 Sep 2019 02:33:21 +0300 Subject: [PATCH 2756/2854] Fix DrawableFlag returns empty texture if there's no flag avaliable for needed country --- osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs | 7 +++++++ osu.Game/Users/Drawables/DrawableFlag.cs | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs index 0ceb5f21d3..c0da605cdb 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs @@ -61,10 +61,17 @@ namespace osu.Game.Tests.Visual.Online FullName = "Belarus" }; + var unknownCountry = new Country + { + FlagName = "CK", + FullName = "Cook Islands" + }; + AddStep("Set country", () => countryBindable.Value = country); AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance); AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score); AddAssert("Check country is Null", () => countryBindable.Value == null); + AddStep("Set country with no flag", () => countryBindable.Value = unknownCountry); } } } diff --git a/osu.Game/Users/Drawables/DrawableFlag.cs b/osu.Game/Users/Drawables/DrawableFlag.cs index 368354e48e..fab561c1af 100644 --- a/osu.Game/Users/Drawables/DrawableFlag.cs +++ b/osu.Game/Users/Drawables/DrawableFlag.cs @@ -26,7 +26,7 @@ namespace osu.Game.Users.Drawables if (ts == null) throw new ArgumentNullException(nameof(ts)); - Texture = ts.Get($@"Flags/{country?.FlagName ?? @"__"}"); + Texture = ts.Get($@"Flags/{country?.FlagName ?? @"__"}") ?? ts.Get($@"Flags/__"); } } } From 96453d8197be660721540348198819d551bf04ef Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 15 Sep 2019 02:46:28 +0300 Subject: [PATCH 2757/2854] Remove redundant string interpolation --- osu.Game/Users/Drawables/DrawableFlag.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Users/Drawables/DrawableFlag.cs b/osu.Game/Users/Drawables/DrawableFlag.cs index fab561c1af..1d648e46b6 100644 --- a/osu.Game/Users/Drawables/DrawableFlag.cs +++ b/osu.Game/Users/Drawables/DrawableFlag.cs @@ -26,7 +26,7 @@ namespace osu.Game.Users.Drawables if (ts == null) throw new ArgumentNullException(nameof(ts)); - Texture = ts.Get($@"Flags/{country?.FlagName ?? @"__"}") ?? ts.Get($@"Flags/__"); + Texture = ts.Get($@"Flags/{country?.FlagName ?? @"__"}") ?? ts.Get(@"Flags/__"); } } } From 2381b4c00365e8d59003b9fc235f25e953618cb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Sep 2019 00:20:56 +0900 Subject: [PATCH 2758/2854] Move video behind storyboard --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 5c7ac9e762..309f4837e5 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -143,8 +143,8 @@ namespace osu.Game.Screens.Play private void addUnderlayComponents(Container target) { - target.Add(DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both }); target.Add(DimmableVideo = new DimmableVideo(Beatmap.Value.Video) { RelativeSizeAxes = Axes.Both }); + target.Add(DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both }); } private void addGameplayComponents(Container target, WorkingBeatmap working) From 43760bdfcdfffd6301a4cba30c2d86c131b8a2c2 Mon Sep 17 00:00:00 2001 From: HoLLy-HaCKeR Date: Sun, 15 Sep 2019 18:29:14 +0200 Subject: [PATCH 2759/2854] Specify Rider analysis rules explicitly --- osu.Android.sln.DotSettings | 21 ++++++++++++++++++++- osu.iOS.sln.DotSettings | 22 +++++++++++++++++++++- osu.sln.DotSettings | 12 ++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/osu.Android.sln.DotSettings b/osu.Android.sln.DotSettings index 3f5bd9d34d..5a97fc7518 100644 --- a/osu.Android.sln.DotSettings +++ b/osu.Android.sln.DotSettings @@ -1,4 +1,4 @@ - + True True True @@ -167,6 +167,14 @@ WARNING <?xml version="1.0" encoding="utf-16"?><Profile name="Code Cleanup (peppy)"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSUseVar><BehavourStyle>CAN_CHANGE_TO_EXPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_EXPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSUpdateFileHeader>True</CSUpdateFileHeader><CSCodeStyleAttributes ArrangeTypeAccessModifier="False" ArrangeTypeMemberAccessModifier="False" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="False" ArrangeBraces="False" ArrangeAttributes="False" ArrangeArgumentsStyle="False" /><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CSArrangeQualifiers>True</CSArrangeQualifiers></Profile> Code Cleanup (peppy) + Required + Required + Required + Explicit + ExpressionBody + ExpressionBody + True + NEXT_LINE True True True @@ -176,12 +184,22 @@ True True NEXT_LINE + 1 + 1 + NEXT_LINE + MULTILINE NEXT_LINE + 1 + 1 True + NEXT_LINE NEVER NEVER + True False + True NEVER + False False True False @@ -189,6 +207,7 @@ True True False + False CHOP_IF_LONG True 200 diff --git a/osu.iOS.sln.DotSettings b/osu.iOS.sln.DotSettings index 3b2b851d45..752b817910 100644 --- a/osu.iOS.sln.DotSettings +++ b/osu.iOS.sln.DotSettings @@ -1,4 +1,4 @@ - + True True True @@ -165,8 +165,17 @@ HINT HINT WARNING + WARNING <?xml version="1.0" encoding="utf-16"?><Profile name="Code Cleanup (peppy)"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSUseVar><BehavourStyle>CAN_CHANGE_TO_EXPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_EXPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSUpdateFileHeader>True</CSUpdateFileHeader><CSCodeStyleAttributes ArrangeTypeAccessModifier="False" ArrangeTypeMemberAccessModifier="False" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="False" ArrangeBraces="False" ArrangeAttributes="False" ArrangeArgumentsStyle="False" /><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CSArrangeQualifiers>True</CSArrangeQualifiers></Profile> Code Cleanup (peppy) + Required + Required + Required + Explicit + ExpressionBody + ExpressionBody + True + NEXT_LINE True True True @@ -176,12 +185,22 @@ True True NEXT_LINE + 1 + 1 + NEXT_LINE + MULTILINE NEXT_LINE + 1 + 1 True + NEXT_LINE NEVER NEVER + True False + True NEVER + False False True False @@ -189,6 +208,7 @@ True True False + False CHOP_IF_LONG True 200 diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index c3e274569d..ed162eed6e 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -218,9 +218,14 @@ WARNING <?xml version="1.0" encoding="utf-16"?><Profile name="Code Cleanup (peppy)"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSUseVar><BehavourStyle>CAN_CHANGE_TO_EXPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_EXPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSUpdateFileHeader>True</CSUpdateFileHeader><CSCodeStyleAttributes ArrangeTypeAccessModifier="False" ArrangeTypeMemberAccessModifier="False" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="False" ArrangeBraces="False" ArrangeAttributes="False" ArrangeArgumentsStyle="False" /><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CSArrangeQualifiers>True</CSArrangeQualifiers></Profile> Code Cleanup (peppy) + Required + Required + Required + Explicit ExpressionBody ExpressionBody True + NEXT_LINE True True True @@ -232,14 +237,20 @@ NEXT_LINE 1 1 + NEXT_LINE + MULTILINE NEXT_LINE 1 1 True + NEXT_LINE NEVER NEVER + True False + True NEVER + False False True False @@ -247,6 +258,7 @@ True True False + False CHOP_IF_LONG True 200 From a407e267a238575d1418a94f3d912479a84f0bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 15 Sep 2019 22:55:25 +0200 Subject: [PATCH 2760/2854] Fix PF/SD legacy mod conversion Upon investigating an user report in #6091 that indicated that viewing replays using the Perfect mod would also display the Sudden Death mod icon despite Perfect being the more restrictive of the two, it turned out that the logic of importing legacy scores was missing that corner case. A similar case of Double Time/Nightcore mutual exclusion was handled, but PF/SD was missed. Add analogous handling of PF/SD legacy mods for all four rulesets, and additionally cover a tiny fraction of all cases with unit tests. The most problematic cases (NC+HD and PF+SD) are covered in all four basic rulesets. --- .../CatchLegacyModConversionTest.cs | 29 +++++++++++++++ osu.Game.Rulesets.Catch/CatchRuleset.cs | 11 +++--- .../ManiaLegacyModConversionTest.cs | 30 ++++++++++++++++ osu.Game.Rulesets.Mania/ManiaRuleset.cs | 11 +++--- .../OsuLegacyModConversionTest.cs | 30 ++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 11 +++--- .../TaikoLegacyModConversionTest.cs | 29 +++++++++++++++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 11 +++--- .../Tests/Beatmaps/LegacyModConversionTest.cs | 35 +++++++++++++++++++ 9 files changed, 173 insertions(+), 24 deletions(-) create mode 100644 osu.Game.Rulesets.Catch.Tests/CatchLegacyModConversionTest.cs create mode 100644 osu.Game.Rulesets.Mania.Tests/ManiaLegacyModConversionTest.cs create mode 100644 osu.Game.Rulesets.Osu.Tests/OsuLegacyModConversionTest.cs create mode 100644 osu.Game.Rulesets.Taiko.Tests/TaikoLegacyModConversionTest.cs create mode 100644 osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs diff --git a/osu.Game.Rulesets.Catch.Tests/CatchLegacyModConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchLegacyModConversionTest.cs new file mode 100644 index 0000000000..04e6dea376 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/CatchLegacyModConversionTest.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using NUnit.Framework; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets.Catch.Mods; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + public class CatchLegacyModConversionTest : LegacyModConversionTest + { + [TestCase(LegacyMods.Easy, new[] { typeof(CatchModEasy) })] + [TestCase(LegacyMods.HardRock | LegacyMods.DoubleTime, new[] { typeof(CatchModHardRock), typeof(CatchModDoubleTime) })] + [TestCase(LegacyMods.DoubleTime, new[] { typeof(CatchModDoubleTime) })] + [TestCase(LegacyMods.Nightcore, new[] { typeof(CatchModNightcore) })] + [TestCase(LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(CatchModNightcore) })] + [TestCase(LegacyMods.Flashlight | LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(CatchModFlashlight), typeof(CatchModNightcore) })] + [TestCase(LegacyMods.Perfect, new[] { typeof(CatchModPerfect) })] + [TestCase(LegacyMods.SuddenDeath, new[] { typeof(CatchModSuddenDeath) })] + [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath, new[] { typeof(CatchModPerfect) })] + [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath | LegacyMods.DoubleTime, new[] { typeof(CatchModDoubleTime), typeof(CatchModPerfect) })] + public new void Test(LegacyMods legacyMods, Type[] expectedMods) => base.Test(legacyMods, expectedMods); + + protected override Ruleset CreateRuleset() => new CatchRuleset(); + } +} diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 5428b4eeb8..71d68ace94 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -46,6 +46,11 @@ namespace osu.Game.Rulesets.Catch else if (mods.HasFlag(LegacyMods.DoubleTime)) yield return new CatchModDoubleTime(); + if (mods.HasFlag(LegacyMods.Perfect)) + yield return new CatchModPerfect(); + else if (mods.HasFlag(LegacyMods.SuddenDeath)) + yield return new CatchModSuddenDeath(); + if (mods.HasFlag(LegacyMods.Autoplay)) yield return new CatchModAutoplay(); @@ -67,14 +72,8 @@ namespace osu.Game.Rulesets.Catch if (mods.HasFlag(LegacyMods.NoFail)) yield return new CatchModNoFail(); - if (mods.HasFlag(LegacyMods.Perfect)) - yield return new CatchModPerfect(); - if (mods.HasFlag(LegacyMods.Relax)) yield return new CatchModRelax(); - - if (mods.HasFlag(LegacyMods.SuddenDeath)) - yield return new CatchModSuddenDeath(); } public override IEnumerable GetModsFor(ModType type) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaLegacyModConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaLegacyModConversionTest.cs new file mode 100644 index 0000000000..957743c5f1 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/ManiaLegacyModConversionTest.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using NUnit.Framework; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Mania.Tests +{ + [TestFixture] + public class ManiaLegacyModConversionTest : LegacyModConversionTest + { + [TestCase(LegacyMods.Easy, new[] { typeof(ManiaModEasy) })] + [TestCase(LegacyMods.HardRock | LegacyMods.DoubleTime, new[] { typeof(ManiaModHardRock), typeof(ManiaModDoubleTime) })] + [TestCase(LegacyMods.DoubleTime, new[] { typeof(ManiaModDoubleTime) })] + [TestCase(LegacyMods.Nightcore, new[] { typeof(ManiaModNightcore) })] + [TestCase(LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(ManiaModNightcore) })] + [TestCase(LegacyMods.Flashlight | LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(ManiaModFlashlight), typeof(ManiaModNightcore) })] + [TestCase(LegacyMods.Perfect, new[] { typeof(ManiaModPerfect) })] + [TestCase(LegacyMods.SuddenDeath, new[] { typeof(ManiaModSuddenDeath) })] + [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath, new[] { typeof(ManiaModPerfect) })] + [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath | LegacyMods.DoubleTime, new[] { typeof(ManiaModDoubleTime), typeof(ManiaModPerfect) })] + [TestCase(LegacyMods.Random | LegacyMods.SuddenDeath, new[] { typeof(ManiaModRandom), typeof(ManiaModSuddenDeath) })] + public new void Test(LegacyMods legacyMods, Type[] expectedMods) => base.Test(legacyMods, expectedMods); + + protected override Ruleset CreateRuleset() => new ManiaRuleset(); + } +} diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 0c4e7d4858..c74a292331 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -46,6 +46,11 @@ namespace osu.Game.Rulesets.Mania else if (mods.HasFlag(LegacyMods.DoubleTime)) yield return new ManiaModDoubleTime(); + if (mods.HasFlag(LegacyMods.Perfect)) + yield return new ManiaModPerfect(); + else if (mods.HasFlag(LegacyMods.SuddenDeath)) + yield return new ManiaModSuddenDeath(); + if (mods.HasFlag(LegacyMods.Autoplay)) yield return new ManiaModAutoplay(); @@ -97,14 +102,8 @@ namespace osu.Game.Rulesets.Mania if (mods.HasFlag(LegacyMods.NoFail)) yield return new ManiaModNoFail(); - if (mods.HasFlag(LegacyMods.Perfect)) - yield return new ManiaModPerfect(); - if (mods.HasFlag(LegacyMods.Random)) yield return new ManiaModRandom(); - - if (mods.HasFlag(LegacyMods.SuddenDeath)) - yield return new ManiaModSuddenDeath(); } public override IEnumerable GetModsFor(ModType type) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuLegacyModConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuLegacyModConversionTest.cs new file mode 100644 index 0000000000..495f2738b5 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/OsuLegacyModConversionTest.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using NUnit.Framework; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class OsuLegacyModConversionTest : LegacyModConversionTest + { + [TestCase(LegacyMods.Easy, new[] { typeof(OsuModEasy) })] + [TestCase(LegacyMods.HardRock | LegacyMods.DoubleTime, new[] { typeof(OsuModHardRock), typeof(OsuModDoubleTime) })] + [TestCase(LegacyMods.DoubleTime, new[] { typeof(OsuModDoubleTime) })] + [TestCase(LegacyMods.Nightcore, new[] { typeof(OsuModNightcore) })] + [TestCase(LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(OsuModNightcore) })] + [TestCase(LegacyMods.Flashlight | LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(OsuModFlashlight), typeof(OsuModFlashlight) })] + [TestCase(LegacyMods.Perfect, new[] { typeof(OsuModPerfect) })] + [TestCase(LegacyMods.SuddenDeath, new[] { typeof(OsuModSuddenDeath) })] + [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath, new[] { typeof(OsuModPerfect) })] + [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath | LegacyMods.DoubleTime, new[] { typeof(OsuModDoubleTime), typeof(OsuModPerfect) })] + [TestCase(LegacyMods.SpunOut | LegacyMods.Easy, new[] { typeof(OsuModSpunOut), typeof(OsuModEasy) })] + public new void Test(LegacyMods legacyMods, Type[] expectedMods) => base.Test(legacyMods, expectedMods); + + protected override Ruleset CreateRuleset() => new OsuRuleset(); + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index ceb9ed9343..df2ae81a5a 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -52,6 +52,11 @@ namespace osu.Game.Rulesets.Osu else if (mods.HasFlag(LegacyMods.DoubleTime)) yield return new OsuModDoubleTime(); + if (mods.HasFlag(LegacyMods.Perfect)) + yield return new OsuModPerfect(); + else if (mods.HasFlag(LegacyMods.SuddenDeath)) + yield return new OsuModSuddenDeath(); + if (mods.HasFlag(LegacyMods.Autopilot)) yield return new OsuModAutopilot(); @@ -76,18 +81,12 @@ namespace osu.Game.Rulesets.Osu if (mods.HasFlag(LegacyMods.NoFail)) yield return new OsuModNoFail(); - if (mods.HasFlag(LegacyMods.Perfect)) - yield return new OsuModPerfect(); - if (mods.HasFlag(LegacyMods.Relax)) yield return new OsuModRelax(); if (mods.HasFlag(LegacyMods.SpunOut)) yield return new OsuModSpunOut(); - if (mods.HasFlag(LegacyMods.SuddenDeath)) - yield return new OsuModSuddenDeath(); - if (mods.HasFlag(LegacyMods.Target)) yield return new OsuModTarget(); diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoLegacyModConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoLegacyModConversionTest.cs new file mode 100644 index 0000000000..a59544386b --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoLegacyModConversionTest.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using NUnit.Framework; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets.Taiko.Mods; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + [TestFixture] + public class TaikoLegacyModConversionTest : LegacyModConversionTest + { + [TestCase(LegacyMods.Easy, new[] { typeof(TaikoModEasy) })] + [TestCase(LegacyMods.HardRock | LegacyMods.DoubleTime, new[] { typeof(TaikoModHardRock), typeof(TaikoModDoubleTime) })] + [TestCase(LegacyMods.DoubleTime, new[] { typeof(TaikoModDoubleTime) })] + [TestCase(LegacyMods.Nightcore, new[] { typeof(TaikoModNightcore) })] + [TestCase(LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(TaikoModNightcore) })] + [TestCase(LegacyMods.Flashlight | LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(TaikoModFlashlight), typeof(TaikoModNightcore) })] + [TestCase(LegacyMods.Perfect, new[] { typeof(TaikoModPerfect) })] + [TestCase(LegacyMods.SuddenDeath, new[] { typeof(TaikoModSuddenDeath) })] + [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath, new[] { typeof(TaikoModPerfect) })] + [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath | LegacyMods.DoubleTime, new[] { typeof(TaikoModDoubleTime), typeof(TaikoModPerfect) })] + public new void Test(LegacyMods legacyMods, Type[] expectedMods) => base.Test(legacyMods, expectedMods); + + protected override Ruleset CreateRuleset() => new TaikoRuleset(); + } +} diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 7fdb823388..b2655f592c 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -45,6 +45,11 @@ namespace osu.Game.Rulesets.Taiko else if (mods.HasFlag(LegacyMods.DoubleTime)) yield return new TaikoModDoubleTime(); + if (mods.HasFlag(LegacyMods.Perfect)) + yield return new TaikoModPerfect(); + else if (mods.HasFlag(LegacyMods.SuddenDeath)) + yield return new TaikoModSuddenDeath(); + if (mods.HasFlag(LegacyMods.Autoplay)) yield return new TaikoModAutoplay(); @@ -66,14 +71,8 @@ namespace osu.Game.Rulesets.Taiko if (mods.HasFlag(LegacyMods.NoFail)) yield return new TaikoModNoFail(); - if (mods.HasFlag(LegacyMods.Perfect)) - yield return new TaikoModPerfect(); - if (mods.HasFlag(LegacyMods.Relax)) yield return new TaikoModRelax(); - - if (mods.HasFlag(LegacyMods.SuddenDeath)) - yield return new TaikoModSuddenDeath(); } public override IEnumerable GetModsFor(ModType type) diff --git a/osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs b/osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs new file mode 100644 index 0000000000..e9251f8011 --- /dev/null +++ b/osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs @@ -0,0 +1,35 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using NUnit.Framework; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets; + +namespace osu.Game.Tests.Beatmaps +{ + /// + /// Base class for tests of converting enumeration flags to ruleset mod instances. + /// + public abstract class LegacyModConversionTest + { + /// + /// Creates the whose legacy mod conversion is to be tested. + /// + /// + protected abstract Ruleset CreateRuleset(); + + protected void Test(LegacyMods legacyMods, Type[] expectedMods) + { + var ruleset = CreateRuleset(); + var mods = ruleset.ConvertLegacyMods(legacyMods).ToList(); + Assert.AreEqual(expectedMods.Length, mods.Count); + + foreach (var modType in expectedMods) + { + Assert.IsNotNull(mods.SingleOrDefault(mod => mod.GetType() == modType)); + } + } + } +} From efedfefe635e63c8cad19c73ff56e29482741be9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Sep 2019 15:54:11 +0900 Subject: [PATCH 2761/2854] Fix disclaimer potentially pushing a null screen --- osu.Game/Screens/Menu/Disclaimer.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 073ab639e3..17f999d519 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -173,7 +173,11 @@ namespace osu.Game.Screens.Menu .Then(5500) .FadeOut(250) .ScaleTo(0.9f, 250, Easing.InQuint) - .Finally(d => this.Push(nextScreen)); + .Finally(d => + { + if (nextScreen != null) + this.Push(nextScreen); + }); } } } From f0bcb2b9337a94fa429b9aa609c5444f95dbab34 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Sep 2019 16:12:18 +0900 Subject: [PATCH 2762/2854] Debounce user-requested replay seeks --- osu.Game/Screens/Play/SongProgressBar.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/SongProgressBar.cs b/osu.Game/Screens/Play/SongProgressBar.cs index dd7b5826d5..33c7595b37 100644 --- a/osu.Game/Screens/Play/SongProgressBar.cs +++ b/osu.Game/Screens/Play/SongProgressBar.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.MathUtils; +using osu.Framework.Threading; namespace osu.Game.Screens.Play { @@ -121,6 +122,12 @@ namespace osu.Game.Screens.Play handleBase.X = newX; } - protected override void OnUserChange(double value) => OnSeek?.Invoke(value); + private ScheduledDelegate scheduledSeek; + + protected override void OnUserChange(double value) + { + scheduledSeek?.Cancel(); + scheduledSeek = Schedule(() => OnSeek?.Invoke(value)); + } } } From 77947e83097164eb73adc4b380fd2b80fcf643e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Sep 2019 22:33:27 +0900 Subject: [PATCH 2763/2854] Fix rewind tests failing --- .../Replays/CatchAutoGenerator.cs | 1 + .../Replays/ManiaAutoGenerator.cs | 1 + .../Visual/Gameplay/TestSceneAutoplay.cs | 19 ++++++++++++++----- osu.Game/Screens/Play/GameplayClock.cs | 2 ++ osu.Game/Tests/Visual/OsuTestScene.cs | 4 ++-- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index 4ea1f22006..7f972a25ae 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -39,6 +39,7 @@ namespace osu.Game.Rulesets.Catch.Replays // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled addFrame(-100000, lastPosition); + addFrame(0, lastPosition); void moveToNext(CatchHitObject h) { diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 7b8bbc2095..5d333e2047 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -49,6 +49,7 @@ namespace osu.Game.Rulesets.Mania.Replays { // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled Replay.Frames.Add(new ManiaReplayFrame(-100000, 0)); + Replay.Frames.Add(new ManiaReplayFrame(0, 0)); var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs index e2b620ea98..883aa9fc36 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Linq; +using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; @@ -12,6 +13,8 @@ namespace osu.Game.Tests.Visual.Gameplay [Description("Player instantiated with an autoplay mod.")] public class TestSceneAutoplay : AllPlayersTestScene { + private ClockBackedTestWorkingBeatmap.TrackVirtualManual track; + protected override Player CreatePlayer(Ruleset ruleset) { Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); @@ -22,11 +25,17 @@ namespace osu.Game.Tests.Visual.Gameplay { AddUntilStep("score above zero", () => ((ScoreAccessiblePlayer)Player).ScoreProcessor.TotalScore.Value > 0); AddUntilStep("key counter counted keys", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 5)); - AddStep("rewind", () => - { - ((ScoreAccessiblePlayer)Player).GameplayClockContainer.Seek(0); - }); - AddUntilStep("key counter counted no", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0)); + AddStep("rewind", () => track.Seek(-10000)); + AddUntilStep("key counter reset", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0)); + } + + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) + { + var working = base.CreateWorkingBeatmap(beatmap); + + track = (ClockBackedTestWorkingBeatmap.TrackVirtualManual)working.Track; + + return working; } private class ScoreAccessiblePlayer : TestPlayer diff --git a/osu.Game/Screens/Play/GameplayClock.cs b/osu.Game/Screens/Play/GameplayClock.cs index b1948d02d5..379c4c89ed 100644 --- a/osu.Game/Screens/Play/GameplayClock.cs +++ b/osu.Game/Screens/Play/GameplayClock.cs @@ -42,5 +42,7 @@ namespace osu.Game.Screens.Play public double FramesPerSecond => underlyingClock.FramesPerSecond; public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo; + + public IClock Source => underlyingClock; } } diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 2b8baab57c..32a32cfa43 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -239,7 +239,7 @@ namespace osu.Game.Tests.Visual public override bool Seek(double seek) { - offset = MathHelper.Clamp(seek, 0, Length); + offset = Math.Min(seek, Length); lastReferenceTime = null; return offset == seek; From 057c4aa795657654822ce73c086352e75a9677f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Sep 2019 22:42:20 +0900 Subject: [PATCH 2764/2854] Remove unused using statement --- osu.Game/Tests/Visual/OsuTestScene.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 32a32cfa43..2bc9273f69 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -20,7 +20,6 @@ using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Tests.Beatmaps; -using osuTK; namespace osu.Game.Tests.Visual { From 3ab352ffe51b0a37fef9af64569b91c9bbc0d2b0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Sep 2019 23:08:37 +0900 Subject: [PATCH 2765/2854] Randomise beatmap playback order on startup Closes #6135. --- osu.Game/Overlays/Music/PlaylistList.cs | 18 ++++++++++-------- osu.Game/Overlays/MusicController.cs | 21 +++++++++++---------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index 539601c359..6ad0aa23ec 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -44,6 +45,8 @@ namespace osu.Game.Overlays.Music private class ItemsScrollContainer : OsuScrollContainer { + private BindableList beatmaps; + public Action Selected; public Action OrderChanged; @@ -73,20 +76,19 @@ namespace osu.Game.Overlays.Music } [BackgroundDependencyLoader] - private void load(BeatmapManager beatmaps, IBindable beatmap) + private void load(MusicController musicController, IBindable beatmap) { - beatmaps.GetAllUsableBeatmapSets().ForEach(addBeatmapSet); - beatmaps.ItemAdded += addBeatmapSet; - beatmaps.ItemRemoved += removeBeatmapSet; + beatmaps = musicController.BeatmapSets.GetBoundCopy(); + + beatmaps.ItemsAdded += i => i.ForEach(addBeatmapSet); + beatmaps.ItemsRemoved += i => i.ForEach(removeBeatmapSet); + beatmaps.ForEach(addBeatmapSet); beatmapBacking.BindTo(beatmap); beatmapBacking.ValueChanged += _ => updateSelectedSet(); } - private void addBeatmapSet(BeatmapSetInfo obj) => Schedule(() => - { - items.Insert(items.Count - 1, new PlaylistItem(obj) { OnSelect = set => Selected?.Invoke(set) }); - }); + private void addBeatmapSet(BeatmapSetInfo obj) => Schedule(() => { items.Insert(items.Count - 1, new PlaylistItem(obj) { OnSelect = set => Selected?.Invoke(set) }); }); private void removeBeatmapSet(BeatmapSetInfo obj) => Schedule(() => { diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 6ad147735b..92246dfc62 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; +using osu.Framework.MathUtils; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Input.Bindings; @@ -24,7 +25,7 @@ namespace osu.Game.Overlays [Resolved] private BeatmapManager beatmaps { get; set; } - private List beatmapSets; + public readonly BindableList BeatmapSets = new BindableList(); public bool IsUserPaused { get; private set; } @@ -46,7 +47,7 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load() { - beatmapSets = beatmaps.GetAllUsableBeatmapSets(); + BeatmapSets.AddRange(beatmaps.GetAllUsableBeatmapSets().OrderBy(_ => RNG.Next())); beatmaps.ItemAdded += handleBeatmapAdded; beatmaps.ItemRemoved += handleBeatmapRemoved; } @@ -65,8 +66,8 @@ namespace osu.Game.Overlays /// The new position. public void ChangeBeatmapSetPosition(BeatmapSetInfo beatmapSetInfo, int index) { - beatmapSets.Remove(beatmapSetInfo); - beatmapSets.Insert(index, beatmapSetInfo); + BeatmapSets.Remove(beatmapSetInfo); + BeatmapSets.Insert(index, beatmapSetInfo); } /// @@ -75,10 +76,10 @@ namespace osu.Game.Overlays public bool IsPlaying => beatmap.Value.Track.IsRunning; private void handleBeatmapAdded(BeatmapSetInfo set) => - Schedule(() => beatmapSets.Add(set)); + Schedule(() => BeatmapSets.Add(set)); private void handleBeatmapRemoved(BeatmapSetInfo set) => - Schedule(() => beatmapSets.RemoveAll(s => s.ID == set.ID)); + Schedule(() => BeatmapSets.RemoveAll(s => s.ID == set.ID)); private ScheduledDelegate seekDelegate; @@ -140,7 +141,7 @@ namespace osu.Game.Overlays { queuedDirection = TrackChangeDirection.Prev; - var playable = beatmapSets.TakeWhile(i => i.ID != current.BeatmapSetInfo.ID).LastOrDefault() ?? beatmapSets.LastOrDefault(); + var playable = BeatmapSets.TakeWhile(i => i.ID != current.BeatmapSetInfo.ID).LastOrDefault() ?? BeatmapSets.LastOrDefault(); if (playable != null) { @@ -165,7 +166,7 @@ namespace osu.Game.Overlays if (!instant) queuedDirection = TrackChangeDirection.Next; - var playable = beatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).Skip(1).FirstOrDefault() ?? beatmapSets.FirstOrDefault(); + var playable = BeatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).Skip(1).FirstOrDefault() ?? BeatmapSets.FirstOrDefault(); if (playable != null) { @@ -200,8 +201,8 @@ namespace osu.Game.Overlays else { //figure out the best direction based on order in playlist. - var last = beatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count(); - var next = beatmap.NewValue == null ? -1 : beatmapSets.TakeWhile(b => b.ID != beatmap.NewValue.BeatmapSetInfo?.ID).Count(); + var last = BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count(); + var next = beatmap.NewValue == null ? -1 : BeatmapSets.TakeWhile(b => b.ID != beatmap.NewValue.BeatmapSetInfo?.ID).Count(); direction = last > next ? TrackChangeDirection.Prev : TrackChangeDirection.Next; } From b5b29a21e783b93d21ea955d473fce33d59c8d47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Sep 2019 02:15:18 +0900 Subject: [PATCH 2766/2854] Move menu cursor rotation to more appropriate settings section --- .../Overlays/Settings/Sections/Graphics/DetailSettings.cs | 5 ----- .../{MainMenuSettings.cs => UserInterfaceSettings.cs} | 7 ++++++- osu.Game/Overlays/Settings/Sections/GraphicsSection.cs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) rename osu.Game/Overlays/Settings/Sections/Graphics/{MainMenuSettings.cs => UserInterfaceSettings.cs} (71%) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs index 56e56f6ca8..9be34c3073 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs @@ -26,11 +26,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics LabelText = "Video", Bindable = config.GetBindable(OsuSetting.ShowVideoBackground) }, - new SettingsCheckbox - { - LabelText = "Rotate cursor when dragging", - Bindable = config.GetBindable(OsuSetting.CursorRotation) - }, new SettingsEnumDropdown { LabelText = "Screenshot format", diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs similarity index 71% rename from osu.Game/Overlays/Settings/Sections/Graphics/MainMenuSettings.cs rename to osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs index 92f64d0e14..dd822fedb6 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs @@ -6,7 +6,7 @@ using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Graphics { - public class MainMenuSettings : SettingsSubsection + public class UserInterfaceSettings : SettingsSubsection { protected override string Header => "User Interface"; @@ -15,6 +15,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { Children = new[] { + new SettingsCheckbox + { + LabelText = "Rotate cursor when dragging", + Bindable = config.GetBindable(OsuSetting.CursorRotation) + }, new SettingsCheckbox { LabelText = "Parallax", diff --git a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs index 3d6086d3ea..89caa3dc8f 100644 --- a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Settings.Sections new RendererSettings(), new LayoutSettings(), new DetailSettings(), - new MainMenuSettings(), + new UserInterfaceSettings(), }; } } From 63cc8d4f90b29a165f43eb704cc9c0a057766a5f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Sep 2019 02:16:57 +0900 Subject: [PATCH 2767/2854] Add hit lighting setting --- osu.Game/Configuration/OsuConfigManager.cs | 5 ++++- .../Overlays/Settings/Sections/Graphics/DetailSettings.cs | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index e26021d930..4cbf2b4d94 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -81,6 +81,8 @@ namespace osu.Game.Configuration Set(OsuSetting.DimLevel, 0.3, 0, 1, 0.01); Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01); + Set(OsuSetting.HitLighting, true); + Set(OsuSetting.ShowInterface, true); Set(OsuSetting.ShowHealthDisplayWhenCantFail, true); Set(OsuSetting.KeyOverlay, false); @@ -180,6 +182,7 @@ namespace osu.Game.Configuration ScalingSizeX, ScalingSizeY, UIScale, - IntroSequence + IntroSequence, + HitLighting } } diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs index 56e56f6ca8..5189d573cc 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs @@ -27,6 +27,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics Bindable = config.GetBindable(OsuSetting.ShowVideoBackground) }, new SettingsCheckbox + { + LabelText = "Hit Lighting", + Bindable = config.GetBindable(OsuSetting.HitLighting) + }, + new SettingsCheckbox { LabelText = "Rotate cursor when dragging", Bindable = config.GetBindable(OsuSetting.CursorRotation) From ba76f09c99df0c59c37c85c32ccbdc24875a9d30 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Sep 2019 02:49:54 +0900 Subject: [PATCH 2768/2854] Add initial implementation of hit lighting Requires a supporting skin, like osu!classic for now. --- .../Objects/Drawables/DrawableOsuJudgement.cs | 44 +++++++++++++++++++ .../Rulesets/Judgements/DrawableJudgement.cs | 8 +++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index 938a2293ba..022e9ea12b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -1,22 +1,66 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Configuration; using osuTK; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableOsuJudgement : DrawableJudgement { + private SkinnableSprite lighting; + private Bindable lightingColour; + public DrawableOsuJudgement(JudgementResult result, DrawableHitObject judgedObject) : base(result, judgedObject) { } + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + if (config.Get(OsuSetting.HitLighting) && Result.Type != HitResult.Miss) + { + AddInternal(lighting = new SkinnableSprite("lighting") + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Blending = BlendingParameters.Additive, + Depth = float.MaxValue + }); + + if (JudgedObject != null) + { + lightingColour = JudgedObject.AccentColour.GetBoundCopy(); + lightingColour.BindValueChanged(colour => lighting.Colour = colour.NewValue, true); + } + else + { + lighting.Colour = Color4.White; + } + } + } + + protected override double FadeOutDelay => lighting == null ? base.FadeOutDelay : 1400; + protected override void ApplyHitAnimations() { + if (lighting != null) + { + JudgementBody.Delay(FadeInDuration).FadeOut(400); + + lighting.ScaleTo(0.8f).ScaleTo(1.2f, 600, Easing.Out); + lighting.FadeIn(200).Then().Delay(200).FadeOut(1000); + } + JudgementText?.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint); base.ApplyHitAnimations(); } diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 4f8cb7660b..4c503ea59c 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -34,10 +34,14 @@ namespace osu.Game.Rulesets.Judgements /// /// Duration of initial fade in. - /// Default fade out will start immediately after this duration. /// protected virtual double FadeInDuration => 100; + /// + /// Duration to wait until fade out begins. Defaults to . + /// + protected virtual double FadeOutDelay => FadeInDuration; + /// /// Creates a drawable which visualises a . /// @@ -76,7 +80,7 @@ namespace osu.Game.Rulesets.Judgements JudgementBody.ScaleTo(0.9f); JudgementBody.ScaleTo(1, 500, Easing.OutElastic); - this.Delay(FadeInDuration).FadeOut(400); + this.Delay(FadeOutDelay).FadeOut(400); } protected override void LoadComplete() From 26eca5b1f425403df404da0ab4b4157e9f5fce52 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Sep 2019 02:56:03 +0900 Subject: [PATCH 2769/2854] Fix judgement sizes not matching skins stable --- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 2 +- osu.Game/Rulesets/Judgements/DrawableJudgement.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index df12ebc514..d1757de445 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Osu.UI { Origin = Anchor.Centre, Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition, - Scale = new Vector2(((OsuHitObject)judgedObject.HitObject).Scale * 1.65f) + Scale = new Vector2(((OsuHitObject)judgedObject.HitObject).Scale) }; judgementLayer.Add(explosion); diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 4f8cb7660b..061c8cb3e1 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Judgements /// public class DrawableJudgement : CompositeDrawable { - private const float judgement_size = 80; + private const float judgement_size = 128; private OsuColour colours; @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Judgements Child = new SkinnableDrawable(new GameplaySkinComponent(Result.Type), _ => JudgementText = new OsuSpriteText { Text = Result.Type.GetDescription().ToUpperInvariant(), - Font = OsuFont.Numeric.With(size: 12), + Font = OsuFont.Numeric.With(size: 20), Colour = judgementColour(Result.Type), Scale = new Vector2(0.85f, 1), }) From adc2dfa6c6ffecf27ae299d0cecd1e2844d380f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Sep 2019 03:00:17 +0900 Subject: [PATCH 2770/2854] Fix HitCircleLongCombo test stacking off-screen --- osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs index 399cf22599..95c2810e94 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs @@ -29,7 +29,8 @@ namespace osu.Game.Rulesets.Osu.Tests }; for (int i = 0; i < 512; i++) - beatmap.HitObjects.Add(new HitCircle { Position = new Vector2(256, 192), StartTime = i * 100 }); + if (i % 32 < 20) + beatmap.HitObjects.Add(new HitCircle { Position = new Vector2(256, 192), StartTime = i * 100 }); return beatmap; } From 7e791f7cd78e2fed4e67f91a739e8a6411da891a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Sep 2019 13:14:33 +0900 Subject: [PATCH 2771/2854] Expose as IBindableList --- osu.Game/Overlays/MusicController.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 92246dfc62..db94b0278f 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -25,7 +25,9 @@ namespace osu.Game.Overlays [Resolved] private BeatmapManager beatmaps { get; set; } - public readonly BindableList BeatmapSets = new BindableList(); + public IBindableList BeatmapSets => beatmapSets; + + private readonly BindableList beatmapSets = new BindableList(); public bool IsUserPaused { get; private set; } @@ -47,7 +49,7 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load() { - BeatmapSets.AddRange(beatmaps.GetAllUsableBeatmapSets().OrderBy(_ => RNG.Next())); + beatmapSets.AddRange(beatmaps.GetAllUsableBeatmapSets().OrderBy(_ => RNG.Next())); beatmaps.ItemAdded += handleBeatmapAdded; beatmaps.ItemRemoved += handleBeatmapRemoved; } @@ -66,8 +68,8 @@ namespace osu.Game.Overlays /// The new position. public void ChangeBeatmapSetPosition(BeatmapSetInfo beatmapSetInfo, int index) { - BeatmapSets.Remove(beatmapSetInfo); - BeatmapSets.Insert(index, beatmapSetInfo); + beatmapSets.Remove(beatmapSetInfo); + beatmapSets.Insert(index, beatmapSetInfo); } /// @@ -76,10 +78,10 @@ namespace osu.Game.Overlays public bool IsPlaying => beatmap.Value.Track.IsRunning; private void handleBeatmapAdded(BeatmapSetInfo set) => - Schedule(() => BeatmapSets.Add(set)); + Schedule(() => beatmapSets.Add(set)); private void handleBeatmapRemoved(BeatmapSetInfo set) => - Schedule(() => BeatmapSets.RemoveAll(s => s.ID == set.ID)); + Schedule(() => beatmapSets.RemoveAll(s => s.ID == set.ID)); private ScheduledDelegate seekDelegate; From 91bdece9af42fe2848d9fef7b56432cdaaee279c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Sep 2019 13:15:39 +0900 Subject: [PATCH 2772/2854] Localise OrderChanged handling and fix callbacks The dragged item's position now only updates after the drag finishes. Local handling changes were required to ignore the bindable remove/add events that are fired as a result. --- osu.Game/Overlays/Music/PlaylistList.cs | 42 +++++++++++++++++----- osu.Game/Overlays/Music/PlaylistOverlay.cs | 8 ----- osu.Game/Overlays/NowPlayingOverlay.cs | 1 - 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index 6ad0aa23ec..636945edd2 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -19,7 +19,6 @@ namespace osu.Game.Overlays.Music public class PlaylistList : CompositeDrawable { public Action Selected; - public Action OrderChanged; private readonly ItemsScrollContainer items; @@ -29,7 +28,6 @@ namespace osu.Game.Overlays.Music { RelativeSizeAxes = Axes.Both, Selected = set => Selected?.Invoke(set), - OrderChanged = (s, i) => OrderChanged?.Invoke(s, i) }; } @@ -45,16 +43,17 @@ namespace osu.Game.Overlays.Music private class ItemsScrollContainer : OsuScrollContainer { - private BindableList beatmaps; + private IBindableList beatmaps; public Action Selected; - public Action OrderChanged; private readonly SearchContainer search; private readonly FillFlowContainer items; private readonly IBindable beatmapBacking = new Bindable(); + private MusicController musicController; + public ItemsScrollContainer() { Children = new Drawable[] @@ -78,6 +77,8 @@ namespace osu.Game.Overlays.Music [BackgroundDependencyLoader] private void load(MusicController musicController, IBindable beatmap) { + this.musicController = musicController; + beatmaps = musicController.BeatmapSets.GetBoundCopy(); beatmaps.ItemsAdded += i => i.ForEach(addBeatmapSet); @@ -88,10 +89,21 @@ namespace osu.Game.Overlays.Music beatmapBacking.ValueChanged += _ => updateSelectedSet(); } - private void addBeatmapSet(BeatmapSetInfo obj) => Schedule(() => { items.Insert(items.Count - 1, new PlaylistItem(obj) { OnSelect = set => Selected?.Invoke(set) }); }); + private void addBeatmapSet(BeatmapSetInfo obj) => Schedule(() => + { + if (obj == draggedItem?.BeatmapSetInfo) + { + draggedItem = null; + return; + } + + items.Insert(items.Count - 1, new PlaylistItem(obj) { OnSelect = set => Selected?.Invoke(set) }); + }); private void removeBeatmapSet(BeatmapSetInfo obj) => Schedule(() => { + if (obj == draggedItem?.BeatmapSetInfo) return; + var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == obj.ID); if (itemToRemove != null) items.Remove(itemToRemove); @@ -114,6 +126,8 @@ namespace osu.Game.Overlays.Music private Vector2 nativeDragPosition; private PlaylistItem draggedItem; + private int? dragDestination; + protected override bool OnDragStart(DragStartEvent e) { nativeDragPosition = e.ScreenSpaceMousePosition; @@ -133,10 +147,20 @@ namespace osu.Game.Overlays.Music protected override bool OnDragEnd(DragEndEvent e) { nativeDragPosition = e.ScreenSpaceMousePosition; - var handled = draggedItem != null || base.OnDragEnd(e); - draggedItem = null; - return handled; + if (draggedItem != null) + { + if (dragDestination != null) + { + // draggedItem is nulled when the BindableList's add event is received so we can quietly ignore the callbacks. + musicController.ChangeBeatmapSetPosition(draggedItem.BeatmapSetInfo, dragDestination.Value); + dragDestination = null; + } + + return true; + } + + return base.OnDragEnd(e); } protected override void Update() @@ -212,7 +236,7 @@ namespace osu.Game.Overlays.Music } items.SetLayoutPosition(draggedItem, dstIndex); - OrderChanged?.Invoke(draggedItem.BeatmapSetInfo, dstIndex); + dragDestination = dstIndex; } private class ItemSearchContainer : FillFlowContainer, IHasFilterableChildren diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index ec3d708645..ae81a6c117 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -22,12 +21,6 @@ namespace osu.Game.Overlays.Music private const float transition_duration = 600; private const float playlist_height = 510; - /// - /// Invoked when the order of an item in the list has changed. - /// The second parameter indicates the new index of the item. - /// - public Action OrderChanged; - private readonly Bindable beatmap = new Bindable(); private BeatmapManager beatmaps; @@ -65,7 +58,6 @@ namespace osu.Game.Overlays.Music RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Top = 95, Bottom = 10, Right = 10 }, Selected = itemSelected, - OrderChanged = (s, i) => OrderChanged?.Invoke(s, i) }, filter = new FilterControl { diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index cf42c8005a..6b79f2af07 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -81,7 +81,6 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.X, Y = player_height + 10, - OrderChanged = musicController.ChangeBeatmapSetPosition }, playerContainer = new Container { From 2db1e236a7180174ffa78318e29d92c0e9b6842f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Sep 2019 14:08:09 +0900 Subject: [PATCH 2773/2854] Fix frame count dependent tests regressing --- .../TestSceneAutoGeneration.cs | 101 ++++++++++-------- 1 file changed, 54 insertions(+), 47 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs index f260357df5..8206e33c7c 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs @@ -16,6 +16,11 @@ namespace osu.Game.Rulesets.Mania.Tests [HeadlessTest] public class TestSceneAutoGeneration : OsuTestScene { + /// + /// The number of frames which are generated at the start of a replay regardless of hitobject content. + /// + private const int frame_offset = 2; + [Test] public void TestSingleNote() { @@ -28,11 +33,11 @@ namespace osu.Game.Rulesets.Mania.Tests var generated = new ManiaAutoGenerator(beatmap).Generate(); - Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); - Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); - Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); - Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released"); + Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames"); + Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time"); + Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time"); + Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Special1), "Special1 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Special1), "Special1 has not been released"); } [Test] @@ -49,11 +54,11 @@ namespace osu.Game.Rulesets.Mania.Tests var generated = new ManiaAutoGenerator(beatmap).Generate(); - Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); - Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); - Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); - Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released"); + Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames"); + Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time"); + Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time"); + Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Special1), "Special1 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Special1), "Special1 has not been released"); } [Test] @@ -69,11 +74,11 @@ namespace osu.Game.Rulesets.Mania.Tests var generated = new ManiaAutoGenerator(beatmap).Generate(); - Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); - Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); - Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); - Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released"); + Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames"); + Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time"); + Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time"); + Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released"); } [Test] @@ -91,11 +96,13 @@ namespace osu.Game.Rulesets.Mania.Tests var generated = new ManiaAutoGenerator(beatmap).Generate(); - Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); - Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); - Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); - Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released"); + Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames"); + + Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time"); + Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time"); + + Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released"); } [Test] @@ -112,15 +119,15 @@ namespace osu.Game.Rulesets.Mania.Tests var generated = new ManiaAutoGenerator(beatmap).Generate(); - Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames"); - Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); - Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect first note release time"); - Assert.AreEqual(2000, generated.Frames[3].Time, "Incorrect second note hit time"); - Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time"); - Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released"); - Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released"); + Assert.IsTrue(generated.Frames.Count == frame_offset + 4, "Replay must have 4 generated frames"); + Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time"); + Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect first note release time"); + Assert.AreEqual(2000, generated.Frames[frame_offset + 2].Time, "Incorrect second note hit time"); + Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time"); + Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released"); + Assert.IsTrue(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key2), "Key2 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[frame_offset + 3], ManiaAction.Key2), "Key2 has not been released"); } [Test] @@ -139,16 +146,16 @@ namespace osu.Game.Rulesets.Mania.Tests var generated = new ManiaAutoGenerator(beatmap).Generate(); - Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames"); - Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); - Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect first note release time"); - Assert.AreEqual(2000, generated.Frames[2].Time, "Incorrect second note hit time"); - Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time"); - Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed"); - Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key1), "Key1 has not been released"); - Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has been released"); - Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released"); + Assert.IsTrue(generated.Frames.Count == frame_offset + 4, "Replay must have 4 generated frames"); + Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time"); + Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 2].Time, "Incorrect first note release time"); + Assert.AreEqual(2000, generated.Frames[frame_offset + 1].Time, "Incorrect second note hit time"); + Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time"); + Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed"); + Assert.IsTrue(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key1), "Key1 has not been released"); + Assert.IsTrue(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key2), "Key2 has been released"); + Assert.IsFalse(checkContains(generated.Frames[frame_offset + 3], ManiaAction.Key2), "Key2 has not been released"); } [Test] @@ -166,14 +173,14 @@ namespace osu.Game.Rulesets.Mania.Tests var generated = new ManiaAutoGenerator(beatmap).Generate(); - Assert.IsTrue(generated.Frames.Count == 4, "Replay must have 4 frames"); - Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); - Assert.AreEqual(3000, generated.Frames[2].Time, "Incorrect second note press time + first note release time"); - Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect second note release time"); - Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released"); - Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key2), "Key2 has not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been released"); + Assert.IsTrue(generated.Frames.Count == frame_offset + 3, "Replay must have 3 generated frames"); + Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time"); + Assert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect second note press time + first note release time"); + Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 2].Time, "Incorrect second note release time"); + Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released"); + Assert.IsTrue(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key2), "Key2 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key2), "Key2 has not been released"); } private bool checkContains(ReplayFrame frame, params ManiaAction[] actions) => actions.All(action => ((ManiaReplayFrame)frame).Actions.Contains(action)); From 2046f64b22b31c753390da30bc71d33b59381c96 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Sep 2019 16:07:02 +0900 Subject: [PATCH 2774/2854] Revert clamping logic --- osu.Game/Tests/Visual/OsuTestScene.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 2bc9273f69..8e98d51962 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -20,6 +20,7 @@ using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Tests.Beatmaps; +using osuTK; namespace osu.Game.Tests.Visual { @@ -238,7 +239,7 @@ namespace osu.Game.Tests.Visual public override bool Seek(double seek) { - offset = Math.Min(seek, Length); + offset = MathHelper.Clamp(seek, 0, Length); lastReferenceTime = null; return offset == seek; From 381daffe527afef49687cbd1aed9ba026f718ef2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Sep 2019 16:07:29 +0900 Subject: [PATCH 2775/2854] Generate better temporary frames to support framed handling flaws --- osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs | 8 ++++---- osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs | 2 +- osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs | 8 ++++---- osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs | 3 ++- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index 7f972a25ae..6c8515eb90 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -37,10 +37,6 @@ namespace osu.Game.Rulesets.Catch.Replays float lastPosition = 0.5f; double lastTime = 0; - // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled - addFrame(-100000, lastPosition); - addFrame(0, lastPosition); - void moveToNext(CatchHitObject h) { float positionChange = Math.Abs(lastPosition - h.X); @@ -128,6 +124,10 @@ namespace osu.Game.Rulesets.Catch.Replays private void addFrame(double time, float? position = null, bool dashing = false) { + // todo: can be removed once FramedReplayInputHandler correctly handles rewinding before first frame. + if (Replay.Frames.Count == 0) + Replay.Frames.Add(new CatchReplayFrame(time - 1, position, false, null)); + var last = currentFrame; currentFrame = new CatchReplayFrame(time, position, dashing, last); Replay.Frames.Add(currentFrame); diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs index 8206e33c7c..a5248c7712 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Tests /// /// The number of frames which are generated at the start of a replay regardless of hitobject content. /// - private const int frame_offset = 2; + private const int frame_offset = 1; [Test] public void TestSingleNote() diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 5d333e2047..2b336ca16d 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -47,10 +47,6 @@ namespace osu.Game.Rulesets.Mania.Replays public override Replay Generate() { - // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled - Replay.Frames.Add(new ManiaReplayFrame(-100000, 0)); - Replay.Frames.Add(new ManiaReplayFrame(0, 0)); - var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time); var actions = new List(); @@ -71,6 +67,10 @@ namespace osu.Game.Rulesets.Mania.Replays } } + // todo: can be removed once FramedReplayInputHandler correctly handles rewinding before first frame. + if (Replay.Frames.Count == 0) + Replay.Frames.Add(new ManiaReplayFrame(group.First().Time - 1)); + Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray())); } diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index 70ba5cd938..72c7eb60e0 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -21,7 +21,8 @@ namespace osu.Game.Rulesets.Mania.Replays public ManiaReplayFrame(double time, params ManiaAction[] actions) : base(time) { - Actions.AddRange(actions); + if (actions.Length > 0) + Actions.AddRange(actions); } public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame lastFrame = null) From e17cd9e964e5eaeded62b3b2a4584a813bec5470 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Sep 2019 16:14:31 +0900 Subject: [PATCH 2776/2854] Reduce length of tests --- osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs index 883aa9fc36..f94071a7a9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected override void AddCheckSteps() { AddUntilStep("score above zero", () => ((ScoreAccessiblePlayer)Player).ScoreProcessor.TotalScore.Value > 0); - AddUntilStep("key counter counted keys", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 5)); + AddUntilStep("key counter counted keys", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2)); AddStep("rewind", () => track.Seek(-10000)); AddUntilStep("key counter reset", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0)); } From 61b396f235bedbd8306a26f84cbf5472df9f92d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Sep 2019 17:09:43 +0900 Subject: [PATCH 2777/2854] Remove redundant length check --- osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index 72c7eb60e0..70ba5cd938 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -21,8 +21,7 @@ namespace osu.Game.Rulesets.Mania.Replays public ManiaReplayFrame(double time, params ManiaAction[] actions) : base(time) { - if (actions.Length > 0) - Actions.AddRange(actions); + Actions.AddRange(actions); } public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame lastFrame = null) From cfdac956c2b28acd95829b6cac682d803e9fea67 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Sep 2019 20:04:49 +0900 Subject: [PATCH 2778/2854] Fix issues with colour and skin application --- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 23 +++++++++++-------- .../Objects/Drawables/DrawableHitCircle.cs | 8 +++---- .../Objects/Drawables/DrawableSlider.cs | 4 ++-- .../Objects/Drawables/DrawableHitObject.cs | 15 +++++++++++- 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index 7c75bd608e..10a6334479 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -5,12 +5,12 @@ using System; using System.Linq; using osu.Framework.Bindables; using System.Collections.Generic; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { @@ -22,7 +22,8 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override string Description => "Put your faith in the approach circles..."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModeObjectScaleTween) }; + + public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn) }; private Bindable increaseFirstObjectVisibility = new Bindable(); public void ReadFromConfig(OsuConfigManager config) @@ -38,26 +39,28 @@ namespace osu.Game.Rulesets.Osu.Mods protected void ApplyTraceableState(DrawableHitObject drawable, ArmedState state) { - if (!(drawable is DrawableOsuHitObject d)) + if (!(drawable is DrawableOsuHitObject drawableOsu)) return; - var h = d.HitObject; + var h = drawableOsu.HitObject; switch (drawable) { case DrawableHitCircle circle: // we only want to see the approach circle using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) - { - circle.ApproachCircle.Show(); - } + circle.CirclePiece.Hide(); break; case DrawableSlider slider: - ApplyTraceableState(slider.HeadCircle, state); - slider.Body.AccentColour = Color4.Transparent; - slider.Body.BorderColour = slider.HeadCircle.AccentColour.Value; + slider.AccentColour.BindValueChanged(_ => + { + //will trigger on skin change. + slider.Body.AccentColour = slider.AccentColour.Value.Opacity(0); + slider.Body.BorderColour = slider.AccentColour.Value; + }, true); + break; case DrawableSpinner spinner: diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 83646c561d..c90f230f93 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach { - public ApproachCircle ApproachCircle; + public ApproachCircle ApproachCircle { get; } private readonly IBindable positionBindable = new Bindable(); private readonly IBindable stackHeightBindable = new Bindable(); @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly HitArea hitArea; - private readonly SkinnableDrawable mainContent; + public SkinnableDrawable CirclePiece { get; } public DrawableHitCircle(HitCircle h) : base(h) @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables return true; }, }, - mainContent = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.HitCircle), _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)), + CirclePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.HitCircle), _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)), ApproachCircle = new ApproachCircle { Alpha = 0, @@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateInitialTransforms(); - mainContent.FadeInFromZero(HitObject.TimeFadeIn); + CirclePiece.FadeInFromZero(HitObject.TimeFadeIn); ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt)); ApproachCircle.ScaleTo(1f, HitObject.TimePreempt); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 08b43b0345..643a0f7336 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -163,9 +163,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private float sliderPathRadius; - protected override void SkinChanged(ISkinSource skin, bool allowFallback) + protected override void ApplySkin(ISkinSource skin, bool allowFallback) { - base.SkinChanged(skin, allowFallback); + base.ApplySkin(skin, allowFallback); Body.BorderSize = skin.GetConfig(OsuSkinConfiguration.SliderBorderSize)?.Value ?? SliderBody.DEFAULT_BORDER_SIZE; sliderPathRadius = skin.GetConfig(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 00b57f7249..9a7f1e8522 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -240,7 +240,7 @@ namespace osu.Game.Rulesets.Objects.Drawables #endregion - protected override void SkinChanged(ISkinSource skin, bool allowFallback) + protected sealed override void SkinChanged(ISkinSource skin, bool allowFallback) { base.SkinChanged(skin, allowFallback); @@ -250,6 +250,19 @@ namespace osu.Game.Rulesets.Objects.Drawables AccentColour.Value = comboColours?.Count > 0 ? comboColours[combo.ComboIndex % comboColours.Count] : Color4.White; } + + ApplySkin(skin, allowFallback); + + updateState(State.Value, true); + } + + /// + /// Called when a change is made to the skin. + /// + /// The new skin. + /// Whether fallback to default skin should be allowed if the custom skin is missing this resource. + protected virtual void ApplySkin(ISkinSource skin, bool allowFallback) + { } /// From aa1a6256431f0e6ac5a6cd793d3d57112e346faf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Sep 2019 20:07:44 +0900 Subject: [PATCH 2779/2854] Add back incompatibility marker --- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 3 +-- osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index 10a6334479..7e20feba02 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => "Put your faith in the approach circles..."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn), typeof(OsuModeObjectScaleTween) }; private Bindable increaseFirstObjectVisibility = new Bindable(); public void ReadFromConfig(OsuConfigManager config) @@ -65,7 +65,6 @@ namespace osu.Game.Rulesets.Osu.Mods case DrawableSpinner spinner: spinner.Disc.Hide(); - //spinner.Ticks.Hide(); // do they contribute to the theme? debatable spinner.Background.Hide(); break; } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs b/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs index e926ade41b..923278f484 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Mods private Bindable increaseFirstObjectVisibility = new Bindable(); - public override Type[] IncompatibleMods => new[] { typeof(OsuModSpinIn) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModSpinIn), typeof(OsuModTraceable) }; public void ReadFromConfig(OsuConfigManager config) { From 5901a915e7850fb064cde851cbb5d5a4d8af193b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Sep 2019 20:19:57 +0900 Subject: [PATCH 2780/2854] Always update drawable hitobject state on skin change --- .../Objects/Drawables/DrawableSlider.cs | 4 ++-- .../Objects/Drawables/DrawableHitObject.cs | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 08b43b0345..643a0f7336 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -163,9 +163,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private float sliderPathRadius; - protected override void SkinChanged(ISkinSource skin, bool allowFallback) + protected override void ApplySkin(ISkinSource skin, bool allowFallback) { - base.SkinChanged(skin, allowFallback); + base.ApplySkin(skin, allowFallback); Body.BorderSize = skin.GetConfig(OsuSkinConfiguration.SliderBorderSize)?.Value ?? SliderBody.DEFAULT_BORDER_SIZE; sliderPathRadius = skin.GetConfig(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 00b57f7249..9a7f1e8522 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -240,7 +240,7 @@ namespace osu.Game.Rulesets.Objects.Drawables #endregion - protected override void SkinChanged(ISkinSource skin, bool allowFallback) + protected sealed override void SkinChanged(ISkinSource skin, bool allowFallback) { base.SkinChanged(skin, allowFallback); @@ -250,6 +250,19 @@ namespace osu.Game.Rulesets.Objects.Drawables AccentColour.Value = comboColours?.Count > 0 ? comboColours[combo.ComboIndex % comboColours.Count] : Color4.White; } + + ApplySkin(skin, allowFallback); + + updateState(State.Value, true); + } + + /// + /// Called when a change is made to the skin. + /// + /// The new skin. + /// Whether fallback to default skin should be allowed if the custom skin is missing this resource. + protected virtual void ApplySkin(ISkinSource skin, bool allowFallback) + { } /// From 646a64792a8a7be0f00b202b545cd4421d6ffce1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2019 14:36:31 +0000 Subject: [PATCH 2781/2854] Bump ppy.osu.Framework.Android from 2019.911.0 to 2019.918.0 Bumps [ppy.osu.Framework.Android](https://github.com/ppy/osu-framework) from 2019.911.0 to 2019.918.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.911.0...2019.918.0) Signed-off-by: dependabot-preview[bot] --- osu.Android.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android.props b/osu.Android.props index 4167d07698..85e8cb9ceb 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + From 1d7377df65c29f90712bb9a97630acd05b9bab3a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2019 14:51:01 +0000 Subject: [PATCH 2782/2854] Bump ppy.osu.Framework from 2019.911.0 to 2019.918.0 Bumps [ppy.osu.Framework](https://github.com/ppy/osu-framework) from 2019.911.0 to 2019.918.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.911.0...2019.918.0) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5703293caf..a733a0e7f9 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 683dccf3ae..f70853d70b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -118,7 +118,7 @@ - + From 5dc5181c9014f8f75ab32b6b17d16de5092e3e2c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2019 15:27:39 +0000 Subject: [PATCH 2783/2854] Bump ppy.osu.Framework.iOS from 2019.911.0 to 2019.918.0 Bumps [ppy.osu.Framework.iOS](https://github.com/ppy/osu-framework) from 2019.911.0 to 2019.918.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.911.0...2019.918.0) Signed-off-by: dependabot-preview[bot] --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index f70853d70b..4bfa1ebcd0 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -119,7 +119,7 @@ - + From 1150e9fdfbc14254816c5f26b978d6104decf147 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 01:45:42 +0900 Subject: [PATCH 2784/2854] Bring other mods up-to-date --- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 1 + osu.Game/Rulesets/Mods/ModAutoplay.cs | 2 ++ osu.Game/Rulesets/Mods/ModBlockFail.cs | 2 ++ 3 files changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index ca72f18e9c..65d7acc911 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -25,6 +25,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail), typeof(ModAutoplay) }; public bool AllowFail => false; + public bool RestartOnFail => false; private OsuInputManager inputManager; diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index f0dffa60ce..070a10b1c8 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -26,8 +26,10 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.Automation; public override string Description => "Watch a perfect automated play through the song."; public override double ScoreMultiplier => 1; + public bool AllowFail => false; public bool RestartOnFail => false; + public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) }; public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0; diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs index 26efc3932d..55c074bde2 100644 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Mods /// public bool AllowFail => false; + public virtual bool RestartOnFail => false; + public void ReadFromConfig(OsuConfigManager config) { showHealthBar = config.GetBindable(OsuSetting.ShowHealthDisplayWhenCantFail); From 2fcc8c2d720d60bbd6431e720628f056f41594a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 01:45:59 +0900 Subject: [PATCH 2785/2854] Simplify implementation, play fail animation during restart --- osu.Game/Screens/Play/Player.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ca17e8a7bc..dac5561a45 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -360,7 +360,9 @@ namespace osu.Game.Screens.Play private bool onFail() { - if (Mods.Value.OfType().Any(m => !m.AllowFail)) + var failOverrideMods = Mods.Value.OfType(); + + if (failOverrideMods.Any(m => !m.AllowFail)) return false; HasFailed = true; @@ -371,13 +373,10 @@ namespace osu.Game.Screens.Play if (PauseOverlay.State.Value == Visibility.Visible) PauseOverlay.Hide(); - if (Beatmap.Value.Mods.Value.OfType().Any(m => m.RestartOnFail)) - { - Restart(); - return true; - } - failAnimation.Start(); + if (failOverrideMods.Any(m => m.RestartOnFail)) + Restart(); + return true; } From 2296ea75d7e64183b6b625810da21c3ef5b46458 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 02:03:30 +0900 Subject: [PATCH 2786/2854] Add reference to xmldoc Co-Authored-By: Salman Ahmed --- osu.Game/Rulesets/Mods/IApplicableFailOverride.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs index ae5903f085..120bfc9a23 100644 --- a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs +++ b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods bool AllowFail { get; } /// - /// Whether we want to restart on fail. Only used if AllowFail is true. + /// Whether we want to restart on fail. Only used if is true. /// bool RestartOnFail { get; } } From 92556db9cd8bae5df028a295338d8ad1065e2a55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 02:37:35 +0900 Subject: [PATCH 2787/2854] Add query-based filter modes to song select search field --- osu.Game/Beatmaps/BeatmapManager.cs | 1 + .../Select/Carousel/CarouselBeatmap.cs | 24 +++- osu.Game/Screens/Select/FilterControl.cs | 118 ++++++++++++++++-- osu.Game/Screens/Select/FilterCriteria.cs | 33 ++++- 4 files changed, 163 insertions(+), 13 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index b9ed3664ef..02d7b2d98f 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -301,6 +301,7 @@ namespace osu.Game.Beatmaps var ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); beatmap.BeatmapInfo.Ruleset = ruleset; + // TODO: this should be done in a better place once we actually need to dynamically update it. beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0; beatmap.BeatmapInfo.Length = calculateLength(beatmap); diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 712ab7b571..60e556a261 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -24,12 +24,26 @@ namespace osu.Game.Screens.Select.Carousel { base.Filter(criteria); - bool match = criteria.Ruleset == null || Beatmap.RulesetID == criteria.Ruleset.ID || (Beatmap.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps); + bool match = + criteria.Ruleset == null || + Beatmap.RulesetID == criteria.Ruleset.ID || + (Beatmap.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps); - foreach (var criteriaTerm in criteria.SearchTerms) - match &= - Beatmap.Metadata.SearchableTerms.Any(term => term.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0) || - Beatmap.Version.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0; + match &= criteria.StarDifficulty.IsInRange(Beatmap.StarDifficulty); + match &= criteria.ApproachRate.IsInRange(Beatmap.BaseDifficulty.ApproachRate); + match &= criteria.DrainRate.IsInRange(Beatmap.BaseDifficulty.DrainRate); + match &= criteria.CircleSize.IsInRange(Beatmap.BaseDifficulty.CircleSize); + match &= criteria.Length.IsInRange(Beatmap.Length); + match &= criteria.BPM.IsInRange(Beatmap.BPM); + + match &= !criteria.BeatDivisor.HasValue || criteria.BeatDivisor == Beatmap.BeatDivisor; + match &= !criteria.OnlineStatus.HasValue || criteria.OnlineStatus == Beatmap.Status; + + if (match) + foreach (var criteriaTerm in criteria.SearchTerms) + match &= + Beatmap.Metadata.SearchableTerms.Any(term => term.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0) || + Beatmap.Version.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0; Filtered.Value = !match; } diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index ed74b01fc9..01e7ed9383 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -16,6 +16,8 @@ using Container = osu.Framework.Graphics.Containers.Container; using osu.Framework.Graphics.Shapes; using osu.Game.Configuration; using osu.Game.Rulesets; +using System.Text.RegularExpressions; +using osu.Game.Beatmaps; namespace osu.Game.Screens.Select { @@ -33,14 +35,24 @@ namespace osu.Game.Screens.Select private Bindable groupMode; - public FilterCriteria CreateCriteria() => new FilterCriteria + public FilterCriteria CreateCriteria() { - Group = groupMode.Value, - Sort = sortMode.Value, - SearchText = searchTextBox.Text, - AllowConvertedBeatmaps = showConverted.Value, - Ruleset = ruleset.Value - }; + var query = searchTextBox.Text; + + var criteria = new FilterCriteria + { + Group = groupMode.Value, + Sort = sortMode.Value, + AllowConvertedBeatmaps = showConverted.Value, + Ruleset = ruleset.Value + }; + + applyQueries(criteria, ref query); + + criteria.SearchText = query; + + return criteria; + } public Action Exit; @@ -169,5 +181,97 @@ namespace osu.Game.Screens.Select } private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria()); + + private static readonly Regex query_syntax_regex = new Regex( + @"\b(?stars|ar|dr|cs|divisor|length|objects|bpm|status)(?[:><]+)(?\S*)", + RegexOptions.Compiled | RegexOptions.IgnoreCase); + + private void applyQueries(FilterCriteria criteria, ref string query) + { + foreach (Match match in query_syntax_regex.Matches(query)) + { + var key = match.Groups["key"].Value.ToLower(); + var op = match.Groups["op"].Value; + var value = match.Groups["value"].Value; + + switch (key) + { + case "stars" when double.TryParse(value, out var stars): + updateCriteriaRange(ref criteria.StarDifficulty, op, stars, 0.5); + break; + + case "ar" when double.TryParse(value, out var ar): + updateCriteriaRange(ref criteria.ApproachRate, op, ar, 0.3); + break; + + case "dr" when double.TryParse(value, out var dr): + updateCriteriaRange(ref criteria.DrainRate, op, dr, 0.3); + break; + + case "cs" when double.TryParse(value, out var cs): + updateCriteriaRange(ref criteria.CircleSize, op, cs, 0.3); + break; + + case "bpm" when double.TryParse(value, out var bpm): + updateCriteriaRange(ref criteria.BPM, op, bpm, 0.3); + break; + + case "length" when double.TryParse(value.TrimEnd('m', 's', 'h'), out var length): + var scale = + value.EndsWith("ms") ? 1 : + value.EndsWith("s") ? 1000 : + value.EndsWith("m") ? 60000 : + value.EndsWith("h") ? 3600000 : 1000; + + updateCriteriaRange(ref criteria.Length, op, length * scale, scale / 2.0); + break; + + case "divisor" when op == ":" && int.TryParse(value, out var divisor): + criteria.BeatDivisor = divisor; + break; + + case "status" when op == ":" && Enum.TryParse(value, ignoreCase: true, out var statusValue): + criteria.OnlineStatus = statusValue; + break; + } + + query = query.Remove(match.Index, match.Length); + } + } + + private void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, double value, double equalityToleration = 0) + { + switch (op) + { + default: + return; + + case ":": + range.IsInclusive = true; + range.Min = value - equalityToleration; + range.Max = value + equalityToleration; + break; + + case ">": + range.IsInclusive = false; + range.Min = value; + break; + + case ">:": + range.IsInclusive = true; + range.Min = value; + break; + + case "<": + range.IsInclusive = false; + range.Max = value; + break; + + case "<:": + range.IsInclusive = true; + range.Max = value; + break; + } + } } } diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 140010ff54..84d63c16e0 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Screens.Select.Filter; @@ -13,6 +14,17 @@ namespace osu.Game.Screens.Select public GroupMode Group; public SortMode Sort; + public OptionalRange StarDifficulty; + public OptionalRange ApproachRate; + public OptionalRange DrainRate; + public OptionalRange CircleSize; + public OptionalRange Length; + public OptionalRange BPM; + + public int? BeatDivisor; + + public BeatmapSetOnlineStatus? OnlineStatus; + public string[] SearchTerms = Array.Empty(); public RulesetInfo Ruleset; @@ -26,8 +38,27 @@ namespace osu.Game.Screens.Select set { searchText = value; - SearchTerms = searchText.Split(',', ' ', '!').Where(s => !string.IsNullOrEmpty(s)).ToArray(); + SearchTerms = searchText.Split(new[] { ',', ' ', '!' }, StringSplitOptions.RemoveEmptyEntries).ToArray(); } } + + public struct OptionalRange : IEquatable + { + public bool IsInRange(double value) + { + if (Min.HasValue && (IsInclusive ? value < Min.Value : value <= Min.Value)) + return false; + if (Max.HasValue && (IsInclusive ? value > Max.Value : value >= Max.Value)) + return false; + + return true; + } + + public double? Min; + public double? Max; + public bool IsInclusive; + + public bool Equals(OptionalRange range) => Min == range.Min && Max == range.Max; + } } } From ecd721e8c5be287068bc1f51d2f85b4a44c225b3 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 18 Sep 2019 22:49:28 +0300 Subject: [PATCH 2788/2854] Add bypass fail property to Player --- osu.Game/Screens/Play/Player.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 309f4837e5..fbaab61b42 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -86,6 +86,11 @@ namespace osu.Game.Screens.Play [Cached(Type = typeof(IBindable>))] protected new readonly Bindable> Mods = new Bindable>(Array.Empty()); + /// + /// Whether to block the player from failing. + /// + protected virtual bool BypassFail => false; + private readonly bool allowPause; private readonly bool showResults; @@ -360,7 +365,7 @@ namespace osu.Game.Screens.Play private bool onFail() { - if (Mods.Value.OfType().Any(m => !m.AllowFail)) + if (Mods.Value.OfType().Any(m => !m.AllowFail) || BypassFail) return false; HasFailed = true; From 775b90e06643da66c6ebc72218e2163bbf15c8de Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 18 Sep 2019 22:49:48 +0300 Subject: [PATCH 2789/2854] Bypass fail on replays --- osu.Game/Screens/Play/ReplayPlayer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index a9c0ee3a15..da9f558c16 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -9,6 +9,9 @@ namespace osu.Game.Screens.Play { private readonly Score score; + // Block replays from failing. (see https://github.com/ppy/osu/issues/6108) + protected override bool BypassFail => true; + public ReplayPlayer(Score score, bool allowPause = true, bool showResults = true) : base(allowPause, showResults) { From 871adb16e0ba068e305ca79e342623bfff2b6e34 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 18 Sep 2019 22:51:03 +0300 Subject: [PATCH 2790/2854] Add asserts for fail bypassing --- osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs index 3fbce9d43c..d89304cf44 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs @@ -26,12 +26,14 @@ namespace osu.Game.Tests.Visual.Gameplay { AddUntilStep("score above zero", () => ((ScoreAccessibleReplayPlayer)Player).ScoreProcessor.TotalScore.Value > 0); AddUntilStep("key counter counted keys", () => ((ScoreAccessibleReplayPlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 0)); + AddAssert("cannot fail", () => ((ScoreAccessibleReplayPlayer)Player).BypassFail); } private class ScoreAccessibleReplayPlayer : ReplayPlayer { public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; public new HUDOverlay HUDOverlay => base.HUDOverlay; + public new bool BypassFail => base.BypassFail; protected override bool PauseOnFocusLost => false; From ea6318ed73c22607c2aace18d7fa9cc7a659ebd1 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 18 Sep 2019 23:17:24 +0300 Subject: [PATCH 2791/2854] Fix failing test --- .../Visual/Gameplay/TestSceneFailJudgement.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs index d57ec44f39..e6d302a5ca 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs @@ -17,9 +17,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected override Player CreatePlayer(Ruleset ruleset) { Mods.Value = Array.Empty(); - - var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Array.Empty()); - return new FailPlayer(ruleset.GetAutoplayMod().CreateReplayScore(beatmap)); + return new FailPlayer(); } protected override void AddCheckSteps() @@ -29,16 +27,12 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("total judgements == 1", () => ((FailPlayer)Player).ScoreProcessor.JudgedHits == 1); } - private class FailPlayer : ReplayPlayer + private class FailPlayer : TestPlayer { - public new DrawableRuleset DrawableRuleset => base.DrawableRuleset; - public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; - protected override bool PauseOnFocusLost => false; - - public FailPlayer(Score score) - : base(score, false, false) + public FailPlayer() + : base(false, false) { } From 3efcf0493c45c95b5a648bbcce99ab4af831ac7d Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 18 Sep 2019 23:28:48 +0300 Subject: [PATCH 2792/2854] Remove redundant using directive --- osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs index e6d302a5ca..cca6301b02 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs @@ -6,8 +6,6 @@ using System.Linq; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; -using osu.Game.Scoring; using osu.Game.Screens.Play; namespace osu.Game.Tests.Visual.Gameplay From 3fa1b53b2ae3d670bc7f1f2a6f8b55cb57659b93 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 12:39:15 +0900 Subject: [PATCH 2793/2854] Add back combo colours for osu!classic --- osu.Game/Skinning/DefaultLegacySkin.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 98f158c725..4b6eea6b6e 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -13,6 +13,13 @@ namespace osu.Game.Skinning : base(Info, storage, audioManager, string.Empty) { Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); + Configuration.ComboColours.AddRange(new[] + { + new Color4(255, 192, 0, 255), + new Color4(0, 202, 0, 255), + new Color4(18, 124, 255, 255), + new Color4(242, 24, 57, 255), + }); } public static SkinInfo Info { get; } = new SkinInfo From e5509cd3908175a866809317576b0d896b53afe8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 13:19:48 +0900 Subject: [PATCH 2794/2854] Rename test --- ...stSceneLeaderboard.cs => TestSceneBeatmapLeaderboard.cs} | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) rename osu.Game.Tests/Visual/SongSelect/{TestSceneLeaderboard.cs => TestSceneBeatmapLeaderboard.cs} (98%) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs similarity index 98% rename from osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs rename to osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 186f27a8b2..cb4cd63266 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Online.Leaderboards; @@ -14,8 +13,7 @@ using osuTK; namespace osu.Game.Tests.Visual.SongSelect { - [Description("PlaySongSelect leaderboard")] - public class TestSceneLeaderboard : OsuTestScene + public class TestSceneBeatmapLeaderboard : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { @@ -26,7 +24,7 @@ namespace osu.Game.Tests.Visual.SongSelect private readonly FailableLeaderboard leaderboard; - public TestSceneLeaderboard() + public TestSceneBeatmapLeaderboard() { Add(leaderboard = new FailableLeaderboard { From 0644443979bde65a602504c34eeecbbfcff332f9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 13:51:50 +0900 Subject: [PATCH 2795/2854] Use resolved attribute for music controller --- osu.Game/Overlays/Music/PlaylistList.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index 636945edd2..cd9c91f3af 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -43,8 +43,6 @@ namespace osu.Game.Overlays.Music private class ItemsScrollContainer : OsuScrollContainer { - private IBindableList beatmaps; - public Action Selected; private readonly SearchContainer search; @@ -52,7 +50,10 @@ namespace osu.Game.Overlays.Music private readonly IBindable beatmapBacking = new Bindable(); - private MusicController musicController; + private IBindableList beatmaps; + + [Resolved] + private MusicController musicController { get; set; } public ItemsScrollContainer() { @@ -75,12 +76,9 @@ namespace osu.Game.Overlays.Music } [BackgroundDependencyLoader] - private void load(MusicController musicController, IBindable beatmap) + private void load(IBindable beatmap) { - this.musicController = musicController; - beatmaps = musicController.BeatmapSets.GetBoundCopy(); - beatmaps.ItemsAdded += i => i.ForEach(addBeatmapSet); beatmaps.ItemsRemoved += i => i.ForEach(removeBeatmapSet); beatmaps.ForEach(addBeatmapSet); From 4b97327b37ad910b93bb3a835a19201369dc6730 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 13:53:52 +0900 Subject: [PATCH 2796/2854] Cleanup draggedItem usages and make them more safe --- osu.Game/Overlays/Music/PlaylistList.cs | 50 ++++++++++++------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index cd9c91f3af..5b528c5ab2 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -87,25 +87,24 @@ namespace osu.Game.Overlays.Music beatmapBacking.ValueChanged += _ => updateSelectedSet(); } - private void addBeatmapSet(BeatmapSetInfo obj) => Schedule(() => - { - if (obj == draggedItem?.BeatmapSetInfo) - { - draggedItem = null; - return; - } - - items.Insert(items.Count - 1, new PlaylistItem(obj) { OnSelect = set => Selected?.Invoke(set) }); - }); - - private void removeBeatmapSet(BeatmapSetInfo obj) => Schedule(() => + private void addBeatmapSet(BeatmapSetInfo obj) { if (obj == draggedItem?.BeatmapSetInfo) return; - var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == obj.ID); - if (itemToRemove != null) - items.Remove(itemToRemove); - }); + Schedule(() => items.Insert(items.Count - 1, new PlaylistItem(obj) { OnSelect = set => Selected?.Invoke(set) })); + } + + private void removeBeatmapSet(BeatmapSetInfo obj) + { + if (obj == draggedItem?.BeatmapSetInfo) return; + + Schedule(() => + { + var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == obj.ID); + if (itemToRemove != null) + items.Remove(itemToRemove); + }); + } private void updateSelectedSet() { @@ -146,19 +145,16 @@ namespace osu.Game.Overlays.Music { nativeDragPosition = e.ScreenSpaceMousePosition; - if (draggedItem != null) - { - if (dragDestination != null) - { - // draggedItem is nulled when the BindableList's add event is received so we can quietly ignore the callbacks. - musicController.ChangeBeatmapSetPosition(draggedItem.BeatmapSetInfo, dragDestination.Value); - dragDestination = null; - } + if (draggedItem == null) + return base.OnDragEnd(e); - return true; - } + if (dragDestination != null) + musicController.ChangeBeatmapSetPosition(draggedItem.BeatmapSetInfo, dragDestination.Value); - return base.OnDragEnd(e); + draggedItem = null; + dragDestination = null; + + return true; } protected override void Update() From 9de0bcae1eef5ff0beaa2fb70f26d66a62cb725e Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 19 Sep 2019 07:58:54 +0300 Subject: [PATCH 2797/2854] Check for blocking fail mods by default --- osu.Game/Screens/Play/Player.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index fbaab61b42..e8134253f5 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -87,9 +87,11 @@ namespace osu.Game.Screens.Play protected new readonly Bindable> Mods = new Bindable>(Array.Empty()); /// - /// Whether to block the player from failing. + /// Whether failing should be allowed. + /// + /// By default, this checks whether any selected mod disallows failing. /// - protected virtual bool BypassFail => false; + protected virtual bool AllowFail => !Mods.Value.OfType().Any(m => !m.AllowFail); private readonly bool allowPause; private readonly bool showResults; @@ -365,7 +367,7 @@ namespace osu.Game.Screens.Play private bool onFail() { - if (Mods.Value.OfType().Any(m => !m.AllowFail) || BypassFail) + if (!AllowFail) return false; HasFailed = true; From e793854735a812acb0d4e01ddbff87c5d20922d3 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 19 Sep 2019 08:00:41 +0300 Subject: [PATCH 2798/2854] Invert BypassFail usage --- osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs | 4 ++-- osu.Game/Screens/Play/ReplayPlayer.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs index d89304cf44..36335bc54a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs @@ -26,14 +26,14 @@ namespace osu.Game.Tests.Visual.Gameplay { AddUntilStep("score above zero", () => ((ScoreAccessibleReplayPlayer)Player).ScoreProcessor.TotalScore.Value > 0); AddUntilStep("key counter counted keys", () => ((ScoreAccessibleReplayPlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 0)); - AddAssert("cannot fail", () => ((ScoreAccessibleReplayPlayer)Player).BypassFail); + AddAssert("cannot fail", () => !((ScoreAccessibleReplayPlayer)Player).AllowFail); } private class ScoreAccessibleReplayPlayer : ReplayPlayer { public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; public new HUDOverlay HUDOverlay => base.HUDOverlay; - public new bool BypassFail => base.BypassFail; + public new bool AllowFail => base.AllowFail; protected override bool PauseOnFocusLost => false; diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index da9f558c16..b040549efc 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -9,8 +9,8 @@ namespace osu.Game.Screens.Play { private readonly Score score; - // Block replays from failing. (see https://github.com/ppy/osu/issues/6108) - protected override bool BypassFail => true; + // Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108) + protected override bool AllowFail => false; public ReplayPlayer(Score score, bool allowPause = true, bool showResults = true) : base(allowPause, showResults) From 177a789d792b55d69a232c56d82a2813ea6f0159 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 14:04:51 +0900 Subject: [PATCH 2799/2854] Add setting to adjust hold-to-confirm activation time --- osu.Game/Configuration/OsuConfigManager.cs | 5 ++++- .../Containers/HoldToConfirmContainer.cs | 18 ++++++++---------- osu.Game/Overlays/HoldToConfirmOverlay.cs | 2 +- .../Sections/Graphics/UserInterfaceSettings.cs | 15 ++++++++++++++- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index e26021d930..62590a0a8f 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -112,6 +112,8 @@ namespace osu.Game.Configuration Set(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f); + Set(OsuSetting.UIHoldActivationDelay, 200, 0, 500); + Set(OsuSetting.IntroSequence, IntroSequence.Triangles); } @@ -180,6 +182,7 @@ namespace osu.Game.Configuration ScalingSizeX, ScalingSizeY, UIScale, - IntroSequence + IntroSequence, + UIHoldActivationDelay } } diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs index 773265d19b..a345fb554f 100644 --- a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs +++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs @@ -2,9 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Configuration; namespace osu.Game.Graphics.Containers { @@ -12,11 +14,8 @@ namespace osu.Game.Graphics.Containers { public Action Action; - private const int default_activation_delay = 200; private const int fadeout_delay = 200; - private readonly double activationDelay; - private bool fired; private bool confirming; @@ -27,13 +26,12 @@ namespace osu.Game.Graphics.Containers public Bindable Progress = new BindableDouble(); - /// - /// Create a new instance. - /// - /// The time requried before an action is confirmed. - protected HoldToConfirmContainer(double activationDelay = default_activation_delay) + private Bindable holdActivationDelay; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) { - this.activationDelay = activationDelay; + holdActivationDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); } protected void BeginConfirm() @@ -42,7 +40,7 @@ namespace osu.Game.Graphics.Containers confirming = true; - this.TransformBindableTo(Progress, 1, activationDelay * (1 - Progress.Value), Easing.Out).OnComplete(_ => Confirm()); + this.TransformBindableTo(Progress, 1, holdActivationDelay.Value * (1 - Progress.Value), Easing.Out).OnComplete(_ => Confirm()); } protected virtual void Confirm() diff --git a/osu.Game/Overlays/HoldToConfirmOverlay.cs b/osu.Game/Overlays/HoldToConfirmOverlay.cs index fdc6f096bc..eb325d8dd3 100644 --- a/osu.Game/Overlays/HoldToConfirmOverlay.cs +++ b/osu.Game/Overlays/HoldToConfirmOverlay.cs @@ -51,7 +51,7 @@ namespace osu.Game.Overlays protected override void Dispose(bool isDisposing) { - audio.Tracks.RemoveAdjustment(AdjustableProperty.Volume, audioVolume); + audio?.Tracks.RemoveAdjustment(AdjustableProperty.Volume, audioVolume); base.Dispose(isDisposing); } } diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs index dd822fedb6..a6956b7d9a 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Settings.Sections.Graphics { @@ -13,7 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - Children = new[] + Children = new Drawable[] { new SettingsCheckbox { @@ -25,7 +27,18 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics LabelText = "Parallax", Bindable = config.GetBindable(OsuSetting.MenuParallax) }, + new SettingsSlider + { + LabelText = "Hold-to-confirm activation time", + Bindable = config.GetBindable(OsuSetting.UIHoldActivationDelay), + KeyboardStep = 50 + }, }; } + + private class TimeSlider : OsuSliderBar + { + public override string TooltipText => Current.Value.ToString("N0") + "ms"; + } } } From 762adb783ae359a5d3f1d5bebeabd1ec3ef79be8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 14:15:06 +0900 Subject: [PATCH 2800/2854] Fix duplicate invocation of updateState on load complete --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 9a7f1e8522..b94de0df89 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -253,7 +253,8 @@ namespace osu.Game.Rulesets.Objects.Drawables ApplySkin(skin, allowFallback); - updateState(State.Value, true); + if (IsLoaded) + updateState(State.Value, true); } /// From a7b6895d4c9c79119922792b6d688faa48ee2166 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 14:26:15 +0900 Subject: [PATCH 2801/2854] Revert changes to BeatmapDetailArea --- .../SongSelect/TestSceneBeatmapDetailArea.cs | 1 - osu.Game/Screens/Select/BeatmapDetailArea.cs | 77 ++++++++----------- osu.Game/Screens/Select/SongSelect.cs | 1 - 3 files changed, 30 insertions(+), 49 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs index ee47888d99..ed9e01a67e 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs @@ -163,7 +163,6 @@ namespace osu.Game.Tests.Visual.SongSelect })); AddStep("null beatmap", () => detailsArea.Beatmap = null); - AddStep("Toggle top score visibility", () => detailsArea.TopScore.ToggleVisibility()); } } } diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs index 648dae68bc..5348de68d6 100644 --- a/osu.Game/Screens/Select/BeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs @@ -1,21 +1,23 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; -using osu.Game.Screens.Select.Details; using osu.Game.Screens.Select.Leaderboards; namespace osu.Game.Screens.Select { public class BeatmapDetailArea : Container { - private const float padding = 10; + private const float details_padding = 10; + + private readonly Container content; + protected override Container Content => content; public readonly BeatmapDetails Details; public readonly BeatmapLeaderboard Leaderboard; - public readonly UserTopScoreContainer TopScore; private WorkingBeatmap beatmap; @@ -27,13 +29,12 @@ namespace osu.Game.Screens.Select beatmap = value; Details.Beatmap = beatmap?.BeatmapInfo; Leaderboard.Beatmap = beatmap is DummyWorkingBeatmap ? null : beatmap?.BeatmapInfo; - TopScore.Hide(); } } public BeatmapDetailArea() { - Children = new Drawable[] + AddRangeInternal(new Drawable[] { new BeatmapDetailAreaTabControl { @@ -57,51 +58,33 @@ namespace osu.Game.Screens.Select } }, }, - new Container + content = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = BeatmapDetailAreaTabControl.HEIGHT }, + }, + }); + + AddRange(new Drawable[] + { + Details = new BeatmapDetails + { + RelativeSizeAxes = Axes.X, + Alpha = 0, + Margin = new MarginPadding { Top = details_padding }, + }, + Leaderboard = new BeatmapLeaderboard { - Padding = new MarginPadding { Top = BeatmapDetailAreaTabControl.HEIGHT, Bottom = padding }, RelativeSizeAxes = Axes.Both, - Child = new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] - { - new Dimension(GridSizeMode.Distributed), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - Details = new BeatmapDetails - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - Padding = new MarginPadding { Top = padding }, - }, - Leaderboard = new BeatmapLeaderboard - { - RelativeSizeAxes = Axes.Both, - } - } - } - }, - new Drawable[] - { - TopScore = new UserTopScoreContainer - { - Score = { BindTarget = Leaderboard.TopScore } - } - } - }, - }, } - }; + }); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + Details.Height = Math.Min(DrawHeight - details_padding * 3 - BeatmapDetailAreaTabControl.HEIGHT, 450); } } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 173442384e..7f9804c6a3 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -226,7 +226,6 @@ namespace osu.Game.Screens.Select void displayScore(ScoreInfo score) => this.Push(new SoloResults(score)); BeatmapDetails.Leaderboard.ScoreSelected += displayScore; - BeatmapDetails.TopScore.ScoreSelected += displayScore; } [BackgroundDependencyLoader(true)] From da4d83063e73c4cda5a705fa0500f4153388a029 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 19 Sep 2019 08:31:11 +0300 Subject: [PATCH 2802/2854] Simplify LINQ expression --- osu.Game/Screens/Play/Player.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e8134253f5..30d6cfbf68 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -89,9 +89,9 @@ namespace osu.Game.Screens.Play /// /// Whether failing should be allowed. /// - /// By default, this checks whether any selected mod disallows failing. + /// By default, this checks whether all selected mods allow failing. /// - protected virtual bool AllowFail => !Mods.Value.OfType().Any(m => !m.AllowFail); + protected virtual bool AllowFail => Mods.Value.OfType().All(m => m.AllowFail); private readonly bool allowPause; private readonly bool showResults; From 4967ffd8e557291a261a6b678026a91a2901965f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 14:52:31 +0900 Subject: [PATCH 2803/2854] Move inside leaderboard --- osu.Game/Online/Leaderboards/Leaderboard.cs | 39 ++++++++++++++++--- .../Select/Leaderboards/BeatmapLeaderboard.cs | 12 ++++++ 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 147556b78b..d66a9a7535 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -35,6 +35,10 @@ namespace osu.Game.Online.Leaderboards private bool scoresLoadedOnce; + private readonly Container content; + + protected override Container Content => content; + private IEnumerable scores; public IEnumerable Scores @@ -60,13 +64,13 @@ namespace osu.Game.Online.Leaderboards // ensure placeholder is hidden when displaying scores PlaceholderState = PlaceholderState.Successful; - var sf = CreateScoreFlow(); - sf.ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1)); + var scoreFlow = CreateScoreFlow(); + scoreFlow.ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1)); // schedule because we may not be loaded yet (LoadComponentAsync complains). - showScoresDelegate = Schedule(() => LoadComponentAsync(sf, _ => + showScoresDelegate = Schedule(() => LoadComponentAsync(scoreFlow, _ => { - scrollContainer.Add(scrollFlow = sf); + scrollContainer.Add(scrollFlow = scoreFlow); int i = 0; @@ -164,10 +168,33 @@ namespace osu.Game.Online.Leaderboards { Children = new Drawable[] { - scrollContainer = new OsuScrollContainer + new GridContainer { RelativeSizeAxes = Axes.Both, - ScrollbarVisible = false, + RowDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] + { + scrollContainer = new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + ScrollbarVisible = false, + } + }, + new Drawable[] + { + content = new Container + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }, + } + }, }, loading = new LoadingAnimation(), placeholderContainer = new Container diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 4e4def4911..0eef784279 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -80,6 +80,18 @@ namespace osu.Game.Screens.Select.Leaderboards if (filterMods) UpdateScores(); }; + + TopScore.BindValueChanged(newTopScore); + } + + private void newTopScore(ValueChangedEvent score) + { + Content.Clear(); + + if (score.NewValue != null) + { + + } } protected override bool IsOnlineScope => Scope != BeatmapLeaderboardScope.Local; From c76e27549a6db11e251376d5d15c2a3d710545b9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 14:56:52 +0900 Subject: [PATCH 2804/2854] Remove spacing --- osu.Game/Screens/Play/Player.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 30d6cfbf68..4b234ab296 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -88,7 +88,6 @@ namespace osu.Game.Screens.Play /// /// Whether failing should be allowed. - /// /// By default, this checks whether all selected mods allow failing. /// protected virtual bool AllowFail => Mods.Value.OfType().All(m => m.AllowFail); From 098e89cb66a2896d673c0cb924eecfc92147ff56 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 15:23:33 +0900 Subject: [PATCH 2805/2854] Improve state reset flow --- osu.Game/Online/Leaderboards/Leaderboard.cs | 13 ++++--- .../Select/Details/UserTopScoreContainer.cs | 22 ++++++------ .../Select/Leaderboards/BeatmapLeaderboard.cs | 36 ++++++++++++------- 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index d66a9a7535..83de0635fb 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -120,9 +120,7 @@ namespace osu.Game.Online.Leaderboards { if (value != PlaceholderState.Successful) { - getScoresRequest?.Cancel(); - getScoresRequest = null; - Scores = null; + Reset(); } if (value == placeholderState) @@ -166,7 +164,7 @@ namespace osu.Game.Online.Leaderboards protected Leaderboard() { - Children = new Drawable[] + InternalChildren = new Drawable[] { new GridContainer { @@ -204,6 +202,13 @@ namespace osu.Game.Online.Leaderboards }; } + protected virtual void Reset() + { + getScoresRequest?.Cancel(); + getScoresRequest = null; + Scores = null; + } + private IAPIProvider api; private ScheduledDelegate pendingUpdateScores; diff --git a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs index c10f4d4fd4..959fbb927a 100644 --- a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs @@ -17,9 +17,8 @@ namespace osu.Game.Screens.Select.Details public class UserTopScoreContainer : VisibilityContainer { private const int height = 90; - private const int duration = 800; + private const int duration = 500; - private readonly Container contentContainer; private readonly Container scoreContainer; public Bindable Score = new Bindable(); @@ -38,7 +37,7 @@ namespace osu.Game.Screens.Select.Details Children = new Drawable[] { - contentContainer = new Container + new Container { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, @@ -74,15 +73,12 @@ namespace osu.Game.Screens.Select.Details { var newScore = score.NewValue; - if (newScore == null) - { - Hide(); - return; - } - scoreContainer.Clear(); loadScoreCancellation?.Cancel(); + if (newScore == null) + return; + LoadComponentAsync(new LeaderboardScore(newScore.Score, newScore.Position) { Action = () => ScoreSelected?.Invoke(newScore.Score) @@ -93,12 +89,14 @@ namespace osu.Game.Screens.Select.Details }, (loadScoreCancellation = new CancellationTokenSource()).Token); } - protected override void PopIn() => this.ResizeHeightTo(height, duration / 4f, Easing.OutQuint).OnComplete(_ => contentContainer.FadeIn(duration, Easing.OutQuint)); + protected override void PopIn() + { + this.FadeIn(duration, Easing.OutQuint); + } protected override void PopOut() { - this.ResizeHeightTo(0); - contentContainer.FadeOut(duration / 4f, Easing.OutQuint); + this.FadeOut(duration, Easing.OutQuint); } } } diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 0eef784279..d038049504 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -14,13 +14,12 @@ using osu.Game.Online.Leaderboards; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; +using osu.Game.Screens.Select.Details; namespace osu.Game.Screens.Select.Leaderboards { public class BeatmapLeaderboard : Leaderboard { - public Bindable TopScore = new Bindable(); - public Action ScoreSelected; private BeatmapInfo beatmap; @@ -40,8 +39,25 @@ namespace osu.Game.Screens.Select.Leaderboards } } + public APILegacyUserTopScoreInfo TopScore + { + get => topScoreContainer.Score.Value; + set + { + if (value == null) + topScoreContainer.Hide(); + else + { + topScoreContainer.Show(); + topScoreContainer.Score.Value = value; + } + } + } + private bool filterMods; + private UserTopScoreContainer topScoreContainer; + /// /// Whether to apply the game's currently selected mods as a filter when retrieving scores. /// @@ -81,25 +97,19 @@ namespace osu.Game.Screens.Select.Leaderboards UpdateScores(); }; - TopScore.BindValueChanged(newTopScore); + Content.Add(topScoreContainer = new UserTopScoreContainer()); } - private void newTopScore(ValueChangedEvent score) + protected override void Reset() { - Content.Clear(); - - if (score.NewValue != null) - { - - } + base.Reset(); + TopScore = null; } protected override bool IsOnlineScope => Scope != BeatmapLeaderboardScope.Local; protected override APIRequest FetchScores(Action> scoresCallback) { - TopScore.Value = null; - if (Beatmap == null) { PlaceholderState = PlaceholderState.NoneSelected; @@ -161,7 +171,7 @@ namespace osu.Game.Screens.Select.Leaderboards req.Success += r => { scoresCallback?.Invoke(r.Scores); - TopScore.Value = r.UserScore; + TopScore = r.UserScore; }; return req; From 9b35de9ce17c1fbe85ccabbc4e58bba81bef805f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 15:23:37 +0900 Subject: [PATCH 2806/2854] Update tests --- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index cb4cd63266..bbd874fcd7 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -5,8 +5,12 @@ using System; using System.Collections.Generic; using osu.Framework.Graphics; using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Scoring; +using osu.Game.Screens.Select.Details; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; using osuTK; @@ -20,6 +24,8 @@ namespace osu.Game.Tests.Visual.SongSelect typeof(Placeholder), typeof(MessagePlaceholder), typeof(RetrievalFailurePlaceholder), + typeof(UserTopScoreContainer), + typeof(Leaderboard), }; private readonly FailableLeaderboard leaderboard; @@ -35,6 +41,7 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddStep(@"New Scores", newScores); + AddStep(@"Show personal best", showPersonalBest); AddStep(@"Empty Scores", () => leaderboard.SetRetrievalState(PlaceholderState.NoScores)); AddStep(@"Network failure", () => leaderboard.SetRetrievalState(PlaceholderState.NetworkFailure)); AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter)); @@ -45,6 +52,32 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep($"{status} beatmap", () => showBeatmapWithStatus(status)); } + private void showPersonalBest() + { + leaderboard.TopScore = new APILegacyUserTopScoreInfo + { + Position = 999, + Score = new APILegacyScoreInfo + { + Rank = ScoreRank.XH, + Accuracy = 1, + MaxCombo = 244, + TotalScore = 1707827, + Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, + User = new User + { + Id = 6602580, + Username = @"waaiiru", + Country = new Country + { + FullName = @"Spain", + FlagName = @"ES", + }, + }, + } + }; + } + private void newScores() { var scores = new[] From 80f46e02d8578ed0c23db9bb68bd7806f637663c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 15:33:49 +0900 Subject: [PATCH 2807/2854] Add equals (=) query operator variants --- osu.Game/Screens/Select/FilterControl.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 01e7ed9383..b25e65092e 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -183,7 +183,7 @@ namespace osu.Game.Screens.Select private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria()); private static readonly Regex query_syntax_regex = new Regex( - @"\b(?stars|ar|dr|cs|divisor|length|objects|bpm|status)(?[:><]+)(?\S*)", + @"\b(?stars|ar|dr|cs|divisor|length|objects|bpm|status)(?[=:><]+)(?\S*)", RegexOptions.Compiled | RegexOptions.IgnoreCase); private void applyQueries(FilterCriteria criteria, ref string query) @@ -246,6 +246,7 @@ namespace osu.Game.Screens.Select default: return; + case "=": case ":": range.IsInclusive = true; range.Min = value - equalityToleration; @@ -257,6 +258,7 @@ namespace osu.Game.Screens.Select range.Min = value; break; + case ">=": case ">:": range.IsInclusive = true; range.Min = value; @@ -267,6 +269,7 @@ namespace osu.Game.Screens.Select range.Max = value; break; + case "<=": case "<:": range.IsInclusive = true; range.Max = value; From e0fd8609d1f96f5fd0169248a3e6b2d3611097f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 15:34:46 +0900 Subject: [PATCH 2808/2854] Fix margins and clean up implementation --- .../Select/Details/UserTopScoreContainer.cs | 28 ++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs index 959fbb927a..9d03f8439a 100644 --- a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs @@ -16,7 +16,6 @@ namespace osu.Game.Screens.Select.Details { public class UserTopScoreContainer : VisibilityContainer { - private const int height = 90; private const int duration = 500; private readonly Container scoreContainer; @@ -30,33 +29,30 @@ namespace osu.Game.Screens.Select.Details public UserTopScoreContainer() { RelativeSizeAxes = Axes.X; - Height = height; + AutoSizeAxes = Axes.Y; - Anchor = Anchor.BottomLeft; - Origin = Anchor.BottomLeft; + Margin = new MarginPadding { Vertical = 5 }; Children = new Drawable[] { - new Container + new FillFlowContainer { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.X, - Height = height, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, Children = new Drawable[] { new OsuSpriteText { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Margin = new MarginPadding { Top = 5 }, Text = @"your personal best".ToUpper(), Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold), }, scoreContainer = new Container { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, } @@ -89,14 +85,8 @@ namespace osu.Game.Screens.Select.Details }, (loadScoreCancellation = new CancellationTokenSource()).Token); } - protected override void PopIn() - { - this.FadeIn(duration, Easing.OutQuint); - } + protected override void PopIn() => this.FadeIn(duration, Easing.OutQuint); - protected override void PopOut() - { - this.FadeOut(duration, Easing.OutQuint); - } + protected override void PopOut() => this.FadeOut(duration, Easing.OutQuint); } } From 2b6c9aeb266e4f352fddebabb066314527ff658e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 15:38:40 +0900 Subject: [PATCH 2809/2854] Move top score container to more local namespace --- .../Visual/SongSelect/TestSceneBeatmapLeaderboard.cs | 1 - .../Visual/SongSelect/TestSceneUserTopScoreContainer.cs | 2 +- osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 1 - .../{Details => Leaderboards}/UserTopScoreContainer.cs | 6 +++--- 4 files changed, 4 insertions(+), 6 deletions(-) rename osu.Game/Screens/Select/{Details => Leaderboards}/UserTopScoreContainer.cs (98%) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index bbd874fcd7..fb27ec7654 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -10,7 +10,6 @@ using osu.Game.Online.Leaderboards; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Scoring; -using osu.Game.Screens.Select.Details; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; using osuTK; diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs index 38ebb58e76..7fac45e0f1 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Screens.Select.Details; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -10,6 +9,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Scoring; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; namespace osu.Game.Tests.Visual.SongSelect diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index d038049504..71aa8a8a31 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -14,7 +14,6 @@ using osu.Game.Online.Leaderboards; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; -using osu.Game.Screens.Select.Details; namespace osu.Game.Screens.Select.Leaderboards { diff --git a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs similarity index 98% rename from osu.Game/Screens/Select/Details/UserTopScoreContainer.cs rename to osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs index 9d03f8439a..301b3a6ae1 100644 --- a/osu.Game/Screens/Select/Details/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Threading; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -9,10 +11,8 @@ using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; using osu.Game.Scoring; -using System; -using System.Threading; -namespace osu.Game.Screens.Select.Details +namespace osu.Game.Screens.Select.Leaderboards { public class UserTopScoreContainer : VisibilityContainer { From 033c68a428bc74c126466d8b39b0bb119e8611dd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 15:44:00 +0900 Subject: [PATCH 2810/2854] Fade in score, not container --- osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs index 301b3a6ae1..c5872e271d 100644 --- a/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -81,7 +81,7 @@ namespace osu.Game.Screens.Select.Leaderboards }, drawableScore => { scoreContainer.Child = drawableScore; - Show(); + drawableScore.FadeInFromZero(duration, Easing.OutQuint); }, (loadScoreCancellation = new CancellationTokenSource()).Token); } From 36d0695e5c243e2012b2f128364a52574665117b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 15:44:05 +0900 Subject: [PATCH 2811/2854] Add spacing --- osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs index c5872e271d..da8f676cd0 100644 --- a/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -11,6 +11,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; using osu.Game.Scoring; +using osuTK; namespace osu.Game.Screens.Select.Leaderboards { @@ -40,6 +41,7 @@ namespace osu.Game.Screens.Select.Leaderboards RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, + Spacing = new Vector2(5), Children = new Drawable[] { new OsuSpriteText From c1daa187fe2aa68426341c6786b18bbee4c621df Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 15:44:14 +0900 Subject: [PATCH 2812/2854] Reduce default tolerance --- osu.Game/Screens/Select/FilterControl.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index b25e65092e..4c91208aec 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -197,23 +197,23 @@ namespace osu.Game.Screens.Select switch (key) { case "stars" when double.TryParse(value, out var stars): - updateCriteriaRange(ref criteria.StarDifficulty, op, stars, 0.5); + updateCriteriaRange(ref criteria.StarDifficulty, op, stars); break; case "ar" when double.TryParse(value, out var ar): - updateCriteriaRange(ref criteria.ApproachRate, op, ar, 0.3); + updateCriteriaRange(ref criteria.ApproachRate, op, ar); break; case "dr" when double.TryParse(value, out var dr): - updateCriteriaRange(ref criteria.DrainRate, op, dr, 0.3); + updateCriteriaRange(ref criteria.DrainRate, op, dr); break; case "cs" when double.TryParse(value, out var cs): - updateCriteriaRange(ref criteria.CircleSize, op, cs, 0.3); + updateCriteriaRange(ref criteria.CircleSize, op, cs); break; case "bpm" when double.TryParse(value, out var bpm): - updateCriteriaRange(ref criteria.BPM, op, bpm, 0.3); + updateCriteriaRange(ref criteria.BPM, op, bpm); break; case "length" when double.TryParse(value.TrimEnd('m', 's', 'h'), out var length): @@ -239,7 +239,7 @@ namespace osu.Game.Screens.Select } } - private void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, double value, double equalityToleration = 0) + private void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, double value, double tolerance = 0.05) { switch (op) { @@ -249,8 +249,8 @@ namespace osu.Game.Screens.Select case "=": case ":": range.IsInclusive = true; - range.Min = value - equalityToleration; - range.Max = value + equalityToleration; + range.Min = value - tolerance; + range.Max = value + tolerance; break; case ">": From 48ee95955b80ea7a385e55649fcd71e308d2b197 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 15:45:08 +0900 Subject: [PATCH 2813/2854] Remove unnecessary redirection --- osu.Game/Screens/Select/SongSelect.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 7f9804c6a3..fca801ce78 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -223,9 +223,7 @@ namespace osu.Game.Screens.Select }); } - void displayScore(ScoreInfo score) => this.Push(new SoloResults(score)); - - BeatmapDetails.Leaderboard.ScoreSelected += displayScore; + BeatmapDetails.Leaderboard.ScoreSelected += score => this.Push(new SoloResults(score)); } [BackgroundDependencyLoader(true)] From e2f7d4bc629defae3f03c64040be75857594c27f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 15:45:43 +0900 Subject: [PATCH 2814/2854] Remove unnecessary ToMetric avoidance --- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index e21ad413b6..9387482f14 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -80,7 +80,7 @@ namespace osu.Game.Online.Leaderboards Anchor = Anchor.Centre, Origin = Anchor.Centre, Font = OsuFont.GetFont(size: 20, italics: true), - Text = rank <= 999 ? rank.ToString() : rank.ToMetric(decimals: rank < 100000 ? 1 : 0), + Text = rank.ToMetric(decimals: rank < 100000 ? 1 : 0), }, }, }, From a214e7e72fbf131c001b07b1f9288764bd5cdf85 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 16:26:22 +0900 Subject: [PATCH 2815/2854] Add confirmation dialog when exiting game --- osu.Game/Screens/Menu/MainMenu.cs | 49 ++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index a006877082..9393f785cd 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -1,18 +1,22 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osuTK; using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API; using osu.Game.Overlays; +using osu.Game.Overlays.Dialog; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Charts; using osu.Game.Screens.Edit; @@ -51,16 +55,23 @@ namespace osu.Game.Screens.Menu [Resolved] private IAPIProvider api { get; set; } + [Resolved] + private DialogOverlay dialogOverlay { get; set; } + private BackgroundScreenDefault background; protected override BackgroundScreen CreateBackground() => background; + private Bindable holdDelay; + [BackgroundDependencyLoader(true)] - private void load(DirectOverlay direct, SettingsOverlay settings) + private void load(DirectOverlay direct, SettingsOverlay settings, OsuConfigManager config) { if (host.CanExit) AddInternal(new ExitConfirmOverlay { Action = this.Exit }); + holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); + AddRangeInternal(new Drawable[] { new ParallaxContainer @@ -141,6 +152,7 @@ namespace osu.Game.Screens.Menu } private bool loginDisplayed; + private bool exitConfirmed; protected override void LogoArriving(OsuLogo logo, bool resuming) { @@ -221,9 +233,44 @@ namespace osu.Game.Screens.Menu public override bool OnExiting(IScreen next) { + if (holdDelay.Value == 0 && !exitConfirmed) + { + dialogOverlay?.Push(new ConfirmExitDialog(() => + { + exitConfirmed = true; + this.Exit(); + })); + + return true; + } + buttons.State = ButtonSystemState.Exit; this.FadeOut(3000); return base.OnExiting(next); } + + public class ConfirmExitDialog : PopupDialog + { + public ConfirmExitDialog(Action confirm) + { + HeaderText = "Are you sure you want to exit?"; + BodyText = "Last chance to back out."; + + Icon = FontAwesome.Solid.ExclamationTriangle; + + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton + { + Text = @"Good bye", + Action = confirm + }, + new PopupDialogCancelButton + { + Text = @"Just a little more" + }, + }; + } + } } } From 929f05884b8bf5cd4ce70c9481ffabf0486174e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 16:28:06 +0900 Subject: [PATCH 2816/2854] Always confirm exit when button is clicked --- osu.Game/Screens/Menu/MainMenu.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 9393f785cd..7645734859 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -85,7 +85,11 @@ namespace osu.Game.Screens.Menu OnEdit = delegate { this.Push(new Editor()); }, OnSolo = onSolo, OnMulti = delegate { this.Push(new Multiplayer()); }, - OnExit = this.Exit, + OnExit = delegate + { + exitConfirmed = true; + this.Exit(); + }, } } }, From 3c21b68b738af2d902341d934f4c34f920b5c5b8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 16:51:57 +0900 Subject: [PATCH 2817/2854] Make OptionalRange generic --- osu.Game/Screens/Select/FilterControl.cs | 27 ++++++++---- osu.Game/Screens/Select/FilterCriteria.cs | 51 ++++++++++++++++------- 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 4c91208aec..8fda6c6a97 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -196,19 +196,19 @@ namespace osu.Game.Screens.Select switch (key) { - case "stars" when double.TryParse(value, out var stars): + case "stars" when float.TryParse(value, out var stars): updateCriteriaRange(ref criteria.StarDifficulty, op, stars); break; - case "ar" when double.TryParse(value, out var ar): + case "ar" when float.TryParse(value, out var ar): updateCriteriaRange(ref criteria.ApproachRate, op, ar); break; - case "dr" when double.TryParse(value, out var dr): + case "dr" when float.TryParse(value, out var dr): updateCriteriaRange(ref criteria.DrainRate, op, dr); break; - case "cs" when double.TryParse(value, out var cs): + case "cs" when float.TryParse(value, out var cs): updateCriteriaRange(ref criteria.CircleSize, op, cs); break; @@ -239,7 +239,8 @@ namespace osu.Game.Screens.Select } } - private void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, double value, double tolerance = 0.05) + private void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, T value, double tolerance = 0.05f) + where T : struct, IComparable { switch (op) { @@ -249,8 +250,20 @@ namespace osu.Game.Screens.Select case "=": case ":": range.IsInclusive = true; - range.Min = value - tolerance; - range.Max = value + tolerance; + + switch (value) + { + case float _: + range.Min = (T)(object)((float)(object)value - tolerance); + range.Max = (T)(object)((float)(object)value + tolerance); + break; + + case double _: + range.Min = (T)(object)((double)(object)value - tolerance); + range.Max = (T)(object)((double)(object)value + tolerance); + break; + } + break; case ">": diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 84d63c16e0..4867f27c0b 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -14,12 +14,12 @@ namespace osu.Game.Screens.Select public GroupMode Group; public SortMode Sort; - public OptionalRange StarDifficulty; - public OptionalRange ApproachRate; - public OptionalRange DrainRate; - public OptionalRange CircleSize; - public OptionalRange Length; - public OptionalRange BPM; + public OptionalRange StarDifficulty; + public OptionalRange ApproachRate; + public OptionalRange DrainRate; + public OptionalRange CircleSize; + public OptionalRange Length; + public OptionalRange BPM; public int? BeatDivisor; @@ -42,23 +42,44 @@ namespace osu.Game.Screens.Select } } - public struct OptionalRange : IEquatable + public struct OptionalRange : IEquatable> + where T : struct, IComparable { - public bool IsInRange(double value) + public bool IsInRange(T value) { - if (Min.HasValue && (IsInclusive ? value < Min.Value : value <= Min.Value)) - return false; - if (Max.HasValue && (IsInclusive ? value > Max.Value : value >= Max.Value)) - return false; + if (Min.HasValue) + { + int comparison = value.CompareTo(Min.Value); + + if (comparison < 0) + return false; + + if (!IsInclusive && comparison == 0) + return false; + } + + if (Max.HasValue) + { + int comparison = value.CompareTo(Max.Value); + + if (comparison > 0) + return false; + + if (!IsInclusive && comparison == 0) + return false; + } return true; } - public double? Min; - public double? Max; + public T? Min; + public T? Max; public bool IsInclusive; - public bool Equals(OptionalRange range) => Min == range.Min && Max == range.Max; + public bool Equals(OptionalRange other) + => Min.Equals(other.Min) + && Max.Equals(other.Max) + && IsInclusive.Equals(other.IsInclusive); } } } From 0915a94470bd67b3d1cdcabb21bd12c6d3e66373 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 16:53:27 +0900 Subject: [PATCH 2818/2854] Make BeatDivisor use OptionalRange --- osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 2 +- osu.Game/Screens/Select/FilterControl.cs | 4 ++-- osu.Game/Screens/Select/FilterCriteria.cs | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 60e556a261..7017310018 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Select.Carousel match &= criteria.Length.IsInRange(Beatmap.Length); match &= criteria.BPM.IsInRange(Beatmap.BPM); - match &= !criteria.BeatDivisor.HasValue || criteria.BeatDivisor == Beatmap.BeatDivisor; + match &= criteria.BeatDivisor.IsInRange(Beatmap.BeatDivisor); match &= !criteria.OnlineStatus.HasValue || criteria.OnlineStatus == Beatmap.Status; if (match) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 8fda6c6a97..829f16d622 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -226,8 +226,8 @@ namespace osu.Game.Screens.Select updateCriteriaRange(ref criteria.Length, op, length * scale, scale / 2.0); break; - case "divisor" when op == ":" && int.TryParse(value, out var divisor): - criteria.BeatDivisor = divisor; + case "divisor" when int.TryParse(value, out var divisor): + updateCriteriaRange(ref criteria.BeatDivisor, op, divisor); break; case "status" when op == ":" && Enum.TryParse(value, ignoreCase: true, out var statusValue): diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 4867f27c0b..4584c63c2e 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -20,8 +20,7 @@ namespace osu.Game.Screens.Select public OptionalRange CircleSize; public OptionalRange Length; public OptionalRange BPM; - - public int? BeatDivisor; + public OptionalRange BeatDivisor; public BeatmapSetOnlineStatus? OnlineStatus; From 167bb9fcc16751087fc687d3424e7ea7cfafa555 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 17:11:28 +0900 Subject: [PATCH 2819/2854] Fix ugly casts --- osu.Game/Screens/Select/FilterControl.cs | 46 +++++++++++++++--------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 829f16d622..6ada0e4980 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -239,8 +239,36 @@ namespace osu.Game.Screens.Select } } - private void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, T value, double tolerance = 0.05f) - where T : struct, IComparable + private void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, float value, float tolerance = 0.05f) + { + switch (op) + { + case "=": + case ":": + range.Min = value - tolerance; + range.Max = value + tolerance; + break; + } + + updateCriteriaRange(ref range, op, value); + } + + private void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, double value, double tolerance = 0.05f) + { + switch (op) + { + case "=": + case ":": + range.Min = value - tolerance; + range.Max = value + tolerance; + break; + } + + updateCriteriaRange(ref range, op, value); + } + + private void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, T value) + where T : struct, IComparable { switch (op) { @@ -250,20 +278,6 @@ namespace osu.Game.Screens.Select case "=": case ":": range.IsInclusive = true; - - switch (value) - { - case float _: - range.Min = (T)(object)((float)(object)value - tolerance); - range.Max = (T)(object)((float)(object)value + tolerance); - break; - - case double _: - range.Min = (T)(object)((double)(object)value - tolerance); - range.Max = (T)(object)((double)(object)value + tolerance); - break; - } - break; case ">": From d7831d8f5d6cdced76b9ff070b1d0d55c932818b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 17:11:43 +0900 Subject: [PATCH 2820/2854] Use non-generic IComparable interface --- osu.Game/Screens/Select/FilterCriteria.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 4584c63c2e..ff55ef5b12 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets; @@ -42,13 +43,13 @@ namespace osu.Game.Screens.Select } public struct OptionalRange : IEquatable> - where T : struct, IComparable + where T : struct, IComparable { public bool IsInRange(T value) { - if (Min.HasValue) + if (Min != null) { - int comparison = value.CompareTo(Min.Value); + int comparison = Comparer.Default.Compare(value, Min.Value); if (comparison < 0) return false; @@ -57,9 +58,9 @@ namespace osu.Game.Screens.Select return false; } - if (Max.HasValue) + if (Max != null) { - int comparison = value.CompareTo(Max.Value); + int comparison = Comparer.Default.Compare(value, Max.Value); if (comparison > 0) return false; From 7683f7ff23198fc782135b4959550abe13236c97 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 17:12:07 +0900 Subject: [PATCH 2821/2854] Make OnlineStatus use OptionalRange --- osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 2 +- osu.Game/Screens/Select/FilterControl.cs | 4 ++-- osu.Game/Screens/Select/FilterCriteria.cs | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 7017310018..9cc84c8bdd 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Select.Carousel match &= criteria.BPM.IsInRange(Beatmap.BPM); match &= criteria.BeatDivisor.IsInRange(Beatmap.BeatDivisor); - match &= !criteria.OnlineStatus.HasValue || criteria.OnlineStatus == Beatmap.Status; + match &= criteria.OnlineStatus.IsInRange(Beatmap.Status); if (match) foreach (var criteriaTerm in criteria.SearchTerms) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 6ada0e4980..f1c8bb3a4e 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -230,8 +230,8 @@ namespace osu.Game.Screens.Select updateCriteriaRange(ref criteria.BeatDivisor, op, divisor); break; - case "status" when op == ":" && Enum.TryParse(value, ignoreCase: true, out var statusValue): - criteria.OnlineStatus = statusValue; + case "status" when Enum.TryParse(value, true, out var statusValue): + updateCriteriaRange(ref criteria.OnlineStatus, op, statusValue); break; } diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index ff55ef5b12..674f64efb4 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -22,8 +22,7 @@ namespace osu.Game.Screens.Select public OptionalRange Length; public OptionalRange BPM; public OptionalRange BeatDivisor; - - public BeatmapSetOnlineStatus? OnlineStatus; + public OptionalRange OnlineStatus; public string[] SearchTerms = Array.Empty(); From e075dd7ea84258862fac010f95e4a56d62d6ea0c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 17:16:34 +0900 Subject: [PATCH 2822/2854] Fix equals operator not working --- osu.Game/Screens/Select/FilterControl.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index f1c8bb3a4e..cd958a9503 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -241,6 +241,8 @@ namespace osu.Game.Screens.Select private void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, float value, float tolerance = 0.05f) { + updateCriteriaRange(ref range, op, value); + switch (op) { case "=": @@ -249,12 +251,12 @@ namespace osu.Game.Screens.Select range.Max = value + tolerance; break; } - - updateCriteriaRange(ref range, op, value); } - private void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, double value, double tolerance = 0.05f) + private void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, double value, double tolerance = 0.05) { + updateCriteriaRange(ref range, op, value); + switch (op) { case "=": @@ -263,8 +265,6 @@ namespace osu.Game.Screens.Select range.Max = value + tolerance; break; } - - updateCriteriaRange(ref range, op, value); } private void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, T value) @@ -278,6 +278,8 @@ namespace osu.Game.Screens.Select case "=": case ":": range.IsInclusive = true; + range.Min = value; + range.Max = value; break; case ">": From 96ea507320f6b10a9b902f5c1cc6f2df17a9c07a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 17:21:22 +0900 Subject: [PATCH 2823/2854] Reorder comparison for readability --- osu.Game/Screens/Select/FilterCriteria.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 674f64efb4..a3fa1b10ca 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Select if (comparison < 0) return false; - if (!IsInclusive && comparison == 0) + if (comparison == 0 && !IsInclusive) return false; } @@ -64,7 +64,7 @@ namespace osu.Game.Screens.Select if (comparison > 0) return false; - if (!IsInclusive && comparison == 0) + if (comparison == 0 && !IsInclusive) return false; } From e6c36a8bc7974e5c2b3f31c7e41467bc53809d7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 17:27:54 +0900 Subject: [PATCH 2824/2854] Fix scaling mode being applied to judgements --- osu.Game/Rulesets/Judgements/DrawableJudgement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 061c8cb3e1..b77fc1cfb0 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Judgements Font = OsuFont.Numeric.With(size: 20), Colour = judgementColour(Result.Type), Scale = new Vector2(0.85f, 1), - }) + }, confineMode: ConfineMode.NoScaling) }; } From 5120d82ef84cba05df1b775cd1689b1e7d60a562 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 17:36:42 +0900 Subject: [PATCH 2825/2854] Fix crash with multiple range criterias --- osu.Game/Screens/Select/FilterControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index cd958a9503..e3c23f7e22 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -235,7 +235,7 @@ namespace osu.Game.Screens.Select break; } - query = query.Remove(match.Index, match.Length); + query = query.Replace(match.ToString(), ""); } } From fa54a0bfd3954576adc0a07fef7619d6d6050f3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 17:40:46 +0900 Subject: [PATCH 2826/2854] Fix test failures --- osu.Game/Screens/Menu/MainMenu.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 7645734859..2d8c48873a 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Menu [Resolved] private IAPIProvider api { get; set; } - [Resolved] + [Resolved(canBeNull: true)] private DialogOverlay dialogOverlay { get; set; } private BackgroundScreenDefault background; @@ -237,9 +237,9 @@ namespace osu.Game.Screens.Menu public override bool OnExiting(IScreen next) { - if (holdDelay.Value == 0 && !exitConfirmed) + if (holdDelay.Value == 0 && !exitConfirmed && dialogOverlay != null) { - dialogOverlay?.Push(new ConfirmExitDialog(() => + dialogOverlay.Push(new ConfirmExitDialog(() => { exitConfirmed = true; this.Exit(); From da15b900f7d628f4790dfd263c045382cdcd4f51 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 17:44:24 +0900 Subject: [PATCH 2827/2854] Remove virtual member from ModBlockFail --- osu.Game/Rulesets/Mods/ModBlockFail.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs index 55c074bde2..7d7ecfa416 100644 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mods /// public bool AllowFail => false; - public virtual bool RestartOnFail => false; + public bool RestartOnFail => false; public void ReadFromConfig(OsuConfigManager config) { From 65276cd235a0026170d4e06b2faffc515b045c06 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 17:58:10 +0900 Subject: [PATCH 2828/2854] Remove whitespace --- osu.Game/Rulesets/Mods/ModBlockFail.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs index 7d7ecfa416..957a8d3348 100644 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -15,7 +15,6 @@ namespace osu.Game.Rulesets.Mods /// We never fail, 'yo. /// public bool AllowFail => false; - public bool RestartOnFail => false; public void ReadFromConfig(OsuConfigManager config) From bc9941a990ea7430f2f8f1d87714beb8517e1dab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 18:00:11 +0900 Subject: [PATCH 2829/2854] Newline required when xmldocs are involved --- osu.Game/Rulesets/Mods/ModBlockFail.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs index 957a8d3348..7d7ecfa416 100644 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Mods /// We never fail, 'yo. /// public bool AllowFail => false; + public bool RestartOnFail => false; public void ReadFromConfig(OsuConfigManager config) From ddff9882cf25e447df3e5632b0bc65f65d125d73 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 17:35:45 +0900 Subject: [PATCH 2830/2854] Fix importing archives which are nested in a single folder within a zip --- .../Beatmaps/IO/ImportBeatmapTest.cs | 50 ++++++++++++++++++- osu.Game/Database/ArchiveModelManager.cs | 7 ++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index ad0ed00989..8b39946ab0 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -15,7 +15,10 @@ using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.IO; using osu.Game.Tests.Resources; +using SharpCompress.Archives; using SharpCompress.Archives.Zip; +using SharpCompress.Common; +using SharpCompress.Writers.Zip; namespace osu.Game.Tests.Beatmaps.IO { @@ -135,7 +138,7 @@ namespace osu.Game.Tests.Beatmaps.IO using (var zip = ZipArchive.Open(brokenOsz)) { zip.AddEntry("broken.osu", brokenOsu, false); - zip.SaveTo(outStream, SharpCompress.Common.CompressionType.Deflate); + zip.SaveTo(outStream, CompressionType.Deflate); } // this will trigger purging of the existing beatmap (online set id match) but should rollback due to broken osu. @@ -366,6 +369,51 @@ namespace osu.Game.Tests.Beatmaps.IO } } + [Test] + public async Task TestImportNestedStructure() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportNestedStructure")) + { + try + { + var osu = loadOsu(host); + + var temp = TestResources.GetTestBeatmapForImport(); + + string extractedFolder = $"{temp}_extracted"; + string subfolder = Path.Combine(extractedFolder, "subfolder"); + + Directory.CreateDirectory(subfolder); + + try + { + using (var zip = ZipArchive.Open(temp)) + zip.WriteToDirectory(subfolder); + + using (var zip = ZipArchive.Create()) + { + zip.AddAllFromDirectory(extractedFolder); + zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate)); + } + + var imported = await osu.Dependencies.Get().Import(temp); + + ensureLoaded(osu); + + Assert.IsFalse(imported.Files.Any(f => f.Filename.Contains("subfolder")), "Files contain common subfolder"); + } + finally + { + Directory.Delete(extractedFolder, true); + } + } + finally + { + host.Exit(); + } + } + } + public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null) { var temp = path ?? TestResources.GetTestBeatmapForImport(); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 52d3f013ce..6c79b0d472 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -11,6 +11,7 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using osu.Framework; using osu.Framework.Extensions; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.IO.File; using osu.Framework.Logging; using osu.Framework.Platform; @@ -481,12 +482,16 @@ namespace osu.Game.Database { var fileInfos = new List(); + string prefix = reader.Filenames.GetCommonPrefix(); + if (!(prefix.EndsWith("/") || prefix.EndsWith("\\"))) + prefix = string.Empty; + // import files to manager foreach (string file in reader.Filenames) using (Stream s = reader.GetStream(file)) fileInfos.Add(new TFileModel { - Filename = FileSafety.PathStandardise(file), + Filename = FileSafety.PathStandardise(file.Substring(prefix.Length)), FileInfo = files.Add(s) }); From cffee1fd5e39c0857d877793be0a5184bce5cc3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 20:02:45 +0900 Subject: [PATCH 2831/2854] Fix imported beatmap paths not correctly matching files --- osu.Game/Beatmaps/BeatmapManager.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 02d7b2d98f..55b8b80e44 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -89,7 +89,7 @@ namespace osu.Game.Beatmaps protected override Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default) { if (archive != null) - beatmapSet.Beatmaps = createBeatmapDifficulties(archive); + beatmapSet.Beatmaps = createBeatmapDifficulties(beatmapSet.Files); foreach (BeatmapInfo b in beatmapSet.Beatmaps) { @@ -279,13 +279,13 @@ namespace osu.Game.Beatmaps /// /// Create all required s for the provided archive. /// - private List createBeatmapDifficulties(ArchiveReader reader) + private List createBeatmapDifficulties(List files) { var beatmapInfos = new List(); - foreach (var name in reader.Filenames.Where(f => f.EndsWith(".osu"))) + foreach (var file in files.Where(f => f.Filename.EndsWith(".osu"))) { - using (var raw = reader.GetStream(name)) + using (var raw = Files.Store.GetStream(file.FileInfo.StoragePath)) using (var ms = new MemoryStream()) //we need a memory stream so we can seek using (var sr = new StreamReader(ms)) { @@ -295,7 +295,7 @@ namespace osu.Game.Beatmaps var decoder = Decoder.GetDecoder(sr); IBeatmap beatmap = decoder.Decode(sr); - beatmap.BeatmapInfo.Path = name; + beatmap.BeatmapInfo.Path = file.Filename; beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); From 50d4206c4584950d20e8824585a613a5a6b5ada2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 20:17:58 +0900 Subject: [PATCH 2832/2854] Fix exit scenarios --- .../TestSceneHoldToConfirmOverlay.cs | 3 --- .../Containers/HoldToConfirmContainer.cs | 13 +++++++--- osu.Game/Overlays/DialogOverlay.cs | 26 +++++++++++-------- osu.Game/Screens/Menu/ExitConfirmOverlay.cs | 7 ++++- osu.Game/Screens/Menu/MainMenu.cs | 16 ++++++++---- 5 files changed, 41 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs index f787754aa4..d4143f3f8d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs @@ -61,10 +61,7 @@ namespace osu.Game.Tests.Visual.UserInterface private class TestHoldToConfirmOverlay : ExitConfirmOverlay { - protected override bool AllowMultipleFires => true; - public void Begin() => BeginConfirm(); - public void Abort() => AbortConfirm(); } } } diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs index a345fb554f..5d549ba217 100644 --- a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs +++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs @@ -16,7 +16,11 @@ namespace osu.Game.Graphics.Containers private const int fadeout_delay = 200; - private bool fired; + /// + /// Whether currently in a fired state (and the confirm has been sent). + /// + public bool Fired { get; private set; } + private bool confirming; /// @@ -36,7 +40,7 @@ namespace osu.Game.Graphics.Containers protected void BeginConfirm() { - if (confirming || (!AllowMultipleFires && fired)) return; + if (confirming || (!AllowMultipleFires && Fired)) return; confirming = true; @@ -46,14 +50,15 @@ namespace osu.Game.Graphics.Containers protected virtual void Confirm() { Action?.Invoke(); - fired = true; + Fired = true; } protected void AbortConfirm() { - if (!AllowMultipleFires && fired) return; + if (!AllowMultipleFires && Fired) return; confirming = false; + Fired = false; this.TransformBindableTo(Progress, 0, fadeout_delay, Easing.Out); } diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 6aaeff8554..59d748bc5d 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -13,7 +13,8 @@ namespace osu.Game.Overlays public class DialogOverlay : OsuFocusedOverlayContainer { private readonly Container dialogContainer; - private PopupDialog currentDialog; + + public PopupDialog CurrentDialog { get; private set; } public DialogOverlay() { @@ -31,15 +32,15 @@ namespace osu.Game.Overlays public void Push(PopupDialog dialog) { - if (dialog == currentDialog) return; + if (dialog == CurrentDialog) return; - currentDialog?.Hide(); - currentDialog = dialog; + CurrentDialog?.Hide(); + CurrentDialog = dialog; - dialogContainer.Add(currentDialog); + dialogContainer.Add(CurrentDialog); - currentDialog.Show(); - currentDialog.State.ValueChanged += state => onDialogOnStateChanged(dialog, state.NewValue); + CurrentDialog.Show(); + CurrentDialog.State.ValueChanged += state => onDialogOnStateChanged(dialog, state.NewValue); Show(); } @@ -52,8 +53,11 @@ namespace osu.Game.Overlays //handle the dialog being dismissed. dialog.Delay(PopupDialog.EXIT_DURATION).Expire(); - if (dialog == currentDialog) + if (dialog == CurrentDialog) + { Hide(); + CurrentDialog = null; + } } protected override void PopIn() @@ -66,9 +70,9 @@ namespace osu.Game.Overlays { base.PopOut(); - if (currentDialog?.State.Value == Visibility.Visible) + if (CurrentDialog?.State.Value == Visibility.Visible) { - currentDialog.Hide(); + CurrentDialog.Hide(); return; } @@ -80,7 +84,7 @@ namespace osu.Game.Overlays switch (action) { case GlobalAction.Select: - currentDialog?.Buttons.OfType().FirstOrDefault()?.Click(); + CurrentDialog?.Buttons.OfType().FirstOrDefault()?.Click(); return true; } diff --git a/osu.Game/Screens/Menu/ExitConfirmOverlay.cs b/osu.Game/Screens/Menu/ExitConfirmOverlay.cs index 519834859d..aaa3a77e74 100644 --- a/osu.Game/Screens/Menu/ExitConfirmOverlay.cs +++ b/osu.Game/Screens/Menu/ExitConfirmOverlay.cs @@ -9,6 +9,10 @@ namespace osu.Game.Screens.Menu { public class ExitConfirmOverlay : HoldToConfirmOverlay, IKeyBindingHandler { + protected override bool AllowMultipleFires => true; + + public void Abort() => AbortConfirm(); + public bool OnPressed(GlobalAction action) { if (action == GlobalAction.Back) @@ -24,7 +28,8 @@ namespace osu.Game.Screens.Menu { if (action == GlobalAction.Back) { - AbortConfirm(); + if (!Fired) + AbortConfirm(); return true; } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 2d8c48873a..23fb2d8e37 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -64,11 +64,13 @@ namespace osu.Game.Screens.Menu private Bindable holdDelay; + private ExitConfirmOverlay exitConfirmOverlay; + [BackgroundDependencyLoader(true)] private void load(DirectOverlay direct, SettingsOverlay settings, OsuConfigManager config) { if (host.CanExit) - AddInternal(new ExitConfirmOverlay { Action = this.Exit }); + AddInternal(exitConfirmOverlay = new ExitConfirmOverlay { Action = this.Exit }); holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); @@ -237,12 +239,15 @@ namespace osu.Game.Screens.Menu public override bool OnExiting(IScreen next) { - if (holdDelay.Value == 0 && !exitConfirmed && dialogOverlay != null) + if (holdDelay.Value == 0 && !exitConfirmed && dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog)) { dialogOverlay.Push(new ConfirmExitDialog(() => { exitConfirmed = true; this.Exit(); + }, () => + { + exitConfirmOverlay.Abort(); })); return true; @@ -253,9 +258,9 @@ namespace osu.Game.Screens.Menu return base.OnExiting(next); } - public class ConfirmExitDialog : PopupDialog + private class ConfirmExitDialog : PopupDialog { - public ConfirmExitDialog(Action confirm) + public ConfirmExitDialog(Action confirm, Action cancel) { HeaderText = "Are you sure you want to exit?"; BodyText = "Last chance to back out."; @@ -271,7 +276,8 @@ namespace osu.Game.Screens.Menu }, new PopupDialogCancelButton { - Text = @"Just a little more" + Text = @"Just a little more", + Action = cancel }, }; } From 94d3bcc612b96c63814e6fb5523f3b794ee12877 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Sep 2019 20:47:33 +0900 Subject: [PATCH 2833/2854] Fix top score not being selectable --- osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 71aa8a8a31..337d46ecdd 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -96,7 +96,10 @@ namespace osu.Game.Screens.Select.Leaderboards UpdateScores(); }; - Content.Add(topScoreContainer = new UserTopScoreContainer()); + Content.Add(topScoreContainer = new UserTopScoreContainer + { + ScoreSelected = s => ScoreSelected?.Invoke(s) + }); } protected override void Reset() From 23c5cb6367e1cf976eaae4cec6f6662b48adc9e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 22:35:14 +0900 Subject: [PATCH 2834/2854] Expand tests to cover new behaviour --- .../Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs index d4143f3f8d..feef1dae6b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs @@ -52,11 +52,18 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("start confirming", () => overlay.Begin()); AddStep("abort confirming", () => overlay.Abort()); + AddAssert("ensure not fired internally", () => !overlay.Fired); AddAssert("ensure aborted", () => !fired); AddStep("start confirming", () => overlay.Begin()); AddUntilStep("wait until confirmed", () => fired); + AddAssert("ensure fired internally", () => overlay.Fired); + + AddStep("abort after fire", () => overlay.Abort()); + AddAssert("ensure not fired internally", () => !overlay.Fired); + AddStep("start confirming", () => overlay.Begin()); + AddUntilStep("wait until fired again", () => overlay.Fired); } private class TestHoldToConfirmOverlay : ExitConfirmOverlay From 67796e098258dd194367aa2bf031c66064b205cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 22:46:21 +0900 Subject: [PATCH 2835/2854] Apply code styling suggestions --- osu.Game/Screens/Menu/MainMenu.cs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 23fb2d8e37..0274973161 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -87,11 +87,7 @@ namespace osu.Game.Screens.Menu OnEdit = delegate { this.Push(new Editor()); }, OnSolo = onSolo, OnMulti = delegate { this.Push(new Multiplayer()); }, - OnExit = delegate - { - exitConfirmed = true; - this.Exit(); - }, + OnExit = confirmAndExit, } } }, @@ -120,6 +116,12 @@ namespace osu.Game.Screens.Menu preloadSongSelect(); } + private void confirmAndExit() + { + exitConfirmed = true; + this.Exit(); + } + private void preloadSongSelect() { if (songSelect == null) @@ -241,15 +243,7 @@ namespace osu.Game.Screens.Menu { if (holdDelay.Value == 0 && !exitConfirmed && dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog)) { - dialogOverlay.Push(new ConfirmExitDialog(() => - { - exitConfirmed = true; - this.Exit(); - }, () => - { - exitConfirmOverlay.Abort(); - })); - + dialogOverlay.Push(new ConfirmExitDialog(confirmAndExit, () => exitConfirmOverlay.Abort())); return true; } From f10b390ca0ccc1aa3cce2392cfbb58b1428bfc06 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2019 16:32:39 +0000 Subject: [PATCH 2836/2854] Bump Microsoft.NET.Test.Sdk from 16.2.0 to 16.3.0 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.2.0 to 16.3.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.2.0...v16.3) Signed-off-by: dependabot-preview[bot] --- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index c527a81f51..36342024b0 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index af10d5e06e..09bf9241f2 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index c331c811d2..791043bcc6 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index d2a0a8fa6f..b0e0efdc68 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 84f67c9319..75e6354612 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index bba3c92245..491cf54686 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -5,7 +5,7 @@ - + From 636582e089c12f3eb9b814e84c7f3b54cd370e0d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Sep 2019 02:22:49 +0900 Subject: [PATCH 2837/2854] Always show exit confirmation when closing via alt-f4 or window control --- osu.Game/Screens/Menu/MainMenu.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 0274973161..dd81569e26 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -69,11 +69,22 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader(true)] private void load(DirectOverlay direct, SettingsOverlay settings, OsuConfigManager config) { - if (host.CanExit) - AddInternal(exitConfirmOverlay = new ExitConfirmOverlay { Action = this.Exit }); - holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); + if (host.CanExit) + { + AddInternal(exitConfirmOverlay = new ExitConfirmOverlay + { + Action = () => + { + if (holdDelay.Value > 0) + confirmAndExit(); + else + this.Exit(); + } + }); + } + AddRangeInternal(new Drawable[] { new ParallaxContainer @@ -241,7 +252,7 @@ namespace osu.Game.Screens.Menu public override bool OnExiting(IScreen next) { - if (holdDelay.Value == 0 && !exitConfirmed && dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog)) + if (!exitConfirmed && dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog)) { dialogOverlay.Push(new ConfirmExitDialog(confirmAndExit, () => exitConfirmOverlay.Abort())); return true; From 573da7b1e78b44a91db02fecfd8830fdd362d5b5 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 19 Sep 2019 20:34:37 +0300 Subject: [PATCH 2838/2854] Implement ChangelogEntryType --- .../Online/API/Requests/Responses/APIChangelogEntry.cs | 8 +++++++- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelogEntry.cs b/osu.Game/Online/API/Requests/Responses/APIChangelogEntry.cs index 140e228acd..f949ab5da5 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelogEntry.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelogEntry.cs @@ -24,7 +24,7 @@ namespace osu.Game.Online.API.Requests.Responses public string Url { get; set; } [JsonProperty("type")] - public string Type { get; set; } + public ChangelogEntryType Type { get; set; } [JsonProperty("category")] public string Category { get; set; } @@ -44,4 +44,10 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty("github_user")] public APIChangelogUser GithubUser { get; set; } } + + public enum ChangelogEntryType + { + Add, + Fix + } } diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 3d145af562..6b24f39d98 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -76,7 +76,7 @@ namespace osu.Game.Overlays.Changelog var entryColour = entry.Major ? colours.YellowLight : Color4.White; - title.AddIcon(FontAwesome.Solid.Check, t => + title.AddIcon(entry.Type == ChangelogEntryType.Fix ? FontAwesome.Solid.Check : FontAwesome.Solid.Plus, t => { t.Font = fontSmall; t.Colour = entryColour; From daa64f1be7c9de8643014ca9d1aacf610a7d4ac9 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 19 Sep 2019 20:53:06 +0300 Subject: [PATCH 2839/2854] Adjust icon padding --- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 6b24f39d98..6d7b36b832 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Changelog { t.Font = fontSmall; t.Colour = entryColour; - t.Padding = new MarginPadding { Left = -17, Right = 5 }; + t.Padding = new MarginPadding { Left = -17, Top = 5 }; }); title.AddText(entry.Title, t => From 5663e3e6b3b682c8c78c74fd49ebdf1502c0157d Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 19 Sep 2019 20:08:14 +0200 Subject: [PATCH 2840/2854] Fix escaped html strings not being unescaped in changelog entries. --- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 3d145af562..f44faabe52 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Users; using osuTK.Graphics; using osu.Framework.Allocation; +using System.Net; namespace osu.Game.Overlays.Changelog { @@ -149,7 +150,7 @@ namespace osu.Game.Overlays.Changelog }; // todo: use markdown parsing once API returns markdown - message.AddText(Regex.Replace(entry.MessageHtml, @"<(.|\n)*?>", string.Empty), t => + message.AddText(WebUtility.HtmlDecode(Regex.Replace(entry.MessageHtml, @"<(.|\n)*?>", string.Empty)), t => { t.Font = fontSmall; t.Colour = new Color4(235, 184, 254, 255); From f7f9c0f7e0cb93bbbc423fbb3065713271c7b4a3 Mon Sep 17 00:00:00 2001 From: Revel Date: Thu, 19 Sep 2019 15:47:32 -0400 Subject: [PATCH 2841/2854] Update BeatmapDetailAreaTabControl.cs --- osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs b/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs index 7f82d3cc12..6caef8e2aa 100644 --- a/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs +++ b/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Select { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, - Text = @"Mods", + Text = @"Selected Mods", Alpha = 0, }, }; From 033ed2e1f56c670dc42926c26aa1e9209e8b3c4e Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 20 Sep 2019 00:10:28 +0300 Subject: [PATCH 2842/2854] Add setting to always tint slider ball --- .../Configuration/OsuRulesetConfigManager.cs | 2 ++ osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs index f76635a932..ab6e6bac6c 100644 --- a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs @@ -18,6 +18,7 @@ namespace osu.Game.Rulesets.Osu.Configuration base.InitialiseDefaults(); Set(OsuRulesetSetting.SnakingInSliders, true); Set(OsuRulesetSetting.SnakingOutSliders, true); + Set(OsuRulesetSetting.AlwaysTintSliderBall, true); Set(OsuRulesetSetting.ShowCursorTrail, true); } } @@ -26,6 +27,7 @@ namespace osu.Game.Rulesets.Osu.Configuration { SnakingInSliders, SnakingOutSliders, + AlwaysTintSliderBall, ShowCursorTrail } } diff --git a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs index 88adf72551..be86614b57 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs @@ -35,6 +35,11 @@ namespace osu.Game.Rulesets.Osu.UI Bindable = config.GetBindable(OsuRulesetSetting.SnakingOutSliders) }, new SettingsCheckbox + { + LabelText = "Always tint slider ball with combo colour", + Bindable = config.GetBindable(OsuRulesetSetting.AlwaysTintSliderBall) + }, + new SettingsCheckbox { LabelText = "Cursor trail", Bindable = config.GetBindable(OsuRulesetSetting.ShowCursorTrail) From 8fcfd823169948811ca4000afde188ededb2d043 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 20 Sep 2019 00:10:55 +0300 Subject: [PATCH 2843/2854] Add AllowSliderBallTint to skin configuration --- osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs index e7b686d27d..98219cafe8 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs @@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Osu.Skinning HitCircleOverlap, SliderBorderSize, SliderPathRadius, + AllowSliderBallTint, CursorExpand, } } From f6291170b197bb200b2b3c1cd66b65623263047c Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 20 Sep 2019 00:11:37 +0300 Subject: [PATCH 2844/2854] Implement tinting slider ball with combo colour --- .../Objects/Drawables/DrawableSlider.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 643a0f7336..d894fe2a31 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -34,6 +34,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly IBindable scaleBindable = new Bindable(); private readonly IBindable pathBindable = new Bindable(); + private readonly Bindable alwaysTintSliderBall = new Bindable(true); + private bool allowSliderBallTint; + [Resolved(CanBeNull = true)] private OsuRulesetConfigManager config { get; set; } @@ -106,6 +109,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { config?.BindWith(OsuRulesetSetting.SnakingInSliders, Body.SnakingIn); config?.BindWith(OsuRulesetSetting.SnakingOutSliders, Body.SnakingOut); + config?.BindWith(OsuRulesetSetting.AlwaysTintSliderBall, alwaysTintSliderBall); positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); scaleBindable.BindValueChanged(scale => @@ -120,9 +124,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables pathBindable.BindValueChanged(_ => Body.Refresh()); + alwaysTintSliderBall.BindValueChanged(_ => updateSliderBallTint()); + AccentColour.BindValueChanged(colour => { Body.AccentColour = colour.NewValue; + updateSliderBallTint(); foreach (var drawableHitObject in NestedHitObjects) drawableHitObject.AccentColour.Value = colour.NewValue; @@ -173,10 +180,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Body.AccentColour = skin.GetConfig(OsuSkinColour.SliderTrackOverride)?.Value ?? AccentColour.Value; Body.BorderColour = skin.GetConfig(OsuSkinColour.SliderBorder)?.Value ?? Color4.White; + + allowSliderBallTint = skin.GetConfig(OsuSkinConfiguration.AllowSliderBallTint)?.Value ?? false; + updateSliderBallTint(); } private void updatePathRadius() => Body.PathRadius = slider.Scale * sliderPathRadius; + private void updateSliderBallTint() => Ball.Colour = (alwaysTintSliderBall.Value | allowSliderBallTint) ? AccentColour.Value : Color4.White; + protected override void CheckForResult(bool userTriggered, double timeOffset) { if (userTriggered || Time.Current < slider.EndTime) From 1b45014ff678ccd256a5a3c57885fbb58357e1f8 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 20 Sep 2019 00:25:16 +0300 Subject: [PATCH 2845/2854] Use logical-OR --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index d894fe2a31..36ac0be723 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -187,7 +187,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private void updatePathRadius() => Body.PathRadius = slider.Scale * sliderPathRadius; - private void updateSliderBallTint() => Ball.Colour = (alwaysTintSliderBall.Value | allowSliderBallTint) ? AccentColour.Value : Color4.White; + private void updateSliderBallTint() => Ball.Colour = (alwaysTintSliderBall.Value || allowSliderBallTint) ? AccentColour.Value : Color4.White; protected override void CheckForResult(bool userTriggered, double timeOffset) { From d0a4e1e3c2ff107327091d7ece191742f45e1429 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Sep 2019 15:00:27 +0900 Subject: [PATCH 2846/2854] Force a checksum check before skipping FileStore copy op --- osu.Game/IO/FileStore.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs index 370d6786f5..bf4e881ed0 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -50,7 +50,16 @@ namespace osu.Game.IO string path = info.StoragePath; // we may be re-adding a file to fix missing store entries. - if (!Storage.Exists(path)) + bool requiresCopy = !Storage.Exists(path); + + if (!requiresCopy) + { + // even if the file already exists, check the existing checksum for safety. + using (var stream = Storage.GetStream(path)) + requiresCopy |= stream.ComputeSHA2Hash() != hash; + } + + if (requiresCopy) { data.Seek(0, SeekOrigin.Begin); From f306fe27d82d790ec6df2764a04b6e7ab1903593 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Sep 2019 15:05:48 +0900 Subject: [PATCH 2847/2854] Add test to cover corruption case --- .../Beatmaps/IO/ImportBeatmapTest.cs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 8b39946ab0..385ab4064d 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -90,6 +90,48 @@ namespace osu.Game.Tests.Beatmaps.IO } } + [Test] + public async Task TestImportCorruptThenImport() + { + //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImport")) + { + try + { + var osu = loadOsu(host); + + var imported = await LoadOszIntoOsu(osu); + + var firstFile = imported.Files.First(); + + var files = osu.Dependencies.Get(); + + long originalLength; + using (var stream = files.Storage.GetStream(firstFile.FileInfo.StoragePath)) + originalLength = stream.Length; + + using (var stream = files.Storage.GetStream(firstFile.FileInfo.StoragePath, FileAccess.Write, FileMode.Create)) + stream.WriteByte(0); + + var importedSecondTime = await LoadOszIntoOsu(osu); + + using (var stream = files.Storage.GetStream(firstFile.FileInfo.StoragePath)) + Assert.AreEqual(stream.Length, originalLength, "Corruption was not fixed on second import"); + + // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash. + Assert.IsTrue(imported.ID == importedSecondTime.ID); + Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID); + + checkBeatmapSetCount(osu, 1); + checkSingleReferencedFileCount(osu, 18); + } + finally + { + host.Exit(); + } + } + } + [Test] public async Task TestRollbackOnFailure() { From 093ed8421e34d76255083fd4bba7890f4818fbfe Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 20 Sep 2019 15:08:00 +0300 Subject: [PATCH 2848/2854] Remove "allow slider ball tinting" ruleset setting --- .../Configuration/OsuRulesetConfigManager.cs | 2 -- .../Objects/Drawables/DrawableSlider.cs | 10 ++-------- osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs | 5 ----- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs index ab6e6bac6c..f76635a932 100644 --- a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs @@ -18,7 +18,6 @@ namespace osu.Game.Rulesets.Osu.Configuration base.InitialiseDefaults(); Set(OsuRulesetSetting.SnakingInSliders, true); Set(OsuRulesetSetting.SnakingOutSliders, true); - Set(OsuRulesetSetting.AlwaysTintSliderBall, true); Set(OsuRulesetSetting.ShowCursorTrail, true); } } @@ -27,7 +26,6 @@ namespace osu.Game.Rulesets.Osu.Configuration { SnakingInSliders, SnakingOutSliders, - AlwaysTintSliderBall, ShowCursorTrail } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 36ac0be723..a0b60479ac 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -109,7 +109,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { config?.BindWith(OsuRulesetSetting.SnakingInSliders, Body.SnakingIn); config?.BindWith(OsuRulesetSetting.SnakingOutSliders, Body.SnakingOut); - config?.BindWith(OsuRulesetSetting.AlwaysTintSliderBall, alwaysTintSliderBall); positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); scaleBindable.BindValueChanged(scale => @@ -124,12 +123,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables pathBindable.BindValueChanged(_ => Body.Refresh()); - alwaysTintSliderBall.BindValueChanged(_ => updateSliderBallTint()); - AccentColour.BindValueChanged(colour => { Body.AccentColour = colour.NewValue; - updateSliderBallTint(); foreach (var drawableHitObject in NestedHitObjects) drawableHitObject.AccentColour.Value = colour.NewValue; @@ -181,14 +177,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Body.AccentColour = skin.GetConfig(OsuSkinColour.SliderTrackOverride)?.Value ?? AccentColour.Value; Body.BorderColour = skin.GetConfig(OsuSkinColour.SliderBorder)?.Value ?? Color4.White; - allowSliderBallTint = skin.GetConfig(OsuSkinConfiguration.AllowSliderBallTint)?.Value ?? false; - updateSliderBallTint(); + bool allowBallTint = skin.GetConfig(OsuSkinConfiguration.AllowSliderBallTint)?.Value ?? false; + Ball.Colour = allowBallTint ? AccentColour.Value : Color4.White; } private void updatePathRadius() => Body.PathRadius = slider.Scale * sliderPathRadius; - private void updateSliderBallTint() => Ball.Colour = (alwaysTintSliderBall.Value || allowSliderBallTint) ? AccentColour.Value : Color4.White; - protected override void CheckForResult(bool userTriggered, double timeOffset) { if (userTriggered || Time.Current < slider.EndTime) diff --git a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs index be86614b57..88adf72551 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs @@ -35,11 +35,6 @@ namespace osu.Game.Rulesets.Osu.UI Bindable = config.GetBindable(OsuRulesetSetting.SnakingOutSliders) }, new SettingsCheckbox - { - LabelText = "Always tint slider ball with combo colour", - Bindable = config.GetBindable(OsuRulesetSetting.AlwaysTintSliderBall) - }, - new SettingsCheckbox { LabelText = "Cursor trail", Bindable = config.GetBindable(OsuRulesetSetting.ShowCursorTrail) From 57310c86c7ab8d57f1edad558f4f9fdc917595fb Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 20 Sep 2019 15:09:51 +0300 Subject: [PATCH 2849/2854] Remove unnecessary fields --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index a0b60479ac..9e8ad9851c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -34,9 +34,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly IBindable scaleBindable = new Bindable(); private readonly IBindable pathBindable = new Bindable(); - private readonly Bindable alwaysTintSliderBall = new Bindable(true); - private bool allowSliderBallTint; - [Resolved(CanBeNull = true)] private OsuRulesetConfigManager config { get; set; } From 2d99d41a6dc1fb79173f91f5dcbb22514ab96d0b Mon Sep 17 00:00:00 2001 From: Vperus Date: Fri, 20 Sep 2019 18:17:35 +0300 Subject: [PATCH 2850/2854] Remove unused CORNER_RADIUS --- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 31221c05ee..8f353ae138 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -18,8 +18,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables /// public class DrawableNote : DrawableManiaHitObject, IKeyBindingHandler { - public const float CORNER_RADIUS = NotePiece.NOTE_HEIGHT / 2; - private readonly NotePiece headPiece; public DrawableNote(Note hitObject) From ac8fe6045f2cba92ec1d3e819b0f6fa0b3c15164 Mon Sep 17 00:00:00 2001 From: Vperus Date: Fri, 20 Sep 2019 19:58:39 +0300 Subject: [PATCH 2851/2854] Fixed typo Changed CreateReourceStore() to CreateResourceStore() --- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Rulesets/UI/DrawableRuleset.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 197c089f71..dd1b3615c7 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.QuestionCircle }; - public virtual IResourceStore CreateReourceStore() => new NamespacedResourceStore(new DllResourceStore(GetType().Assembly.Location), @"Resources"); + public virtual IResourceStore CreateResourceStore() => new NamespacedResourceStore(new DllResourceStore(GetType().Assembly.Location), @"Resources"); public abstract string Description { get; } diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index a34bb6e8ea..d68b0e94c5 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -153,7 +153,7 @@ namespace osu.Game.Rulesets.UI { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - var resources = Ruleset.CreateReourceStore(); + var resources = Ruleset.CreateResourceStore(); if (resources != null) { From 92f9cf3e06e75eb67d0fa40498e2b436136b9e85 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 21 Sep 2019 02:08:19 +0900 Subject: [PATCH 2852/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 969eb205e0..c57fc342ba 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -62,6 +62,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a733a0e7f9..a27a94b8f9 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 4bfa1ebcd0..a6516e6d1b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -118,8 +118,8 @@ - - + + From bbf3ac77f840a546fe500de6b2638540b674e3a8 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 20 Sep 2019 21:35:26 +0200 Subject: [PATCH 2853/2854] Add visual test for HTML string unescaping. --- .../Online/TestSceneChangelogOverlay.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index f555c276f4..658f678b10 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -68,6 +68,34 @@ namespace osu.Game.Tests.Visual.Online changelog.ShowListing(); changelog.Show(); }); + + AddStep(@"Ensure HTML string unescaping", () => + { + changelog.ShowBuild(new APIChangelogBuild + { + Version = "2019.920.0", + DisplayVersion = "2019.920.0", + UpdateStream = new APIUpdateStream + { + Name = "Test", + DisplayName = "Test" + }, + ChangelogEntries = new List + { + new APIChangelogEntry + { + Category = "Testing HTML strings unescaping", + Title = "Ensuring HTML strings are being unescaped", + MessageHtml = """"This text should appear triple-quoted""" >_<", + GithubUser = new APIChangelogUser + { + DisplayName = "Dummy", + OsuUsername = "Dummy", + } + }, + } + }); + }); } } } From befdd140f462c80d6aa2027b22f77310ecd45ed5 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 20 Sep 2019 23:50:19 +0300 Subject: [PATCH 2854/2854] Reverse padding changes --- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 6d7b36b832..6b24f39d98 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Changelog { t.Font = fontSmall; t.Colour = entryColour; - t.Padding = new MarginPadding { Left = -17, Top = 5 }; + t.Padding = new MarginPadding { Left = -17, Right = 5 }; }); title.AddText(entry.Title, t =>
/// Creates the conversion value for a . A conversion value stores information about the converted . @@ -176,6 +184,32 @@ namespace osu.Game.Tests.Beatmaps [JsonProperty] public List Mappings = new List(); } + + private class ConversionWorkingBeatmap : WorkingBeatmap + { + public Action, IBeatmapConverter> ConversionGenerated; + + private readonly IBeatmap beatmap; + + public ConversionWorkingBeatmap(IBeatmap beatmap) + : base(beatmap.BeatmapInfo, null) + { + this.beatmap = beatmap; + } + + protected override IBeatmap GetBeatmap() => beatmap; + + protected override Texture GetBackground() => throw new NotImplementedException(); + + protected override Track GetTrack() => throw new NotImplementedException(); + + protected override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) + { + var converter = base.CreateBeatmapConverter(beatmap, ruleset); + converter.ObjectConverted += (orig, converted) => ConversionGenerated?.Invoke(orig, converted, converter); + return converter; + } + } } public abstract class BeatmapConversionTest : BeatmapConversionTest, TConvertValue> From ed4dda19363a1a112b4d55b6719c4d456073a459 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 31 Jul 2019 19:49:25 +0900 Subject: [PATCH 1983/2854] Support beatmap conversion tests with mods --- .../CatchBeatmapConversionTest.cs | 2 +- .../ManiaBeatmapConversionTest.cs | 2 +- osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs | 2 +- .../TaikoBeatmapConversionTest.cs | 2 +- osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 8 ++++---- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index e45ed8c6f4..3a695ca179 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase("spinner")] [TestCase("spinner-and-circles")] [TestCase("slider")] - public new void Test(string name) + public void Test(string name) { base.Test(name); } diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs index 51c7ba029a..3ca9dcc42c 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mania.Tests protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; [TestCase("basic")] - public new void Test(string name) + public void Test(string name) { base.Test(name); } diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs index f98d63e6c7..6a6d0abe24 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase("basic")] [TestCase("colinear-perfect-curve")] [TestCase("slider-ticks")] - public new void Test(string name) + public void Test(string name) { base.Test(name); } diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs index 68ae7544c2..bafa814582 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [NonParallelizable] [TestCase("basic")] [TestCase("slider-generating-drumroll")] - public new void Test(string name) + public void Test(string name) { base.Test(name); } diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 1f2d457624..a555a52e42 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -28,9 +28,9 @@ namespace osu.Game.Tests.Beatmaps protected abstract string ResourceAssembly { get; } - protected void Test(string name) + protected void Test(string name, params Type[] mods) { - var ourResult = convert(name); + var ourResult = convert(name, mods.Select(m => (Mod)Activator.CreateInstance(m)).ToArray()); var expectedResult = read(name); Assert.Multiple(() => @@ -92,7 +92,7 @@ namespace osu.Game.Tests.Beatmaps }); } - private ConvertResult convert(string name) + private ConvertResult convert(string name, Mod[] mods) { var beatmap = getBeatmap(name); @@ -110,7 +110,7 @@ namespace osu.Game.Tests.Beatmaps } }; - working.GetPlayableBeatmap(rulesetInstance.RulesetInfo, Array.Empty()); + working.GetPlayableBeatmap(rulesetInstance.RulesetInfo, mods); return new ConvertResult { From fdc6a3958d3655e799420469c1fe17f4cecd11a9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 31 Jul 2019 18:44:01 +0900 Subject: [PATCH 1984/2854] Make catch HR properly utilise the RNG --- .../Beatmaps/CatchBeatmapProcessor.cs | 119 +++++++++++++++++- .../MathUtils/FastRandom.cs | 8 ++ .../Mods/CatchModHardRock.cs | 112 +---------------- 3 files changed, 127 insertions(+), 112 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 645cb5701a..343f48f15c 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -10,6 +10,8 @@ using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Types; using osuTK; using osu.Game.Rulesets.Catch.MathUtils; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Catch.Beatmaps { @@ -26,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps { base.PostProcess(); - applyPositionOffsets(); + ApplyPositionOffsets(Beatmap); initialiseHyperDash((List)Beatmap.HitObjects); @@ -40,15 +42,23 @@ namespace osu.Game.Rulesets.Catch.Beatmaps } } - private void applyPositionOffsets() + public static void ApplyPositionOffsets(IBeatmap beatmap, params Mod[] mods) { var rng = new FastRandom(RNG_SEED); - // todo: HardRock displacement should be applied here - foreach (var obj in Beatmap.HitObjects) + bool shouldApplyHardRockOffset = mods.Any(m => m is ModHardRock); + float? lastPosition = null; + double lastStartTime = 0; + + foreach (var obj in beatmap.HitObjects) { switch (obj) { + case Fruit fruit: + if (shouldApplyHardRockOffset) + applyHardRockOffset(fruit, ref lastPosition, ref lastStartTime, rng); + break; + case BananaShower bananaShower: foreach (var banana in bananaShower.NestedHitObjects.OfType()) { @@ -76,6 +86,107 @@ namespace osu.Game.Rulesets.Catch.Beatmaps } } + private static void applyHardRockOffset(HitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng) + { + if (hitObject is JuiceStream stream) + { + lastPosition = stream.EndX; + lastStartTime = stream.EndTime; + return; + } + + if (!(hitObject is Fruit)) + return; + + var catchObject = (CatchHitObject)hitObject; + + float position = catchObject.X; + double startTime = hitObject.StartTime; + + if (lastPosition == null) + { + lastPosition = position; + lastStartTime = startTime; + + return; + } + + float positionDiff = position - lastPosition.Value; + double timeDiff = startTime - lastStartTime; + + if (timeDiff > 1000) + { + lastPosition = position; + lastStartTime = startTime; + return; + } + + if (positionDiff == 0) + { + applyRandomOffset(ref position, timeDiff / 4d, rng); + catchObject.X = position; + return; + } + + if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3d) + applyOffset(ref position, positionDiff); + + catchObject.X = position; + + lastPosition = position; + lastStartTime = startTime; + } + + /// + /// Applies a random offset in a random direction to a position, ensuring that the final position remains within the boundary of the playfield. + /// + /// The position which the offset should be applied to. + /// The maximum offset, cannot exceed 20px. + /// The random number generator. + private static void applyRandomOffset(ref float position, double maxOffset, FastRandom rng) + { + bool right = rng.NextBool(); + float rand = Math.Min(20, (float)rng.Next(0, Math.Max(0, maxOffset))) / CatchPlayfield.BASE_WIDTH; + + if (right) + { + // Clamp to the right bound + if (position + rand <= 1) + position += rand; + else + position -= rand; + } + else + { + // Clamp to the left bound + if (position - rand >= 0) + position -= rand; + else + position += rand; + } + } + + /// + /// Applies an offset to a position, ensuring that the final position remains within the boundary of the playfield. + /// + /// The position which the offset should be applied to. + /// The amount to offset by. + private static void applyOffset(ref float position, float amount) + { + if (amount > 0) + { + // Clamp to the right bound + if (position + amount < 1) + position += amount; + } + else + { + // Clamp to the left bound + if (position + amount > 0) + position += amount; + } + } + private void initialiseHyperDash(List objects) { List objectWithDroplets = new List(); diff --git a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs index b3605b013b..c721ff862a 100644 --- a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs +++ b/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs @@ -61,6 +61,14 @@ namespace osu.Game.Rulesets.Catch.MathUtils /// The random value. public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound)); + /// + /// Generates a random integer value within the range [, ). + /// + /// The lower bound of the range. + /// The upper bound of the range. + /// The random value. + public int Next(double lowerBound, double upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound)); + /// /// Generates a random double value within the range [0, 1). /// diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs index 060e51e31d..ced1900ba9 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs @@ -1,121 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.MathUtils; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Mods; -using System; -using osu.Game.Rulesets.Objects; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Beatmaps; namespace osu.Game.Rulesets.Catch.Mods { - public class CatchModHardRock : ModHardRock, IApplicableToHitObject + public class CatchModHardRock : ModHardRock, IApplicableToBeatmap { public override double ScoreMultiplier => 1.12; public override bool Ranked => true; - private float? lastPosition; - private double lastStartTime; - - public void ApplyToHitObject(HitObject hitObject) - { - if (hitObject is JuiceStream stream) - { - lastPosition = stream.EndX; - lastStartTime = stream.EndTime; - return; - } - - if (!(hitObject is Fruit)) - return; - - var catchObject = (CatchHitObject)hitObject; - - float position = catchObject.X; - double startTime = hitObject.StartTime; - - if (lastPosition == null) - { - lastPosition = position; - lastStartTime = startTime; - - return; - } - - float positionDiff = position - lastPosition.Value; - double timeDiff = startTime - lastStartTime; - - if (timeDiff > 1000) - { - lastPosition = position; - lastStartTime = startTime; - return; - } - - if (positionDiff == 0) - { - applyRandomOffset(ref position, timeDiff / 4d); - catchObject.X = position; - return; - } - - if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3d) - applyOffset(ref position, positionDiff); - - catchObject.X = position; - - lastPosition = position; - lastStartTime = startTime; - } - - /// - /// Applies a random offset in a random direction to a position, ensuring that the final position remains within the boundary of the playfield. - /// - /// The position which the offset should be applied to. - /// The maximum offset, cannot exceed 20px. - private void applyRandomOffset(ref float position, double maxOffset) - { - bool right = RNG.NextBool(); - float rand = Math.Min(20, (float)RNG.NextDouble(0, Math.Max(0, maxOffset))) / CatchPlayfield.BASE_WIDTH; - - if (right) - { - // Clamp to the right bound - if (position + rand <= 1) - position += rand; - else - position -= rand; - } - else - { - // Clamp to the left bound - if (position - rand >= 0) - position -= rand; - else - position += rand; - } - } - - /// - /// Applies an offset to a position, ensuring that the final position remains within the boundary of the playfield. - /// - /// The position which the offset should be applied to. - /// The amount to offset by. - private void applyOffset(ref float position, float amount) - { - if (amount > 0) - { - // Clamp to the right bound - if (position + amount < 1) - position += amount; - } - else - { - // Clamp to the left bound - if (position + amount > 0) - position += amount; - } - } + public void ApplyToBeatmap(IBeatmap beatmap) => CatchBeatmapProcessor.ApplyPositionOffsets(beatmap, this); } } From 38a2b9d92b5af230ee315f6e223f4a6da3f46373 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Aug 2019 13:33:00 +0900 Subject: [PATCH 1985/2854] Fix multiple invocations by using a separate variable --- .../Beatmaps/CatchBeatmapProcessor.cs | 32 +++++++++---------- .../Objects/CatchHitObject.cs | 14 +++++++- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 343f48f15c..ffff9b854d 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -11,7 +11,6 @@ using osu.Game.Rulesets.Objects.Types; using osuTK; using osu.Game.Rulesets.Catch.MathUtils; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Catch.Beatmaps { @@ -50,8 +49,10 @@ namespace osu.Game.Rulesets.Catch.Beatmaps float? lastPosition = null; double lastStartTime = 0; - foreach (var obj in beatmap.HitObjects) + foreach (var obj in beatmap.HitObjects.OfType()) { + obj.XOffset = 0; + switch (obj) { case Fruit fruit: @@ -62,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps case BananaShower bananaShower: foreach (var banana in bananaShower.NestedHitObjects.OfType()) { - banana.X = (float)rng.NextDouble(); + banana.XOffset = (float)rng.NextDouble(); rng.Next(); // osu!stable retrieved a random banana type rng.Next(); // osu!stable retrieved a random banana rotation rng.Next(); // osu!stable retrieved a random banana colour @@ -75,10 +76,9 @@ namespace osu.Game.Rulesets.Catch.Beatmaps { var hitObject = (CatchHitObject)nested; if (hitObject is TinyDroplet) - hitObject.X += rng.Next(-20, 20) / CatchPlayfield.BASE_WIDTH; + hitObject.XOffset = MathHelper.Clamp(rng.Next(-20, 20) / CatchPlayfield.BASE_WIDTH, -hitObject.X, 1 - hitObject.X); else if (hitObject is Droplet) rng.Next(); // osu!stable retrieved a random droplet rotation - hitObject.X = MathHelper.Clamp(hitObject.X, 0, 1); } break; @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps } } - private static void applyHardRockOffset(HitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng) + private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng) { if (hitObject is JuiceStream stream) { @@ -98,42 +98,40 @@ namespace osu.Game.Rulesets.Catch.Beatmaps if (!(hitObject is Fruit)) return; - var catchObject = (CatchHitObject)hitObject; - - float position = catchObject.X; + float offsetPosition = hitObject.X; double startTime = hitObject.StartTime; if (lastPosition == null) { - lastPosition = position; + lastPosition = offsetPosition; lastStartTime = startTime; return; } - float positionDiff = position - lastPosition.Value; + float positionDiff = offsetPosition - lastPosition.Value; double timeDiff = startTime - lastStartTime; if (timeDiff > 1000) { - lastPosition = position; + lastPosition = offsetPosition; lastStartTime = startTime; return; } if (positionDiff == 0) { - applyRandomOffset(ref position, timeDiff / 4d, rng); - catchObject.X = position; + applyRandomOffset(ref offsetPosition, timeDiff / 4d, rng); + hitObject.XOffset = hitObject.X - offsetPosition; return; } if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3d) - applyOffset(ref position, positionDiff); + applyOffset(ref offsetPosition, positionDiff); - catchObject.X = position; + hitObject.XOffset = hitObject.X - offsetPosition; - lastPosition = position; + lastPosition = offsetPosition; lastStartTime = startTime; } diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index be76edc01b..19a1b59752 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -3,6 +3,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -12,7 +13,18 @@ namespace osu.Game.Rulesets.Catch.Objects { public const double OBJECT_RADIUS = 44; - public float X { get; set; } + private float x; + + public float X + { + get => x + XOffset; + set => x = value; + } + + /// + /// A random offset applied to , set by the . + /// + internal float XOffset { get; set; } public double TimePreempt = 1000; From e2420af10c844d9459fd906b3035086c949e3266 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Aug 2019 13:37:40 +0900 Subject: [PATCH 1986/2854] Fix applying mods to the wrong beatmap --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index baf921ddfc..c6105f6566 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -142,7 +142,7 @@ namespace osu.Game.Beatmaps processor?.PostProcess(); foreach (var mod in mods.OfType()) - mod.ApplyToBeatmap(Beatmap); + mod.ApplyToBeatmap(converted); return converted; } From 423857f403581907eef4c44cc97199e5d84546f3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Aug 2019 14:57:07 +0900 Subject: [PATCH 1987/2854] Fix inverted offset --- osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index ffff9b854d..3ee2928573 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -122,14 +122,14 @@ namespace osu.Game.Rulesets.Catch.Beatmaps if (positionDiff == 0) { applyRandomOffset(ref offsetPosition, timeDiff / 4d, rng); - hitObject.XOffset = hitObject.X - offsetPosition; + hitObject.XOffset = offsetPosition - hitObject.X; return; } if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3d) applyOffset(ref offsetPosition, positionDiff); - hitObject.XOffset = hitObject.X - offsetPosition; + hitObject.XOffset = offsetPosition - hitObject.X; lastPosition = offsetPosition; lastStartTime = startTime; From 8a1d690011665ab19aceb2bd6ada65185517e7d2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Aug 2019 14:57:17 +0900 Subject: [PATCH 1988/2854] Reset tick offsets --- .../Beatmaps/CatchBeatmapProcessor.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 3ee2928573..5ab47c1611 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -74,10 +74,12 @@ namespace osu.Game.Rulesets.Catch.Beatmaps case JuiceStream juiceStream: foreach (var nested in juiceStream.NestedHitObjects) { - var hitObject = (CatchHitObject)nested; - if (hitObject is TinyDroplet) - hitObject.XOffset = MathHelper.Clamp(rng.Next(-20, 20) / CatchPlayfield.BASE_WIDTH, -hitObject.X, 1 - hitObject.X); - else if (hitObject is Droplet) + var catchObject = (CatchHitObject)nested; + catchObject.XOffset = 0; + + if (catchObject is TinyDroplet) + catchObject.XOffset = MathHelper.Clamp(rng.Next(-20, 20) / CatchPlayfield.BASE_WIDTH, -catchObject.X, 1 - catchObject.X); + else if (catchObject is Droplet) rng.Next(); // osu!stable retrieved a random droplet rotation } From b978727422b8a163aaa7402ba91951a6dc303e80 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Aug 2019 14:58:17 +0900 Subject: [PATCH 1989/2854] Add stream + repeat slider tests --- .../CatchBeatmapConversionTest.cs | 7 +- ...ock-repeat-slider-expected-conversion.json | 150 +++++++++++ .../Beatmaps/hardrock-repeat-slider.osu | 18 ++ .../hardrock-stream-expected-conversion.json | 234 ++++++++++++++++++ .../Testing/Beatmaps/hardrock-stream.osu | 107 ++++++++ 5 files changed, 514 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider.osu create mode 100644 osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream.osu diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index 3a695ca179..80ac64c5f9 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using Newtonsoft.Json; using NUnit.Framework; using osu.Framework.MathUtils; +using osu.Game.Rulesets.Catch.Mods; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects; @@ -22,9 +23,11 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase("spinner")] [TestCase("spinner-and-circles")] [TestCase("slider")] - public void Test(string name) + [TestCase("hardrock-stream", new[] { typeof(CatchModHardRock) })] + [TestCase("hardrock-repeat-slider", new[] { typeof(CatchModHardRock) })] + public new void Test(string name, params Type[] mods) { - base.Test(name); + base.Test(name, mods); } protected override IEnumerable CreateConvertValue(HitObject hitObject) diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider-expected-conversion.json b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider-expected-conversion.json new file mode 100644 index 0000000000..83f9e30800 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider-expected-conversion.json @@ -0,0 +1,150 @@ +{ + "Mappings": [{ + "StartTime": 369, + "Objects": [{ + "StartTime": 369, + "Position": 177 + }, + { + "StartTime": 450, + "Position": 216.539276 + }, + { + "StartTime": 532, + "Position": 256.5667 + }, + { + "StartTime": 614, + "Position": 296.594116 + }, + { + "StartTime": 696, + "Position": 336.621521 + }, + { + "StartTime": 778, + "Position": 376.99762 + }, + { + "StartTime": 860, + "Position": 337.318878 + }, + { + "StartTime": 942, + "Position": 297.291443 + }, + { + "StartTime": 1024, + "Position": 257.264038 + }, + { + "StartTime": 1106, + "Position": 217.2366 + }, + { + "StartTime": 1188, + "Position": 177 + }, + { + "StartTime": 1270, + "Position": 216.818192 + }, + { + "StartTime": 1352, + "Position": 256.8456 + }, + { + "StartTime": 1434, + "Position": 296.873047 + }, + { + "StartTime": 1516, + "Position": 336.900452 + }, + { + "StartTime": 1598, + "Position": 376.99762 + }, + { + "StartTime": 1680, + "Position": 337.039948 + }, + { + "StartTime": 1762, + "Position": 297.0125 + }, + { + "StartTime": 1844, + "Position": 256.9851 + }, + { + "StartTime": 1926, + "Position": 216.957672 + }, + { + "StartTime": 2008, + "Position": 177 + }, + { + "StartTime": 2090, + "Position": 217.097137 + }, + { + "StartTime": 2172, + "Position": 257.124573 + }, + { + "StartTime": 2254, + "Position": 297.152 + }, + { + "StartTime": 2336, + "Position": 337.179443 + }, + { + "StartTime": 2418, + "Position": 376.99762 + }, + { + "StartTime": 2500, + "Position": 336.760956 + }, + { + "StartTime": 2582, + "Position": 296.733643 + }, + { + "StartTime": 2664, + "Position": 256.7062 + }, + { + "StartTime": 2746, + "Position": 216.678772 + }, + { + "StartTime": 2828, + "Position": 177 + }, + { + "StartTime": 2909, + "Position": 216.887909 + }, + { + "StartTime": 2991, + "Position": 256.915344 + }, + { + "StartTime": 3073, + "Position": 296.942749 + }, + { + "StartTime": 3155, + "Position": 336.970184 + }, + { + "StartTime": 3237, + "Position": 376.99762 + } + ] + }] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider.osu new file mode 100644 index 0000000000..c1971edf2c --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider.osu @@ -0,0 +1,18 @@ +osu file format v14 + +[General] +StackLeniency: 0.4 +Mode: 0 + +[Difficulty] +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8 +SliderMultiplier:1.6 +SliderTickRate:4 + +[TimingPoints] +369,327.868852459016,4,2,2,32,1,0 + +[HitObjects] +177,191,369,6,0,L|382:192,7,200 diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream-expected-conversion.json b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream-expected-conversion.json new file mode 100644 index 0000000000..bbc16ab912 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream-expected-conversion.json @@ -0,0 +1,234 @@ +{ + "Mappings": [{ + "StartTime": 369, + "Objects": [{ + "StartTime": 369, + "Position": 258 + }] + }, + { + "StartTime": 450, + "Objects": [{ + "StartTime": 450, + "Position": 254 + }] + }, + { + "StartTime": 532, + "Objects": [{ + "StartTime": 532, + "Position": 241 + }] + }, + { + "StartTime": 614, + "Objects": [{ + "StartTime": 614, + "Position": 238 + }] + }, + { + "StartTime": 696, + "Objects": [{ + "StartTime": 696, + "Position": 238 + }] + }, + { + "StartTime": 778, + "Objects": [{ + "StartTime": 778, + "Position": 278 + }] + }, + { + "StartTime": 860, + "Objects": [{ + "StartTime": 860, + "Position": 238 + }] + }, + { + "StartTime": 942, + "Objects": [{ + "StartTime": 942, + "Position": 278 + }] + }, + { + "StartTime": 1024, + "Objects": [{ + "StartTime": 1024, + "Position": 238 + }] + }, + { + "StartTime": 1106, + "Objects": [{ + "StartTime": 1106, + "Position": 278 + }] + }, + { + "StartTime": 1188, + "Objects": [{ + "StartTime": 1188, + "Position": 278 + }] + }, + { + "StartTime": 1270, + "Objects": [{ + "StartTime": 1270, + "Position": 278 + }] + }, + { + "StartTime": 1352, + "Objects": [{ + "StartTime": 1352, + "Position": 238 + }] + }, + { + "StartTime": 1434, + "Objects": [{ + "StartTime": 1434, + "Position": 258 + }] + }, + { + "StartTime": 1516, + "Objects": [{ + "StartTime": 1516, + "Position": 253 + }] + }, + { + "StartTime": 1598, + "Objects": [{ + "StartTime": 1598, + "Position": 238 + }] + }, + { + "StartTime": 1680, + "Objects": [{ + "StartTime": 1680, + "Position": 260 + }] + }, + { + "StartTime": 1762, + "Objects": [{ + "StartTime": 1762, + "Position": 238 + }] + }, + { + "StartTime": 1844, + "Objects": [{ + "StartTime": 1844, + "Position": 278 + }] + }, + { + "StartTime": 1926, + "Objects": [{ + "StartTime": 1926, + "Position": 278 + }] + }, + { + "StartTime": 2008, + "Objects": [{ + "StartTime": 2008, + "Position": 238 + }] + }, + { + "StartTime": 2090, + "Objects": [{ + "StartTime": 2090, + "Position": 238 + }] + }, + { + "StartTime": 2172, + "Objects": [{ + "StartTime": 2172, + "Position": 243 + }] + }, + { + "StartTime": 2254, + "Objects": [{ + "StartTime": 2254, + "Position": 278 + }] + }, + { + "StartTime": 2336, + "Objects": [{ + "StartTime": 2336, + "Position": 278 + }] + }, + { + "StartTime": 2418, + "Objects": [{ + "StartTime": 2418, + "Position": 238 + }] + }, + { + "StartTime": 2500, + "Objects": [{ + "StartTime": 2500, + "Position": 258 + }] + }, + { + "StartTime": 2582, + "Objects": [{ + "StartTime": 2582, + "Position": 256 + }] + }, + { + "StartTime": 2664, + "Objects": [{ + "StartTime": 2664, + "Position": 242 + }] + }, + { + "StartTime": 2746, + "Objects": [{ + "StartTime": 2746, + "Position": 238 + }] + }, + { + "StartTime": 2828, + "Objects": [{ + "StartTime": 2828, + "Position": 238 + }] + }, + { + "StartTime": 2909, + "Objects": [{ + "StartTime": 2909, + "Position": 271 + }] + }, + { + "StartTime": 2991, + "Objects": [{ + "StartTime": 2991, + "Position": 254 + }] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream.osu new file mode 100644 index 0000000000..62835135df --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream.osu @@ -0,0 +1,107 @@ +osu file format v14 + +[General] +AudioFilename: audio.mp3 +AudioLeadIn: 0 +PreviewTime: 53483 +Countdown: 0 +SampleSet: Soft +StackLeniency: 0.4 +Mode: 0 +LetterboxInBreaks: 0 +WidescreenStoryboard: 0 + +[Editor] +Bookmarks: 369,1680,12172,22664,33155,43647,54139,64631,75123,85614,96106,103975,114467,124959,135287 +DistanceSpacing: 1 +BeatDivisor: 4 +GridSize: 8 +TimelineZoom: 2.899999 + +[Metadata] +Title:Silhouette +TitleUnicode:シルエット +Artist:KANA-BOON +ArtistUnicode:KANA-BOON +Creator:Nevo +Version:lit120's Hard +Source:NARUTO-ナルト-疾風伝 +Tags:shippuuden shipuuden shippuden nine tails opening 16 kage cut ver version naotoshi nao tomori shunao shogunmoon sotarks pika [[pika]] frostwich lit120 shiratoi armin a_r_m_i_n +BeatmapID:1653115 +BeatmapSetID:780952 + +[Difficulty] +HPDrainRate:6 +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8 +SliderMultiplier:1.6 +SliderTickRate:1 + +[Events] +//Background and Video events +0,0,"OSpGdEP.png",0,0 +//Break Periods +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples + +[TimingPoints] +369,327.868852459016,4,2,2,32,1,0 +1680,-100,4,2,2,42,0,0 +6926,-100,4,2,2,52,0,0 +11844,-100,4,2,2,5,0,0 +12172,-100,4,2,2,62,0,0 +22664,-100,4,2,2,52,0,0 +43647,-100,4,2,2,52,0,0 +53483,-100,4,2,2,32,0,0 +54139,-100,4,2,2,72,0,1 +96106,-100,4,2,2,52,0,0 +103975,-100,4,2,2,82,0,1 +124959,-100,4,2,2,62,0,0 +130205,-100,4,2,2,72,0,0 +132991,-100,4,2,2,82,0,0 + + +[Colours] +Combo1 : 154,53,255 +Combo2 : 253,211,47 +Combo3 : 86,168,250 +Combo4 : 192,192,192 + +[HitObjects] +258,189,369,1,0,0:0:0:0: +258,189,450,1,0,0:0:0:0: +258,189,532,1,0,0:0:0:0: +258,189,614,1,0,0:0:0:0: +258,189,696,1,0,0:0:0:0: +258,189,778,1,0,0:0:0:0: +258,189,860,1,0,0:0:0:0: +258,189,942,1,0,0:0:0:0: +258,189,1024,1,0,0:0:0:0: +258,189,1106,1,0,0:0:0:0: +258,189,1188,1,0,0:0:0:0: +258,189,1270,1,0,0:0:0:0: +258,189,1352,1,0,0:0:0:0: +258,189,1434,1,0,0:0:0:0: +258,189,1516,1,0,0:0:0:0: +258,189,1598,1,0,0:0:0:0: +258,189,1680,1,0,0:0:0:0: +258,189,1762,1,0,0:0:0:0: +258,189,1844,1,0,0:0:0:0: +258,189,1926,1,0,0:0:0:0: +258,189,2008,1,0,0:0:0:0: +258,189,2090,1,0,0:0:0:0: +258,189,2172,1,0,0:0:0:0: +258,189,2254,1,0,0:0:0:0: +258,189,2336,1,0,0:0:0:0: +258,189,2418,1,0,0:0:0:0: +258,189,2500,1,0,0:0:0:0: +258,189,2582,1,0,0:0:0:0: +258,189,2664,1,0,0:0:0:0: +258,189,2746,1,0,0:0:0:0: +258,189,2828,1,0,0:0:0:0: +258,189,2909,1,0,0:0:0:0: +258,189,2991,1,0,0:0:0:0: From d772bbaf8c77bae4a280a8facca67832b208d007 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 1 Aug 2019 10:04:04 +0300 Subject: [PATCH 1990/2854] Simplify disposing --- osu.Game/OsuGame.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 431e6c91f9..b9e2b79b05 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -307,11 +307,10 @@ namespace osu.Game if (nextBeatmap?.Track != null) nextBeatmap.Track.Completed += currentTrackCompleted; - var oldBeatmap = beatmap.OldValue; - if (oldBeatmap?.Track != null) - oldBeatmap.Track.Completed -= currentTrackCompleted; + using (var oldBeatmap = beatmap.OldValue) + if (oldBeatmap?.Track != null) + oldBeatmap.Track.Completed -= currentTrackCompleted; - oldBeatmap?.Dispose(); nextBeatmap?.LoadBeatmapAsync(); } From 0f36088ef894f0f94869d0c5df3d7bddfbb00151 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 1 Aug 2019 11:06:29 +0300 Subject: [PATCH 1991/2854] Add loved section --- osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs | 1 + osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index f3384163b8..fb1385793f 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -27,6 +27,7 @@ namespace osu.Game.Online.API.Requests { Favourite, RankedAndApproved, + Loved, Unranked, Graveyard } diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index ae91837d29..37f017277f 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -18,6 +18,7 @@ namespace osu.Game.Overlays.Profile.Sections { new PaginatedBeatmapContainer(BeatmapSetType.Favourite, User, "Favourite Beatmaps"), new PaginatedBeatmapContainer(BeatmapSetType.RankedAndApproved, User, "Ranked & Approved Beatmaps"), + new PaginatedBeatmapContainer(BeatmapSetType.Loved, User, "Loved Beatmaps"), new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, "Pending Beatmaps"), new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps"), }; From a2468e599b292d1c4d4da4cc5d49b87d38c01d98 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Aug 2019 17:31:26 +0900 Subject: [PATCH 1992/2854] Fix ticks in repeat spans being returned in reverse order --- .../Rulesets/Objects/SliderEventGenerator.cs | 60 ++++++++++++++----- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs index 7cda7485f9..9047d338aa 100644 --- a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs +++ b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs @@ -3,13 +3,15 @@ using System; using System.Collections.Generic; +using System.Linq; using osuTK; namespace osu.Game.Rulesets.Objects { public static class SliderEventGenerator { - public static IEnumerable Generate(double startTime, double spanDuration, double velocity, double tickDistance, double totalDistance, int spanCount, double? legacyLastTickOffset) + public static IEnumerable Generate(double startTime, double spanDuration, double velocity, double tickDistance, double totalDistance, int spanCount, + double? legacyLastTickOffset) { // A very lenient maximum length of a slider for ticks to be generated. // This exists for edge cases such as /b/1573664 where the beatmap has been edited by the user, and should never be reached in normal usage. @@ -36,24 +38,17 @@ namespace osu.Game.Rulesets.Objects var spanStartTime = startTime + span * spanDuration; var reversed = span % 2 == 1; - for (var d = tickDistance; d <= length; d += tickDistance) + var ticks = generateTicks(span, spanStartTime, spanDuration, reversed, length, tickDistance, minDistanceFromEnd); + + if (reversed) { - if (d >= length - minDistanceFromEnd) - break; - - var pathProgress = d / length; - var timeProgress = reversed ? 1 - pathProgress : pathProgress; - - yield return new SliderEventDescriptor - { - Type = SliderEventType.Tick, - SpanIndex = span, - SpanStartTime = spanStartTime, - Time = spanStartTime + timeProgress * spanDuration, - PathProgress = pathProgress, - }; + // For repeat spans, ticks are returned in reverse-StartTime order, which is undesirable for some rulesets + ticks = ticks.Reverse(); } + foreach (var e in ticks) + yield return e; + if (span < spanCount - 1) { yield return new SliderEventDescriptor @@ -103,6 +98,39 @@ namespace osu.Game.Rulesets.Objects PathProgress = spanCount % 2, }; } + + /// + /// Generates the ticks for a span of the slider. + /// + /// The span index. + /// The start time of the span. + /// The duration of the span. + /// Whether the span is reversed. + /// The length of the path. + /// The distance between each tick. + /// The distance from the end of the path at which ticks are not allowed to be added. + /// A for each tick. If is true, the ticks will be returned in reverse-StartTime order. + private static IEnumerable generateTicks(int spanIndex, double spanStartTime, double spanDuration, bool reversed, double length, double tickDistance, + double minDistanceFromEnd) + { + for (var d = tickDistance; d <= length; d += tickDistance) + { + if (d >= length - minDistanceFromEnd) + break; + + var pathProgress = d / length; + var timeProgress = reversed ? 1 - pathProgress : pathProgress; + + yield return new SliderEventDescriptor + { + Type = SliderEventType.Tick, + SpanIndex = spanIndex, + SpanStartTime = spanStartTime, + Time = spanStartTime + timeProgress * spanDuration, + PathProgress = pathProgress, + }; + } + } } /// From f2b940f930a89ebd14370eff3d231f402579197e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Aug 2019 17:31:08 +0900 Subject: [PATCH 1993/2854] Add tests --- .../OsuBeatmapConversionTest.cs | 2 + .../repeat-slider-expected-conversion.json | 222 +++++++++++ .../Testing/Beatmaps/repeat-slider.osu | 18 + ...ven-repeat-slider-expected-conversion.json | 348 ++++++++++++++++++ .../Testing/Beatmaps/uneven-repeat-slider.osu | 19 + .../Beatmaps/SliderEventGenerationTest.cs | 116 ++++++ 6 files changed, 725 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider-expected-conversion.json create mode 100644 osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider.osu create mode 100644 osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider-expected-conversion.json create mode 100644 osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider.osu create mode 100644 osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs index f98d63e6c7..451ecf97ea 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs @@ -21,6 +21,8 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase("basic")] [TestCase("colinear-perfect-curve")] [TestCase("slider-ticks")] + [TestCase("repeat-slider")] + [TestCase("uneven-repeat-slider")] public new void Test(string name) { base.Test(name); diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider-expected-conversion.json b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider-expected-conversion.json new file mode 100644 index 0000000000..fb4050963f --- /dev/null +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider-expected-conversion.json @@ -0,0 +1,222 @@ +{ + "Mappings": [{ + "StartTime": 369, + "Objects": [{ + "StartTime": 369, + "EndTime": 369, + "X": 177, + "Y": 191 + }, + { + "StartTime": 450, + "EndTime": 450, + "X": 216.539276, + "Y": 191.192871 + }, + { + "StartTime": 532, + "EndTime": 532, + "X": 256.5667, + "Y": 191.388138 + }, + { + "StartTime": 614, + "EndTime": 614, + "X": 296.594116, + "Y": 191.583389 + }, + { + "StartTime": 696, + "EndTime": 696, + "X": 336.621521, + "Y": 191.778641 + }, + { + "StartTime": 778, + "EndTime": 778, + "X": 376.648926, + "Y": 191.9739 + }, + { + "StartTime": 860, + "EndTime": 860, + "X": 337.318878, + "Y": 191.782043 + }, + { + "StartTime": 942, + "EndTime": 942, + "X": 297.291443, + "Y": 191.586792 + }, + { + "StartTime": 1024, + "EndTime": 1024, + "X": 257.264038, + "Y": 191.391541 + }, + { + "StartTime": 1106, + "EndTime": 1106, + "X": 217.2366, + "Y": 191.196274 + }, + { + "StartTime": 1188, + "EndTime": 1188, + "X": 177.209213, + "Y": 191.001022 + }, + { + "StartTime": 1270, + "EndTime": 1270, + "X": 216.818192, + "Y": 191.194229 + }, + { + "StartTime": 1352, + "EndTime": 1352, + "X": 256.8456, + "Y": 191.3895 + }, + { + "StartTime": 1434, + "EndTime": 1434, + "X": 296.873047, + "Y": 191.584747 + }, + { + "StartTime": 1516, + "EndTime": 1516, + "X": 336.900452, + "Y": 191.78 + }, + { + "StartTime": 1598, + "EndTime": 1598, + "X": 376.927917, + "Y": 191.975266 + }, + { + "StartTime": 1680, + "EndTime": 1680, + "X": 337.039948, + "Y": 191.780685 + }, + { + "StartTime": 1762, + "EndTime": 1762, + "X": 297.0125, + "Y": 191.585434 + }, + { + "StartTime": 1844, + "EndTime": 1844, + "X": 256.9851, + "Y": 191.390167 + }, + { + "StartTime": 1926, + "EndTime": 1926, + "X": 216.957672, + "Y": 191.194916 + }, + { + "StartTime": 2008, + "EndTime": 2008, + "X": 177.069717, + "Y": 191.000336 + }, + { + "StartTime": 2090, + "EndTime": 2090, + "X": 217.097137, + "Y": 191.1956 + }, + { + "StartTime": 2172, + "EndTime": 2172, + "X": 257.124573, + "Y": 191.390854 + }, + { + "StartTime": 2254, + "EndTime": 2254, + "X": 297.152, + "Y": 191.5861 + }, + { + "StartTime": 2336, + "EndTime": 2336, + "X": 337.179443, + "Y": 191.781372 + }, + { + "StartTime": 2418, + "EndTime": 2418, + "X": 376.7884, + "Y": 191.974579 + }, + { + "StartTime": 2500, + "EndTime": 2500, + "X": 336.760956, + "Y": 191.779327 + }, + { + "StartTime": 2582, + "EndTime": 2582, + "X": 296.733643, + "Y": 191.584076 + }, + { + "StartTime": 2664, + "EndTime": 2664, + "X": 256.7062, + "Y": 191.388809 + }, + { + "StartTime": 2746, + "EndTime": 2746, + "X": 216.678772, + "Y": 191.193558 + }, + { + "StartTime": 2828, + "EndTime": 2828, + "X": 177.348663, + "Y": 191.0017 + }, + { + "StartTime": 2909, + "EndTime": 2909, + "X": 216.887909, + "Y": 191.19458 + }, + { + "StartTime": 2991, + "EndTime": 2991, + "X": 256.915344, + "Y": 191.389832 + }, + { + "StartTime": 3073, + "EndTime": 3073, + "X": 296.942749, + "Y": 191.585083 + }, + { + "StartTime": 3155, + "EndTime": 3155, + "X": 336.970184, + "Y": 191.78035 + }, + { + "StartTime": 3201, + "EndTime": 3201, + "X": 376.99762, + "Y": 191.9756 + } + ] + }] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider.osu b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider.osu new file mode 100644 index 0000000000..624d905384 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider.osu @@ -0,0 +1,18 @@ +osu file format v14 + +[General] +StackLeniency: 0.4 +Mode: 0 + +[Difficulty] +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8 +SliderMultiplier:1.6 +SliderTickRate:4 + +[TimingPoints] +369,327.868852459016,4,2,2,32,1,0 + +[HitObjects] +177,191,369,6,0,L|382:192,7,200 \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider-expected-conversion.json b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider-expected-conversion.json new file mode 100644 index 0000000000..12d1645c04 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider-expected-conversion.json @@ -0,0 +1,348 @@ +{ + "Mappings": [{ + "StartTime": 369, + "Objects": [{ + "StartTime": 369, + "EndTime": 369, + "X": 127, + "Y": 194 + }, + { + "StartTime": 450, + "EndTime": 450, + "X": 166.53389, + "Y": 193.8691 + }, + { + "StartTime": 532, + "EndTime": 532, + "X": 206.555847, + "Y": 193.736572 + }, + { + "StartTime": 614, + "EndTime": 614, + "X": 246.57782, + "Y": 193.60405 + }, + { + "StartTime": 696, + "EndTime": 696, + "X": 286.5998, + "Y": 193.471527 + }, + { + "StartTime": 778, + "EndTime": 778, + "X": 326.621765, + "Y": 193.339 + }, + { + "StartTime": 860, + "EndTime": 860, + "X": 366.6437, + "Y": 193.206482 + }, + { + "StartTime": 942, + "EndTime": 942, + "X": 406.66568, + "Y": 193.073959 + }, + { + "StartTime": 970, + "EndTime": 970, + "X": 420.331726, + "Y": 193.0287 + }, + { + "StartTime": 997, + "EndTime": 997, + "X": 407.153748, + "Y": 193.072342 + }, + { + "StartTime": 1079, + "EndTime": 1079, + "X": 367.131775, + "Y": 193.204865 + }, + { + "StartTime": 1161, + "EndTime": 1161, + "X": 327.1098, + "Y": 193.337387 + }, + { + "StartTime": 1243, + "EndTime": 1243, + "X": 287.08783, + "Y": 193.46991 + }, + { + "StartTime": 1325, + "EndTime": 1325, + "X": 247.0659, + "Y": 193.602432 + }, + { + "StartTime": 1407, + "EndTime": 1407, + "X": 207.043915, + "Y": 193.734955 + }, + { + "StartTime": 1489, + "EndTime": 1489, + "X": 167.021988, + "Y": 193.867477 + }, + { + "StartTime": 1571, + "EndTime": 1571, + "X": 127, + "Y": 194 + }, + { + "StartTime": 1653, + "EndTime": 1653, + "X": 167.021988, + "Y": 193.867477 + }, + { + "StartTime": 1735, + "EndTime": 1735, + "X": 207.043976, + "Y": 193.734955 + }, + { + "StartTime": 1817, + "EndTime": 1817, + "X": 247.065887, + "Y": 193.602432 + }, + { + "StartTime": 1899, + "EndTime": 1899, + "X": 287.08783, + "Y": 193.46991 + }, + { + "StartTime": 1981, + "EndTime": 1981, + "X": 327.1098, + "Y": 193.337387 + }, + { + "StartTime": 2062, + "EndTime": 2062, + "X": 366.643738, + "Y": 193.206482 + }, + { + "StartTime": 2144, + "EndTime": 2144, + "X": 406.665649, + "Y": 193.073959 + }, + { + "StartTime": 2172, + "EndTime": 2172, + "X": 420.331726, + "Y": 193.0287 + }, + { + "StartTime": 2199, + "EndTime": 2199, + "X": 407.153748, + "Y": 193.072342 + }, + { + "StartTime": 2281, + "EndTime": 2281, + "X": 367.1318, + "Y": 193.204865 + }, + { + "StartTime": 2363, + "EndTime": 2363, + "X": 327.1098, + "Y": 193.337387 + }, + { + "StartTime": 2445, + "EndTime": 2445, + "X": 287.08783, + "Y": 193.46991 + }, + { + "StartTime": 2527, + "EndTime": 2527, + "X": 247.065887, + "Y": 193.602432 + }, + { + "StartTime": 2609, + "EndTime": 2609, + "X": 207.043976, + "Y": 193.734955 + }, + { + "StartTime": 2691, + "EndTime": 2691, + "X": 167.021988, + "Y": 193.867477 + }, + { + "StartTime": 2773, + "EndTime": 2773, + "X": 127, + "Y": 194 + }, + { + "StartTime": 2855, + "EndTime": 2855, + "X": 167.021988, + "Y": 193.867477 + }, + { + "StartTime": 2937, + "EndTime": 2937, + "X": 207.043976, + "Y": 193.734955 + }, + { + "StartTime": 3019, + "EndTime": 3019, + "X": 247.065948, + "Y": 193.602432 + }, + { + "StartTime": 3101, + "EndTime": 3101, + "X": 287.087952, + "Y": 193.46991 + }, + { + "StartTime": 3183, + "EndTime": 3183, + "X": 327.109772, + "Y": 193.337387 + }, + { + "StartTime": 3265, + "EndTime": 3265, + "X": 367.131775, + "Y": 193.204865 + }, + { + "StartTime": 3347, + "EndTime": 3347, + "X": 407.153748, + "Y": 193.072342 + }, + { + "StartTime": 3374, + "EndTime": 3374, + "X": 420.331726, + "Y": 193.0287 + }, + { + "StartTime": 3401, + "EndTime": 3401, + "X": 407.153748, + "Y": 193.072342 + }, + { + "StartTime": 3483, + "EndTime": 3483, + "X": 367.131775, + "Y": 193.204865 + }, + { + "StartTime": 3565, + "EndTime": 3565, + "X": 327.109772, + "Y": 193.337387 + }, + { + "StartTime": 3647, + "EndTime": 3647, + "X": 287.087952, + "Y": 193.46991 + }, + { + "StartTime": 3729, + "EndTime": 3729, + "X": 247.065948, + "Y": 193.602432 + }, + { + "StartTime": 3811, + "EndTime": 3811, + "X": 207.043976, + "Y": 193.734955 + }, + { + "StartTime": 3893, + "EndTime": 3893, + "X": 167.021988, + "Y": 193.867477 + }, + { + "StartTime": 3975, + "EndTime": 3975, + "X": 127, + "Y": 194 + }, + { + "StartTime": 4057, + "EndTime": 4057, + "X": 167.021988, + "Y": 193.867477 + }, + { + "StartTime": 4139, + "EndTime": 4139, + "X": 207.043976, + "Y": 193.734955 + }, + { + "StartTime": 4221, + "EndTime": 4221, + "X": 247.065948, + "Y": 193.602432 + }, + { + "StartTime": 4303, + "EndTime": 4303, + "X": 287.087952, + "Y": 193.46991 + }, + { + "StartTime": 4385, + "EndTime": 4385, + "X": 327.109772, + "Y": 193.337387 + }, + { + "StartTime": 4467, + "EndTime": 4467, + "X": 367.131775, + "Y": 193.204865 + }, + { + "StartTime": 4540, + "EndTime": 4540, + "X": 420.331726, + "Y": 193.0287 + }, + { + "StartTime": 4549, + "EndTime": 4549, + "X": 407.153748, + "Y": 193.072342 + } + ] + }] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider.osu b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider.osu new file mode 100644 index 0000000000..64aeeb0c07 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider.osu @@ -0,0 +1,19 @@ +osu file format v14 + +[General] +StackLeniency: 0.4 +Mode: 0 + +[Difficulty] +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8 +SliderMultiplier:1.6 +SliderTickRate:4 + +[TimingPoints] +369,327.868852459016,4,2,2,32,1,0 + +[HitObjects] +// A slider with an un-even amount of ticks +127,194,369,6,0,L|429:193,7,293.333333333333 diff --git a/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs b/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs new file mode 100644 index 0000000000..9fba0f1668 --- /dev/null +++ b/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs @@ -0,0 +1,116 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Tests.Beatmaps +{ + [TestFixture] + public class SliderEventGenerationTest + { + private const double start_time = 0; + private const double span_duration = 1000; + + [Test] + public void TestSingleSpan() + { + var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, null).ToArray(); + + Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head)); + Assert.That(events[0].Time, Is.EqualTo(start_time)); + + Assert.That(events[1].Type, Is.EqualTo(SliderEventType.Tick)); + Assert.That(events[1].Time, Is.EqualTo(span_duration / 2)); + + Assert.That(events[3].Type, Is.EqualTo(SliderEventType.Tail)); + Assert.That(events[3].Time, Is.EqualTo(span_duration)); + } + + [Test] + public void TestRepeat() + { + var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 2, null).ToArray(); + + Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head)); + Assert.That(events[0].Time, Is.EqualTo(start_time)); + + Assert.That(events[1].Type, Is.EqualTo(SliderEventType.Tick)); + Assert.That(events[1].Time, Is.EqualTo(span_duration / 2)); + + Assert.That(events[2].Type, Is.EqualTo(SliderEventType.Repeat)); + Assert.That(events[2].Time, Is.EqualTo(span_duration)); + + Assert.That(events[3].Type, Is.EqualTo(SliderEventType.Tick)); + Assert.That(events[3].Time, Is.EqualTo(span_duration + span_duration / 2)); + + Assert.That(events[5].Type, Is.EqualTo(SliderEventType.Tail)); + Assert.That(events[5].Time, Is.EqualTo(2 * span_duration)); + } + + [Test] + public void TestNonEvenTicks() + { + var events = SliderEventGenerator.Generate(start_time, span_duration, 1, 300, span_duration, 2, null).ToArray(); + + Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head)); + Assert.That(events[0].Time, Is.EqualTo(start_time)); + + Assert.That(events[1].Type, Is.EqualTo(SliderEventType.Tick)); + Assert.That(events[1].Time, Is.EqualTo(300)); + + Assert.That(events[2].Type, Is.EqualTo(SliderEventType.Tick)); + Assert.That(events[2].Time, Is.EqualTo(600)); + + Assert.That(events[3].Type, Is.EqualTo(SliderEventType.Tick)); + Assert.That(events[3].Time, Is.EqualTo(900)); + + Assert.That(events[4].Type, Is.EqualTo(SliderEventType.Repeat)); + Assert.That(events[4].Time, Is.EqualTo(span_duration)); + + Assert.That(events[5].Type, Is.EqualTo(SliderEventType.Tick)); + Assert.That(events[5].Time, Is.EqualTo(1100)); + + Assert.That(events[6].Type, Is.EqualTo(SliderEventType.Tick)); + Assert.That(events[6].Time, Is.EqualTo(1400)); + + Assert.That(events[7].Type, Is.EqualTo(SliderEventType.Tick)); + Assert.That(events[7].Time, Is.EqualTo(1700)); + + Assert.That(events[9].Type, Is.EqualTo(SliderEventType.Tail)); + Assert.That(events[9].Time, Is.EqualTo(2 * span_duration)); + } + + [Test] + public void TestLegacyLastTickOffset() + { + var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, 100).ToArray(); + + Assert.That(events[2].Type, Is.EqualTo(SliderEventType.LegacyLastTick)); + Assert.That(events[2].Time, Is.EqualTo(900)); + } + + [Test] + public void TestMinimumTickDistance() + { + const double velocity = 5; + const double min_distance = velocity * 10; + + var events = SliderEventGenerator.Generate(start_time, span_duration, velocity, velocity, span_duration, 2, 0).ToArray(); + + Assert.Multiple(() => + { + int tickIndex = -1; + + while (++tickIndex < events.Length) + { + if (events[tickIndex].Type != SliderEventType.Tick) + continue; + + Assert.That(events[tickIndex].Time, Is.LessThan(span_duration - min_distance).Or.GreaterThan(span_duration + min_distance)); + } + }); + } + } +} From 0ff1c6184b31768e3aff483b2c603213ae91e6c7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Aug 2019 17:36:20 +0900 Subject: [PATCH 1994/2854] Add generation comment --- osu.Game/Rulesets/Objects/SliderEventGenerator.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs index 9047d338aa..0d8796b4cb 100644 --- a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs +++ b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs @@ -118,6 +118,7 @@ namespace osu.Game.Rulesets.Objects if (d >= length - minDistanceFromEnd) break; + // Always generate ticks from the start of the path rather than the span to ensure that ticks in repeat spans are positioned identically to those in non-repeat spans var pathProgress = d / length; var timeProgress = reversed ? 1 - pathProgress : pathProgress; From 2ae8cba24d8c951bb7d6331b6c7151d1c02e40b4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Aug 2019 17:58:20 +0900 Subject: [PATCH 1995/2854] Add hardrock spinner test --- .../CatchBeatmapConversionTest.cs | 1 + .../hardrock-spinner-expected-conversion.json | 74 +++++++++++++++++++ .../Testing/Beatmaps/hardrock-spinner.osu | 18 +++++ 3 files changed, 93 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner.osu diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index 80ac64c5f9..dd272599ab 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -25,6 +25,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase("slider")] [TestCase("hardrock-stream", new[] { typeof(CatchModHardRock) })] [TestCase("hardrock-repeat-slider", new[] { typeof(CatchModHardRock) })] + [TestCase("hardrock-spinner", new[] { typeof(CatchModHardRock) })] public new void Test(string name, params Type[] mods) { base.Test(name, mods); diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner-expected-conversion.json b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner-expected-conversion.json new file mode 100644 index 0000000000..7333b600fb --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner-expected-conversion.json @@ -0,0 +1,74 @@ +{ + "Mappings": [{ + "StartTime": 369, + "Objects": [{ + "StartTime": 369, + "Position": 65 + }, + { + "StartTime": 450, + "Position": 482 + }, + { + "StartTime": 532, + "Position": 164 + }, + { + "StartTime": 614, + "Position": 315 + }, + { + "StartTime": 696, + "Position": 145 + }, + { + "StartTime": 778, + "Position": 159 + }, + { + "StartTime": 860, + "Position": 310 + }, + { + "StartTime": 942, + "Position": 441 + }, + { + "StartTime": 1024, + "Position": 428 + }, + { + "StartTime": 1106, + "Position": 243 + }, + { + "StartTime": 1188, + "Position": 422 + }, + { + "StartTime": 1270, + "Position": 481 + }, + { + "StartTime": 1352, + "Position": 104 + }, + { + "StartTime": 1434, + "Position": 473 + }, + { + "StartTime": 1516, + "Position": 135 + }, + { + "StartTime": 1598, + "Position": 360 + }, + { + "StartTime": 1680, + "Position": 123 + } + ] + }] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner.osu new file mode 100644 index 0000000000..208d21a344 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner.osu @@ -0,0 +1,18 @@ +osu file format v14 + +[General] +StackLeniency: 0.4 +Mode: 0 + +[Difficulty] +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8 +SliderMultiplier:1.6 +SliderTickRate:4 + +[TimingPoints] +369,327.868852459016,4,2,2,32,1,0 + +[HitObjects] +256,192,369,12,0,1680,0:0:0:0: From e9eea7157d9cf0fa333513a98171ff6b3203e23c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Aug 2019 18:04:57 +0900 Subject: [PATCH 1996/2854] Trim unnecessary file contents --- .../Testing/Beatmaps/hardrock-stream.osu | 59 +------------------ 1 file changed, 1 insertion(+), 58 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream.osu index 62835135df..321af4604b 100644 --- a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream.osu +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream.osu @@ -1,75 +1,18 @@ osu file format v14 [General] -AudioFilename: audio.mp3 -AudioLeadIn: 0 -PreviewTime: 53483 -Countdown: 0 -SampleSet: Soft StackLeniency: 0.4 Mode: 0 -LetterboxInBreaks: 0 -WidescreenStoryboard: 0 - -[Editor] -Bookmarks: 369,1680,12172,22664,33155,43647,54139,64631,75123,85614,96106,103975,114467,124959,135287 -DistanceSpacing: 1 -BeatDivisor: 4 -GridSize: 8 -TimelineZoom: 2.899999 - -[Metadata] -Title:Silhouette -TitleUnicode:シルエット -Artist:KANA-BOON -ArtistUnicode:KANA-BOON -Creator:Nevo -Version:lit120's Hard -Source:NARUTO-ナルト-疾風伝 -Tags:shippuuden shipuuden shippuden nine tails opening 16 kage cut ver version naotoshi nao tomori shunao shogunmoon sotarks pika [[pika]] frostwich lit120 shiratoi armin a_r_m_i_n -BeatmapID:1653115 -BeatmapSetID:780952 [Difficulty] -HPDrainRate:6 CircleSize:4 OverallDifficulty:7 ApproachRate:8 SliderMultiplier:1.6 SliderTickRate:1 -[Events] -//Background and Video events -0,0,"OSpGdEP.png",0,0 -//Break Periods -//Storyboard Layer 0 (Background) -//Storyboard Layer 1 (Fail) -//Storyboard Layer 2 (Pass) -//Storyboard Layer 3 (Foreground) -//Storyboard Sound Samples - [TimingPoints] 369,327.868852459016,4,2,2,32,1,0 -1680,-100,4,2,2,42,0,0 -6926,-100,4,2,2,52,0,0 -11844,-100,4,2,2,5,0,0 -12172,-100,4,2,2,62,0,0 -22664,-100,4,2,2,52,0,0 -43647,-100,4,2,2,52,0,0 -53483,-100,4,2,2,32,0,0 -54139,-100,4,2,2,72,0,1 -96106,-100,4,2,2,52,0,0 -103975,-100,4,2,2,82,0,1 -124959,-100,4,2,2,62,0,0 -130205,-100,4,2,2,72,0,0 -132991,-100,4,2,2,82,0,0 - - -[Colours] -Combo1 : 154,53,255 -Combo2 : 253,211,47 -Combo3 : 86,168,250 -Combo4 : 192,192,192 [HitObjects] 258,189,369,1,0,0:0:0:0: @@ -104,4 +47,4 @@ Combo4 : 192,192,192 258,189,2746,1,0,0:0:0:0: 258,189,2828,1,0,0:0:0:0: 258,189,2909,1,0,0:0:0:0: -258,189,2991,1,0,0:0:0:0: +258,189,2991,1,0,0:0:0:0: \ No newline at end of file From d55875800067ec90f60f55e492a26c2a30dc0ca3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 1 Aug 2019 19:14:07 +0300 Subject: [PATCH 1997/2854] Add PreviousUsernames field to the User --- osu.Game/Users/User.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 34bd4bbaae..22c0a4fcd0 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -20,6 +20,9 @@ namespace osu.Game.Users [JsonProperty(@"username")] public string Username; + [JsonProperty(@"previous_usernames")] + public string[] PreviousUsernames; + [JsonProperty(@"country")] public Country Country; From 90c59ab39d3058189e530ed25e3597284ea27b5b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 1 Aug 2019 21:26:59 +0300 Subject: [PATCH 1998/2854] implement PreviousUsernamesContainer --- .../TestScenePreviousUsernamesContainer.cs | 52 +++++ .../Header/PreviousUsernamesContainer.cs | 179 ++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs create mode 100644 osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs diff --git a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs new file mode 100644 index 0000000000..0469c230d0 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs @@ -0,0 +1,52 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Overlays.Profile.Header; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Online +{ + [TestFixture] + public class TestScenePreviousUsernamesContainer : OsuTestScene + { + public TestScenePreviousUsernamesContainer() + { + Bindable user = new Bindable(); + + Child = new PreviousUsernamesContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + User = { BindTarget = user }, + }; + + AddStep("single username", () => user.Value = new User + { + PreviousUsernames = new[] { "username1" }, + }); + + AddStep("two usernames", () => user.Value = new User + { + PreviousUsernames = new[] { "longusername", "longerusername" }, + }); + + AddStep("three usernames", () => user.Value = new User + { + PreviousUsernames = new[] { "test", "angelsim", "verylongusername" }, + }); + + AddStep("four usernames", () => user.Value = new User + { + PreviousUsernames = new[] { "ihavenoidea", "howcani", "makethistext" , "anylonger" }, + }); + + AddStep("no username", () => user.Value = new User + { + PreviousUsernames = new string[0], + }); + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs b/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs new file mode 100644 index 0000000000..39bd2388a4 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs @@ -0,0 +1,179 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Users; +using osuTK; +using System; +using System.Linq; + +namespace osu.Game.Overlays.Profile.Header +{ + public class PreviousUsernamesContainer : Container + { + private const int duration = 200; + private const int margin = 10; + private const int width = 350; + private const int move_offset = 30; + + public readonly Bindable User = new Bindable(); + + private readonly HoverIconContainer hoverIcon; + private readonly ContentContainer contentContainer; + + public PreviousUsernamesContainer() + { + AutoSizeAxes = Axes.Y; + Width = width; + Children = new Drawable[] + { + contentContainer = new ContentContainer(), + hoverIcon = new HoverIconContainer(), + }; + + hoverIcon.ActivateHover += () => + { + contentContainer.Show(); + this.MoveToY(-move_offset, duration, Easing.OutQuint); + }; + + User.BindValueChanged(onUserChanged); + } + + private void onUserChanged(ValueChangedEvent user) + { + contentContainer.Clear(); + + var usernames = user.NewValue.PreviousUsernames; + + if (usernames.Any()) + { + var amount = usernames.Count(); + + for (int i = 0; i < amount; i++) + { + string text = (i + 1 == amount) ? usernames[i] : $@"{usernames[i]},"; + + contentContainer.Usernames.AddText(new SpriteText + { + Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true), + Text = text, + }); + } + + Show(); + return; + } + + Hide(); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + contentContainer.Hide(); + this.MoveToY(0, duration, Easing.OutQuint); + } + + private class ContentContainer : VisibilityContainer + { + private const int header_height = 25; + private const int content_padding = 60; + + public readonly TextFlowContainer Usernames; + + private readonly Box background; + + public ContentContainer() + { + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; + Masking = true; + AlwaysPresent = true; + CornerRadius = 5; + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + Height = header_height, + Children = new Drawable[] + { + new SpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Padding = new MarginPadding { Left = content_padding }, + Text = @"formerly known as", + Font = OsuFont.GetFont(size: 14, italics: true) + } + } + }, + Usernames = new TextFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Full, + Spacing = new Vector2(5, 5), + Padding = new MarginPadding { Left = content_padding, Bottom = margin }, + }, + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.GreySeafoamDarker; + } + + public override void Clear(bool disposeChildren) => Usernames.Clear(disposeChildren); + + protected override void PopIn() => this.FadeIn(duration, Easing.OutQuint); + + protected override void PopOut() => this.FadeOut(duration, Easing.OutQuint); + } + + private class HoverIconContainer : Container + { + public Action ActivateHover; + + public HoverIconContainer() + { + AutoSizeAxes = Axes.Both; + Child = new SpriteIcon + { + Margin = new MarginPadding(margin) { Top = 7 }, + Size = new Vector2(20), + Icon = FontAwesome.Solid.IdCard, + }; + } + + protected override bool OnHover(HoverEvent e) + { + ActivateHover?.Invoke(); + return base.OnHover(e); + } + } + } +} From 13fe1732d8cf8b76aa689a585ad6c3fe63128119 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 1 Aug 2019 21:32:04 +0300 Subject: [PATCH 1999/2854] adjust height values to avoid random jumps --- .../Overlays/Profile/Header/PreviousUsernamesContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs b/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs index 39bd2388a4..67b9bcb651 100644 --- a/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs +++ b/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Profile.Header private const int duration = 200; private const int margin = 10; private const int width = 350; - private const int move_offset = 30; + private const int move_offset = 20; public readonly Bindable User = new Bindable(); @@ -163,7 +163,7 @@ namespace osu.Game.Overlays.Profile.Header AutoSizeAxes = Axes.Both; Child = new SpriteIcon { - Margin = new MarginPadding(margin) { Top = 7 }, + Margin = new MarginPadding(margin) { Top = 6 }, Size = new Vector2(20), Icon = FontAwesome.Solid.IdCard, }; From e66a3494b9fbd43c9f49d718af1683106d86dc93 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 1 Aug 2019 22:50:01 +0300 Subject: [PATCH 2000/2854] make the container hidden by default --- osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs b/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs index 67b9bcb651..5165f0d0df 100644 --- a/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs +++ b/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs @@ -45,6 +45,7 @@ namespace osu.Game.Overlays.Profile.Header }; User.BindValueChanged(onUserChanged); + Hide(); } private void onUserChanged(ValueChangedEvent user) From 55475927685aee49e5686899d6898ff8093fc7d4 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 1 Aug 2019 23:04:18 +0300 Subject: [PATCH 2001/2854] CI fixes --- .../Visual/Online/TestScenePreviousUsernamesContainer.cs | 2 +- .../Overlays/Profile/Header/PreviousUsernamesContainer.cs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs index 0469c230d0..a33de887d7 100644 --- a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("four usernames", () => user.Value = new User { - PreviousUsernames = new[] { "ihavenoidea", "howcani", "makethistext" , "anylonger" }, + PreviousUsernames = new[] { "ihavenoidea", "howcani", "makethistext", "anylonger" }, }); AddStep("no username", () => user.Value = new User diff --git a/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs b/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs index 5165f0d0df..1c98cff616 100644 --- a/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs +++ b/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs @@ -25,11 +25,12 @@ namespace osu.Game.Overlays.Profile.Header public readonly Bindable User = new Bindable(); - private readonly HoverIconContainer hoverIcon; private readonly ContentContainer contentContainer; public PreviousUsernamesContainer() { + HoverIconContainer hoverIcon; + AutoSizeAxes = Axes.Y; Width = width; Children = new Drawable[] @@ -56,7 +57,7 @@ namespace osu.Game.Overlays.Profile.Header if (usernames.Any()) { - var amount = usernames.Count(); + var amount = usernames.Length; for (int i = 0; i < amount; i++) { From de96e5dfc605f559269a2e045652ab2453267d2d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 2 Aug 2019 07:41:11 +0300 Subject: [PATCH 2002/2854] Apply suggested changes --- .../TestScenePreviousUsernamesContainer.cs | 5 ++ .../Header/PreviousUsernamesContainer.cs | 58 +++++++++---------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs index a33de887d7..4f3a2e47c6 100644 --- a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs @@ -43,6 +43,11 @@ namespace osu.Game.Tests.Visual.Online PreviousUsernames = new[] { "ihavenoidea", "howcani", "makethistext", "anylonger" }, }); + AddStep("many usernames", () => user.Value = new User + { + PreviousUsernames = new[] { "ihavenoidea", "howcani", "makethistext", "anylonger", "but", "ican", "try", "tomake", "this" }, + }); + AddStep("no username", () => user.Value = new User { PreviousUsernames = new string[0], diff --git a/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs b/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs index 1c98cff616..e8443dff1b 100644 --- a/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs +++ b/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs @@ -16,12 +16,12 @@ using System.Linq; namespace osu.Game.Overlays.Profile.Header { - public class PreviousUsernamesContainer : Container + public class PreviousUsernamesContainer : CompositeDrawable { private const int duration = 200; private const int margin = 10; - private const int width = 350; - private const int move_offset = 20; + private const int width = 310; + private const int move_offset = 15; public readonly Bindable User = new Bindable(); @@ -33,43 +33,35 @@ namespace osu.Game.Overlays.Profile.Header AutoSizeAxes = Axes.Y; Width = width; - Children = new Drawable[] + + AddRangeInternal(new Drawable[] { contentContainer = new ContentContainer(), hoverIcon = new HoverIconContainer(), - }; + }); hoverIcon.ActivateHover += () => { contentContainer.Show(); this.MoveToY(-move_offset, duration, Easing.OutQuint); }; + } - User.BindValueChanged(onUserChanged); - Hide(); + protected override void LoadComplete() + { + base.LoadComplete(); + User.BindValueChanged(onUserChanged, true); } private void onUserChanged(ValueChangedEvent user) { - contentContainer.Clear(); + contentContainer.Text = string.Empty; - var usernames = user.NewValue.PreviousUsernames; + var usernames = user.NewValue?.PreviousUsernames; - if (usernames.Any()) + if (usernames?.Any() ?? false) { - var amount = usernames.Length; - - for (int i = 0; i < amount; i++) - { - string text = (i + 1 == amount) ? usernames[i] : $@"{usernames[i]},"; - - contentContainer.Usernames.AddText(new SpriteText - { - Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true), - Text = text, - }); - } - + contentContainer.Text = string.Join(", ", usernames); Show(); return; } @@ -86,11 +78,15 @@ namespace osu.Game.Overlays.Profile.Header private class ContentContainer : VisibilityContainer { - private const int header_height = 25; - private const int content_padding = 60; + private const int header_height = 20; + private const int content_padding = 40; - public readonly TextFlowContainer Usernames; + public string Text + { + set => usernames.Text = value; + } + private readonly TextFlowContainer usernames; private readonly Box background; public ContentContainer() @@ -126,16 +122,16 @@ namespace osu.Game.Overlays.Profile.Header Origin = Anchor.BottomLeft, Padding = new MarginPadding { Left = content_padding }, Text = @"formerly known as", - Font = OsuFont.GetFont(size: 14, italics: true) + Font = OsuFont.GetFont(size: 10, italics: true) } } }, - Usernames = new TextFlowContainer + usernames = new TextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold, italics: true)) { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Full, - Spacing = new Vector2(5, 5), + Spacing = new Vector2(0, 5), Padding = new MarginPadding { Left = content_padding, Bottom = margin }, }, } @@ -149,8 +145,6 @@ namespace osu.Game.Overlays.Profile.Header background.Colour = colours.GreySeafoamDarker; } - public override void Clear(bool disposeChildren) => Usernames.Clear(disposeChildren); - protected override void PopIn() => this.FadeIn(duration, Easing.OutQuint); protected override void PopOut() => this.FadeOut(duration, Easing.OutQuint); @@ -166,7 +160,7 @@ namespace osu.Game.Overlays.Profile.Header Child = new SpriteIcon { Margin = new MarginPadding(margin) { Top = 6 }, - Size = new Vector2(20), + Size = new Vector2(15), Icon = FontAwesome.Solid.IdCard, }; } From 4c0a9aeab701381ad602cde155b636327ba979f7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 2 Aug 2019 07:44:09 +0300 Subject: [PATCH 2003/2854] Add null user step --- .../Visual/Online/TestScenePreviousUsernamesContainer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs index 4f3a2e47c6..4a3b3403b2 100644 --- a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs @@ -52,6 +52,8 @@ namespace osu.Game.Tests.Visual.Online { PreviousUsernames = new string[0], }); + + AddStep("null user", () => user.Value = null); } } } From ba3f8a643a899f268d75882af1c44ba045f13180 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 2 Aug 2019 14:16:57 +0900 Subject: [PATCH 2004/2854] Update cake --- build/cakebuild.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/cakebuild.csproj b/build/cakebuild.csproj index 8ccce35e26..d5a6d44740 100644 --- a/build/cakebuild.csproj +++ b/build/cakebuild.csproj @@ -5,7 +5,7 @@ netcoreapp2.0 - - + + \ No newline at end of file From e84c79d14051ecb220429c38cdb321e53f9a0b24 Mon Sep 17 00:00:00 2001 From: Joehu Date: Fri, 2 Aug 2019 13:16:33 -0700 Subject: [PATCH 2005/2854] Update osu!direct categories sorting with web changes --- .../API/Requests/SearchBeatmapSetsRequest.cs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index d9d05ff285..c8c36789c4 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -27,24 +27,25 @@ namespace osu.Game.Online.API.Requests } // ReSharper disable once ImpureMethodCallOnReadonlyValueField - protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)searchCategory}&sort={sortCriteria.ToString().ToLowerInvariant()}_{directionString}"; + protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={searchCategory.ToString().ToLowerInvariant()}&sort={sortCriteria.ToString().ToLowerInvariant()}_{directionString}"; } public enum BeatmapSearchCategory { - Any = 7, + Any, - [Description("Ranked & Approved")] - RankedApproved = 0, - Qualified = 3, - Loved = 8, - Favourites = 2, + [Description("Has Leaderboard")] + Leaderboard, + Ranked, + Qualified, + Loved, + Favourites, [Description("Pending & WIP")] - PendingWIP = 4, - Graveyard = 5, + Pending, + Graveyard, [Description("My Maps")] - MyMaps = 6, + Mine, } } From 0082695cd827a574e91a0c8be4d0bd8da1288e8e Mon Sep 17 00:00:00 2001 From: Joehu Date: Fri, 2 Aug 2019 13:22:58 -0700 Subject: [PATCH 2006/2854] Choose default category sorting instead of being always first --- osu.Game/Overlays/Direct/FilterControl.cs | 1 + .../Overlays/SearchableList/SearchableListFilterControl.cs | 4 ++++ osu.Game/Overlays/Social/FilterControl.cs | 1 + osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs | 1 + 4 files changed, 7 insertions(+) diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index 4b43542b43..8b04bf0387 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -18,6 +18,7 @@ namespace osu.Game.Overlays.Direct protected override Color4 BackgroundColour => OsuColour.FromHex(@"384552"); protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked; + protected override BeatmapSearchCategory DefaultCategory => BeatmapSearchCategory.Leaderboard; protected override Drawable CreateSupplementaryControls() => rulesetSelector = new DirectRulesetSelector(); diff --git a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs index b0a8a0e77d..a0c4e9a080 100644 --- a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs +++ b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs @@ -26,6 +26,7 @@ namespace osu.Game.Overlays.SearchableList protected abstract Color4 BackgroundColour { get; } protected abstract T DefaultTab { get; } + protected abstract U DefaultCategory { get; } protected virtual Drawable CreateSupplementaryControls() => null; /// @@ -109,6 +110,9 @@ namespace osu.Game.Overlays.SearchableList Tabs.Current.Value = DefaultTab; Tabs.Current.TriggerChange(); + + DisplayStyleControl.Dropdown.Current.Value = DefaultCategory; + DisplayStyleControl.Dropdown.Current.TriggerChange(); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Social/FilterControl.cs b/osu.Game/Overlays/Social/FilterControl.cs index 6abf6ec98d..1c2cb95dfe 100644 --- a/osu.Game/Overlays/Social/FilterControl.cs +++ b/osu.Game/Overlays/Social/FilterControl.cs @@ -12,6 +12,7 @@ namespace osu.Game.Overlays.Social { protected override Color4 BackgroundColour => OsuColour.FromHex(@"47253a"); protected override SocialSortCriteria DefaultTab => SocialSortCriteria.Rank; + protected override SortDirection DefaultCategory => SortDirection.Ascending; public FilterControl() { diff --git a/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs b/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs index 8e14f76e4b..d0d983bbff 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs @@ -14,6 +14,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components { protected override Color4 BackgroundColour => OsuColour.FromHex(@"362e42"); protected override PrimaryFilter DefaultTab => PrimaryFilter.Open; + protected override SecondaryFilter DefaultCategory => SecondaryFilter.Public; protected override float ContentHorizontalPadding => base.ContentHorizontalPadding + OsuScreen.HORIZONTAL_OVERFLOW_PADDING; From f81238b8b18b38546615c2992df68ca35a19baa8 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 3 Aug 2019 04:45:41 +0300 Subject: [PATCH 2007/2854] Add online test --- .../TestScenePreviousUsernamesContainer.cs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs index 4a3b3403b2..dafc1870e4 100644 --- a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs @@ -2,8 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Overlays.Profile.Header; using osu.Game.Users; @@ -12,10 +15,13 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestScenePreviousUsernamesContainer : OsuTestScene { + [Resolved] + private IAPIProvider api { get; set; } + + private readonly Bindable user = new Bindable(); + public TestScenePreviousUsernamesContainer() { - Bindable user = new Bindable(); - Child = new PreviousUsernamesContainer { Anchor = Anchor.Centre, @@ -55,5 +61,17 @@ namespace osu.Game.Tests.Visual.Online AddStep("null user", () => user.Value = null); } + + protected override void LoadComplete() + { + base.LoadComplete(); + + AddStep("online user (Angelsim)", () => + { + var request = new GetUserRequest(1777162); + request.Success += user => this.user.Value = user; + api.Queue(request); + }); + } } } From 37be4fbf1622d0f80271b5400a497b0bfdfb9984 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 3 Aug 2019 05:34:14 +0300 Subject: [PATCH 2008/2854] Use GridContainer for layout --- .../TestScenePreviousUsernamesContainer.cs | 43 ++--- .../Header/PreviousUsernamesContainer.cs | 159 +++++++++--------- 2 files changed, 90 insertions(+), 112 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs index dafc1870e4..5636f8756d 100644 --- a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs @@ -29,37 +29,22 @@ namespace osu.Game.Tests.Visual.Online User = { BindTarget = user }, }; - AddStep("single username", () => user.Value = new User + User[] users = new[] { - PreviousUsernames = new[] { "username1" }, - }); + new User { PreviousUsernames = new[] { "username1" } }, + new User { PreviousUsernames = new[] { "longusername", "longerusername" } }, + new User { PreviousUsernames = new[] { "test", "angelsim", "verylongusername" } }, + new User { PreviousUsernames = new[] { "ihavenoidea", "howcani", "makethistext", "anylonger" } }, + new User { PreviousUsernames = new string[0] }, + null + }; - AddStep("two usernames", () => user.Value = new User - { - PreviousUsernames = new[] { "longusername", "longerusername" }, - }); - - AddStep("three usernames", () => user.Value = new User - { - PreviousUsernames = new[] { "test", "angelsim", "verylongusername" }, - }); - - AddStep("four usernames", () => user.Value = new User - { - PreviousUsernames = new[] { "ihavenoidea", "howcani", "makethistext", "anylonger" }, - }); - - AddStep("many usernames", () => user.Value = new User - { - PreviousUsernames = new[] { "ihavenoidea", "howcani", "makethistext", "anylonger", "but", "ican", "try", "tomake", "this" }, - }); - - AddStep("no username", () => user.Value = new User - { - PreviousUsernames = new string[0], - }); - - AddStep("null user", () => user.Value = null); + AddStep("single username", () => user.Value = users[0]); + AddStep("two usernames", () => user.Value = users[1]); + AddStep("three usernames", () => user.Value = users[2]); + AddStep("four usernames", () => user.Value = users[3]); + AddStep("no username", () => user.Value = users[4]); + AddStep("null user", () => user.Value = users[5]); } protected override void LoadComplete() diff --git a/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs b/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs index e8443dff1b..36034fe8db 100644 --- a/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs +++ b/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs @@ -25,7 +25,9 @@ namespace osu.Game.Overlays.Profile.Header public readonly Bindable User = new Bindable(); - private readonly ContentContainer contentContainer; + private readonly TextFlowContainer text; + private readonly Box background; + private readonly SpriteText header; public PreviousUsernamesContainer() { @@ -33,18 +35,68 @@ namespace osu.Game.Overlays.Profile.Header AutoSizeAxes = Axes.Y; Width = width; + Masking = true; + CornerRadius = 5; AddRangeInternal(new Drawable[] { - contentContainer = new ContentContainer(), - hoverIcon = new HoverIconContainer(), + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new GridContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize) + }, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Distributed) + }, + Content = new[] + { + new Drawable[] + { + hoverIcon = new HoverIconContainer(), + header = new SpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Text = @"formerly known as", + Font = OsuFont.GetFont(size: 10, italics: true) + } + }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + }, + text = new TextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold, italics: true)) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Full, + Margin = new MarginPadding { Bottom = margin, Top = margin / 2 } + } + } + } + } }); - hoverIcon.ActivateHover += () => - { - contentContainer.Show(); - this.MoveToY(-move_offset, duration, Easing.OutQuint); - }; + hoverIcon.ActivateHover += showContent; + hideContent(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.GreySeafoamDarker; } protected override void LoadComplete() @@ -55,13 +107,13 @@ namespace osu.Game.Overlays.Profile.Header private void onUserChanged(ValueChangedEvent user) { - contentContainer.Text = string.Empty; + text.Text = string.Empty; var usernames = user.NewValue?.PreviousUsernames; if (usernames?.Any() ?? false) { - contentContainer.Text = string.Join(", ", usernames); + text.Text = string.Join(", ", usernames); Show(); return; } @@ -72,82 +124,23 @@ namespace osu.Game.Overlays.Profile.Header protected override void OnHoverLost(HoverLostEvent e) { base.OnHoverLost(e); - contentContainer.Hide(); - this.MoveToY(0, duration, Easing.OutQuint); + hideContent(); } - private class ContentContainer : VisibilityContainer + private void showContent() { - private const int header_height = 20; - private const int content_padding = 40; + text.FadeIn(duration, Easing.OutQuint); + header.FadeIn(duration, Easing.OutQuint); + background.FadeIn(duration, Easing.OutQuint); + this.MoveToY(-move_offset, duration, Easing.OutQuint); + } - public string Text - { - set => usernames.Text = value; - } - - private readonly TextFlowContainer usernames; - private readonly Box background; - - public ContentContainer() - { - AutoSizeAxes = Axes.Y; - RelativeSizeAxes = Axes.X; - Masking = true; - AlwaysPresent = true; - CornerRadius = 5; - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - Height = header_height, - Children = new Drawable[] - { - new SpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Padding = new MarginPadding { Left = content_padding }, - Text = @"formerly known as", - Font = OsuFont.GetFont(size: 10, italics: true) - } - } - }, - usernames = new TextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold, italics: true)) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Full, - Spacing = new Vector2(0, 5), - Padding = new MarginPadding { Left = content_padding, Bottom = margin }, - }, - } - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - background.Colour = colours.GreySeafoamDarker; - } - - protected override void PopIn() => this.FadeIn(duration, Easing.OutQuint); - - protected override void PopOut() => this.FadeOut(duration, Easing.OutQuint); + private void hideContent() + { + text.FadeOut(duration, Easing.OutQuint); + header.FadeOut(duration, Easing.OutQuint); + background.FadeOut(duration, Easing.OutQuint); + this.MoveToY(0, duration, Easing.OutQuint); } private class HoverIconContainer : Container @@ -159,7 +152,7 @@ namespace osu.Game.Overlays.Profile.Header AutoSizeAxes = Axes.Both; Child = new SpriteIcon { - Margin = new MarginPadding(margin) { Top = 6 }, + Margin = new MarginPadding { Top = 6, Left = margin, Right = margin * 2 }, Size = new Vector2(15), Icon = FontAwesome.Solid.IdCard, }; From 416f9d89dba59a8cc85e020e643506f4f02b1b44 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 3 Aug 2019 05:49:01 +0300 Subject: [PATCH 2009/2854] CI fixes --- .../Visual/Online/TestScenePreviousUsernamesContainer.cs | 2 +- osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs index 5636f8756d..b891fda0f4 100644 --- a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual.Online User = { BindTarget = user }, }; - User[] users = new[] + User[] users = { new User { PreviousUsernames = new[] { "username1" } }, new User { PreviousUsernames = new[] { "longusername", "longerusername" } }, diff --git a/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs b/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs index 36034fe8db..b53ac8eb80 100644 --- a/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs +++ b/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs @@ -82,7 +82,7 @@ namespace osu.Game.Overlays.Profile.Header RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Full, - Margin = new MarginPadding { Bottom = margin, Top = margin / 2 } + Margin = new MarginPadding { Bottom = margin, Top = margin / 2f } } } } From b244b2fe3d378f7050ba3f8dc9f88a098a98b776 Mon Sep 17 00:00:00 2001 From: Joehu Date: Fri, 2 Aug 2019 21:13:29 -0700 Subject: [PATCH 2010/2854] Add hover click sounds to leaderboard mod filter --- osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs index d158186899..544acc7eb2 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs @@ -81,7 +81,8 @@ namespace osu.Game.Graphics.UserInterface Colour = Color4.White, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, - } + }, + new HoverClickSounds() }; Current.ValueChanged += selected => From c8dd29067b2f44354a624bf67567b6f382bd1bd5 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sun, 4 Aug 2019 00:34:51 +0300 Subject: [PATCH 2011/2854] Update Entity Framework Core --- osu.Desktop/osu.Desktop.csproj | 4 ++-- osu.Game/osu.Game.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 8c9e1f279f..538aaf2d7a 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -28,8 +28,8 @@ - - + + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 1f91ce1cd8..cf325b77be 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -11,8 +11,8 @@ - - + + From 115cf47ed5e8a697c25b24395094f076b0a47955 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sat, 3 Aug 2019 15:20:44 -0700 Subject: [PATCH 2012/2854] Fix settings sidebar showing scrollbar at max ui scale --- osu.Game/Overlays/Settings/Sidebar.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Settings/Sidebar.cs b/osu.Game/Overlays/Settings/Sidebar.cs index a80b7c1108..358f94b659 100644 --- a/osu.Game/Overlays/Settings/Sidebar.cs +++ b/osu.Game/Overlays/Settings/Sidebar.cs @@ -84,6 +84,7 @@ namespace osu.Game.Overlays.Settings Content.Anchor = Anchor.CentreLeft; Content.Origin = Anchor.CentreLeft; RelativeSizeAxes = Axes.Both; + ScrollbarVisible = false; } } From f6f96658c7b7a51f327702d9a612806af0484da2 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sat, 3 Aug 2019 15:22:06 -0700 Subject: [PATCH 2013/2854] Fix settings sidebar being behind toolbar --- osu.Game/Overlays/SettingsPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 474f529bb1..9dd0def453 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -186,7 +186,7 @@ namespace osu.Game.Overlays base.UpdateAfterChildren(); ContentContainer.Margin = new MarginPadding { Left = Sidebar?.DrawWidth ?? 0 }; - ContentContainer.Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; + Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; } protected class SettingsSectionsContainer : SectionsContainer From c8acbdb1d9600db601c9967f0d2e18b15584be80 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 4 Aug 2019 06:15:15 +0300 Subject: [PATCH 2014/2854] Update the colour --- osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs index c5e61f68f4..fa60a37ddb 100644 --- a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs @@ -80,7 +80,6 @@ namespace osu.Game.Overlays.Profile.Header.Components private void load(OsuColour colours) { background.Colour = colours.Pink; - iconContainer.Colour = colours.GreySeafoam; } } } From a30d7912b1b34cb0d06b013683143994549b7f57 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 4 Aug 2019 11:09:12 +0300 Subject: [PATCH 2015/2854] Move DimmedLoadingAnimation to it's own file --- .../UserInterface/DimmedLoadingAnimation.cs | 44 +++++++++++++++++++ osu.Game/Screens/Select/BeatmapDetails.cs | 35 +-------------- 2 files changed, 45 insertions(+), 34 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/DimmedLoadingAnimation.cs diff --git a/osu.Game/Graphics/UserInterface/DimmedLoadingAnimation.cs b/osu.Game/Graphics/UserInterface/DimmedLoadingAnimation.cs new file mode 100644 index 0000000000..30c8c5f143 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/DimmedLoadingAnimation.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osuTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Extensions.Color4Extensions; + +namespace osu.Game.Graphics.UserInterface +{ + public class DimmedLoadingAnimation : VisibilityContainer + { + private const float transition_duration = 250; + + private readonly LoadingAnimation loading; + + public DimmedLoadingAnimation() + { + RelativeSizeAxes = Axes.Both; + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.5f), + }, + loading = new LoadingAnimation(), + }; + } + + protected override void PopIn() + { + this.FadeIn(transition_duration, Easing.OutQuint); + loading.Show(); + } + + protected override void PopOut() + { + this.FadeOut(transition_duration, Easing.OutQuint); + loading.Hide(); + } + } +} diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 23dd87d8ea..9d2e6f1719 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -156,10 +156,7 @@ namespace osu.Game.Screens.Select }, }, }, - loading = new DimmedLoadingAnimation - { - RelativeSizeAxes = Axes.Both, - }, + loading = new DimmedLoadingAnimation(), }; } @@ -365,35 +362,5 @@ namespace osu.Game.Screens.Select }); } } - - private class DimmedLoadingAnimation : VisibilityContainer - { - private readonly LoadingAnimation loading; - - public DimmedLoadingAnimation() - { - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f), - }, - loading = new LoadingAnimation(), - }; - } - - protected override void PopIn() - { - this.FadeIn(transition_duration, Easing.OutQuint); - loading.Show(); - } - - protected override void PopOut() - { - this.FadeOut(transition_duration, Easing.OutQuint); - loading.Hide(); - } - } } } From fd44ca3233a90207122b0ae16d9606bf09fa9755 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 4 Aug 2019 13:54:23 +0300 Subject: [PATCH 2016/2854] Rename Animation to Layer --- .../{DimmedLoadingAnimation.cs => DimmedLoadingLayer.cs} | 4 ++-- osu.Game/Screens/Select/BeatmapDetails.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename osu.Game/Graphics/UserInterface/{DimmedLoadingAnimation.cs => DimmedLoadingLayer.cs} (91%) diff --git a/osu.Game/Graphics/UserInterface/DimmedLoadingAnimation.cs b/osu.Game/Graphics/UserInterface/DimmedLoadingLayer.cs similarity index 91% rename from osu.Game/Graphics/UserInterface/DimmedLoadingAnimation.cs rename to osu.Game/Graphics/UserInterface/DimmedLoadingLayer.cs index 30c8c5f143..b7d2222f33 100644 --- a/osu.Game/Graphics/UserInterface/DimmedLoadingAnimation.cs +++ b/osu.Game/Graphics/UserInterface/DimmedLoadingLayer.cs @@ -9,13 +9,13 @@ using osu.Framework.Extensions.Color4Extensions; namespace osu.Game.Graphics.UserInterface { - public class DimmedLoadingAnimation : VisibilityContainer + public class DimmedLoadingLayer : VisibilityContainer { private const float transition_duration = 250; private readonly LoadingAnimation loading; - public DimmedLoadingAnimation() + public DimmedLoadingLayer() { RelativeSizeAxes = Axes.Both; Children = new Drawable[] diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 9d2e6f1719..577d999388 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Select private readonly MetadataSection description, source, tags; private readonly Container failRetryContainer; private readonly FailRetryGraph failRetryGraph; - private readonly DimmedLoadingAnimation loading; + private readonly DimmedLoadingLayer loading; private IAPIProvider api; @@ -156,7 +156,7 @@ namespace osu.Game.Screens.Select }, }, }, - loading = new DimmedLoadingAnimation(), + loading = new DimmedLoadingLayer(), }; } From 3ae5428dad6e81dc0f7d5e3043247063774ee2d2 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 4 Aug 2019 14:15:16 +0300 Subject: [PATCH 2017/2854] ProfileRulesetSelector improvements --- .../Online/TestSceneProfileRulesetSelector.cs | 8 +++++- .../Components/ProfileRulesetSelector.cs | 28 ++++++++++--------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs index c344cb9598..d439e6a7c3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs @@ -9,6 +9,7 @@ using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Taiko; +using osu.Game.Users; namespace osu.Game.Tests.Visual.Online { @@ -34,7 +35,12 @@ namespace osu.Game.Tests.Visual.Online AddStep("set mania as default", () => selector.SetDefaultRuleset(new ManiaRuleset().RulesetInfo)); AddStep("set taiko as default", () => selector.SetDefaultRuleset(new TaikoRuleset().RulesetInfo)); AddStep("set catch as default", () => selector.SetDefaultRuleset(new CatchRuleset().RulesetInfo)); - AddStep("select default ruleset", selector.SelectDefaultRuleset); + + AddStep("User with osu as default", () => selector.User.Value = new User { PlayMode = "osu" }); + AddStep("User with mania as default", () => selector.User.Value = new User { PlayMode = "mania" }); + AddStep("User with taiko as default", () => selector.User.Value = new User { PlayMode = "taiko" }); + AddStep("User with catch as default", () => selector.User.Value = new User { PlayMode = "fruits" }); + AddStep("null user", () => selector.User.Value = null); } } } diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs index b6112a6501..09bc2f2cdc 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs @@ -2,11 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Rulesets; +using osu.Game.Users; using osuTK; using osuTK.Graphics; @@ -16,6 +18,8 @@ namespace osu.Game.Overlays.Profile.Header.Components { private Color4 accentColour = Color4.White; + public readonly Bindable User = new Bindable(); + public ProfileRulesetSelector() { TabContainer.Masking = false; @@ -32,24 +36,22 @@ namespace osu.Game.Overlays.Profile.Header.Components ((ProfileRulesetTabItem)tabItem).AccentColour = accentColour; } - public void SetDefaultRuleset(RulesetInfo ruleset) + protected override void LoadComplete() { - // Todo: This method shouldn't exist, but bindables don't provide the concept of observing a change to the default value - foreach (TabItem tabItem in TabContainer) - ((ProfileRulesetTabItem)tabItem).IsDefault = ((ProfileRulesetTabItem)tabItem).Value.ID == ruleset.ID; + base.LoadComplete(); + + User.BindValueChanged(onUserChanged, true); } - public void SelectDefaultRuleset() + private void onUserChanged(ValueChangedEvent user) + { + SetDefaultRuleset(Rulesets.GetRuleset(user.NewValue?.PlayMode ?? "osu")); + } + + public void SetDefaultRuleset(RulesetInfo ruleset) { - // Todo: This method shouldn't exist, but bindables don't provide the concept of observing a change to the default value foreach (TabItem tabItem in TabContainer) - { - if (((ProfileRulesetTabItem)tabItem).IsDefault) - { - Current.Value = ((ProfileRulesetTabItem)tabItem).Value; - return; - } - } + ((ProfileRulesetTabItem)tabItem).IsDefault = ((ProfileRulesetTabItem)tabItem).Value.ID == ruleset.ID; } protected override TabItem CreateTabItem(RulesetInfo value) => new ProfileRulesetTabItem(value) From d693a54c84eb088a31fc9ebefcc9d7d811db60d1 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 4 Aug 2019 14:35:26 +0300 Subject: [PATCH 2018/2854] Move RankHistoryData to User Statistics --- .../Visual/Online/TestSceneRankGraph.cs | 47 +++++++------------ .../Online/TestSceneUserProfileOverlay.cs | 12 ++--- .../Profile/Header/Components/RankGraph.cs | 19 +++++--- .../Profile/Header/DetailHeaderContainer.cs | 2 +- osu.Game/Users/User.cs | 5 +- osu.Game/Users/UserStatistics.cs | 3 ++ 6 files changed, 42 insertions(+), 46 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs b/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs index 709e75ab13..c70cc4ae4e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs @@ -69,28 +69,22 @@ namespace osu.Game.Tests.Visual.Online } }); - AddStep("null user", () => graph.User.Value = null); + AddStep("null user", () => graph.Statistics.Value = null); AddStep("rank only", () => { - graph.User.Value = new User + graph.Statistics.Value = new UserStatistics { - Statistics = new UserStatistics - { - Ranks = new UserStatistics.UserRanks { Global = 123456 }, - PP = 12345, - } + Ranks = new UserStatistics.UserRanks { Global = 123456 }, + PP = 12345, }; }); AddStep("with rank history", () => { - graph.User.Value = new User + graph.Statistics.Value = new UserStatistics { - Statistics = new UserStatistics - { - Ranks = new UserStatistics.UserRanks { Global = 89000 }, - PP = 12345, - }, + Ranks = new UserStatistics.UserRanks { Global = 89000 }, + PP = 12345, RankHistory = new User.RankHistoryData { Data = data, @@ -100,13 +94,10 @@ namespace osu.Game.Tests.Visual.Online AddStep("with zero values", () => { - graph.User.Value = new User + graph.Statistics.Value = new UserStatistics { - Statistics = new UserStatistics - { - Ranks = new UserStatistics.UserRanks { Global = 89000 }, - PP = 12345, - }, + Ranks = new UserStatistics.UserRanks { Global = 89000 }, + PP = 12345, RankHistory = new User.RankHistoryData { Data = dataWithZeros, @@ -116,13 +107,10 @@ namespace osu.Game.Tests.Visual.Online AddStep("small amount of data", () => { - graph.User.Value = new User + graph.Statistics.Value = new UserStatistics { - Statistics = new UserStatistics - { - Ranks = new UserStatistics.UserRanks { Global = 12000 }, - PP = 12345, - }, + Ranks = new UserStatistics.UserRanks { Global = 12000 }, + PP = 12345, RankHistory = new User.RankHistoryData { Data = smallData, @@ -132,13 +120,10 @@ namespace osu.Game.Tests.Visual.Online AddStep("graph with edges", () => { - graph.User.Value = new User + graph.Statistics.Value = new UserStatistics { - Statistics = new UserStatistics - { - Ranks = new UserStatistics.UserRanks { Global = 12000 }, - PP = 12345, - }, + Ranks = new UserStatistics.UserRanks { Global = 12000 }, + PP = 12345, RankHistory = new User.RankHistoryData { Data = edgyData, diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs index c2376aa153..84c99d8c3a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs @@ -50,12 +50,12 @@ namespace osu.Game.Tests.Visual.Online { Current = 727, Progress = 69, - } - }, - RankHistory = new User.RankHistoryData - { - Mode = @"osu", - Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray() + }, + RankHistory = new User.RankHistoryData + { + Mode = @"osu", + Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray() + }, }, Badges = new[] { diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index 5f79386b76..4818cd8df6 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private KeyValuePair[] ranks; private int dayIndex; - public Bindable User = new Bindable(); + public readonly Bindable Statistics = new Bindable(); public RankGraph() { @@ -56,8 +56,6 @@ namespace osu.Game.Overlays.Profile.Header.Components }; graph.OnBallMove += i => dayIndex = i; - - User.ValueChanged += userChanged; } [BackgroundDependencyLoader] @@ -66,18 +64,25 @@ namespace osu.Game.Overlays.Profile.Header.Components graph.LineColour = colours.Yellow; } - private void userChanged(ValueChangedEvent e) + protected override void LoadComplete() + { + base.LoadComplete(); + + Statistics.BindValueChanged(statistics => updateStatistics(statistics.NewValue)); + } + + private void updateStatistics(UserStatistics statistics) { placeholder.FadeIn(fade_duration, Easing.Out); - if (e.NewValue?.Statistics?.Ranks.Global == null) + if (statistics?.Ranks.Global == null) { graph.FadeOut(fade_duration, Easing.Out); ranks = null; return; } - int[] userRanks = e.NewValue.RankHistory?.Data ?? new[] { e.NewValue.Statistics.Ranks.Global.Value }; + int[] userRanks = statistics.RankHistory?.Data ?? new[] { statistics.Ranks.Global.Value }; ranks = userRanks.Select((x, index) => new KeyValuePair(index, x)).Where(x => x.Value != 0).ToArray(); if (ranks.Length > 1) @@ -191,7 +196,7 @@ namespace osu.Game.Overlays.Profile.Header.Components } } - public string TooltipText => User.Value?.Statistics?.Ranks.Global == null ? "" : $"#{ranks[dayIndex].Value:#,##0}|{ranked_days - ranks[dayIndex].Key + 1}"; + public string TooltipText => Statistics.Value?.Ranks.Global == null ? "" : $"#{ranks[dayIndex].Value:#,##0}|{ranked_days - ranks[dayIndex].Key + 1}"; public ITooltip GetCustomTooltip() => new RankGraphTooltip(); diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 0db1cb32d7..6ee0d9ee8f 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -179,7 +179,7 @@ namespace osu.Game.Overlays.Profile.Header detailGlobalRank.Content = user?.Statistics?.Ranks.Global?.ToString("\\##,##0") ?? "-"; detailCountryRank.Content = user?.Statistics?.Ranks.Country?.ToString("\\##,##0") ?? "-"; - rankGraph.User.Value = user; + rankGraph.Statistics.Value = user?.Statistics; } private class ScoreRankInfo : CompositeDrawable diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 34bd4bbaae..b738eff4a6 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -159,7 +159,10 @@ namespace osu.Game.Users } [JsonProperty(@"rankHistory")] - public RankHistoryData RankHistory; + private RankHistoryData rankHistory + { + set => Statistics.RankHistory = value; + } [JsonProperty("badges")] public Badge[] Badges; diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs index 7afbef01c5..032ec2e05f 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -4,6 +4,7 @@ using System; using Newtonsoft.Json; using osu.Game.Scoring; +using static osu.Game.Users.User; namespace osu.Game.Users { @@ -113,5 +114,7 @@ namespace osu.Game.Users [JsonProperty(@"country")] public int? Country; } + + public RankHistoryData RankHistory; } } From cd7b6d2d27de45747cf439a4676e0b3c0ba5cc43 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 4 Aug 2019 15:00:02 +0300 Subject: [PATCH 2019/2854] TestCase improvement --- .../Online/TestSceneProfileRulesetSelector.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs index d439e6a7c3..1f5ba67e03 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Taiko; using osu.Game.Users; +using osu.Framework.Bindables; namespace osu.Game.Tests.Visual.Online { @@ -24,11 +25,13 @@ namespace osu.Game.Tests.Visual.Online public TestSceneProfileRulesetSelector() { ProfileRulesetSelector selector; + Bindable user = new Bindable(); Child = selector = new ProfileRulesetSelector { Anchor = Anchor.Centre, Origin = Anchor.Centre, + User = { BindTarget = user } }; AddStep("set osu! as default", () => selector.SetDefaultRuleset(new OsuRuleset().RulesetInfo)); @@ -36,11 +39,11 @@ namespace osu.Game.Tests.Visual.Online AddStep("set taiko as default", () => selector.SetDefaultRuleset(new TaikoRuleset().RulesetInfo)); AddStep("set catch as default", () => selector.SetDefaultRuleset(new CatchRuleset().RulesetInfo)); - AddStep("User with osu as default", () => selector.User.Value = new User { PlayMode = "osu" }); - AddStep("User with mania as default", () => selector.User.Value = new User { PlayMode = "mania" }); - AddStep("User with taiko as default", () => selector.User.Value = new User { PlayMode = "taiko" }); - AddStep("User with catch as default", () => selector.User.Value = new User { PlayMode = "fruits" }); - AddStep("null user", () => selector.User.Value = null); + AddStep("User with osu as default", () => user.Value = new User { PlayMode = "osu" }); + AddStep("User with mania as default", () => user.Value = new User { PlayMode = "mania" }); + AddStep("User with taiko as default", () => user.Value = new User { PlayMode = "taiko" }); + AddStep("User with catch as default", () => user.Value = new User { PlayMode = "fruits" }); + AddStep("null user", () => user.Value = null); } } } From 2f5d23b35419f78763924593e5dc03876b0baeb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=BCbner?= Date: Mon, 5 Aug 2019 01:02:42 +0200 Subject: [PATCH 2020/2854] add join command --- osu.Game/Online/Chat/ChannelManager.cs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 3af11ff20f..30135d5b8b 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -213,8 +213,27 @@ namespace osu.Game.Online.Chat PostMessage(content, true); break; + case "j": + case "join": + if (string.IsNullOrWhiteSpace(content)) + { + target.AddNewMessages(new ErrorMessage("Usage: /join [channel]")); + break; + } + + var channel = availableChannels.Where(c => c.Name == content || c.Name == $"#{content}").FirstOrDefault(); + if (channel == null) + { + target.AddNewMessages(new ErrorMessage($"Channel '{content}' not found.")); + break; + } + + JoinChannel(channel); + CurrentChannel.Value = channel; + break; + case "help": - target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]")); + target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action], /join [channel]")); break; default: From d83971713118afe7dff0ea2f31d47e540b9d3f27 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Aug 2019 13:18:29 +0900 Subject: [PATCH 2021/2854] Remove unnecessary intermediate method --- .../Profile/Header/Components/ProfileRulesetSelector.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs index 09bc2f2cdc..2c9a3dd5f9 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs @@ -40,12 +40,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { base.LoadComplete(); - User.BindValueChanged(onUserChanged, true); - } - - private void onUserChanged(ValueChangedEvent user) - { - SetDefaultRuleset(Rulesets.GetRuleset(user.NewValue?.PlayMode ?? "osu")); + User.BindValueChanged(u => SetDefaultRuleset(Rulesets.GetRuleset(u.NewValue?.PlayMode ?? "osu")), true); } public void SetDefaultRuleset(RulesetInfo ruleset) From 379c9e8b7caf3f8382418d818741632ce5dd0303 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Aug 2019 10:02:28 +0200 Subject: [PATCH 2022/2854] Use expression body --- osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs | 5 +---- osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs | 5 +---- osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index 3a695ca179..b9699a88e4 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -22,10 +22,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase("spinner")] [TestCase("spinner-and-circles")] [TestCase("slider")] - public void Test(string name) - { - base.Test(name); - } + public void Test(string name) => base.Test(name); protected override IEnumerable CreateConvertValue(HitObject hitObject) { diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs index 3ca9dcc42c..6f10540973 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs @@ -20,10 +20,7 @@ namespace osu.Game.Rulesets.Mania.Tests protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; [TestCase("basic")] - public void Test(string name) - { - base.Test(name); - } + public void Test(string name) => base.Test(name); protected override IEnumerable CreateConvertValue(HitObject hitObject) { diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs index bafa814582..6c1882b4e2 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs @@ -20,10 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [NonParallelizable] [TestCase("basic")] [TestCase("slider-generating-drumroll")] - public void Test(string name) - { - base.Test(name); - } + public void Test(string name) => base.Test(name); protected override IEnumerable CreateConvertValue(HitObject hitObject) { From ee9e8f6261d92ca24f364b7147c54dca4e542d39 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Aug 2019 17:58:16 +0900 Subject: [PATCH 2023/2854] Fix memory leaks from download buttons --- osu.Game/Online/DownloadTrackingComposite.cs | 30 +++++++++++--------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index 62d6efcb6f..7bfdc7ff69 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -48,22 +48,24 @@ namespace osu.Game.Online attachDownload(manager.GetExistingDownload(modelInfo.NewValue)); }, true); - manager.DownloadBegan += download => - { - if (download.Model.Equals(Model.Value)) - attachDownload(download); - }; - - manager.DownloadFailed += download => - { - if (download.Model.Equals(Model.Value)) - attachDownload(null); - }; - + manager.DownloadBegan += downloadBegan; + manager.DownloadFailed += downloadFailed; manager.ItemAdded += itemAdded; manager.ItemRemoved += itemRemoved; } + private void downloadBegan(ArchiveDownloadRequest request) + { + if (request.Model.Equals(Model.Value)) + attachDownload(request); + } + + private void downloadFailed(ArchiveDownloadRequest request) + { + if (request.Model.Equals(Model.Value)) + attachDownload(null); + } + private ArchiveDownloadRequest attachedRequest; private void attachDownload(ArchiveDownloadRequest request) @@ -126,8 +128,10 @@ namespace osu.Game.Online if (manager != null) { - manager.DownloadBegan -= attachDownload; + manager.DownloadBegan -= downloadBegan; + manager.DownloadFailed -= downloadFailed; manager.ItemAdded -= itemAdded; + manager.ItemRemoved -= itemRemoved; } State.UnbindAll(); From 749a00cc2f324490096fadd5bc3eeee12f13ce81 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 5 Aug 2019 12:49:54 +0300 Subject: [PATCH 2024/2854] Force bindable change --- osu.Game/Overlays/Profile/Header/Components/RankGraph.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index 4818cd8df6..9cb9d48de7 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -68,7 +68,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { base.LoadComplete(); - Statistics.BindValueChanged(statistics => updateStatistics(statistics.NewValue)); + Statistics.BindValueChanged(statistics => updateStatistics(statistics.NewValue), true); } private void updateStatistics(UserStatistics statistics) From 11916782ba9dd82b51ede9cf57e4e36252f05e34 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Aug 2019 19:23:13 +0900 Subject: [PATCH 2025/2854] Fix drawable channels remaining in memory after being closed --- osu.Game/Overlays/ChatOverlay.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index e223856b27..53a05656b1 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -256,6 +256,9 @@ namespace osu.Game.Overlays loadedChannels.Add(loaded); LoadComponentAsync(loaded, l => { + if (currentChannel.Value != e.NewValue) + return; + loading.Hide(); currentChannelContainer.Clear(false); @@ -381,7 +384,18 @@ namespace osu.Game.Overlays foreach (Channel channel in channels) { ChannelTabControl.RemoveChannel(channel); - loadedChannels.Remove(loadedChannels.Find(c => c.Channel == channel)); + + var loaded = loadedChannels.Find(c => c.Channel == channel); + + if (loaded != null) + { + loadedChannels.Remove(loaded); + + // Because the container is only cleared in the async load callback of a new channel, it is forcefully cleared + // to ensure that the previous channel doesn't get updated after it's disposed + currentChannelContainer.Remove(loaded); + loaded.Dispose(); + } } } From a7ac411c2562e1f774b77111c10abc8ea6667fe8 Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 5 Aug 2019 17:57:04 -0700 Subject: [PATCH 2026/2854] Fix footer button hover sounds playing in unclickable area --- osu.Game/Screens/Select/Footer.cs | 2 +- osu.Game/Screens/Select/FooterButton.cs | 12 ++++++------ osu.Game/Screens/Select/FooterButtonMods.cs | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index 0680711f1c..1dc7081c1c 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -94,7 +94,7 @@ namespace osu.Game.Screens.Select buttons = new FillFlowContainer { Direction = FillDirection.Horizontal, - Spacing = new Vector2(0.2f, 0), + Spacing = new Vector2(-FooterButton.SHEAR_WIDTH, 0), AutoSizeAxes = Axes.Both, } } diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs index e18a086a10..90bc902ad8 100644 --- a/osu.Game/Screens/Select/FooterButton.cs +++ b/osu.Game/Screens/Select/FooterButton.cs @@ -17,7 +17,9 @@ namespace osu.Game.Screens.Select { public class FooterButton : OsuClickableContainer { - private static readonly Vector2 shearing = new Vector2(0.15f, 0); + public static readonly float SHEAR_WIDTH = 7.5f; + + protected static readonly Vector2 SHEARING = new Vector2(SHEAR_WIDTH / Footer.HEIGHT, 0); public string Text { @@ -59,16 +61,16 @@ namespace osu.Game.Screens.Select private readonly Box box; private readonly Box light; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos); - public FooterButton() { AutoSizeAxes = Axes.Both; + Shear = SHEARING; Children = new Drawable[] { TextContainer = new Container { - Size = new Vector2(100, 50), + Size = new Vector2(100 - SHEAR_WIDTH, 50), + Shear = -SHEARING, Child = SpriteText = new OsuSpriteText { Anchor = Anchor.Centre, @@ -78,14 +80,12 @@ namespace osu.Game.Screens.Select box = new Box { RelativeSizeAxes = Axes.Both, - Shear = shearing, EdgeSmoothness = new Vector2(2, 0), Colour = Color4.White, Alpha = 0, }, light = new Box { - Shear = shearing, Height = 4, EdgeSmoothness = new Vector2(2, 0), RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Screens/Select/FooterButtonMods.cs b/osu.Game/Screens/Select/FooterButtonMods.cs index fce4d1b2e2..58d92dbca6 100644 --- a/osu.Game/Screens/Select/FooterButtonMods.cs +++ b/osu.Game/Screens/Select/FooterButtonMods.cs @@ -32,6 +32,7 @@ namespace osu.Game.Screens.Select { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, + Shear = -SHEARING, Child = modDisplay = new FooterModDisplay { DisplayUnrankedText = false, From cd6fe918821aec9d150311bcc6ac0ec653b672dc Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 5 Aug 2019 19:56:35 +0900 Subject: [PATCH 2027/2854] Log error for invalid events --- .../Formats/LegacyBeatmapDecoderTest.cs | 17 + osu.Game.Tests/Resources/invalid-events.osu | 1004 +++++++++++++++++ .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 6 +- .../Formats/LegacyStoryboardDecoder.cs | 3 +- 4 files changed, 1028 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Resources/invalid-events.osu diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index d087251e7e..98464b8d91 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -482,5 +482,22 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(hitObjects[0].Samples[0].Bank, hitObjects[0].Samples[1].Bank); } } + + [Test] + public void TestDecodeInvalidEvents() + { + using (var normalResStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var normalStream = new StreamReader(normalResStream)) + using (var resStream = TestResources.OpenResource("invalid-events.osu")) + using (var stream = new StreamReader(resStream)) + { + var decoder = Decoder.GetDecoder(stream); + var goodBeatmap = decoder.Decode(normalStream); + Beatmap badBeatmap = null; + + Assert.DoesNotThrow(() => badBeatmap = decoder.Decode(stream)); + Assert.AreEqual(goodBeatmap.HitObjects.Count, badBeatmap.HitObjects.Count); + } + } } } diff --git a/osu.Game.Tests/Resources/invalid-events.osu b/osu.Game.Tests/Resources/invalid-events.osu new file mode 100644 index 0000000000..a8e767585c --- /dev/null +++ b/osu.Game.Tests/Resources/invalid-events.osu @@ -0,0 +1,1004 @@ +osu file format v14 + +[General] +AudioFilename: 03. Renatus - Soleily 192kbps.mp3 +AudioLeadIn: 0 +PreviewTime: 164471 +Countdown: 0 +SampleSet: Soft +StackLeniency: 0.7 +Mode: 0 +LetterboxInBreaks: 0 +WidescreenStoryboard: 0 + +[Editor] +Bookmarks: 11505,22054,32604,43153,53703,64252,74802,85351,95901,106450,116999,119637,130186,140735,151285,161834,164471,175020,185570,196119,206669,209306 +DistanceSpacing: 1.8 +BeatDivisor: 4 +GridSize: 4 +TimelineZoom: 2 + +[Metadata] +Title:Renatus +TitleUnicode:Renatus +Artist:Soleily +ArtistUnicode:Soleily +Creator:Gamu +Version:Insane +Source: +Tags:MBC7 Unisphere 地球ヤバイEP Chikyu Yabai +BeatmapID:557821 +BeatmapSetID:241526 + +[Difficulty] +HPDrainRate:6.5 +CircleSize:4 +OverallDifficulty:8 +ApproachRate:9 +SliderMultiplier:1.8 +SliderTickRate:2 + +[Events] +bad,event,this,should,fail +//Background and Video events +0,0,"machinetop_background.jpg",0,0 +//Break Periods +2,122474,140135 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples + +[TimingPoints] +956,329.67032967033,4,2,0,60,1,0 +20736,-100,4,2,0,65,0,0 +22054,-100,4,2,0,70,0,0 +43153,-100,4,2,0,60,0,0 +48428,-100,4,2,0,50,0,0 +52879,-100,4,2,0,50,0,0 +53373,-100,4,2,0,60,0,0 +53703,-100,4,2,0,70,0,1 +74719,-100,4,2,0,70,0,0 +74802,-100,4,2,0,70,0,1 +95901,-100,4,2,0,70,0,0 +116999,-133.333333333333,4,2,0,50,0,0 +117164,-133.333333333333,4,2,0,30,0,0 +117329,-79.9999999999999,4,2,0,50,0,0 +117659,-100,4,2,0,50,0,0 +118977,-100,4,2,0,60,0,0 +119307,-100,4,2,0,70,0,0 +119637,659.340659340659,4,2,0,80,1,0 +119966,-100,4,2,0,70,0,0 +120296,-100,4,2,0,60,0,0 +120626,-100,4,2,0,50,0,0 +120955,-100,4,2,0,40,0,0 +121285,-100,4,2,0,30,0,0 +121615,-100,4,2,0,20,0,0 +121944,-100,4,2,0,10,0,0 +122274,-100,4,2,0,5,0,0 +140735,-100,4,2,0,50,0,0 +151285,-80,4,2,0,60,0,0 +161834,329.67032967033,4,2,0,65,1,0 +164141,-100,4,2,0,70,0,0 +164471,-100,4,2,0,70,0,1 +185487,-100,4,2,0,70,0,0 +185570,-100,4,2,0,70,0,1 +206669,659.340659340659,4,2,0,80,1,0 +206998,-100,4,2,0,70,0,0 +207328,-100,4,2,0,60,0,0 +207658,-100,4,2,0,50,0,0 +207987,-100,4,2,0,40,0,0 +208317,-100,4,2,0,30,0,0 +208647,-100,4,2,0,20,0,0 +208976,-100,4,2,0,10,0,0 +209306,-100,4,2,0,5,0,0 + + +[Colours] +Combo1 : 142,199,255 +Combo2 : 255,128,128 +Combo3 : 128,255,255 +Combo4 : 128,255,128 +Combo5 : 255,187,255 +Combo6 : 255,177,140 +Combo7 : 100,100,100,100 + +[HitObjects] +192,168,956,6,0,P|184:128|200:80,1,90,4|0,1:2|0:0,0:0:0:0: +304,56,1285,1,8,0:0:0:0: +244,236,1450,2,0,P|204:252|156:244,1,90,2|0,0:0|0:0,0:0:0:0: +276,156,1780,2,0,P|310:181|329:226,1,90,2|8,1:2|0:0,0:0:0:0: +300,328,2109,1,2,0:0:0:0: +192,332,2274,6,0,L|144:340,2,45,0|0|0,1:0|0:0|0:0,0:0:0:0: +388,300,2604,1,8,0:0:0:0: +244,236,2769,1,0,1:0:0:0: +232,208,2851,1,0,0:0:0:0: +224,176,2934,1,0,0:0:0:0: +228,144,3016,1,0,0:0:0:0: +244,116,3098,1,0,1:0:0:0: +332,52,3263,2,0,P|376:48|424:56,1,90,8|0,0:0|0:0,0:0:0:0: +488,228,3593,5,0,1:0:0:0: +460,240,3675,1,0,0:0:0:0: +428,236,3758,1,0,0:0:0:0: +292,160,3923,2,0,P|288:204|300:252,1,90,8|0,0:0|0:0,0:0:0:0: +316,276,4170,1,0,0:0:0:0: +344,292,4252,2,0,L|388:300,1,45,0|0,0:0|0:0,0:0:0:0: +288,356,4417,2,0,L|244:364,1,45,0|0,1:0|0:0,0:0:0:0: +168,328,4582,2,0,P|124:324|72:332,1,90,8|0,0:0|0:0,0:0:0:0: +24,188,4912,5,0,1:0:0:0: +56,192,4994,1,0,0:0:0:0: +88,196,5076,1,0,0:0:0:0: +148,108,5241,1,8,0:0:0:0: +188,240,5406,1,0,1:0:0:0: +188,240,5488,1,0,0:0:0:0: +188,240,5571,2,0,L|168:328,1,90,0|0,0:0|1:0,0:0:0:0: +260,216,5901,2,0,P|236:180|188:164,1,90,8|0,0:0|0:0,0:0:0:0: +248,296,6230,6,0,L|348:292,1,90,0|0,1:0|0:0,0:0:0:0: +504,232,6560,1,8,0:0:0:0: +400,204,6725,1,0,0:0:0:0: +392,176,6807,1,0,0:0:0:0: +384,144,6890,1,0,0:0:0:0: +376,116,6972,1,0,0:0:0:0: +368,88,7054,1,0,1:0:0:0: +188,48,7219,2,0,L|208:140,1,90,8|0,0:0|0:0,0:0:0:0: +248,296,7549,5,0,1:0:0:0: +207,135,7714,1,0,0:0:0:0: +156,232,7879,1,8,0:0:0:0: +316,191,8043,1,0,1:0:0:0: +316,191,8126,1,0,0:0:0:0: +316,191,8208,2,0,L|372:200,1,45,0|0,0:0|0:0,0:0:0:0: +492,200,8373,2,0,L|447:207,1,45,0|0,1:0|0:0,0:0:0:0: +408,136,8538,2,0,P|396:92|400:48,1,90,8|0,0:0|0:0,0:0:0:0: +260,32,8868,5,0,1:0:0:0: +252,64,8950,1,0,0:0:0:0: +236,92,9032,2,0,P|204:116|148:128,1,90,0|8,0:0|0:0,0:0:0:0: +28,188,9362,1,0,0:0:0:0: +60,196,9445,1,0,0:0:0:0: +88,212,9527,2,0,P|112:244|124:300,1,90,0|0,0:0|1:0,0:0:0:0: +112,128,9857,2,0,P|152:156|184:196,1,90,8|0,0:0|0:0,0:0:0:0: +216,288,10186,5,0,1:0:0:0: +216,288,10269,1,0,0:0:0:0: +216,288,10351,1,0,0:0:0:0: +268,192,10516,1,8,0:0:0:0: +356,128,10681,1,0,1:0:0:0: +388,120,10763,1,0,0:0:0:0: +420,128,10846,2,0,P|440:168|436:220,1,90,0|0,0:0|1:0,0:0:0:0: +332,328,11175,2,0,L|280:332,1,45,8|8,0:0|0:0,0:0:0:0: +216,288,11340,2,0,L|164:292,1,45,0|0,1:0|0:0,1:0:0:0: +100,248,11505,5,4,1:2:0:0: +148,116,11670,1,2,0:0:0:0: +268,192,11835,1,10,0:0:0:0: +136,328,11999,2,0,L|44:336,1,90,2|0,0:0|0:0,0:0:0:0: +216,288,12329,1,2,1:2:0:0: +148,116,12494,1,10,0:0:0:0: +100,248,12659,1,2,0:0:0:0: +268,192,12824,5,0,1:0:0:0: +268,192,12906,1,0,0:0:0:0: +268,192,12988,1,0,0:0:0:0: +340,272,13153,2,0,P|384:276|432:264,1,90,8|0,0:0|1:0,0:0:0:0: +452,244,13401,1,0,0:0:0:0: +468,216,13483,2,0,L|476:124,1,90,0|0,0:0|1:0,0:0:0:0: +368,32,13813,2,0,L|360:121,1,90,8|0,0:0|0:0,0:0:0:0: +340,272,14142,6,0,L|316:316,2,45,0|0|0,1:0|0:0|0:0,0:0:0:0: +452,244,14472,1,8,0:0:0:0: +268,192,14637,1,0,0:0:0:0: +236,188,14719,1,0,0:0:0:0: +204,192,14802,2,0,P|172:228|160:272,1,90,0|0,0:0|1:0,0:0:0:0: +128,140,15131,2,0,P|160:104|172:60,1,90,8|0,0:0|0:0,0:0:0:0: +64,52,15461,6,0,L|20:68,2,45,0|0|0,1:0|0:0|0:0,0:0:0:0: +171,64,15791,1,8,0:0:0:0: +264,8,15956,2,0,L|356:12,1,90,0|0,1:0|0:0,0:0:0:0: +452,56,16285,1,0,1:0:0:0: +296,140,16450,2,0,L|206:136,1,90,8|0,0:0|0:0,0:0:0:0: +108,184,16780,6,0,P|92:224|96:272,1,90,0|0,1:0|0:0,0:0:0:0: +200,244,17109,1,8,0:0:0:0: +108,108,17274,2,0,L|12:116,1,90,0|0,0:0|0:0,0:0:0:0: +200,244,17604,1,0,1:0:0:0: +296,140,17769,2,0,L|385:132,1,90,8|0,0:0|0:0,0:0:0:0: +480,184,18098,5,0,1:0:0:0: +488,216,18181,1,0,0:0:0:0: +496,248,18263,2,0,L|492:340,1,90,0|8,0:0|0:0,0:0:0:0: +404,224,18593,2,0,L|396:176,2,45,0|0|0,1:0|0:0|0:0,0:0:0:0: +304,264,18923,1,0,1:0:0:0: +200,244,19087,2,0,P|156:240|108:248,1,90,8|0,0:0|0:0,0:0:0:0: +296,140,19417,6,0,P|340:144|388:136,1,90,0|0,1:0|0:0,0:0:0:0: +440,44,19747,1,8,0:0:0:0: +404,224,19912,1,0,0:0:0:0: +404,224,19994,1,0,0:0:0:0: +404,224,20076,2,0,L|412:320,1,90,0|0,0:0|1:0,0:0:0:0: +200,244,20406,2,0,L|192:154,1,90,8|0,0:0|0:0,0:0:0:0: +184,44,20736,5,4,1:2:0:0: +152,40,20818,1,0,0:0:0:0: +120,48,20901,1,0,0:0:0:0: +96,68,20983,1,0,0:0:0:0: +76,92,21065,1,2,0:3:0:0: +64,120,21148,1,0,0:0:0:0: +60,152,21230,1,0,1:0:0:0: +64,184,21313,1,0,0:0:0:0: +76,212,21395,2,0,L|96:252,3,45,0|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: +144,316,21725,2,0,L|188:324,3,45,0|0|2|0,0:0|0:0|0:3|0:0,0:0:0:0: +268,340,22054,6,0,L|364:336,1,90,4|0,1:2|0:0,0:0:0:0: +452,280,22384,1,8,0:0:0:0: +512,188,22549,2,0,P|516:144|504:96,1,90,2|0,0:0|0:0,0:0:0:0: +340,24,22879,2,0,P|336:68|348:116,1,90,2|8,1:2|0:0,0:0:0:0: +420,192,23208,1,2,0:0:0:0: +328,252,23373,6,0,L|232:240,1,90,0|0,1:0|0:0,0:0:0:0: +64,256,23703,1,8,0:0:0:0: +144,184,23868,2,0,P|148:140|136:88,1,90,0|0,1:0|0:0,0:0:0:0: +40,52,24197,1,2,1:2:0:0: +139,95,24362,1,8,0:0:0:0: +216,20,24527,1,0,0:0:0:0: +315,63,24692,6,0,P|360:72|408:68,1,90,2|0,1:2|0:0,0:0:0:0: +492,132,25021,1,8,0:0:0:0: +412,204,25186,2,0,P|403:249|407:297,1,90,2|0,0:0|0:0,0:0:0:0: +268,328,25516,2,0,P|277:283|273:235,1,90,2|8,1:2|0:0,0:0:0:0: +232,140,25846,2,0,P|187:131|139:135,1,90,2|0,0:0|1:0,0:0:0:0: +64,208,26175,5,2,0:0:0:0: +44,316,26340,1,8,0:0:0:0: +148,280,26505,1,2,1:2:0:0: +456,208,26835,1,2,1:2:0:0: +476,316,26999,1,10,0:0:0:0: +372,280,27164,1,2,0:0:0:0: +356,172,27329,6,0,L|380:80,1,90,0|0,1:0|0:0,0:0:0:0: +456,208,27659,1,8,0:0:0:0: +300,236,27824,1,2,0:0:0:0: +300,236,27906,1,0,0:0:0:0: +300,236,27988,2,0,L|208:228,1,90,0|2,0:0|1:2,0:0:0:0: +140,312,28318,1,8,0:0:0:0: +372,280,28483,2,0,L|464:272,1,90,2|0,0:0|1:0,0:0:0:0: +500,136,28813,5,2,0:0:0:0: +432,56,28977,1,8,0:0:0:0: +328,24,29142,2,0,P|284:24|236:28,1,90,2|0,1:2|0:0,0:0:0:0: +80,144,29472,1,2,1:2:0:0: +116,44,29637,1,10,0:0:0:0: +184,128,29802,1,2,0:0:0:0: +20,88,29966,6,0,P|1:164|73:227,1,180,2|10,1:2|0:0,0:0:0:0: +184,128,30461,2,0,P|227:120|276:124,1,90,2|0,0:0|0:0,0:0:0:0: +392,188,30791,1,2,1:2:0:0: +272,260,30956,1,8,0:0:0:0: +396,328,31120,1,0,0:0:0:0: +256,348,31285,5,2,1:2:0:0: +224,344,31368,1,0,1:0:0:0: +192,340,31450,2,0,L|172:248,1,90,2|0,1:2|1:0,0:0:0:0: +8,136,31780,2,0,L|27:223,1,90,2|0,1:2|0:0,0:0:0:0: +56,328,32109,1,2,1:2:0:0: +108,192,32274,1,2,1:2:0:0: +100,160,32357,1,0,1:0:0:0: +92,132,32439,1,2,1:2:0:0: +84,104,32521,1,0,1:0:0:0: +76,72,32604,6,0,P|100:112|148:136,1,90,4|0,1:2|0:0,0:0:0:0: +240,168,32934,1,8,0:0:0:0: +336,124,33098,2,0,L|344:80,2,45,2|0|0,0:0|0:0|0:0,0:0:0:0: +264,248,33428,2,0,P|220:248|176:220,1,90,2|8,1:2|0:0,0:0:0:0: +260,84,33758,1,2,0:0:0:0: +344,212,33923,5,0,1:0:0:0: +344,212,34005,1,0,0:0:0:0: +344,212,34087,1,0,0:0:0:0: +440,160,34252,1,8,0:0:0:0: +312,320,34417,2,0,P|272:336|220:324,1,90,0|0,1:0|0:0,0:0:0:0: +156,176,34747,2,0,P|196:160|248:172,2,90,2|8|0,1:2|0:0|0:0,0:0:0:0: +132,280,35241,5,2,1:2:0:0: +132,280,35324,1,0,0:0:0:0: +132,280,35406,2,0,L|120:376,1,90,0|8,0:0|0:0,0:0:0:0: +312,320,35736,2,0,L|300:230,1,90,2|0,0:0|0:0,0:0:0:0: +316,124,36065,1,2,1:2:0:0: +400,192,36230,1,8,0:0:0:0: +300,230,36395,2,0,P|255:231|211:224,1,90,2|0,0:0|1:0,0:0:0:0: +24,132,36725,5,0,0:0:0:0: +132,152,36890,1,8,0:0:0:0: +60,232,37054,1,2,1:2:0:0: +60,232,37137,1,0,0:0:0:0: +60,232,37219,1,0,0:0:0:0: +92,56,37384,2,0,L|184:44,1,90,2|10,1:2|0:0,0:0:0:0: +316,124,37714,2,0,L|226:135,1,90,2|0,0:0|1:0,0:0:0:0: +60,232,38043,6,0,P|52:276|64:328,1,90,0|8,0:0|0:0,0:0:0:0: +220,152,38373,2,0,P|176:144|124:156,1,90,2|0,0:0|0:0,0:0:0:0: +176,252,38703,1,2,1:2:0:0: +323,213,38868,2,0,L|316:124,1,90,8|2,0:0|0:0,0:0:0:0: +332,320,39197,5,0,1:0:0:0: +424,260,39362,1,2,0:0:0:0: +260,272,39527,2,0,P|246:313|256:360,1,90,8|2,0:0|1:2,0:0:0:0: +408,336,39857,1,0,0:0:0:0: +176,252,40021,2,0,L|80:260,2,90,2|10|2,1:2|0:0|0:0,0:0:0:0: +324,212,40516,5,2,1:2:0:0: +324,212,40598,1,0,1:0:0:0: +324,212,40681,1,0,1:0:0:0: +200,336,40846,1,2,1:2:0:0: +236,188,41010,1,2,1:2:0:0: +236,188,41093,1,0,1:0:0:0: +236,188,41175,1,0,1:0:0:0: +281,357,41340,1,2,1:2:0:0: +176,252,41505,1,2,1:2:0:0: +176,252,41587,1,0,1:0:0:0: +176,252,41670,1,0,1:0:0:0: +344,297,41835,5,2,1:2:0:0: +432,232,41999,1,2,1:2:0:0: +444,204,42082,1,0,1:0:0:0: +448,172,42164,1,0,1:0:0:0: +444,140,42247,1,0,1:0:0:0: +432,112,42329,2,0,L|440:64,2,45,2|0|0,1:2|1:0|1:0,0:0:0:0: +236,188,42659,1,0,0:0:0:0: +340,172,42824,1,2,0:3:0:0: +272,88,42988,1,0,0:0:0:0: +132,160,43153,6,0,P|148:248|220:296,1,180,4|8,1:2|0:0,0:0:0:0: +324,320,43648,2,0,L|336:364,2,45,0|0|0,0:0|0:0|0:0,0:0:0:0: +292,216,43977,1,0,1:0:0:0: +396,240,44142,2,0,P|440:244|488:232,1,90,8|0,0:0|0:0,0:0:0:0: +328,124,44472,6,0,P|284:120|236:132,1,90,0|0,1:0|0:0,0:0:0:0: +168,212,44802,1,8,0:0:0:0: +192,316,44966,1,0,1:0:0:0: +140,220,45131,1,0,0:0:0:0: +83,310,45296,1,0,1:0:0:0: +114,205,45461,1,8,0:0:0:0: +10,229,45626,1,0,0:0:0:0: +106,176,45791,6,0,P|113:133|108:85,1,90,0|0,1:0|0:0,0:0:0:0: +204,136,46120,1,8,0:0:0:0: +256,40,46285,1,0,0:0:0:0: +256,40,46368,1,0,0:0:0:0: +256,40,46450,2,0,L|356:44,1,90,0|0,0:0|1:0,0:0:0:0: +501,124,46780,2,0,L|412:128,1,90,8|0,0:0|0:0,0:0:0:0: +324,192,47109,5,0,1:0:0:0: +356,296,47274,1,0,0:0:0:0: +284,216,47439,1,8,0:0:0:0: +269,323,47604,1,0,1:0:0:0: +237,220,47769,1,0,0:0:0:0: +178,311,47934,1,0,1:0:0:0: +191,203,48098,1,8,0:0:0:0: +99,261,48263,1,0,0:0:0:0: +156,168,48428,6,0,B|176:112|136:64|136:64|200:96,1,180,4|8,1:2|0:0,0:0:0:0: +300,124,48923,2,0,L|392:120,1,90,0|0,0:0|0:0,0:0:0:0: +468,48,49252,1,0,1:0:0:0: +390,120,49417,2,0,P|390:164|406:208,1,90,8|0,0:0|0:0,0:0:0:0: +352,344,49747,6,0,P|352:300|336:256,1,90,4|0,1:2|0:0,0:0:0:0: +240,208,50076,1,8,0:0:0:0: +163,320,50241,2,0,P|207:324|252:316,1,90,0|0,1:0|0:0,0:0:0:0: +240,208,50571,1,0,1:0:0:0: +76,296,50736,2,0,P|76:340|92:384,1,90,8|0,0:0|0:0,0:0:0:0: +312,164,51065,6,0,P|236:124|160:184,1,180,4|8,1:2|0:0,0:0:0:0: +247,297,51560,2,0,L|240:208,1,90,0|0,0:0|0:0,0:0:0:0: +224,48,51890,1,0,1:0:0:0: +332,56,52054,2,0,L|366:58,5,30,8|0|0|0|0|0,0:0|0:0|0:0|0:0|0:0|0:0,0:0:0:0: +408,64,52384,6,0,P|420:108|416:156,1,90,0|0,1:0|0:0,0:0:0:0: +360,260,52714,1,8,0:0:0:0: +247,297,52879,2,0,B|203:281|159:297|159:297|115:313|71:297,1,180,0|0,1:0|1:0,0:0:0:0: +116,196,53373,1,8,0:0:0:0: +120,164,53456,1,0,0:0:0:0: +124,132,53538,1,0,0:0:0:0: +128,100,53620,1,0,0:0:0:0: +132,68,53703,5,4,1:2:0:0: +40,136,53868,1,0,0:0:0:0: +204,160,54032,2,0,L|304:152,1,90,8|0,0:0|0:0,0:0:0:0: +408,64,54362,1,0,0:0:0:0: +408,64,54445,1,0,0:0:0:0: +408,64,54527,2,0,P|404:112|416:160,1,90,0|8,1:0|0:0,0:0:0:0: +484,236,54857,1,0,0:0:0:0: +428,328,55021,5,0,1:0:0:0: +328,296,55186,1,0,0:0:0:0: +328,296,55269,1,0,0:0:0:0: +328,296,55351,1,8,0:0:0:0: +416,300,55516,1,0,1:0:0:0: +472,208,55681,1,0,0:0:0:0: +316,268,55846,1,0,1:0:0:0: +460,180,56010,1,8,0:0:0:0: +304,240,56175,1,0,0:0:0:0: +404,272,56340,5,0,1:0:0:0: +448,152,56505,1,0,0:0:0:0: +448,152,56587,1,0,0:0:0:0: +448,152,56670,2,0,P|456:112|448:60,1,90,8|0,0:0|0:0,0:0:0:0: +268,28,56999,2,0,P|260:68|268:120,1,90,0|0,0:0|1:0,0:0:0:0: +404,272,57329,2,0,P|444:280|496:272,2,90,8|0|0,0:0|0:0|1:0,0:0:0:0: +304,240,57824,5,0,0:0:0:0: +252,336,57988,1,8,0:0:0:0: +196,244,58153,1,0,1:0:0:0: +24,256,58318,1,0,0:0:0:0: +116,200,58483,1,0,1:0:0:0: +136,60,58648,1,8,0:0:0:0: +192,152,58813,1,0,0:0:0:0: +304,240,58977,6,0,P|348:252|396:248,1,90,0|0,1:0|0:0,0:0:0:0: +456,116,59307,2,0,P|412:104|364:108,1,90,8|0,0:0|0:0,0:0:0:0: +273,161,59637,1,0,0:0:0:0: +136,60,59802,1,0,1:0:0:0: +192,152,59966,1,8,0:0:0:0: +23,177,60131,1,0,0:0:0:0: +129,203,60296,5,0,1:0:0:0: +88,304,60461,2,0,P|132:311|176:303,1,90,0|8,0:0|0:0,0:0:0:0: +304,240,60791,1,0,1:0:0:0: +304,240,60873,1,0,0:0:0:0: +304,240,60956,2,0,L|312:288,3,45,0|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: +384,256,61285,2,0,L|392:304,3,45,8|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: +464,272,61615,5,2,1:2:0:0: +488,168,61780,1,2,0:0:0:0: +428,80,61945,1,10,0:0:0:0: +332,32,62109,2,0,P|288:28|240:36,1,90,2|0,0:0|0:0,0:0:0:0: +28,216,62439,1,2,1:2:0:0: +88,304,62604,1,10,0:0:0:0: +184,352,62769,2,0,P|228:356|276:348,1,90,2|0,0:0|1:0,0:0:0:0: +384,256,63098,6,0,P|409:219|426:174,1,90,2|8,0:0|0:0,0:0:0:0: +428,80,63428,2,0,L|420:36,2,45,2|0|0,1:2|0:0|0:0,0:0:0:0: +456,288,63758,1,2,1:2:0:0: +324,200,63923,1,10,1:2:0:0: +292,204,64005,1,0,1:0:0:0: +260,208,64087,1,2,1:2:0:0: +228,212,64170,1,0,1:0:0:0: +196,216,64252,5,4,1:2:0:0: +104,160,64417,1,0,0:0:0:0: +228,296,64582,2,0,L|320:284,1,90,8|0,0:0|0:0,0:0:0:0: +344,112,64912,1,0,0:0:0:0: +344,112,64994,1,0,0:0:0:0: +344,112,65076,2,0,L|254:123,1,90,0|8,1:0|0:0,0:0:0:0: +144,284,65406,2,0,P|148:328|176:364,1,90,0|0,0:0|1:0,0:0:0:0: +196,216,65736,5,0,0:0:0:0: +196,216,65818,1,0,0:0:0:0: +196,216,65901,2,0,P|155:198|110:205,1,90,8|0,0:0|1:0,0:0:0:0: +36,284,66230,1,0,0:0:0:0: +4,180,66395,1,0,1:0:0:0: +132,24,66560,1,8,0:0:0:0: +100,128,66725,1,0,0:0:0:0: +24,48,66890,5,0,1:0:0:0: +212,108,67054,1,0,0:0:0:0: +212,108,67137,1,0,0:0:0:0: +212,108,67219,2,0,L|300:92,1,90,8|0,0:0|0:0,0:0:0:0: +472,144,67549,2,0,L|384:160,1,90,0|0,0:0|1:0,0:0:0:0: +196,216,67879,2,0,P|240:216|288:240,1,90,8|0,0:0|0:0,0:0:0:0: +324,336,68208,5,0,1:0:0:0: +144,288,68373,1,0,0:0:0:0: +58,170,68538,1,8,0:0:0:0: +196,215,68703,1,0,1:0:0:0: +58,260,68868,1,0,0:0:0:0: +144,142,69032,2,0,L|138:108,2,30,0|0|0,1:0|0:0|0:0,0:0:0:0: +144,142,69197,2,0,P|184:124|232:132,1,90,8|0,0:0|0:0,0:0:0:0: +312,248,69527,6,0,L|324:338,1,90,0|0,1:0|0:0,0:0:0:0: +436,248,69857,1,8,0:0:0:0: +432,216,69939,1,0,0:0:0:0: +428,184,70021,1,0,0:0:0:0: +328,120,70186,1,0,0:0:0:0: +324,152,70269,1,0,0:0:0:0: +320,184,70351,1,0,1:0:0:0: +316,216,70434,1,0,0:0:0:0: +312,248,70516,2,0,L|320:300,1,45,8|0,0:0|0:0,0:0:0:0: +244,340,70681,2,0,L|237:295,1,45,0|0,0:0|0:0,0:0:0:0: +216,224,70846,6,0,P|168:216|124:224,1,90,0|0,1:0|0:0,0:0:0:0: +40,288,71175,1,8,0:0:0:0: +2,95,71340,2,0,P|-4:139|4:184,1,90,0|0,1:0|0:0,0:0:0:0: +164,304,71670,1,0,1:0:0:0: +312,248,71835,1,8,0:0:0:0: +244,340,71999,1,0,0:0:0:0: +216,224,72164,6,0,L|228:132,1,90,0|0,1:0|0:0,0:0:0:0: +332,148,72494,2,0,L|344:56,1,90,8|0,0:0|0:0,0:0:0:0: +312,248,72824,1,0,0:0:0:0: +164,304,72988,1,0,1:0:0:0: +332,336,73153,1,8,0:0:0:0: +360,324,73236,1,0,0:0:0:0: +384,304,73318,1,0,0:0:0:0: +399,276,73401,1,0,0:0:0:0: +403,244,73483,6,0,L|396:200,3,45,4|0|2|0,1:2|0:0|0:0|1:0,0:0:0:0: +420,112,73813,2,0,L|427:68,3,45,2|0|2|0,1:2|0:0|1:2|0:0,0:0:0:0: +352,16,74142,2,0,L|345:60,3,45,0|0|2|0,0:0|1:0|1:2|0:0,0:0:0:0: +332,148,74472,1,2,1:2:0:0: +332,148,74554,1,0,1:0:0:0: +332,148,74637,1,2,1:2:0:0: +332,148,74719,1,0,1:0:0:0: +332,148,74802,6,0,P|360:216|320:312,1,180,4|2,1:2|0:3,0:0:0:0: +190,310,75296,2,0,P|151:231|180:148,1,180,4|0,1:2|0:0,0:0:0:0: +256,56,75791,1,0,0:0:0:0: +332,148,75956,1,2,0:3:0:0: +179,148,76120,5,4,1:2:0:0: +336,64,76285,1,4,1:2:0:0: +256,224,76450,1,2,0:3:0:0: +176,64,76615,1,4,1:2:0:0: +256,140,76780,2,0,L|256:324,1,180,2|0,0:0|0:0,0:0:0:0: +364,300,77274,1,2,0:3:0:0: +148,300,77439,6,0,P|104:316|76:356,1,90,4|0,1:2|0:0,0:0:0:0: +24,252,77769,2,0,L|16:208,3,45,8|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: +96,212,78098,2,0,L|104:168,3,45,0|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: +32,128,78428,2,0,L|24:84,3,45,8|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: +104,88,78758,5,2,1:2:0:0: +204,132,78923,1,0,0:0:0:0: +236,124,79005,1,0,0:0:0:0: +268,116,79087,2,0,L|280:68,3,45,8|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: +348,100,79417,2,0,L|360:52,3,45,0|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: +428,84,79747,1,8,1:2:0:0: +460,76,79829,1,0,1:0:0:0: +492,68,79912,1,0,1:0:0:0: +492,260,80076,6,0,P|400:248|328:296,1,180,4|2,1:2|0:3,0:0:0:0: +144,236,80571,2,0,P|236:248|308:200,1,180,4|0,1:2|0:0,0:0:0:0: +348,100,81065,2,0,P|348:56|336:8,1,90,0|2,0:0|0:3,0:0:0:0: +140,48,81395,5,4,1:2:0:0: +244,68,81560,1,4,1:2:0:0: +144,236,81725,1,2,0:3:0:0: +176,133,81890,1,4,1:2:0:0: +184,304,82054,2,0,P|100:300|68:220,1,180,2|0,0:0|0:0,0:0:0:0: +100,116,82549,1,2,0:3:0:0: +264,244,82714,6,0,L|272:340,1,90,4|0,1:2|0:0,0:0:0:0: +380,316,83043,1,8,0:0:0:0: +396,288,83126,1,0,0:0:0:0: +400,256,83208,1,0,0:0:0:0: +396,224,83291,1,0,0:0:0:0: +380,196,83373,2,0,L|336:176,3,45,0|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: +272,148,83703,1,8,0:0:0:0: +256,120,83785,1,0,0:0:0:0: +252,88,83868,1,0,0:0:0:0: +256,56,83950,1,0,0:0:0:0: +272,28,84032,6,0,L|316:8,3,45,2|0|0|0,1:2|0:0|0:0|0:0,0:0:0:0: +360,72,84362,2,0,L|408:72,3,45,8|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: +421,149,84692,2,0,L|464:169,3,45,2|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: +443,244,85021,2,0,L|473:281,3,45,8|0|0|0,1:2|1:0|0:0|0:0,0:0:0:0: +422,339,85351,6,0,L|240:348,1,180,4|2,1:2|0:3,0:0:0:0: +76,172,85846,2,0,L|255:163,1,180,4|0,1:2|0:0,0:0:0:0: +421,149,86340,2,0,P|435:107|428:56,1,90,0|2,0:0|0:3,0:0:0:0: +228,56,86670,5,4,1:2:0:0: +280,192,86835,1,4,1:2:0:0: +328,96,86999,1,2,0:3:0:0: +180,152,87164,1,4,1:2:0:0: +28,100,87330,2,0,P|16:56|20:8,1,90,2|0,0:0|0:0,0:0:0:0: +0,180,87659,1,0,0:0:0:0: +28,284,87824,1,2,0:3:0:0: +108,352,87988,6,0,P|152:360|196:356,1,90,4|0,1:2|0:0,0:0:0:0: +276,284,88318,1,8,0:0:0:0: +304,272,88401,1,0,0:0:0:0: +336,268,88483,1,0,0:0:0:0: +368,272,88565,1,0,0:0:0:0: +396,284,88648,2,0,L|432:312,1,45,0|0,0:0|0:0,0:0:0:0: +488,252,88813,2,0,L|452:224,1,45,0|0,1:0|0:0,0:0:0:0: +400,164,88977,2,0,L|396:116,3,45,8|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: +316,64,89307,6,0,L|320:160,1,90,2|0,1:2|0:0,0:0:0:0: +276,284,89637,1,8,0:0:0:0: +248,296,89719,1,0,0:0:0:0: +216,300,89802,1,0,1:0:0:0: +184,296,89884,1,0,0:0:0:0: +156,284,89966,2,0,L|120:256,1,45,0|0,0:0|0:0,0:0:0:0: +176,200,90131,2,0,L|140:172,1,45,0|0,1:0|0:0,0:0:0:0: +196,116,90296,2,0,L|160:88,3,45,8|0|0|0,1:2|1:0|1:0|0:0,0:0:0:0: +92,44,90626,6,0,P|48:44|24:160,1,180,4|2,1:2|0:3,0:0:0:0: +156,284,91120,2,0,B|200:300|244:284|244:284|288:268|332:284,1,180,4|0,1:2|0:0,0:0:0:0: +176,200,91615,2,0,P|176:156|196:116,1,90,0|2,0:0|0:3,0:0:0:0: +264,28,91945,6,0,L|353:39,1,90,4|0,1:2|1:0,0:0:0:0: +453,159,92274,2,0,L|364:148,1,90,2|4,0:3|1:2,0:0:0:0: +268,196,92604,2,0,P|260:268|328:348,1,180,2|0,0:0|0:0,0:0:0:0: +364,248,93098,1,2,0:3:0:0: +176,200,93263,5,4,1:2:0:0: +72,228,93428,1,0,1:0:0:0: +152,92,93593,1,0,1:0:0:0: +256,64,93758,1,0,1:0:0:0: +336,200,93923,5,0,1:0:0:0: +440,228,94087,1,0,1:0:0:0: +360,92,94252,1,0,1:0:0:0: +256,64,94417,1,0,1:0:0:0: +176,200,94582,5,2,1:2:0:0: +168,228,94664,1,0,1:0:0:0: +168,260,94747,1,0,1:0:0:0: +172,292,94829,1,0,1:0:0:0: +192,316,94912,1,0,1:0:0:0: +220,328,94994,1,0,1:0:0:0: +252,332,95076,1,0,1:0:0:0: +280,320,95159,1,0,1:0:0:0: +300,296,95241,2,0,L|308:248,3,45,2|0|0|0,1:2|1:0|1:0|1:0,0:0:0:0: +312,172,95571,2,0,L|304:127,3,45,0|0|0|0,1:0|1:0|1:0|1:0,0:0:0:0: +256,64,95901,6,0,P|208:56|164:60,1,90,4|0,1:2|0:0,0:0:0:0: +76,116,96230,1,8,0:0:0:0: +60,224,96395,1,0,0:0:0:0: +60,224,96477,1,0,0:0:0:0: +160,184,96642,1,0,0:0:0:0: +160,184,96725,1,0,1:0:0:0: +63,26,96890,2,0,L|76:116,1,90,8|0,0:0|0:0,0:0:0:0: +136,272,97219,5,0,1:0:0:0: +168,268,97302,1,0,0:0:0:0: +200,264,97384,1,0,0:0:0:0: +232,260,97466,1,0,0:0:0:0: +264,256,97549,1,8,0:0:0:0: +384,136,97714,1,0,1:0:0:0: +376,168,97796,1,0,0:0:0:0: +380,200,97879,1,0,0:0:0:0: +392,228,97961,1,0,0:0:0:0: +416,248,98043,2,0,P|464:260|512:260,2,90,0|8|0,1:0|0:0|0:0,0:0:0:0: +231,105,98538,6,0,L|188:116,2,45,0|0|0,1:0|0:0|0:0,0:0:0:0: +376,56,98868,2,0,L|420:64,1,45,8|0,0:0|0:0,0:0:0:0: +384,136,99032,1,0,0:0:0:0: +384,136,99115,2,0,P|340:128|304:92,1,90,0|0,0:0|0:0,0:0:0:0: +303,18,99362,2,0,L|207:26,2,90,0|8|0,1:0|0:0|0:0,0:0:0:0: +452,88,99857,5,0,1:0:0:0: +465,116,99939,1,0,0:0:0:0: +466,147,100021,1,0,0:0:0:0: +456,177,100104,1,0,0:0:0:0: +436,201,100186,2,0,P|416:213|389:216,3,45,8|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: +320,188,100516,2,0,P|300:176|273:173,3,45,0|0|0|0,0:0|1:0|1:0|0:0,0:0:0:0: +204,200,100846,2,0,P|192:220|189:247,3,45,8|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: +188,320,101175,6,0,P|143:322|100:310,1,90,0|0,1:0|0:0,0:0:0:0: +76,292,101423,1,0,0:0:0:0: +76,292,101505,1,8,0:0:0:0: +76,292,101587,2,0,L|72:248,1,45 +12,68,101835,2,0,L|6:24,2,45,0|0|0,0:0|0:0|1:0,0:0:0:0: +104,140,102164,2,0,L|171:132,3,45,8|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: +224,124,102494,6,0,P|236:164|232:216,1,90,0|0,1:0|0:0,0:0:0:0: +288,296,102824,1,8,0:0:0:0: +288,296,102906,1,0,0:0:0:0: +288,296,102988,2,0,P|328:284|380:288,1,90,0|0,1:0|0:0,0:0:0:0: +404,304,103236,1,0,0:0:0:0: +424,328,103318,1,0,1:0:0:0: +448,188,103483,2,0,L|440:140,3,45,8|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: +424,72,103813,5,0,1:0:0:0: +324,112,103977,1,0,0:0:0:0: +324,112,104060,1,0,0:0:0:0: +324,112,104142,2,0,P|280:116|232:104,1,90,8|0,0:0|0:0,0:0:0:0: +160,28,104472,1,0,0:0:0:0: +216,208,104637,1,0,1:0:0:0: +216,208,104719,1,0,0:0:0:0: +216,208,104802,1,8,0:0:0:0: +352,240,104966,1,0,0:0:0:0: +384,244,105049,1,0,0:0:0:0: +416,248,105131,6,0,L|460:240,4,45,0|0|0|0|8,1:0|0:0|0:0|0:0|0:0,0:0:0:0: +272,288,105626,1,0,1:0:0:0: +264,320,105708,1,0,0:0:0:0: +256,352,105791,2,0,L|204:356,5,30,0|0|0|0|0|0,0:0|0:0|0:0|1:0|0:0|0:0,0:0:0:0: +156,332,106120,2,0,L|104:336,5,30,8|0|0|0|0|0,0:0|0:0|0:0|1:0|0:0|0:0,0:0:0:0: +56,312,106450,5,4,1:2:0:0: +4,188,106615,1,0,0:0:0:0: +168,220,106780,2,0,P|127:232|79:228,1,90,8|0,0:0|0:0,0:0:0:0: +112,124,107109,1,0,0:0:0:0: +272,216,107274,2,0,L|264:316,1,90,0|8,1:0|0:0,0:0:0:0: +400,268,107604,1,0,0:0:0:0: +428,132,107769,5,0,1:0:0:0: +428,132,107851,1,0,0:0:0:0: +428,132,107934,1,0,0:0:0:0: +428,132,108016,1,0,0:0:0:0: +428,132,108098,1,8,0:0:0:0: +332,84,108263,2,0,P|288:80|232:88,1,90,0|0,1:0|0:0,0:0:0:0: +112,124,108593,1,0,1:0:0:0: +148,264,108758,1,8,0:0:0:0: +16,236,108923,1,0,0:0:0:0: +264,126,109087,6,0,L|272:216,1,90,0|0,1:0|0:0,0:0:0:0: +452,224,109417,2,0,L|460:320,1,90,8|0,0:0|0:0,0:0:0:0: +360,232,109747,1,0,0:0:0:0: +348,56,109912,1,0,1:0:0:0: +416,140,110076,1,8,0:0:0:0: +256,112,110241,2,0,P|212:120|160:112,1,90,0|0,0:0|1:0,0:0:0:0: +348,56,110571,6,0,L|331:150,1,90,0|8,0:0|0:0,0:0:0:0: +208,328,110901,2,0,L|191:239,1,90,0|0,1:0|0:0,0:0:0:0: +184,216,111148,1,0,1:0:0:0: +178,194,111230,1,0,1:0:0:0: +68,272,111395,1,8,0:0:0:0: +56,136,111560,1,0,1:0:0:0: +178,194,111725,6,0,P|219:203|267:199,1,90,4|0,1:2|0:0,0:0:0:0: +364,148,112054,1,8,0:0:0:0: +384,256,112219,2,0,P|406:291|443:322,1,90,0|0,0:0|0:0,0:0:0:0: +488,224,112549,1,0,1:0:0:0: +304,232,112714,2,0,L|208:224,2,90,8|0|0,0:0|0:0|1:0,0:0:0:0: +208,328,113208,6,0,L|112:320,1,90,0|8,0:0|0:0,0:0:0:0: +26,184,113538,2,0,L|116:192,1,90,0|0,1:0|0:0,0:0:0:0: +304,232,113868,1,0,1:0:0:0: +116,192,114032,1,8,0:0:0:0: +224,132,114197,1,0,0:0:0:0: +208,328,114362,6,0,B|272:360|320:312|320:312|340:368,1,180,4|8,1:2|0:0,0:0:0:0: +304,232,114857,2,0,P|300:184|308:140,1,90,0|0,0:0|0:0,0:0:0:0: +384,64,115186,1,0,1:0:0:0: +307,143,115351,1,8,0:0:0:0: +256,48,115516,1,0,0:0:0:0: +456,24,115681,6,0,B|482:101|420:136|420:136|440:184,1,180,4|8,1:2|0:0,0:0:0:0: +384,64,116175,2,0,P|340:56|296:64,1,90,0|0,1:0|0:0,0:0:0:0: +211,171,116505,1,0,1:0:0:0: +439,181,116670,2,0,L|448:84,1,90,8|0,0:0|0:0,0:0:0:0: +372,296,116999,6,2,L|304:292,1,67.5000025749208,2|0,0:1|0:0,0:0:0:0: +136,252,117329,6,2,P|196:260|212:172,1,168.75,0|0,0:0|0:0,0:0:0:0: +192,148,117659,1,2,0:3:0:0: +164,132,117741,1,2,0:3:0:0: +132,124,117824,1,2,1:3:0:0: +100,132,117906,1,2,0:3:0:0: +72,148,117988,2,0,L|52:56,1,90,2|8,0:3|0:0,0:0:0:0: +36,244,118318,5,0,1:0:0:0: +76,344,118483,1,0,1:0:0:0: +184,352,118648,1,0,1:0:0:0: +244,264,118813,1,0,1:0:0:0: +244,264,118895,1,0,1:0:0:0: +244,264,118977,2,0,L|288:260,3,45,2|0|2|0,1:2|0:0|0:0|0:0,0:0:0:0: +332,328,119307,2,0,L|376:324,3,45,2|0|2|0,1:2|0:0|0:0|0:0,0:0:0:0: +412,252,119637,5,4,1:2:0:0: +256,192,119719,12,0,122274,0:0:0:0: +256,192,140735,6,0,L|228:156,1,45,4|0,1:2|0:0,0:0:0:0: +152,132,141065,2,0,P|129:129|104:136,1,45 +48,192,141395,2,0,P|40:236|52:280,1,90,8|8,0:0|0:0,0:0:0:0: +196,352,142054,6,0,L|308:340,1,90,8|8,0:0|1:2,0:0:0:0: +336,280,142549,1,0,0:0:0:0: +404,324,142713,1,8,0:0:0:0: +404,324,142878,1,8,0:0:0:0: +292,120,143373,5,0,1:0:0:0: +212,104,143538,1,0,0:0:0:0: +140,140,143702,1,0,0:0:0:0: +120,220,143867,1,0,0:0:0:0: +144,296,144032,2,0,P|184:320|228:316,1,90,10|8,0:0|0:0,0:0:0:0: +372,212,144691,6,0,P|327:209|290:232,1,90,10|8,0:0|1:2,0:0:0:0: +348,288,145186,1,0,0:0:0:0: +452,220,145351,1,10,0:0:0:0: +452,220,145516,1,8,0:0:0:0: +328,36,146010,5,2,1:2:0:0: +264,88,146175,1,0,0:0:0:0: +184,108,146340,1,0,0:0:0:0: +104,88,146505,1,0,0:0:0:0: +44,36,146669,1,8,0:0:0:0: +44,36,146999,1,8,0:0:0:0: +44,36,147329,6,0,L|24:84,1,45,8|0,0:0|0:0,0:0:0:0: +52,156,147658,2,0,L|71:204,1,45,8|0,1:2|0:0,0:0:0:0: +144,236,147988,1,8,0:0:0:0: +144,236,148153,1,8,0:0:0:0: +316,64,148647,5,0,1:0:0:0: +380,116,148812,1,0,0:0:0:0: +408,192,148977,1,0,0:0:0:0: +380,268,149142,1,0,0:0:0:0: +316,320,149307,2,0,L|224:316,1,90,10|8,0:0|0:0,0:0:0:0: +64,248,149966,5,10,0:0:0:0: +144,236,150131,1,0,0:0:0:0: +188,168,150296,1,8,1:2:0:0: +192,88,150461,1,0,0:0:0:0: +140,24,150626,2,0,P|120:16|96:20,1,45,10|0,0:0|0:0,0:0:0:0: +260,132,150955,2,0,P|280:140|304:136,1,45,2|0,0:0|0:0,0:0:0:0: +476,48,151285,6,0,L|484:160,1,112.5,4|0,1:2|0:0,0:0:0:0: +464,236,151779,1,0,0:0:0:0: +436,308,151944,2,0,P|380:320|324:308,1,112.5,8|8,0:0|0:0,0:0:0:0: +76,308,152604,6,0,P|132:320|188:308,1,112.5,8|8,0:0|1:2,0:0:0:0: +256,88,153263,1,8,0:0:0:0: +256,168,153428,1,8,0:0:0:0: +256,168,153922,5,4,1:2:0:0: +256,248,154087,1,0,0:0:0:0: +324,128,154252,1,0,0:0:0:0: +188,128,154417,1,0,0:0:0:0: +332,212,154582,2,0,L|388:204,1,56.25,10|0,0:0|0:0,0:0:0:0: +492,152,154911,2,0,L|436:144,1,56.25,8|0,0:0|0:0,0:0:0:0: +324,128,155241,5,10,0:0:0:0: +180,212,155406,1,0,0:0:0:0: +332,212,155571,1,8,1:2:0:0: +188,128,155735,1,0,0:0:0:0: +256,248,155900,1,10,0:0:0:0: +256,248,156065,2,0,L|256:304,2,56.25,0|0|0,0:0|0:0|0:0,0:0:0:0: +180,212,156560,6,0,L|124:204,1,56.25,4|0,1:2|0:0,0:0:0:0: +20,152,156889,2,0,L|76:144,1,56.25,0|0,0:0|0:0,0:0:0:0: +188,128,157219,2,0,P|212:72|192:16,1,112.5,8|8,0:0|0:0,0:0:0:0: +132,72,157713,1,0,0:0:0:0: +180,212,157878,6,0,L|236:208,1,56.25,8|0,0:0|0:0,0:0:0:0: +360,252,158208,2,8,L|304:248,1,56.25,8|0,1:2|0:0,0:0:0:0: +168,292,158538,2,0,L|160:356,2,56.25,8|8|0,0:0|0:0|0:0,0:0:0:0: +180,212,159032,1,0,0:0:0:0: +144,140,159197,6,0,P|104:128|36:148,1,112.5,2|0,1:2|0:0,0:0:0:0: +12,220,159691,1,0,0:0:0:0: +36,296,159856,2,0,P|60:316|92:324,1,56.25,8|0,0:0|0:0,0:0:0:0: +215,264,160186,2,0,P|189:273|168:292,1,56.25,8|0,0:0|0:0,0:0:0:0: +228,344,160516,6,0,L|284:340,1,56.25,10|0,0:0|0:0,0:0:0:0: +328,276,160845,2,0,L|384:272,1,56.25,8|0,1:2|0:0,0:0:0:0: +428,208,161175,1,8,0:0:0:0: +440,128,161340,1,8,0:0:0:0: +400,60,161505,1,2,0:0:0:0: +328,28,161669,1,0,0:0:0:0: +212,76,161834,6,0,P|200:120|208:164,1,90,2|0,1:2|1:0,0:0:0:0: +300,308,162163,2,0,P|312:264|304:220,1,90,2|0,1:2|1:0,0:0:0:0: +140,236,162493,2,0,P|184:248|228:240,1,90,2|0,1:2|1:0,0:0:0:0: +372,148,162823,2,0,P|328:136|284:144,1,90,2|0,1:2|1:0,0:0:0:0: +104,316,163152,5,2,1:2:0:0: +78,297,163235,1,0,1:0:0:0: +60,270,163317,1,0,1:0:0:0: +54,239,163399,1,0,1:0:0:0: +58,207,163482,1,2,1:2:0:0: +74,180,163564,1,0,1:0:0:0: +98,159,163647,1,0,1:0:0:0: +127,149,163729,1,0,1:0:0:0: +158,150,163812,2,0,L|208:160,1,45,2|0,1:2|1:0,0:0:0:0: +344,184,163976,2,0,L|294:194,1,45,0|0,1:0|1:0,0:0:0:0: +140,236,164141,1,4,1:2:0:0: +140,236,164471,6,0,L|232:252,1,90,4|0,1:2|0:0,0:0:0:0: +344,184,164801,1,8,0:0:0:0: +380,284,164965,1,0,0:0:0:0: +368,104,165130,2,0,P|324:104|284:128,1,90,0|0,0:0|1:0,0:0:0:0: +356,360,165460,2,0,P|400:360|440:336,1,90,8|0,0:0|0:0,0:0:0:0: +432,208,165790,5,0,1:0:0:0: +292,260,165954,1,0,0:0:0:0: +344,184,166119,1,8,0:0:0:0: +204,236,166284,1,0,1:0:0:0: +204,236,166366,1,0,0:0:0:0: +204,236,166449,2,0,L|216:328,1,90,0|0,0:0|1:0,0:0:0:0: +120,208,166779,2,0,L|131:118,1,90,8|0,0:0|0:0,0:0:0:0: +204,236,167108,5,0,1:0:0:0: +32,216,167273,1,0,0:0:0:0: +130,118,167438,1,8,0:0:0:0: +110,298,167603,1,0,0:0:0:0: +110,298,167685,1,0,0:0:0:0: +110,298,167768,2,0,L|121:208,1,90,0|0,0:0|1:0,0:0:0:0: +304,40,168097,2,0,L|315:130,1,90,8|0,0:0|0:0,0:0:0:0: +328,236,168427,5,0,1:0:0:0: +184,148,168592,1,0,0:0:0:0: +314,129,168757,1,8,0:0:0:0: +197,254,168921,1,0,1:0:0:0: +197,254,169004,1,0,0:0:0:0: +197,254,169086,2,0,P|220:292|260:312,1,90,0|0,0:0|1:0,0:0:0:0: +409,210,169416,2,0,P|365:211|328:236,1,90,8|0,0:0|0:0,0:0:0:0: +488,232,169746,6,0,P|487:192|464:149,1,90,0|0,1:0|0:0,0:0:0:0: +314,129,170075,1,8,0:0:0:0: +409,210,170240,1,0,0:0:0:0: +332,40,170405,2,0,L|240:36,1,90,0|0,0:0|1:0,0:0:0:0: +68,144,170735,2,0,L|157:140,1,90,8|0,0:0|0:0,0:0:0:0: +314,129,171064,5,0,1:0:0:0: +332,40,171229,1,0,0:0:0:0: +324,216,171394,1,8,0:0:0:0: +306,305,171559,1,0,1:0:0:0: +257,178,171724,1,0,0:0:0:0: +168,160,171888,1,0,1:0:0:0: +384,164,172053,1,8,0:0:0:0: +473,182,172218,1,0,0:0:0:0: +306,305,172383,6,0,L|216:312,1,90,0|0,1:0|0:0,0:0:0:0: +60,172,172713,1,8,0:0:0:0: +120,260,172877,1,0,0:0:0:0: +168,160,173042,2,0,L|172:68,1,90,0|0,0:0|1:0,0:0:0:0: +309,216,173372,2,0,L|306:306,1,90,8|0,0:0|0:0,0:0:0:0: +120,260,173702,5,0,1:0:0:0: +152,256,173784,1,0,1:0:0:0: +184,252,173866,1,0,1:0:0:0: +309,216,174031,1,8,0:0:0:0: +103,168,174196,1,0,1:0:0:0: +135,164,174279,1,0,1:0:0:0: +167,160,174361,1,0,1:0:0:0: +292,124,174526,1,0,1:0:0:0: +87,76,174691,1,8,1:2:0:0: +119,72,174773,1,0,1:0:0:0: +151,68,174855,1,0,1:0:0:0: +276,32,175020,6,0,L|368:40,1,90,0|0,1:0|0:0,0:0:0:0: +448,108,175350,1,8,0:0:0:0: +292,124,175515,1,0,0:0:0:0: +292,124,175597,1,0,0:0:0:0: +292,124,175680,2,0,L|308:216,1,90,0|0,0:0|1:0,0:0:0:0: +328,320,176009,1,8,0:0:0:0: +408,248,176174,1,0,0:0:0:0: +220,300,176339,6,0,P|176:304|128:292,1,90,0|0,1:0|0:0,0:0:0:0: +16,120,176669,1,8,0:0:0:0: +120,152,176834,1,0,1:0:0:0: +120,152,176916,1,0,0:0:0:0: +120,152,176998,2,0,L|124:200,1,45 +212,176,177163,2,0,L|239:215,1,45,0|0,1:0|0:0,0:0:0:0: +292,124,177328,2,0,P|302:79|283:30,1,90,8|0,0:0|0:0,0:0:0:0: +344,192,177658,6,0,P|372:156|376:104,1,90,0|0,1:0|0:0,0:0:0:0: +212,88,177987,1,8,0:0:0:0: +272,228,178152,1,0,0:0:0:0: +272,228,178235,1,0,0:0:0:0: +272,228,178317,1,0,0:0:0:0: +292,124,178482,1,0,1:0:0:0: +180,180,178647,1,8,0:0:0:0: +200,284,178812,1,0,0:0:0:0: +292,124,178976,5,0,1:0:0:0: +288,92,179059,1,0,0:0:0:0: +280,60,179141,2,0,P|248:24|208:14,1,90,0|8,0:0|0:0,0:0:0:0: +22,65,179471,2,0,P|67:71|112:68,1,90,0|0,1:0|0:0,0:0:0:0: +212,88,179801,1,0,1:0:0:0: +22,65,179965,1,8,0:0:0:0: +180,180,180130,5,0,0:0:0:0: +180,180,180213,1,0,0:0:0:0: +180,180,180295,2,0,P|184:224|172:272,1,90,0|0,1:0|0:0,0:0:0:0: +76,216,180625,2,0,P|72:172|84:124,1,90,8|0,0:0|0:0,0:0:0:0: +380,240,180954,2,0,P|384:284|372:332,1,90,0|0,0:0|1:0,0:0:0:0: +276,276,181284,2,0,P|272:232|284:184,1,90,8|0,0:0|0:0,0:0:0:0: +374,129,181614,5,0,1:0:0:0: +300,352,181779,2,0,L|204:348,2,90,0|8|0,0:0|0:0|1:0,0:0:0:0: +448,180,182273,1,2,0:0:0:0: +448,180,182438,1,2,1:2:0:0: +276,276,182603,1,10,0:0:0:0: +276,276,182768,1,2,0:0:0:0: +96,200,182932,6,0,L|88:108,1,90,0|0,1:0|0:0,0:0:0:0: +96,200,183262,1,8,0:0:0:0: +12,68,183427,2,0,P|72:24|164:68,1,180,0|0,0:0|1:0,0:0:0:0: +140,272,183921,2,0,P|92:284|52:271,1,90,8|0,0:0|0:0,0:0:0:0: +176,156,184251,5,0,1:0:0:0: +208,152,184334,1,0,1:0:0:0: +240,148,184416,1,0,1:0:0:0: +308,64,184581,1,8,0:0:0:0: +296,240,184746,1,0,1:0:0:0: +312,268,184828,1,0,1:0:0:0: +336,284,184910,1,0,1:0:0:0: +368,292,184993,1,0,1:0:0:0: +400,288,185075,1,0,1:0:0:0: +464,184,185240,1,8,0:0:0:0: +468,152,185323,1,0,0:0:0:0: +472,120,185405,2,0,L|464:76,1,45,0|0,1:0|1:0,0:0:0:0: +388,96,185570,6,0,P|360:132|316:148,1,90,4|0,1:2|0:0,0:0:0:0: +224,46,185899,2,0,P|268:43|308:63,1,90,8|0,0:0|0:0,0:0:0:0: +296,240,186229,1,0,0:0:0:0: +308,64,186394,1,0,1:0:0:0: +296,240,186559,2,0,L|312:332,1,90,8|0,0:0|0:0,0:0:0:0: +464,184,186888,6,0,P|420:180|372:188,1,90,0|0,1:0|0:0,0:0:0:0: +296,240,187218,1,8,0:0:0:0: +136,292,187383,2,0,P|94:277|54:249,1,90,0|0,1:0|0:0,0:0:0:0: +21,159,187713,1,0,1:0:0:0: +104,8,187877,2,0,L|124:96,1,90,10|0,0:0|0:0,0:0:0:0: +124,96,188207,6,0,P|152:132|196:148,1,90,0|0,1:0|0:0,0:0:0:0: +287,46,188537,2,0,P|243:43|204:63,1,90,8|0,0:0|0:0,0:0:0:0: +216,240,188866,1,2,0:0:0:0: +204,64,189031,1,0,1:0:0:0: +216,240,189196,2,0,L|200:332,1,90,8|0,0:0|0:0,0:0:0:0: +40,240,189526,5,2,1:2:0:0: +128,192,189691,1,0,0:0:0:0: +216,240,189855,1,8,0:0:0:0: +304,192,190020,1,0,1:0:0:0: +392,240,190185,2,0,L|400:332,1,90,2|0,0:0|1:0,0:0:0:0: +464,168,190515,2,0,L|456:76,1,90,8|0,0:0|0:0,0:0:0:0: +392,240,190844,6,0,P|364:272|312:292,1,90,2|0,1:2|0:0,0:0:0:0: +220,140,191174,2,0,P|248:108|296:92,1,90,8|0,0:0|0:0,0:0:0:0: +324,96,191421,1,0,0:0:0:0: +356,104,191504,2,0,L|340:16,1,90,0|0,0:0|1:0,0:0:0:0: +256,276,191834,2,0,L|272:364,1,90,8|0,0:0|0:0,0:0:0:0: +392,240,192163,5,0,1:0:0:0: +356,104,192328,1,0,0:0:0:0: +220,140,192493,1,8,0:0:0:0: +256,276,192658,1,0,1:0:0:0: +305,191,192823,1,0,0:0:0:0: +212,56,192987,1,0,1:0:0:0: +200,220,193152,1,10,0:0:0:0: +200,220,193482,6,0,P|156:228|108:220,1,90,0|0,1:0|0:0,0:0:0:0: +88,116,193812,1,8,0:0:0:0: +16,192,193976,1,0,0:0:0:0: +16,192,194059,1,0,0:0:0:0: +16,192,194141,2,0,L|28:288,1,90,2|0,0:0|1:0,0:0:0:0: +188,309,194471,2,0,L|200:220,1,90,8|0,0:0|0:0,0:0:0:0: +216,112,194801,5,2,1:2:0:0: +216,112,194883,1,0,1:0:0:0: +216,112,194965,1,0,1:0:0:0: +361,25,195130,1,8,0:0:0:0: +294,180,195295,1,0,1:0:0:0: +294,180,195377,1,0,1:0:0:0: +294,180,195460,1,2,0:0:0:0: +256,16,195625,1,0,1:0:0:0: +384,127,195790,1,10,1:2:0:0: +416,132,195872,1,0,1:0:0:0: +448,140,195954,2,0,L|452:84,1,45,0|0,1:0|1:0,0:0:0:0: +416,216,196119,6,0,P|412:264|432:312,1,90,4|0,1:2|0:0,0:0:0:0: +304,268,196449,2,0,P|308:220|288:172,1,90,8|0,0:0|0:0,0:0:0:0: +216,112,196779,2,0,L|120:104,1,90,0|0,0:0|1:0,0:0:0:0: +52,248,197108,2,0,L|141:255,1,90,8|0,0:0|0:0,0:0:0:0: +304,268,197438,5,0,1:0:0:0: +416,216,197603,1,0,0:0:0:0: +408,340,197768,1,8,0:0:0:0: +332,180,197932,1,0,1:0:0:0: +332,180,198015,1,0,0:0:0:0: +332,180,198097,2,0,P|360:140|400:120,1,90,0|0,0:0|1:0,0:0:0:0: +484,284,198427,1,10,0:0:0:0: +304,268,198592,1,2,0:0:0:0: +416,216,198757,6,0,P|428:172|420:124,1,90,2|0,1:2|0:0,0:0:0:0: +344,52,199086,1,8,0:0:0:0: +332,180,199251,1,0,0:0:0:0: +164,236,199416,2,0,P|152:192|160:144,1,90,0|0,0:0|1:0,0:0:0:0: +236,72,199746,1,8,0:0:0:0: +248,200,199910,1,0,0:0:0:0: +156,328,200075,6,0,L|56:320,1,90,2|0,1:2|0:0,0:0:0:0: +164,236,200405,1,8,0:0:0:0: +256,292,200570,2,0,P|300:296|344:284,1,90,0|0,1:0|0:0,0:0:0:0: +432,220,200899,2,0,L|460:308,2,90,0|8|0,1:0|0:0|0:0,0:0:0:0: +392,120,201394,5,4,1:2:0:0: +396,32,201559,1,0,1:0:0:0: +316,72,201724,1,0,1:0:0:0: +256,6,201888,1,0,1:0:0:0: +228,91,202053,1,0,1:0:0:0: +139,87,202218,1,0,1:0:0:0: +179,166,202383,1,0,1:0:0:0: +113,226,202548,1,0,1:0:0:0: +197,253,202713,5,4,1:2:0:0: +193,342,202877,1,0,1:0:0:0: +272,302,203042,1,0,1:0:0:0: +332,367,203207,1,0,1:0:0:0: +359,283,203372,1,2,1:2:0:0: +448,287,203537,1,2,1:2:0:0: +407,208,203702,1,2,1:2:0:0: +472,147,203866,1,2,1:2:0:0: +387,121,204031,5,4,1:2:0:0: +360,100,204114,1,0,1:0:0:0: +344,72,204196,1,0,1:0:0:0: +336,40,204279,1,0,1:0:0:0: +340,8,204361,1,0,1:0:0:0: +316,28,204443,1,0,1:0:0:0: +284,32,204526,1,0,1:0:0:0: +252,28,204608,1,0,1:0:0:0: +228,8,204691,2,0,L|184:20,7,45,4|0|0|0|0|0|0|0,1:2|1:0|1:0|1:0|1:0|1:0|1:0|1:0,0:0:0:0: +112,56,205350,5,4,1:2:0:0: +100,84,205432,1,0,1:0:0:0: +96,116,205515,1,0,1:0:0:0: +100,148,205597,1,0,1:0:0:0: +112,176,205680,1,0,1:0:0:0: +124,204,205762,1,0,1:0:0:0: +128,236,205844,1,0,1:0:0:0: +124,268,205927,1,0,1:0:0:0: +112,296,206009,2,0,L|71:313,3,45,2|0|2|0,1:2|0:0|0:0|0:0,0:0:0:0: +192,312,206339,2,0,L|175:353,3,45,2|0|2|0,1:2|0:0|0:0|0:0,0:0:0:0: +256,264,206669,5,4,1:2:0:0: +256,192,206751,12,0,209306,0:0:0:0: diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 3cd425ea44..24843f8c32 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -290,8 +290,12 @@ namespace osu.Game.Beatmaps.Formats string[] split = line.Split(','); EventType type; + if (!Enum.TryParse(split[0], out type)) - throw new InvalidDataException($@"Unknown event type {split[0]}"); + { + Logger.Log($"A beatmap event could not be parsed and will be ignored: {split[0]}", LoggingTarget.Runtime, LogLevel.Important); + return; + } switch (type) { diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index f6e2bf6966..1dcafb4669 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -10,6 +10,7 @@ using osuTK; using osuTK.Graphics; using osu.Framework.Graphics; using osu.Framework.IO.File; +using osu.Framework.Logging; using osu.Game.Storyboards; namespace osu.Game.Beatmaps.Formats @@ -83,7 +84,7 @@ namespace osu.Game.Beatmaps.Formats EventType type; if (!Enum.TryParse(split[0], out type)) - throw new InvalidDataException($@"Unknown event type {split[0]}"); + Logger.Log($"A storyboard event could not be parsed and will be ignored: {split[0]}", LoggingTarget.Runtime, LogLevel.Important); switch (type) { From a5c17ae26d81b778c2b8e87f2c1c074d7bf72267 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 6 Aug 2019 10:14:36 +0900 Subject: [PATCH 2028/2854] Don't use GetDecoder --- osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 98464b8d91..7e0374023e 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -486,12 +486,13 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeInvalidEvents() { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + using (var normalResStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var normalStream = new StreamReader(normalResStream)) using (var resStream = TestResources.OpenResource("invalid-events.osu")) using (var stream = new StreamReader(resStream)) { - var decoder = Decoder.GetDecoder(stream); var goodBeatmap = decoder.Decode(normalStream); Beatmap badBeatmap = null; From 1fc7ddf621611e9d773fb5b0df218d8a18992681 Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 5 Aug 2019 18:14:41 -0700 Subject: [PATCH 2029/2854] Fix text depth regression --- osu.Game/Screens/Select/FooterButton.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs index 90bc902ad8..15088f0eb3 100644 --- a/osu.Game/Screens/Select/FooterButton.cs +++ b/osu.Game/Screens/Select/FooterButton.cs @@ -67,16 +67,6 @@ namespace osu.Game.Screens.Select Shear = SHEARING; Children = new Drawable[] { - TextContainer = new Container - { - Size = new Vector2(100 - SHEAR_WIDTH, 50), - Shear = -SHEARING, - Child = SpriteText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } - }, box = new Box { RelativeSizeAxes = Axes.Both, @@ -90,6 +80,16 @@ namespace osu.Game.Screens.Select EdgeSmoothness = new Vector2(2, 0), RelativeSizeAxes = Axes.X, }, + TextContainer = new Container + { + Size = new Vector2(100 - SHEAR_WIDTH, 50), + Shear = -SHEARING, + Child = SpriteText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }, }; } From b8c38d4dfd5ba9a8cd3acc70ab0f0fcda7495cb7 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 6 Aug 2019 10:36:26 +0900 Subject: [PATCH 2030/2854] remove unnecessary assert --- osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 7e0374023e..bbc3b05d89 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -494,9 +494,8 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var stream = new StreamReader(resStream)) { var goodBeatmap = decoder.Decode(normalStream); - Beatmap badBeatmap = null; + var badBeatmap = decoder.Decode(stream); - Assert.DoesNotThrow(() => badBeatmap = decoder.Decode(stream)); Assert.AreEqual(goodBeatmap.HitObjects.Count, badBeatmap.HitObjects.Count); } } From 2c32d886d7cf77e7615ebe93a7afadb00b1d81ef Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 6 Aug 2019 10:39:54 +0900 Subject: [PATCH 2031/2854] Add better asserts --- osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index bbc3b05d89..e88c3c8ecc 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -496,6 +496,8 @@ namespace osu.Game.Tests.Beatmaps.Formats var goodBeatmap = decoder.Decode(normalStream); var badBeatmap = decoder.Decode(stream); + Assert.AreEqual(goodBeatmap.Breaks[0].Duration, badBeatmap.Breaks[0].Duration); + Assert.AreEqual(goodBeatmap.Metadata.BackgroundFile, badBeatmap.Metadata.BackgroundFile); Assert.AreEqual(goodBeatmap.HitObjects.Count, badBeatmap.HitObjects.Count); } } From 66b02c02831a2004c53b755d0159fea08c9bf2ad Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 6 Aug 2019 12:27:10 +0900 Subject: [PATCH 2032/2854] log type as well --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 24843f8c32..c92c0ba22d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -293,7 +293,7 @@ namespace osu.Game.Beatmaps.Formats if (!Enum.TryParse(split[0], out type)) { - Logger.Log($"A beatmap event could not be parsed and will be ignored: {split[0]}", LoggingTarget.Runtime, LogLevel.Important); + Logger.Log($"A beatmap {type} event could not be parsed and will be ignored: {split[0]}", LoggingTarget.Runtime, LogLevel.Important); return; } diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 1dcafb4669..512eea0822 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -84,7 +84,7 @@ namespace osu.Game.Beatmaps.Formats EventType type; if (!Enum.TryParse(split[0], out type)) - Logger.Log($"A storyboard event could not be parsed and will be ignored: {split[0]}", LoggingTarget.Runtime, LogLevel.Important); + Logger.Log($"A storyboard {type} event could not be parsed and will be ignored: {split[0]}", LoggingTarget.Runtime, LogLevel.Important); switch (type) { From 497d2cb67784287c5217fe0c5279bfab7748bab7 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 6 Aug 2019 12:35:18 +0900 Subject: [PATCH 2033/2854] shorten tests and rename --- .../Formats/LegacyBeatmapDecoderTest.cs | 13 +- osu.Game.Tests/Resources/invalid-events.osu | 992 +----------------- osu.Game.Tests/Resources/valid-events.osu | 12 + 3 files changed, 19 insertions(+), 998 deletions(-) create mode 100644 osu.Game.Tests/Resources/valid-events.osu diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index e88c3c8ecc..49a6f646ba 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -488,17 +488,16 @@ namespace osu.Game.Tests.Beatmaps.Formats { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; - using (var normalResStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var normalStream = new StreamReader(normalResStream)) - using (var resStream = TestResources.OpenResource("invalid-events.osu")) - using (var stream = new StreamReader(resStream)) + using (var goodResStream = TestResources.OpenResource("valid-events.osu")) + using (var goodStream = new StreamReader(goodResStream)) + using (var badResStream = TestResources.OpenResource("invalid-events.osu")) + using (var badStream = new StreamReader(badResStream)) { - var goodBeatmap = decoder.Decode(normalStream); - var badBeatmap = decoder.Decode(stream); + var goodBeatmap = decoder.Decode(goodStream); + var badBeatmap = decoder.Decode(badStream); Assert.AreEqual(goodBeatmap.Breaks[0].Duration, badBeatmap.Breaks[0].Duration); Assert.AreEqual(goodBeatmap.Metadata.BackgroundFile, badBeatmap.Metadata.BackgroundFile); - Assert.AreEqual(goodBeatmap.HitObjects.Count, badBeatmap.HitObjects.Count); } } } diff --git a/osu.Game.Tests/Resources/invalid-events.osu b/osu.Game.Tests/Resources/invalid-events.osu index a8e767585c..df86b26dba 100644 --- a/osu.Game.Tests/Resources/invalid-events.osu +++ b/osu.Game.Tests/Resources/invalid-events.osu @@ -1,43 +1,5 @@ osu file format v14 -[General] -AudioFilename: 03. Renatus - Soleily 192kbps.mp3 -AudioLeadIn: 0 -PreviewTime: 164471 -Countdown: 0 -SampleSet: Soft -StackLeniency: 0.7 -Mode: 0 -LetterboxInBreaks: 0 -WidescreenStoryboard: 0 - -[Editor] -Bookmarks: 11505,22054,32604,43153,53703,64252,74802,85351,95901,106450,116999,119637,130186,140735,151285,161834,164471,175020,185570,196119,206669,209306 -DistanceSpacing: 1.8 -BeatDivisor: 4 -GridSize: 4 -TimelineZoom: 2 - -[Metadata] -Title:Renatus -TitleUnicode:Renatus -Artist:Soleily -ArtistUnicode:Soleily -Creator:Gamu -Version:Insane -Source: -Tags:MBC7 Unisphere 地球ヤバイEP Chikyu Yabai -BeatmapID:557821 -BeatmapSetID:241526 - -[Difficulty] -HPDrainRate:6.5 -CircleSize:4 -OverallDifficulty:8 -ApproachRate:9 -SliderMultiplier:1.8 -SliderTickRate:2 - [Events] bad,event,this,should,fail //Background and Video events @@ -45,960 +7,8 @@ bad,event,this,should,fail //Break Periods 2,122474,140135 //Storyboard Layer 0 (Background) +this,is,also,bad //Storyboard Layer 1 (Fail) //Storyboard Layer 2 (Pass) //Storyboard Layer 3 (Foreground) //Storyboard Sound Samples - -[TimingPoints] -956,329.67032967033,4,2,0,60,1,0 -20736,-100,4,2,0,65,0,0 -22054,-100,4,2,0,70,0,0 -43153,-100,4,2,0,60,0,0 -48428,-100,4,2,0,50,0,0 -52879,-100,4,2,0,50,0,0 -53373,-100,4,2,0,60,0,0 -53703,-100,4,2,0,70,0,1 -74719,-100,4,2,0,70,0,0 -74802,-100,4,2,0,70,0,1 -95901,-100,4,2,0,70,0,0 -116999,-133.333333333333,4,2,0,50,0,0 -117164,-133.333333333333,4,2,0,30,0,0 -117329,-79.9999999999999,4,2,0,50,0,0 -117659,-100,4,2,0,50,0,0 -118977,-100,4,2,0,60,0,0 -119307,-100,4,2,0,70,0,0 -119637,659.340659340659,4,2,0,80,1,0 -119966,-100,4,2,0,70,0,0 -120296,-100,4,2,0,60,0,0 -120626,-100,4,2,0,50,0,0 -120955,-100,4,2,0,40,0,0 -121285,-100,4,2,0,30,0,0 -121615,-100,4,2,0,20,0,0 -121944,-100,4,2,0,10,0,0 -122274,-100,4,2,0,5,0,0 -140735,-100,4,2,0,50,0,0 -151285,-80,4,2,0,60,0,0 -161834,329.67032967033,4,2,0,65,1,0 -164141,-100,4,2,0,70,0,0 -164471,-100,4,2,0,70,0,1 -185487,-100,4,2,0,70,0,0 -185570,-100,4,2,0,70,0,1 -206669,659.340659340659,4,2,0,80,1,0 -206998,-100,4,2,0,70,0,0 -207328,-100,4,2,0,60,0,0 -207658,-100,4,2,0,50,0,0 -207987,-100,4,2,0,40,0,0 -208317,-100,4,2,0,30,0,0 -208647,-100,4,2,0,20,0,0 -208976,-100,4,2,0,10,0,0 -209306,-100,4,2,0,5,0,0 - - -[Colours] -Combo1 : 142,199,255 -Combo2 : 255,128,128 -Combo3 : 128,255,255 -Combo4 : 128,255,128 -Combo5 : 255,187,255 -Combo6 : 255,177,140 -Combo7 : 100,100,100,100 - -[HitObjects] -192,168,956,6,0,P|184:128|200:80,1,90,4|0,1:2|0:0,0:0:0:0: -304,56,1285,1,8,0:0:0:0: -244,236,1450,2,0,P|204:252|156:244,1,90,2|0,0:0|0:0,0:0:0:0: -276,156,1780,2,0,P|310:181|329:226,1,90,2|8,1:2|0:0,0:0:0:0: -300,328,2109,1,2,0:0:0:0: -192,332,2274,6,0,L|144:340,2,45,0|0|0,1:0|0:0|0:0,0:0:0:0: -388,300,2604,1,8,0:0:0:0: -244,236,2769,1,0,1:0:0:0: -232,208,2851,1,0,0:0:0:0: -224,176,2934,1,0,0:0:0:0: -228,144,3016,1,0,0:0:0:0: -244,116,3098,1,0,1:0:0:0: -332,52,3263,2,0,P|376:48|424:56,1,90,8|0,0:0|0:0,0:0:0:0: -488,228,3593,5,0,1:0:0:0: -460,240,3675,1,0,0:0:0:0: -428,236,3758,1,0,0:0:0:0: -292,160,3923,2,0,P|288:204|300:252,1,90,8|0,0:0|0:0,0:0:0:0: -316,276,4170,1,0,0:0:0:0: -344,292,4252,2,0,L|388:300,1,45,0|0,0:0|0:0,0:0:0:0: -288,356,4417,2,0,L|244:364,1,45,0|0,1:0|0:0,0:0:0:0: -168,328,4582,2,0,P|124:324|72:332,1,90,8|0,0:0|0:0,0:0:0:0: -24,188,4912,5,0,1:0:0:0: -56,192,4994,1,0,0:0:0:0: -88,196,5076,1,0,0:0:0:0: -148,108,5241,1,8,0:0:0:0: -188,240,5406,1,0,1:0:0:0: -188,240,5488,1,0,0:0:0:0: -188,240,5571,2,0,L|168:328,1,90,0|0,0:0|1:0,0:0:0:0: -260,216,5901,2,0,P|236:180|188:164,1,90,8|0,0:0|0:0,0:0:0:0: -248,296,6230,6,0,L|348:292,1,90,0|0,1:0|0:0,0:0:0:0: -504,232,6560,1,8,0:0:0:0: -400,204,6725,1,0,0:0:0:0: -392,176,6807,1,0,0:0:0:0: -384,144,6890,1,0,0:0:0:0: -376,116,6972,1,0,0:0:0:0: -368,88,7054,1,0,1:0:0:0: -188,48,7219,2,0,L|208:140,1,90,8|0,0:0|0:0,0:0:0:0: -248,296,7549,5,0,1:0:0:0: -207,135,7714,1,0,0:0:0:0: -156,232,7879,1,8,0:0:0:0: -316,191,8043,1,0,1:0:0:0: -316,191,8126,1,0,0:0:0:0: -316,191,8208,2,0,L|372:200,1,45,0|0,0:0|0:0,0:0:0:0: -492,200,8373,2,0,L|447:207,1,45,0|0,1:0|0:0,0:0:0:0: -408,136,8538,2,0,P|396:92|400:48,1,90,8|0,0:0|0:0,0:0:0:0: -260,32,8868,5,0,1:0:0:0: -252,64,8950,1,0,0:0:0:0: -236,92,9032,2,0,P|204:116|148:128,1,90,0|8,0:0|0:0,0:0:0:0: -28,188,9362,1,0,0:0:0:0: -60,196,9445,1,0,0:0:0:0: -88,212,9527,2,0,P|112:244|124:300,1,90,0|0,0:0|1:0,0:0:0:0: -112,128,9857,2,0,P|152:156|184:196,1,90,8|0,0:0|0:0,0:0:0:0: -216,288,10186,5,0,1:0:0:0: -216,288,10269,1,0,0:0:0:0: -216,288,10351,1,0,0:0:0:0: -268,192,10516,1,8,0:0:0:0: -356,128,10681,1,0,1:0:0:0: -388,120,10763,1,0,0:0:0:0: -420,128,10846,2,0,P|440:168|436:220,1,90,0|0,0:0|1:0,0:0:0:0: -332,328,11175,2,0,L|280:332,1,45,8|8,0:0|0:0,0:0:0:0: -216,288,11340,2,0,L|164:292,1,45,0|0,1:0|0:0,1:0:0:0: -100,248,11505,5,4,1:2:0:0: -148,116,11670,1,2,0:0:0:0: -268,192,11835,1,10,0:0:0:0: -136,328,11999,2,0,L|44:336,1,90,2|0,0:0|0:0,0:0:0:0: -216,288,12329,1,2,1:2:0:0: -148,116,12494,1,10,0:0:0:0: -100,248,12659,1,2,0:0:0:0: -268,192,12824,5,0,1:0:0:0: -268,192,12906,1,0,0:0:0:0: -268,192,12988,1,0,0:0:0:0: -340,272,13153,2,0,P|384:276|432:264,1,90,8|0,0:0|1:0,0:0:0:0: -452,244,13401,1,0,0:0:0:0: -468,216,13483,2,0,L|476:124,1,90,0|0,0:0|1:0,0:0:0:0: -368,32,13813,2,0,L|360:121,1,90,8|0,0:0|0:0,0:0:0:0: -340,272,14142,6,0,L|316:316,2,45,0|0|0,1:0|0:0|0:0,0:0:0:0: -452,244,14472,1,8,0:0:0:0: -268,192,14637,1,0,0:0:0:0: -236,188,14719,1,0,0:0:0:0: -204,192,14802,2,0,P|172:228|160:272,1,90,0|0,0:0|1:0,0:0:0:0: -128,140,15131,2,0,P|160:104|172:60,1,90,8|0,0:0|0:0,0:0:0:0: -64,52,15461,6,0,L|20:68,2,45,0|0|0,1:0|0:0|0:0,0:0:0:0: -171,64,15791,1,8,0:0:0:0: -264,8,15956,2,0,L|356:12,1,90,0|0,1:0|0:0,0:0:0:0: -452,56,16285,1,0,1:0:0:0: -296,140,16450,2,0,L|206:136,1,90,8|0,0:0|0:0,0:0:0:0: -108,184,16780,6,0,P|92:224|96:272,1,90,0|0,1:0|0:0,0:0:0:0: -200,244,17109,1,8,0:0:0:0: -108,108,17274,2,0,L|12:116,1,90,0|0,0:0|0:0,0:0:0:0: -200,244,17604,1,0,1:0:0:0: -296,140,17769,2,0,L|385:132,1,90,8|0,0:0|0:0,0:0:0:0: -480,184,18098,5,0,1:0:0:0: -488,216,18181,1,0,0:0:0:0: -496,248,18263,2,0,L|492:340,1,90,0|8,0:0|0:0,0:0:0:0: -404,224,18593,2,0,L|396:176,2,45,0|0|0,1:0|0:0|0:0,0:0:0:0: -304,264,18923,1,0,1:0:0:0: -200,244,19087,2,0,P|156:240|108:248,1,90,8|0,0:0|0:0,0:0:0:0: -296,140,19417,6,0,P|340:144|388:136,1,90,0|0,1:0|0:0,0:0:0:0: -440,44,19747,1,8,0:0:0:0: -404,224,19912,1,0,0:0:0:0: -404,224,19994,1,0,0:0:0:0: -404,224,20076,2,0,L|412:320,1,90,0|0,0:0|1:0,0:0:0:0: -200,244,20406,2,0,L|192:154,1,90,8|0,0:0|0:0,0:0:0:0: -184,44,20736,5,4,1:2:0:0: -152,40,20818,1,0,0:0:0:0: -120,48,20901,1,0,0:0:0:0: -96,68,20983,1,0,0:0:0:0: -76,92,21065,1,2,0:3:0:0: -64,120,21148,1,0,0:0:0:0: -60,152,21230,1,0,1:0:0:0: -64,184,21313,1,0,0:0:0:0: -76,212,21395,2,0,L|96:252,3,45,0|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: -144,316,21725,2,0,L|188:324,3,45,0|0|2|0,0:0|0:0|0:3|0:0,0:0:0:0: -268,340,22054,6,0,L|364:336,1,90,4|0,1:2|0:0,0:0:0:0: -452,280,22384,1,8,0:0:0:0: -512,188,22549,2,0,P|516:144|504:96,1,90,2|0,0:0|0:0,0:0:0:0: -340,24,22879,2,0,P|336:68|348:116,1,90,2|8,1:2|0:0,0:0:0:0: -420,192,23208,1,2,0:0:0:0: -328,252,23373,6,0,L|232:240,1,90,0|0,1:0|0:0,0:0:0:0: -64,256,23703,1,8,0:0:0:0: -144,184,23868,2,0,P|148:140|136:88,1,90,0|0,1:0|0:0,0:0:0:0: -40,52,24197,1,2,1:2:0:0: -139,95,24362,1,8,0:0:0:0: -216,20,24527,1,0,0:0:0:0: -315,63,24692,6,0,P|360:72|408:68,1,90,2|0,1:2|0:0,0:0:0:0: -492,132,25021,1,8,0:0:0:0: -412,204,25186,2,0,P|403:249|407:297,1,90,2|0,0:0|0:0,0:0:0:0: -268,328,25516,2,0,P|277:283|273:235,1,90,2|8,1:2|0:0,0:0:0:0: -232,140,25846,2,0,P|187:131|139:135,1,90,2|0,0:0|1:0,0:0:0:0: -64,208,26175,5,2,0:0:0:0: -44,316,26340,1,8,0:0:0:0: -148,280,26505,1,2,1:2:0:0: -456,208,26835,1,2,1:2:0:0: -476,316,26999,1,10,0:0:0:0: -372,280,27164,1,2,0:0:0:0: -356,172,27329,6,0,L|380:80,1,90,0|0,1:0|0:0,0:0:0:0: -456,208,27659,1,8,0:0:0:0: -300,236,27824,1,2,0:0:0:0: -300,236,27906,1,0,0:0:0:0: -300,236,27988,2,0,L|208:228,1,90,0|2,0:0|1:2,0:0:0:0: -140,312,28318,1,8,0:0:0:0: -372,280,28483,2,0,L|464:272,1,90,2|0,0:0|1:0,0:0:0:0: -500,136,28813,5,2,0:0:0:0: -432,56,28977,1,8,0:0:0:0: -328,24,29142,2,0,P|284:24|236:28,1,90,2|0,1:2|0:0,0:0:0:0: -80,144,29472,1,2,1:2:0:0: -116,44,29637,1,10,0:0:0:0: -184,128,29802,1,2,0:0:0:0: -20,88,29966,6,0,P|1:164|73:227,1,180,2|10,1:2|0:0,0:0:0:0: -184,128,30461,2,0,P|227:120|276:124,1,90,2|0,0:0|0:0,0:0:0:0: -392,188,30791,1,2,1:2:0:0: -272,260,30956,1,8,0:0:0:0: -396,328,31120,1,0,0:0:0:0: -256,348,31285,5,2,1:2:0:0: -224,344,31368,1,0,1:0:0:0: -192,340,31450,2,0,L|172:248,1,90,2|0,1:2|1:0,0:0:0:0: -8,136,31780,2,0,L|27:223,1,90,2|0,1:2|0:0,0:0:0:0: -56,328,32109,1,2,1:2:0:0: -108,192,32274,1,2,1:2:0:0: -100,160,32357,1,0,1:0:0:0: -92,132,32439,1,2,1:2:0:0: -84,104,32521,1,0,1:0:0:0: -76,72,32604,6,0,P|100:112|148:136,1,90,4|0,1:2|0:0,0:0:0:0: -240,168,32934,1,8,0:0:0:0: -336,124,33098,2,0,L|344:80,2,45,2|0|0,0:0|0:0|0:0,0:0:0:0: -264,248,33428,2,0,P|220:248|176:220,1,90,2|8,1:2|0:0,0:0:0:0: -260,84,33758,1,2,0:0:0:0: -344,212,33923,5,0,1:0:0:0: -344,212,34005,1,0,0:0:0:0: -344,212,34087,1,0,0:0:0:0: -440,160,34252,1,8,0:0:0:0: -312,320,34417,2,0,P|272:336|220:324,1,90,0|0,1:0|0:0,0:0:0:0: -156,176,34747,2,0,P|196:160|248:172,2,90,2|8|0,1:2|0:0|0:0,0:0:0:0: -132,280,35241,5,2,1:2:0:0: -132,280,35324,1,0,0:0:0:0: -132,280,35406,2,0,L|120:376,1,90,0|8,0:0|0:0,0:0:0:0: -312,320,35736,2,0,L|300:230,1,90,2|0,0:0|0:0,0:0:0:0: -316,124,36065,1,2,1:2:0:0: -400,192,36230,1,8,0:0:0:0: -300,230,36395,2,0,P|255:231|211:224,1,90,2|0,0:0|1:0,0:0:0:0: -24,132,36725,5,0,0:0:0:0: -132,152,36890,1,8,0:0:0:0: -60,232,37054,1,2,1:2:0:0: -60,232,37137,1,0,0:0:0:0: -60,232,37219,1,0,0:0:0:0: -92,56,37384,2,0,L|184:44,1,90,2|10,1:2|0:0,0:0:0:0: -316,124,37714,2,0,L|226:135,1,90,2|0,0:0|1:0,0:0:0:0: -60,232,38043,6,0,P|52:276|64:328,1,90,0|8,0:0|0:0,0:0:0:0: -220,152,38373,2,0,P|176:144|124:156,1,90,2|0,0:0|0:0,0:0:0:0: -176,252,38703,1,2,1:2:0:0: -323,213,38868,2,0,L|316:124,1,90,8|2,0:0|0:0,0:0:0:0: -332,320,39197,5,0,1:0:0:0: -424,260,39362,1,2,0:0:0:0: -260,272,39527,2,0,P|246:313|256:360,1,90,8|2,0:0|1:2,0:0:0:0: -408,336,39857,1,0,0:0:0:0: -176,252,40021,2,0,L|80:260,2,90,2|10|2,1:2|0:0|0:0,0:0:0:0: -324,212,40516,5,2,1:2:0:0: -324,212,40598,1,0,1:0:0:0: -324,212,40681,1,0,1:0:0:0: -200,336,40846,1,2,1:2:0:0: -236,188,41010,1,2,1:2:0:0: -236,188,41093,1,0,1:0:0:0: -236,188,41175,1,0,1:0:0:0: -281,357,41340,1,2,1:2:0:0: -176,252,41505,1,2,1:2:0:0: -176,252,41587,1,0,1:0:0:0: -176,252,41670,1,0,1:0:0:0: -344,297,41835,5,2,1:2:0:0: -432,232,41999,1,2,1:2:0:0: -444,204,42082,1,0,1:0:0:0: -448,172,42164,1,0,1:0:0:0: -444,140,42247,1,0,1:0:0:0: -432,112,42329,2,0,L|440:64,2,45,2|0|0,1:2|1:0|1:0,0:0:0:0: -236,188,42659,1,0,0:0:0:0: -340,172,42824,1,2,0:3:0:0: -272,88,42988,1,0,0:0:0:0: -132,160,43153,6,0,P|148:248|220:296,1,180,4|8,1:2|0:0,0:0:0:0: -324,320,43648,2,0,L|336:364,2,45,0|0|0,0:0|0:0|0:0,0:0:0:0: -292,216,43977,1,0,1:0:0:0: -396,240,44142,2,0,P|440:244|488:232,1,90,8|0,0:0|0:0,0:0:0:0: -328,124,44472,6,0,P|284:120|236:132,1,90,0|0,1:0|0:0,0:0:0:0: -168,212,44802,1,8,0:0:0:0: -192,316,44966,1,0,1:0:0:0: -140,220,45131,1,0,0:0:0:0: -83,310,45296,1,0,1:0:0:0: -114,205,45461,1,8,0:0:0:0: -10,229,45626,1,0,0:0:0:0: -106,176,45791,6,0,P|113:133|108:85,1,90,0|0,1:0|0:0,0:0:0:0: -204,136,46120,1,8,0:0:0:0: -256,40,46285,1,0,0:0:0:0: -256,40,46368,1,0,0:0:0:0: -256,40,46450,2,0,L|356:44,1,90,0|0,0:0|1:0,0:0:0:0: -501,124,46780,2,0,L|412:128,1,90,8|0,0:0|0:0,0:0:0:0: -324,192,47109,5,0,1:0:0:0: -356,296,47274,1,0,0:0:0:0: -284,216,47439,1,8,0:0:0:0: -269,323,47604,1,0,1:0:0:0: -237,220,47769,1,0,0:0:0:0: -178,311,47934,1,0,1:0:0:0: -191,203,48098,1,8,0:0:0:0: -99,261,48263,1,0,0:0:0:0: -156,168,48428,6,0,B|176:112|136:64|136:64|200:96,1,180,4|8,1:2|0:0,0:0:0:0: -300,124,48923,2,0,L|392:120,1,90,0|0,0:0|0:0,0:0:0:0: -468,48,49252,1,0,1:0:0:0: -390,120,49417,2,0,P|390:164|406:208,1,90,8|0,0:0|0:0,0:0:0:0: -352,344,49747,6,0,P|352:300|336:256,1,90,4|0,1:2|0:0,0:0:0:0: -240,208,50076,1,8,0:0:0:0: -163,320,50241,2,0,P|207:324|252:316,1,90,0|0,1:0|0:0,0:0:0:0: -240,208,50571,1,0,1:0:0:0: -76,296,50736,2,0,P|76:340|92:384,1,90,8|0,0:0|0:0,0:0:0:0: -312,164,51065,6,0,P|236:124|160:184,1,180,4|8,1:2|0:0,0:0:0:0: -247,297,51560,2,0,L|240:208,1,90,0|0,0:0|0:0,0:0:0:0: -224,48,51890,1,0,1:0:0:0: -332,56,52054,2,0,L|366:58,5,30,8|0|0|0|0|0,0:0|0:0|0:0|0:0|0:0|0:0,0:0:0:0: -408,64,52384,6,0,P|420:108|416:156,1,90,0|0,1:0|0:0,0:0:0:0: -360,260,52714,1,8,0:0:0:0: -247,297,52879,2,0,B|203:281|159:297|159:297|115:313|71:297,1,180,0|0,1:0|1:0,0:0:0:0: -116,196,53373,1,8,0:0:0:0: -120,164,53456,1,0,0:0:0:0: -124,132,53538,1,0,0:0:0:0: -128,100,53620,1,0,0:0:0:0: -132,68,53703,5,4,1:2:0:0: -40,136,53868,1,0,0:0:0:0: -204,160,54032,2,0,L|304:152,1,90,8|0,0:0|0:0,0:0:0:0: -408,64,54362,1,0,0:0:0:0: -408,64,54445,1,0,0:0:0:0: -408,64,54527,2,0,P|404:112|416:160,1,90,0|8,1:0|0:0,0:0:0:0: -484,236,54857,1,0,0:0:0:0: -428,328,55021,5,0,1:0:0:0: -328,296,55186,1,0,0:0:0:0: -328,296,55269,1,0,0:0:0:0: -328,296,55351,1,8,0:0:0:0: -416,300,55516,1,0,1:0:0:0: -472,208,55681,1,0,0:0:0:0: -316,268,55846,1,0,1:0:0:0: -460,180,56010,1,8,0:0:0:0: -304,240,56175,1,0,0:0:0:0: -404,272,56340,5,0,1:0:0:0: -448,152,56505,1,0,0:0:0:0: -448,152,56587,1,0,0:0:0:0: -448,152,56670,2,0,P|456:112|448:60,1,90,8|0,0:0|0:0,0:0:0:0: -268,28,56999,2,0,P|260:68|268:120,1,90,0|0,0:0|1:0,0:0:0:0: -404,272,57329,2,0,P|444:280|496:272,2,90,8|0|0,0:0|0:0|1:0,0:0:0:0: -304,240,57824,5,0,0:0:0:0: -252,336,57988,1,8,0:0:0:0: -196,244,58153,1,0,1:0:0:0: -24,256,58318,1,0,0:0:0:0: -116,200,58483,1,0,1:0:0:0: -136,60,58648,1,8,0:0:0:0: -192,152,58813,1,0,0:0:0:0: -304,240,58977,6,0,P|348:252|396:248,1,90,0|0,1:0|0:0,0:0:0:0: -456,116,59307,2,0,P|412:104|364:108,1,90,8|0,0:0|0:0,0:0:0:0: -273,161,59637,1,0,0:0:0:0: -136,60,59802,1,0,1:0:0:0: -192,152,59966,1,8,0:0:0:0: -23,177,60131,1,0,0:0:0:0: -129,203,60296,5,0,1:0:0:0: -88,304,60461,2,0,P|132:311|176:303,1,90,0|8,0:0|0:0,0:0:0:0: -304,240,60791,1,0,1:0:0:0: -304,240,60873,1,0,0:0:0:0: -304,240,60956,2,0,L|312:288,3,45,0|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: -384,256,61285,2,0,L|392:304,3,45,8|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: -464,272,61615,5,2,1:2:0:0: -488,168,61780,1,2,0:0:0:0: -428,80,61945,1,10,0:0:0:0: -332,32,62109,2,0,P|288:28|240:36,1,90,2|0,0:0|0:0,0:0:0:0: -28,216,62439,1,2,1:2:0:0: -88,304,62604,1,10,0:0:0:0: -184,352,62769,2,0,P|228:356|276:348,1,90,2|0,0:0|1:0,0:0:0:0: -384,256,63098,6,0,P|409:219|426:174,1,90,2|8,0:0|0:0,0:0:0:0: -428,80,63428,2,0,L|420:36,2,45,2|0|0,1:2|0:0|0:0,0:0:0:0: -456,288,63758,1,2,1:2:0:0: -324,200,63923,1,10,1:2:0:0: -292,204,64005,1,0,1:0:0:0: -260,208,64087,1,2,1:2:0:0: -228,212,64170,1,0,1:0:0:0: -196,216,64252,5,4,1:2:0:0: -104,160,64417,1,0,0:0:0:0: -228,296,64582,2,0,L|320:284,1,90,8|0,0:0|0:0,0:0:0:0: -344,112,64912,1,0,0:0:0:0: -344,112,64994,1,0,0:0:0:0: -344,112,65076,2,0,L|254:123,1,90,0|8,1:0|0:0,0:0:0:0: -144,284,65406,2,0,P|148:328|176:364,1,90,0|0,0:0|1:0,0:0:0:0: -196,216,65736,5,0,0:0:0:0: -196,216,65818,1,0,0:0:0:0: -196,216,65901,2,0,P|155:198|110:205,1,90,8|0,0:0|1:0,0:0:0:0: -36,284,66230,1,0,0:0:0:0: -4,180,66395,1,0,1:0:0:0: -132,24,66560,1,8,0:0:0:0: -100,128,66725,1,0,0:0:0:0: -24,48,66890,5,0,1:0:0:0: -212,108,67054,1,0,0:0:0:0: -212,108,67137,1,0,0:0:0:0: -212,108,67219,2,0,L|300:92,1,90,8|0,0:0|0:0,0:0:0:0: -472,144,67549,2,0,L|384:160,1,90,0|0,0:0|1:0,0:0:0:0: -196,216,67879,2,0,P|240:216|288:240,1,90,8|0,0:0|0:0,0:0:0:0: -324,336,68208,5,0,1:0:0:0: -144,288,68373,1,0,0:0:0:0: -58,170,68538,1,8,0:0:0:0: -196,215,68703,1,0,1:0:0:0: -58,260,68868,1,0,0:0:0:0: -144,142,69032,2,0,L|138:108,2,30,0|0|0,1:0|0:0|0:0,0:0:0:0: -144,142,69197,2,0,P|184:124|232:132,1,90,8|0,0:0|0:0,0:0:0:0: -312,248,69527,6,0,L|324:338,1,90,0|0,1:0|0:0,0:0:0:0: -436,248,69857,1,8,0:0:0:0: -432,216,69939,1,0,0:0:0:0: -428,184,70021,1,0,0:0:0:0: -328,120,70186,1,0,0:0:0:0: -324,152,70269,1,0,0:0:0:0: -320,184,70351,1,0,1:0:0:0: -316,216,70434,1,0,0:0:0:0: -312,248,70516,2,0,L|320:300,1,45,8|0,0:0|0:0,0:0:0:0: -244,340,70681,2,0,L|237:295,1,45,0|0,0:0|0:0,0:0:0:0: -216,224,70846,6,0,P|168:216|124:224,1,90,0|0,1:0|0:0,0:0:0:0: -40,288,71175,1,8,0:0:0:0: -2,95,71340,2,0,P|-4:139|4:184,1,90,0|0,1:0|0:0,0:0:0:0: -164,304,71670,1,0,1:0:0:0: -312,248,71835,1,8,0:0:0:0: -244,340,71999,1,0,0:0:0:0: -216,224,72164,6,0,L|228:132,1,90,0|0,1:0|0:0,0:0:0:0: -332,148,72494,2,0,L|344:56,1,90,8|0,0:0|0:0,0:0:0:0: -312,248,72824,1,0,0:0:0:0: -164,304,72988,1,0,1:0:0:0: -332,336,73153,1,8,0:0:0:0: -360,324,73236,1,0,0:0:0:0: -384,304,73318,1,0,0:0:0:0: -399,276,73401,1,0,0:0:0:0: -403,244,73483,6,0,L|396:200,3,45,4|0|2|0,1:2|0:0|0:0|1:0,0:0:0:0: -420,112,73813,2,0,L|427:68,3,45,2|0|2|0,1:2|0:0|1:2|0:0,0:0:0:0: -352,16,74142,2,0,L|345:60,3,45,0|0|2|0,0:0|1:0|1:2|0:0,0:0:0:0: -332,148,74472,1,2,1:2:0:0: -332,148,74554,1,0,1:0:0:0: -332,148,74637,1,2,1:2:0:0: -332,148,74719,1,0,1:0:0:0: -332,148,74802,6,0,P|360:216|320:312,1,180,4|2,1:2|0:3,0:0:0:0: -190,310,75296,2,0,P|151:231|180:148,1,180,4|0,1:2|0:0,0:0:0:0: -256,56,75791,1,0,0:0:0:0: -332,148,75956,1,2,0:3:0:0: -179,148,76120,5,4,1:2:0:0: -336,64,76285,1,4,1:2:0:0: -256,224,76450,1,2,0:3:0:0: -176,64,76615,1,4,1:2:0:0: -256,140,76780,2,0,L|256:324,1,180,2|0,0:0|0:0,0:0:0:0: -364,300,77274,1,2,0:3:0:0: -148,300,77439,6,0,P|104:316|76:356,1,90,4|0,1:2|0:0,0:0:0:0: -24,252,77769,2,0,L|16:208,3,45,8|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: -96,212,78098,2,0,L|104:168,3,45,0|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: -32,128,78428,2,0,L|24:84,3,45,8|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: -104,88,78758,5,2,1:2:0:0: -204,132,78923,1,0,0:0:0:0: -236,124,79005,1,0,0:0:0:0: -268,116,79087,2,0,L|280:68,3,45,8|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: -348,100,79417,2,0,L|360:52,3,45,0|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: -428,84,79747,1,8,1:2:0:0: -460,76,79829,1,0,1:0:0:0: -492,68,79912,1,0,1:0:0:0: -492,260,80076,6,0,P|400:248|328:296,1,180,4|2,1:2|0:3,0:0:0:0: -144,236,80571,2,0,P|236:248|308:200,1,180,4|0,1:2|0:0,0:0:0:0: -348,100,81065,2,0,P|348:56|336:8,1,90,0|2,0:0|0:3,0:0:0:0: -140,48,81395,5,4,1:2:0:0: -244,68,81560,1,4,1:2:0:0: -144,236,81725,1,2,0:3:0:0: -176,133,81890,1,4,1:2:0:0: -184,304,82054,2,0,P|100:300|68:220,1,180,2|0,0:0|0:0,0:0:0:0: -100,116,82549,1,2,0:3:0:0: -264,244,82714,6,0,L|272:340,1,90,4|0,1:2|0:0,0:0:0:0: -380,316,83043,1,8,0:0:0:0: -396,288,83126,1,0,0:0:0:0: -400,256,83208,1,0,0:0:0:0: -396,224,83291,1,0,0:0:0:0: -380,196,83373,2,0,L|336:176,3,45,0|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: -272,148,83703,1,8,0:0:0:0: -256,120,83785,1,0,0:0:0:0: -252,88,83868,1,0,0:0:0:0: -256,56,83950,1,0,0:0:0:0: -272,28,84032,6,0,L|316:8,3,45,2|0|0|0,1:2|0:0|0:0|0:0,0:0:0:0: -360,72,84362,2,0,L|408:72,3,45,8|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: -421,149,84692,2,0,L|464:169,3,45,2|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: -443,244,85021,2,0,L|473:281,3,45,8|0|0|0,1:2|1:0|0:0|0:0,0:0:0:0: -422,339,85351,6,0,L|240:348,1,180,4|2,1:2|0:3,0:0:0:0: -76,172,85846,2,0,L|255:163,1,180,4|0,1:2|0:0,0:0:0:0: -421,149,86340,2,0,P|435:107|428:56,1,90,0|2,0:0|0:3,0:0:0:0: -228,56,86670,5,4,1:2:0:0: -280,192,86835,1,4,1:2:0:0: -328,96,86999,1,2,0:3:0:0: -180,152,87164,1,4,1:2:0:0: -28,100,87330,2,0,P|16:56|20:8,1,90,2|0,0:0|0:0,0:0:0:0: -0,180,87659,1,0,0:0:0:0: -28,284,87824,1,2,0:3:0:0: -108,352,87988,6,0,P|152:360|196:356,1,90,4|0,1:2|0:0,0:0:0:0: -276,284,88318,1,8,0:0:0:0: -304,272,88401,1,0,0:0:0:0: -336,268,88483,1,0,0:0:0:0: -368,272,88565,1,0,0:0:0:0: -396,284,88648,2,0,L|432:312,1,45,0|0,0:0|0:0,0:0:0:0: -488,252,88813,2,0,L|452:224,1,45,0|0,1:0|0:0,0:0:0:0: -400,164,88977,2,0,L|396:116,3,45,8|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: -316,64,89307,6,0,L|320:160,1,90,2|0,1:2|0:0,0:0:0:0: -276,284,89637,1,8,0:0:0:0: -248,296,89719,1,0,0:0:0:0: -216,300,89802,1,0,1:0:0:0: -184,296,89884,1,0,0:0:0:0: -156,284,89966,2,0,L|120:256,1,45,0|0,0:0|0:0,0:0:0:0: -176,200,90131,2,0,L|140:172,1,45,0|0,1:0|0:0,0:0:0:0: -196,116,90296,2,0,L|160:88,3,45,8|0|0|0,1:2|1:0|1:0|0:0,0:0:0:0: -92,44,90626,6,0,P|48:44|24:160,1,180,4|2,1:2|0:3,0:0:0:0: -156,284,91120,2,0,B|200:300|244:284|244:284|288:268|332:284,1,180,4|0,1:2|0:0,0:0:0:0: -176,200,91615,2,0,P|176:156|196:116,1,90,0|2,0:0|0:3,0:0:0:0: -264,28,91945,6,0,L|353:39,1,90,4|0,1:2|1:0,0:0:0:0: -453,159,92274,2,0,L|364:148,1,90,2|4,0:3|1:2,0:0:0:0: -268,196,92604,2,0,P|260:268|328:348,1,180,2|0,0:0|0:0,0:0:0:0: -364,248,93098,1,2,0:3:0:0: -176,200,93263,5,4,1:2:0:0: -72,228,93428,1,0,1:0:0:0: -152,92,93593,1,0,1:0:0:0: -256,64,93758,1,0,1:0:0:0: -336,200,93923,5,0,1:0:0:0: -440,228,94087,1,0,1:0:0:0: -360,92,94252,1,0,1:0:0:0: -256,64,94417,1,0,1:0:0:0: -176,200,94582,5,2,1:2:0:0: -168,228,94664,1,0,1:0:0:0: -168,260,94747,1,0,1:0:0:0: -172,292,94829,1,0,1:0:0:0: -192,316,94912,1,0,1:0:0:0: -220,328,94994,1,0,1:0:0:0: -252,332,95076,1,0,1:0:0:0: -280,320,95159,1,0,1:0:0:0: -300,296,95241,2,0,L|308:248,3,45,2|0|0|0,1:2|1:0|1:0|1:0,0:0:0:0: -312,172,95571,2,0,L|304:127,3,45,0|0|0|0,1:0|1:0|1:0|1:0,0:0:0:0: -256,64,95901,6,0,P|208:56|164:60,1,90,4|0,1:2|0:0,0:0:0:0: -76,116,96230,1,8,0:0:0:0: -60,224,96395,1,0,0:0:0:0: -60,224,96477,1,0,0:0:0:0: -160,184,96642,1,0,0:0:0:0: -160,184,96725,1,0,1:0:0:0: -63,26,96890,2,0,L|76:116,1,90,8|0,0:0|0:0,0:0:0:0: -136,272,97219,5,0,1:0:0:0: -168,268,97302,1,0,0:0:0:0: -200,264,97384,1,0,0:0:0:0: -232,260,97466,1,0,0:0:0:0: -264,256,97549,1,8,0:0:0:0: -384,136,97714,1,0,1:0:0:0: -376,168,97796,1,0,0:0:0:0: -380,200,97879,1,0,0:0:0:0: -392,228,97961,1,0,0:0:0:0: -416,248,98043,2,0,P|464:260|512:260,2,90,0|8|0,1:0|0:0|0:0,0:0:0:0: -231,105,98538,6,0,L|188:116,2,45,0|0|0,1:0|0:0|0:0,0:0:0:0: -376,56,98868,2,0,L|420:64,1,45,8|0,0:0|0:0,0:0:0:0: -384,136,99032,1,0,0:0:0:0: -384,136,99115,2,0,P|340:128|304:92,1,90,0|0,0:0|0:0,0:0:0:0: -303,18,99362,2,0,L|207:26,2,90,0|8|0,1:0|0:0|0:0,0:0:0:0: -452,88,99857,5,0,1:0:0:0: -465,116,99939,1,0,0:0:0:0: -466,147,100021,1,0,0:0:0:0: -456,177,100104,1,0,0:0:0:0: -436,201,100186,2,0,P|416:213|389:216,3,45,8|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: -320,188,100516,2,0,P|300:176|273:173,3,45,0|0|0|0,0:0|1:0|1:0|0:0,0:0:0:0: -204,200,100846,2,0,P|192:220|189:247,3,45,8|0|0|0,0:0|0:0|1:0|0:0,0:0:0:0: -188,320,101175,6,0,P|143:322|100:310,1,90,0|0,1:0|0:0,0:0:0:0: -76,292,101423,1,0,0:0:0:0: -76,292,101505,1,8,0:0:0:0: -76,292,101587,2,0,L|72:248,1,45 -12,68,101835,2,0,L|6:24,2,45,0|0|0,0:0|0:0|1:0,0:0:0:0: -104,140,102164,2,0,L|171:132,3,45,8|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: -224,124,102494,6,0,P|236:164|232:216,1,90,0|0,1:0|0:0,0:0:0:0: -288,296,102824,1,8,0:0:0:0: -288,296,102906,1,0,0:0:0:0: -288,296,102988,2,0,P|328:284|380:288,1,90,0|0,1:0|0:0,0:0:0:0: -404,304,103236,1,0,0:0:0:0: -424,328,103318,1,0,1:0:0:0: -448,188,103483,2,0,L|440:140,3,45,8|0|0|0,0:0|0:0|0:0|0:0,0:0:0:0: -424,72,103813,5,0,1:0:0:0: -324,112,103977,1,0,0:0:0:0: -324,112,104060,1,0,0:0:0:0: -324,112,104142,2,0,P|280:116|232:104,1,90,8|0,0:0|0:0,0:0:0:0: -160,28,104472,1,0,0:0:0:0: -216,208,104637,1,0,1:0:0:0: -216,208,104719,1,0,0:0:0:0: -216,208,104802,1,8,0:0:0:0: -352,240,104966,1,0,0:0:0:0: -384,244,105049,1,0,0:0:0:0: -416,248,105131,6,0,L|460:240,4,45,0|0|0|0|8,1:0|0:0|0:0|0:0|0:0,0:0:0:0: -272,288,105626,1,0,1:0:0:0: -264,320,105708,1,0,0:0:0:0: -256,352,105791,2,0,L|204:356,5,30,0|0|0|0|0|0,0:0|0:0|0:0|1:0|0:0|0:0,0:0:0:0: -156,332,106120,2,0,L|104:336,5,30,8|0|0|0|0|0,0:0|0:0|0:0|1:0|0:0|0:0,0:0:0:0: -56,312,106450,5,4,1:2:0:0: -4,188,106615,1,0,0:0:0:0: -168,220,106780,2,0,P|127:232|79:228,1,90,8|0,0:0|0:0,0:0:0:0: -112,124,107109,1,0,0:0:0:0: -272,216,107274,2,0,L|264:316,1,90,0|8,1:0|0:0,0:0:0:0: -400,268,107604,1,0,0:0:0:0: -428,132,107769,5,0,1:0:0:0: -428,132,107851,1,0,0:0:0:0: -428,132,107934,1,0,0:0:0:0: -428,132,108016,1,0,0:0:0:0: -428,132,108098,1,8,0:0:0:0: -332,84,108263,2,0,P|288:80|232:88,1,90,0|0,1:0|0:0,0:0:0:0: -112,124,108593,1,0,1:0:0:0: -148,264,108758,1,8,0:0:0:0: -16,236,108923,1,0,0:0:0:0: -264,126,109087,6,0,L|272:216,1,90,0|0,1:0|0:0,0:0:0:0: -452,224,109417,2,0,L|460:320,1,90,8|0,0:0|0:0,0:0:0:0: -360,232,109747,1,0,0:0:0:0: -348,56,109912,1,0,1:0:0:0: -416,140,110076,1,8,0:0:0:0: -256,112,110241,2,0,P|212:120|160:112,1,90,0|0,0:0|1:0,0:0:0:0: -348,56,110571,6,0,L|331:150,1,90,0|8,0:0|0:0,0:0:0:0: -208,328,110901,2,0,L|191:239,1,90,0|0,1:0|0:0,0:0:0:0: -184,216,111148,1,0,1:0:0:0: -178,194,111230,1,0,1:0:0:0: -68,272,111395,1,8,0:0:0:0: -56,136,111560,1,0,1:0:0:0: -178,194,111725,6,0,P|219:203|267:199,1,90,4|0,1:2|0:0,0:0:0:0: -364,148,112054,1,8,0:0:0:0: -384,256,112219,2,0,P|406:291|443:322,1,90,0|0,0:0|0:0,0:0:0:0: -488,224,112549,1,0,1:0:0:0: -304,232,112714,2,0,L|208:224,2,90,8|0|0,0:0|0:0|1:0,0:0:0:0: -208,328,113208,6,0,L|112:320,1,90,0|8,0:0|0:0,0:0:0:0: -26,184,113538,2,0,L|116:192,1,90,0|0,1:0|0:0,0:0:0:0: -304,232,113868,1,0,1:0:0:0: -116,192,114032,1,8,0:0:0:0: -224,132,114197,1,0,0:0:0:0: -208,328,114362,6,0,B|272:360|320:312|320:312|340:368,1,180,4|8,1:2|0:0,0:0:0:0: -304,232,114857,2,0,P|300:184|308:140,1,90,0|0,0:0|0:0,0:0:0:0: -384,64,115186,1,0,1:0:0:0: -307,143,115351,1,8,0:0:0:0: -256,48,115516,1,0,0:0:0:0: -456,24,115681,6,0,B|482:101|420:136|420:136|440:184,1,180,4|8,1:2|0:0,0:0:0:0: -384,64,116175,2,0,P|340:56|296:64,1,90,0|0,1:0|0:0,0:0:0:0: -211,171,116505,1,0,1:0:0:0: -439,181,116670,2,0,L|448:84,1,90,8|0,0:0|0:0,0:0:0:0: -372,296,116999,6,2,L|304:292,1,67.5000025749208,2|0,0:1|0:0,0:0:0:0: -136,252,117329,6,2,P|196:260|212:172,1,168.75,0|0,0:0|0:0,0:0:0:0: -192,148,117659,1,2,0:3:0:0: -164,132,117741,1,2,0:3:0:0: -132,124,117824,1,2,1:3:0:0: -100,132,117906,1,2,0:3:0:0: -72,148,117988,2,0,L|52:56,1,90,2|8,0:3|0:0,0:0:0:0: -36,244,118318,5,0,1:0:0:0: -76,344,118483,1,0,1:0:0:0: -184,352,118648,1,0,1:0:0:0: -244,264,118813,1,0,1:0:0:0: -244,264,118895,1,0,1:0:0:0: -244,264,118977,2,0,L|288:260,3,45,2|0|2|0,1:2|0:0|0:0|0:0,0:0:0:0: -332,328,119307,2,0,L|376:324,3,45,2|0|2|0,1:2|0:0|0:0|0:0,0:0:0:0: -412,252,119637,5,4,1:2:0:0: -256,192,119719,12,0,122274,0:0:0:0: -256,192,140735,6,0,L|228:156,1,45,4|0,1:2|0:0,0:0:0:0: -152,132,141065,2,0,P|129:129|104:136,1,45 -48,192,141395,2,0,P|40:236|52:280,1,90,8|8,0:0|0:0,0:0:0:0: -196,352,142054,6,0,L|308:340,1,90,8|8,0:0|1:2,0:0:0:0: -336,280,142549,1,0,0:0:0:0: -404,324,142713,1,8,0:0:0:0: -404,324,142878,1,8,0:0:0:0: -292,120,143373,5,0,1:0:0:0: -212,104,143538,1,0,0:0:0:0: -140,140,143702,1,0,0:0:0:0: -120,220,143867,1,0,0:0:0:0: -144,296,144032,2,0,P|184:320|228:316,1,90,10|8,0:0|0:0,0:0:0:0: -372,212,144691,6,0,P|327:209|290:232,1,90,10|8,0:0|1:2,0:0:0:0: -348,288,145186,1,0,0:0:0:0: -452,220,145351,1,10,0:0:0:0: -452,220,145516,1,8,0:0:0:0: -328,36,146010,5,2,1:2:0:0: -264,88,146175,1,0,0:0:0:0: -184,108,146340,1,0,0:0:0:0: -104,88,146505,1,0,0:0:0:0: -44,36,146669,1,8,0:0:0:0: -44,36,146999,1,8,0:0:0:0: -44,36,147329,6,0,L|24:84,1,45,8|0,0:0|0:0,0:0:0:0: -52,156,147658,2,0,L|71:204,1,45,8|0,1:2|0:0,0:0:0:0: -144,236,147988,1,8,0:0:0:0: -144,236,148153,1,8,0:0:0:0: -316,64,148647,5,0,1:0:0:0: -380,116,148812,1,0,0:0:0:0: -408,192,148977,1,0,0:0:0:0: -380,268,149142,1,0,0:0:0:0: -316,320,149307,2,0,L|224:316,1,90,10|8,0:0|0:0,0:0:0:0: -64,248,149966,5,10,0:0:0:0: -144,236,150131,1,0,0:0:0:0: -188,168,150296,1,8,1:2:0:0: -192,88,150461,1,0,0:0:0:0: -140,24,150626,2,0,P|120:16|96:20,1,45,10|0,0:0|0:0,0:0:0:0: -260,132,150955,2,0,P|280:140|304:136,1,45,2|0,0:0|0:0,0:0:0:0: -476,48,151285,6,0,L|484:160,1,112.5,4|0,1:2|0:0,0:0:0:0: -464,236,151779,1,0,0:0:0:0: -436,308,151944,2,0,P|380:320|324:308,1,112.5,8|8,0:0|0:0,0:0:0:0: -76,308,152604,6,0,P|132:320|188:308,1,112.5,8|8,0:0|1:2,0:0:0:0: -256,88,153263,1,8,0:0:0:0: -256,168,153428,1,8,0:0:0:0: -256,168,153922,5,4,1:2:0:0: -256,248,154087,1,0,0:0:0:0: -324,128,154252,1,0,0:0:0:0: -188,128,154417,1,0,0:0:0:0: -332,212,154582,2,0,L|388:204,1,56.25,10|0,0:0|0:0,0:0:0:0: -492,152,154911,2,0,L|436:144,1,56.25,8|0,0:0|0:0,0:0:0:0: -324,128,155241,5,10,0:0:0:0: -180,212,155406,1,0,0:0:0:0: -332,212,155571,1,8,1:2:0:0: -188,128,155735,1,0,0:0:0:0: -256,248,155900,1,10,0:0:0:0: -256,248,156065,2,0,L|256:304,2,56.25,0|0|0,0:0|0:0|0:0,0:0:0:0: -180,212,156560,6,0,L|124:204,1,56.25,4|0,1:2|0:0,0:0:0:0: -20,152,156889,2,0,L|76:144,1,56.25,0|0,0:0|0:0,0:0:0:0: -188,128,157219,2,0,P|212:72|192:16,1,112.5,8|8,0:0|0:0,0:0:0:0: -132,72,157713,1,0,0:0:0:0: -180,212,157878,6,0,L|236:208,1,56.25,8|0,0:0|0:0,0:0:0:0: -360,252,158208,2,8,L|304:248,1,56.25,8|0,1:2|0:0,0:0:0:0: -168,292,158538,2,0,L|160:356,2,56.25,8|8|0,0:0|0:0|0:0,0:0:0:0: -180,212,159032,1,0,0:0:0:0: -144,140,159197,6,0,P|104:128|36:148,1,112.5,2|0,1:2|0:0,0:0:0:0: -12,220,159691,1,0,0:0:0:0: -36,296,159856,2,0,P|60:316|92:324,1,56.25,8|0,0:0|0:0,0:0:0:0: -215,264,160186,2,0,P|189:273|168:292,1,56.25,8|0,0:0|0:0,0:0:0:0: -228,344,160516,6,0,L|284:340,1,56.25,10|0,0:0|0:0,0:0:0:0: -328,276,160845,2,0,L|384:272,1,56.25,8|0,1:2|0:0,0:0:0:0: -428,208,161175,1,8,0:0:0:0: -440,128,161340,1,8,0:0:0:0: -400,60,161505,1,2,0:0:0:0: -328,28,161669,1,0,0:0:0:0: -212,76,161834,6,0,P|200:120|208:164,1,90,2|0,1:2|1:0,0:0:0:0: -300,308,162163,2,0,P|312:264|304:220,1,90,2|0,1:2|1:0,0:0:0:0: -140,236,162493,2,0,P|184:248|228:240,1,90,2|0,1:2|1:0,0:0:0:0: -372,148,162823,2,0,P|328:136|284:144,1,90,2|0,1:2|1:0,0:0:0:0: -104,316,163152,5,2,1:2:0:0: -78,297,163235,1,0,1:0:0:0: -60,270,163317,1,0,1:0:0:0: -54,239,163399,1,0,1:0:0:0: -58,207,163482,1,2,1:2:0:0: -74,180,163564,1,0,1:0:0:0: -98,159,163647,1,0,1:0:0:0: -127,149,163729,1,0,1:0:0:0: -158,150,163812,2,0,L|208:160,1,45,2|0,1:2|1:0,0:0:0:0: -344,184,163976,2,0,L|294:194,1,45,0|0,1:0|1:0,0:0:0:0: -140,236,164141,1,4,1:2:0:0: -140,236,164471,6,0,L|232:252,1,90,4|0,1:2|0:0,0:0:0:0: -344,184,164801,1,8,0:0:0:0: -380,284,164965,1,0,0:0:0:0: -368,104,165130,2,0,P|324:104|284:128,1,90,0|0,0:0|1:0,0:0:0:0: -356,360,165460,2,0,P|400:360|440:336,1,90,8|0,0:0|0:0,0:0:0:0: -432,208,165790,5,0,1:0:0:0: -292,260,165954,1,0,0:0:0:0: -344,184,166119,1,8,0:0:0:0: -204,236,166284,1,0,1:0:0:0: -204,236,166366,1,0,0:0:0:0: -204,236,166449,2,0,L|216:328,1,90,0|0,0:0|1:0,0:0:0:0: -120,208,166779,2,0,L|131:118,1,90,8|0,0:0|0:0,0:0:0:0: -204,236,167108,5,0,1:0:0:0: -32,216,167273,1,0,0:0:0:0: -130,118,167438,1,8,0:0:0:0: -110,298,167603,1,0,0:0:0:0: -110,298,167685,1,0,0:0:0:0: -110,298,167768,2,0,L|121:208,1,90,0|0,0:0|1:0,0:0:0:0: -304,40,168097,2,0,L|315:130,1,90,8|0,0:0|0:0,0:0:0:0: -328,236,168427,5,0,1:0:0:0: -184,148,168592,1,0,0:0:0:0: -314,129,168757,1,8,0:0:0:0: -197,254,168921,1,0,1:0:0:0: -197,254,169004,1,0,0:0:0:0: -197,254,169086,2,0,P|220:292|260:312,1,90,0|0,0:0|1:0,0:0:0:0: -409,210,169416,2,0,P|365:211|328:236,1,90,8|0,0:0|0:0,0:0:0:0: -488,232,169746,6,0,P|487:192|464:149,1,90,0|0,1:0|0:0,0:0:0:0: -314,129,170075,1,8,0:0:0:0: -409,210,170240,1,0,0:0:0:0: -332,40,170405,2,0,L|240:36,1,90,0|0,0:0|1:0,0:0:0:0: -68,144,170735,2,0,L|157:140,1,90,8|0,0:0|0:0,0:0:0:0: -314,129,171064,5,0,1:0:0:0: -332,40,171229,1,0,0:0:0:0: -324,216,171394,1,8,0:0:0:0: -306,305,171559,1,0,1:0:0:0: -257,178,171724,1,0,0:0:0:0: -168,160,171888,1,0,1:0:0:0: -384,164,172053,1,8,0:0:0:0: -473,182,172218,1,0,0:0:0:0: -306,305,172383,6,0,L|216:312,1,90,0|0,1:0|0:0,0:0:0:0: -60,172,172713,1,8,0:0:0:0: -120,260,172877,1,0,0:0:0:0: -168,160,173042,2,0,L|172:68,1,90,0|0,0:0|1:0,0:0:0:0: -309,216,173372,2,0,L|306:306,1,90,8|0,0:0|0:0,0:0:0:0: -120,260,173702,5,0,1:0:0:0: -152,256,173784,1,0,1:0:0:0: -184,252,173866,1,0,1:0:0:0: -309,216,174031,1,8,0:0:0:0: -103,168,174196,1,0,1:0:0:0: -135,164,174279,1,0,1:0:0:0: -167,160,174361,1,0,1:0:0:0: -292,124,174526,1,0,1:0:0:0: -87,76,174691,1,8,1:2:0:0: -119,72,174773,1,0,1:0:0:0: -151,68,174855,1,0,1:0:0:0: -276,32,175020,6,0,L|368:40,1,90,0|0,1:0|0:0,0:0:0:0: -448,108,175350,1,8,0:0:0:0: -292,124,175515,1,0,0:0:0:0: -292,124,175597,1,0,0:0:0:0: -292,124,175680,2,0,L|308:216,1,90,0|0,0:0|1:0,0:0:0:0: -328,320,176009,1,8,0:0:0:0: -408,248,176174,1,0,0:0:0:0: -220,300,176339,6,0,P|176:304|128:292,1,90,0|0,1:0|0:0,0:0:0:0: -16,120,176669,1,8,0:0:0:0: -120,152,176834,1,0,1:0:0:0: -120,152,176916,1,0,0:0:0:0: -120,152,176998,2,0,L|124:200,1,45 -212,176,177163,2,0,L|239:215,1,45,0|0,1:0|0:0,0:0:0:0: -292,124,177328,2,0,P|302:79|283:30,1,90,8|0,0:0|0:0,0:0:0:0: -344,192,177658,6,0,P|372:156|376:104,1,90,0|0,1:0|0:0,0:0:0:0: -212,88,177987,1,8,0:0:0:0: -272,228,178152,1,0,0:0:0:0: -272,228,178235,1,0,0:0:0:0: -272,228,178317,1,0,0:0:0:0: -292,124,178482,1,0,1:0:0:0: -180,180,178647,1,8,0:0:0:0: -200,284,178812,1,0,0:0:0:0: -292,124,178976,5,0,1:0:0:0: -288,92,179059,1,0,0:0:0:0: -280,60,179141,2,0,P|248:24|208:14,1,90,0|8,0:0|0:0,0:0:0:0: -22,65,179471,2,0,P|67:71|112:68,1,90,0|0,1:0|0:0,0:0:0:0: -212,88,179801,1,0,1:0:0:0: -22,65,179965,1,8,0:0:0:0: -180,180,180130,5,0,0:0:0:0: -180,180,180213,1,0,0:0:0:0: -180,180,180295,2,0,P|184:224|172:272,1,90,0|0,1:0|0:0,0:0:0:0: -76,216,180625,2,0,P|72:172|84:124,1,90,8|0,0:0|0:0,0:0:0:0: -380,240,180954,2,0,P|384:284|372:332,1,90,0|0,0:0|1:0,0:0:0:0: -276,276,181284,2,0,P|272:232|284:184,1,90,8|0,0:0|0:0,0:0:0:0: -374,129,181614,5,0,1:0:0:0: -300,352,181779,2,0,L|204:348,2,90,0|8|0,0:0|0:0|1:0,0:0:0:0: -448,180,182273,1,2,0:0:0:0: -448,180,182438,1,2,1:2:0:0: -276,276,182603,1,10,0:0:0:0: -276,276,182768,1,2,0:0:0:0: -96,200,182932,6,0,L|88:108,1,90,0|0,1:0|0:0,0:0:0:0: -96,200,183262,1,8,0:0:0:0: -12,68,183427,2,0,P|72:24|164:68,1,180,0|0,0:0|1:0,0:0:0:0: -140,272,183921,2,0,P|92:284|52:271,1,90,8|0,0:0|0:0,0:0:0:0: -176,156,184251,5,0,1:0:0:0: -208,152,184334,1,0,1:0:0:0: -240,148,184416,1,0,1:0:0:0: -308,64,184581,1,8,0:0:0:0: -296,240,184746,1,0,1:0:0:0: -312,268,184828,1,0,1:0:0:0: -336,284,184910,1,0,1:0:0:0: -368,292,184993,1,0,1:0:0:0: -400,288,185075,1,0,1:0:0:0: -464,184,185240,1,8,0:0:0:0: -468,152,185323,1,0,0:0:0:0: -472,120,185405,2,0,L|464:76,1,45,0|0,1:0|1:0,0:0:0:0: -388,96,185570,6,0,P|360:132|316:148,1,90,4|0,1:2|0:0,0:0:0:0: -224,46,185899,2,0,P|268:43|308:63,1,90,8|0,0:0|0:0,0:0:0:0: -296,240,186229,1,0,0:0:0:0: -308,64,186394,1,0,1:0:0:0: -296,240,186559,2,0,L|312:332,1,90,8|0,0:0|0:0,0:0:0:0: -464,184,186888,6,0,P|420:180|372:188,1,90,0|0,1:0|0:0,0:0:0:0: -296,240,187218,1,8,0:0:0:0: -136,292,187383,2,0,P|94:277|54:249,1,90,0|0,1:0|0:0,0:0:0:0: -21,159,187713,1,0,1:0:0:0: -104,8,187877,2,0,L|124:96,1,90,10|0,0:0|0:0,0:0:0:0: -124,96,188207,6,0,P|152:132|196:148,1,90,0|0,1:0|0:0,0:0:0:0: -287,46,188537,2,0,P|243:43|204:63,1,90,8|0,0:0|0:0,0:0:0:0: -216,240,188866,1,2,0:0:0:0: -204,64,189031,1,0,1:0:0:0: -216,240,189196,2,0,L|200:332,1,90,8|0,0:0|0:0,0:0:0:0: -40,240,189526,5,2,1:2:0:0: -128,192,189691,1,0,0:0:0:0: -216,240,189855,1,8,0:0:0:0: -304,192,190020,1,0,1:0:0:0: -392,240,190185,2,0,L|400:332,1,90,2|0,0:0|1:0,0:0:0:0: -464,168,190515,2,0,L|456:76,1,90,8|0,0:0|0:0,0:0:0:0: -392,240,190844,6,0,P|364:272|312:292,1,90,2|0,1:2|0:0,0:0:0:0: -220,140,191174,2,0,P|248:108|296:92,1,90,8|0,0:0|0:0,0:0:0:0: -324,96,191421,1,0,0:0:0:0: -356,104,191504,2,0,L|340:16,1,90,0|0,0:0|1:0,0:0:0:0: -256,276,191834,2,0,L|272:364,1,90,8|0,0:0|0:0,0:0:0:0: -392,240,192163,5,0,1:0:0:0: -356,104,192328,1,0,0:0:0:0: -220,140,192493,1,8,0:0:0:0: -256,276,192658,1,0,1:0:0:0: -305,191,192823,1,0,0:0:0:0: -212,56,192987,1,0,1:0:0:0: -200,220,193152,1,10,0:0:0:0: -200,220,193482,6,0,P|156:228|108:220,1,90,0|0,1:0|0:0,0:0:0:0: -88,116,193812,1,8,0:0:0:0: -16,192,193976,1,0,0:0:0:0: -16,192,194059,1,0,0:0:0:0: -16,192,194141,2,0,L|28:288,1,90,2|0,0:0|1:0,0:0:0:0: -188,309,194471,2,0,L|200:220,1,90,8|0,0:0|0:0,0:0:0:0: -216,112,194801,5,2,1:2:0:0: -216,112,194883,1,0,1:0:0:0: -216,112,194965,1,0,1:0:0:0: -361,25,195130,1,8,0:0:0:0: -294,180,195295,1,0,1:0:0:0: -294,180,195377,1,0,1:0:0:0: -294,180,195460,1,2,0:0:0:0: -256,16,195625,1,0,1:0:0:0: -384,127,195790,1,10,1:2:0:0: -416,132,195872,1,0,1:0:0:0: -448,140,195954,2,0,L|452:84,1,45,0|0,1:0|1:0,0:0:0:0: -416,216,196119,6,0,P|412:264|432:312,1,90,4|0,1:2|0:0,0:0:0:0: -304,268,196449,2,0,P|308:220|288:172,1,90,8|0,0:0|0:0,0:0:0:0: -216,112,196779,2,0,L|120:104,1,90,0|0,0:0|1:0,0:0:0:0: -52,248,197108,2,0,L|141:255,1,90,8|0,0:0|0:0,0:0:0:0: -304,268,197438,5,0,1:0:0:0: -416,216,197603,1,0,0:0:0:0: -408,340,197768,1,8,0:0:0:0: -332,180,197932,1,0,1:0:0:0: -332,180,198015,1,0,0:0:0:0: -332,180,198097,2,0,P|360:140|400:120,1,90,0|0,0:0|1:0,0:0:0:0: -484,284,198427,1,10,0:0:0:0: -304,268,198592,1,2,0:0:0:0: -416,216,198757,6,0,P|428:172|420:124,1,90,2|0,1:2|0:0,0:0:0:0: -344,52,199086,1,8,0:0:0:0: -332,180,199251,1,0,0:0:0:0: -164,236,199416,2,0,P|152:192|160:144,1,90,0|0,0:0|1:0,0:0:0:0: -236,72,199746,1,8,0:0:0:0: -248,200,199910,1,0,0:0:0:0: -156,328,200075,6,0,L|56:320,1,90,2|0,1:2|0:0,0:0:0:0: -164,236,200405,1,8,0:0:0:0: -256,292,200570,2,0,P|300:296|344:284,1,90,0|0,1:0|0:0,0:0:0:0: -432,220,200899,2,0,L|460:308,2,90,0|8|0,1:0|0:0|0:0,0:0:0:0: -392,120,201394,5,4,1:2:0:0: -396,32,201559,1,0,1:0:0:0: -316,72,201724,1,0,1:0:0:0: -256,6,201888,1,0,1:0:0:0: -228,91,202053,1,0,1:0:0:0: -139,87,202218,1,0,1:0:0:0: -179,166,202383,1,0,1:0:0:0: -113,226,202548,1,0,1:0:0:0: -197,253,202713,5,4,1:2:0:0: -193,342,202877,1,0,1:0:0:0: -272,302,203042,1,0,1:0:0:0: -332,367,203207,1,0,1:0:0:0: -359,283,203372,1,2,1:2:0:0: -448,287,203537,1,2,1:2:0:0: -407,208,203702,1,2,1:2:0:0: -472,147,203866,1,2,1:2:0:0: -387,121,204031,5,4,1:2:0:0: -360,100,204114,1,0,1:0:0:0: -344,72,204196,1,0,1:0:0:0: -336,40,204279,1,0,1:0:0:0: -340,8,204361,1,0,1:0:0:0: -316,28,204443,1,0,1:0:0:0: -284,32,204526,1,0,1:0:0:0: -252,28,204608,1,0,1:0:0:0: -228,8,204691,2,0,L|184:20,7,45,4|0|0|0|0|0|0|0,1:2|1:0|1:0|1:0|1:0|1:0|1:0|1:0,0:0:0:0: -112,56,205350,5,4,1:2:0:0: -100,84,205432,1,0,1:0:0:0: -96,116,205515,1,0,1:0:0:0: -100,148,205597,1,0,1:0:0:0: -112,176,205680,1,0,1:0:0:0: -124,204,205762,1,0,1:0:0:0: -128,236,205844,1,0,1:0:0:0: -124,268,205927,1,0,1:0:0:0: -112,296,206009,2,0,L|71:313,3,45,2|0|2|0,1:2|0:0|0:0|0:0,0:0:0:0: -192,312,206339,2,0,L|175:353,3,45,2|0|2|0,1:2|0:0|0:0|0:0,0:0:0:0: -256,264,206669,5,4,1:2:0:0: -256,192,206751,12,0,209306,0:0:0:0: diff --git a/osu.Game.Tests/Resources/valid-events.osu b/osu.Game.Tests/Resources/valid-events.osu new file mode 100644 index 0000000000..ed3614b4ed --- /dev/null +++ b/osu.Game.Tests/Resources/valid-events.osu @@ -0,0 +1,12 @@ +osu file format v14 + +[Events] +//Background and Video events +0,0,"machinetop_background.jpg",0,0 +//Break Periods +2,122474,140135 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples From ee6a90c48da5c6a1a9612358f68a0011e3095a4d Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 5 Aug 2019 20:43:30 -0700 Subject: [PATCH 2034/2854] Fix back button hover sounds playing in unclickable area --- osu.Game/Graphics/UserInterface/BackButton.cs | 4 ++-- osu.Game/Graphics/UserInterface/TwoLayerButton.cs | 13 ++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/BackButton.cs b/osu.Game/Graphics/UserInterface/BackButton.cs index 48bf0848ae..c4735b4a16 100644 --- a/osu.Game/Graphics/UserInterface/BackButton.cs +++ b/osu.Game/Graphics/UserInterface/BackButton.cs @@ -22,8 +22,8 @@ namespace osu.Game.Graphics.UserInterface Child = button = new TwoLayerButton { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, Text = @"back", Icon = OsuIcon.LeftCircle, Action = () => Action?.Invoke() diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs index c74d00cd1e..75d3847417 100644 --- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs +++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs @@ -15,6 +15,7 @@ using System; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Game.Screens.Select; namespace osu.Game.Graphics.UserInterface { @@ -28,7 +29,9 @@ namespace osu.Game.Graphics.UserInterface private const int transform_time = 600; private const int pulse_length = 250; - private const float shear = 0.1f; + private const float SHEAR_WIDTH = 5f; + + private static readonly Vector2 shearing = new Vector2(SHEAR_WIDTH / Footer.HEIGHT, 0); public static readonly Vector2 SIZE_EXTENDED = new Vector2(140, 50); public static readonly Vector2 SIZE_RETRACTED = new Vector2(100, 50); @@ -56,7 +59,7 @@ namespace osu.Game.Graphics.UserInterface c1.Origin = c1.Anchor = value.HasFlag(Anchor.x2) ? Anchor.TopLeft : Anchor.TopRight; c2.Origin = c2.Anchor = value.HasFlag(Anchor.x2) ? Anchor.TopRight : Anchor.TopLeft; - X = value.HasFlag(Anchor.x2) ? SIZE_RETRACTED.X * shear * 0.5f : 0; + X = value.HasFlag(Anchor.x2) ? SIZE_RETRACTED.X * shearing.X * 0.5f : 0; Remove(c1); Remove(c2); @@ -70,6 +73,7 @@ namespace osu.Game.Graphics.UserInterface public TwoLayerButton() { Size = SIZE_RETRACTED; + Shear = shearing; Children = new Drawable[] { @@ -82,7 +86,6 @@ namespace osu.Game.Graphics.UserInterface new Container { RelativeSizeAxes = Axes.Both, - Shear = new Vector2(shear, 0), Masking = true, MaskingSmoothness = 2, EdgeEffect = new EdgeEffectParameters @@ -105,6 +108,7 @@ namespace osu.Game.Graphics.UserInterface { Anchor = Anchor.Centre, Origin = Anchor.Centre, + Shear = -shearing, }, } }, @@ -119,7 +123,6 @@ namespace osu.Game.Graphics.UserInterface new Container { RelativeSizeAxes = Axes.Both, - Shear = new Vector2(shear, 0), Masking = true, MaskingSmoothness = 2, EdgeEffect = new EdgeEffectParameters @@ -144,6 +147,7 @@ namespace osu.Game.Graphics.UserInterface { Origin = Anchor.Centre, Anchor = Anchor.Centre, + Shear = -shearing, } } }, @@ -188,7 +192,6 @@ namespace osu.Game.Graphics.UserInterface var flash = new Box { RelativeSizeAxes = Axes.Both, - Shear = new Vector2(shear, 0), Colour = Color4.White.Opacity(0.5f), }; Add(flash); From 11aa3544c49ca6c6d4adc86216eae8ac693c038b Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 5 Aug 2019 20:57:17 -0700 Subject: [PATCH 2035/2854] Fix shear width naming --- osu.Game/Graphics/UserInterface/TwoLayerButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs index 75d3847417..b56dd201c2 100644 --- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs +++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs @@ -29,9 +29,9 @@ namespace osu.Game.Graphics.UserInterface private const int transform_time = 600; private const int pulse_length = 250; - private const float SHEAR_WIDTH = 5f; + private const float shear_width = 5f; - private static readonly Vector2 shearing = new Vector2(SHEAR_WIDTH / Footer.HEIGHT, 0); + private static readonly Vector2 shearing = new Vector2(shear_width / Footer.HEIGHT, 0); public static readonly Vector2 SIZE_EXTENDED = new Vector2(140, 50); public static readonly Vector2 SIZE_RETRACTED = new Vector2(100, 50); From ac0abe0692cd81496b6fd5b8839ede050202752d Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 6 Aug 2019 14:21:34 +0900 Subject: [PATCH 2036/2854] Use newly exposed UpdateState --- .../Containers/OsuFocusedOverlayContainer.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index c33e980a9a..eff853abcf 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -55,15 +55,6 @@ namespace osu.Game.Graphics.Containers samplePopOut = audio.Samples.Get(@"UI/overlay-pop-out"); } - protected override void LoadComplete() - { - base.LoadComplete(); - - // This must be added after the base LoadComplete, since onStateChanged contains logic that - // must be run after other state change logic has been completed. - State.ValueChanged += onStateChanged; - } - /// /// Whether mouse input should be blocked screen-wide while this overlay is visible. /// Performing mouse actions outside of the valid extents will hide the overlay. @@ -101,8 +92,10 @@ namespace osu.Game.Graphics.Containers public bool OnReleased(GlobalAction action) => false; - private void onStateChanged(ValueChangedEvent state) + protected override void UpdateState(ValueChangedEvent state) { + base.UpdateState(state); + switch (state.NewValue) { case Visibility.Visible: From dd701eaa6292586c181b35a9cc79bc1e0e677732 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 6 Aug 2019 14:10:03 +0300 Subject: [PATCH 2037/2854] Safely cancel the completion task on restart or immediate exit --- osu.Game/Screens/Play/Player.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 8dc16af575..2f7f793bbf 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -255,7 +255,7 @@ namespace osu.Game.Screens.Play private void performImmediateExit() { // if a restart has been requested, cancel any pending completion (user has shown intent to restart). - onCompletionEvent = null; + onCompletionEvent?.Cancel(); ValidForResume = false; @@ -275,12 +275,8 @@ namespace osu.Game.Screens.Play sampleRestart?.Play(); - // if a restart has been requested, cancel any pending completion (user has shown intent to restart). - onCompletionEvent = null; - - ValidForResume = false; RestartRequested?.Invoke(); - this.Exit(); + performImmediateExit(); } private ScheduledDelegate onCompletionEvent; @@ -306,8 +302,6 @@ namespace osu.Game.Screens.Play scoreManager.Import(score).Wait(); this.Push(CreateResults(score)); - - onCompletionEvent = null; }); } } @@ -471,9 +465,9 @@ namespace osu.Game.Screens.Play public override bool OnExiting(IScreen next) { - if (onCompletionEvent != null) + if (onCompletionEvent != null && !onCompletionEvent.Cancelled && !onCompletionEvent.Completed) + // proceed to result screen if beatmap already finished playing { - // Proceed to result screen if beatmap already finished playing onCompletionEvent.RunTask(); return true; } From 2a68bb2749bf36bc632a1881f124dbbdcc592c96 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 6 Aug 2019 14:11:43 +0300 Subject: [PATCH 2038/2854] onCompletionEvent -> pushResults --- osu.Game/Screens/Play/Player.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 2f7f793bbf..d0ef3bc104 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -255,7 +255,7 @@ namespace osu.Game.Screens.Play private void performImmediateExit() { // if a restart has been requested, cancel any pending completion (user has shown intent to restart). - onCompletionEvent?.Cancel(); + pushResults?.Cancel(); ValidForResume = false; @@ -279,12 +279,12 @@ namespace osu.Game.Screens.Play performImmediateExit(); } - private ScheduledDelegate onCompletionEvent; + private ScheduledDelegate pushResults; private void onCompletion() { // Only show the completion screen if the player hasn't failed - if (ScoreProcessor.HasFailed || onCompletionEvent != null) + if (ScoreProcessor.HasFailed || pushResults != null) return; ValidForResume = false; @@ -293,7 +293,7 @@ namespace osu.Game.Screens.Play using (BeginDelayedSequence(1000)) { - onCompletionEvent = Schedule(delegate + pushResults = Schedule(delegate { if (!this.IsCurrentScreen()) return; @@ -465,10 +465,10 @@ namespace osu.Game.Screens.Play public override bool OnExiting(IScreen next) { - if (onCompletionEvent != null && !onCompletionEvent.Cancelled && !onCompletionEvent.Completed) + if (pushResults != null && !pushResults.Cancelled && !pushResults.Completed) // proceed to result screen if beatmap already finished playing { - onCompletionEvent.RunTask(); + pushResults.RunTask(); return true; } From dda078277a80aa9a465c9edf7ea7b12df3e04f4d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Aug 2019 23:05:12 +0900 Subject: [PATCH 2039/2854] Minor variable name changes --- osu.Game/Screens/Play/Player.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index d0ef3bc104..96b8073d11 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -255,7 +255,7 @@ namespace osu.Game.Screens.Play private void performImmediateExit() { // if a restart has been requested, cancel any pending completion (user has shown intent to restart). - pushResults?.Cancel(); + completionProgressDelegate?.Cancel(); ValidForResume = false; @@ -279,12 +279,12 @@ namespace osu.Game.Screens.Play performImmediateExit(); } - private ScheduledDelegate pushResults; + private ScheduledDelegate completionProgressDelegate; private void onCompletion() { // Only show the completion screen if the player hasn't failed - if (ScoreProcessor.HasFailed || pushResults != null) + if (ScoreProcessor.HasFailed || completionProgressDelegate != null) return; ValidForResume = false; @@ -293,7 +293,7 @@ namespace osu.Game.Screens.Play using (BeginDelayedSequence(1000)) { - pushResults = Schedule(delegate + completionProgressDelegate = Schedule(delegate { if (!this.IsCurrentScreen()) return; @@ -465,10 +465,10 @@ namespace osu.Game.Screens.Play public override bool OnExiting(IScreen next) { - if (pushResults != null && !pushResults.Cancelled && !pushResults.Completed) - // proceed to result screen if beatmap already finished playing + if (completionProgressDelegate != null && !completionProgressDelegate.Cancelled && !completionProgressDelegate.Completed) { - pushResults.RunTask(); + // proceed to result screen if beatmap already finished playing + completionProgressDelegate.RunTask(); return true; } From ffb6794b4ebea7651ed29418f5339964e4208ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=BCbner?= Date: Tue, 6 Aug 2019 16:11:13 +0200 Subject: [PATCH 2040/2854] formatting inspection --- osu.Game/Online/Chat/ChannelManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 30135d5b8b..b5d8879e50 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -222,6 +222,7 @@ namespace osu.Game.Online.Chat } var channel = availableChannels.Where(c => c.Name == content || c.Name == $"#{content}").FirstOrDefault(); + if (channel == null) { target.AddNewMessages(new ErrorMessage($"Channel '{content}' not found.")); From 3cfbbac5dc9ae96335a476b829b46de88b4f2299 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 6 Aug 2019 17:47:31 +0300 Subject: [PATCH 2041/2854] Expand requests --- osu.Game/Online/API/Requests/GetUserRequest.cs | 7 +++++-- .../Online/API/Requests/GetUserScoresRequest.cs | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserRequest.cs b/osu.Game/Online/API/Requests/GetUserRequest.cs index 37ed0574f1..18fb4bb7db 100644 --- a/osu.Game/Online/API/Requests/GetUserRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRequest.cs @@ -2,18 +2,21 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Users; +using osu.Game.Rulesets; namespace osu.Game.Online.API.Requests { public class GetUserRequest : APIRequest { private readonly long? userId; + private readonly RulesetInfo ruleset; - public GetUserRequest(long? userId = null) + public GetUserRequest(long? userId = null, RulesetInfo ruleset = null) { this.userId = userId; + this.ruleset = ruleset; } - protected override string Target => userId.HasValue ? $@"users/{userId}" : @"me"; + protected override string Target => userId.HasValue ? (ruleset != null ? $@"users/{userId}/{ruleset.ShortName}" : $@"users/{userId}") : @"me"; } } diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs index d41966fe1b..7b4d66e7b2 100644 --- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Framework.IO.Network; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets; namespace osu.Game.Online.API.Requests { @@ -10,12 +12,24 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; private readonly ScoreType type; + private readonly RulesetInfo ruleset; - public GetUserScoresRequest(long userId, ScoreType type, int page = 0, int itemsPerPage = 5) + public GetUserScoresRequest(long userId, ScoreType type, int page = 0, int itemsPerPage = 5, RulesetInfo ruleset = null) : base(page, itemsPerPage) { this.userId = userId; this.type = type; + this.ruleset = ruleset; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + + if (ruleset != null) + req.AddParameter("mode", ruleset.ShortName); + + return req; } protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLowerInvariant()}"; From 91b9a496f5c1e3a60fee8136473ee64098c94d20 Mon Sep 17 00:00:00 2001 From: Joehu Date: Tue, 6 Aug 2019 18:29:07 -0700 Subject: [PATCH 2042/2854] Remove now unnecessary .gitmodules and .travis.yml --- .gitmodules | 0 .travis.yml | 2 -- 2 files changed, 2 deletions(-) delete mode 100644 .gitmodules delete mode 100644 .travis.yml diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ce353d9b27..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,2 +0,0 @@ -language: csharp -solution: osu.sln \ No newline at end of file From 616de1830a4c4200fba87a0ef8758e9cea0a6907 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Aug 2019 12:20:49 +0900 Subject: [PATCH 2043/2854] Less sheep --- osu.Game/Screens/Select/FooterButton.cs | 6 +++--- osu.Game/Screens/Select/FooterButtonMods.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs index 15088f0eb3..c1478aa4ce 100644 --- a/osu.Game/Screens/Select/FooterButton.cs +++ b/osu.Game/Screens/Select/FooterButton.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Select { public static readonly float SHEAR_WIDTH = 7.5f; - protected static readonly Vector2 SHEARING = new Vector2(SHEAR_WIDTH / Footer.HEIGHT, 0); + protected static readonly Vector2 SHEAR = new Vector2(SHEAR_WIDTH / Footer.HEIGHT, 0); public string Text { @@ -64,7 +64,7 @@ namespace osu.Game.Screens.Select public FooterButton() { AutoSizeAxes = Axes.Both; - Shear = SHEARING; + Shear = SHEAR; Children = new Drawable[] { box = new Box @@ -83,7 +83,7 @@ namespace osu.Game.Screens.Select TextContainer = new Container { Size = new Vector2(100 - SHEAR_WIDTH, 50), - Shear = -SHEARING, + Shear = -SHEAR, Child = SpriteText = new OsuSpriteText { Anchor = Anchor.Centre, diff --git a/osu.Game/Screens/Select/FooterButtonMods.cs b/osu.Game/Screens/Select/FooterButtonMods.cs index 58d92dbca6..29b1364944 100644 --- a/osu.Game/Screens/Select/FooterButtonMods.cs +++ b/osu.Game/Screens/Select/FooterButtonMods.cs @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Select { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Shear = -SHEARING, + Shear = -SHEAR, Child = modDisplay = new FooterModDisplay { DisplayUnrankedText = false, From c16e84e5e64dda14c07afe74ba14adb940b8438c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 7 Aug 2019 06:35:57 +0300 Subject: [PATCH 2044/2854] Fix unability to get local user with specific ruleset --- osu.Game/Online/API/Requests/GetUserRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/GetUserRequest.cs b/osu.Game/Online/API/Requests/GetUserRequest.cs index 18fb4bb7db..acf9aefa19 100644 --- a/osu.Game/Online/API/Requests/GetUserRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRequest.cs @@ -17,6 +17,6 @@ namespace osu.Game.Online.API.Requests this.ruleset = ruleset; } - protected override string Target => userId.HasValue ? (ruleset != null ? $@"users/{userId}/{ruleset.ShortName}" : $@"users/{userId}") : @"me"; + protected override string Target => userId.HasValue ? (ruleset != null ? $@"users/{userId}/{ruleset.ShortName}" : $@"users/{userId}") : ruleset != null ? $@"me/{ruleset.ShortName}" : $@"me"; } } From 6ed9c983ff1b8f37e981764370d2f9b255a024ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Aug 2019 12:36:00 +0900 Subject: [PATCH 2045/2854] Rename shear variable --- osu.Game/Graphics/UserInterface/TwoLayerButton.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs index b56dd201c2..aa96796cf1 100644 --- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs +++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs @@ -31,7 +31,7 @@ namespace osu.Game.Graphics.UserInterface private const float shear_width = 5f; - private static readonly Vector2 shearing = new Vector2(shear_width / Footer.HEIGHT, 0); + private static readonly Vector2 shear = new Vector2(shear_width / Footer.HEIGHT, 0); public static readonly Vector2 SIZE_EXTENDED = new Vector2(140, 50); public static readonly Vector2 SIZE_RETRACTED = new Vector2(100, 50); @@ -59,7 +59,7 @@ namespace osu.Game.Graphics.UserInterface c1.Origin = c1.Anchor = value.HasFlag(Anchor.x2) ? Anchor.TopLeft : Anchor.TopRight; c2.Origin = c2.Anchor = value.HasFlag(Anchor.x2) ? Anchor.TopRight : Anchor.TopLeft; - X = value.HasFlag(Anchor.x2) ? SIZE_RETRACTED.X * shearing.X * 0.5f : 0; + X = value.HasFlag(Anchor.x2) ? SIZE_RETRACTED.X * shear.X * 0.5f : 0; Remove(c1); Remove(c2); @@ -73,7 +73,7 @@ namespace osu.Game.Graphics.UserInterface public TwoLayerButton() { Size = SIZE_RETRACTED; - Shear = shearing; + Shear = shear; Children = new Drawable[] { @@ -108,7 +108,7 @@ namespace osu.Game.Graphics.UserInterface { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = -shearing, + Shear = -shear, }, } }, @@ -147,7 +147,7 @@ namespace osu.Game.Graphics.UserInterface { Origin = Anchor.Centre, Anchor = Anchor.Centre, - Shear = -shearing, + Shear = -shear, } } }, From 24f9872315e094d1526aca7226878f42911a1e59 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 7 Aug 2019 06:45:39 +0300 Subject: [PATCH 2046/2854] Fix unnecessary scores loading --- osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index a6cc2b0500..ec2697447a 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -132,7 +132,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Scores = null; - if (beatmap?.OnlineBeatmapID.HasValue != true) + if (beatmap?.OnlineBeatmapID.HasValue != true || beatmap?.Status <= BeatmapSetOnlineStatus.Pending) return; loadingAnimation.Show(); From b1c78c43f382a6aae24e2ddcb8ac102013243999 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 7 Aug 2019 06:57:46 +0300 Subject: [PATCH 2047/2854] Simplify target string --- osu.Game/Online/API/Requests/GetUserRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/GetUserRequest.cs b/osu.Game/Online/API/Requests/GetUserRequest.cs index acf9aefa19..31b7e95b39 100644 --- a/osu.Game/Online/API/Requests/GetUserRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRequest.cs @@ -17,6 +17,6 @@ namespace osu.Game.Online.API.Requests this.ruleset = ruleset; } - protected override string Target => userId.HasValue ? (ruleset != null ? $@"users/{userId}/{ruleset.ShortName}" : $@"users/{userId}") : ruleset != null ? $@"me/{ruleset.ShortName}" : $@"me"; + protected override string Target => userId.HasValue ? $@"users/{userId}/{ruleset?.ShortName}" : $@"me/{ruleset?.ShortName}"; } } From b9384d7a53cb5e59074ff2b538b90084664c3d6b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 7 Aug 2019 07:00:50 +0300 Subject: [PATCH 2048/2854] Remove unnecessary null check --- osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index ec2697447a..4bbcd8d631 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -132,7 +132,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Scores = null; - if (beatmap?.OnlineBeatmapID.HasValue != true || beatmap?.Status <= BeatmapSetOnlineStatus.Pending) + if (beatmap?.OnlineBeatmapID.HasValue != true || beatmap.Status <= BeatmapSetOnlineStatus.Pending) return; loadingAnimation.Show(); From b064df91a7065b1b28091208f3638418b1b4df40 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 7 Aug 2019 08:33:55 +0300 Subject: [PATCH 2049/2854] Initial implementation --- .../TestSceneLeaderboardScopeSelector.cs | 37 +++++ .../BeatmapSet/LeaderboardScopeSelector.cs | 157 ++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs create mode 100644 osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs new file mode 100644 index 0000000000..f4c0d32946 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs @@ -0,0 +1,37 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Overlays.BeatmapSet; +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Bindables; +using osu.Game.Screens.Select.Leaderboards; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneLeaderboardScopeSelector : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(LeaderboardScopeSelector), + }; + + public TestSceneLeaderboardScopeSelector() + { + LeaderboardScopeSelector selector; + Bindable scope = new Bindable(); + + Add(selector = new LeaderboardScopeSelector + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Current = { BindTarget = scope } + }); + + AddStep(@"Select global", () => scope.Value = BeatmapLeaderboardScope.Global); + AddStep(@"Select country", () => scope.Value = BeatmapLeaderboardScope.Country); + AddStep(@"Select friend", () => scope.Value = BeatmapLeaderboardScope.Friend); + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs new file mode 100644 index 0000000000..c74c7cbc2b --- /dev/null +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs @@ -0,0 +1,157 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.UserInterface; +using osu.Game.Screens.Select.Leaderboards; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osuTK; +using osu.Game.Graphics.UserInterface; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Sprites; +using osu.Framework.Extensions; +using osu.Game.Graphics; +using osu.Framework.Allocation; +using osuTK.Graphics; +using osu.Framework.Input.Events; +using osu.Framework.Graphics.Colour; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class LeaderboardScopeSelector : TabControl + { + protected override Dropdown CreateDropdown() => null; + + protected override TabItem CreateTabItem(BeatmapLeaderboardScope value) => new ScopeSelectorTabItem(value); + + public LeaderboardScopeSelector() + { + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; + + AddItem(BeatmapLeaderboardScope.Global); + AddItem(BeatmapLeaderboardScope.Country); + AddItem(BeatmapLeaderboardScope.Friend); + + AddInternal(new Line + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }); + } + + protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(20, 0), + }; + + private class ScopeSelectorTabItem : TabItem + { + private const float transition_duration = 100; + + private readonly Box box; + + protected readonly OsuSpriteText Text; + + public ScopeSelectorTabItem(BeatmapLeaderboardScope value) + : base(value) + { + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + Text = new OsuSpriteText + { + Margin = new MarginPadding { Bottom = 8 }, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + Text = value.GetDescription() + " Ranking", + Font = OsuFont.GetFont(weight: FontWeight.Regular), + }, + box = new Box + { + RelativeSizeAxes = Axes.X, + Height = 5, + Scale = new Vector2(1f, 0f), + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + }, + new HoverClickSounds() + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + box.Colour = colours.Blue; + } + + protected override bool OnHover(HoverEvent e) + { + Text.FadeColour(Color4.LightSkyBlue); + + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + + Text.FadeColour(Color4.White); + } + + protected override void OnActivated() + { + box.ScaleTo(new Vector2(1f), transition_duration); + Text.Font = Text.Font.With(weight: FontWeight.Black); + } + + protected override void OnDeactivated() + { + box.ScaleTo(new Vector2(1f, 0f), transition_duration); + Text.Font = Text.Font.With(weight: FontWeight.Regular); + } + } + + private class Line : GridContainer + { + public Line() + { + Height = 1; + RelativeSizeAxes = Axes.X; + Width = 0.8f; + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(mode: GridSizeMode.Relative, size: 0.4f), + new Dimension(), + }; + Content = new[] + { + new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientHorizontal(Color4.Transparent, Color4.Gray), + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Gray, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientHorizontal(Color4.Gray, Color4.Transparent), + }, + } + }; + } + } + } +} From 1d42f0959af01834fbd4f86bd08c84b2f6b14932 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 7 Aug 2019 08:46:27 +0300 Subject: [PATCH 2050/2854] ModIcon improvements --- osu.Game/Overlays/Mods/ModButton.cs | 2 +- osu.Game/Rulesets/UI/ModIcon.cs | 23 ++++++----------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index fa1ee500a8..7b8745cf42 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -96,7 +96,7 @@ namespace osu.Game.Overlays.Mods } } - foregroundIcon.Highlighted = Selected; + foregroundIcon.Highlighted.Value = Selected; SelectionChanged?.Invoke(SelectedMod); return true; diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 86feea09a8..5bb1de7a38 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -11,11 +11,14 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Rulesets.Mods; using osuTK; +using osu.Framework.Bindables; namespace osu.Game.Rulesets.UI { public class ModIcon : Container, IHasTooltip { + public readonly BindableBool Highlighted = new BindableBool(); + private readonly SpriteIcon modIcon; private readonly SpriteIcon background; @@ -97,26 +100,12 @@ namespace osu.Game.Rulesets.UI highlightedColour = colours.PinkLight; break; } - - applyStyle(); } - private bool highlighted; - - public bool Highlighted + protected override void LoadComplete() { - get => highlighted; - - set - { - highlighted = value; - applyStyle(); - } - } - - private void applyStyle() - { - background.Colour = highlighted ? highlightedColour : backgroundColour; + base.LoadComplete(); + Highlighted.BindValueChanged(highlighted => background.Colour = highlighted.NewValue ? highlightedColour : backgroundColour, true); } } } From a99d6536c22011b34879899017d789b296ad6fba Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 7 Aug 2019 08:49:04 +0300 Subject: [PATCH 2051/2854] CI fix --- .../Visual/Online/TestSceneLeaderboardScopeSelector.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs index f4c0d32946..cc3b2ac68b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs @@ -19,10 +19,9 @@ namespace osu.Game.Tests.Visual.Online public TestSceneLeaderboardScopeSelector() { - LeaderboardScopeSelector selector; Bindable scope = new Bindable(); - Add(selector = new LeaderboardScopeSelector + Add(new LeaderboardScopeSelector { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 487979b0164091f9c2928204e660d218ca9faada Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 7 Aug 2019 09:31:07 +0300 Subject: [PATCH 2052/2854] Add Testing --- .../Visual/Online/TestSceneUserRequest.cs | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneUserRequest.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserRequest.cs b/osu.Game.Tests/Visual/Online/TestSceneUserRequest.cs new file mode 100644 index 0000000000..18d6028cb8 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneUserRequest.cs @@ -0,0 +1,109 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mania; +using osu.Game.Users; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Taiko; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Tests.Visual.Online +{ + [TestFixture] + public class TestSceneUserRequest : OsuTestScene + { + [Resolved] + private IAPIProvider api { get; set; } + + private readonly Bindable user = new Bindable(); + private GetUserRequest request; + private readonly DimmedLoadingLayer loading; + + public TestSceneUserRequest() + { + Add(new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new UserTestContainer + { + User = { BindTarget = user } + }, + loading = new DimmedLoadingLayer + { + Alpha = 0 + } + } + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + AddStep(@"local user", () => getUser()); + AddStep(@"local user with taiko ruleset", () => getUser(ruleset: new TaikoRuleset().RulesetInfo)); + AddStep(@"cookiezi", () => getUser(124493)); + AddStep(@"cookiezi with mania ruleset", () => getUser(124493, new ManiaRuleset().RulesetInfo)); + } + + private void getUser(long? userId = null, RulesetInfo ruleset = null) + { + loading.Show(); + + request?.Cancel(); + request = new GetUserRequest(userId, ruleset); + request.Success += user => + { + this.user.Value = user; + loading.Hide(); + }; + api.Queue(request); + } + + private class UserTestContainer : FillFlowContainer + { + public readonly Bindable User = new Bindable(); + + public UserTestContainer() + { + AutoSizeAxes = Axes.Both; + Direction = FillDirection.Vertical; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + User.BindValueChanged(onUserUpdate, true); + } + + private void onUserUpdate(ValueChangedEvent user) + { + Clear(); + + AddRange(new Drawable[] + { + new SpriteText + { + Text = $@"Username: {user.NewValue?.Username}" + }, + new SpriteText + { + Text = $@"RankedScore: {user.NewValue?.Statistics.RankedScore}" + }, + }); + } + } + } +} From 606e977e2d27427229ba09ef62b064b9c4fd826c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Aug 2019 16:07:41 +0900 Subject: [PATCH 2053/2854] Fix twitter link shortener being used --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52fc29cb98..c4d676f4be 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ If you are not interested in developing the game, you can still consume our [bin | ------------- | ------------- | - **Linux** users are recommended to self-compile until we have official deployment in place. -- **iOS** users can join the [TestFlight beta program](https://t.co/PasE1zrHhw) (note that due to high demand this is regularly full). +- **iOS** users can join the [TestFlight beta program](https://testflight.apple.com/join/2tLcjWlF) (note that due to high demand this is regularly full). - **Android** users can self-compile, and expect a public beta soon. If your platform is not listed above, there is still a chance you can manually build it by following the instructions below. From 669c2462eca3cae95a5944e2581585eeb7d5312c Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 7 Aug 2019 16:25:38 +0900 Subject: [PATCH 2054/2854] Don't consider the type --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index c92c0ba22d..24843f8c32 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -293,7 +293,7 @@ namespace osu.Game.Beatmaps.Formats if (!Enum.TryParse(split[0], out type)) { - Logger.Log($"A beatmap {type} event could not be parsed and will be ignored: {split[0]}", LoggingTarget.Runtime, LogLevel.Important); + Logger.Log($"A beatmap event could not be parsed and will be ignored: {split[0]}", LoggingTarget.Runtime, LogLevel.Important); return; } From c0d3d47c2995dc581f716b4a17ecad8f2d742064 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 7 Aug 2019 16:49:53 +0900 Subject: [PATCH 2055/2854] Don't consider type for storyboards either --- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 512eea0822..1dcafb4669 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -84,7 +84,7 @@ namespace osu.Game.Beatmaps.Formats EventType type; if (!Enum.TryParse(split[0], out type)) - Logger.Log($"A storyboard {type} event could not be parsed and will be ignored: {split[0]}", LoggingTarget.Runtime, LogLevel.Important); + Logger.Log($"A storyboard event could not be parsed and will be ignored: {split[0]}", LoggingTarget.Runtime, LogLevel.Important); switch (type) { From c6d481238d91e63051c920ea6f1d670f28e673cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Aug 2019 16:56:22 +0900 Subject: [PATCH 2056/2854] Move QuadBatch to a local variable to stop resharper complaining --- osu.Game/Rulesets/Mods/ModFlashlight.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index cb0c2fafe5..7247bafd23 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -155,12 +155,13 @@ namespace osu.Game.Rulesets.Mods private Vector2 flashlightSize; private float flashlightDim; - private readonly VertexBatch quadBatch = new QuadBatch(1, 1); private readonly Action addAction; public FlashlightDrawNode(Flashlight source) : base(source) { + var quadBatch = new QuadBatch(1, 1); + addAction = v => quadBatch.Add(new PositionAndColourVertex { Position = v.Position, From 7bcec31ea370d87a1a58958c0a405f3ff780de0c Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 7 Aug 2019 17:08:41 +0900 Subject: [PATCH 2057/2854] mention that the event was the type --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 24843f8c32..b1261855f2 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -293,7 +293,7 @@ namespace osu.Game.Beatmaps.Formats if (!Enum.TryParse(split[0], out type)) { - Logger.Log($"A beatmap event could not be parsed and will be ignored: {split[0]}", LoggingTarget.Runtime, LogLevel.Important); + Logger.Log($"Unknown beatmap event of type {split[0]} could not be parsed and will be ignored.", LoggingTarget.Runtime, LogLevel.Important); return; } diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 512eea0822..584388333d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -84,7 +84,7 @@ namespace osu.Game.Beatmaps.Formats EventType type; if (!Enum.TryParse(split[0], out type)) - Logger.Log($"A storyboard {type} event could not be parsed and will be ignored: {split[0]}", LoggingTarget.Runtime, LogLevel.Important); + Logger.Log($"Unknown storyboard event of type {split[0]} could not be parsed and will be ignored.", LoggingTarget.Runtime, LogLevel.Important); switch (type) { From b0e06597159d5259965c6b716f0a35c092645827 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Aug 2019 17:33:30 +0900 Subject: [PATCH 2058/2854] Dispose instead --- osu.Game/Rulesets/Mods/ModFlashlight.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 7247bafd23..4f939362bb 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -155,13 +155,12 @@ namespace osu.Game.Rulesets.Mods private Vector2 flashlightSize; private float flashlightDim; + private readonly VertexBatch quadBatch = new QuadBatch(1, 1); private readonly Action addAction; public FlashlightDrawNode(Flashlight source) : base(source) { - var quadBatch = new QuadBatch(1, 1); - addAction = v => quadBatch.Add(new PositionAndColourVertex { Position = v.Position, @@ -194,6 +193,12 @@ namespace osu.Game.Rulesets.Mods shader.Unbind(); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + quadBatch?.Dispose(); + } } } } From 38df49995ce77e2176b491375144a80227579f58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Aug 2019 18:29:50 +0900 Subject: [PATCH 2059/2854] Fix cursor scale not matching osu-stable --- .../UI/OsuPlayfieldAdjustmentContainer.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs index f7888f8022..9c8be868b0 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs @@ -13,13 +13,15 @@ namespace osu.Game.Rulesets.Osu.UI protected override Container Content => content; private readonly Container content; + private const float playfield_size_adjust = 0.8f; + public OsuPlayfieldAdjustmentContainer() { Anchor = Anchor.Centre; Origin = Anchor.Centre; // Calculated from osu!stable as 512 (default gamefield size) / 640 (default window size) - Size = new Vector2(0.8f); + Size = new Vector2(playfield_size_adjust); InternalChild = new Container { @@ -41,7 +43,19 @@ namespace osu.Game.Rulesets.Osu.UI { base.Update(); + // The following calculation results in a constant of 1.6 when OsuPlayfieldAdjustmentContainer + // is consuming the full game_size. This matches the osu-stable "magic ratio". + // + // game_size = DrawSizePreservingFillContainer.TargetSize = new Vector2(1024, 768) + // + // Parent is a 4:3 aspect enforced, using height as the constricting dimension + // Parent.ChildSize.X = min(game_size.X, game_size.Y * (4 / 3)) * playfield_size_adjust + // Parent.ChildSize.X = 819.2 + // + // Scale = 819.2 / 512 + // Scale = 1.6 Scale = new Vector2(Parent.ChildSize.X / OsuPlayfield.BASE_SIZE.X); + // Size = 0.625 Size = Vector2.Divide(Vector2.One, Scale); } } From da15e19912783b3848d9315ed365013c712dfa2c Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 7 Aug 2019 18:40:58 +0900 Subject: [PATCH 2060/2854] return on storyboard side --- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 584388333d..f950437e75 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -83,8 +83,12 @@ namespace osu.Game.Beatmaps.Formats storyboardSprite = null; EventType type; + if (!Enum.TryParse(split[0], out type)) + { Logger.Log($"Unknown storyboard event of type {split[0]} could not be parsed and will be ignored.", LoggingTarget.Runtime, LogLevel.Important); + return; + } switch (type) { From ec13143ab3ffffebc718b97e3cd1964d4b88905a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Aug 2019 18:45:56 +0900 Subject: [PATCH 2061/2854] Add further xmldoc --- osu.Game/Skinning/LegacySkin.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 19f04c9b7a..3eda76e40f 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -20,6 +20,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.UI; using osuTK; using osuTK.Graphics; @@ -360,6 +361,10 @@ namespace osu.Game.Skinning } } + /// + /// A sprite which is displayed within the playfield, but historically was not considered part of the playfield. + /// Performs scale adjustment to undo the scale applied by (osu! ruleset specifically). + /// private class NonPlayfieldSprite : Sprite { public override Texture Texture @@ -368,7 +373,8 @@ namespace osu.Game.Skinning set { if (value != null) - value.ScaleAdjust *= 2f; + // stable "magic ratio". see OsuPlayfieldAdjustmentContainer for full explanation. + value.ScaleAdjust *= 1.6f; base.Texture = value; } } From 15a592e25eec64efd0d7e53ae9d52356a4dbf6ec Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 7 Aug 2019 19:25:40 +0900 Subject: [PATCH 2062/2854] Just assert doesn't throw and don't catch at LegacyDecoder --- .../Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 10 ++-------- osu.Game.Tests/Resources/valid-events.osu | 12 ------------ osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 9 +-------- 3 files changed, 3 insertions(+), 28 deletions(-) delete mode 100644 osu.Game.Tests/Resources/valid-events.osu diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 49a6f646ba..535320530d 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -484,20 +484,14 @@ namespace osu.Game.Tests.Beatmaps.Formats } [Test] - public void TestDecodeInvalidEvents() + public void TestInvalidEventStillPasses() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; - using (var goodResStream = TestResources.OpenResource("valid-events.osu")) - using (var goodStream = new StreamReader(goodResStream)) using (var badResStream = TestResources.OpenResource("invalid-events.osu")) using (var badStream = new StreamReader(badResStream)) { - var goodBeatmap = decoder.Decode(goodStream); - var badBeatmap = decoder.Decode(badStream); - - Assert.AreEqual(goodBeatmap.Breaks[0].Duration, badBeatmap.Breaks[0].Duration); - Assert.AreEqual(goodBeatmap.Metadata.BackgroundFile, badBeatmap.Metadata.BackgroundFile); + Assert.DoesNotThrow(() => decoder.Decode(badStream)); } } } diff --git a/osu.Game.Tests/Resources/valid-events.osu b/osu.Game.Tests/Resources/valid-events.osu deleted file mode 100644 index ed3614b4ed..0000000000 --- a/osu.Game.Tests/Resources/valid-events.osu +++ /dev/null @@ -1,12 +0,0 @@ -osu file format v14 - -[Events] -//Background and Video events -0,0,"machinetop_background.jpg",0,0 -//Break Periods -2,122474,140135 -//Storyboard Layer 0 (Background) -//Storyboard Layer 1 (Fail) -//Storyboard Layer 2 (Pass) -//Storyboard Layer 3 (Foreground) -//Storyboard Sound Samples diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 7999c82761..17e278c48e 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -43,14 +43,7 @@ namespace osu.Game.Beatmaps.Formats continue; } - try - { - ParseLine(output, section, line); - } - catch (Exception e) - { - Logger.Error(e, $"Failed to process line \"{line}\" into {output}"); - } + ParseLine(output, section, line); } } From 994c1f21ec5d9391787d73a79b4e5846e2627c08 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 7 Aug 2019 19:33:16 +0900 Subject: [PATCH 2063/2854] Output stacked positions for conversion values --- .../OsuBeatmapConversionTest.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs index 392170f0a8..8f975b14a2 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs @@ -8,7 +8,6 @@ using osu.Framework.MathUtils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.UI; using osu.Game.Tests.Beatmaps; namespace osu.Game.Rulesets.Osu.Tests @@ -31,22 +30,22 @@ namespace osu.Game.Rulesets.Osu.Tests { case Slider slider: foreach (var nested in slider.NestedHitObjects) - yield return createConvertValue(nested); + yield return createConvertValue((OsuHitObject)nested); break; default: - yield return createConvertValue(hitObject); + yield return createConvertValue((OsuHitObject)hitObject); break; } - ConvertValue createConvertValue(HitObject obj) => new ConvertValue + ConvertValue createConvertValue(OsuHitObject obj) => new ConvertValue { StartTime = obj.StartTime, EndTime = (obj as IHasEndTime)?.EndTime ?? obj.StartTime, - X = (obj as IHasPosition)?.X ?? OsuPlayfield.BASE_SIZE.X / 2, - Y = (obj as IHasPosition)?.Y ?? OsuPlayfield.BASE_SIZE.Y / 2, + X = obj.StackedPosition.X, + Y = obj.StackedPosition.Y }; } From 42de5934f6f93c95049f254d748256ca9eb7d8b0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 7 Aug 2019 19:33:35 +0900 Subject: [PATCH 2064/2854] Fix incorrect hitobject indices --- osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index 7bb1f42802..a9f8b87fb4 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -211,14 +211,14 @@ namespace osu.Game.Rulesets.Osu.Beatmaps if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.Position) < stack_distance) { currHitObject.StackHeight++; - startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[i].StartTime; + startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[j].StartTime; } else if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.EndPosition) < stack_distance) { //Case for sliders - bump notes down and right, rather than up and left. sliderStack++; beatmap.HitObjects[j].StackHeight -= sliderStack; - startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[i].StartTime; + startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[j].StartTime; } } } From b8ccba02f2017dea054b3fce5413e90dff08670c Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 7 Aug 2019 19:33:54 +0900 Subject: [PATCH 2065/2854] Log to runtime instead --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 17e278c48e..8365fb17d3 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -43,7 +43,14 @@ namespace osu.Game.Beatmaps.Formats continue; } - ParseLine(output, section, line); + try + { + ParseLine(output, section, line); + } + catch (Exception e) + { + Logger.Log($"Failed to process line \"{line}\" into {output}", LoggingTarget.Runtime, LogLevel.Important); + } } } From bc3a340286afb799b3af13197fd10f950b0e19f2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 7 Aug 2019 19:35:39 +0900 Subject: [PATCH 2066/2854] Fix incorrect path position being used for old stacking algorithm --- .../OsuBeatmapConversionTest.cs | 1 + .../Beatmaps/OsuBeatmapProcessor.cs | 8 +- .../old-stacking-expected-conversion.json | 278 ++++++++++++++++++ .../Testing/Beatmaps/old-stacking.osu | 40 +++ 4 files changed, 326 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking-expected-conversion.json create mode 100644 osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking.osu diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs index 8f975b14a2..e9fdf924c3 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs @@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase("slider-ticks")] [TestCase("repeat-slider")] [TestCase("uneven-repeat-slider")] + [TestCase("old-stacking")] public void Test(string name) => base.Test(name); protected override IEnumerable CreateConvertValue(HitObject hitObject) diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index a9f8b87fb4..bb19b783aa 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; +using osuTK; namespace osu.Game.Rulesets.Osu.Beatmaps { @@ -208,12 +209,17 @@ namespace osu.Game.Rulesets.Osu.Beatmaps if (beatmap.HitObjects[j].StartTime - stackThreshold > startTime) break; + // The start position of the hitobject, or the position at the end of the path if the hitobject is a slider + Vector2 position2 = currHitObject is Slider currSlider + ? currSlider.Position + currSlider.Path.PositionAt(1) + : currHitObject.Position; + if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.Position) < stack_distance) { currHitObject.StackHeight++; startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[j].StartTime; } - else if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.EndPosition) < stack_distance) + else if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, position2) < stack_distance) { //Case for sliders - bump notes down and right, rather than up and left. sliderStack++; diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking-expected-conversion.json b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking-expected-conversion.json new file mode 100644 index 0000000000..b994cbd85a --- /dev/null +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking-expected-conversion.json @@ -0,0 +1,278 @@ +{ + "Mappings": [{ + "StartTime": 32165, + "Objects": [{ + "StartTime": 32165, + "EndTime": 32165, + "X": 32, + "Y": 320 + }] + }, + { + "StartTime": 32517, + "Objects": [{ + "StartTime": 32517, + "EndTime": 32517, + "X": 246.396057, + "Y": 182.396057 + }] + }, + { + "StartTime": 32605, + "Objects": [{ + "StartTime": 32605, + "EndTime": 32605, + "X": 249.597382, + "Y": 185.597382 + }] + }, + { + "StartTime": 32693, + "Objects": [{ + "StartTime": 32693, + "EndTime": 32693, + "X": 252.798691, + "Y": 188.798691 + }] + }, + { + "StartTime": 32781, + "Objects": [{ + "StartTime": 32781, + "EndTime": 32781, + "X": 256, + "Y": 192 + }] + }, + { + "StartTime": 33248, + "Objects": [{ + "StartTime": 33248, + "EndTime": 33248, + "X": 39.3960648, + "Y": 76.3960648 + }] + }, + { + "StartTime": 33307, + "Objects": [{ + "StartTime": 33307, + "EndTime": 33307, + "X": 42.5973778, + "Y": 79.597374 + }] + }, + { + "StartTime": 33383, + "Objects": [{ + "StartTime": 33383, + "EndTime": 33383, + "X": 45.798687, + "Y": 82.79869 + }] + }, + { + "StartTime": 33459, + "Objects": [{ + "StartTime": 33459, + "EndTime": 33459, + "X": 49, + "Y": 86 + }, + { + "StartTime": 33635, + "EndTime": 33635, + "X": 123.847847, + "Y": 85.7988 + }, + { + "StartTime": 33811, + "EndTime": 33811, + "X": 198.6957, + "Y": 85.5975952 + }, + { + "StartTime": 33988, + "EndTime": 33988, + "X": 273.9688, + "Y": 85.39525 + }, + { + "StartTime": 34164, + "EndTime": 34164, + "X": 348.816681, + "Y": 85.19404 + }, + { + "StartTime": 34246, + "EndTime": 34246, + "X": 398.998718, + "Y": 85.05914 + } + ] + }, + { + "StartTime": 34341, + "Objects": [{ + "StartTime": 34341, + "EndTime": 34341, + "X": 401.201324, + "Y": 88.20131 + }] + }, + { + "StartTime": 34400, + "Objects": [{ + "StartTime": 34400, + "EndTime": 34400, + "X": 404.402618, + "Y": 91.402626 + }] + }, + { + "StartTime": 34459, + "Objects": [{ + "StartTime": 34459, + "EndTime": 34459, + "X": 407.603943, + "Y": 94.6039352 + }] + }, + { + "StartTime": 34989, + "Objects": [{ + "StartTime": 34989, + "EndTime": 34989, + "X": 163, + "Y": 138 + }, + { + "StartTime": 35018, + "EndTime": 35018, + "X": 188, + "Y": 138 + } + ] + }, + { + "StartTime": 35106, + "Objects": [{ + "StartTime": 35106, + "EndTime": 35106, + "X": 163, + "Y": 138 + }, + { + "StartTime": 35135, + "EndTime": 35135, + "X": 188, + "Y": 138 + } + ] + }, + { + "StartTime": 35224, + "Objects": [{ + "StartTime": 35224, + "EndTime": 35224, + "X": 163, + "Y": 138 + }, + { + "StartTime": 35253, + "EndTime": 35253, + "X": 188, + "Y": 138 + } + ] + }, + { + "StartTime": 35695, + "Objects": [{ + "StartTime": 35695, + "EndTime": 35695, + "X": 166, + "Y": 76 + }, + { + "StartTime": 35871, + "EndTime": 35871, + "X": 240.99855, + "Y": 75.53417 + }, + { + "StartTime": 36011, + "EndTime": 36011, + "X": 315.9971, + "Y": 75.0683441 + } + ] + }, + { + "StartTime": 36106, + "Objects": [{ + "StartTime": 36106, + "EndTime": 36106, + "X": 315, + "Y": 75 + }, + { + "StartTime": 36282, + "EndTime": 36282, + "X": 240.001526, + "Y": 75.47769 + }, + { + "StartTime": 36422, + "EndTime": 36422, + "X": 165.003052, + "Y": 75.95539 + } + ] + }, + { + "StartTime": 36518, + "Objects": [{ + "StartTime": 36518, + "EndTime": 36518, + "X": 166, + "Y": 76 + }, + { + "StartTime": 36694, + "EndTime": 36694, + "X": 240.99855, + "Y": 75.53417 + }, + { + "StartTime": 36834, + "EndTime": 36834, + "X": 315.9971, + "Y": 75.0683441 + } + ] + }, + { + "StartTime": 36929, + "Objects": [{ + "StartTime": 36929, + "EndTime": 36929, + "X": 315, + "Y": 75 + }, + { + "StartTime": 37105, + "EndTime": 37105, + "X": 240.001526, + "Y": 75.47769 + }, + { + "StartTime": 37245, + "EndTime": 37245, + "X": 165.003052, + "Y": 75.95539 + } + ] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking.osu b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking.osu new file mode 100644 index 0000000000..4bc9226d67 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking.osu @@ -0,0 +1,40 @@ +osu file format v3 + +[Difficulty] +HPDrainRate:3 +CircleSize:5 +OverallDifficulty:8 +ApproachRate:8 +SliderMultiplier:1.5 +SliderTickRate:2 + +[TimingPoints] +48,352.941176470588,4,1,1,100,1,0 + +[HitObjects] +// Hit circles +32,320,32165,5,2,0:0:0:0: +256,192,32517,5,0,0:0:0:0: +256,192,32605,1,0,0:0:0:0: +256,192,32693,1,0,0:0:0:0: +256,192,32781,1,0,0:0:0:0: + +// Hit circles on slider endpoints +49,86,33248,1,0,0:0:0:0: +49,86,33307,1,0,0:0:0:0: +49,86,33383,1,0,0:0:0:0: +49,86,33459,2,0,L|421:85,1,350 +398,85,34341,1,0,0:0:0:0: +398,85,34400,1,0,0:0:0:0: +398,85,34459,1,0,0:0:0:0: + +// Sliders +163,138,34989,2,0,L|196:138,1,25 +163,138,35106,2,0,L|196:138,1,25 +163,138,35224,2,0,L|196:138,1,25 + +// Reversed sliders +166,76,35695,2,0,L|327:75,1,150 +315,75,36106,2,0,L|158:76,1,150 +166,76,36518,2,0,L|327:75,1,150 +315,75,36929,2,0,L|158:76,1,150 From bde89adcb72b85fb76476065e017b8533ea79338 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Wed, 7 Aug 2019 19:45:29 +0900 Subject: [PATCH 2067/2854] show exception message --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 8365fb17d3..d6d6804d16 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -49,7 +49,7 @@ namespace osu.Game.Beatmaps.Formats } catch (Exception e) { - Logger.Log($"Failed to process line \"{line}\" into {output}", LoggingTarget.Runtime, LogLevel.Important); + Logger.Log($"Failed to process line \"{line}\" into {output}: {e.Message}", LoggingTarget.Runtime, LogLevel.Important); } } } From 2ab288902759280a2258a07fdd2dbb501d2f398b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Aug 2019 19:56:17 +0900 Subject: [PATCH 2068/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index e64102c401..c4cdffa8a5 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index cf325b77be..02bf053468 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 66f398a927..5b43a6f46f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From c2b3c28c798e2994402ba2fbacd72d9093a99e94 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 7 Aug 2019 16:15:53 +0300 Subject: [PATCH 2069/2854] Use IsBreakTime for checking if currently in a break Rather than iterating over all breaks to find which is in current time --- osu.Game/Screens/Play/Player.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 96b8073d11..e7398be176 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -66,6 +66,8 @@ namespace osu.Game.Screens.Play private SampleChannel sampleRestart; + private BreakOverlay breakOverlay; + protected ScoreProcessor ScoreProcessor { get; private set; } protected DrawableRuleset DrawableRuleset { get; private set; } @@ -134,7 +136,7 @@ namespace osu.Game.Screens.Play } } }, - new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) + breakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -411,8 +413,7 @@ namespace osu.Game.Screens.Play PauseOverlay.Hide(); // breaks and time-based conditions may allow instant resume. - double time = GameplayClockContainer.GameplayClock.CurrentTime; - if (Beatmap.Value.Beatmap.Breaks.Any(b => b.Contains(time)) || time < Beatmap.Value.Beatmap.HitObjects.First().StartTime) + if (breakOverlay.IsBreakTime.Value || GameplayClockContainer.GameplayClock.CurrentTime < Beatmap.Value.Beatmap.HitObjects.First().StartTime) completeResume(); else DrawableRuleset.RequestResume(completeResume); From d3657d82cd042e13c1ea848c003b7e094483575b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 7 Aug 2019 16:28:16 +0300 Subject: [PATCH 2070/2854] Simplify final check for break time --- osu.Game/Screens/Play/BreakOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 93267e5f2a..ea9bba9c95 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -153,7 +153,7 @@ namespace osu.Game.Screens.Play // If the current break doesn't have effects, IsBreakTime should be false. // We also assume that the overlay's fade out transform is "not break time". var currentBreak = breaks[CurrentBreakIndex]; - isBreakTime.Value = currentBreak.HasEffect && time >= currentBreak.StartTime && time <= currentBreak.EndTime - fade_duration; + isBreakTime.Value = currentBreak.HasEffect && currentBreak.Contains(time); } private void initializeBreaks() From ba269a49eef92af0c1ab44dcc73950ed7a8156b4 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 7 Aug 2019 16:59:35 +0300 Subject: [PATCH 2071/2854] Expose break fade duration and add it in the calculation --- osu.Game/Beatmaps/Timing/BreakPeriod.cs | 4 +++- osu.Game/Screens/Play/BreakOverlay.cs | 22 +++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/osu.Game/Beatmaps/Timing/BreakPeriod.cs b/osu.Game/Beatmaps/Timing/BreakPeriod.cs index 856a5fefd4..5d79c7a86b 100644 --- a/osu.Game/Beatmaps/Timing/BreakPeriod.cs +++ b/osu.Game/Beatmaps/Timing/BreakPeriod.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Screens.Play; + namespace osu.Game.Beatmaps.Timing { public class BreakPeriod @@ -35,6 +37,6 @@ namespace osu.Game.Beatmaps.Timing /// /// The time to check in milliseconds. /// Whether the time falls within this . - public bool Contains(double time) => time >= StartTime && time <= EndTime; + public bool Contains(double time) => time >= StartTime && time <= EndTime - BreakOverlay.BREAK_FADE_DURATION; } } diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index ea9bba9c95..444b1bd210 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -17,7 +17,11 @@ namespace osu.Game.Screens.Play { public class BreakOverlay : Container { - private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2; + /// + /// The duration of the break overlay fading. + /// + public const double BREAK_FADE_DURATION = BreakPeriod.MIN_BREAK_DURATION / 2; + private const float remaining_time_container_max_size = 0.3f; private const int vertical_margin = 25; @@ -172,25 +176,25 @@ namespace osu.Game.Screens.Play using (BeginAbsoluteSequence(b.StartTime, true)) { - fadeContainer.FadeIn(fade_duration); - breakArrows.Show(fade_duration); + fadeContainer.FadeIn(BREAK_FADE_DURATION); + breakArrows.Show(BREAK_FADE_DURATION); remainingTimeAdjustmentBox - .ResizeWidthTo(remaining_time_container_max_size, fade_duration, Easing.OutQuint) - .Delay(b.Duration - fade_duration) + .ResizeWidthTo(remaining_time_container_max_size, BREAK_FADE_DURATION, Easing.OutQuint) + .Delay(b.Duration - BREAK_FADE_DURATION) .ResizeWidthTo(0); remainingTimeBox - .ResizeWidthTo(0, b.Duration - fade_duration) + .ResizeWidthTo(0, b.Duration - BREAK_FADE_DURATION) .Then() .ResizeWidthTo(1); remainingTimeCounter.CountTo(b.Duration).CountTo(0, b.Duration); - using (BeginDelayedSequence(b.Duration - fade_duration, true)) + using (BeginDelayedSequence(b.Duration - BREAK_FADE_DURATION, true)) { - fadeContainer.FadeOut(fade_duration); - breakArrows.Hide(fade_duration); + fadeContainer.FadeOut(BREAK_FADE_DURATION); + breakArrows.Hide(BREAK_FADE_DURATION); } } } From 167a81f40a3561b34f3bfb2e5c6acadb93f891c1 Mon Sep 17 00:00:00 2001 From: jorolf Date: Wed, 7 Aug 2019 18:12:11 +0200 Subject: [PATCH 2072/2854] add special skin hit "animations" --- .../Resources/special-skin/hit0-0@2x.png | Bin 0 -> 4020 bytes .../Resources/special-skin/hit100-0@2x.png | Bin 0 -> 5653 bytes .../Resources/special-skin/hit300-0@2x.png | Bin 0 -> 5004 bytes .../Resources/special-skin/hit50-0@2x.png | Bin 0 -> 3861 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit0-0@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit100-0@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit300-0@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit50-0@2x.png diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit0-0@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit0-0@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9d2ed3a7fa3d5bc0aa43098e9247fa3f6a7c6f80 GIT binary patch literal 4020 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit100-0@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit100-0@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..777af3bd415263824913549c5c4c708c4d6e450a GIT binary patch literal 5653 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 Wr=9MwmK5XvL>cIqJgU`pj`<%3F8Egf literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit300-0@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit300-0@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7ee45a7057ca5f1e11d9bea30375c92e139cf01e GIT binary patch literal 5004 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 F_J2M}_?iF! literal 0 HcmV?d00001 From c77e6074f647574b09403131874ba9566ea1e807 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 7 Aug 2019 19:58:26 +0300 Subject: [PATCH 2073/2854] Disallow adding to score after the player has failed --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 2e863f7edb..fd851f2cbb 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -316,6 +316,9 @@ namespace osu.Game.Rulesets.Scoring /// The to apply. protected virtual void ApplyResult(JudgementResult result) { + if (HasFailed) + return; + result.ComboAtJudgement = Combo.Value; result.HighestComboAtJudgement = HighestCombo.Value; result.HealthAtJudgement = Health.Value; From d5b26b86dafbe9b870b50dd23b74f68ad101471d Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 7 Aug 2019 22:18:10 +0300 Subject: [PATCH 2074/2854] Fix storyboard not showing on disabled user dim --- osu.Game/Graphics/Containers/UserDimContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index 03de5f651f..dd5134168f 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -76,9 +76,9 @@ namespace osu.Game.Graphics.Containers /// protected virtual void UpdateVisuals() { - ContentDisplayed = ShowDimContent; + ContentDisplayed = !EnableUserDim.Value || ShowDimContent; - dimContent.FadeTo((ContentDisplayed) ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint); + dimContent.FadeTo(ContentDisplayed ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint); dimContent.FadeColour(EnableUserDim.Value ? OsuColour.Gray(1 - (float)UserDimLevel.Value) : Color4.White, BACKGROUND_FADE_DURATION, Easing.OutQuint); } } From 40a33b338201d543c27e97f7769e1731d5461352 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Aug 2019 10:41:23 +0900 Subject: [PATCH 2075/2854] Move IsLoaded check to more correct place --- osu.Game/Screens/Play/BreakOverlay.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 444b1bd210..78690f156f 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -40,7 +40,8 @@ namespace osu.Game.Screens.Play isBreakTime.Value = false; CurrentBreakIndex = 0; - initializeBreaks(); + if (IsLoaded) + initializeBreaks(); } } @@ -162,8 +163,6 @@ namespace osu.Game.Screens.Play private void initializeBreaks() { - if (!IsLoaded) return; // we need a clock. - FinishTransforms(true); Scheduler.CancelDelayedTasks(); From 99f5ca07ce8562e87fa4dae3de9a24c77805cacf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Aug 2019 10:42:54 +0900 Subject: [PATCH 2076/2854] Remove redundant comment --- osu.Game/Screens/Play/BreakOverlay.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 78690f156f..2d014db210 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -154,9 +154,6 @@ namespace osu.Game.Screens.Play CurrentBreakIndex--; } - // This ensures that IsBreakTime is generally consistent with the overlay's transforms during a break. - // If the current break doesn't have effects, IsBreakTime should be false. - // We also assume that the overlay's fade out transform is "not break time". var currentBreak = breaks[CurrentBreakIndex]; isBreakTime.Value = currentBreak.HasEffect && currentBreak.Contains(time); } From 7d42561da93c7f816de3f71e6de32f1d46848250 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Aug 2019 12:58:20 +0900 Subject: [PATCH 2077/2854] Remove linq usage in BreakOverlay update --- osu.Game/Screens/Play/BreakOverlay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 2d014db210..6fdee85f45 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -138,7 +137,7 @@ namespace osu.Game.Screens.Play private void updateBreakTimeBindable() { - if (breaks?.Any() != true) + if (breaks == null || breaks.Count == 0) return; var time = Clock.CurrentTime; From ffed642929e7ec23ba8e3c60c252a9d0522c9327 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 8 Aug 2019 07:08:51 +0300 Subject: [PATCH 2078/2854] Implement MatchBeatmapPanel --- .../Screens/Multi/Match/Components/Header.cs | 16 ++++++ .../Match/Components/MatchBeatmapPanel.cs | 49 +++++++++++++++++++ .../Screens/Multi/Match/MatchSubScreen.cs | 2 + 3 files changed, 67 insertions(+) create mode 100644 osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs diff --git a/osu.Game/Screens/Multi/Match/Components/Header.cs b/osu.Game/Screens/Multi/Match/Components/Header.cs index 73994fa369..629a19e5e8 100644 --- a/osu.Game/Screens/Multi/Match/Components/Header.cs +++ b/osu.Game/Screens/Multi/Match/Components/Header.cs @@ -29,6 +29,10 @@ namespace osu.Game.Screens.Multi.Match.Components public Action RequestBeatmapSelection; + public BindableBool ShowBeatmapPanel = new BindableBool(); + + private MatchBeatmapPanel beatmapPanel; + public Header() { RelativeSizeAxes = Axes.X; @@ -55,6 +59,12 @@ namespace osu.Game.Screens.Multi.Match.Components RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.4f), Color4.Black.Opacity(0.6f)), }, + beatmapPanel = new MatchBeatmapPanel + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Margin = new MarginPadding { Right = 100 }, + } } }, new Box @@ -114,6 +124,12 @@ namespace osu.Game.Screens.Multi.Match.Components beatmapButton.Action = () => RequestBeatmapSelection?.Invoke(); } + protected override void LoadComplete() + { + base.LoadComplete(); + ShowBeatmapPanel.BindValueChanged(value => beatmapPanel.FadeTo(value.NewValue ? 1 : 0, 200, Easing.OutQuint), true); + } + private class BeatmapSelectButton : HeaderButton { [Resolved(typeof(Room), nameof(Room.RoomID))] diff --git a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs new file mode 100644 index 0000000000..916115f11b --- /dev/null +++ b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Direct; +using osu.Game.Rulesets; + +namespace osu.Game.Screens.Multi.Match.Components +{ + public class MatchBeatmapPanel : MultiplayerComposite + { + [Resolved] + private IAPIProvider api { get; set; } + + [Resolved] + private RulesetStore rulesets { get; set; } + + private GetBeatmapSetRequest request; + + public MatchBeatmapPanel() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + CurrentItem.BindValueChanged(item => + { + var id = item.NewValue?.Beatmap.OnlineBeatmapID ?? 0; + + if (id != 0) + { + request?.Cancel(); + request = new GetBeatmapSetRequest(id, BeatmapSetLookupType.BeatmapId); + request.Success += beatmap => + { + ClearInternal(); + AddInternal(new DirectGridPanel(beatmap.ToBeatmapSet(rulesets))); + }; + api.Queue(request); + } + }, true); + } + } +} diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 5469d4c8c8..16c6b412fa 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -148,12 +148,14 @@ namespace osu.Game.Screens.Multi.Match if (tab.NewValue is SettingsMatchPage) { + header.ShowBeatmapPanel.Value = false; settings.Show(); info.FadeOut(fade_duration, Easing.OutQuint); bottomRow.FadeOut(fade_duration, Easing.OutQuint); } else { + header.ShowBeatmapPanel.Value = true; settings.Hide(); info.FadeIn(fade_duration, Easing.OutQuint); bottomRow.FadeIn(fade_duration, Easing.OutQuint); From a4459972b6f00d3e533a769468cecf4a7e5462ad Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 8 Aug 2019 07:15:30 +0300 Subject: [PATCH 2079/2854] Remove ViewBeatmapButton --- .../Visual/Multiplayer/TestSceneMatchInfo.cs | 2 +- .../Screens/Multi/Match/Components/Info.cs | 8 +--- .../Match/Components/ViewBeatmapButton.cs | 46 ------------------- 3 files changed, 2 insertions(+), 54 deletions(-) delete mode 100644 osu.Game/Screens/Multi/Match/Components/ViewBeatmapButton.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchInfo.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchInfo.cs index 3f0c0b07b7..a6c036a876 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchInfo.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchInfo.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Multiplayer typeof(Info), typeof(HeaderButton), typeof(ReadyButton), - typeof(ViewBeatmapButton) + typeof(MatchBeatmapPanel) }; [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Multi/Match/Components/Info.cs b/osu.Game/Screens/Multi/Match/Components/Info.cs index a185c4db50..74f000c21f 100644 --- a/osu.Game/Screens/Multi/Match/Components/Info.cs +++ b/osu.Game/Screens/Multi/Match/Components/Info.cs @@ -28,7 +28,6 @@ namespace osu.Game.Screens.Multi.Match.Components private void load() { ReadyButton readyButton; - ViewBeatmapButton viewBeatmapButton; HostInfo hostInfo; InternalChildren = new Drawable[] @@ -80,7 +79,6 @@ namespace osu.Game.Screens.Multi.Match.Components Direction = FillDirection.Horizontal, Children = new Drawable[] { - viewBeatmapButton = new ViewBeatmapButton(), readyButton = new ReadyButton { Action = () => OnStart?.Invoke() @@ -91,11 +89,7 @@ namespace osu.Game.Screens.Multi.Match.Components }, }; - CurrentItem.BindValueChanged(item => - { - viewBeatmapButton.Beatmap.Value = item.NewValue?.Beatmap; - readyButton.Beatmap.Value = item.NewValue?.Beatmap; - }, true); + CurrentItem.BindValueChanged(item => readyButton.Beatmap.Value = item.NewValue?.Beatmap, true); hostInfo.Host.BindTo(Host); } diff --git a/osu.Game/Screens/Multi/Match/Components/ViewBeatmapButton.cs b/osu.Game/Screens/Multi/Match/Components/ViewBeatmapButton.cs deleted file mode 100644 index 8d1ff21124..0000000000 --- a/osu.Game/Screens/Multi/Match/Components/ViewBeatmapButton.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Game.Beatmaps; -using osuTK; - -namespace osu.Game.Screens.Multi.Match.Components -{ - public class ViewBeatmapButton : HeaderButton - { - public readonly Bindable Beatmap = new Bindable(); - - [Resolved(CanBeNull = true)] - private OsuGame osuGame { get; set; } - - public ViewBeatmapButton() - { - RelativeSizeAxes = Axes.Y; - Size = new Vector2(200, 1); - - Text = "View beatmap"; - } - - [BackgroundDependencyLoader] - private void load() - { - if (osuGame != null) - Beatmap.BindValueChanged(beatmap => updateAction(beatmap.NewValue), true); - } - - private void updateAction(BeatmapInfo beatmap) - { - if (beatmap == null) - { - Enabled.Value = false; - return; - } - - Action = () => osuGame.ShowBeatmap(beatmap.OnlineBeatmapID ?? 0); - Enabled.Value = true; - } - } -} From 77df6a0cb7ef7736808811d4c2033d5882d8aa6a Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 7 Aug 2019 21:16:36 -0700 Subject: [PATCH 2080/2854] Remove unused direct placeholder screen --- osu.Game/Screens/Direct/OnlineListing.cs | 9 --------- osu.Game/Screens/Menu/MainMenu.cs | 2 -- 2 files changed, 11 deletions(-) delete mode 100644 osu.Game/Screens/Direct/OnlineListing.cs diff --git a/osu.Game/Screens/Direct/OnlineListing.cs b/osu.Game/Screens/Direct/OnlineListing.cs deleted file mode 100644 index 8376383674..0000000000 --- a/osu.Game/Screens/Direct/OnlineListing.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Screens.Direct -{ - public class OnlineListing : ScreenWhiteBox - { - } -} diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index fc8ddd8bd4..499b5089f6 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -14,7 +14,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Overlays; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Charts; -using osu.Game.Screens.Direct; using osu.Game.Screens.Edit; using osu.Game.Screens.Multi; using osu.Game.Screens.Select; @@ -65,7 +64,6 @@ namespace osu.Game.Screens.Menu buttons = new ButtonSystem { OnChart = delegate { this.Push(new ChartListing()); }, - OnDirect = delegate { this.Push(new OnlineListing()); }, OnEdit = delegate { this.Push(new Editor()); }, OnSolo = onSolo, OnMulti = delegate { this.Push(new Multiplayer()); }, From a345955f45c51d4ecaeb9cc728db1f30ce40d1f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Aug 2019 13:57:04 +0900 Subject: [PATCH 2081/2854] Add mentions linking ScoreProcessor apply/revert methods together --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 2e863f7edb..ba2375bec1 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -313,6 +313,9 @@ namespace osu.Game.Rulesets.Scoring /// /// Applies the score change of a to this . /// + /// + /// Any changes applied via this method can be reverted via . + /// /// The to apply. protected virtual void ApplyResult(JudgementResult result) { @@ -357,7 +360,7 @@ namespace osu.Game.Rulesets.Scoring } /// - /// Reverts the score change of a that was applied to this . + /// Reverts the score change of a that was applied to this via . /// /// The judgement scoring result. protected virtual void RevertResult(JudgementResult result) From ac2060f1cf4d1befc59d2b56531d58736f0dd021 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 8 Aug 2019 14:44:04 +0900 Subject: [PATCH 2082/2854] Throw exceptions and let LegacyDecoder handle them --- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 153 ++++----- .../Formats/LegacyStoryboardDecoder.cs | 6 +- .../Objects/Legacy/ConvertHitObjectParser.cs | 323 +++++++++--------- 3 files changed, 223 insertions(+), 259 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index b1261855f2..02d969b571 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -5,7 +5,6 @@ using System; using System.IO; using System.Linq; using osu.Framework.IO.File; -using osu.Framework.Logging; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Beatmaps.ControlPoints; @@ -292,10 +291,7 @@ namespace osu.Game.Beatmaps.Formats EventType type; if (!Enum.TryParse(split[0], out type)) - { - Logger.Log($"Unknown beatmap event of type {split[0]} could not be parsed and will be ignored.", LoggingTarget.Runtime, LogLevel.Important); - return; - } + throw new InvalidDataException($@"Unknown event type: {split[0]}"); switch (type) { @@ -323,90 +319,79 @@ namespace osu.Game.Beatmaps.Formats private void handleTimingPoint(string line) { - try + string[] split = line.Split(','); + + double time = getOffsetTime(Parsing.ParseDouble(split[0].Trim())); + double beatLength = Parsing.ParseDouble(split[1].Trim()); + double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1; + + TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple; + if (split.Length >= 3) + timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)Parsing.ParseInt(split[2]); + + LegacySampleBank sampleSet = defaultSampleBank; + if (split.Length >= 4) + sampleSet = (LegacySampleBank)Parsing.ParseInt(split[3]); + + int customSampleBank = 0; + if (split.Length >= 5) + customSampleBank = Parsing.ParseInt(split[4]); + + int sampleVolume = defaultSampleVolume; + if (split.Length >= 6) + sampleVolume = Parsing.ParseInt(split[5]); + + bool timingChange = true; + if (split.Length >= 7) + timingChange = split[6][0] == '1'; + + bool kiaiMode = false; + bool omitFirstBarSignature = false; + + if (split.Length >= 8) { - string[] split = line.Split(','); - - double time = getOffsetTime(Parsing.ParseDouble(split[0].Trim())); - double beatLength = Parsing.ParseDouble(split[1].Trim()); - double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1; - - TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple; - if (split.Length >= 3) - timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)Parsing.ParseInt(split[2]); - - LegacySampleBank sampleSet = defaultSampleBank; - if (split.Length >= 4) - sampleSet = (LegacySampleBank)Parsing.ParseInt(split[3]); - - int customSampleBank = 0; - if (split.Length >= 5) - customSampleBank = Parsing.ParseInt(split[4]); - - int sampleVolume = defaultSampleVolume; - if (split.Length >= 6) - sampleVolume = Parsing.ParseInt(split[5]); - - bool timingChange = true; - if (split.Length >= 7) - timingChange = split[6][0] == '1'; - - bool kiaiMode = false; - bool omitFirstBarSignature = false; - - if (split.Length >= 8) - { - EffectFlags effectFlags = (EffectFlags)Parsing.ParseInt(split[7]); - kiaiMode = effectFlags.HasFlag(EffectFlags.Kiai); - omitFirstBarSignature = effectFlags.HasFlag(EffectFlags.OmitFirstBarLine); - } - - string stringSampleSet = sampleSet.ToString().ToLowerInvariant(); - if (stringSampleSet == @"none") - stringSampleSet = @"normal"; - - if (timingChange) - { - var controlPoint = CreateTimingControlPoint(); - controlPoint.Time = time; - controlPoint.BeatLength = beatLength; - controlPoint.TimeSignature = timeSignature; - - handleTimingControlPoint(controlPoint); - } - - handleDifficultyControlPoint(new DifficultyControlPoint - { - Time = time, - SpeedMultiplier = speedMultiplier, - AutoGenerated = timingChange - }); - - handleEffectControlPoint(new EffectControlPoint - { - Time = time, - KiaiMode = kiaiMode, - OmitFirstBarLine = omitFirstBarSignature, - AutoGenerated = timingChange - }); - - handleSampleControlPoint(new LegacySampleControlPoint - { - Time = time, - SampleBank = stringSampleSet, - SampleVolume = sampleVolume, - CustomSampleBank = customSampleBank, - AutoGenerated = timingChange - }); + EffectFlags effectFlags = (EffectFlags)Parsing.ParseInt(split[7]); + kiaiMode = effectFlags.HasFlag(EffectFlags.Kiai); + omitFirstBarSignature = effectFlags.HasFlag(EffectFlags.OmitFirstBarLine); } - catch (FormatException) + + string stringSampleSet = sampleSet.ToString().ToLowerInvariant(); + if (stringSampleSet == @"none") + stringSampleSet = @"normal"; + + if (timingChange) { - Logger.Log("A timing point could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important); + var controlPoint = CreateTimingControlPoint(); + controlPoint.Time = time; + controlPoint.BeatLength = beatLength; + controlPoint.TimeSignature = timeSignature; + + handleTimingControlPoint(controlPoint); } - catch (OverflowException) + + handleDifficultyControlPoint(new DifficultyControlPoint { - Logger.Log("A timing point could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important); - } + Time = time, + SpeedMultiplier = speedMultiplier, + AutoGenerated = timingChange + }); + + handleEffectControlPoint(new EffectControlPoint + { + Time = time, + KiaiMode = kiaiMode, + OmitFirstBarLine = omitFirstBarSignature, + AutoGenerated = timingChange + }); + + handleSampleControlPoint(new LegacySampleControlPoint + { + Time = time, + SampleBank = stringSampleSet, + SampleVolume = sampleVolume, + CustomSampleBank = customSampleBank, + AutoGenerated = timingChange + }); } private void handleTimingControlPoint(TimingControlPoint newPoint) diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index f950437e75..3ae1c3ef12 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -10,7 +10,6 @@ using osuTK; using osuTK.Graphics; using osu.Framework.Graphics; using osu.Framework.IO.File; -using osu.Framework.Logging; using osu.Game.Storyboards; namespace osu.Game.Beatmaps.Formats @@ -85,10 +84,7 @@ namespace osu.Game.Beatmaps.Formats EventType type; if (!Enum.TryParse(split[0], out type)) - { - Logger.Log($"Unknown storyboard event of type {split[0]} could not be parsed and will be ignored.", LoggingTarget.Runtime, LogLevel.Important); - return; - } + throw new InvalidDataException($@"Unknown event type: {split[0]}"); switch (type) { diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index d70c1bf7d3..442e37edf5 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -10,7 +10,6 @@ using osu.Game.Beatmaps.Formats; using osu.Game.Audio; using System.Linq; using JetBrains.Annotations; -using osu.Framework.Logging; using osu.Framework.MathUtils; namespace osu.Game.Rulesets.Objects.Legacy @@ -41,208 +40,192 @@ namespace osu.Game.Rulesets.Objects.Legacy [CanBeNull] public override HitObject Parse(string text) { - try + string[] split = text.Split(','); + + Vector2 pos = new Vector2((int)Parsing.ParseFloat(split[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE)); + + double startTime = Parsing.ParseDouble(split[2]) + Offset; + + ConvertHitObjectType type = (ConvertHitObjectType)Parsing.ParseInt(split[3]); + + int comboOffset = (int)(type & ConvertHitObjectType.ComboOffset) >> 4; + type &= ~ConvertHitObjectType.ComboOffset; + + bool combo = type.HasFlag(ConvertHitObjectType.NewCombo); + type &= ~ConvertHitObjectType.NewCombo; + + var soundType = (LegacySoundType)Parsing.ParseInt(split[4]); + var bankInfo = new SampleBankInfo(); + + HitObject result = null; + + if (type.HasFlag(ConvertHitObjectType.Circle)) { - string[] split = text.Split(','); + result = CreateHit(pos, combo, comboOffset); - Vector2 pos = new Vector2((int)Parsing.ParseFloat(split[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE)); + if (split.Length > 5) + readCustomSampleBanks(split[5], bankInfo); + } + else if (type.HasFlag(ConvertHitObjectType.Slider)) + { + PathType pathType = PathType.Catmull; + double? length = null; - double startTime = Parsing.ParseDouble(split[2]) + Offset; + string[] pointSplit = split[5].Split('|'); - ConvertHitObjectType type = (ConvertHitObjectType)Parsing.ParseInt(split[3]); + int pointCount = 1; + foreach (var t in pointSplit) + if (t.Length > 1) + pointCount++; - int comboOffset = (int)(type & ConvertHitObjectType.ComboOffset) >> 4; - type &= ~ConvertHitObjectType.ComboOffset; + var points = new Vector2[pointCount]; - bool combo = type.HasFlag(ConvertHitObjectType.NewCombo); - type &= ~ConvertHitObjectType.NewCombo; + int pointIndex = 1; - var soundType = (LegacySoundType)Parsing.ParseInt(split[4]); - var bankInfo = new SampleBankInfo(); - - HitObject result = null; - - if (type.HasFlag(ConvertHitObjectType.Circle)) + foreach (string t in pointSplit) { - result = CreateHit(pos, combo, comboOffset); - - if (split.Length > 5) - readCustomSampleBanks(split[5], bankInfo); - } - else if (type.HasFlag(ConvertHitObjectType.Slider)) - { - PathType pathType = PathType.Catmull; - double? length = null; - - string[] pointSplit = split[5].Split('|'); - - int pointCount = 1; - foreach (var t in pointSplit) - if (t.Length > 1) - pointCount++; - - var points = new Vector2[pointCount]; - - int pointIndex = 1; - - foreach (string t in pointSplit) + if (t.Length == 1) { - if (t.Length == 1) + switch (t) { - switch (t) - { - case @"C": - pathType = PathType.Catmull; - break; - - case @"B": - pathType = PathType.Bezier; - break; - - case @"L": - pathType = PathType.Linear; - break; - - case @"P": - pathType = PathType.PerfectCurve; - break; - } - - continue; - } - - string[] temp = t.Split(':'); - points[pointIndex++] = new Vector2((int)Parsing.ParseDouble(temp[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseDouble(temp[1], Parsing.MAX_COORDINATE_VALUE)) - pos; - } - - // osu-stable special-cased colinear perfect curves to a CurveType.Linear - bool isLinear(Vector2[] p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) - (p[1].X - p[0].X) * (p[2].Y - p[0].Y)); - - if (points.Length == 3 && pathType == PathType.PerfectCurve && isLinear(points)) - pathType = PathType.Linear; - - int repeatCount = Parsing.ParseInt(split[6]); - - if (repeatCount > 9000) - throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high"); - - // osu-stable treated the first span of the slider as a repeat, but no repeats are happening - repeatCount = Math.Max(0, repeatCount - 1); - - if (split.Length > 7) - { - length = Math.Max(0, Parsing.ParseDouble(split[7])); - if (length == 0) - length = null; - } - - if (split.Length > 10) - readCustomSampleBanks(split[10], bankInfo); - - // One node for each repeat + the start and end nodes - int nodes = repeatCount + 2; - - // Populate node sample bank infos with the default hit object sample bank - var nodeBankInfos = new List(); - for (int i = 0; i < nodes; i++) - nodeBankInfos.Add(bankInfo.Clone()); - - // Read any per-node sample banks - if (split.Length > 9 && split[9].Length > 0) - { - string[] sets = split[9].Split('|'); - - for (int i = 0; i < nodes; i++) - { - if (i >= sets.Length) + case @"C": + pathType = PathType.Catmull; break; - SampleBankInfo info = nodeBankInfos[i]; - readCustomSampleBanks(sets[i], info); - } - } - - // Populate node sound types with the default hit object sound type - var nodeSoundTypes = new List(); - for (int i = 0; i < nodes; i++) - nodeSoundTypes.Add(soundType); - - // Read any per-node sound types - if (split.Length > 8 && split[8].Length > 0) - { - string[] adds = split[8].Split('|'); - - for (int i = 0; i < nodes; i++) - { - if (i >= adds.Length) + case @"B": + pathType = PathType.Bezier; break; - int sound; - int.TryParse(adds[i], out sound); - nodeSoundTypes[i] = (LegacySoundType)sound; + case @"L": + pathType = PathType.Linear; + break; + + case @"P": + pathType = PathType.PerfectCurve; + break; } + + continue; } - // Generate the final per-node samples - var nodeSamples = new List>(nodes); + string[] temp = t.Split(':'); + points[pointIndex++] = new Vector2((int)Parsing.ParseDouble(temp[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseDouble(temp[1], Parsing.MAX_COORDINATE_VALUE)) - pos; + } + + // osu-stable special-cased colinear perfect curves to a CurveType.Linear + bool isLinear(Vector2[] p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) - (p[1].X - p[0].X) * (p[2].Y - p[0].Y)); + + if (points.Length == 3 && pathType == PathType.PerfectCurve && isLinear(points)) + pathType = PathType.Linear; + + int repeatCount = Parsing.ParseInt(split[6]); + + if (repeatCount > 9000) + throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high"); + + // osu-stable treated the first span of the slider as a repeat, but no repeats are happening + repeatCount = Math.Max(0, repeatCount - 1); + + if (split.Length > 7) + { + length = Math.Max(0, Parsing.ParseDouble(split[7])); + if (length == 0) + length = null; + } + + if (split.Length > 10) + readCustomSampleBanks(split[10], bankInfo); + + // One node for each repeat + the start and end nodes + int nodes = repeatCount + 2; + + // Populate node sample bank infos with the default hit object sample bank + var nodeBankInfos = new List(); + for (int i = 0; i < nodes; i++) + nodeBankInfos.Add(bankInfo.Clone()); + + // Read any per-node sample banks + if (split.Length > 9 && split[9].Length > 0) + { + string[] sets = split[9].Split('|'); + for (int i = 0; i < nodes; i++) - nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i])); - - result = CreateSlider(pos, combo, comboOffset, points, length, pathType, repeatCount, nodeSamples); - - // The samples are played when the slider ends, which is the last node - result.Samples = nodeSamples[nodeSamples.Count - 1]; - } - else if (type.HasFlag(ConvertHitObjectType.Spinner)) - { - double endTime = Math.Max(startTime, Parsing.ParseDouble(split[5]) + Offset); - - result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, endTime); - - if (split.Length > 6) - readCustomSampleBanks(split[6], bankInfo); - } - else if (type.HasFlag(ConvertHitObjectType.Hold)) - { - // Note: Hold is generated by BMS converts - - double endTime = Math.Max(startTime, Parsing.ParseDouble(split[2])); - - if (split.Length > 5 && !string.IsNullOrEmpty(split[5])) { - string[] ss = split[5].Split(':'); - endTime = Math.Max(startTime, Parsing.ParseDouble(ss[0])); - readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo); + if (i >= sets.Length) + break; + + SampleBankInfo info = nodeBankInfos[i]; + readCustomSampleBanks(sets[i], info); } - - result = CreateHold(pos, combo, comboOffset, endTime + Offset); } - if (result == null) + // Populate node sound types with the default hit object sound type + var nodeSoundTypes = new List(); + for (int i = 0; i < nodes; i++) + nodeSoundTypes.Add(soundType); + + // Read any per-node sound types + if (split.Length > 8 && split[8].Length > 0) { - Logger.Log($"Unknown hit object type: {type}. Skipped.", level: LogLevel.Error); - return null; + string[] adds = split[8].Split('|'); + + for (int i = 0; i < nodes; i++) + { + if (i >= adds.Length) + break; + + int sound; + int.TryParse(adds[i], out sound); + nodeSoundTypes[i] = (LegacySoundType)sound; + } } - result.StartTime = startTime; + // Generate the final per-node samples + var nodeSamples = new List>(nodes); + for (int i = 0; i < nodes; i++) + nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i])); - if (result.Samples.Count == 0) - result.Samples = convertSoundType(soundType, bankInfo); + result = CreateSlider(pos, combo, comboOffset, points, length, pathType, repeatCount, nodeSamples); - FirstObject = false; - - return result; + // The samples are played when the slider ends, which is the last node + result.Samples = nodeSamples[nodeSamples.Count - 1]; } - catch (FormatException) + else if (type.HasFlag(ConvertHitObjectType.Spinner)) { - Logger.Log("A hitobject could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important); + double endTime = Math.Max(startTime, Parsing.ParseDouble(split[5]) + Offset); + + result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, endTime); + + if (split.Length > 6) + readCustomSampleBanks(split[6], bankInfo); } - catch (OverflowException) + else if (type.HasFlag(ConvertHitObjectType.Hold)) { - Logger.Log("A hitobject could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important); + // Note: Hold is generated by BMS converts + + double endTime = Math.Max(startTime, Parsing.ParseDouble(split[2])); + + if (split.Length > 5 && !string.IsNullOrEmpty(split[5])) + { + string[] ss = split[5].Split(':'); + endTime = Math.Max(startTime, Parsing.ParseDouble(ss[0])); + readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo); + } + + result = CreateHold(pos, combo, comboOffset, endTime + Offset); } - return null; + if (result == null) + throw new InvalidDataException($"Unknown hit object type: {type}"); + + result.StartTime = startTime; + + if (result.Samples.Count == 0) + result.Samples = convertSoundType(soundType, bankInfo); + + FirstObject = false; + + return result; } private void readCustomSampleBanks(string str, SampleBankInfo bankInfo) From de4ad1f62586fb3c55cd8067ac9672f284c8cfbf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 8 Aug 2019 14:44:49 +0900 Subject: [PATCH 2083/2854] Fix bad log message --- osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 442e37edf5..e990938291 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Objects.Legacy } if (result == null) - throw new InvalidDataException($"Unknown hit object type: {type}"); + throw new InvalidDataException($"Unknown hit object type: {split[3]}"); result.StartTime = startTime; From cbcdc2890088bb151ae6c444d4ae95e005a63f6b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 8 Aug 2019 09:04:24 +0300 Subject: [PATCH 2084/2854] Fix hard crash when clicking on a preview button --- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 16c6b412fa..13a21a4516 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.GameTypes; @@ -18,7 +19,7 @@ using PlaylistItem = osu.Game.Online.Multiplayer.PlaylistItem; namespace osu.Game.Screens.Multi.Match { - public class MatchSubScreen : MultiplayerSubScreen + public class MatchSubScreen : MultiplayerSubScreen, IPreviewTrackOwner { public override bool DisallowExternalBeatmapRulesetChanges => true; @@ -44,6 +45,9 @@ namespace osu.Game.Screens.Multi.Match [Resolved] private BeatmapManager beatmapManager { get; set; } + [Resolved] + private PreviewTrackManager trackManager { get; set; } + [Resolved(CanBeNull = true)] private OsuGame game { get; set; } @@ -184,6 +188,8 @@ namespace osu.Game.Screens.Multi.Match Mods.Value = Array.Empty(); + trackManager.StopAnyPlaying(this); + return base.OnExiting(next); } @@ -237,6 +243,13 @@ namespace osu.Game.Screens.Multi.Match } } + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + dependencies.CacheAs(this); + return dependencies; + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From 8668bce25d23d69d23c1bc3c0e15a741b9862b1a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 8 Aug 2019 09:52:42 +0300 Subject: [PATCH 2085/2854] Fix preview can be played on start --- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 13a21a4516..3136326cdd 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -231,6 +231,8 @@ namespace osu.Game.Screens.Multi.Match private void onStart() { + trackManager.StopAnyPlaying(this); + switch (type.Value) { default: From bcd443a3aa8178cc68481b96d3055d18a94014e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=BCbner?= Date: Thu, 8 Aug 2019 08:56:52 +0200 Subject: [PATCH 2086/2854] remove /j --- osu.Game/Online/Chat/ChannelManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index b5d8879e50..9acf6bd39f 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -213,7 +213,6 @@ namespace osu.Game.Online.Chat PostMessage(content, true); break; - case "j": case "join": if (string.IsNullOrWhiteSpace(content)) { From 0bed3bfece7397236bc891a34e5fe8ab7c343f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=BCbner?= Date: Thu, 8 Aug 2019 09:02:09 +0200 Subject: [PATCH 2087/2854] formatting inspection 2 --- osu.Game/Online/Chat/ChannelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 9acf6bd39f..4f6066cab1 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -221,7 +221,7 @@ namespace osu.Game.Online.Chat } var channel = availableChannels.Where(c => c.Name == content || c.Name == $"#{content}").FirstOrDefault(); - + if (channel == null) { target.AddNewMessages(new ErrorMessage($"Channel '{content}' not found.")); From 6d5a7041fdf7873c36eff28d8e624e68fc0088ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Aug 2019 17:10:06 +0900 Subject: [PATCH 2088/2854] Move system user colour assignment to ensure consistency --- osu.Game/Online/Chat/ErrorMessage.cs | 2 +- osu.Game/Users/User.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ErrorMessage.cs b/osu.Game/Online/Chat/ErrorMessage.cs index a8ff0e9a98..87a65fb3f1 100644 --- a/osu.Game/Online/Chat/ErrorMessage.cs +++ b/osu.Game/Online/Chat/ErrorMessage.cs @@ -8,7 +8,7 @@ namespace osu.Game.Online.Chat public ErrorMessage(string message) : base(message) { - Sender.Colour = @"ff0000"; + // todo: this should likely be styled differently in the future. } } } diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index b738eff4a6..a5f3578711 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -187,6 +187,7 @@ namespace osu.Game.Users public static readonly User SYSTEM_USER = new User { Username = "system", + Colour = @"9c0101", Id = 0 }; From a3dbaef4cabb1fdd37c96cdd9dd5ad88ef1b62df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Aug 2019 17:29:50 +0900 Subject: [PATCH 2089/2854] Adjust background gradient --- osu.Game/Screens/Multi/Match/Components/Header.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Match/Components/Header.cs b/osu.Game/Screens/Multi/Match/Components/Header.cs index 629a19e5e8..7f2b1278b7 100644 --- a/osu.Game/Screens/Multi/Match/Components/Header.cs +++ b/osu.Game/Screens/Multi/Match/Components/Header.cs @@ -57,7 +57,7 @@ namespace osu.Game.Screens.Multi.Match.Components new Box { RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.4f), Color4.Black.Opacity(0.6f)), + Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.7f), Color4.Black.Opacity(0.8f)), }, beatmapPanel = new MatchBeatmapPanel { From 1b559c15852901bd2c7819b166479cbb76f69475 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 8 Aug 2019 12:01:33 +0300 Subject: [PATCH 2090/2854] Use async loading --- osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs index 916115f11b..dc889eeb94 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs @@ -39,7 +39,8 @@ namespace osu.Game.Screens.Multi.Match.Components request.Success += beatmap => { ClearInternal(); - AddInternal(new DirectGridPanel(beatmap.ToBeatmapSet(rulesets))); + var panel = new DirectGridPanel(beatmap.ToBeatmapSet(rulesets)); + LoadComponentAsync(panel, p => { AddInternal(panel); }); }; api.Queue(request); } From 08a92c38d728e030b64bc0135607ce25ce01f5cd Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 8 Aug 2019 12:04:44 +0300 Subject: [PATCH 2091/2854] adjust naming --- .../Screens/Multi/Match/Components/MatchBeatmapPanel.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs index dc889eeb94..b927e38edb 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs @@ -30,12 +30,12 @@ namespace osu.Game.Screens.Multi.Match.Components { CurrentItem.BindValueChanged(item => { - var id = item.NewValue?.Beatmap.OnlineBeatmapID ?? 0; + var onlineId = item.NewValue?.Beatmap.OnlineBeatmapID ?? 0; - if (id != 0) + if (onlineId != 0) { request?.Cancel(); - request = new GetBeatmapSetRequest(id, BeatmapSetLookupType.BeatmapId); + request = new GetBeatmapSetRequest(onlineId, BeatmapSetLookupType.BeatmapId); request.Success += beatmap => { ClearInternal(); From e9b5c91690a424be0f26b8474fc27e03d326add8 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 8 Aug 2019 12:08:51 +0300 Subject: [PATCH 2092/2854] Fade out existing panel on beatmap change --- .../Multi/Match/Components/MatchBeatmapPanel.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs index b927e38edb..f73059d069 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs @@ -19,6 +19,7 @@ namespace osu.Game.Screens.Multi.Match.Components private RulesetStore rulesets { get; set; } private GetBeatmapSetRequest request; + private DirectGridPanel panel; public MatchBeatmapPanel() { @@ -30,16 +31,23 @@ namespace osu.Game.Screens.Multi.Match.Components { CurrentItem.BindValueChanged(item => { + request?.Cancel(); + + if (panel != null) + { + panel.FadeOut(200); + panel.Expire(); + panel = null; + } + var onlineId = item.NewValue?.Beatmap.OnlineBeatmapID ?? 0; if (onlineId != 0) { - request?.Cancel(); request = new GetBeatmapSetRequest(onlineId, BeatmapSetLookupType.BeatmapId); request.Success += beatmap => { - ClearInternal(); - var panel = new DirectGridPanel(beatmap.ToBeatmapSet(rulesets)); + panel = new DirectGridPanel(beatmap.ToBeatmapSet(rulesets)); LoadComponentAsync(panel, p => { AddInternal(panel); }); }; api.Queue(request); From 460cf141de6ce895cfa69f3dd00cdc62a0d50205 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 8 Aug 2019 12:18:26 +0300 Subject: [PATCH 2093/2854] Add testing --- .../Multiplayer/TestSceneMatchBeatmapPanel.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs new file mode 100644 index 0000000000..46fd43508b --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs @@ -0,0 +1,40 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Online.Multiplayer; +using osu.Game.Screens.Multi.Match.Components; +using osu.Framework.Graphics; +using osu.Framework.MathUtils; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneMatchBeatmapPanel : MultiplayerTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(MatchBeatmapPanel) + }; + + public TestSceneMatchBeatmapPanel() + { + Add(new MatchBeatmapPanel + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + + var playlist = Room.Playlist; + + playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1763072 } }); + playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 2101557 } }); + playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1973466 } }); + playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 2109801 } }); + playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1922035 } }); + + AddStep("Select random beatmap", () => Room.CurrentItem.Value = playlist[RNG.Next(playlist.Count)]); + } + } +} From 9ab132520cdb6af2063ca8de0d87a56785d44dd6 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 8 Aug 2019 12:25:46 +0300 Subject: [PATCH 2094/2854] Testcase improvements --- .../Multiplayer/TestSceneMatchBeatmapPanel.cs | 35 ++++++++++++++----- .../Screens/Multi/Match/MatchSubScreen.cs | 2 ++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs index 46fd43508b..db2b61cdd9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs @@ -8,16 +8,21 @@ using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Match.Components; using osu.Framework.Graphics; using osu.Framework.MathUtils; +using osu.Game.Audio; +using osu.Framework.Allocation; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneMatchBeatmapPanel : MultiplayerTestScene + public class TestSceneMatchBeatmapPanel : MultiplayerTestScene, IPreviewTrackOwner { public override IReadOnlyList RequiredTypes => new[] { typeof(MatchBeatmapPanel) }; + [Resolved] + private PreviewTrackManager trackManager { get; set; } + public TestSceneMatchBeatmapPanel() { Add(new MatchBeatmapPanel @@ -26,15 +31,29 @@ namespace osu.Game.Tests.Visual.Multiplayer Origin = Anchor.Centre, }); - var playlist = Room.Playlist; + Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1763072 } }); + Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 2101557 } }); + Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1973466 } }); + Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 2109801 } }); + Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1922035 } }); + } - playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1763072 } }); - playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 2101557 } }); - playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1973466 } }); - playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 2109801 } }); - playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1922035 } }); + protected override void LoadComplete() + { + base.LoadComplete(); - AddStep("Select random beatmap", () => Room.CurrentItem.Value = playlist[RNG.Next(playlist.Count)]); + AddStep("Select random beatmap", () => + { + Room.CurrentItem.Value = Room.Playlist[RNG.Next(Room.Playlist.Count)]; + trackManager.StopAnyPlaying(this); + }); + } + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + dependencies.CacheAs(this); + return dependencies; } } } diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 3136326cdd..c89c32759d 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -206,6 +206,8 @@ namespace osu.Game.Screens.Multi.Match if (e.NewValue?.Ruleset != null) Ruleset.Value = e.NewValue.Ruleset; + + trackManager.StopAnyPlaying(this); } /// From 3ebfa0505c21bec9ef00fe643b05dbd52336444b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 8 Aug 2019 18:26:03 +0900 Subject: [PATCH 2095/2854] Don't share single scheduler across all model managers --- osu.Game/Database/ArchiveModelManager.cs | 29 +++++++++++------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index efb76deff8..52d3f013ce 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -31,10 +31,21 @@ namespace osu.Game.Database /// /// The model type. /// The associated file join type. - public abstract class ArchiveModelManager : ArchiveModelManager, ICanAcceptFiles, IModelManager + public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : INamedFileInfo, new() { + private const int import_queue_request_concurrency = 1; + + /// + /// A singleton scheduler shared by all . + /// + /// + /// This scheduler generally performs IO and CPU intensive work so concurrency is limited harshly. + /// It is mainly being used as a queue mechanism for large imports. + /// + private static readonly ThreadedTaskScheduler import_scheduler = new ThreadedTaskScheduler(import_queue_request_concurrency, nameof(ArchiveModelManager)); + /// /// Set an endpoint for notifications to be posted to. /// @@ -336,7 +347,7 @@ namespace osu.Game.Database flushEvents(true); return item; - }, cancellationToken, TaskCreationOptions.HideScheduler, IMPORT_SCHEDULER).Unwrap(); + }, cancellationToken, TaskCreationOptions.HideScheduler, import_scheduler).Unwrap(); /// /// Perform an update of the specified item. @@ -646,18 +657,4 @@ namespace osu.Game.Database #endregion } - - public abstract class ArchiveModelManager - { - private const int import_queue_request_concurrency = 1; - - /// - /// A singleton scheduler shared by all . - /// - /// - /// This scheduler generally performs IO and CPU intensive work so concurrency is limited harshly. - /// It is mainly being used as a queue mechanism for large imports. - /// - protected static readonly ThreadedTaskScheduler IMPORT_SCHEDULER = new ThreadedTaskScheduler(import_queue_request_concurrency, nameof(ArchiveModelManager)); - } } From 566d874641922a3b680b648542aecc7702a0236b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 8 Aug 2019 15:25:07 +0300 Subject: [PATCH 2096/2854] Prevent failing when reverting to a hasFailedAtJudgement --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index fd851f2cbb..5ca33ff0bc 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Scoring /// /// Whether this ScoreProcessor has already triggered the failed state. /// - public virtual bool HasFailed { get; private set; } + public virtual bool HasFailed { get; protected set; } /// /// The default conditions for failing. @@ -309,6 +309,7 @@ namespace osu.Game.Rulesets.Scoring } private readonly Dictionary scoreResultCounts = new Dictionary(); + private Judgement hasFailedAtJudgement; /// /// Applies the score change of a to this . @@ -317,7 +318,12 @@ namespace osu.Game.Rulesets.Scoring protected virtual void ApplyResult(JudgementResult result) { if (HasFailed) + { + if (hasFailedAtJudgement == null) + hasFailedAtJudgement = result.Judgement; + return; + } result.ComboAtJudgement = Combo.Value; result.HighestComboAtJudgement = HighestCombo.Value; @@ -365,6 +371,17 @@ namespace osu.Game.Rulesets.Scoring /// The judgement scoring result. protected virtual void RevertResult(JudgementResult result) { + if (HasFailed) + { + if (hasFailedAtJudgement == result.Judgement) + { + hasFailedAtJudgement = null; + HasFailed = false; + } + + return; + } + Combo.Value = result.ComboAtJudgement; HighestCombo.Value = result.HighestComboAtJudgement; Health.Value = result.HealthAtJudgement; From 537973fc458f50a260e1e8d31f217a146b02cd88 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 8 Aug 2019 15:59:29 +0300 Subject: [PATCH 2097/2854] Add test for disabling user dim on storyboard --- .../Background/TestSceneUserDimContainer.cs | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index f114559114..bffee7f1f7 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -149,10 +149,10 @@ namespace osu.Game.Tests.Visual.Background } /// - /// Check if the is properly accepting user-defined visual changes at all. + /// Check if the is properly accepting user-defined visual changes in background at all. /// [Test] - public void DisableUserDimTest() + public void DisableUserDimBackgroundTest() { performFullSetup(); waitForDim(); @@ -165,6 +165,28 @@ namespace osu.Game.Tests.Visual.Background AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); } + /// + /// Check if the is properly accepting user-defined visual changes in storyboard at all. + /// + [Test] + public void DisableUserDimStoryboardTest() + { + performFullSetup(); + createFakeStoryboard(); + AddStep("Storyboard Enabled", () => + { + player.ReplacesBackground.Value = true; + player.StoryboardEnabled.Value = true; + }); + AddStep("EnableUserDim enabled", () => player.DimmableStoryboard.EnableUserDim.Value = true); + AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f); + waitForDim(); + AddAssert("Storyboard is invisible", () => !player.IsStoryboardVisible); + AddStep("EnableUserDim disabled", () => player.DimmableStoryboard.EnableUserDim.Value = false); + waitForDim(); + AddAssert("Storyboard is visible", () => player.IsStoryboardVisible); + } + /// /// Check if the visual settings container retains dim and blur when pausing /// From 88b9942b2ac601d33c40a41f8d3625b7788eabf5 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 8 Aug 2019 17:07:06 +0300 Subject: [PATCH 2098/2854] Move EnableUserDim check to defualt value of ShowDimContent --- osu.Game/Graphics/Containers/UserDimContainer.cs | 2 +- osu.Game/Screens/Play/DimmableStoryboard.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index dd5134168f..b7da5592a8 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -69,7 +69,7 @@ namespace osu.Game.Graphics.Containers /// /// Whether the content of this container should currently be visible. /// - protected virtual bool ShowDimContent => true; + protected virtual bool ShowDimContent => !EnableUserDim.Value; /// /// Should be invoked when any dependent dim level or user setting is changed and bring the visual state up-to-date. diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs index 45dff039b6..10ddaeb354 100644 --- a/osu.Game/Screens/Play/DimmableStoryboard.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Play base.LoadComplete(); } - protected override bool ShowDimContent => ShowStoryboard.Value && UserDimLevel.Value < 1; + protected override bool ShowDimContent => base.ShowDimContent || ShowStoryboard.Value && UserDimLevel.Value < 1; private void initializeStoryboard(bool async) { From bedb744a2e067a53d6aa5fdcbc1c6ede16245b9e Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 8 Aug 2019 17:11:26 +0300 Subject: [PATCH 2099/2854] Add parentheses --- osu.Game/Screens/Play/DimmableStoryboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs index 10ddaeb354..8d1f703791 100644 --- a/osu.Game/Screens/Play/DimmableStoryboard.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Play base.LoadComplete(); } - protected override bool ShowDimContent => base.ShowDimContent || ShowStoryboard.Value && UserDimLevel.Value < 1; + protected override bool ShowDimContent => base.ShowDimContent || (ShowStoryboard.Value && UserDimLevel.Value < 1); private void initializeStoryboard(bool async) { From a3d90da7d4663ee1e0653bdcb9d70143bc89ae9b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 8 Aug 2019 17:29:52 +0300 Subject: [PATCH 2100/2854] Remove unnecessary check --- osu.Game/Graphics/Containers/UserDimContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index b7da5592a8..6696a5bfe0 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -76,7 +76,7 @@ namespace osu.Game.Graphics.Containers /// protected virtual void UpdateVisuals() { - ContentDisplayed = !EnableUserDim.Value || ShowDimContent; + ContentDisplayed = ShowDimContent; dimContent.FadeTo(ContentDisplayed ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint); dimContent.FadeColour(EnableUserDim.Value ? OsuColour.Gray(1 - (float)UserDimLevel.Value) : Color4.White, BACKGROUND_FADE_DURATION, Easing.OutQuint); From 0fcc6c1676089fd2dd963f937bf317b68252336e Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 8 Aug 2019 22:13:48 +0300 Subject: [PATCH 2101/2854] Add DimLevel property --- osu.Game/Graphics/Containers/UserDimContainer.cs | 6 ++++-- osu.Game/Screens/Play/DimmableStoryboard.cs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index 6696a5bfe0..023964d701 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -36,6 +36,8 @@ namespace osu.Game.Graphics.Containers protected Bindable ShowStoryboard { get; private set; } + protected double DimLevel => EnableUserDim.Value ? UserDimLevel.Value : 0; + protected override Container Content => dimContent; private Container dimContent { get; } @@ -69,7 +71,7 @@ namespace osu.Game.Graphics.Containers /// /// Whether the content of this container should currently be visible. /// - protected virtual bool ShowDimContent => !EnableUserDim.Value; + protected virtual bool ShowDimContent => true; /// /// Should be invoked when any dependent dim level or user setting is changed and bring the visual state up-to-date. @@ -79,7 +81,7 @@ namespace osu.Game.Graphics.Containers ContentDisplayed = ShowDimContent; dimContent.FadeTo(ContentDisplayed ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint); - dimContent.FadeColour(EnableUserDim.Value ? OsuColour.Gray(1 - (float)UserDimLevel.Value) : Color4.White, BACKGROUND_FADE_DURATION, Easing.OutQuint); + dimContent.FadeColour(OsuColour.Gray(1 - (float)DimLevel), BACKGROUND_FADE_DURATION, Easing.OutQuint); } } } diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs index 8d1f703791..2154526e54 100644 --- a/osu.Game/Screens/Play/DimmableStoryboard.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Play base.LoadComplete(); } - protected override bool ShowDimContent => base.ShowDimContent || (ShowStoryboard.Value && UserDimLevel.Value < 1); + protected override bool ShowDimContent => ShowStoryboard.Value && DimLevel < 1; private void initializeStoryboard(bool async) { From 565034e658ace24fd3d2ab493bc0df5496a9cdfb Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 8 Aug 2019 22:14:51 +0300 Subject: [PATCH 2102/2854] Remove unnecessary using directive --- osu.Game/Graphics/Containers/UserDimContainer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index 023964d701..2b7635cc88 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -6,7 +6,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Configuration; -using osuTK.Graphics; namespace osu.Game.Graphics.Containers { From 7e9c100c9b25dd450e290da9d74ac068010f0044 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Aug 2019 12:29:33 +0900 Subject: [PATCH 2103/2854] Apply new resharper refactors --- osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs | 2 +- osu.Game/Beatmaps/Legacy/LegacyMods.cs | 2 +- osu.Game/Graphics/UserInterface/Bar.cs | 2 +- osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs index a3cd455886..e4a28167ec 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// /// Keep the same as last row. /// - ForceStack = 1 << 0, + ForceStack = 1, /// /// Keep different from last row. diff --git a/osu.Game/Beatmaps/Legacy/LegacyMods.cs b/osu.Game/Beatmaps/Legacy/LegacyMods.cs index 8e53c24e7b..583e950e49 100644 --- a/osu.Game/Beatmaps/Legacy/LegacyMods.cs +++ b/osu.Game/Beatmaps/Legacy/LegacyMods.cs @@ -9,7 +9,7 @@ namespace osu.Game.Beatmaps.Legacy public enum LegacyMods { None = 0, - NoFail = 1 << 0, + NoFail = 1, Easy = 1 << 1, TouchDevice = 1 << 2, Hidden = 1 << 3, diff --git a/osu.Game/Graphics/UserInterface/Bar.cs b/osu.Game/Graphics/UserInterface/Bar.cs index 2a858ccbcf..f8d5955503 100644 --- a/osu.Game/Graphics/UserInterface/Bar.cs +++ b/osu.Game/Graphics/UserInterface/Bar.cs @@ -110,7 +110,7 @@ namespace osu.Game.Graphics.UserInterface [Flags] public enum BarDirection { - LeftToRight = 1 << 0, + LeftToRight = 1, RightToLeft = 1 << 1, TopToBottom = 1 << 2, BottomToTop = 1 << 3, diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs index c9f7224643..eab37b682c 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy [Flags] internal enum ConvertHitObjectType { - Circle = 1 << 0, + Circle = 1, Slider = 1 << 1, NewCombo = 1 << 2, Spinner = 1 << 3, From ca33efead43eafe29d0fbc0861e4918cae261f68 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2019 03:58:06 +0000 Subject: [PATCH 2104/2854] Bump ppy.osu.Game.Resources from 2019.702.0 to 2019.731.1 Bumps [ppy.osu.Game.Resources](https://github.com/ppy/osu-resources) from 2019.702.0 to 2019.731.1. - [Release notes](https://github.com/ppy/osu-resources/releases) - [Commits](https://github.com/ppy/osu-resources/compare/2019.702.0...2019.731.1) Signed-off-by: dependabot-preview[bot] --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index c4cdffa8a5..40c15a1162 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -62,7 +62,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 02bf053468..8ee325c2ac 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,7 +14,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 5b43a6f46f..b46438f766 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -104,7 +104,7 @@ - + From 58e98e53d276c96eb362f75b84cb9579bb3d4cb0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2019 03:58:06 +0000 Subject: [PATCH 2105/2854] Bump NUnit3TestAdapter from 3.13.0 to 3.14.0 Bumps [NUnit3TestAdapter](https://github.com/nunit/nunit3-vs-adapter) from 3.13.0 to 3.14.0. - [Release notes](https://github.com/nunit/nunit3-vs-adapter/releases) - [Commits](https://github.com/nunit/nunit3-vs-adapter/compare/V3.13...V3.14) Signed-off-by: dependabot-preview[bot] --- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 9acf47a67c..4100404da6 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index df5131dd8b..013d2a71d4 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index bb3e5a66f3..92c5c77aac 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index 5510c3a9d9..82055ecaee 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 659f5415c3..50530088c2 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index dad2fe0877..257db89a20 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -7,7 +7,7 @@ - + WinExe From 88fa06efbadc23501209021ad4df1436bd4acb7f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Aug 2019 12:29:58 +0900 Subject: [PATCH 2106/2854] Refactor as proposed --- .../Rulesets/Judgements/JudgementResult.cs | 5 ++++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 29 ++++++------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs index 195fe316ac..0b3c3943c3 100644 --- a/osu.Game/Rulesets/Judgements/JudgementResult.cs +++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs @@ -42,6 +42,11 @@ namespace osu.Game.Rulesets.Judgements /// public double HealthAtJudgement { get; internal set; } + /// + /// Whether the user was in a failed state prior to this occurring. + /// + public bool FailedAtJudgement { get; internal set; } + /// /// Whether a miss or hit occurred. /// diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 5ca33ff0bc..dd8aae27d8 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -309,7 +309,6 @@ namespace osu.Game.Rulesets.Scoring } private readonly Dictionary scoreResultCounts = new Dictionary(); - private Judgement hasFailedAtJudgement; /// /// Applies the score change of a to this . @@ -317,17 +316,13 @@ namespace osu.Game.Rulesets.Scoring /// The to apply. protected virtual void ApplyResult(JudgementResult result) { - if (HasFailed) - { - if (hasFailedAtJudgement == null) - hasFailedAtJudgement = result.Judgement; - - return; - } - result.ComboAtJudgement = Combo.Value; result.HighestComboAtJudgement = HighestCombo.Value; result.HealthAtJudgement = Health.Value; + result.FailedAtJudgement = HasFailed; + + if (HasFailed) + return; JudgedHits++; @@ -371,21 +366,15 @@ namespace osu.Game.Rulesets.Scoring /// The judgement scoring result. protected virtual void RevertResult(JudgementResult result) { - if (HasFailed) - { - if (hasFailedAtJudgement == result.Judgement) - { - hasFailedAtJudgement = null; - HasFailed = false; - } - - return; - } - Combo.Value = result.ComboAtJudgement; HighestCombo.Value = result.HighestComboAtJudgement; Health.Value = result.HealthAtJudgement; + // Todo: Revert HasFailed state with proper player support + + if (result.FailedAtJudgement) + return; + JudgedHits--; if (result.Type != HitResult.None) From 33f4b628a5fb5316f0be69d52f9856e8d3ccef4e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Aug 2019 13:42:02 +0900 Subject: [PATCH 2107/2854] Make HasFailed private set --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index dd8aae27d8..a39b432e20 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Scoring /// /// Whether this ScoreProcessor has already triggered the failed state. /// - public virtual bool HasFailed { get; protected set; } + public virtual bool HasFailed { get; private set; } /// /// The default conditions for failing. From d47a8c0826c06af5595d01e8a946df9141065169 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Aug 2019 14:04:51 +0900 Subject: [PATCH 2108/2854] Remove unused combo result count statistic --- .../Scoring/OsuScoreProcessor.cs | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index cf0565c6da..dc4e59294b 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; -using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; @@ -22,8 +20,6 @@ namespace osu.Game.Rulesets.Osu.Scoring private float hpDrainRate; - private readonly Dictionary comboResultCounts = new Dictionary(); - protected override void ApplyBeatmap(Beatmap beatmap) { base.ApplyBeatmap(beatmap); @@ -31,22 +27,6 @@ namespace osu.Game.Rulesets.Osu.Scoring hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; } - protected override void Reset(bool storeResults) - { - base.Reset(storeResults); - comboResultCounts.Clear(); - } - - protected override void ApplyResult(JudgementResult result) - { - base.ApplyResult(result); - - var osuResult = (OsuJudgementResult)result; - - if (result.Type != HitResult.None) - comboResultCounts[osuResult.ComboType] = comboResultCounts.GetOrDefault(osuResult.ComboType) + 1; - } - protected override double HealthAdjustmentFactorFor(JudgementResult result) { switch (result.Type) From a9c4b5ac4e9c9c25527c4e71fb550809f7962d81 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Aug 2019 14:04:56 +0900 Subject: [PATCH 2109/2854] Add tests --- .../Visual/Gameplay/TestSceneFailJudgement.cs | 60 +++++++++++++++++++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 4 +- 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs new file mode 100644 index 0000000000..bb0901524f --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs @@ -0,0 +1,60 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneFailJudgement : AllPlayersTestScene + { + protected override Player CreatePlayer(Ruleset ruleset) + { + Mods.Value = Array.Empty(); + + var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Array.Empty()); + return new FailPlayer(ruleset.GetAutoplayMod().CreateReplayScore(beatmap)); + } + + protected override void AddCheckSteps() + { + AddUntilStep("wait for fail", () => Player.HasFailed); + AddUntilStep("wait for multiple judged objects", () => ((FailPlayer)Player).DrawableRuleset.Playfield.AllHitObjects.Count(h => h.AllJudged) > 1); + AddAssert("total judgements == 1", () => + { + int count = 0; + + foreach (var stat in (HitResult[])Enum.GetValues(typeof(HitResult))) + count += ((FailPlayer)Player).ScoreProcessor.GetStatistic(stat); + + return count == 1; + }); + } + + private class FailPlayer : ReplayPlayer + { + public new DrawableRuleset DrawableRuleset => base.DrawableRuleset; + + public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; + + protected override bool PauseOnFocusLost => false; + + public FailPlayer(Score score) + : base(score, false, false) + { + } + + protected override void LoadComplete() + { + base.LoadComplete(); + ScoreProcessor.FailConditions += (_, __) => true; + } + } + } +} diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index a39b432e20..1e7cd3810d 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -193,7 +193,7 @@ namespace osu.Game.Rulesets.Scoring score.Statistics[result] = GetStatistic(result); } - protected abstract int GetStatistic(HitResult result); + public abstract int GetStatistic(HitResult result); public abstract double GetStandardisedScore(); } @@ -421,7 +421,7 @@ namespace osu.Game.Rulesets.Scoring } } - protected override int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result); + public override int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result); public override double GetStandardisedScore() => getScore(ScoringMode.Standardised); From 5486c7736277d3d16b9ab1a45dc4051f24a5e7ce Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Aug 2019 15:25:42 +0900 Subject: [PATCH 2110/2854] Remove explicit autosize --- osu.Game/Skinning/LegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 3426ddaf16..5357a75e00 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -140,7 +140,7 @@ namespace osu.Game.Skinning if (texture != null && animatable) { - var animation = new TextureAnimation { DefaultFrameLength = frametime, AutoSizeAxes = Axes.None }; + var animation = new TextureAnimation { DefaultFrameLength = frametime }; for (int i = 1; texture != null; i++) { From 5073bce2dcbf8a5bce4f33b06036822524494082 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 9 Aug 2019 10:47:52 +0300 Subject: [PATCH 2111/2854] Basic request implementation --- .../Requests/GetUserKudosuHistoryRequest.cs | 21 +++++++++++++++++++ .../Requests/Responses/APIKudosuHistory.cs | 20 ++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs create mode 100644 osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs diff --git a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs new file mode 100644 index 0000000000..e90e297672 --- /dev/null +++ b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Game.Online.API.Requests.Responses; + +namespace osu.Game.Online.API.Requests +{ + public class GetUserKudosuHistoryRequest : PaginatedAPIRequest> + { + private readonly long userId; + + public GetUserKudosuHistoryRequest(long userId, int page = 0, int itemsPerPage = 5) + : base(page, itemsPerPage) + { + this.userId = userId; + } + + protected override string Target => $"users/{userId}/kudosu"; + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs new file mode 100644 index 0000000000..949ce200d2 --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using Newtonsoft.Json; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APIKudosuHistory + { + [JsonProperty("id")] + public int ID; + + [JsonProperty("createdAt")] + public DateTimeOffset CreatedAt; + + [JsonProperty("amount")] + public int Amount; + } +} From cf2a9db8e048abc2e788c8ee210a2d7b958c9010 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 9 Aug 2019 11:14:38 +0300 Subject: [PATCH 2112/2854] Implement basic layout for kudosu history --- .../Kudosu/DrawableKudosuHistoryItem.cs | 56 +++++++++++++++++++ .../Kudosu/PaginatedKudosuHistoryContainer.cs | 51 +++++++++++++++++ .../Profile/Sections/KudosuSection.cs | 4 +- 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs create mode 100644 osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs new file mode 100644 index 0000000000..1671073242 --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -0,0 +1,56 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Chat; + +namespace osu.Game.Overlays.Profile.Sections.Kudosu +{ + public class DrawableKudosuHistoryItem : DrawableProfileRow + { + private readonly APIKudosuHistory historyItem; + private LinkFlowContainer content; + + public DrawableKudosuHistoryItem(APIKudosuHistory historyItem) + { + this.historyItem = historyItem; + } + + [BackgroundDependencyLoader] + private void load() + { + LeftFlowContainer.Padding = new MarginPadding { Left = 10 }; + + LeftFlowContainer.Add(content = new LinkFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }); + + RightFlowContainer.Add(new DrawableDate(historyItem.CreatedAt) + { + Font = OsuFont.GetFont(size: 13), + Colour = OsuColour.Gray(0xAA), + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }); + + var formatted = createMessage(); + + content.AddLinks(formatted.Text, formatted.Links); + } + + protected override Drawable CreateLeftVisual() => new Container + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + }; + + private MessageFormatter.MessageFormatterResult createMessage() => MessageFormatter.FormatText($@"{historyItem.Amount}"); + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs new file mode 100644 index 0000000000..29b1d3c5aa --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs @@ -0,0 +1,51 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Game.Online.API.Requests; +using osu.Game.Users; +using System.Linq; +using osu.Framework.Bindables; + +namespace osu.Game.Overlays.Profile.Sections.Kudosu +{ + public class PaginatedKudosuHistoryContainer : PaginatedContainer + { + private GetUserKudosuHistoryRequest request; + + public PaginatedKudosuHistoryContainer(Bindable user, string header, string missing) + : base(user, header, missing) + { + ItemsPerPage = 5; + } + + protected override void ShowMore() + { + request = new GetUserKudosuHistoryRequest(User.Value.Id, VisiblePages++, ItemsPerPage); + request.Success += items => Schedule(() => + { + MoreButton.FadeTo(items.Count == ItemsPerPage ? 1 : 0); + MoreButton.IsLoading = false; + + if (!items.Any() && VisiblePages == 1) + { + MissingText.Show(); + return; + } + + MissingText.Hide(); + + foreach (var item in items) + ItemsContainer.Add(new DrawableKudosuHistoryItem(item)); + }); + + Api.Queue(request); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + request?.Cancel(); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/KudosuSection.cs b/osu.Game/Overlays/Profile/Sections/KudosuSection.cs index a17b68933c..9ccce7d837 100644 --- a/osu.Game/Overlays/Profile/Sections/KudosuSection.cs +++ b/osu.Game/Overlays/Profile/Sections/KudosuSection.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; using osu.Game.Overlays.Profile.Sections.Kudosu; namespace osu.Game.Overlays.Profile.Sections @@ -13,9 +14,10 @@ namespace osu.Game.Overlays.Profile.Sections public KudosuSection() { - Children = new[] + Children = new Drawable[] { new KudosuInfo(User), + new PaginatedKudosuHistoryContainer(User, null, @"This user hasn't received any kudosu!"), }; } } From 093359c13bc05d590e227a9db7615f4dc287be57 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 9 Aug 2019 11:19:14 +0300 Subject: [PATCH 2113/2854] fix incorrect json property --- osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs index 949ce200d2..271dcc320e 100644 --- a/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs +++ b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs @@ -11,7 +11,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty("id")] public int ID; - [JsonProperty("createdAt")] + [JsonProperty("created_at")] public DateTimeOffset CreatedAt; [JsonProperty("amount")] From b0eb2c6e28cec2831e02f3f63125532041232365 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Fri, 9 Aug 2019 19:21:08 +0930 Subject: [PATCH 2114/2854] Remove iOS BASS libraries and properly reference --- .../osu.Game.Rulesets.Catch.Tests.iOS.csproj | 8 -------- .../osu.Game.Rulesets.Mania.Tests.iOS.csproj | 8 -------- .../osu.Game.Rulesets.Osu.Tests.iOS.csproj | 8 -------- .../osu.Game.Rulesets.Taiko.Tests.iOS.csproj | 8 -------- osu.iOS.props | 18 +++++++++++++----- osu.iOS/libbass.a | Bin 1717480 -> 0 bytes osu.iOS/libbass_fx.a | Bin 621624 -> 0 bytes osu.iOS/osu.iOS.csproj | 6 ------ 8 files changed, 13 insertions(+), 43 deletions(-) delete mode 100644 osu.iOS/libbass.a delete mode 100644 osu.iOS/libbass_fx.a diff --git a/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj b/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj index 37e7c45a4e..7990c35e09 100644 --- a/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj +++ b/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj @@ -13,14 +13,6 @@ - - libbass.a - PreserveNewest - - - libbass_fx.a - PreserveNewest - Linker.xml diff --git a/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj b/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj index 24abccb19d..58c2e2aa5a 100644 --- a/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj +++ b/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj @@ -13,14 +13,6 @@ - - libbass.a - PreserveNewest - - - libbass_fx.a - PreserveNewest - Linker.xml diff --git a/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj b/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj index 9930a166e3..c7787bd162 100644 --- a/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj +++ b/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj @@ -13,14 +13,6 @@ - - libbass.a - PreserveNewest - - - libbass_fx.a - PreserveNewest - Linker.xml diff --git a/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj b/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj index d2817b743c..3e46bb89af 100644 --- a/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj +++ b/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj @@ -13,14 +13,6 @@ - - libbass.a - PreserveNewest - - - libbass_fx.a - PreserveNewest - Linker.xml diff --git a/osu.iOS.props b/osu.iOS.props index b46438f766..82c60d0aed 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -3,7 +3,8 @@ {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Resources PackageReference - --nolinkaway -gcc_flags "-lstdc++ -framework AudioToolbox -framework SystemConfiguration -framework CFNetwork -framework Accelerate + --nolinkaway + -lstdc++ -lbz2 -framework AudioToolbox -framework AVFoundation -framework CoreMedia -framework VideoToolbox -framework SystemConfiguration -framework CFNetwork -framework Accelerate true @@ -24,7 +25,7 @@ false Default - $(DefaultMtouchExtraArgs) -force_load $(OutputPath)\libbass.a -force_load $(OutputPath)\libbass_fx.a" + $(DefaultMtouchExtraArgs) -gcc_flags "$(DefaultMtouchGccFlags)" false cjk,mideast,other,rare,west @@ -44,7 +45,7 @@ NSUrlSessionHandler Default - $(DefaultMtouchExtraArgs) -force_load $(OutputPath)\libbass.a -force_load $(OutputPath)\libbass_fx.a" + $(DefaultMtouchExtraArgs) -gcc_flags "$(DefaultMtouchGccFlags)" false cjk,mideast,other,rare,west @@ -62,7 +63,7 @@ NSUrlSessionHandler Default - $(DefaultMtouchExtraArgs) -force_load $(OutputPath)\libbass.a -force_load $(OutputPath)\libbass_fx.a" + $(DefaultMtouchExtraArgs) -gcc_flags "$(DefaultMtouchGccFlags)" false cjk,mideast,other,rare,west @@ -86,10 +87,17 @@ NSUrlSessionHandler Default - $(DefaultMtouchExtraArgs) -force_load $(OutputPath)\libbass.a -force_load $(OutputPath)\libbass_fx.a" + $(DefaultMtouchExtraArgs) -gcc_flags "$(DefaultMtouchGccFlags)" false cjk,mideast,other,rare,west + + + Static + False + True + + diff --git a/osu.iOS/libbass.a b/osu.iOS/libbass.a deleted file mode 100644 index c34e6a0a0cfb30a240d70443fa0276f6e2774f90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1717480 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 z�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

n1vJMuGM~kIoXjExl$^E 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 PzG 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

h`#jTBZQRdTM3sKKP!c16W2fH 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 ydCELbndd3bprQ_nijA<#w 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

    mToN2G;_Q9zfoOf#Lji2km|0>|0bK?8Z)3&9s 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

    +8|`Fe}5he^`<0O^nL5??QI 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 - - PreserveNewest - - - PreserveNewest - From 1351bfdd1fb2aeac020f6f032125db47bda0e3c0 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Fri, 9 Aug 2019 19:21:21 +0930 Subject: [PATCH 2115/2854] Add iOS FFmpeg references --- osu.iOS.props | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.iOS.props b/osu.iOS.props index 82c60d0aed..58c2a55b74 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -97,6 +97,11 @@ False True + + Static + False + True + From f68e41f7b9fef17f238cf322d655f9c7b8164074 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Fri, 9 Aug 2019 19:30:31 +0930 Subject: [PATCH 2116/2854] Remove BASS references from osu.Game.Tests.iOS --- osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj | 8 -------- 1 file changed, 8 deletions(-) diff --git a/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj b/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj index ea5ab699f3..5c0713b895 100644 --- a/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj +++ b/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj @@ -13,14 +13,6 @@ - - libbass.a - PreserveNewest - - - libbass_fx.a - PreserveNewest - Linker.xml From 90b1fe81f3ffa99167bfa20af7b87ba3c60c8ca9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Aug 2019 19:12:29 +0900 Subject: [PATCH 2117/2854] Update cached usage in line with framework changes --- osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 +- osu.Game.Tournament/Screens/Ladder/LadderScreen.cs | 2 +- osu.Game/Graphics/Backgrounds/Triangles.cs | 4 ++-- osu.Game/Graphics/UserInterface/LineGraph.cs | 2 +- osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 2 +- osu.Game/Screens/Play/SquareGraph.cs | 2 +- osu.Game/Screens/Select/BeatmapCarousel.cs | 4 ++-- osu.Game/Storyboards/CommandTimeline.cs | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs index 8102718edf..a92e56d3c3 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces } } - private Cached subtractionCache = new Cached(); + private readonly Cached subtractionCache = new Cached(); public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) { diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index a4638c31f2..d3279652c7 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Objects public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity; public double Duration => EndTime - StartTime; - private Cached endPositionCache; + private readonly Cached endPositionCache = new Cached(); public override Vector2 EndPosition => endPositionCache.IsValid ? endPositionCache.Value : endPositionCache.Value = Position + this.CurvePositionAt(1); diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs index 67531ce5d3..83a41a662f 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -88,7 +88,7 @@ namespace osu.Game.Tournament.Screens.Ladder }; } - private Cached layout = new Cached(); + private readonly Cached layout = new Cached(); protected override void Update() { diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 2b68e8530d..dffa0c4fd5 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -191,7 +191,7 @@ namespace osu.Game.Graphics.Backgrounds private readonly List parts = new List(); private Vector2 size; - private TriangleBatch vertexBatch; + private QuadBatch vertexBatch; public TrianglesDrawNode(Triangles source) : base(source) @@ -217,7 +217,7 @@ namespace osu.Game.Graphics.Backgrounds if (Source.AimCount > 0 && (vertexBatch == null || vertexBatch.Size != Source.AimCount)) { vertexBatch?.Dispose(); - vertexBatch = new TriangleBatch(Source.AimCount, 1); + vertexBatch = new QuadBatch(Source.AimCount, 1); } shader.Bind(); diff --git a/osu.Game/Graphics/UserInterface/LineGraph.cs b/osu.Game/Graphics/UserInterface/LineGraph.cs index 714e953816..6d65b77cbf 100644 --- a/osu.Game/Graphics/UserInterface/LineGraph.cs +++ b/osu.Game/Graphics/UserInterface/LineGraph.cs @@ -93,7 +93,7 @@ namespace osu.Game.Graphics.UserInterface return base.Invalidate(invalidation, source, shallPropagate); } - private Cached pathCached = new Cached(); + private readonly Cached pathCached = new Cached(); protected override void Update() { diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 069e2d1a0b..19247d8a37 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.UI.Scrolling [Resolved] private IScrollingInfo scrollingInfo { get; set; } - private Cached initialStateCache = new Cached(); + private readonly Cached initialStateCache = new Cached(); public ScrollingHitObjectContainer() { diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 5b7a9574b6..9c56725c4e 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -75,7 +75,7 @@ namespace osu.Game.Screens.Play return base.Invalidate(invalidation, source, shallPropagate); } - private Cached layout = new Cached(); + private readonly Cached layout = new Cached(); private ScheduledDelegate scheduledCreate; protected override void Update() diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index cf88b697d9..7366fa8c17 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -93,8 +93,8 @@ namespace osu.Game.Screens.Select } private readonly List yPositions = new List(); - private Cached itemsCache = new Cached(); - private Cached scrollPositionCache = new Cached(); + private readonly Cached itemsCache = new Cached(); + private readonly Cached scrollPositionCache = new Cached(); private readonly Container scrollableContent; diff --git a/osu.Game/Storyboards/CommandTimeline.cs b/osu.Game/Storyboards/CommandTimeline.cs index aa1f137cf3..bcf642b4ea 100644 --- a/osu.Game/Storyboards/CommandTimeline.cs +++ b/osu.Game/Storyboards/CommandTimeline.cs @@ -15,10 +15,10 @@ namespace osu.Game.Storyboards public IEnumerable Commands => commands.OrderBy(c => c.StartTime); public bool HasCommands => commands.Count > 0; - private Cached startTimeBacking; + private readonly Cached startTimeBacking = new Cached(); public double StartTime => startTimeBacking.IsValid ? startTimeBacking : startTimeBacking.Value = HasCommands ? commands.Min(c => c.StartTime) : double.MinValue; - private Cached endTimeBacking; + private readonly Cached endTimeBacking = new Cached(); public double EndTime => endTimeBacking.IsValid ? endTimeBacking : endTimeBacking.Value = HasCommands ? commands.Max(c => c.EndTime) : double.MaxValue; public T StartValue => HasCommands ? commands.OrderBy(c => c.StartTime).First().StartValue : default; From cb0cd7ed589ef56f1b9a3cedae662a709397a022 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Aug 2019 20:05:28 +0900 Subject: [PATCH 2118/2854] Add triangles intro --- .../Visual/Menus/TestSceneIntroTriangles.cs | 15 + osu.Game/Configuration/IntroSequence.cs | 11 + osu.Game/Configuration/OsuConfigManager.cs | 5 +- .../Sections/Audio/MainMenuSettings.cs | 11 +- osu.Game/Screens/BackgroundScreen.cs | 16 +- .../Backgrounds/BackgroundScreenDefault.cs | 5 + osu.Game/Screens/Loader.cs | 19 +- osu.Game/Screens/Menu/IntroTriangles.cs | 413 ++++++++++++++++++ 8 files changed, 486 insertions(+), 9 deletions(-) create mode 100644 osu.Game.Tests/Visual/Menus/TestSceneIntroTriangles.cs create mode 100644 osu.Game/Configuration/IntroSequence.cs create mode 100644 osu.Game/Screens/Menu/IntroTriangles.cs diff --git a/osu.Game.Tests/Visual/Menus/TestSceneIntroTriangles.cs b/osu.Game.Tests/Visual/Menus/TestSceneIntroTriangles.cs new file mode 100644 index 0000000000..df79584167 --- /dev/null +++ b/osu.Game.Tests/Visual/Menus/TestSceneIntroTriangles.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Screens; +using osu.Game.Screens.Menu; + +namespace osu.Game.Tests.Visual.Menus +{ + [TestFixture] + public class TestSceneIntroTriangles : IntroTestScene + { + protected override IScreen CreateScreen() => new IntroTriangles(); + } +} diff --git a/osu.Game/Configuration/IntroSequence.cs b/osu.Game/Configuration/IntroSequence.cs new file mode 100644 index 0000000000..1eb953be36 --- /dev/null +++ b/osu.Game/Configuration/IntroSequence.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Configuration +{ + public enum IntroSequence + { + Circles, + Triangles + } +} diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 1da7c7ec1d..19f46c1d6a 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -105,6 +105,8 @@ namespace osu.Game.Configuration Set(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f); Set(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f); + + Set(OsuSetting.IntroSequence, IntroSequence.Triangles); } public OsuConfigManager(Storage storage) @@ -167,6 +169,7 @@ namespace osu.Game.Configuration ScalingPositionY, ScalingSizeX, ScalingSizeY, - UIScale + UIScale, + IntroSequence } } diff --git a/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs index 4e43caff23..5ccdc952ba 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs @@ -1,7 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Audio @@ -13,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - Children = new[] + Children = new Drawable[] { new SettingsCheckbox { @@ -25,6 +28,12 @@ namespace osu.Game.Overlays.Settings.Sections.Audio LabelText = "osu! music theme", Bindable = config.GetBindable(OsuSetting.MenuMusic) }, + new SettingsDropdown + { + LabelText = "Intro sequence", + Bindable = config.GetBindable(OsuSetting.IntroSequence), + Items = Enum.GetValues(typeof(IntroSequence)).Cast() + }, }; } } diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index bbe162cf7c..5dfaceccf5 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -11,8 +11,11 @@ namespace osu.Game.Screens { public abstract class BackgroundScreen : Screen, IEquatable { - protected BackgroundScreen() + private readonly bool animateOnEnter; + + protected BackgroundScreen(bool animateOnEnter = true) { + this.animateOnEnter = animateOnEnter; Anchor = Anchor.Centre; Origin = Anchor.Centre; } @@ -39,11 +42,14 @@ namespace osu.Game.Screens public override void OnEntering(IScreen last) { - this.FadeOut(); - this.MoveToX(x_movement_amount); + if (animateOnEnter) + { + this.FadeOut(); + this.MoveToX(x_movement_amount); - this.FadeIn(transition_length, Easing.InOutQuart); - this.MoveToX(0, transition_length, Easing.InOutQuart); + this.FadeIn(transition_length, Easing.InOutQuart); + this.MoveToX(0, transition_length, Easing.InOutQuart); + } base.OnEntering(last); } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 55338ea01a..2d7fe6a6a3 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -25,6 +25,11 @@ namespace osu.Game.Screens.Backgrounds private Bindable user; private Bindable skin; + public BackgroundScreenDefault(bool animateOnEnter = true) + : base(animateOnEnter) + { + } + [BackgroundDependencyLoader] private void load(IAPIProvider api, SkinManager skinManager) { diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index de00ba2e9f..850349272e 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -9,6 +9,8 @@ using osu.Framework.Graphics.Shaders; using osu.Game.Screens.Menu; using osuTK; using osu.Framework.Screens; +using osu.Game.Configuration; +using IntroSequence = osu.Game.Configuration.IntroSequence; namespace osu.Game.Screens { @@ -45,6 +47,8 @@ namespace osu.Game.Screens private OsuScreen loadableScreen; private ShaderPrecompiler precompiler; + private IntroSequence introSequence; + protected virtual OsuScreen CreateLoadableScreen() { if (showDisclaimer) @@ -53,7 +57,17 @@ namespace osu.Game.Screens return getIntroSequence(); } - private IntroScreen getIntroSequence() => new IntroCircles(); + private IntroScreen getIntroSequence() + { + switch (introSequence) + { + case IntroSequence.Circles: + return new IntroCircles(); + + default: + return new IntroTriangles(); + } + } protected virtual ShaderPrecompiler CreateShaderPrecompiler() => new ShaderPrecompiler(); @@ -79,9 +93,10 @@ namespace osu.Game.Screens } [BackgroundDependencyLoader] - private void load(OsuGameBase game) + private void load(OsuGameBase game, OsuConfigManager config) { showDisclaimer = game.IsDeployedBuild; + introSequence = config.Get(OsuSetting.IntroSequence); } ///

    public int MaxCatchUpFrames { get; set; } = 5; + /// + /// Whether to enable frame-stable playback. + /// + internal bool FrameStablePlayback = true; + [Cached] public GameplayClock GameplayClock { get; } @@ -113,6 +118,9 @@ namespace osu.Game.Rulesets.UI try { + if (!FrameStablePlayback) + manualClock.CurrentTime = newProposedTime; + if (firstConsumption) { // On the first update, frame-stability seeking would result in unexpected/unwanted behaviour. From 0f4bada21e05264e6376f2227aa17524e145c474 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Aug 2019 18:27:45 +0900 Subject: [PATCH 2204/2854] Fix right click absolute scrolling interfering with context menus --- osu.Game/Screens/Select/BeatmapCarousel.cs | 33 +++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 6fda81e47d..7df27de55f 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -115,7 +115,7 @@ namespace osu.Game.Screens.Select InternalChild = new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, - Child = scroll = new OsuScrollContainer + Child = scroll = new CarouselScrollContainer { Masking = false, RelativeSizeAxes = Axes.Both, @@ -694,5 +694,36 @@ namespace osu.Game.Screens.Select base.PerformSelection(); } } + + private class CarouselScrollContainer : OsuScrollContainer + { + private bool rightMouseScrollBlocked; + + protected override bool OnMouseDown(MouseDownEvent e) + { + if (e.Button == MouseButton.Right) + { + // we need to block right click absolute scrolling when hovering a carousel item so context menus can display. + // this can be reconsidered when we have an alternative to right click scrolling. + if (GetContainingInputManager().HoveredDrawables.OfType().Any()) + { + rightMouseScrollBlocked = true; + return false; + } + + rightMouseScrollBlocked = false; + } + + return base.OnMouseDown(e); + } + + protected override bool OnDragStart(DragStartEvent e) + { + if (rightMouseScrollBlocked) + return false; + + return base.OnDragStart(e); + } + } } } From b57298406ff7f6a40b1dc661ac2769a363d49d3f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Aug 2019 19:25:33 +0900 Subject: [PATCH 2205/2854] Fix right click blocking not resetting correctly --- osu.Game/Screens/Select/BeatmapCarousel.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 7df27de55f..23c581c6f9 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -710,10 +710,9 @@ namespace osu.Game.Screens.Select rightMouseScrollBlocked = true; return false; } - - rightMouseScrollBlocked = false; } + rightMouseScrollBlocked = false; return base.OnMouseDown(e); } From 386d78881394f344ab87054b926bbe3eb6963a81 Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Thu, 15 Aug 2019 18:32:45 +0200 Subject: [PATCH 2206/2854] Change if-else to ternary if --- osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index a46ecab6f0..6326c3a044 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -113,11 +113,7 @@ namespace osu.Game.Rulesets.Taiko.Replays else throw new InvalidOperationException("Unknown hit object type."); - TaikoHitObject nextHitObject; - if (i < Beatmap.HitObjects.Count - 1) - nextHitObject = Beatmap.HitObjects[i + 1]; - else - nextHitObject = null; + TaikoHitObject nextHitObject = i < Beatmap.HitObjects.Count - 1 ? Beatmap.HitObjects[i + 1] : null; bool canDelayKeyUp = nextHitObject != null && nextHitObject.StartTime > endTime + KEY_UP_DELAY; From aa3651f65eb98777c6254ae29e2512f2db098ba4 Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Thu, 15 Aug 2019 19:45:10 +0200 Subject: [PATCH 2207/2854] Change the logic so the last button gets unpressed instead of staying pressed forever --- osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index 6326c3a044..1d35393de0 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -115,7 +115,7 @@ namespace osu.Game.Rulesets.Taiko.Replays TaikoHitObject nextHitObject = i < Beatmap.HitObjects.Count - 1 ? Beatmap.HitObjects[i + 1] : null; - bool canDelayKeyUp = nextHitObject != null && nextHitObject.StartTime > endTime + KEY_UP_DELAY; + bool canDelayKeyUp = nextHitObject == null || nextHitObject.StartTime > endTime + KEY_UP_DELAY; if (canDelayKeyUp) Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY)); From ff601eefe6afaf6683af631f71361e05d644278a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Aug 2019 13:21:28 +0900 Subject: [PATCH 2208/2854] Fix failing test --- osu.Game.Tests/Visual/Gameplay/TestSceneHoldForMenuButton.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHoldForMenuButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHoldForMenuButton.cs index d42b61ea55..0c5ead10cf 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHoldForMenuButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHoldForMenuButton.cs @@ -17,6 +17,8 @@ namespace osu.Game.Tests.Visual.Gameplay { private bool exitAction; + protected override double TimePerAction => 100; // required for the early exit test, since hold-to-confirm delay is 200ms + [BackgroundDependencyLoader] private void load() { From d732d276b1186ce2108c2a5c88a6c2ec45907284 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Aug 2019 13:21:41 +0900 Subject: [PATCH 2209/2854] Make activation delay customisable --- .../Graphics/Containers/HoldToConfirmContainer.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs index 18a4241a79..773265d19b 100644 --- a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs +++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs @@ -12,9 +12,11 @@ namespace osu.Game.Graphics.Containers { public Action Action; - private const int activate_delay = 200; + private const int default_activation_delay = 200; private const int fadeout_delay = 200; + private readonly double activationDelay; + private bool fired; private bool confirming; @@ -25,13 +27,22 @@ namespace osu.Game.Graphics.Containers public Bindable Progress = new BindableDouble(); + /// + /// Create a new instance. + /// + /// The time requried before an action is confirmed. + protected HoldToConfirmContainer(double activationDelay = default_activation_delay) + { + this.activationDelay = activationDelay; + } + protected void BeginConfirm() { if (confirming || (!AllowMultipleFires && fired)) return; confirming = true; - this.TransformBindableTo(Progress, 1, activate_delay * (1 - Progress.Value), Easing.Out).OnComplete(_ => Confirm()); + this.TransformBindableTo(Progress, 1, activationDelay * (1 - Progress.Value), Easing.Out).OnComplete(_ => Confirm()); } protected virtual void Confirm() From 3949d46383b6adece23f0e43445ee92b3ff31585 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Aug 2019 13:46:08 +0900 Subject: [PATCH 2210/2854] Add test fix to other test --- .../Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs index a017418e3a..f787754aa4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs @@ -13,6 +13,8 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneHoldToConfirmOverlay : OsuTestScene { + protected override double TimePerAction => 100; // required for the early exit test, since hold-to-confirm delay is 200ms + public override IReadOnlyList RequiredTypes => new[] { typeof(ExitConfirmOverlay), From b2e05252d78c32b0e2711354bd9aac50797a2c95 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Aug 2019 14:16:00 +0900 Subject: [PATCH 2211/2854] Update fastlane and dependencies --- Gemfile.lock | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 17c0db12e7..56e640599f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,7 +6,7 @@ GEM public_suffix (>= 2.0.2, < 4.0) atomos (0.1.3) babosa (1.0.2) - claide (1.0.2) + claide (1.0.3) colored (1.2) colored2 (3.1.2) commander-fastlane (4.4.6) @@ -14,11 +14,11 @@ GEM declarative (0.0.10) declarative-option (0.1.0) digest-crc (0.4.1) - domain_name (0.5.20180417) + domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.1) + dotenv (2.7.5) emoji_regex (1.0.1) - excon (0.62.0) + excon (0.66.0) faraday (0.15.4) multipart-post (>= 1.2, < 3) faraday-cookie_jar (0.0.6) @@ -27,7 +27,7 @@ GEM faraday_middleware (0.13.1) faraday (>= 0.7.4, < 1.0) fastimage (2.1.5) - fastlane (2.117.0) + fastlane (2.129.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.3, < 3.0.0) babosa (>= 1.0.2, < 2.0.0) @@ -46,8 +46,8 @@ GEM google-cloud-storage (>= 1.15.0, < 2.0.0) highline (>= 1.7.2, < 2.0.0) json (< 3.0.0) - mini_magick (~> 4.5.1) - multi_json + jwt (~> 2.1.0) + mini_magick (>= 4.9.4, < 5.0.0) multi_xml (~> 0.5) multipart-post (~> 2.0.0) plist (>= 3.1.0, < 4.0.0) @@ -56,12 +56,12 @@ GEM security (= 0.1.3) simctl (~> 1.6.3) slack-notifier (>= 2.0.0, < 3.0.0) - terminal-notifier (>= 1.6.2, < 2.0.0) + terminal-notifier (>= 2.0.0, < 3.0.0) terminal-table (>= 1.4.5, < 2.0.0) tty-screen (>= 0.6.3, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0) word_wrap (~> 1.0.0) - xcodeproj (>= 1.6.0, < 2.0.0) + xcodeproj (>= 1.8.1, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) fastlane-plugin-clean_testflight_testers (0.2.0) @@ -79,7 +79,7 @@ GEM signet (~> 0.9) google-cloud-core (1.3.0) google-cloud-env (~> 1.0) - google-cloud-env (1.0.5) + google-cloud-env (1.2.0) faraday (~> 0.11) google-cloud-storage (1.16.0) digest-crc (~> 0.4) @@ -102,8 +102,8 @@ GEM memoist (0.16.0) mime-types (3.2.2) mime-types-data (~> 3.2015) - mime-types-data (3.2018.0812) - mini_magick (4.5.1) + mime-types-data (3.2019.0331) + mini_magick (4.9.5) mini_portile2 (2.4.0) multi_json (1.13.1) multi_xml (0.6.0) @@ -112,7 +112,7 @@ GEM naturally (2.2.0) nokogiri (1.10.1) mini_portile2 (~> 2.4.0) - os (1.0.0) + os (1.0.1) plist (3.5.0) public_suffix (2.0.5) representable (3.0.4) @@ -121,7 +121,7 @@ GEM uber (< 0.2.0) retriable (3.1.2) rouge (2.0.7) - rubyzip (1.2.2) + rubyzip (1.2.3) security (0.1.3) signet (0.11.0) addressable (~> 2.3) @@ -136,20 +136,20 @@ GEM fastlane (>= 2.29.0) highline (~> 1.7) nokogiri (~> 1.7) - terminal-notifier (1.8.0) + terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - tty-cursor (0.6.1) - tty-screen (0.6.5) - tty-spinner (0.9.0) - tty-cursor (~> 0.6.0) + tty-cursor (0.7.0) + tty-screen (0.7.0) + tty-spinner (0.9.1) + tty-cursor (~> 0.7) uber (0.1.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.5) - unicode-display_width (1.4.1) + unf_ext (0.0.7.6) + unicode-display_width (1.6.0) word_wrap (1.0.0) - xcodeproj (1.8.1) + xcodeproj (1.12.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) From 7843b9c532bea4e80934a4da675e37f18d68ab68 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Aug 2019 15:44:17 +0900 Subject: [PATCH 2212/2854] Update fastlane plugins --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 56e640599f..f7c19064b4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -64,7 +64,7 @@ GEM xcodeproj (>= 1.8.1, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) - fastlane-plugin-clean_testflight_testers (0.2.0) + fastlane-plugin-clean_testflight_testers (0.3.0) fastlane-plugin-souyuz (0.8.1) souyuz (>= 0.8.1) fastlane-plugin-xamarin (0.6.3) @@ -110,7 +110,7 @@ GEM multipart-post (2.0.0) nanaimo (0.2.6) naturally (2.2.0) - nokogiri (1.10.1) + nokogiri (1.10.4) mini_portile2 (~> 2.4.0) os (1.0.1) plist (3.5.0) From 3c208b709bfdfd9432ab67a695da5503deee83a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Aug 2019 15:44:38 +0900 Subject: [PATCH 2213/2854] Reorder functions in Fastfile for readability --- fastlane/Fastfile | 29 +++++++++++++---------------- fastlane/README.md | 30 +++++++++++++++--------------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 48c16caf0f..0b60e28b0f 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -1,22 +1,6 @@ update_fastlane -default_platform(:ios) - platform :ios do - lane :testflight_prune_dry do - clean_testflight_testers(days_of_inactivity:45, dry_run: true) - end - - # Specify a custom number for what's "inactive" - lane :testflight_prune do - clean_testflight_testers(days_of_inactivity: 45) # 120 days, so about 4 months - end - - lane :update_version do |options| - options[:plist_path] = '../osu.iOS/Info.plist' - app_version(options) - end - desc 'Deploy to testflight' lane :beta do |options| update_version(options) @@ -62,4 +46,17 @@ platform :ios do match(options) end + + lane :update_version do |options| + options[:plist_path] = '../osu.iOS/Info.plist' + app_version(options) + end + + lane :testflight_prune_dry do + clean_testflight_testers(days_of_inactivity:45, dry_run: true) + end + + lane :testflight_prune do + clean_testflight_testers(days_of_inactivity: 45) + end end diff --git a/fastlane/README.md b/fastlane/README.md index 53bbc62cae..fbccf1c8c0 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -16,21 +16,6 @@ or alternatively using `brew cask install fastlane` # Available Actions ## iOS -### ios testflight_prune_dry -``` -fastlane ios testflight_prune_dry -``` - -### ios testflight_prune -``` -fastlane ios testflight_prune -``` - -### ios update_version -``` -fastlane ios update_version -``` - ### ios beta ``` fastlane ios beta @@ -46,6 +31,21 @@ Compile the project fastlane ios provision ``` Install provisioning profiles using match +### ios update_version +``` +fastlane ios update_version +``` + +### ios testflight_prune_dry +``` +fastlane ios testflight_prune_dry +``` + +### ios testflight_prune +``` +fastlane ios testflight_prune +``` + ---- From 536e7d875736de01a5b574259fcdd8085c37aa60 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Aug 2019 19:32:17 +0900 Subject: [PATCH 2214/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 7bc60ef884..bb283dc0c5 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -61,6 +61,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f5e4d4b1fb..758c4dda4c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 63fa354418..d6ad35b663 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -118,8 +118,8 @@ - - + + From 24d2b504dd4ce36f2e128a28075444f69860de73 Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Fri, 16 Aug 2019 12:39:54 +0200 Subject: [PATCH 2215/2854] Stop autoplay from missing on very dense notes when playing mania --- .../Replays/ManiaAutoGenerator.cs | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index e5669816fa..49bb47cc2b 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Replays; using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Replays; @@ -77,10 +78,35 @@ namespace osu.Game.Rulesets.Mania.Replays private IEnumerable generateActionPoints() { - foreach (var obj in Beatmap.HitObjects) + for (int i = 0; i < Beatmap.HitObjects.Count; i++) { - yield return new HitPoint { Time = obj.StartTime, Column = obj.Column }; - yield return new ReleasePoint { Time = ((obj as IHasEndTime)?.EndTime ?? obj.StartTime) + RELEASE_DELAY, Column = obj.Column }; + var currentObject = Beatmap.HitObjects[i]; + + double endTime = (currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime; + + var nextObjectInTheSameColumn = getNextObjectInTheSameColumn(i); + + bool canDelayKeyUp = nextObjectInTheSameColumn == null || + nextObjectInTheSameColumn.StartTime > endTime + KEY_UP_DELAY; + + double releaseDelay = canDelayKeyUp ? RELEASE_DELAY : nextObjectInTheSameColumn.StartTime - endTime - 1; + + yield return new HitPoint { Time = currentObject.StartTime, Column = currentObject.Column }; + + yield return new ReleasePoint { Time = endTime + releaseDelay, Column = currentObject.Column }; + } + + ManiaHitObject getNextObjectInTheSameColumn(int currentIndex) + { + int desiredColumn = Beatmap.HitObjects[currentIndex++].Column; + + for (; currentIndex < Beatmap.HitObjects.Count; currentIndex++) + { + if (Beatmap.HitObjects[currentIndex].Column == desiredColumn) + return Beatmap.HitObjects[currentIndex]; + } + + return null; } } From 132d51a2cc7ff16b0f35227fa03cb97efee60088 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Aug 2019 19:47:35 +0900 Subject: [PATCH 2216/2854] Update tooltip implementation --- .../Profile/Header/Components/RankGraph.cs | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index 9cb9d48de7..de760eedfd 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -196,17 +196,30 @@ namespace osu.Game.Overlays.Profile.Header.Components } } - public string TooltipText => Statistics.Value?.Ranks.Global == null ? "" : $"#{ranks[dayIndex].Value:#,##0}|{ranked_days - ranks[dayIndex].Key + 1}"; + public object TooltipContent + { + get + { + if (Statistics.Value?.Ranks.Global == null) + return null; + + var days = ranked_days - ranks[dayIndex].Key + 1; + + return new TooltipDisplayContent + { + Rank = $"#{ranks[dayIndex].Value:#,##0}", + Time = days == 0 ? "now" : $"{days} days ago" + }; + } + } public ITooltip GetCustomTooltip() => new RankGraphTooltip(); - public class RankGraphTooltip : VisibilityContainer, ITooltip + private class RankGraphTooltip : VisibilityContainer, ITooltip { private readonly OsuSpriteText globalRankingText, timeText; private readonly Box background; - public string TooltipText { get; set; } - public RankGraphTooltip() { AutoSizeAxes = Axes.Both; @@ -260,11 +273,14 @@ namespace osu.Game.Overlays.Profile.Header.Components background.Colour = colours.GreySeafoamDark; } - public void Refresh() + public bool SetContent(object content) { - var info = TooltipText.Split('|'); - globalRankingText.Text = info[0]; - timeText.Text = info[1] == "0" ? "now" : $"{info[1]} days ago"; + if (!(content is TooltipDisplayContent info)) + return false; + + globalRankingText.Text = info.Rank; + timeText.Text = info.Time; + return true; } private bool instantMove = true; @@ -280,9 +296,24 @@ namespace osu.Game.Overlays.Profile.Header.Components this.MoveTo(pos, 200, Easing.OutQuint); } + public void Refresh() + { + } + + public string TooltipText + { + set => throw new InvalidOperationException(); + } + protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); } + + private class TooltipDisplayContent + { + public string Rank; + public string Time; + } } } From 7de1757aae1bae0baf006a010cd3be4eb172da62 Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Fri, 16 Aug 2019 12:50:48 +0200 Subject: [PATCH 2217/2854] Small improvements --- osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 49bb47cc2b..4fea834748 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -81,19 +81,18 @@ namespace osu.Game.Rulesets.Mania.Replays for (int i = 0; i < Beatmap.HitObjects.Count; i++) { var currentObject = Beatmap.HitObjects[i]; + var nextObjectInTheSameColumn = getNextObjectInTheSameColumn(i); double endTime = (currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime; - var nextObjectInTheSameColumn = getNextObjectInTheSameColumn(i); - bool canDelayKeyUp = nextObjectInTheSameColumn == null || nextObjectInTheSameColumn.StartTime > endTime + KEY_UP_DELAY; - double releaseDelay = canDelayKeyUp ? RELEASE_DELAY : nextObjectInTheSameColumn.StartTime - endTime - 1; + double calculatedDelay = canDelayKeyUp ? RELEASE_DELAY : (nextObjectInTheSameColumn.StartTime - endTime) * 0.9; yield return new HitPoint { Time = currentObject.StartTime, Column = currentObject.Column }; - yield return new ReleasePoint { Time = endTime + releaseDelay, Column = currentObject.Column }; + yield return new ReleasePoint { Time = endTime + calculatedDelay, Column = currentObject.Column }; } ManiaHitObject getNextObjectInTheSameColumn(int currentIndex) From 58d2268b9e6da29b4208ab7d4bf8e0e9c96ab0a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Aug 2019 19:52:35 +0900 Subject: [PATCH 2218/2854] Combine conditionals that provide the same behaviour --- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index 7c143aa158..66ba6f7e64 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -118,9 +118,6 @@ namespace osu.Game.Rulesets.UI try { - if (!FrameStablePlayback) - manualClock.CurrentTime = newProposedTime; - if (firstConsumption) { // On the first update, frame-stability seeking would result in unexpected/unwanted behaviour. @@ -141,7 +138,7 @@ namespace osu.Game.Rulesets.UI : Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time); } - if (!isAttached) + if (!isAttached || !FrameStablePlayback) { manualClock.CurrentTime = newProposedTime; } From d11b896148508b3e1555982c64ee44ed20b4ff29 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Aug 2019 20:21:00 +0900 Subject: [PATCH 2219/2854] Move FrameStablePlayback handling to early return --- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index 66ba6f7e64..25fe5cdd96 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -118,7 +118,14 @@ namespace osu.Game.Rulesets.UI try { - if (firstConsumption) + if (!FrameStablePlayback) + { + manualClock.CurrentTime = newProposedTime; + requireMoreUpdateLoops = false; + return; + } + + else if (firstConsumption) { // On the first update, frame-stability seeking would result in unexpected/unwanted behaviour. // Instead we perform an initial seek to the proposed time. @@ -138,7 +145,7 @@ namespace osu.Game.Rulesets.UI : Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time); } - if (!isAttached || !FrameStablePlayback) + if (!isAttached) { manualClock.CurrentTime = newProposedTime; } From 152df9f3d570ee36d05a088c2bbe6d88c3e4a033 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Aug 2019 20:23:09 +0900 Subject: [PATCH 2220/2854] Remove accidental blank line --- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index 25fe5cdd96..05d3c02381 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -124,7 +124,6 @@ namespace osu.Game.Rulesets.UI requireMoreUpdateLoops = false; return; } - else if (firstConsumption) { // On the first update, frame-stability seeking would result in unexpected/unwanted behaviour. From 0f9706e7985428f1273fd23a9b0e3925dbf1f357 Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Sat, 17 Aug 2019 00:18:25 +0200 Subject: [PATCH 2221/2854] Fix using invalid constant --- osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 4fea834748..43150958d0 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Mania.Replays double endTime = (currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime; bool canDelayKeyUp = nextObjectInTheSameColumn == null || - nextObjectInTheSameColumn.StartTime > endTime + KEY_UP_DELAY; + nextObjectInTheSameColumn.StartTime > endTime + RELEASE_DELAY; double calculatedDelay = canDelayKeyUp ? RELEASE_DELAY : (nextObjectInTheSameColumn.StartTime - endTime) * 0.9; From 4fa9abeecec9540da8b24faee553fd482006379d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Aug 2019 15:16:24 +0900 Subject: [PATCH 2222/2854] Replace DifficultyColouredContainer with a more scalable solution --- osu.Game/Beatmaps/BeatmapInfo.cs | 16 ++++ osu.Game/Beatmaps/DifficultyRating.cs | 15 ++++ .../Drawables/DifficultyColouredContainer.cs | 85 ------------------- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 11 ++- osu.Game/Graphics/OsuColour.cs | 26 ++++++ osu.Game/Screens/Select/BeatmapInfoWedge.cs | 14 +-- 6 files changed, 73 insertions(+), 94 deletions(-) create mode 100644 osu.Game/Beatmaps/DifficultyRating.cs delete mode 100644 osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 8042f6b4b9..700f981088 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -129,6 +129,22 @@ namespace osu.Game.Beatmaps ///
    diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs new file mode 100644 index 0000000000..87d6012205 --- /dev/null +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -0,0 +1,413 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.IO; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Audio.Track; +using osu.Framework.Bindables; +using osu.Framework.Screens; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Video; +using osu.Framework.MathUtils; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.IO.Archives; +using osu.Game.Rulesets; +using osu.Game.Screens.Backgrounds; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Menu +{ + public class IntroTriangles : IntroScreen + { + private const string menu_music_beatmap_hash = "a1556d0801b3a6b175dda32ef546f0ec812b400499f575c44fccbe9c67f9b1e5"; + + private SampleChannel welcome; + + protected override BackgroundScreen CreateBackground() => background = new BackgroundScreenDefault(false) + { + Alpha = 0, + }; + + [Resolved] + private AudioManager audio { get; set; } + + private Bindable menuMusic; + private Track track; + private WorkingBeatmap introBeatmap; + + private BackgroundScreenDefault background; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game) + { + menuMusic = config.GetBindable(OsuSetting.MenuMusic); + + BeatmapSetInfo setInfo = null; + + if (!menuMusic.Value) + { + var sets = beatmaps.GetAllUsableBeatmapSets(); + if (sets.Count > 0) + setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID); + } + + if (setInfo == null) + { + setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == menu_music_beatmap_hash); + + if (setInfo == null) + { + // we need to import the default menu background beatmap + setInfo = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream(@"Tracks/triangles.osz"), "triangles.osz")).Result; + + setInfo.Protected = true; + beatmaps.Update(setInfo); + } + } + + introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); + + track = introBeatmap.Track; + track.Reset(); + + if (config.Get(OsuSetting.MenuVoice) && !menuMusic.Value) + // triangles has welcome sound included in the track. only play this if the user doesn't want menu music. + welcome = audio.Samples.Get(@"welcome"); + } + + protected override void LogoArriving(OsuLogo logo, bool resuming) + { + base.LogoArriving(logo, resuming); + + logo.Triangles = true; + + if (!resuming) + { + Beatmap.Value = introBeatmap; + introBeatmap = null; + + PrepareMenuLoad(); + + LoadComponentAsync(new TrianglesIntroSequence(logo, background) + { + RelativeSizeAxes = Axes.Both, + Clock = new FramedClock(track), + LoadMenu = LoadMenu + }, t => + { + AddInternal(t); + welcome?.Play(); + + // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu. + if (menuMusic.Value) + track.Start(); + }); + } + } + + public override void OnResuming(IScreen last) + { + base.OnResuming(last); + background.FadeOut(100); + } + + public override void OnSuspending(IScreen next) + { + track = null; + base.OnSuspending(next); + } + + private class TrianglesIntroSequence : CompositeDrawable + { + private readonly OsuLogo logo; + private readonly BackgroundScreenDefault background; + private OsuSpriteText welcomeText; + + private RulesetFlow rulesets; + private Container rulesetsScale; + private Drawable logoContainerSecondary; + private Drawable logoContainer; + + private GlitchingTriangles triangles; + + public Action LoadMenu; + + public TrianglesIntroSequence(OsuLogo logo, BackgroundScreenDefault background) + { + this.logo = logo; + this.background = background; + } + + private OsuGameBase game; + + [BackgroundDependencyLoader] + private void load(TextureStore textures, OsuGameBase game) + { + this.game = game; + + InternalChildren = new[] + { + triangles = new GlitchingTriangles + { + Alpha = 0, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(0.4f, 0.16f) + }, + welcomeText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Padding = new MarginPadding { Bottom = 10 }, + Font = OsuFont.GetFont(weight: FontWeight.Light, size: 42), + Alpha = 1, + Spacing = new Vector2(5), + }, + rulesetsScale = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + rulesets = new RulesetFlow() + } + }, + logoContainerSecondary = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Child = logoContainer = new LazerLogo(textures.GetStream("Menu/logo-triangles.mp4")) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }, + }; + } + + private const double text_1 = 200; + private const double text_2 = 400; + private const double text_3 = 700; + private const double text_4 = 900; + private const double text_glitch = 1060; + + private const double rulesets_1 = 1450; + private const double rulesets_2 = 1650; + private const double rulesets_3 = 1850; + + private const double logo_scale_duration = 920; + private const double logo_1 = 2080; + private const double logo_2 = logo_1 + logo_scale_duration; + + protected override void LoadComplete() + { + base.LoadComplete(); + + const float scale_start = 1.2f; + const float scale_adjust = 0.8f; + + rulesets.Hide(); + logoContainer.Hide(); + background.Hide(); + + using (BeginAbsoluteSequence(0, true)) + { + using (BeginDelayedSequence(text_1, true)) + welcomeText.FadeIn().OnComplete(t => t.Text = "wel"); + + using (BeginDelayedSequence(text_2, true)) + welcomeText.FadeIn().OnComplete(t => t.Text = "welcome"); + + using (BeginDelayedSequence(text_3, true)) + welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to"); + + using (BeginDelayedSequence(text_4, true)) + { + welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to osu!"); + welcomeText.TransformTo(nameof(welcomeText.Spacing), new Vector2(50, 0), 5000); + } + + using (BeginDelayedSequence(text_glitch, true)) + triangles.FadeIn(); + + using (BeginDelayedSequence(rulesets_1, true)) + { + rulesetsScale.ScaleTo(0.8f, 1000); + rulesets.FadeIn().ScaleTo(1).TransformSpacingTo(new Vector2(200, 0)); + welcomeText.FadeOut(); + triangles.FadeOut(); + } + + using (BeginDelayedSequence(rulesets_2, true)) + { + rulesets.ScaleTo(2).TransformSpacingTo(new Vector2(30, 0)); + } + + using (BeginDelayedSequence(rulesets_3, true)) + { + rulesets.ScaleTo(4).TransformSpacingTo(new Vector2(10, 0)); + rulesetsScale.ScaleTo(1.3f, 1000); + } + + using (BeginDelayedSequence(logo_1, true)) + { + rulesets.FadeOut(); + + // matching flyte curve y = 0.25x^2 + (max(0, x - 0.7) / 0.3) ^ 5 + logoContainer.FadeIn().ScaleTo(scale_start).Then().Delay(logo_scale_duration * 0.7f).ScaleTo(scale_start - scale_adjust, logo_scale_duration * 0.3f, Easing.InQuint); + logoContainerSecondary.ScaleTo(scale_start).Then().ScaleTo(scale_start - scale_adjust * 0.25f, logo_scale_duration, Easing.InQuad); + } + + using (BeginDelayedSequence(logo_2, true)) + { + logoContainer.FadeOut().OnComplete(_ => + { + logo.FadeIn(); + background.FadeIn(); + + game.Add(new GameWideFlash()); + + LoadMenu(); + }); + } + } + } + + private class GameWideFlash : Box + { + private const double flash_length = 1000; + + public GameWideFlash() + { + Colour = Color4.White; + RelativeSizeAxes = Axes.Both; + Blending = BlendingMode.Additive; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + this.FadeOutFromOne(flash_length, Easing.Out); + } + } + + private class LazerLogo : CompositeDrawable + { + public LazerLogo(Stream videoStream) + { + Size = new Vector2(960); + + InternalChild = new VideoSprite(videoStream) + { + RelativeSizeAxes = Axes.Both, + Clock = new FramedOffsetClock(Clock) { Offset = -logo_1 } + }; + } + } + + private class RulesetFlow : FillFlowContainer + { + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + var modes = new List(); + + foreach (var ruleset in rulesets.AvailableRulesets) + { + var icon = new ConstrainedIconContainer + { + Icon = ruleset.CreateInstance().CreateIcon(), + Size = new Vector2(30), + }; + + modes.Add(icon); + } + + AutoSizeAxes = Axes.Both; + Children = modes; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + } + + private class GlitchingTriangles : CompositeDrawable + { + public GlitchingTriangles() + { + RelativeSizeAxes = Axes.Both; + } + + private double? lastGenTime; + + private const double time_between_triangles = 22; + + protected override void Update() + { + base.Update(); + + if (lastGenTime == null || Time.Current - lastGenTime > time_between_triangles) + { + lastGenTime = (lastGenTime ?? Time.Current) + time_between_triangles; + + Drawable triangle = new OutlineTriangle(RNG.NextBool(), (RNG.NextSingle() + 0.2f) * 80) + { + RelativePositionAxes = Axes.Both, + Position = new Vector2(RNG.NextSingle(), RNG.NextSingle()), + }; + + AddInternal(triangle); + + triangle.FadeOutFromOne(120); + } + } + + /// + /// Represents a sprite that is drawn in a triangle shape, instead of a rectangle shape. + /// + public class OutlineTriangle : BufferedContainer + { + public OutlineTriangle(bool outlineOnly, float size) + { + Size = new Vector2(size); + + InternalChildren = new Drawable[] + { + new Triangle { RelativeSizeAxes = Axes.Both }, + }; + + if (outlineOnly) + { + AddInternal(new Triangle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = Color4.Black, + Size = new Vector2(size - 5), + Blending = BlendingMode.None, + }); + } + + Blending = BlendingMode.Additive; + CacheDrawnFrameBuffer = true; + } + } + } + } + } +} From 8e6a3162ab3bfead6842379973fd671f927e39c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Aug 2019 20:05:36 +0900 Subject: [PATCH 2119/2854] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 40c15a1162..4ec67ab30d 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -62,7 +62,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8ee325c2ac..fb9269e5f0 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,7 +14,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index b46438f766..1532e2d71b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -104,7 +104,7 @@ - + From 6264a6a1c95b9b3c924e2ace2b4f8ff0594862f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Aug 2019 20:18:02 +0900 Subject: [PATCH 2120/2854] Adjust slider snaking and hitcircle fading to match stable --- .../Objects/Drawables/Pieces/SnakingSliderBody.cs | 2 +- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs index 0590ca1d96..70a1bad4a3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces var spanProgress = slider.ProgressAt(completionProgress); double start = 0; - double end = SnakingIn.Value ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1; + double end = SnakingIn.Value ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / (slider.TimePreempt / 3), 0, 1) : 1; if (span >= slider.SpanCount() - 1) { diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index d1221fd2d3..b52bfcd181 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Objects base.ApplyDefaultsToSelf(controlPointInfo, difficulty); TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450); - TimeFadeIn = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300); + TimeFadeIn = 400; // as per osu-stable Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2; } From 7dd62ae6f350211cb09bd7b58afc4adf2f83e80b Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Fri, 9 Aug 2019 20:48:08 +0930 Subject: [PATCH 2121/2854] Remove Android BASS libraries --- osu.Android.props | 4 +--- osu.Android/lib/arm64-v8a/libbass.so | Bin 308904 -> 0 bytes osu.Android/lib/arm64-v8a/libbass_fx.so | Bin 154408 -> 0 bytes osu.Android/lib/armeabi-v7a/libbass.so | Bin 222620 -> 0 bytes osu.Android/lib/armeabi-v7a/libbass_fx.so | Bin 92176 -> 0 bytes osu.Android/lib/x86/libbass.so | Bin 316716 -> 0 bytes osu.Android/lib/x86/libbass_fx.so | Bin 153532 -> 0 bytes 7 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 osu.Android/lib/arm64-v8a/libbass.so delete mode 100644 osu.Android/lib/arm64-v8a/libbass_fx.so delete mode 100644 osu.Android/lib/armeabi-v7a/libbass.so delete mode 100644 osu.Android/lib/armeabi-v7a/libbass_fx.so delete mode 100644 osu.Android/lib/x86/libbass.so delete mode 100644 osu.Android/lib/x86/libbass_fx.so diff --git a/osu.Android.props b/osu.Android.props index 40c15a1162..82d8696ca5 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -49,9 +49,7 @@ osu.licenseheader - - Always - + diff --git a/osu.Android/lib/arm64-v8a/libbass.so b/osu.Android/lib/arm64-v8a/libbass.so deleted file mode 100644 index d5c24b3e4b2918a33153cf6a0561366996be16f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308904 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

    ^m>m@_QI`Gyr3D`HGSXQzB#7TNkp#fsh! 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%0A0ic&Bx^ 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 T?MJo0F6IAiepbA(NM`E) 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 z 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 EAM_!vo&W#< diff --git a/osu.Android/lib/armeabi-v7a/libbass_fx.so b/osu.Android/lib/armeabi-v7a/libbass_fx.so deleted file mode 100644 index 006e2feb30bb005eec727f829cc3965fb136f16c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92176 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 V>7s+MhuN$Sd><=0>Ypgz{{j9{WcmOA diff --git a/osu.Android/lib/x86/libbass.so b/osu.Android/lib/x86/libbass.so deleted file mode 100644 index b0f758a42b7397b3289557f51de6b55df7b3b4ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 316716 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 z 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 kkiFcJ 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 E0opfv^#A|> From caf13ae52ddbf06fbf11fe3c711e1fb304f8915f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Aug 2019 23:29:32 +0900 Subject: [PATCH 2122/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 40c15a1162..3b2e6574ac 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8ee325c2ac..1dd19ac7ed 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index b46438f766..1c3faeed39 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From fc4c0cc2e72d9108881cba86f1854110ee17549e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 10 Aug 2019 10:52:37 +0200 Subject: [PATCH 2123/2854] Fix visual discrepancies with master --- osu.Game/Overlays/OSD/Toast.cs | 1 + osu.Game/Overlays/OSD/TrackedSettingToast.cs | 13 +++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/OSD/Toast.cs b/osu.Game/Overlays/OSD/Toast.cs index 67c9b46c77..db5e6e4a6a 100644 --- a/osu.Game/Overlays/OSD/Toast.cs +++ b/osu.Game/Overlays/OSD/Toast.cs @@ -65,6 +65,7 @@ namespace osu.Game.Overlays.OSD Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Name = "Shortcut", + Alpha = 0.3f, Margin = new MarginPadding { Bottom = 15 }, Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), Text = string.IsNullOrEmpty(keybinding) ? "NO KEY BOUND" : keybinding.ToUpperInvariant() diff --git a/osu.Game/Overlays/OSD/TrackedSettingToast.cs b/osu.Game/Overlays/OSD/TrackedSettingToast.cs index 9812dcd797..0f4bd34779 100644 --- a/osu.Game/Overlays/OSD/TrackedSettingToast.cs +++ b/osu.Game/Overlays/OSD/TrackedSettingToast.cs @@ -17,6 +17,8 @@ namespace osu.Game.Overlays.OSD { public class TrackedSettingToast : Toast { + private const int lights_bottom_margin = 40; + public TrackedSettingToast(SettingDescription description) : base(description.Name, description.Value, description.Shortcut) { @@ -24,17 +26,16 @@ namespace osu.Game.Overlays.OSD Children = new Drawable[] { - new FillFlowContainer + new Container { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Direction = FillDirection.Vertical, - Margin = new MarginPadding { Top = 70 }, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Margin = new MarginPadding { Bottom = lights_bottom_margin }, Children = new Drawable[] { optionLights = new FillFlowContainer { - Padding = new MarginPadding { Bottom = 5 }, + Margin = new MarginPadding { Bottom = 5 }, Spacing = new Vector2(5, 0), Direction = FillDirection.Horizontal, Anchor = Anchor.TopCentre, From b0a71779877c55e6b181e8e50207de80ff79e96a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 10 Aug 2019 13:26:19 +0200 Subject: [PATCH 2124/2854] Add ScreenTitleIcon class, used for displaying custom specific icons on overlays. --- .../Graphics/UserInterface/ScreenTitle.cs | 2 +- .../Graphics/UserInterface/ScreenTitleIcon.cs | 61 +++++++++++++++++++ .../Overlays/Changelog/ChangelogHeader.cs | 45 +------------- 3 files changed, 63 insertions(+), 45 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs diff --git a/osu.Game/Graphics/UserInterface/ScreenTitle.cs b/osu.Game/Graphics/UserInterface/ScreenTitle.cs index 7b39238e5e..10fc312d8b 100644 --- a/osu.Game/Graphics/UserInterface/ScreenTitle.cs +++ b/osu.Game/Graphics/UserInterface/ScreenTitle.cs @@ -15,7 +15,7 @@ namespace osu.Game.Graphics.UserInterface { public const float ICON_WIDTH = ICON_SIZE + icon_spacing; - protected const float ICON_SIZE = 25; + public const float ICON_SIZE = 25; private SpriteIcon iconSprite; private readonly OsuSpriteText titleText, pageText; diff --git a/osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs b/osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs new file mode 100644 index 0000000000..12e0617e35 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs @@ -0,0 +1,61 @@ +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osuTK; + +namespace osu.Game.Graphics.UserInterface +{ + ///

    + /// A custom icon class for use with + /// + public class ScreenTitleIcon : CompositeDrawable + { + private const float circle_allowance = 0.8f; + + private string icon; + + public ScreenTitleIcon(string icon) + { + this.icon = icon; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures, OsuColour colours) + { + Size = new Vector2(ScreenTitle.ICON_SIZE / circle_allowance); + + InternalChildren = new Drawable[] + { + new CircularContainer + { + Masking = true, + BorderColour = colours.Violet, + BorderThickness = 3, + MaskingSmoothness = 1, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Sprite + { + RelativeSizeAxes = Axes.Both, + Texture = textures.Get(icon), + Size = new Vector2(circle_allowance), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Violet, + Alpha = 0, + AlwaysPresent = true, + }, + } + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index fca62fbb44..8b78216136 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -7,13 +7,11 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; -using osuTK; namespace osu.Game.Overlays.Changelog { @@ -123,48 +121,7 @@ namespace osu.Game.Overlays.Changelog AccentColour = colours.Violet; } - protected override Drawable CreateIcon() => new ChangelogIcon(); - - internal class ChangelogIcon : CompositeDrawable - { - private const float circle_allowance = 0.8f; - - [BackgroundDependencyLoader] - private void load(TextureStore textures, OsuColour colours) - { - Size = new Vector2(ICON_SIZE / circle_allowance); - - InternalChildren = new Drawable[] - { - new CircularContainer - { - Masking = true, - BorderColour = colours.Violet, - BorderThickness = 3, - MaskingSmoothness = 1, - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Sprite - { - RelativeSizeAxes = Axes.Both, - Texture = textures.Get(@"Icons/changelog"), - Size = new Vector2(circle_allowance), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Violet, - Alpha = 0, - AlwaysPresent = true, - }, - } - }, - }; - } - } + protected override Drawable CreateIcon() => new ScreenTitleIcon(@"Icons/changelog"); } } } From 8eb3409a648bb03ff702d2f66807e6c0e0c02c86 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 10 Aug 2019 14:03:44 +0200 Subject: [PATCH 2125/2854] Add missing licence header --- osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs b/osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs index 12e0617e35..8a42645b37 100644 --- a/osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs +++ b/osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs @@ -1,4 +1,7 @@ -using osu.Framework.Allocation; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; From d55f9404dab5cd639d8f3524df9e97c31bf909f3 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 10 Aug 2019 14:23:37 +0200 Subject: [PATCH 2126/2854] Fix CI issues --- osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs b/osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs index 8a42645b37..4ce554009a 100644 --- a/osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs +++ b/osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs @@ -18,7 +18,7 @@ namespace osu.Game.Graphics.UserInterface { private const float circle_allowance = 0.8f; - private string icon; + private readonly string icon; public ScreenTitleIcon(string icon) { From 7825923cb63f157e7e9fcd7307ae05b9d3d0b51d Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 10 Aug 2019 16:33:16 +0200 Subject: [PATCH 2127/2854] Use media keys as default bindings for jukebox. --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 6 +++--- osu.Game/Overlays/MusicController.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 8c927c2bc6..809ec9a09e 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -45,9 +45,9 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.Enter, GlobalAction.Select), new KeyBinding(InputKey.KeypadEnter, GlobalAction.Select), - new KeyBinding(InputKey.F1, GlobalAction.MusicPrev), - new KeyBinding(InputKey.F5, GlobalAction.MusicNext), - new KeyBinding(InputKey.F3, GlobalAction.MusicPlay), + new KeyBinding(InputKey.TrackPrevious, GlobalAction.MusicPrev), + new KeyBinding(InputKey.TrackNext, GlobalAction.MusicNext), + new KeyBinding(InputKey.PlayPause, GlobalAction.MusicPlay), }; public IEnumerable InGameKeyBindings => new[] diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 724be21957..ed51a80924 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -565,7 +565,7 @@ namespace osu.Game.Overlays { if (beatmap.Disabled) return false; - play(); + togglePause(); return true; } From 684c37bf05e8bc3367d5c48d4997a7e1bab75157 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Aug 2019 16:15:44 +0900 Subject: [PATCH 2128/2854] Rename class to better match usage --- ...{ScreenTitleIcon.cs => ScreenTitleTextureIcon.cs} | 12 ++++++------ osu.Game/Overlays/Changelog/ChangelogHeader.cs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) rename osu.Game/Graphics/UserInterface/{ScreenTitleIcon.cs => ScreenTitleTextureIcon.cs} (85%) diff --git a/osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs b/osu.Game/Graphics/UserInterface/ScreenTitleTextureIcon.cs similarity index 85% rename from osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs rename to osu.Game/Graphics/UserInterface/ScreenTitleTextureIcon.cs index 4ce554009a..f590e7e357 100644 --- a/osu.Game/Graphics/UserInterface/ScreenTitleIcon.cs +++ b/osu.Game/Graphics/UserInterface/ScreenTitleTextureIcon.cs @@ -12,17 +12,17 @@ using osuTK; namespace osu.Game.Graphics.UserInterface { /// - /// A custom icon class for use with + /// A custom icon class for use with based off a texture resource. /// - public class ScreenTitleIcon : CompositeDrawable + public class ScreenTitleTextureIcon : CompositeDrawable { private const float circle_allowance = 0.8f; - private readonly string icon; + private readonly string textureName; - public ScreenTitleIcon(string icon) + public ScreenTitleTextureIcon(string textureName) { - this.icon = icon; + this.textureName = textureName; } [BackgroundDependencyLoader] @@ -44,7 +44,7 @@ namespace osu.Game.Graphics.UserInterface new Sprite { RelativeSizeAxes = Axes.Both, - Texture = textures.Get(icon), + Texture = textures.Get(textureName), Size = new Vector2(circle_allowance), Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 8b78216136..b2e9be24b3 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -121,7 +121,7 @@ namespace osu.Game.Overlays.Changelog AccentColour = colours.Violet; } - protected override Drawable CreateIcon() => new ScreenTitleIcon(@"Icons/changelog"); + protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog"); } } } From fd334e0319729576afb81d600b71128f1fff2970 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 11 Aug 2019 14:57:21 +0300 Subject: [PATCH 2129/2854] Implement basic layout for AccuracyBar --- osu.Game/Screens/Play/HUD/AccuracyBar.cs | 19 +++++++++++++++++++ osu.Game/Screens/Play/HUDOverlay.cs | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 osu.Game/Screens/Play/HUD/AccuracyBar.cs diff --git a/osu.Game/Screens/Play/HUD/AccuracyBar.cs b/osu.Game/Screens/Play/HUD/AccuracyBar.cs new file mode 100644 index 0000000000..8b85014b2f --- /dev/null +++ b/osu.Game/Screens/Play/HUD/AccuracyBar.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Judgements; + +namespace osu.Game.Screens.Play.HUD +{ + public class AccuracyBar : Container + { + public AccuracyBar(bool mirrored) + { + } + + public void OnNewJudgement(JudgementResult judgement) + { + } + } +} diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 43b9491750..88ad57c175 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -35,6 +35,8 @@ namespace osu.Game.Screens.Play public readonly ModDisplay ModDisplay; public readonly HoldForMenuButton HoldToQuit; public readonly PlayerSettingsOverlay PlayerSettingsOverlay; + public readonly AccuracyBar LeftAccuracyBar; + public readonly AccuracyBar RightAccuracyBar; public Bindable ShowHealthbar = new Bindable(true); @@ -84,6 +86,8 @@ namespace osu.Game.Screens.Play HealthDisplay = CreateHealthDisplay(), Progress = CreateProgress(), ModDisplay = CreateModsContainer(), + LeftAccuracyBar = CreateAccuracyBar(false), + RightAccuracyBar = CreateAccuracyBar(), } }, PlayerSettingsOverlay = CreatePlayerSettingsOverlay(), @@ -256,6 +260,15 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20, Right = 10 }, }; + protected virtual AccuracyBar CreateAccuracyBar(bool mirrored = true) => new AccuracyBar(mirrored) + { + Anchor = mirrored ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = mirrored ? Anchor.CentreRight : Anchor.CentreLeft, + AutoSizeAxes = Axes.X, + Height = 300, + Margin = new MarginPadding { Horizontal = 20 } + }; + protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); protected virtual void BindProcessor(ScoreProcessor processor) @@ -265,6 +278,12 @@ namespace osu.Game.Screens.Play ComboCounter?.Current.BindTo(processor.Combo); HealthDisplay?.Current.BindTo(processor.Health); + if (LeftAccuracyBar != null) + processor.NewJudgement += LeftAccuracyBar.OnNewJudgement; + + if (RightAccuracyBar != null) + processor.NewJudgement += RightAccuracyBar.OnNewJudgement; + if (HealthDisplay is StandardHealthDisplay shd) processor.NewJudgement += shd.Flash; } From ed409d113b2e02bc49c252c4074468393bc6a8ae Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 11 Aug 2019 15:53:15 +0300 Subject: [PATCH 2130/2854] Add judgement lines generator --- osu.Game/Screens/Play/HUD/AccuracyBar.cs | 42 +++++++++++++++++++++++- osu.Game/Screens/Play/HUDOverlay.cs | 4 +-- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/AccuracyBar.cs b/osu.Game/Screens/Play/HUD/AccuracyBar.cs index 8b85014b2f..8b14bc47eb 100644 --- a/osu.Game/Screens/Play/HUD/AccuracyBar.cs +++ b/osu.Game/Screens/Play/HUD/AccuracyBar.cs @@ -1,19 +1,59 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Judgements; +using osuTK.Graphics; +using osuTK; namespace osu.Game.Screens.Play.HUD { public class AccuracyBar : Container { - public AccuracyBar(bool mirrored) + private const int bar_width = 5; + private const int bar_height = 250; + private const int spacing = 3; + + private readonly bool mirrored; + + public AccuracyBar(bool mirrored = false) { + this.mirrored = mirrored; + + Size = new Vector2(bar_width, bar_height); + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }; } public void OnNewJudgement(JudgementResult judgement) { + Container judgementLine; + + Add(judgementLine = CreateJudgementLine(judgement.TimeOffset)); + + judgementLine.FadeOut(5000, Easing.OutQuint); + judgementLine.Expire(); } + + protected virtual Container CreateJudgementLine(double offset) => new CircularContainer + { + Anchor = mirrored ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = mirrored ? Anchor.CentreLeft : Anchor.CentreRight, + Masking = true, + Size = new Vector2(10, 2), + RelativePositionAxes = Axes.Y, + Y = (float)offset / bar_height, + X = mirrored ? spacing : -spacing, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + } + }; } } diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 88ad57c175..3fafc21ea8 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -264,9 +264,7 @@ namespace osu.Game.Screens.Play { Anchor = mirrored ? Anchor.CentreRight : Anchor.CentreLeft, Origin = mirrored ? Anchor.CentreRight : Anchor.CentreLeft, - AutoSizeAxes = Axes.X, - Height = 300, - Margin = new MarginPadding { Horizontal = 20 } + Margin = new MarginPadding { Horizontal = 30 } }; protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); From 0a255fe4d1750440e02fc429f40021f7df9b55c8 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 11 Aug 2019 16:38:03 +0300 Subject: [PATCH 2131/2854] Add moving arrow --- osu.Game/Screens/Play/HUD/AccuracyBar.cs | 46 ++++++++++++++++++++---- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/AccuracyBar.cs b/osu.Game/Screens/Play/HUD/AccuracyBar.cs index 8b14bc47eb..3c13111b22 100644 --- a/osu.Game/Screens/Play/HUD/AccuracyBar.cs +++ b/osu.Game/Screens/Play/HUD/AccuracyBar.cs @@ -7,6 +7,8 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Judgements; using osuTK.Graphics; using osuTK; +using osu.Framework.Graphics.Sprites; +using System.Collections.Generic; namespace osu.Game.Screens.Play.HUD { @@ -17,16 +19,31 @@ namespace osu.Game.Screens.Play.HUD private const int spacing = 3; private readonly bool mirrored; + private readonly SpriteIcon arrow; + private readonly List judgementOffsets = new List(); public AccuracyBar(bool mirrored = false) { this.mirrored = mirrored; Size = new Vector2(bar_width, bar_height); - Child = new Box + + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + arrow = new SpriteIcon + { + Anchor = mirrored ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = mirrored ? Anchor.CentreRight : Anchor.CentreLeft, + X = mirrored ? -spacing : spacing, + RelativePositionAxes = Axes.Y, + Icon = mirrored ? FontAwesome.Solid.ChevronRight : FontAwesome.Solid.ChevronLeft, + Size = new Vector2(10), + } }; } @@ -34,20 +51,22 @@ namespace osu.Game.Screens.Play.HUD { Container judgementLine; - Add(judgementLine = CreateJudgementLine(judgement.TimeOffset)); + Add(judgementLine = CreateJudgementLine(judgement)); judgementLine.FadeOut(5000, Easing.OutQuint); judgementLine.Expire(); + + arrow.MoveToY(calculateArrowPosition(judgement) / bar_height, 500, Easing.OutQuint); } - protected virtual Container CreateJudgementLine(double offset) => new CircularContainer + protected virtual Container CreateJudgementLine(JudgementResult judgement) => new CircularContainer { Anchor = mirrored ? Anchor.CentreRight : Anchor.CentreLeft, Origin = mirrored ? Anchor.CentreLeft : Anchor.CentreRight, Masking = true, Size = new Vector2(10, 2), RelativePositionAxes = Axes.Y, - Y = (float)offset / bar_height, + Y = (float)judgement.TimeOffset / bar_height, X = mirrored ? spacing : -spacing, Child = new Box { @@ -55,5 +74,20 @@ namespace osu.Game.Screens.Play.HUD Colour = Color4.White, } }; + + private float calculateArrowPosition(JudgementResult judgement) + { + if (judgementOffsets.Count > 5) + judgementOffsets.RemoveAt(0); + + judgementOffsets.Add(judgement.TimeOffset); + + double offsets = 0; + + foreach (var offset in judgementOffsets) + offsets += offset; + + return (float)offsets / judgementOffsets.Count; + } } } From cc4ee2df0591301b420675255a28e0612862654f Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 11 Aug 2019 16:41:56 +0200 Subject: [PATCH 2132/2854] add ToString() override to Beatmap class --- osu.Game/Beatmaps/Beatmap.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 4ebeee40bf..6079a112e6 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -60,5 +60,7 @@ namespace osu.Game.Beatmaps public class Beatmap : Beatmap { public new Beatmap Clone() => (Beatmap)base.Clone(); + + public override string ToString() => BeatmapInfo?.ToString(); } } From 2a35c3c3e24708ef174f5482250d4de773ccb6d9 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 11 Aug 2019 18:04:54 +0300 Subject: [PATCH 2133/2854] Calculate real position for judgement lines --- osu.Game/Screens/Play/HUD/AccuracyBar.cs | 25 ++++++++++++++++++++---- osu.Game/Screens/Play/HUDOverlay.cs | 6 ++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/AccuracyBar.cs b/osu.Game/Screens/Play/HUD/AccuracyBar.cs index 3c13111b22..590e9ca4d9 100644 --- a/osu.Game/Screens/Play/HUD/AccuracyBar.cs +++ b/osu.Game/Screens/Play/HUD/AccuracyBar.cs @@ -9,6 +9,10 @@ using osuTK.Graphics; using osuTK; using osu.Framework.Graphics.Sprites; using System.Collections.Generic; +using osu.Game.Rulesets.Objects; +using osu.Game.Beatmaps; +using osu.Framework.Bindables; +using osu.Framework.Allocation; namespace osu.Game.Screens.Play.HUD { @@ -18,6 +22,11 @@ namespace osu.Game.Screens.Play.HUD private const int bar_height = 250; private const int spacing = 3; + public HitWindows HitWindows { get; set; } + + [Resolved] + private Bindable beatmap { get; set; } + private readonly bool mirrored; private readonly SpriteIcon arrow; private readonly List judgementOffsets = new List(); @@ -47,6 +56,12 @@ namespace osu.Game.Screens.Play.HUD }; } + protected override void LoadComplete() + { + base.LoadComplete(); + HitWindows.SetDifficulty(beatmap.Value.BeatmapInfo.BaseDifficulty.OverallDifficulty); + } + public void OnNewJudgement(JudgementResult judgement) { Container judgementLine; @@ -56,7 +71,7 @@ namespace osu.Game.Screens.Play.HUD judgementLine.FadeOut(5000, Easing.OutQuint); judgementLine.Expire(); - arrow.MoveToY(calculateArrowPosition(judgement) / bar_height, 500, Easing.OutQuint); + arrow.MoveToY(getRelativeJudgementPosition(calculateArrowPosition(judgement)), 500, Easing.OutQuint); } protected virtual Container CreateJudgementLine(JudgementResult judgement) => new CircularContainer @@ -66,7 +81,7 @@ namespace osu.Game.Screens.Play.HUD Masking = true, Size = new Vector2(10, 2), RelativePositionAxes = Axes.Y, - Y = (float)judgement.TimeOffset / bar_height, + Y = getRelativeJudgementPosition(judgement.TimeOffset), X = mirrored ? spacing : -spacing, Child = new Box { @@ -75,7 +90,9 @@ namespace osu.Game.Screens.Play.HUD } }; - private float calculateArrowPosition(JudgementResult judgement) + private float getRelativeJudgementPosition(double value) => (float)(value / HitWindows.Miss); + + private double calculateArrowPosition(JudgementResult judgement) { if (judgementOffsets.Count > 5) judgementOffsets.RemoveAt(0); @@ -87,7 +104,7 @@ namespace osu.Game.Screens.Play.HUD foreach (var offset in judgementOffsets) offsets += offset; - return (float)offsets / judgementOffsets.Count; + return offsets / judgementOffsets.Count; } } } diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 3fafc21ea8..d30a32343a 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -277,10 +277,16 @@ namespace osu.Game.Screens.Play HealthDisplay?.Current.BindTo(processor.Health); if (LeftAccuracyBar != null) + { processor.NewJudgement += LeftAccuracyBar.OnNewJudgement; + LeftAccuracyBar.HitWindows = processor.CreateHitWindows(); + } if (RightAccuracyBar != null) + { processor.NewJudgement += RightAccuracyBar.OnNewJudgement; + RightAccuracyBar.HitWindows = processor.CreateHitWindows(); + } if (HealthDisplay is StandardHealthDisplay shd) processor.NewJudgement += shd.Flash; From 177a317a48b34d73643b946a90db01fe245e24c9 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 11 Aug 2019 18:11:49 +0300 Subject: [PATCH 2134/2854] rename AccuracyBar to HitErrorDisplay --- .../{AccuracyBar.cs => HitErrorDisplay.cs} | 4 ++-- osu.Game/Screens/Play/HUDOverlay.cs | 22 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) rename osu.Game/Screens/Play/HUD/{AccuracyBar.cs => HitErrorDisplay.cs} (97%) diff --git a/osu.Game/Screens/Play/HUD/AccuracyBar.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs similarity index 97% rename from osu.Game/Screens/Play/HUD/AccuracyBar.cs rename to osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index 590e9ca4d9..2d33cb08a0 100644 --- a/osu.Game/Screens/Play/HUD/AccuracyBar.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -16,7 +16,7 @@ using osu.Framework.Allocation; namespace osu.Game.Screens.Play.HUD { - public class AccuracyBar : Container + public class HitErrorDisplay : Container { private const int bar_width = 5; private const int bar_height = 250; @@ -31,7 +31,7 @@ namespace osu.Game.Screens.Play.HUD private readonly SpriteIcon arrow; private readonly List judgementOffsets = new List(); - public AccuracyBar(bool mirrored = false) + public HitErrorDisplay(bool mirrored = false) { this.mirrored = mirrored; diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index d30a32343a..a9a469486e 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -35,8 +35,8 @@ namespace osu.Game.Screens.Play public readonly ModDisplay ModDisplay; public readonly HoldForMenuButton HoldToQuit; public readonly PlayerSettingsOverlay PlayerSettingsOverlay; - public readonly AccuracyBar LeftAccuracyBar; - public readonly AccuracyBar RightAccuracyBar; + public readonly HitErrorDisplay LeftHitErrorDisplay; + public readonly HitErrorDisplay RightHitErrorDisplay; public Bindable ShowHealthbar = new Bindable(true); @@ -86,8 +86,8 @@ namespace osu.Game.Screens.Play HealthDisplay = CreateHealthDisplay(), Progress = CreateProgress(), ModDisplay = CreateModsContainer(), - LeftAccuracyBar = CreateAccuracyBar(false), - RightAccuracyBar = CreateAccuracyBar(), + LeftHitErrorDisplay = CreateAccuracyBar(false), + RightHitErrorDisplay = CreateAccuracyBar(), } }, PlayerSettingsOverlay = CreatePlayerSettingsOverlay(), @@ -260,7 +260,7 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20, Right = 10 }, }; - protected virtual AccuracyBar CreateAccuracyBar(bool mirrored = true) => new AccuracyBar(mirrored) + protected virtual HitErrorDisplay CreateAccuracyBar(bool mirrored = true) => new HitErrorDisplay(mirrored) { Anchor = mirrored ? Anchor.CentreRight : Anchor.CentreLeft, Origin = mirrored ? Anchor.CentreRight : Anchor.CentreLeft, @@ -276,16 +276,16 @@ namespace osu.Game.Screens.Play ComboCounter?.Current.BindTo(processor.Combo); HealthDisplay?.Current.BindTo(processor.Health); - if (LeftAccuracyBar != null) + if (LeftHitErrorDisplay != null) { - processor.NewJudgement += LeftAccuracyBar.OnNewJudgement; - LeftAccuracyBar.HitWindows = processor.CreateHitWindows(); + processor.NewJudgement += LeftHitErrorDisplay.OnNewJudgement; + LeftHitErrorDisplay.HitWindows = processor.CreateHitWindows(); } - if (RightAccuracyBar != null) + if (RightHitErrorDisplay != null) { - processor.NewJudgement += RightAccuracyBar.OnNewJudgement; - RightAccuracyBar.HitWindows = processor.CreateHitWindows(); + processor.NewJudgement += RightHitErrorDisplay.OnNewJudgement; + RightHitErrorDisplay.HitWindows = processor.CreateHitWindows(); } if (HealthDisplay is StandardHealthDisplay shd) From 5e0ac28ca88cecd41586c162d5d3b6862b11cfdd Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 11 Aug 2019 18:30:03 +0300 Subject: [PATCH 2135/2854] Add basic colours --- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 41 ++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index 2d33cb08a0..dba32d4ef8 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -13,12 +13,14 @@ using osu.Game.Rulesets.Objects; using osu.Game.Beatmaps; using osu.Framework.Bindables; using osu.Framework.Allocation; +using osu.Framework.Graphics.Colour; +using osu.Framework.Extensions.Color4Extensions; namespace osu.Game.Screens.Play.HUD { public class HitErrorDisplay : Container { - private const int bar_width = 5; + private const int bar_width = 4; private const int bar_height = 250; private const int spacing = 3; @@ -39,10 +41,43 @@ namespace osu.Game.Screens.Play.HUD Children = new Drawable[] { - new Box + new FillFlowContainer { RelativeSizeAxes = Axes.Both, - Colour = Color4.White, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0), Color4.Orange), + Height = 0.3f + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Green, + Height = 0.15f + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Blue, + Height = 0.1f + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Green, + Height = 0.15f + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.Orange, Color4.Black.Opacity(0)), + Height = 0.3f + } + } }, arrow = new SpriteIcon { From 3136d46c7f366c169413f6d3f64f135dbdb98ff2 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 11 Aug 2019 19:04:56 +0300 Subject: [PATCH 2136/2854] Do not generate new judgement line on miss --- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index dba32d4ef8..874a2cc088 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -99,6 +99,9 @@ namespace osu.Game.Screens.Play.HUD public void OnNewJudgement(JudgementResult judgement) { + if (!judgement.IsHit) + return; + Container judgementLine; Add(judgementLine = CreateJudgementLine(judgement)); From e7964c165f3c2c206dc404d23c7f3e6bcb4f5acd Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 11 Aug 2019 19:09:50 +0300 Subject: [PATCH 2137/2854] Make judgement lines alive for a bit longer --- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index 874a2cc088..4cac73c975 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -106,7 +106,7 @@ namespace osu.Game.Screens.Play.HUD Add(judgementLine = CreateJudgementLine(judgement)); - judgementLine.FadeOut(5000, Easing.OutQuint); + judgementLine.FadeOut(10000, Easing.OutQuint); judgementLine.Expire(); arrow.MoveToY(getRelativeJudgementPosition(calculateArrowPosition(judgement)), 500, Easing.OutQuint); From ce62f6b56e76e13f7ca17891d37453514e3eb3e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Aug 2019 01:40:11 +0900 Subject: [PATCH 2138/2854] ToString should never return null --- osu.Game/Beatmaps/Beatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 6079a112e6..a09a1bb9cb 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -61,6 +61,6 @@ namespace osu.Game.Beatmaps { public new Beatmap Clone() => (Beatmap)base.Clone(); - public override string ToString() => BeatmapInfo?.ToString(); + public override string ToString() => BeatmapInfo?.ToString() ?? base.ToString(); } } From ff4b271f64c508227f8d70c4301046afad16f2e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Aug 2019 01:42:05 +0900 Subject: [PATCH 2139/2854] Add extra quotations around output --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index d6d6804d16..9a8197ad82 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -36,7 +36,7 @@ namespace osu.Game.Beatmaps.Formats { if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) { - Logger.Log($"Unknown section \"{line}\" in {output}"); + Logger.Log($"Unknown section \"{line}\" in \"{output}\""); section = Section.None; } @@ -49,7 +49,7 @@ namespace osu.Game.Beatmaps.Formats } catch (Exception e) { - Logger.Log($"Failed to process line \"{line}\" into {output}: {e.Message}", LoggingTarget.Runtime, LogLevel.Important); + Logger.Log($"Failed to process line \"{line}\" into \"{output}\": {e.Message}", LoggingTarget.Runtime, LogLevel.Important); } } } From 359261d4a47353221ac5ca15f86b71f7253a8087 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Aug 2019 02:04:06 +0900 Subject: [PATCH 2140/2854] Fix game not starting if intro music is disabled --- osu.Game/Screens/Menu/IntroTriangles.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index 87d6012205..ba0d624959 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -104,14 +104,14 @@ namespace osu.Game.Screens.Menu LoadComponentAsync(new TrianglesIntroSequence(logo, background) { RelativeSizeAxes = Axes.Both, - Clock = new FramedClock(track), + Clock = new FramedClock(menuMusic.Value ? track : null), LoadMenu = LoadMenu }, t => { AddInternal(t); welcome?.Play(); - // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu. + // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu. if (menuMusic.Value) track.Start(); }); From e4eed83d85bec2274d40c14d4965cde6f3743def Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 11 Aug 2019 19:14:49 +0200 Subject: [PATCH 2141/2854] Add dual bindings for Jukebox hotkeys --- .../Input/Bindings/GlobalActionContainer.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 809ec9a09e..b2cbb77087 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -46,8 +46,11 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.KeypadEnter, GlobalAction.Select), new KeyBinding(InputKey.TrackPrevious, GlobalAction.MusicPrev), + new KeyBinding(InputKey.F1, GlobalAction.MusicPrev), new KeyBinding(InputKey.TrackNext, GlobalAction.MusicNext), + new KeyBinding(InputKey.F5, GlobalAction.MusicNext), new KeyBinding(InputKey.PlayPause, GlobalAction.MusicPlay), + new KeyBinding(InputKey.F3, GlobalAction.MusicPlay) }; public IEnumerable InGameKeyBindings => new[] @@ -92,16 +95,6 @@ namespace osu.Game.Input.Bindings [Description("Toggle mute")] ToggleMute, - // Game-wide beatmap jukebox keybindings - [Description("Jukebox next track")] - MusicNext, - - [Description("Jukebox previous track")] - MusicPrev, - - [Description("Jukebox play / pause current track")] - MusicPlay, - // In-Game Keybindings [Description("Skip cutscene")] SkipCutscene, @@ -129,5 +122,15 @@ namespace osu.Game.Input.Bindings [Description("Quick exit (Hold)")] QuickExit, + + // Game-wide beatmap jukebox keybindings + [Description("Jukebox next track")] + MusicNext, + + [Description("Jukebox previous track")] + MusicPrev, + + [Description("Jukebox play / pause current track")] + MusicPlay, } } From bc32726f3caee2c95c3111095f4b089e0326fc64 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Aug 2019 23:08:14 +0300 Subject: [PATCH 2142/2854] Apply renaming suggestions Co-Authored-By: Dean Herbert --- osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index bffee7f1f7..813ca904ca 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -149,7 +149,7 @@ namespace osu.Game.Tests.Visual.Background } /// - /// Check if the is properly accepting user-defined visual changes in background at all. + /// Ensure is properly accepting user-defined visual changes for a background. /// [Test] public void DisableUserDimBackgroundTest() @@ -166,7 +166,7 @@ namespace osu.Game.Tests.Visual.Background } /// - /// Check if the is properly accepting user-defined visual changes in storyboard at all. + /// Ensure is properly accepting user-defined visual changes for a storyboard. /// [Test] public void DisableUserDimStoryboardTest() From fe20e1924352fcfea0918e14f55b0c43b6038ffd Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 11 Aug 2019 23:19:22 +0300 Subject: [PATCH 2143/2854] Rename toggling steps --- .../Visual/Background/TestSceneUserDimContainer.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index 813ca904ca..3061a3a542 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -119,14 +119,14 @@ namespace osu.Game.Tests.Visual.Background { performFullSetup(); createFakeStoryboard(); - AddStep("Storyboard Enabled", () => + AddStep("Enable Storyboard", () => { player.ReplacesBackground.Value = true; player.StoryboardEnabled.Value = true; }); waitForDim(); AddAssert("Background is invisible, storyboard is visible", () => songSelect.IsBackgroundInvisible() && player.IsStoryboardVisible); - AddStep("Storyboard Disabled", () => + AddStep("Disable Storyboard", () => { player.ReplacesBackground.Value = false; player.StoryboardEnabled.Value = false; @@ -157,10 +157,10 @@ namespace osu.Game.Tests.Visual.Background performFullSetup(); waitForDim(); AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); - AddStep("EnableUserDim disabled", () => songSelect.DimEnabled.Value = false); + AddStep("Enable user dim", () => songSelect.DimEnabled.Value = false); waitForDim(); AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled()); - AddStep("EnableUserDim enabled", () => songSelect.DimEnabled.Value = true); + AddStep("Disable user dim", () => songSelect.DimEnabled.Value = true); waitForDim(); AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); } @@ -173,16 +173,16 @@ namespace osu.Game.Tests.Visual.Background { performFullSetup(); createFakeStoryboard(); - AddStep("Storyboard Enabled", () => + AddStep("Enable Storyboard", () => { player.ReplacesBackground.Value = true; player.StoryboardEnabled.Value = true; }); - AddStep("EnableUserDim enabled", () => player.DimmableStoryboard.EnableUserDim.Value = true); + AddStep("Enable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = true); AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f); waitForDim(); AddAssert("Storyboard is invisible", () => !player.IsStoryboardVisible); - AddStep("EnableUserDim disabled", () => player.DimmableStoryboard.EnableUserDim.Value = false); + AddStep("Disable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = false); waitForDim(); AddAssert("Storyboard is visible", () => player.IsStoryboardVisible); } From 45b4fc9201849483d0d08ec770c6c5b89d413d6a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Aug 2019 15:00:32 +0900 Subject: [PATCH 2144/2854] Add xmldoc --- osu.Game.Rulesets.Osu/OsuInputManager.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index 58e275ba26..cdea7276f3 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -18,6 +18,10 @@ namespace osu.Game.Rulesets.Osu set => ((OsuKeyBindingContainer)KeyBindingContainer).AllowUserPresses = value; } + /// + /// Whether the user's cursor movement events should be accepted. + /// Can be used to block only movement while still accepting button input. + /// public bool AllowUserCursorMovement { get; set; } = true; protected override RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) From 707911acac196e5c97ce6d6f5b95a0dbeb3dcdbc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Aug 2019 15:05:27 +0900 Subject: [PATCH 2145/2854] Tidy up code formatting / variable naming --- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index 1853b0228f..ca72f18e9c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -29,19 +29,21 @@ namespace osu.Game.Rulesets.Osu.Mods private OsuInputManager inputManager; private List replayFrames; - private int frameIndex; + + private int currentFrame; public void Update(Playfield playfield) { - // If we are on the last replay frame, no need to do anything - if (frameIndex == replayFrames.Count - 1) return; + if (currentFrame == replayFrames.Count - 1) return; - // Check if we are closer to the next replay frame then the current one - if (Math.Abs(replayFrames[frameIndex].Time - playfield.Time.Current) >= Math.Abs(replayFrames[frameIndex + 1].Time - playfield.Time.Current)) + double time = playfield.Time.Current; + + // Very naive implementation of autopilot based on proximity to replay frames. + // TODO: this needs to be based on user interactions to better match stable (pausing until judgement is registered). + if (Math.Abs(replayFrames[currentFrame + 1].Time - time) <= Math.Abs(replayFrames[currentFrame].Time - time)) { - // If we are, move to the next frame, and update the mouse position - frameIndex++; - new MousePositionAbsoluteInput { Position = playfield.ToScreenSpace(replayFrames[frameIndex].Position) }.Apply(inputManager.CurrentState, inputManager); + currentFrame++; + new MousePositionAbsoluteInput { Position = playfield.ToScreenSpace(replayFrames[currentFrame].Position) }.Apply(inputManager.CurrentState, inputManager); } // TODO: Implement the functionality to automatically spin spinners From 520924b440c0a642597d25d58d24efbcd705ff3a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 12 Aug 2019 09:45:21 +0300 Subject: [PATCH 2146/2854] Don't create a new request if onlineId is null --- .../Screens/Multi/Match/Components/MatchBeatmapPanel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs index f73059d069..397f158f2b 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs @@ -40,15 +40,15 @@ namespace osu.Game.Screens.Multi.Match.Components panel = null; } - var onlineId = item.NewValue?.Beatmap.OnlineBeatmapID ?? 0; + var onlineId = item.NewValue?.Beatmap.OnlineBeatmapID; - if (onlineId != 0) + if (onlineId.HasValue) { - request = new GetBeatmapSetRequest(onlineId, BeatmapSetLookupType.BeatmapId); + request = new GetBeatmapSetRequest(onlineId.Value, BeatmapSetLookupType.BeatmapId); request.Success += beatmap => { panel = new DirectGridPanel(beatmap.ToBeatmapSet(rulesets)); - LoadComponentAsync(panel, p => { AddInternal(panel); }); + LoadComponentAsync(panel, p => { AddInternal(p); }); }; api.Queue(request); } From 5b68a2f34a2acc1abe91bbb65197e8dcde93ff0d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 12 Aug 2019 09:50:49 +0300 Subject: [PATCH 2147/2854] Rename trackManager to previewTrackManager --- .../Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs | 4 ++-- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs index db2b61cdd9..f148170847 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }; [Resolved] - private PreviewTrackManager trackManager { get; set; } + private PreviewTrackManager previewTrackManager { get; set; } public TestSceneMatchBeatmapPanel() { @@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("Select random beatmap", () => { Room.CurrentItem.Value = Room.Playlist[RNG.Next(Room.Playlist.Count)]; - trackManager.StopAnyPlaying(this); + previewTrackManager.StopAnyPlaying(this); }); } diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index c89c32759d..8d79b21b16 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Multi.Match private BeatmapManager beatmapManager { get; set; } [Resolved] - private PreviewTrackManager trackManager { get; set; } + private PreviewTrackManager previewTrackManager { get; set; } [Resolved(CanBeNull = true)] private OsuGame game { get; set; } @@ -188,7 +188,7 @@ namespace osu.Game.Screens.Multi.Match Mods.Value = Array.Empty(); - trackManager.StopAnyPlaying(this); + previewTrackManager.StopAnyPlaying(this); return base.OnExiting(next); } @@ -207,7 +207,7 @@ namespace osu.Game.Screens.Multi.Match if (e.NewValue?.Ruleset != null) Ruleset.Value = e.NewValue.Ruleset; - trackManager.StopAnyPlaying(this); + previewTrackManager.StopAnyPlaying(this); } /// @@ -233,7 +233,7 @@ namespace osu.Game.Screens.Multi.Match private void onStart() { - trackManager.StopAnyPlaying(this); + previewTrackManager.StopAnyPlaying(this); switch (type.Value) { From cb910a651881819595c639c542af9c1f8686c6c2 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 12 Aug 2019 10:02:45 +0300 Subject: [PATCH 2148/2854] Replace if/else statement --- .../Screens/Multi/Match/MatchSubScreen.cs | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 8d79b21b16..dafea70092 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -150,20 +150,12 @@ namespace osu.Game.Screens.Multi.Match { const float fade_duration = 500; - if (tab.NewValue is SettingsMatchPage) - { - header.ShowBeatmapPanel.Value = false; - settings.Show(); - info.FadeOut(fade_duration, Easing.OutQuint); - bottomRow.FadeOut(fade_duration, Easing.OutQuint); - } - else - { - header.ShowBeatmapPanel.Value = true; - settings.Hide(); - info.FadeIn(fade_duration, Easing.OutQuint); - bottomRow.FadeIn(fade_duration, Easing.OutQuint); - } + var settingsDisplayed = tab.NewValue is SettingsMatchPage; + + header.ShowBeatmapPanel.Value = !settingsDisplayed; + settings.State.Value = settingsDisplayed ? Visibility.Visible : Visibility.Hidden; + info.FadeTo(settingsDisplayed ? 0 : 1, fade_duration, Easing.OutQuint); + bottomRow.FadeTo(settingsDisplayed ? 0 : 1, fade_duration, Easing.OutQuint); }, true); chat.Exit += () => From 75cb0d093b59cf74b50a328f7331158be5f6feca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Aug 2019 16:10:25 +0900 Subject: [PATCH 2149/2854] Use description correctly Required for localisation --- osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs | 2 +- .../Select/Leaderboards/BeatmapLeaderboardScope.cs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs index c74c7cbc2b..7eb9b1829c 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs @@ -69,7 +69,7 @@ namespace osu.Game.Overlays.BeatmapSet Margin = new MarginPadding { Bottom = 8 }, Origin = Anchor.BottomCentre, Anchor = Anchor.BottomCentre, - Text = value.GetDescription() + " Ranking", + Text = value.GetDescription(), Font = OsuFont.GetFont(weight: FontWeight.Regular), }, box = new Box diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScope.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScope.cs index 9e480b61c6..dc4c2ba4e2 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScope.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScope.cs @@ -1,13 +1,22 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.ComponentModel; + namespace osu.Game.Screens.Select.Leaderboards { public enum BeatmapLeaderboardScope { + [Description("Local Ranking")] Local, + + [Description("Country Ranking")] Country, + + [Description("Global Ranking")] Global, + + [Description("Friend Ranking")] Friend, } } From 982066dfdfe2bf2356ff1d7acfc5c0827d0894cd Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 12 Aug 2019 10:38:34 +0300 Subject: [PATCH 2150/2854] Convert to method group --- osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs index 397f158f2b..7939b18e97 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens.Multi.Match.Components request.Success += beatmap => { panel = new DirectGridPanel(beatmap.ToBeatmapSet(rulesets)); - LoadComponentAsync(panel, p => { AddInternal(p); }); + LoadComponentAsync(panel, AddInternal); }; api.Queue(request); } From 1bbd0ca54e0bcb4ab90b40cbaa0b533223bf043f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2019 08:30:26 +0000 Subject: [PATCH 2151/2854] Bump ppy.osu.Game.Resources from 2019.731.1 to 2019.809.0 Bumps [ppy.osu.Game.Resources](https://github.com/ppy/osu-resources) from 2019.731.1 to 2019.809.0. - [Release notes](https://github.com/ppy/osu-resources/releases) - [Commits](https://github.com/ppy/osu-resources/compare/2019.731.1...2019.809.0) Signed-off-by: dependabot-preview[bot] --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 3b2e6574ac..721d341c08 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -62,7 +62,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 1dd19ac7ed..b5266fd75d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,7 +14,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 1c3faeed39..103d89cadc 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -104,7 +104,7 @@ - + From 9c15024014d9e196e97a9dee1e817c30993080af Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 12 Aug 2019 10:53:06 +0200 Subject: [PATCH 2152/2854] Reword and add missing xmldoc to OnScreenDisplay --- osu.Game/Overlays/OnScreenDisplay.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs index a30ce5c56f..a92320945e 100644 --- a/osu.Game/Overlays/OnScreenDisplay.cs +++ b/osu.Game/Overlays/OnScreenDisplay.cs @@ -16,6 +16,10 @@ using osu.Game.Overlays.OSD; namespace osu.Game.Overlays { + /// + /// An on-screen display which automatically tracks and displays toast notifications for . + /// Can also display custom content via + /// public class OnScreenDisplay : Container { private readonly Container box; @@ -97,7 +101,7 @@ namespace osu.Game.Overlays } /// - /// Displays the given as parameter on the OSD + /// Displays the provided temporarily. /// /// public void Display(Toast toast) From 144d41f143fb429a848591ecb77bce3d548b64c9 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 12 Aug 2019 12:33:01 +0300 Subject: [PATCH 2153/2854] Add ability to not add all the items if enum --- osu.Game/Graphics/UserInterface/OsuTabControl.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 11f41b1a48..1bb37560b2 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -31,6 +31,8 @@ namespace osu.Game.Graphics.UserInterface protected virtual float StripWidth() => TabContainer.Children.Sum(c => c.IsPresent ? c.DrawWidth + TabContainer.Spacing.X : 0) - TabContainer.Spacing.X; protected virtual float StripHeight() => 1; + protected virtual bool AddAllItemsIfEnum => true; + private static bool isEnumType => typeof(T).IsEnum; public OsuTabControl() @@ -45,7 +47,7 @@ namespace osu.Game.Graphics.UserInterface Colour = Color4.White.Opacity(0), }); - if (isEnumType) + if (isEnumType && AddAllItemsIfEnum) foreach (var val in (T[])Enum.GetValues(typeof(T))) AddItem(val); } From fc521ac93b3727bc69c3dbebdb94ee53085be1eb Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 12 Aug 2019 13:08:15 +0300 Subject: [PATCH 2154/2854] Expose BoxColour property --- osu.Game/Graphics/UserInterface/PageTabControl.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/PageTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs index 156a556b5e..f8d1c7502a 100644 --- a/osu.Game/Graphics/UserInterface/PageTabControl.cs +++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs @@ -32,6 +32,12 @@ namespace osu.Game.Graphics.UserInterface protected readonly SpriteText Text; + protected Color4 BoxColour + { + get => box.Colour; + set => box.Colour = value; + } + public PageTabItem(T value) : base(value) { @@ -66,7 +72,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { - box.Colour = colours.Yellow; + BoxColour = colours.Yellow; } protected override bool OnHover(HoverEvent e) From 6533f114d42762d30e98a06770033fdfae247b8b Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 12 Aug 2019 12:09:09 +0200 Subject: [PATCH 2155/2854] Apply review suggestions --- .../UserInterface/TestSceneOnScreenDisplay.cs | 14 ++++++++++++++ osu.Game/Overlays/OSD/Toast.cs | 14 +++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs index 4decfc7dd6..558c027e1d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs @@ -24,6 +24,11 @@ namespace osu.Game.Tests.Visual.UserInterface Add(osd); AddStep("Display empty osd toast", () => osd.Display(new EmptyToast())); + AddAssert("Toast width is 240", () => osd.Child.Width == 240); + + AddStep("Display toast with lengthy text", () => osd.Display(new LengthyToast())); + AddAssert("Toast width is greater than 240", () => osd.Child.Width > 240); + AddRepeatStep("Change toggle (no bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingNoKeybind), 2); AddRepeatStep("Change toggle (with bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingWithKeybind), 2); AddRepeatStep("Change enum (no bind)", () => config.IncrementEnumSetting(TestConfigSetting.EnumSettingNoKeybind), 3); @@ -96,6 +101,15 @@ namespace osu.Game.Tests.Visual.UserInterface } } + private class LengthyToast : Toast + { + public LengthyToast() + : base("Toast with a very very very long text", "A very very very very very very long text also", "A very very very very very long shortcut") + { + + } + } + private class TestOnScreenDisplay : OnScreenDisplay { protected override void DisplayTemporarily(Drawable toDisplay) => toDisplay.FadeIn().ResizeHeightTo(110); diff --git a/osu.Game/Overlays/OSD/Toast.cs b/osu.Game/Overlays/OSD/Toast.cs index db5e6e4a6a..46c53ec409 100644 --- a/osu.Game/Overlays/OSD/Toast.cs +++ b/osu.Game/Overlays/OSD/Toast.cs @@ -13,22 +13,30 @@ namespace osu.Game.Overlays.OSD { public abstract class Toast : Container { + private const int toast_minimum_width = 240; + private readonly Container content; protected override Container Content => content; protected readonly OsuSpriteText ValueText; - protected Toast(string description, string value, string keybinding) + protected Toast(string description, string value, string shortcut) { Anchor = Anchor.Centre; Origin = Anchor.Centre; - Width = 240; // A toast's height is decided (and transformed) by the containing OnScreenDisplay. RelativeSizeAxes = Axes.Y; + AutoSizeAxes = Axes.X; InternalChildren = new Drawable[] { + new Container //this container exists just to set a minimum width for the toast + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = toast_minimum_width + }, new Box { RelativeSizeAxes = Axes.Both, @@ -68,7 +76,7 @@ namespace osu.Game.Overlays.OSD Alpha = 0.3f, Margin = new MarginPadding { Bottom = 15 }, Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), - Text = string.IsNullOrEmpty(keybinding) ? "NO KEY BOUND" : keybinding.ToUpperInvariant() + Text = string.IsNullOrEmpty(shortcut) ? "NO KEY BOUND" : shortcut.ToUpperInvariant() }, }; } From 5e49d0fb28e704a5c53f34ce1358a20173269cdb Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 12 Aug 2019 12:11:01 +0200 Subject: [PATCH 2156/2854] Fix CI issues --- osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs index 558c027e1d..45720548c8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs @@ -106,7 +106,6 @@ namespace osu.Game.Tests.Visual.UserInterface public LengthyToast() : base("Toast with a very very very long text", "A very very very very very very long text also", "A very very very very very long shortcut") { - } } From ba49a4c2da7d0791c53ac49955a41114acfa3a88 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 12 Aug 2019 13:16:57 +0300 Subject: [PATCH 2157/2854] Use existing PageTabControl for layout --- .../BeatmapSet/LeaderboardScopeSelector.cs | 63 ++++--------------- 1 file changed, 12 insertions(+), 51 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs index 7eb9b1829c..f54509ff77 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs @@ -8,25 +8,24 @@ using osu.Framework.Graphics.Containers; using osuTK; using osu.Game.Graphics.UserInterface; using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Sprites; -using osu.Framework.Extensions; using osu.Game.Graphics; using osu.Framework.Allocation; using osuTK.Graphics; -using osu.Framework.Input.Events; using osu.Framework.Graphics.Colour; +using osu.Framework.Input.Events; namespace osu.Game.Overlays.BeatmapSet { - public class LeaderboardScopeSelector : TabControl + public class LeaderboardScopeSelector : PageTabControl { + protected override bool AddAllItemsIfEnum => false; + protected override Dropdown CreateDropdown() => null; protected override TabItem CreateTabItem(BeatmapLeaderboardScope value) => new ScopeSelectorTabItem(value); public LeaderboardScopeSelector() { - AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; AddItem(BeatmapLeaderboardScope.Global); @@ -42,57 +41,31 @@ namespace osu.Game.Overlays.BeatmapSet protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, Direction = FillDirection.Horizontal, Spacing = new Vector2(20, 0), }; - private class ScopeSelectorTabItem : TabItem + private class ScopeSelectorTabItem : PageTabItem { - private const float transition_duration = 100; - - private readonly Box box; - - protected readonly OsuSpriteText Text; - public ScopeSelectorTabItem(BeatmapLeaderboardScope value) : base(value) { - AutoSizeAxes = Axes.Both; - - Children = new Drawable[] - { - Text = new OsuSpriteText - { - Margin = new MarginPadding { Bottom = 8 }, - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - Text = value.GetDescription(), - Font = OsuFont.GetFont(weight: FontWeight.Regular), - }, - box = new Box - { - RelativeSizeAxes = Axes.X, - Height = 5, - Scale = new Vector2(1f, 0f), - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - }, - new HoverClickSounds() - }; + Text.Font = OsuFont.GetFont(size: 16); } [BackgroundDependencyLoader] private void load(OsuColour colours) { - box.Colour = colours.Blue; + BoxColour = colours.Blue; } protected override bool OnHover(HoverEvent e) { - Text.FadeColour(Color4.LightSkyBlue); + Text.FadeColour(BoxColour); return base.OnHover(e); } @@ -103,18 +76,6 @@ namespace osu.Game.Overlays.BeatmapSet Text.FadeColour(Color4.White); } - - protected override void OnActivated() - { - box.ScaleTo(new Vector2(1f), transition_duration); - Text.Font = Text.Font.With(weight: FontWeight.Black); - } - - protected override void OnDeactivated() - { - box.ScaleTo(new Vector2(1f, 0f), transition_duration); - Text.Font = Text.Font.With(weight: FontWeight.Regular); - } } private class Line : GridContainer From 9c36cb4af4f60a8675640471a99adf65968ea7dd Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 12 Aug 2019 14:33:30 +0300 Subject: [PATCH 2158/2854] Use existing AccentColour logic instead of weird BoxColour --- .../Graphics/UserInterface/PageTabControl.cs | 26 ++++++++++++------- .../BeatmapSet/LeaderboardScopeSelector.cs | 14 +++++----- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PageTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs index f8d1c7502a..a0d3745180 100644 --- a/osu.Game/Graphics/UserInterface/PageTabControl.cs +++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs @@ -24,7 +24,13 @@ namespace osu.Game.Graphics.UserInterface Height = 30; } - public class PageTabItem : TabItem + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.Yellow; + } + + public class PageTabItem : TabItem, IHasAccentColour { private const float transition_duration = 100; @@ -32,10 +38,16 @@ namespace osu.Game.Graphics.UserInterface protected readonly SpriteText Text; - protected Color4 BoxColour + private Color4 accentColour; + + public Color4 AccentColour { - get => box.Colour; - set => box.Colour = value; + get => accentColour; + set + { + accentColour = value; + box.Colour = accentColour; + } } public PageTabItem(T value) @@ -69,12 +81,6 @@ namespace osu.Game.Graphics.UserInterface Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Exo, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true); } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BoxColour = colours.Yellow; - } - protected override bool OnHover(HoverEvent e) { if (!Active.Value) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs index f54509ff77..c867cc3780 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs @@ -39,6 +39,12 @@ namespace osu.Game.Overlays.BeatmapSet }); } + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.Blue; + } + protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer { Anchor = Anchor.BottomCentre, @@ -57,15 +63,9 @@ namespace osu.Game.Overlays.BeatmapSet Text.Font = OsuFont.GetFont(size: 16); } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BoxColour = colours.Blue; - } - protected override bool OnHover(HoverEvent e) { - Text.FadeColour(BoxColour); + Text.FadeColour(AccentColour); return base.OnHover(e); } From 883102ee5d912b503db9ea9a9e7e162657023b40 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 12 Aug 2019 16:40:52 +0300 Subject: [PATCH 2159/2854] Move score multiplier logic inside score calculation --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index ba2375bec1..c8858233aa 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -398,7 +398,7 @@ namespace osu.Game.Rulesets.Scoring if (rollingMaxBaseScore != 0) Accuracy.Value = baseScore / rollingMaxBaseScore; - TotalScore.Value = getScore(Mode.Value) * scoreMultiplier; + TotalScore.Value = getScore(Mode.Value); } private double getScore(ScoringMode mode) @@ -407,11 +407,11 @@ namespace osu.Game.Rulesets.Scoring { default: case ScoringMode.Standardised: - return max_score * (base_portion * baseScore / maxBaseScore + combo_portion * HighestCombo.Value / maxHighestCombo) + bonusScore; + return (max_score * (base_portion * baseScore / maxBaseScore + combo_portion * HighestCombo.Value / maxHighestCombo) + bonusScore) * scoreMultiplier; case ScoringMode.Classic: // should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1) - return bonusScore + baseScore * (1 + Math.Max(0, HighestCombo.Value - 1) / 25); + return bonusScore + baseScore * ((1 + Math.Max(0, HighestCombo.Value - 1) * scoreMultiplier) / 25); } } From c0f0fbbaa93927fb5c520182e0ece74eaaff5e09 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Aug 2019 00:14:37 +0900 Subject: [PATCH 2160/2854] Rename variable and add xmldoc --- osu.Game/Graphics/UserInterface/OsuTabControl.cs | 7 +++++-- osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 1bb37560b2..c55d14456b 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -31,7 +31,10 @@ namespace osu.Game.Graphics.UserInterface protected virtual float StripWidth() => TabContainer.Children.Sum(c => c.IsPresent ? c.DrawWidth + TabContainer.Spacing.X : 0) - TabContainer.Spacing.X; protected virtual float StripHeight() => 1; - protected virtual bool AddAllItemsIfEnum => true; + /// + /// Whether entries should be automatically populated if is an type. + /// + protected virtual bool AddEnumEntriesAutomatically => true; private static bool isEnumType => typeof(T).IsEnum; @@ -47,7 +50,7 @@ namespace osu.Game.Graphics.UserInterface Colour = Color4.White.Opacity(0), }); - if (isEnumType && AddAllItemsIfEnum) + if (isEnumType && AddEnumEntriesAutomatically) foreach (var val in (T[])Enum.GetValues(typeof(T))) AddItem(val); } diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs index c867cc3780..04713fd88c 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.BeatmapSet { public class LeaderboardScopeSelector : PageTabControl { - protected override bool AddAllItemsIfEnum => false; + protected override bool AddEnumEntriesAutomatically => false; protected override Dropdown CreateDropdown() => null; From 433b701df3478eb7038ed4002d7f0ba9d9489f86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Aug 2019 00:21:47 +0900 Subject: [PATCH 2161/2854] Make line slightly thicker (to display better at low resolutions) Also tidies up code. --- .../Overlays/BeatmapSet/LeaderboardScopeSelector.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs index 04713fd88c..dcd58db427 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.BeatmapSet AddItem(BeatmapLeaderboardScope.Country); AddItem(BeatmapLeaderboardScope.Friend); - AddInternal(new Line + AddInternal(new GradientLine { Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, @@ -78,19 +78,20 @@ namespace osu.Game.Overlays.BeatmapSet } } - private class Line : GridContainer + private class GradientLine : GridContainer { - public Line() + public GradientLine() { - Height = 1; RelativeSizeAxes = Axes.X; - Width = 0.8f; + Size = new Vector2(0.8f, 1.5f); + ColumnDimensions = new[] { new Dimension(), new Dimension(mode: GridSizeMode.Relative, size: 0.4f), new Dimension(), }; + Content = new[] { new Drawable[] From 5447e7cf222ea170409d18e0a8261185b090119a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Aug 2019 11:34:45 +0900 Subject: [PATCH 2162/2854] Fix file ordering --- osu.Game/Overlays/OSD/TrackedSettingToast.cs | 35 +++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/OSD/TrackedSettingToast.cs b/osu.Game/Overlays/OSD/TrackedSettingToast.cs index 0f4bd34779..8e8a99a0a7 100644 --- a/osu.Game/Overlays/OSD/TrackedSettingToast.cs +++ b/osu.Game/Overlays/OSD/TrackedSettingToast.cs @@ -66,12 +66,7 @@ namespace osu.Game.Overlays.OSD ValueText.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre; for (int i = 0; i < optionCount; i++) - { - optionLights.Add(new OptionLight - { - Glowing = i == selectedOption - }); - } + optionLights.Add(new OptionLight { Glowing = i == selectedOption }); } private class OptionLight : Container @@ -109,20 +104,6 @@ namespace osu.Game.Overlays.OSD } } - private void updateGlow() - { - if (glowing) - { - fill.FadeColour(glowingColour, transition_speed, Easing.OutQuint); - FadeEdgeEffectTo(glow_strength, transition_speed, Easing.OutQuint); - } - else - { - FadeEdgeEffectTo(0, transition_speed, Easing.OutQuint); - fill.FadeColour(idleColour, transition_speed, Easing.OutQuint); - } - } - [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -147,6 +128,20 @@ namespace osu.Game.Overlays.OSD updateGlow(); FinishTransforms(true); } + + private void updateGlow() + { + if (glowing) + { + fill.FadeColour(glowingColour, transition_speed, Easing.OutQuint); + FadeEdgeEffectTo(glow_strength, transition_speed, Easing.OutQuint); + } + else + { + FadeEdgeEffectTo(0, transition_speed, Easing.OutQuint); + fill.FadeColour(idleColour, transition_speed, Easing.OutQuint); + } + } } } } From f3380c9372c14a2c87db03b832c27001845a1e55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Aug 2019 12:06:57 +0900 Subject: [PATCH 2163/2854] Remove "jukebox" terminology --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 6 +++--- osu.Game/OsuGame.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index b2cbb77087..c54e3f596e 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -124,13 +124,13 @@ namespace osu.Game.Input.Bindings QuickExit, // Game-wide beatmap jukebox keybindings - [Description("Jukebox next track")] + [Description("Next track")] MusicNext, - [Description("Jukebox previous track")] + [Description("Previous track")] MusicPrev, - [Description("Jukebox play / pause current track")] + [Description("Play / pause")] MusicPlay, } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index af77c8816a..1541d1fa29 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -301,7 +301,7 @@ namespace osu.Game }, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true); } - #region Beatmap jukebox progression + #region Beatmap progression private void beatmapChanged(ValueChangedEvent beatmap) { From 0c0c4052168e9cba36bcd096c359c9c89c1e4a1a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Aug 2019 12:27:49 +0900 Subject: [PATCH 2164/2854] Add note to README about ffmpeg requirement on linux --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c4d676f4be..5dc4da12a4 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,9 @@ Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh ## Requirements - A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed. +- When running on linux, please have a system-wide ffmpeg installation available to support video decoding. +- When running on Windows 7 or 8.1, there are **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs. - When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio 2017+](https://visualstudio.microsoft.com/vs/), [Jetbrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/). -- Note that there are **[additional requirements for Windows 7 and Windows 8.1](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** which you may need to manually install if your operating system is not up-to-date. ## Running osu! From 33a119b7263501823c8f2c1f71a0d881bfd602fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Aug 2019 12:35:06 +0900 Subject: [PATCH 2165/2854] Fix double grammar Co-Authored-By: Dan Balasescu --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5dc4da12a4..56491a4be4 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh - A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed. - When running on linux, please have a system-wide ffmpeg installation available to support video decoding. -- When running on Windows 7 or 8.1, there are **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs. +- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs. - When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio 2017+](https://visualstudio.microsoft.com/vs/), [Jetbrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/). ## Running osu! From 5dbde38a6b00586a7d7b9223e775ea7b4fddbf61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Aug 2019 12:40:20 +0900 Subject: [PATCH 2166/2854] Group key bindings together --- .../Input/Bindings/GlobalActionContainer.cs | 30 +++++++++++-------- .../KeyBinding/GlobalKeyBindingsSection.cs | 12 ++++++++ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index c54e3f596e..8073200c47 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -20,7 +20,7 @@ namespace osu.Game.Input.Bindings handler = game; } - public override IEnumerable DefaultKeyBindings => GlobalKeyBindings.Concat(InGameKeyBindings); + public override IEnumerable DefaultKeyBindings => GlobalKeyBindings.Concat(InGameKeyBindings).Concat(AudioControlKeyBindings); public IEnumerable GlobalKeyBindings => new[] { @@ -32,11 +32,6 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings), new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar), new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings), - new KeyBinding(InputKey.Up, GlobalAction.IncreaseVolume), - new KeyBinding(InputKey.MouseWheelUp, GlobalAction.IncreaseVolume), - new KeyBinding(InputKey.Down, GlobalAction.DecreaseVolume), - new KeyBinding(InputKey.MouseWheelDown, GlobalAction.DecreaseVolume), - new KeyBinding(InputKey.F4, GlobalAction.ToggleMute), new KeyBinding(InputKey.Escape, GlobalAction.Back), new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back), @@ -44,13 +39,6 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.Space, GlobalAction.Select), new KeyBinding(InputKey.Enter, GlobalAction.Select), new KeyBinding(InputKey.KeypadEnter, GlobalAction.Select), - - new KeyBinding(InputKey.TrackPrevious, GlobalAction.MusicPrev), - new KeyBinding(InputKey.F1, GlobalAction.MusicPrev), - new KeyBinding(InputKey.TrackNext, GlobalAction.MusicNext), - new KeyBinding(InputKey.F5, GlobalAction.MusicNext), - new KeyBinding(InputKey.PlayPause, GlobalAction.MusicPlay), - new KeyBinding(InputKey.F3, GlobalAction.MusicPlay) }; public IEnumerable InGameKeyBindings => new[] @@ -62,6 +50,22 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed), }; + public IEnumerable AudioControlKeyBindings => new[] + { + new KeyBinding(InputKey.Up, GlobalAction.IncreaseVolume), + new KeyBinding(InputKey.MouseWheelUp, GlobalAction.IncreaseVolume), + new KeyBinding(InputKey.Down, GlobalAction.DecreaseVolume), + new KeyBinding(InputKey.MouseWheelDown, GlobalAction.DecreaseVolume), + new KeyBinding(InputKey.F4, GlobalAction.ToggleMute), + + new KeyBinding(InputKey.TrackPrevious, GlobalAction.MusicPrev), + new KeyBinding(InputKey.F1, GlobalAction.MusicPrev), + new KeyBinding(InputKey.TrackNext, GlobalAction.MusicNext), + new KeyBinding(InputKey.F5, GlobalAction.MusicNext), + new KeyBinding(InputKey.PlayPause, GlobalAction.MusicPlay), + new KeyBinding(InputKey.F3, GlobalAction.MusicPlay) + }; + protected override IEnumerable KeyBindingInputQueue => handler == null ? base.KeyBindingInputQueue : base.KeyBindingInputQueue.Prepend(handler); } diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs index 7e33d7ba27..56e93b6a1e 100644 --- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs @@ -15,6 +15,7 @@ namespace osu.Game.Overlays.KeyBinding public GlobalKeyBindingsSection(GlobalActionContainer manager) { Add(new DefaultBindingsSubsection(manager)); + Add(new AudioControlKeyBindingsSubsection(manager)); Add(new InGameKeyBindingsSubsection(manager)); } @@ -39,5 +40,16 @@ namespace osu.Game.Overlays.KeyBinding Defaults = manager.InGameKeyBindings; } } + + private class AudioControlKeyBindingsSubsection : KeyBindingsSubsection + { + protected override string Header => "Audio"; + + public AudioControlKeyBindingsSubsection(GlobalActionContainer manager) + : base(null) + { + Defaults = manager.AudioControlKeyBindings; + } + } } } From 5681d1097c107c4cbbe060c36997c08cc19ebe74 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Aug 2019 14:07:40 +0900 Subject: [PATCH 2167/2854] Move into components namespace --- .../Visual/Online/TestScenePreviousUsernamesContainer.cs | 2 +- .../Header/{ => Components}/PreviousUsernamesContainer.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename osu.Game/Overlays/Profile/Header/{ => Components}/PreviousUsernamesContainer.cs (99%) diff --git a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs index b891fda0f4..43373f872a 100644 --- a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs @@ -7,7 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Online.API; using osu.Game.Online.API.Requests; -using osu.Game.Overlays.Profile.Header; +using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Users; namespace osu.Game.Tests.Visual.Online diff --git a/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernamesContainer.cs similarity index 99% rename from osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs rename to osu.Game/Overlays/Profile/Header/Components/PreviousUsernamesContainer.cs index b53ac8eb80..ef9c01e12b 100644 --- a/osu.Game/Overlays/Profile/Header/PreviousUsernamesContainer.cs +++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernamesContainer.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -11,10 +13,8 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Users; using osuTK; -using System; -using System.Linq; -namespace osu.Game.Overlays.Profile.Header +namespace osu.Game.Overlays.Profile.Header.Components { public class PreviousUsernamesContainer : CompositeDrawable { From 8d3f2f76459d062cd2ee54afa747d96981794e9a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Aug 2019 14:09:10 +0900 Subject: [PATCH 2168/2854] Drop container from name --- ....cs => TestSceneUserProfilePreviousUsernames.cs} | 13 ++++++++++--- ...usUsernamesContainer.cs => PreviousUsernames.cs} | 4 ++-- 2 files changed, 12 insertions(+), 5 deletions(-) rename osu.Game.Tests/Visual/Online/{TestScenePreviousUsernamesContainer.cs => TestSceneUserProfilePreviousUsernames.cs} (85%) rename osu.Game/Overlays/Profile/Header/Components/{PreviousUsernamesContainer.cs => PreviousUsernames.cs} (98%) diff --git a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs similarity index 85% rename from osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs rename to osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs index 43373f872a..d09a50b12c 100644 --- a/osu.Game.Tests/Visual/Online/TestScenePreviousUsernamesContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -13,16 +15,21 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual.Online { [TestFixture] - public class TestScenePreviousUsernamesContainer : OsuTestScene + public class TestSceneUserProfilePreviousUsernames : OsuTestScene { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(PreviousUsernames) + }; + [Resolved] private IAPIProvider api { get; set; } private readonly Bindable user = new Bindable(); - public TestScenePreviousUsernamesContainer() + public TestSceneUserProfilePreviousUsernames() { - Child = new PreviousUsernamesContainer + Child = new PreviousUsernames { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernamesContainer.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs similarity index 98% rename from osu.Game/Overlays/Profile/Header/Components/PreviousUsernamesContainer.cs rename to osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs index ef9c01e12b..f18f319e27 100644 --- a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernamesContainer.cs +++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs @@ -16,7 +16,7 @@ using osuTK; namespace osu.Game.Overlays.Profile.Header.Components { - public class PreviousUsernamesContainer : CompositeDrawable + public class PreviousUsernames : CompositeDrawable { private const int duration = 200; private const int margin = 10; @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private readonly Box background; private readonly SpriteText header; - public PreviousUsernamesContainer() + public PreviousUsernames() { HoverIconContainer hoverIcon; From ad24265730b98ac86b5b8c739afdf2da172782ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Aug 2019 14:29:58 +0900 Subject: [PATCH 2169/2854] Split visual component out of MusicController --- .../Visual/Gameplay/TestSceneStoryboard.cs | 40 +- .../TestSceneBeatSyncedContainer.cs | 29 +- ...oller.cs => TestSceneNowPlayingOverlay.cs} | 18 +- osu.Game/OsuGame.cs | 4 +- osu.Game/Overlays/MusicController.cs | 475 +++--------------- osu.Game/Overlays/NowPlayingOverlay.cs | 403 +++++++++++++++ .../Overlays/Toolbar/ToolbarMusicButton.cs | 2 +- 7 files changed, 532 insertions(+), 439 deletions(-) rename osu.Game.Tests/Visual/UserInterface/{TestSceneMusicController.cs => TestSceneNowPlayingOverlay.cs} (58%) create mode 100644 osu.Game/Overlays/NowPlayingOverlay.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs index ead7a4b7fc..ff8437311e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs @@ -21,32 +21,38 @@ namespace osu.Game.Tests.Visual.Gameplay private readonly Container storyboardContainer; private DrawableStoryboard storyboard; + [Cached] + private MusicController musicController = new MusicController(); + public TestSceneStoryboard() { Clock = new FramedClock(); - Add(new Container + AddRange(new Drawable[] { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + musicController, + new Container { - new Box + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - storyboardContainer = new Container - { - RelativeSizeAxes = Axes.Both, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + storyboardContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, }, }, - }); - - Add(new MusicController - { - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - State = { Value = Visibility.Visible }, + new NowPlayingOverlay + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + State = { Value = Visibility.Visible }, + } }); AddStep("Restart", restart); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index 28f0cc027e..94228e22f0 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -3,6 +3,7 @@ using System; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -22,30 +23,36 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestSceneBeatSyncedContainer : OsuTestScene { - private readonly MusicController mc; + private readonly NowPlayingOverlay np; + + [Cached] + private MusicController musicController = new MusicController(); public TestSceneBeatSyncedContainer() { Clock = new FramedClock(); Clock.ProcessFrame(); - Add(new BeatContainer + AddRange(new Drawable[] { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - }); - - Add(mc = new MusicController - { - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, + musicController, + new BeatContainer + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }, + np = new NowPlayingOverlay + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + } }); } protected override void LoadComplete() { base.LoadComplete(); - mc.ToggleVisibility(); + np.ToggleVisibility(); } private class BeatContainer : BeatSyncedContainer diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs similarity index 58% rename from osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs index ab2ca47100..e3daa9c279 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Timing; using osu.Game.Overlays; @@ -9,22 +10,27 @@ using osu.Game.Overlays; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneMusicController : OsuTestScene + public class TestSceneNowPlayingOverlay : OsuTestScene { - public TestSceneMusicController() + [Cached] + private MusicController musicController = new MusicController(); + + public TestSceneNowPlayingOverlay() { Clock = new FramedClock(); - var mc = new MusicController + var np = new NowPlayingOverlay { Origin = Anchor.Centre, Anchor = Anchor.Centre }; - Add(mc); - AddStep(@"show", () => mc.Show()); + Add(musicController); + Add(np); + + AddStep(@"show", () => np.Show()); AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state); - AddStep(@"show", () => mc.Hide()); + AddStep(@"show", () => np.Hide()); } } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b9e2b79b05..edf3424e0e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -469,6 +469,8 @@ namespace osu.Game loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add); loadComponentSingleFile(new OnScreenDisplay(), Add, true); + loadComponentSingleFile(musicController = new MusicController(), Add, true); + loadComponentSingleFile(notifications = new NotificationOverlay { GetToolbarHeight = () => ToolbarOffset, @@ -495,7 +497,7 @@ namespace osu.Game Origin = Anchor.TopRight, }, rightFloatingOverlayContent.Add, true); - loadComponentSingleFile(musicController = new MusicController + loadComponentSingleFile(new NowPlayingOverlay { GetToolbarHeight = () => ToolbarOffset, Anchor = Anchor.TopRight, diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index abbcec5094..d1086d589d 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -4,281 +4,87 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Framework.Input.Events; -using osu.Framework.Localisation; using osu.Framework.Threading; using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Music; using osu.Game.Rulesets.Mods; -using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays { - public class MusicController : OsuFocusedOverlayContainer + /// + /// Handles playback of the global music track. + /// + public class MusicController : Component { - private const float player_height = 130; - private const float transition_length = 800; - private const float progress_height = 10; - private const float bottom_black_area_height = 55; - - private Drawable background; - private ProgressBar progressBar; - - private IconButton prevButton; - private IconButton playButton; - private IconButton nextButton; - private IconButton playlistButton; - - private SpriteText title, artist; - - private PlaylistOverlay playlist; - - private BeatmapManager beatmaps; + [Resolved] + private BeatmapManager beatmaps { get; set; } private List beatmapSets; - private Container dragContainer; - private Container playerContainer; - public bool IsUserPaused { get; private set; } + /// + /// Fired when the global has changed. + /// Includes direction information for display purposes. + /// + public event Action TrackChanged; + [Resolved] - private Bindable beatmap { get; set; } + private IBindable beatmap { get; set; } [Resolved] private IBindable> mods { get; set; } - /// - /// Provide a source for the toolbar height. - /// - public Func GetToolbarHeight; - - public MusicController() - { - Width = 400; - Margin = new MarginPadding(10); - } - [BackgroundDependencyLoader] - private void load(Bindable beatmap, BeatmapManager beatmaps, OsuColour colours) + private void load() { - this.beatmaps = beatmaps; - - Children = new Drawable[] - { - dragContainer = new DragContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - playlist = new PlaylistOverlay - { - RelativeSizeAxes = Axes.X, - Y = player_height + 10, - OrderChanged = playlistOrderChanged - }, - playerContainer = new Container - { - RelativeSizeAxes = Axes.X, - Height = player_height, - Masking = true, - CornerRadius = 5, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(40), - Radius = 5, - }, - Children = new[] - { - background = new Background(), - title = new OsuSpriteText - { - Origin = Anchor.BottomCentre, - Anchor = Anchor.TopCentre, - Position = new Vector2(0, 40), - Font = OsuFont.GetFont(size: 25, italics: true), - Colour = Color4.White, - Text = @"Nothing to play", - }, - artist = new OsuSpriteText - { - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Position = new Vector2(0, 45), - Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold, italics: true), - Colour = Color4.White, - Text = @"Nothing to play", - }, - new Container - { - Padding = new MarginPadding { Bottom = progress_height }, - Height = bottom_black_area_height, - RelativeSizeAxes = Axes.X, - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5), - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Children = new[] - { - prevButton = new MusicIconButton - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Action = prev, - Icon = FontAwesome.Solid.StepBackward, - }, - playButton = new MusicIconButton - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(1.4f), - IconScale = new Vector2(1.4f), - Action = togglePause, - Icon = FontAwesome.Regular.PlayCircle, - }, - nextButton = new MusicIconButton - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Action = () => next(), - Icon = FontAwesome.Solid.StepForward, - }, - } - }, - playlistButton = new MusicIconButton - { - Origin = Anchor.Centre, - Anchor = Anchor.CentreRight, - Position = new Vector2(-bottom_black_area_height / 2, 0), - Icon = FontAwesome.Solid.Bars, - Action = () => playlist.ToggleVisibility(), - }, - } - }, - progressBar = new ProgressBar - { - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - Height = progress_height, - FillColour = colours.Yellow, - OnSeek = attemptSeek - } - }, - }, - } - } - }; - beatmapSets = beatmaps.GetAllUsableBeatmapSets(); beatmaps.ItemAdded += handleBeatmapAdded; beatmaps.ItemRemoved += handleBeatmapRemoved; - - playlist.State.ValueChanged += s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint); } - private ScheduledDelegate seekDelegate; - - private void attemptSeek(double progress) + protected override void LoadComplete() { - seekDelegate?.Cancel(); - seekDelegate = Schedule(() => - { - if (!beatmap.Disabled) - current?.Track.Seek(progress); - }); + beatmap.BindValueChanged(beatmapChanged, true); + mods.BindValueChanged(_ => updateAudioAdjustments(), true); + base.LoadComplete(); } - private void playlistOrderChanged(BeatmapSetInfo beatmapSetInfo, int index) + /// + /// Change the position of a in the current playlist. + /// + /// The beatmap to move. + /// The new position. + public void ChangeBeatmapSetPosition(BeatmapSetInfo beatmapSetInfo, int index) { beatmapSets.Remove(beatmapSetInfo); beatmapSets.Insert(index, beatmapSetInfo); } - private void handleBeatmapAdded(BeatmapSetInfo set) => Schedule(() => beatmapSets.Add(set)); + private void handleBeatmapAdded(BeatmapSetInfo set) => + Schedule(() => beatmapSets.Add(set)); - private void handleBeatmapRemoved(BeatmapSetInfo set) => Schedule(() => beatmapSets.RemoveAll(s => s.ID == set.ID)); + private void handleBeatmapRemoved(BeatmapSetInfo set) => + Schedule(() => beatmapSets.RemoveAll(s => s.ID == set.ID)); - protected override void LoadComplete() + private ScheduledDelegate seekDelegate; + + public void SeekTo(double position) { - beatmap.BindValueChanged(beatmapChanged, true); - beatmap.BindDisabledChanged(beatmapDisabledChanged, true); - mods.BindValueChanged(_ => updateAudioAdjustments(), true); - base.LoadComplete(); - } - - private void beatmapDisabledChanged(bool disabled) - { - if (disabled) - playlist.Hide(); - - playButton.Enabled.Value = !disabled; - prevButton.Enabled.Value = !disabled; - nextButton.Enabled.Value = !disabled; - playlistButton.Enabled.Value = !disabled; - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - Height = dragContainer.Height; - - dragContainer.Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; - } - - protected override void Update() - { - base.Update(); - - if (pendingBeatmapSwitch != null) + seekDelegate?.Cancel(); + seekDelegate = Schedule(() => { - pendingBeatmapSwitch(); - pendingBeatmapSwitch = null; - } - - var track = current?.TrackLoaded ?? false ? current.Track : null; - - if (track?.IsDummyDevice == false) - { - progressBar.EndTime = track.Length; - progressBar.CurrentTime = track.CurrentTime; - - playButton.Icon = track.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle; - } - else - { - progressBar.CurrentTime = 0; - progressBar.EndTime = 1; - playButton.Icon = FontAwesome.Regular.PlayCircle; - } + if (!beatmap.Disabled) + current?.Track.Seek(position); + }); } - private void togglePause() + /// + /// Toggle pause / play. + /// + public void TogglePause() { var track = current?.Track; @@ -301,46 +107,57 @@ namespace osu.Game.Overlays } } - private void prev() + /// + /// Play the previous track. + /// + public void PrevTrack() { - queuedDirection = TransformDirection.Prev; + queuedDirection = TrackChangeDirection.Prev; var playable = beatmapSets.TakeWhile(i => i.ID != current.BeatmapSetInfo.ID).LastOrDefault() ?? beatmapSets.LastOrDefault(); if (playable != null) { - beatmap.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value); + if (beatmap is Bindable working) + working.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value); beatmap.Value.Track.Restart(); } } + /// + /// Play the next random or playlist track. + /// + public void NextTrack() => next(); + private void next(bool instant = false) { if (!instant) - queuedDirection = TransformDirection.Next; + queuedDirection = TrackChangeDirection.Next; var playable = beatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).Skip(1).FirstOrDefault() ?? beatmapSets.FirstOrDefault(); if (playable != null) { - beatmap.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value); + if (beatmap is Bindable working) + working.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value); beatmap.Value.Track.Restart(); } } private WorkingBeatmap current; - private TransformDirection? queuedDirection; + + private TrackChangeDirection? queuedDirection; private void beatmapChanged(ValueChangedEvent beatmap) { - TransformDirection direction = TransformDirection.None; + TrackChangeDirection direction = TrackChangeDirection.None; if (current != null) { bool audioEquals = beatmap.NewValue?.BeatmapInfo?.AudioEquals(current.BeatmapInfo) ?? false; if (audioEquals) - direction = TransformDirection.None; + direction = TrackChangeDirection.None; else if (queuedDirection.HasValue) { direction = queuedDirection.Value; @@ -352,13 +169,13 @@ namespace osu.Game.Overlays var last = beatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count(); var next = beatmap.NewValue == null ? -1 : beatmapSets.TakeWhile(b => b.ID != beatmap.NewValue.BeatmapSetInfo?.ID).Count(); - direction = last > next ? TransformDirection.Prev : TransformDirection.Next; + direction = last > next ? TrackChangeDirection.Prev : TrackChangeDirection.Next; } } - progressBar.CurrentTime = 0; + current = beatmap.NewValue; + TrackChanged?.Invoke(current, direction); - updateDisplay(current = beatmap.NewValue, direction); updateAudioAdjustments(); queuedDirection = null; @@ -376,167 +193,19 @@ namespace osu.Game.Overlays mod.ApplyToClock(track); } - private Action pendingBeatmapSwitch; - - private void updateDisplay(WorkingBeatmap beatmap, TransformDirection direction) + protected override void Dispose(bool isDisposing) { - // avoid using scheduler as our scheduler may not be run for a long time, holding references to beatmaps. - pendingBeatmapSwitch = delegate - { - // todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync() - Task.Run(() => - { - if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists - { - title.Text = @"Nothing to play"; - artist.Text = @"Nothing to play"; - } - else - { - BeatmapMetadata metadata = beatmap.Metadata; - title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)); - artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist)); - } - }); + base.Dispose(isDisposing); - LoadComponentAsync(new Background(beatmap) { Depth = float.MaxValue }, newBackground => - { - switch (direction) - { - case TransformDirection.Next: - newBackground.Position = new Vector2(400, 0); - newBackground.MoveToX(0, 500, Easing.OutCubic); - background.MoveToX(-400, 500, Easing.OutCubic); - break; - - case TransformDirection.Prev: - newBackground.Position = new Vector2(-400, 0); - newBackground.MoveToX(0, 500, Easing.OutCubic); - background.MoveToX(400, 500, Easing.OutCubic); - break; - } - - background.Expire(); - background = newBackground; - - playerContainer.Add(newBackground); - }); - }; + beatmaps.ItemAdded -= handleBeatmapAdded; + beatmaps.ItemRemoved -= handleBeatmapRemoved; } + } - protected override void PopIn() - { - base.PopIn(); - - this.FadeIn(transition_length, Easing.OutQuint); - dragContainer.ScaleTo(1, transition_length, Easing.OutElastic); - } - - protected override void PopOut() - { - base.PopOut(); - - this.FadeOut(transition_length, Easing.OutQuint); - dragContainer.ScaleTo(0.9f, transition_length, Easing.OutQuint); - } - - private enum TransformDirection - { - None, - Next, - Prev - } - - private class MusicIconButton : IconButton - { - public MusicIconButton() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - HoverColour = colours.YellowDark.Opacity(0.6f); - FlashColour = colours.Yellow; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - // works with AutoSizeAxes above to make buttons autosize with the scale animation. - Content.AutoSizeAxes = Axes.None; - Content.Size = new Vector2(DEFAULT_BUTTON_SIZE); - } - } - - private class Background : BufferedContainer - { - private readonly Sprite sprite; - private readonly WorkingBeatmap beatmap; - - public Background(WorkingBeatmap beatmap = null) - { - this.beatmap = beatmap; - CacheDrawnFrameBuffer = true; - Depth = float.MaxValue; - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - sprite = new Sprite - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(150), - FillMode = FillMode.Fill, - }, - new Box - { - RelativeSizeAxes = Axes.X, - Height = bottom_black_area_height, - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - Colour = Color4.Black.Opacity(0.5f) - } - }; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4"); - } - } - - private class DragContainer : Container - { - protected override bool OnDragStart(DragStartEvent e) - { - return true; - } - - protected override bool OnDrag(DragEvent e) - { - Vector2 change = e.MousePosition - e.MouseDownPosition; - - // Diminish the drag distance as we go further to simulate "rubber band" feeling. - change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length; - - this.MoveTo(change); - return true; - } - - protected override bool OnDragEnd(DragEndEvent e) - { - this.MoveTo(Vector2.Zero, 800, Easing.OutElastic); - return base.OnDragEnd(e); - } - } - - /// - /// Play the next random or playlist track. - /// - public void NextTrack() => next(); + public enum TrackChangeDirection + { + None, + Next, + Prev } } diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs new file mode 100644 index 0000000000..98bad5323d --- /dev/null +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -0,0 +1,403 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Events; +using osu.Framework.Localisation; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Music; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays +{ + public class NowPlayingOverlay : OsuFocusedOverlayContainer + { + private const float player_height = 130; + private const float transition_length = 800; + private const float progress_height = 10; + private const float bottom_black_area_height = 55; + + private Drawable background; + private ProgressBar progressBar; + + private IconButton prevButton; + private IconButton playButton; + private IconButton nextButton; + private IconButton playlistButton; + + private SpriteText title, artist; + + private PlaylistOverlay playlist; + + private Container dragContainer; + private Container playerContainer; + + /// + /// Provide a source for the toolbar height. + /// + public Func GetToolbarHeight; + + [Resolved] + private MusicController musicController { get; set; } + + [Resolved] + private Bindable beatmap { get; set; } + + public NowPlayingOverlay() + { + Width = 400; + Margin = new MarginPadding(10); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Children = new Drawable[] + { + dragContainer = new DragContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + playlist = new PlaylistOverlay + { + RelativeSizeAxes = Axes.X, + Y = player_height + 10, + OrderChanged = musicController.ChangeBeatmapSetPosition + }, + playerContainer = new Container + { + RelativeSizeAxes = Axes.X, + Height = player_height, + Masking = true, + CornerRadius = 5, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(40), + Radius = 5, + }, + Children = new[] + { + background = new Background(), + title = new OsuSpriteText + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.TopCentre, + Position = new Vector2(0, 40), + Font = OsuFont.GetFont(size: 25, italics: true), + Colour = Color4.White, + Text = @"Nothing to play", + }, + artist = new OsuSpriteText + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Position = new Vector2(0, 45), + Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold, italics: true), + Colour = Color4.White, + Text = @"Nothing to play", + }, + new Container + { + Padding = new MarginPadding { Bottom = progress_height }, + Height = bottom_black_area_height, + RelativeSizeAxes = Axes.X, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Children = new[] + { + prevButton = new MusicIconButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = musicController.PrevTrack, + Icon = FontAwesome.Solid.StepBackward, + }, + playButton = new MusicIconButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(1.4f), + IconScale = new Vector2(1.4f), + Action = musicController.TogglePause, + Icon = FontAwesome.Regular.PlayCircle, + }, + nextButton = new MusicIconButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = musicController.NextTrack, + Icon = FontAwesome.Solid.StepForward, + }, + } + }, + playlistButton = new MusicIconButton + { + Origin = Anchor.Centre, + Anchor = Anchor.CentreRight, + Position = new Vector2(-bottom_black_area_height / 2, 0), + Icon = FontAwesome.Solid.Bars, + Action = () => playlist.ToggleVisibility(), + }, + } + }, + progressBar = new ProgressBar + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + Height = progress_height, + FillColour = colours.Yellow, + OnSeek = musicController.SeekTo + } + }, + }, + } + } + }; + + playlist.State.ValueChanged += s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + beatmap.BindDisabledChanged(beatmapDisabledChanged, true); + + musicController.TrackChanged += trackChanged; + trackChanged(beatmap.Value); + } + + protected override void PopIn() + { + base.PopIn(); + + this.FadeIn(transition_length, Easing.OutQuint); + dragContainer.ScaleTo(1, transition_length, Easing.OutElastic); + } + + protected override void PopOut() + { + base.PopOut(); + + this.FadeOut(transition_length, Easing.OutQuint); + dragContainer.ScaleTo(0.9f, transition_length, Easing.OutQuint); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + Height = dragContainer.Height; + dragContainer.Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; + } + + protected override void Update() + { + base.Update(); + + if (pendingBeatmapSwitch != null) + { + pendingBeatmapSwitch(); + pendingBeatmapSwitch = null; + } + + var track = beatmap.Value?.TrackLoaded ?? false ? beatmap.Value.Track : null; + + if (track?.IsDummyDevice == false) + { + progressBar.EndTime = track.Length; + progressBar.CurrentTime = track.CurrentTime; + + playButton.Icon = track.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle; + } + else + { + progressBar.CurrentTime = 0; + progressBar.EndTime = 1; + playButton.Icon = FontAwesome.Regular.PlayCircle; + } + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + musicController.TrackChanged -= trackChanged; + } + + private Action pendingBeatmapSwitch; + + private void trackChanged(WorkingBeatmap beatmap, TrackChangeDirection direction = TrackChangeDirection.None) + { + // avoid using scheduler as our scheduler may not be run for a long time, holding references to beatmaps. + pendingBeatmapSwitch = delegate + { + // todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync() + Task.Run(() => + { + if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists + { + title.Text = @"Nothing to play"; + artist.Text = @"Nothing to play"; + } + else + { + BeatmapMetadata metadata = beatmap.Metadata; + title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)); + artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist)); + } + }); + + LoadComponentAsync(new Background(beatmap) { Depth = float.MaxValue }, newBackground => + { + switch (direction) + { + case TrackChangeDirection.Next: + newBackground.Position = new Vector2(400, 0); + newBackground.MoveToX(0, 500, Easing.OutCubic); + background.MoveToX(-400, 500, Easing.OutCubic); + break; + + case TrackChangeDirection.Prev: + newBackground.Position = new Vector2(-400, 0); + newBackground.MoveToX(0, 500, Easing.OutCubic); + background.MoveToX(400, 500, Easing.OutCubic); + break; + } + + background.Expire(); + background = newBackground; + + playerContainer.Add(newBackground); + }); + }; + } + + private void beatmapDisabledChanged(bool disabled) + { + if (disabled) + playlist.Hide(); + + playButton.Enabled.Value = !disabled; + prevButton.Enabled.Value = !disabled; + nextButton.Enabled.Value = !disabled; + playlistButton.Enabled.Value = !disabled; + } + + private class MusicIconButton : IconButton + { + public MusicIconButton() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + HoverColour = colours.YellowDark.Opacity(0.6f); + FlashColour = colours.Yellow; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // works with AutoSizeAxes above to make buttons autosize with the scale animation. + Content.AutoSizeAxes = Axes.None; + Content.Size = new Vector2(DEFAULT_BUTTON_SIZE); + } + } + + private class Background : BufferedContainer + { + private readonly Sprite sprite; + private readonly WorkingBeatmap beatmap; + + public Background(WorkingBeatmap beatmap = null) + { + this.beatmap = beatmap; + CacheDrawnFrameBuffer = true; + Depth = float.MaxValue; + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + sprite = new Sprite + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(150), + FillMode = FillMode.Fill, + }, + new Box + { + RelativeSizeAxes = Axes.X, + Height = bottom_black_area_height, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + Colour = Color4.Black.Opacity(0.5f) + } + }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4"); + } + } + + private class DragContainer : Container + { + protected override bool OnDragStart(DragStartEvent e) + { + return true; + } + + protected override bool OnDrag(DragEvent e) + { + Vector2 change = e.MousePosition - e.MouseDownPosition; + + // Diminish the drag distance as we go further to simulate "rubber band" feeling. + change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length; + + this.MoveTo(change); + return true; + } + + protected override bool OnDragEnd(DragEndEvent e) + { + this.MoveTo(Vector2.Zero, 800, Easing.OutElastic); + return base.OnDragEnd(e); + } + } + } +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs index f03df2ed93..b29aec5842 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Toolbar } [BackgroundDependencyLoader(true)] - private void load(MusicController music) + private void load(NowPlayingOverlay music) { StateContainer = music; } From 9aac5efa4e5bf8ff6374731b815ec5e26a8a75d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Aug 2019 14:38:49 +0900 Subject: [PATCH 2170/2854] Move logic out of OsuGame --- osu.Game/OsuGame.cs | 25 +------ osu.Game/Overlays/MusicController.cs | 74 +++++++++++++++++-- osu.Game/Overlays/NowPlayingOverlay.cs | 6 +- osu.Game/Overlays/OSD/MusicControllerToast.cs | 13 ---- 4 files changed, 70 insertions(+), 48 deletions(-) delete mode 100644 osu.Game/Overlays/OSD/MusicControllerToast.cs diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index ab7e5b19d1..0e804ecbaf 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -68,8 +68,6 @@ namespace osu.Game private BeatmapSetOverlay beatmapSetOverlay; - private OnScreenDisplay osd; - [Cached] private readonly ScreenshotManager screenshotManager = new ScreenshotManager(); @@ -469,7 +467,7 @@ namespace osu.Game }); loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add); - loadComponentSingleFile(osd = new OnScreenDisplay(), Add, true); + loadComponentSingleFile(new OnScreenDisplay(), Add, true); loadComponentSingleFile(musicController = new MusicController(), Add, true); @@ -734,27 +732,6 @@ namespace osu.Game case GlobalAction.ToggleGameplayMouseButtons: LocalConfig.Set(OsuSetting.MouseDisableButtons, !LocalConfig.Get(OsuSetting.MouseDisableButtons)); return true; - - case GlobalAction.MusicPlay: - if (!musicController.IsLoaded) return true; - - if (musicController.PlayTrack()) - osd.Display(new Overlays.OSD.MusicControllerToast(musicController.IsPlaying ? "Play track" : "Pause track")); - return true; - - case GlobalAction.MusicNext: - if (!musicController.IsLoaded) return true; - - if (musicController.NextTrack()) - osd.Display(new Overlays.OSD.MusicControllerToast("Next track")); - return true; - - case GlobalAction.MusicPrev: - if (!musicController.IsLoaded) return true; - - if (musicController.PreviousTrack()) - osd.Display(new Overlays.OSD.MusicControllerToast("Previous track")); - return true; } return false; diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index d1086d589d..14f7b574da 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -7,8 +7,11 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Input.Bindings; using osu.Framework.Threading; using osu.Game.Beatmaps; +using osu.Game.Input.Bindings; +using osu.Game.Overlays.OSD; using osu.Game.Rulesets.Mods; namespace osu.Game.Overlays @@ -16,7 +19,7 @@ namespace osu.Game.Overlays /// /// Handles playback of the global music track. /// - public class MusicController : Component + public class MusicController : Component, IKeyBindingHandler { [Resolved] private BeatmapManager beatmaps { get; set; } @@ -37,6 +40,9 @@ namespace osu.Game.Overlays [Resolved] private IBindable> mods { get; set; } + [Resolved(canBeNull: true)] + private OnScreenDisplay onScreenDisplay { get; set; } + [BackgroundDependencyLoader] private void load() { @@ -58,11 +64,17 @@ namespace osu.Game.Overlays /// The beatmap to move. /// The new position. public void ChangeBeatmapSetPosition(BeatmapSetInfo beatmapSetInfo, int index) + { beatmapSets.Remove(beatmapSetInfo); beatmapSets.Insert(index, beatmapSetInfo); } + /// + /// Returns whether the current beatmap track is playing. + /// + public bool IsPlaying => beatmap.Value.Track.IsRunning; + private void handleBeatmapAdded(BeatmapSetInfo set) => Schedule(() => beatmapSets.Add(set)); @@ -84,15 +96,17 @@ namespace osu.Game.Overlays /// /// Toggle pause / play. /// - public void TogglePause() + public bool TogglePause() { var track = current?.Track; if (track == null) { - if (!beatmap.Disabled) - next(true); - return; + if (beatmap.Disabled) + return false; + + next(true); + return true; } if (track.IsRunning) @@ -105,12 +119,14 @@ namespace osu.Game.Overlays track.Start(); IsUserPaused = false; } + + return true; } /// /// Play the previous track. /// - public void PrevTrack() + public bool PrevTrack() { queuedDirection = TrackChangeDirection.Prev; @@ -121,15 +137,19 @@ namespace osu.Game.Overlays if (beatmap is Bindable working) working.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value); beatmap.Value.Track.Restart(); + + return true; } + + return false; } /// /// Play the next random or playlist track. /// - public void NextTrack() => next(); + public bool NextTrack() => next(); - private void next(bool instant = false) + private bool next(bool instant = false) { if (!instant) queuedDirection = TrackChangeDirection.Next; @@ -141,7 +161,10 @@ namespace osu.Game.Overlays if (beatmap is Bindable working) working.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value); beatmap.Value.Track.Restart(); + return true; } + + return false; } private WorkingBeatmap current; @@ -200,6 +223,41 @@ namespace osu.Game.Overlays beatmaps.ItemAdded -= handleBeatmapAdded; beatmaps.ItemRemoved -= handleBeatmapRemoved; } + + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.MusicPlay: + if (TogglePause()) + onScreenDisplay?.Display(new MusicControllerToast(IsPlaying ? "Play track" : "Pause track")); + return true; + + case GlobalAction.MusicNext: + if (NextTrack()) + onScreenDisplay?.Display(new MusicControllerToast("Next track")); + + return true; + + case GlobalAction.MusicPrev: + if (PrevTrack()) + onScreenDisplay?.Display(new MusicControllerToast("Previous track")); + + return true; + } + + return false; + } + + public bool OnReleased(GlobalAction action) => false; + + public class MusicControllerToast : Toast + { + public MusicControllerToast(string action) + : base("Music Playback", action, string.Empty) + { + } + } } public enum TrackChangeDirection diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 98bad5323d..f14adcb53d 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -138,7 +138,7 @@ namespace osu.Game.Overlays { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Action = musicController.PrevTrack, + Action = () => musicController.PrevTrack(), Icon = FontAwesome.Solid.StepBackward, }, playButton = new MusicIconButton @@ -147,14 +147,14 @@ namespace osu.Game.Overlays Origin = Anchor.Centre, Scale = new Vector2(1.4f), IconScale = new Vector2(1.4f), - Action = musicController.TogglePause, + Action = () => musicController.TogglePause(), Icon = FontAwesome.Regular.PlayCircle, }, nextButton = new MusicIconButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Action = musicController.NextTrack, + Action = () => musicController.NextTrack(), Icon = FontAwesome.Solid.StepForward, }, } diff --git a/osu.Game/Overlays/OSD/MusicControllerToast.cs b/osu.Game/Overlays/OSD/MusicControllerToast.cs deleted file mode 100644 index d9e0ad2c07..0000000000 --- a/osu.Game/Overlays/OSD/MusicControllerToast.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Overlays.OSD -{ - public class MusicControllerToast : Toast - { - public MusicControllerToast(string value) - : base("Music Playback", value, "") - { - } - } -} From de1ab56a2c28e408879d273fde84c509d91c31cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Aug 2019 14:45:27 +0900 Subject: [PATCH 2171/2854] Fix potential nullref on disposal --- osu.Game/Overlays/MusicController.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index d1086d589d..681e318c92 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -197,8 +197,11 @@ namespace osu.Game.Overlays { base.Dispose(isDisposing); - beatmaps.ItemAdded -= handleBeatmapAdded; - beatmaps.ItemRemoved -= handleBeatmapRemoved; + if (beatmaps != null) + { + beatmaps.ItemAdded -= handleBeatmapAdded; + beatmaps.ItemRemoved -= handleBeatmapRemoved; + } } } From 2cbdf8c01c3044a028a876e6fd9753996b4b34fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Aug 2019 14:46:57 +0900 Subject: [PATCH 2172/2854] Update public methods in line with future usage --- osu.Game/Overlays/MusicController.cs | 35 ++++++++++++++++++++------ osu.Game/Overlays/NowPlayingOverlay.cs | 6 ++--- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 681e318c92..da9c34238e 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -63,6 +63,11 @@ namespace osu.Game.Overlays beatmapSets.Insert(index, beatmapSetInfo); } + /// + /// Returns whether the current beatmap track is playing. + /// + public bool IsPlaying => beatmap.Value.Track.IsRunning; + private void handleBeatmapAdded(BeatmapSetInfo set) => Schedule(() => beatmapSets.Add(set)); @@ -84,15 +89,18 @@ namespace osu.Game.Overlays /// /// Toggle pause / play. /// - public void TogglePause() + /// Whether the operation was successful. + public bool TogglePause() { var track = current?.Track; if (track == null) { - if (!beatmap.Disabled) - next(true); - return; + if (beatmap.Disabled) + return false; + + next(true); + return true; } if (track.IsRunning) @@ -105,12 +113,15 @@ namespace osu.Game.Overlays track.Start(); IsUserPaused = false; } + + return true; } /// /// Play the previous track. /// - public void PrevTrack() + /// Whether the operation was successful. + public bool PrevTrack() { queuedDirection = TrackChangeDirection.Prev; @@ -121,15 +132,20 @@ namespace osu.Game.Overlays if (beatmap is Bindable working) working.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value); beatmap.Value.Track.Restart(); + + return true; } + + return false; } /// /// Play the next random or playlist track. /// - public void NextTrack() => next(); + /// Whether the operation was successful. + public bool NextTrack() => next(); - private void next(bool instant = false) + private bool next(bool instant = false) { if (!instant) queuedDirection = TrackChangeDirection.Next; @@ -141,7 +157,10 @@ namespace osu.Game.Overlays if (beatmap is Bindable working) working.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value); beatmap.Value.Track.Restart(); + return true; } + + return false; } private WorkingBeatmap current; diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 98bad5323d..f14adcb53d 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -138,7 +138,7 @@ namespace osu.Game.Overlays { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Action = musicController.PrevTrack, + Action = () => musicController.PrevTrack(), Icon = FontAwesome.Solid.StepBackward, }, playButton = new MusicIconButton @@ -147,14 +147,14 @@ namespace osu.Game.Overlays Origin = Anchor.Centre, Scale = new Vector2(1.4f), IconScale = new Vector2(1.4f), - Action = musicController.TogglePause, + Action = () => musicController.TogglePause(), Icon = FontAwesome.Regular.PlayCircle, }, nextButton = new MusicIconButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Action = musicController.NextTrack, + Action = () => musicController.NextTrack(), Icon = FontAwesome.Solid.StepForward, }, } From 81f8b5f325702b5d1ae227006b9c26b7d2dd2834 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Aug 2019 14:52:51 +0900 Subject: [PATCH 2173/2854] Fix merge issue --- osu.Game/Overlays/MusicController.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index b055c7ef1b..91220907a0 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -64,7 +64,6 @@ namespace osu.Game.Overlays /// The beatmap to move. /// The new position. public void ChangeBeatmapSetPosition(BeatmapSetInfo beatmapSetInfo, int index) - { beatmapSets.Remove(beatmapSetInfo); beatmapSets.Insert(index, beatmapSetInfo); From b942192e004c95891e02048d007077836c51f9b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Aug 2019 15:04:04 +0900 Subject: [PATCH 2174/2854] Fix remaining nullref --- osu.Game/Overlays/NowPlayingOverlay.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index f14adcb53d..a3243a655e 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -247,13 +247,6 @@ namespace osu.Game.Overlays } } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - musicController.TrackChanged -= trackChanged; - } - private Action pendingBeatmapSwitch; private void trackChanged(WorkingBeatmap beatmap, TrackChangeDirection direction = TrackChangeDirection.None) @@ -313,6 +306,14 @@ namespace osu.Game.Overlays playlistButton.Enabled.Value = !disabled; } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (musicController != null) + musicController.TrackChanged -= trackChanged; + } + private class MusicIconButton : IconButton { public MusicIconButton() From 8ef4b2a0f6a1467c0e8fd9dec63e35d63d1df942 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 13 Aug 2019 11:29:48 +0300 Subject: [PATCH 2175/2854] Hide NotificationOverlay --- osu.Desktop/Overlays/VersionManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 1f1d2cea5f..51e801c185 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -113,13 +113,14 @@ namespace osu.Desktop.Overlays } [BackgroundDependencyLoader] - private void load(OsuColour colours, ChangelogOverlay changelog) + private void load(OsuColour colours, ChangelogOverlay changelog, NotificationOverlay notificationOverlay) { Icon = FontAwesome.Solid.CheckSquare; IconBackgound.Colour = colours.BlueDark; Activated = delegate { + notificationOverlay.Hide(); changelog.ShowBuild(OsuGameBase.CLIENT_STREAM_NAME, version); return true; }; From 6b57c9801d8e9a97d5d520f4e0705530190bd216 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Aug 2019 17:32:43 +0900 Subject: [PATCH 2176/2854] Cull some unnecessary whitespace --- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index dafea70092..ea7a713e0e 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -177,9 +177,7 @@ namespace osu.Game.Screens.Multi.Match public override bool OnExiting(IScreen next) { RoomManager?.PartRoom(); - Mods.Value = Array.Empty(); - previewTrackManager.StopAnyPlaying(this); return base.OnExiting(next); From 92c2dafa1281048437cf7651caf61643b5b302d7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Aug 2019 17:34:16 +0900 Subject: [PATCH 2177/2854] Tighten accessibility --- osu.Game/Screens/Multi/Match/Components/Header.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/Components/Header.cs b/osu.Game/Screens/Multi/Match/Components/Header.cs index 7f2b1278b7..a52d43acf4 100644 --- a/osu.Game/Screens/Multi/Match/Components/Header.cs +++ b/osu.Game/Screens/Multi/Match/Components/Header.cs @@ -25,12 +25,12 @@ namespace osu.Game.Screens.Multi.Match.Components { public const float HEIGHT = 200; - public MatchTabControl Tabs; + public readonly BindableBool ShowBeatmapPanel = new BindableBool(); + + public MatchTabControl Tabs { get; private set; } public Action RequestBeatmapSelection; - public BindableBool ShowBeatmapPanel = new BindableBool(); - private MatchBeatmapPanel beatmapPanel; public Header() From 7c9c9f1ce1f062f667ec38e6d2c300332cbfe857 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Aug 2019 17:38:21 +0900 Subject: [PATCH 2178/2854] Simplify caching --- .../Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs | 8 +------- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs index f148170847..68ad0b42b4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs @@ -13,6 +13,7 @@ using osu.Framework.Allocation; namespace osu.Game.Tests.Visual.Multiplayer { + [Cached(typeof(IPreviewTrackOwner))] public class TestSceneMatchBeatmapPanel : MultiplayerTestScene, IPreviewTrackOwner { public override IReadOnlyList RequiredTypes => new[] @@ -48,12 +49,5 @@ namespace osu.Game.Tests.Visual.Multiplayer previewTrackManager.StopAnyPlaying(this); }); } - - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - dependencies.CacheAs(this); - return dependencies; - } } } diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index ea7a713e0e..f3e10db444 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -19,6 +19,7 @@ using PlaylistItem = osu.Game.Online.Multiplayer.PlaylistItem; namespace osu.Game.Screens.Multi.Match { + [Cached(typeof(IPreviewTrackOwner))] public class MatchSubScreen : MultiplayerSubScreen, IPreviewTrackOwner { public override bool DisallowExternalBeatmapRulesetChanges => true; @@ -237,13 +238,6 @@ namespace osu.Game.Screens.Multi.Match } } - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - dependencies.CacheAs(this); - return dependencies; - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From f2e596de1640c4939ada0d6c18d2b71e89295679 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2019 08:54:17 +0000 Subject: [PATCH 2179/2854] Bump ppy.osu.Framework from 2019.809.0 to 2019.813.0 Bumps [ppy.osu.Framework](https://github.com/ppy/osu-framework) from 2019.809.0 to 2019.813.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.809.0...2019.813.0) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b5266fd75d..e149c4338d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 103d89cadc..2bb95d0772 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,7 +105,7 @@ - + From e356cc8d9ef288056745f4b0b1899c0503284ee5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Aug 2019 17:57:16 +0900 Subject: [PATCH 2180/2854] Refactor MatchBeatmapPanel for thread safety --- .../Match/Components/MatchBeatmapPanel.cs | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs index 7939b18e97..7c1fe91393 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs @@ -1,8 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Threading; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Direct; @@ -18,6 +20,7 @@ namespace osu.Game.Screens.Multi.Match.Components [Resolved] private RulesetStore rulesets { get; set; } + private CancellationTokenSource loadCancellation; private GetBeatmapSetRequest request; private DirectGridPanel panel; @@ -29,30 +32,31 @@ namespace osu.Game.Screens.Multi.Match.Components [BackgroundDependencyLoader] private void load() { - CurrentItem.BindValueChanged(item => + CurrentItem.BindValueChanged(item => loadNewPanel(item.NewValue?.Beatmap), true); + } + + private void loadNewPanel(BeatmapInfo beatmap) + { + loadCancellation?.Cancel(); + request?.Cancel(); + + panel?.FadeOut(200); + panel?.Expire(); + panel = null; + + if (beatmap?.OnlineBeatmapID == null) + return; + + loadCancellation = new CancellationTokenSource(); + + request = new GetBeatmapSetRequest(beatmap.OnlineBeatmapID.Value, BeatmapSetLookupType.BeatmapId); + request.Success += res => Schedule(() => { - request?.Cancel(); + panel = new DirectGridPanel(res.ToBeatmapSet(rulesets)); + LoadComponentAsync(panel, AddInternal, loadCancellation.Token); + }); - if (panel != null) - { - panel.FadeOut(200); - panel.Expire(); - panel = null; - } - - var onlineId = item.NewValue?.Beatmap.OnlineBeatmapID; - - if (onlineId.HasValue) - { - request = new GetBeatmapSetRequest(onlineId.Value, BeatmapSetLookupType.BeatmapId); - request.Success += beatmap => - { - panel = new DirectGridPanel(beatmap.ToBeatmapSet(rulesets)); - LoadComponentAsync(panel, AddInternal); - }; - api.Queue(request); - } - }, true); + api.Queue(request); } } } From fe7bc824df6b2eda0b52e48517b37535363f4537 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2019 09:00:20 +0000 Subject: [PATCH 2181/2854] Bump ppy.osu.Framework.NativeLibs from 2019.307.0 to 2019.813.0 Bumps [ppy.osu.Framework.NativeLibs](https://github.com/ppy/osu-framework) from 2019.307.0 to 2019.813.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.307.0...2019.813.0) Signed-off-by: dependabot-preview[bot] --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index 103d89cadc..09b9da142c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -111,6 +111,6 @@ - + From 6b3eeb88fc9dac58b70f60991aceb147aab67895 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2019 09:00:43 +0000 Subject: [PATCH 2182/2854] Bump ppy.osu.Framework.iOS from 2019.809.0 to 2019.813.0 Bumps [ppy.osu.Framework.iOS](https://github.com/ppy/osu-framework) from 2019.809.0 to 2019.813.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.809.0...2019.813.0) Signed-off-by: dependabot-preview[bot] --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index 103d89cadc..d444a5893f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -106,7 +106,7 @@ - + From 91068dc5709585c1c4175acd45fca05f581ff583 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2019 09:01:00 +0000 Subject: [PATCH 2183/2854] Bump ppy.osu.Framework.Android from 2019.809.0 to 2019.813.0 Bumps [ppy.osu.Framework.Android](https://github.com/ppy/osu-framework) from 2019.809.0 to 2019.813.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.809.0...2019.813.0) Signed-off-by: dependabot-preview[bot] --- osu.Android.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android.props b/osu.Android.props index 721d341c08..85741fcf84 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -63,6 +63,6 @@ - + From 5963f7d9147e7ead009611f4b90b27e67506433b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Aug 2019 19:52:40 +0900 Subject: [PATCH 2184/2854] Update comment --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 8073200c47..b70072a222 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -127,7 +127,7 @@ namespace osu.Game.Input.Bindings [Description("Quick exit (Hold)")] QuickExit, - // Game-wide beatmap jukebox keybindings + // Game-wide beatmap msi ccotolle keybindings [Description("Next track")] MusicNext, From 2cd0015735fa28996b2c374a3d632e76a019c503 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Aug 2019 23:27:27 +0900 Subject: [PATCH 2185/2854] Attempt to appease apple with some plist keys --- osu.iOS/Info.plist | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.iOS/Info.plist b/osu.iOS/Info.plist index 0775d1522d..a118b329aa 100644 --- a/osu.iOS/Info.plist +++ b/osu.iOS/Info.plist @@ -31,6 +31,10 @@ UIStatusBarHidden + NSCameraUsageDescription + We don't really use the camera. + NSMicrophoneUsageDescription + We don't really use the microphone. UISupportedInterfaceOrientations UIInterfaceOrientationPortrait From b6bc84af2c886f964c5aafada6cd82c2065a3bee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Aug 2019 10:52:26 +0900 Subject: [PATCH 2186/2854] Fix chat context menus displaying out-of-bounds --- osu.Game/Overlays/Chat/DrawableChannel.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 8d56e250fc..928201581a 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -32,16 +32,16 @@ namespace osu.Game.Overlays.Chat { Children = new Drawable[] { - scroll = new OsuScrollContainer + new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, - // Some chat lines have effects that slightly protrude to the bottom, - // which we do not want to mask away, hence the padding. - Padding = new MarginPadding { Bottom = 5 }, - Child = new OsuContextMenuContainer + Masking = true, + Child = scroll = new OsuScrollContainer { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.Both, + // Some chat lines have effects that slightly protrude to the bottom, + // which we do not want to mask away, hence the padding. + Padding = new MarginPadding { Bottom = 5 }, Child = ChatLineFlow = new ChatLineContainer { Padding = new MarginPadding { Left = 20, Right = 20 }, From 52f42ddadeed12febc7e23897bd243b42448d982 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Aug 2019 10:53:47 +0900 Subject: [PATCH 2187/2854] Use child instead of children --- osu.Game/Overlays/Chat/DrawableChannel.cs | 31 ++++++++++------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 928201581a..f831266b1b 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -30,27 +30,24 @@ namespace osu.Game.Overlays.Chat [BackgroundDependencyLoader] private void load() { - Children = new Drawable[] + Child = new OsuContextMenuContainer { - new OsuContextMenuContainer + RelativeSizeAxes = Axes.Both, + Masking = true, + Child = scroll = new OsuScrollContainer { RelativeSizeAxes = Axes.Both, - Masking = true, - Child = scroll = new OsuScrollContainer + // Some chat lines have effects that slightly protrude to the bottom, + // which we do not want to mask away, hence the padding. + Padding = new MarginPadding { Bottom = 5 }, + Child = ChatLineFlow = new ChatLineContainer { - RelativeSizeAxes = Axes.Both, - // Some chat lines have effects that slightly protrude to the bottom, - // which we do not want to mask away, hence the padding. - Padding = new MarginPadding { Bottom = 5 }, - Child = ChatLineFlow = new ChatLineContainer - { - Padding = new MarginPadding { Left = 20, Right = 20 }, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - } - }, - } + Padding = new MarginPadding { Left = 20, Right = 20 }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + } + }, }; newMessagesArrived(Channel.Messages); From 3a79a4149a430e9f5dec5ab7a47bf425a9cd7cb0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 14 Aug 2019 15:19:21 +0900 Subject: [PATCH 2188/2854] Disable music controls when beatmap is disabled --- osu.Game/Overlays/MusicController.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 91220907a0..f6208c46cb 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -231,6 +231,9 @@ namespace osu.Game.Overlays public bool OnPressed(GlobalAction action) { + if (beatmap.Disabled) + return false; + switch (action) { case GlobalAction.MusicPlay: From 840a142e98a592ef93d0d8ad1dfbf3796005a039 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2019 12:16:32 +0000 Subject: [PATCH 2189/2854] Bump ppy.osu.Framework.iOS from 2019.813.0 to 2019.814.0 Bumps [ppy.osu.Framework.iOS](https://github.com/ppy/osu-framework) from 2019.813.0 to 2019.814.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.813.0...2019.814.0) Signed-off-by: dependabot-preview[bot] --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index 9ea5081658..d50f23c2c2 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -119,7 +119,7 @@ - + From e36e2a899ae1602dc303246eaae217abfd700421 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2019 12:16:53 +0000 Subject: [PATCH 2190/2854] Bump ppy.osu.Framework.Android from 2019.813.0 to 2019.814.0 Bumps [ppy.osu.Framework.Android](https://github.com/ppy/osu-framework) from 2019.813.0 to 2019.814.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.813.0...2019.814.0) Signed-off-by: dependabot-preview[bot] --- osu.Android.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android.props b/osu.Android.props index 065c66ebba..7bc60ef884 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -61,6 +61,6 @@ - + From 3379672a43a9f1f0536e85db3887b51653b12a22 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2019 12:17:17 +0000 Subject: [PATCH 2191/2854] Bump ppy.osu.Framework from 2019.813.0 to 2019.814.0 Bumps [ppy.osu.Framework](https://github.com/ppy/osu-framework) from 2019.813.0 to 2019.814.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.813.0...2019.814.0) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e149c4338d..f5e4d4b1fb 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 9ea5081658..e831fdc7cb 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -118,7 +118,7 @@ - + From f849b4ce54e4b8e75a8286192b4dfbae24f80e25 Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Wed, 14 Aug 2019 19:49:32 +0200 Subject: [PATCH 2192/2854] Make autoplay unpress sooner, if needed --- .../Replays/TaikoAutoGenerator.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index 422ba748e3..60fd88882e 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -113,14 +113,21 @@ namespace osu.Game.Rulesets.Taiko.Replays else throw new InvalidOperationException("Unknown hit object type."); - Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY)); - if (i < Beatmap.HitObjects.Count - 1) { - double waitTime = Beatmap.HitObjects[i + 1].StartTime - 1000; + var nextHitObject = Beatmap.HitObjects[i + 1]; + + if (!(nextHitObject.StartTime < endTime + KEY_UP_DELAY)) + Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY)); + + double waitTime = nextHitObject.StartTime - 1000; if (waitTime > endTime) Frames.Add(new TaikoReplayFrame(waitTime)); } + else + { + Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY)); + } hitButton = !hitButton; } From e567e81981d6f940a9de97d68a3bab496e6d29cc Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Wed, 14 Aug 2019 20:10:52 +0200 Subject: [PATCH 2193/2854] Refactoring --- osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index 60fd88882e..014511d090 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Taiko.Replays { var nextHitObject = Beatmap.HitObjects[i + 1]; - if (!(nextHitObject.StartTime < endTime + KEY_UP_DELAY)) + if (nextHitObject.StartTime > endTime + KEY_UP_DELAY) Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY)); double waitTime = nextHitObject.StartTime - 1000; From ba539abac77484c3dd5103333e84231a46d81cf2 Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Wed, 14 Aug 2019 21:05:24 +0200 Subject: [PATCH 2194/2854] Refactoring --- .../Replays/TaikoAutoGenerator.cs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index 014511d090..a46ecab6f0 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -113,21 +113,23 @@ namespace osu.Game.Rulesets.Taiko.Replays else throw new InvalidOperationException("Unknown hit object type."); + TaikoHitObject nextHitObject; if (i < Beatmap.HitObjects.Count - 1) + nextHitObject = Beatmap.HitObjects[i + 1]; + else + nextHitObject = null; + + bool canDelayKeyUp = nextHitObject != null && nextHitObject.StartTime > endTime + KEY_UP_DELAY; + + if (canDelayKeyUp) + Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY)); + + if (nextHitObject != null) { - var nextHitObject = Beatmap.HitObjects[i + 1]; - - if (nextHitObject.StartTime > endTime + KEY_UP_DELAY) - Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY)); - double waitTime = nextHitObject.StartTime - 1000; if (waitTime > endTime) Frames.Add(new TaikoReplayFrame(waitTime)); } - else - { - Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY)); - } hitButton = !hitButton; } From 07e7e1187c980f9e2962c013203869d3b4c774bc Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 15 Aug 2019 05:30:35 +0300 Subject: [PATCH 2195/2854] Add adjustment function in SkinnableSound --- osu.Game/Skinning/SkinnableSound.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 8e2b5cec98..205f25bf9d 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -37,6 +37,8 @@ namespace osu.Game.Skinning public void Play() => channels?.ForEach(c => c.Play()); + public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => channels?.ForEach(c => c.AddAdjustment(type, adjustBindable)); + public override bool IsPresent => false; // We don't need to receive updates. protected override void SkinChanged(ISkinSource skin, bool allowFallback) From cfa569b226ab742f9527615852a64373a5fd7cb6 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 15 Aug 2019 05:35:47 +0300 Subject: [PATCH 2196/2854] Add looping field and fix build --- osu.Game/Skinning/SkinnableSound.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 205f25bf9d..f9dd39d04d 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Audio; @@ -35,6 +36,8 @@ namespace osu.Game.Skinning this.audio = audio; } + public bool Looping; + public void Play() => channels?.ForEach(c => c.Play()); public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => channels?.ForEach(c => c.AddAdjustment(type, adjustBindable)); @@ -60,6 +63,7 @@ namespace osu.Game.Skinning if (ch == null) continue; + ch.Looping = Looping; ch.Volume.Value = info.Volume / 100.0; return ch; } From f355cff8bcc9c048f793d2032ebb719be89a6358 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Aug 2019 12:11:54 +0900 Subject: [PATCH 2197/2854] Apply reviews --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 167 +++++++++--------- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 2 +- osu.Game/Overlays/Direct/DirectPanel.cs | 2 +- .../Screens/Multi/Components/ModeTypeInfo.cs | 2 +- .../Carousel/DrawableCarouselBeatmap.cs | 2 +- .../Carousel/DrawableCarouselBeatmapSet.cs | 2 +- 6 files changed, 88 insertions(+), 89 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index f5c8d0d029..b29303ad1e 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -23,7 +23,7 @@ namespace osu.Game.Beatmaps.Drawables { private readonly RulesetInfo ruleset; - public DifficultyIcon(BeatmapInfo beatmap, RulesetInfo ruleset = null, Boolean shouldShowTooltip = false) + public DifficultyIcon(BeatmapInfo beatmap, RulesetInfo ruleset = null, bool shouldShowTooltip = true) : base(beatmap) { if (beatmap == null) @@ -39,89 +39,6 @@ namespace osu.Game.Beatmaps.Drawables public ITooltip GetCustomTooltip() => new DifficultyIconTooltip(AccentColour); - public class DifficultyIconTooltip : VisibilityContainer, ITooltip - { - private readonly OsuSpriteText difficultyName, starRating; - private readonly Box background; - - public string TooltipText { get; set; } - - public DifficultyIconTooltip(Color4 accentColour) - { - AutoSizeAxes = Axes.Both; - Masking = true; - CornerRadius = 5; - - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Padding = new MarginPadding(10), - Children = new Drawable[] - { - difficultyName = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold), - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - starRating = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular), - Colour = accentColour - }, - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Margin = new MarginPadding { Left = 4 }, - Icon = FontAwesome.Solid.Star, - Size = new Vector2(12), - Colour = accentColour, - }, - } - } - } - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - background.Colour = colours.GreyCarmineDark; - } - - public void Refresh() - { - var info = TooltipText.Split('$'); - difficultyName.Text = info[0]; - starRating.Text = info[1]; - } - - public void Move(Vector2 pos) => Position = pos; - - protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); - - protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); - } - [BackgroundDependencyLoader] private void load() { @@ -156,5 +73,87 @@ namespace osu.Game.Beatmaps.Drawables } }; } + + private class DifficultyIconTooltip : VisibilityContainer, ITooltip + { + private readonly OsuSpriteText difficultyName, starRating; + private readonly Box background; + + public string TooltipText { get; set; } + + public DifficultyIconTooltip(Color4 difficultyColour) + { + AutoSizeAxes = Axes.Both; + Masking = true; + CornerRadius = 5; + + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Padding = new MarginPadding(10), + Children = new Drawable[] + { + difficultyName = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold), + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Horizontal, + Colour = difficultyColour, + Children = new Drawable[] + { + starRating = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular), + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Margin = new MarginPadding { Left = 4 }, + Icon = FontAwesome.Solid.Star, + Size = new Vector2(12), + }, + } + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.GreyCarmineDark; + } + + public void Refresh() + { + var info = TooltipText.Split('$'); + difficultyName.Text = info[0]; + starRating.Text = info[1]; + } + + public void Move(Vector2 pos) => Position = pos; + + protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); + + protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); + } } } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index baf702eebc..104315f1c2 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -234,7 +234,7 @@ namespace osu.Game.Overlays.BeatmapSet Colour = Color4.Black.Opacity(0.5f), }, }, - icon = new DifficultyIcon(beatmap) + icon = new DifficultyIcon(beatmap, shouldShowTooltip: false) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 41c565090f..8199d80528 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -143,7 +143,7 @@ namespace osu.Game.Overlays.Direct var icons = new List(); foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty)) - icons.Add(new DifficultyIcon(b, null, true)); + icons.Add(new DifficultyIcon(b)); return icons; } diff --git a/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs index 434c5c443b..6080458aec 100644 --- a/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs +++ b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Multi.Components if (item?.Beatmap != null) { drawableRuleset.FadeIn(transition_duration); - drawableRuleset.Child = new DifficultyIcon(item.Beatmap, item.Ruleset, true) { Size = new Vector2(height) }; + drawableRuleset.Child = new DifficultyIcon(item.Beatmap, item.Ruleset) { Size = new Vector2(height) }; } else drawableRuleset.FadeOut(transition_duration); diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 0a20f2aa6d..fba7a328c1 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -82,7 +82,7 @@ namespace osu.Game.Screens.Select.Carousel Origin = Anchor.CentreLeft, Children = new Drawable[] { - new DifficultyIcon(beatmap) + new DifficultyIcon(beatmap, shouldShowTooltip: false) { Scale = new Vector2(1.8f), }, diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 86ae6a2e58..4ceb82d4cc 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -197,7 +197,7 @@ namespace osu.Game.Screens.Select.Carousel private readonly BindableBool filtered = new BindableBool(); public FilterableDifficultyIcon(CarouselBeatmap item) - : base(item.Beatmap, shouldShowTooltip: true) + : base(item.Beatmap) { filtered.BindTo(item.Filtered); filtered.ValueChanged += isFiltered => Schedule(() => this.FadeTo(isFiltered.NewValue ? 0.1f : 1, 100)); From e73a9c2748ad4e0c8b7c5ef263f7ccad14f4268e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Aug 2019 14:00:12 +0900 Subject: [PATCH 2198/2854] Fix song select context menus displaying off-screen --- osu.Game/Screens/Select/BeatmapCarousel.cs | 26 +++++++++++++--------- osu.Game/Screens/Select/SongSelect.cs | 1 - 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 7366fa8c17..6fda81e47d 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -24,7 +24,7 @@ using osu.Game.Screens.Select.Carousel; namespace osu.Game.Screens.Select { - public class BeatmapCarousel : OsuScrollContainer + public class BeatmapCarousel : CompositeDrawable { private const float bleed_top = FilterControl.HEIGHT; private const float bleed_bottom = Footer.HEIGHT; @@ -61,6 +61,8 @@ namespace osu.Game.Screens.Select /// public bool BeatmapSetsLoaded { get; private set; } + private readonly OsuScrollContainer scroll; + private IEnumerable beatmapSets => root.Children.OfType(); public IEnumerable BeatmapSets @@ -110,13 +112,17 @@ namespace osu.Game.Screens.Select public BeatmapCarousel() { root = new CarouselRoot(this); - Child = new OsuContextMenuContainer + InternalChild = new OsuContextMenuContainer { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = scrollableContent = new Container + RelativeSizeAxes = Axes.Both, + Child = scroll = new OsuScrollContainer { - RelativeSizeAxes = Axes.X, + Masking = false, + RelativeSizeAxes = Axes.Both, + Child = scrollableContent = new Container + { + RelativeSizeAxes = Axes.X, + } } }; } @@ -127,7 +133,7 @@ namespace osu.Game.Screens.Select config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm); config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled); - RightClickScrollingEnabled.ValueChanged += enabled => RightMouseScrollbar = enabled.NewValue; + RightClickScrollingEnabled.ValueChanged += enabled => scroll.RightMouseScrollbar = enabled.NewValue; RightClickScrollingEnabled.TriggerChange(); loadBeatmapSets(beatmaps.GetAllUsableBeatmapSetsEnumerable()); @@ -351,12 +357,12 @@ namespace osu.Game.Screens.Select /// /// The position of the lower visible bound with respect to the current scroll position. /// - private float visibleBottomBound => Current + DrawHeight + bleed_bottom; + private float visibleBottomBound => scroll.Current + DrawHeight + bleed_bottom; /// /// The position of the upper visible bound with respect to the current scroll position. /// - private float visibleUpperBound => Current - bleed_top; + private float visibleUpperBound => scroll.Current - bleed_top; public void FlushPendingFilterOperations() { @@ -628,7 +634,7 @@ namespace osu.Game.Screens.Select private void updateScrollPosition() { - if (scrollTarget != null) ScrollTo(scrollTarget.Value); + if (scrollTarget != null) scroll.ScrollTo(scrollTarget.Value); scrollPositionCache.Validate(); } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 7dd934f91a..8340814db9 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -157,7 +157,6 @@ namespace osu.Game.Screens.Select }, Child = Carousel = new BeatmapCarousel { - Masking = false, RelativeSizeAxes = Axes.Both, Size = new Vector2(1 - wedged_container_size.X, 1), Anchor = Anchor.CentreRight, From f7a92487ef548c060b8af695efe9d49fe76a9b6b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Aug 2019 14:09:30 +0900 Subject: [PATCH 2199/2854] Fix checkbox sounds playing too often --- .../Graphics/UserInterface/OsuCheckbox.cs | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index 47324ee646..6593531099 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -72,17 +72,11 @@ namespace osu.Game.Graphics.UserInterface Current.DisabledChanged += disabled => labelText.Alpha = Nub.Alpha = disabled ? 0.3f : 1; } - protected override void LoadComplete() + [BackgroundDependencyLoader] + private void load(AudioManager audio) { - base.LoadComplete(); - - Current.ValueChanged += enabled => - { - if (enabled.NewValue) - sampleChecked?.Play(); - else - sampleUnchecked?.Play(); - }; + sampleChecked = audio.Samples.Get(@"UI/check-on"); + sampleUnchecked = audio.Samples.Get(@"UI/check-off"); } protected override bool OnHover(HoverEvent e) @@ -99,11 +93,13 @@ namespace osu.Game.Graphics.UserInterface base.OnHoverLost(e); } - [BackgroundDependencyLoader] - private void load(AudioManager audio) + protected override void OnUserChange(bool value) { - sampleChecked = audio.Samples.Get(@"UI/check-on"); - sampleUnchecked = audio.Samples.Get(@"UI/check-off"); + base.OnUserChange(value); + if (value) + sampleChecked?.Play(); + else + sampleUnchecked?.Play(); } } } From 9e13a6aeae1a2a0259f926cf3c0d4db284d04315 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Aug 2019 14:32:31 +0900 Subject: [PATCH 2200/2854] Fix newlines being considered in user profile content --- osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index e7f7c2f490..691e18e6bf 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -138,6 +138,9 @@ namespace osu.Game.Overlays.Profile.Header private void tryAddInfo(IconUsage icon, string content, string link = null) { + // newlines could be contained in API returned user content. + content = content?.Replace("\n", " "); + if (string.IsNullOrEmpty(content)) return; bottomLinkContainer.AddIcon(icon, text => From 8f638879728bff8c8812253ba24476d7721e176d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Aug 2019 14:38:49 +0900 Subject: [PATCH 2201/2854] Make use of existing null check --- osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index 691e18e6bf..158641d816 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -138,11 +138,11 @@ namespace osu.Game.Overlays.Profile.Header private void tryAddInfo(IconUsage icon, string content, string link = null) { - // newlines could be contained in API returned user content. - content = content?.Replace("\n", " "); - if (string.IsNullOrEmpty(content)) return; + // newlines could be contained in API returned user content. + content = content.Replace("\n", " "); + bottomLinkContainer.AddIcon(icon, text => { text.Font = text.Font.With(size: 10); From ef5ed915e5b51781117bcb514ffe8a2751a3ac86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Aug 2019 17:14:00 +0900 Subject: [PATCH 2202/2854] Reduce delay for hold-to-confirm controls --- .../Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs | 7 ++++++- osu.Game/Graphics/Containers/HoldToConfirmContainer.cs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs index 7e6cf1285e..a017418e3a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using osu.Framework.Graphics; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Screens.Menu; @@ -12,7 +13,11 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneHoldToConfirmOverlay : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] { typeof(ExitConfirmOverlay) }; + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ExitConfirmOverlay), + typeof(HoldToConfirmContainer), + }; public TestSceneHoldToConfirmOverlay() { diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs index cda5e150de..18a4241a79 100644 --- a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs +++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs @@ -12,7 +12,7 @@ namespace osu.Game.Graphics.Containers { public Action Action; - private const int activate_delay = 400; + private const int activate_delay = 200; private const int fadeout_delay = 200; private bool fired; From 8c67f58e2d1179f5403e94933dea70df65d90e75 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 15 Aug 2019 18:25:31 +0900 Subject: [PATCH 2203/2854] Disable frame-stable playback in the editor --- osu.Game/Rulesets/Edit/DrawableEditRuleset.cs | 6 ++++++ osu.Game/Rulesets/UI/DrawableRuleset.cs | 9 +++++++++ osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 8 ++++++++ 3 files changed, 23 insertions(+) diff --git a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs b/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs index 2200caeb20..e85ebb5f3a 100644 --- a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs +++ b/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; @@ -57,7 +58,12 @@ namespace osu.Game.Rulesets.Edit this.drawableRuleset = drawableRuleset; InternalChild = drawableRuleset; + } + [BackgroundDependencyLoader] + private void load() + { + drawableRuleset.FrameStablePlayback = false; Playfield.DisplayJudgements.Value = false; } diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index ac81fdc719..eb14bd1f24 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -62,6 +62,15 @@ namespace osu.Game.Rulesets.UI public override GameplayClock FrameStableClock => frameStabilityContainer.GameplayClock; + /// + /// Whether to enable frame-stable playback. + /// + internal bool FrameStablePlayback + { + get => frameStabilityContainer.FrameStablePlayback; + set => frameStabilityContainer.FrameStablePlayback = value; + } + /// /// Invoked when a has been applied by a . /// diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index 1cc56fff8b..7c143aa158 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -24,6 +24,11 @@ namespace osu.Game.Rulesets.UI ///

public List Scores { get; set; } + public DifficultyRating DifficultyRating + { + get + { + var rating = StarDifficulty; + + if (rating < 2.0) return DifficultyRating.Easy; + if (rating < 2.7) return DifficultyRating.Normal; + if (rating < 4.0) return DifficultyRating.Hard; + if (rating < 5.3) return DifficultyRating.Insane; + if (rating < 6.5) return DifficultyRating.Expert; + + return DifficultyRating.ExpertPlus; + } + } + public override string ToString() => $"{Metadata} [{Version}]".Trim(); public bool Equals(BeatmapInfo other) diff --git a/osu.Game/Beatmaps/DifficultyRating.cs b/osu.Game/Beatmaps/DifficultyRating.cs new file mode 100644 index 0000000000..f0ee0ad705 --- /dev/null +++ b/osu.Game/Beatmaps/DifficultyRating.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Beatmaps +{ + public enum DifficultyRating + { + Easy, + Normal, + Hard, + Insane, + Expert, + ExpertPlus + } +} diff --git a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs deleted file mode 100644 index 26ffcca1ec..0000000000 --- a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osuTK.Graphics; - -namespace osu.Game.Beatmaps.Drawables -{ - public abstract class DifficultyColouredContainer : Container, IHasAccentColour - { - public Color4 AccentColour { get; set; } - - private readonly BeatmapInfo beatmap; - private OsuColour palette; - - protected DifficultyColouredContainer(BeatmapInfo beatmap) - { - this.beatmap = beatmap; - } - - [BackgroundDependencyLoader] - private void load(OsuColour palette) - { - if (palette == null) - throw new ArgumentNullException(nameof(palette)); - - this.palette = palette; - AccentColour = getColour(beatmap); - } - - private enum DifficultyRating - { - Easy, - Normal, - Hard, - Insane, - Expert, - ExpertPlus - } - - private DifficultyRating getDifficultyRating(BeatmapInfo beatmap) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - - var rating = beatmap.StarDifficulty; - - if (rating < 2.0) return DifficultyRating.Easy; - if (rating < 2.7) return DifficultyRating.Normal; - if (rating < 4.0) return DifficultyRating.Hard; - if (rating < 5.3) return DifficultyRating.Insane; - if (rating < 6.5) return DifficultyRating.Expert; - - return DifficultyRating.ExpertPlus; - } - - private Color4 getColour(BeatmapInfo beatmap) - { - switch (getDifficultyRating(beatmap)) - { - case DifficultyRating.Easy: - return palette.Green; - - default: - case DifficultyRating.Normal: - return palette.Blue; - - case DifficultyRating.Hard: - return palette.Yellow; - - case DifficultyRating.Insane: - return palette.Pink; - - case DifficultyRating.Expert: - return palette.Purple; - - case DifficultyRating.ExpertPlus: - return palette.Gray0; - } - } - } -} diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 0a0ad28fdf..2b0a0a5ac1 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Rulesets; using osuTK; @@ -16,23 +17,25 @@ using osuTK.Graphics; namespace osu.Game.Beatmaps.Drawables { - public class DifficultyIcon : DifficultyColouredContainer + public class DifficultyIcon : Container { + private readonly BeatmapInfo beatmap; private readonly RulesetInfo ruleset; public DifficultyIcon(BeatmapInfo beatmap, RulesetInfo ruleset = null) - : base(beatmap) { if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); + this.beatmap = beatmap; + this.ruleset = ruleset ?? beatmap.Ruleset; Size = new Vector2(20); } [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { Children = new Drawable[] { @@ -52,7 +55,7 @@ namespace osu.Game.Beatmaps.Drawables Child = new Box { RelativeSizeAxes = Axes.Both, - Colour = AccentColour, + Colour = colours.ForDifficultyRating(beatmap.DifficultyRating), }, }, new ConstrainedIconContainer diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 63ec24f84f..af66f57f14 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Game.Beatmaps; using osuTK.Graphics; namespace osu.Game.Graphics @@ -37,6 +38,31 @@ namespace osu.Game.Graphics } } + public Color4 ForDifficultyRating(DifficultyRating difficulty) + { + switch (difficulty) + { + case DifficultyRating.Easy: + return Green; + + default: + case DifficultyRating.Normal: + return Blue; + + case DifficultyRating.Hard: + return Yellow; + + case DifficultyRating.Insane: + return Pink; + + case DifficultyRating.Expert: + return Purple; + + case DifficultyRating.ExpertPlus: + return Gray0; + } + } + // See https://github.com/ppy/osu-web/blob/master/resources/assets/less/colors.less public readonly Color4 PurpleLighter = FromHex(@"eeeeff"); public readonly Color4 PurpleLight = FromHex(@"aa88ff"); diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index a9e4eaa9b3..5f6307e3b4 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -402,31 +402,35 @@ namespace osu.Game.Screens.Select } } - private class DifficultyColourBar : DifficultyColouredContainer + private class DifficultyColourBar : Container { + private readonly BeatmapInfo beatmap; + public DifficultyColourBar(BeatmapInfo beatmap) - : base(beatmap) { + this.beatmap = beatmap; } [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { const float full_opacity_ratio = 0.7f; + var difficultyColour = colours.ForDifficultyRating(beatmap.DifficultyRating); + Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, - Colour = AccentColour, + Colour = difficultyColour, Width = full_opacity_ratio, }, new Box { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Both, - Colour = AccentColour, + Colour = difficultyColour, Alpha = 0.5f, X = full_opacity_ratio, Width = 1 - full_opacity_ratio, From 50046d5f69bd206f0480271758b0488b3c97f61a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Aug 2019 15:20:27 +0900 Subject: [PATCH 2223/2854] Use new tooltip style --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 9b265de45b..37fbc617cc 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -32,14 +32,17 @@ namespace osu.Game.Beatmaps.Drawables this.beatmap = beatmap; this.ruleset = ruleset ?? beatmap.Ruleset; - TooltipText = shouldShowTooltip ? $"{beatmap.Version}${beatmap.StarDifficulty:0.##}" : String.Empty; + if (shouldShowTooltip) + TooltipContent = beatmap; Size = new Vector2(20); } public string TooltipText { get; set; } - public ITooltip GetCustomTooltip() => new DifficultyIconTooltip(AccentColour); + public ITooltip GetCustomTooltip() => new DifficultyIconTooltip(); + + public object TooltipContent { get; set; } [BackgroundDependencyLoader] private void load(OsuColour colours) @@ -81,9 +84,14 @@ namespace osu.Game.Beatmaps.Drawables private readonly OsuSpriteText difficultyName, starRating; private readonly Box background; - public string TooltipText { get; set; } + private readonly FillFlowContainer difficultyFlow; - public DifficultyIconTooltip(Color4 difficultyColour) + public string TooltipText + { + set { } + } + + public DifficultyIconTooltip() { AutoSizeAxes = Axes.Both; Masking = true; @@ -108,13 +116,12 @@ namespace osu.Game.Beatmaps.Drawables Origin = Anchor.Centre, Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold), }, - new FillFlowContainer + difficultyFlow = new FillFlowContainer { AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, Direction = FillDirection.Horizontal, - Colour = difficultyColour, Children = new Drawable[] { starRating = new OsuSpriteText @@ -138,17 +145,29 @@ namespace osu.Game.Beatmaps.Drawables }; } + private OsuColour colours; + [BackgroundDependencyLoader] private void load(OsuColour colours) { + this.colours = colours; background.Colour = colours.GreyCarmineDark; } + public bool SetContent(object content) + { + if (!(content is BeatmapInfo beatmap)) + return false; + + difficultyName.Text = beatmap.Version; + starRating.Text = $"{beatmap.StarDifficulty:0.##}"; + difficultyFlow.Colour = colours.ForDifficultyRating(beatmap.DifficultyRating); + + return true; + } + public void Refresh() { - var info = TooltipText.Split('$'); - difficultyName.Text = info[0]; - starRating.Text = info[1]; } public void Move(Vector2 pos) => Position = pos; From 097763bb1ca9d7215b9ad7ba9bbe14554f9f24df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Aug 2019 15:29:39 +0900 Subject: [PATCH 2224/2854] Add auto size duration --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 37fbc617cc..962cb33a83 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -106,6 +106,8 @@ namespace osu.Game.Beatmaps.Drawables new FillFlowContainer { AutoSizeAxes = Axes.Both, + AutoSizeDuration = 200, + AutoSizeEasing = Easing.OutQuint, Direction = FillDirection.Vertical, Padding = new MarginPadding(10), Children = new Drawable[] From 87dc6499faabccc71e3062a2365446d31945a281 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Aug 2019 15:34:02 +0900 Subject: [PATCH 2225/2854] Fix json decoding being a bit too eager to consume --- osu.Game/Beatmaps/BeatmapInfo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 700f981088..198046df4f 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -129,6 +129,7 @@ namespace osu.Game.Beatmaps ///
public List Scores { get; set; } + [JsonIgnore] public DifficultyRating DifficultyRating { get From f7024b513efeabd0a19b97716725770ab86138bd Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 18 Aug 2019 01:43:43 +0300 Subject: [PATCH 2226/2854] Visual improvements --- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 83 +++++++++++--------- 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index 4cac73c975..ad72063313 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -14,14 +14,15 @@ using osu.Game.Beatmaps; using osu.Framework.Bindables; using osu.Framework.Allocation; using osu.Framework.Graphics.Colour; +using osu.Game.Graphics; using osu.Framework.Extensions.Color4Extensions; namespace osu.Game.Screens.Play.HUD { public class HitErrorDisplay : Container { - private const int bar_width = 4; - private const int bar_height = 250; + private const int bar_width = 3; + private const int bar_height = 200; private const int spacing = 3; public HitWindows HitWindows { get; set; } @@ -31,6 +32,7 @@ namespace osu.Game.Screens.Play.HUD private readonly bool mirrored; private readonly SpriteIcon arrow; + private readonly FillFlowContainer bar; private readonly List judgementOffsets = new List(); public HitErrorDisplay(bool mirrored = false) @@ -41,43 +43,10 @@ namespace osu.Game.Screens.Play.HUD Children = new Drawable[] { - new FillFlowContainer + bar = new FillFlowContainer { RelativeSizeAxes = Axes.Both, Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0), Color4.Orange), - Height = 0.3f - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Green, - Height = 0.15f - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Blue, - Height = 0.1f - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Green, - Height = 0.15f - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.Orange, Color4.Black.Opacity(0)), - Height = 0.3f - } - } }, arrow = new SpriteIcon { @@ -86,11 +55,49 @@ namespace osu.Game.Screens.Play.HUD X = mirrored ? -spacing : spacing, RelativePositionAxes = Axes.Y, Icon = mirrored ? FontAwesome.Solid.ChevronRight : FontAwesome.Solid.ChevronLeft, - Size = new Vector2(10), + Size = new Vector2(8), } }; } + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + bar.AddRange(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(colours.Yellow.Opacity(0), colours.Yellow), + Height = 0.3f + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Green, + Height = 0.15f + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.BlueLight, + Height = 0.1f + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Green, + Height = 0.15f + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(colours.Yellow, colours.Yellow.Opacity(0)), + Height = 0.3f + } + }); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -117,7 +124,7 @@ namespace osu.Game.Screens.Play.HUD Anchor = mirrored ? Anchor.CentreRight : Anchor.CentreLeft, Origin = mirrored ? Anchor.CentreLeft : Anchor.CentreRight, Masking = true, - Size = new Vector2(10, 2), + Size = new Vector2(8, 2), RelativePositionAxes = Axes.Y, Y = getRelativeJudgementPosition(judgement.TimeOffset), X = mirrored ? spacing : -spacing, From 906984ad952f5df2b3c6becf66c30339af2cef36 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 18 Aug 2019 02:49:07 +0300 Subject: [PATCH 2227/2854] Fix the math --- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 35 +++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index ad72063313..6ef102e575 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -16,6 +16,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Colour; using osu.Game.Graphics; using osu.Framework.Extensions.Color4Extensions; +using System.Linq; namespace osu.Game.Screens.Play.HUD { @@ -30,6 +31,9 @@ namespace osu.Game.Screens.Play.HUD [Resolved] private Bindable beatmap { get; set; } + [Resolved] + private OsuColour colours { get; set; } + private readonly bool mirrored; private readonly SpriteIcon arrow; private readonly FillFlowContainer bar; @@ -60,50 +64,46 @@ namespace osu.Game.Screens.Play.HUD }; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) + protected override void LoadComplete() { + base.LoadComplete(); + HitWindows.SetDifficulty(beatmap.Value.BeatmapInfo.BaseDifficulty.OverallDifficulty); + bar.AddRange(new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(colours.Yellow.Opacity(0), colours.Yellow), - Height = 0.3f + Height = (float)((HitWindows.Meh - HitWindows.Good) / (HitWindows.Meh * 2)) }, new Box { RelativeSizeAxes = Axes.Both, Colour = colours.Green, - Height = 0.15f + Height = (float)((HitWindows.Good - HitWindows.Great) / (HitWindows.Meh * 2)) }, new Box { RelativeSizeAxes = Axes.Both, Colour = colours.BlueLight, - Height = 0.1f + Height = (float)(HitWindows.Great / HitWindows.Meh) }, new Box { RelativeSizeAxes = Axes.Both, Colour = colours.Green, - Height = 0.15f + Height = (float)((HitWindows.Good - HitWindows.Great) / (HitWindows.Meh * 2)) }, new Box { RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(colours.Yellow, colours.Yellow.Opacity(0)), - Height = 0.3f + Height = (float)((HitWindows.Meh - HitWindows.Good) / (HitWindows.Meh * 2)) } }); } - protected override void LoadComplete() - { - base.LoadComplete(); - HitWindows.SetDifficulty(beatmap.Value.BeatmapInfo.BaseDifficulty.OverallDifficulty); - } - public void OnNewJudgement(JudgementResult judgement) { if (!judgement.IsHit) @@ -135,7 +135,7 @@ namespace osu.Game.Screens.Play.HUD } }; - private float getRelativeJudgementPosition(double value) => (float)(value / HitWindows.Miss); + private float getRelativeJudgementPosition(double value) => (float)(value / HitWindows.Meh); private double calculateArrowPosition(JudgementResult judgement) { @@ -144,12 +144,7 @@ namespace osu.Game.Screens.Play.HUD judgementOffsets.Add(judgement.TimeOffset); - double offsets = 0; - - foreach (var offset in judgementOffsets) - offsets += offset; - - return offsets / judgementOffsets.Count; + return judgementOffsets.Average(); } } } From 50133ba8636159d4b3d79eda20ced928a4fdbf68 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 18 Aug 2019 02:57:12 +0300 Subject: [PATCH 2228/2854] naming adjustments --- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index 6ef102e575..1dd77469ca 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -22,6 +22,7 @@ namespace osu.Game.Screens.Play.HUD { public class HitErrorDisplay : Container { + private const int stored_judgements_amount = 5; private const int bar_width = 3; private const int bar_height = 200; private const int spacing = 3; @@ -104,19 +105,19 @@ namespace osu.Game.Screens.Play.HUD }); } - public void OnNewJudgement(JudgementResult judgement) + public void OnNewJudgement(JudgementResult newJudgement) { - if (!judgement.IsHit) + if (!newJudgement.IsHit) return; Container judgementLine; - Add(judgementLine = CreateJudgementLine(judgement)); + Add(judgementLine = CreateJudgementLine(newJudgement)); judgementLine.FadeOut(10000, Easing.OutQuint); judgementLine.Expire(); - arrow.MoveToY(getRelativeJudgementPosition(calculateArrowPosition(judgement)), 500, Easing.OutQuint); + arrow.MoveToY(getRelativeJudgementPosition(calculateArrowPosition(newJudgement)), 500, Easing.OutQuint); } protected virtual Container CreateJudgementLine(JudgementResult judgement) => new CircularContainer @@ -137,12 +138,12 @@ namespace osu.Game.Screens.Play.HUD private float getRelativeJudgementPosition(double value) => (float)(value / HitWindows.Meh); - private double calculateArrowPosition(JudgementResult judgement) + private double calculateArrowPosition(JudgementResult newJudgement) { - if (judgementOffsets.Count > 5) + if (judgementOffsets.Count > stored_judgements_amount) judgementOffsets.RemoveAt(0); - judgementOffsets.Add(judgement.TimeOffset); + judgementOffsets.Add(newJudgement.TimeOffset); return judgementOffsets.Average(); } From ee5568e5968489b8dbce42023f4db425f28fff2c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 18 Aug 2019 14:43:34 +0300 Subject: [PATCH 2229/2854] Use Queue instead of List for stored Judgements --- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index 1dd77469ca..7bb1d30f22 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play.HUD private readonly bool mirrored; private readonly SpriteIcon arrow; private readonly FillFlowContainer bar; - private readonly List judgementOffsets = new List(); + private readonly Queue judgementOffsets = new Queue(); public HitErrorDisplay(bool mirrored = false) { @@ -117,7 +117,7 @@ namespace osu.Game.Screens.Play.HUD judgementLine.FadeOut(10000, Easing.OutQuint); judgementLine.Expire(); - arrow.MoveToY(getRelativeJudgementPosition(calculateArrowPosition(newJudgement)), 500, Easing.OutQuint); + arrow.MoveToY(calculateArrowPosition(newJudgement), 500, Easing.OutQuint); } protected virtual Container CreateJudgementLine(JudgementResult judgement) => new CircularContainer @@ -138,14 +138,14 @@ namespace osu.Game.Screens.Play.HUD private float getRelativeJudgementPosition(double value) => (float)(value / HitWindows.Meh); - private double calculateArrowPosition(JudgementResult newJudgement) + private float calculateArrowPosition(JudgementResult newJudgement) { if (judgementOffsets.Count > stored_judgements_amount) - judgementOffsets.RemoveAt(0); + judgementOffsets.Dequeue(); - judgementOffsets.Add(newJudgement.TimeOffset); + judgementOffsets.Enqueue(newJudgement.TimeOffset); - return judgementOffsets.Average(); + return getRelativeJudgementPosition(judgementOffsets.Average()); } } } From a59a14c9e6f518ab3c99871c5ddae5617dfefc75 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 18 Aug 2019 15:01:04 +0300 Subject: [PATCH 2230/2854] Add setting to enable/disable hit error visibility --- osu.Game/Configuration/OsuConfigManager.cs | 2 ++ osu.Game/Configuration/ScoreMeterType.cs | 8 +++++-- .../Sections/Gameplay/GeneralSettings.cs | 5 ++++ osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 24 +++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 19f46c1d6a..bffbce2a52 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -79,6 +79,7 @@ namespace osu.Game.Configuration Set(OsuSetting.ShowInterface, true); Set(OsuSetting.ShowHealthDisplayWhenCantFail, true); Set(OsuSetting.KeyOverlay, false); + Set(OsuSetting.ScoreMeter, ScoreMeterType.HitError); Set(OsuSetting.FloatingComments, false); @@ -132,6 +133,7 @@ namespace osu.Game.Configuration BlurLevel, ShowStoryboard, KeyOverlay, + ScoreMeter, FloatingComments, ShowInterface, ShowHealthDisplayWhenCantFail, diff --git a/osu.Game/Configuration/ScoreMeterType.cs b/osu.Game/Configuration/ScoreMeterType.cs index 21a63fb3ed..e78220c9c9 100644 --- a/osu.Game/Configuration/ScoreMeterType.cs +++ b/osu.Game/Configuration/ScoreMeterType.cs @@ -1,12 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.ComponentModel; + namespace osu.Game.Configuration { public enum ScoreMeterType { + [Description("None")] None, - Colour, - Error + + [Description("Hit Error")] + HitError } } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 9142492610..520a8852b3 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -44,6 +44,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay LabelText = "Always show key overlay", Bindable = config.GetBindable(OsuSetting.KeyOverlay) }, + new SettingsEnumDropdown + { + LabelText = "Score meter type", + Bindable = config.GetBindable(OsuSetting.ScoreMeter) + }, new SettingsEnumDropdown { LabelText = "Score display mode", diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index 7bb1d30f22..9a5198b7cc 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -17,6 +17,7 @@ using osu.Framework.Graphics.Colour; using osu.Game.Graphics; using osu.Framework.Extensions.Color4Extensions; using System.Linq; +using osu.Game.Configuration; namespace osu.Game.Screens.Play.HUD { @@ -40,6 +41,8 @@ namespace osu.Game.Screens.Play.HUD private readonly FillFlowContainer bar; private readonly Queue judgementOffsets = new Queue(); + private readonly Bindable type = new Bindable(); + public HitErrorDisplay(bool mirrored = false) { this.mirrored = mirrored; @@ -65,6 +68,12 @@ namespace osu.Game.Screens.Play.HUD }; } + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.BindWith(OsuSetting.ScoreMeter, type); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -103,6 +112,21 @@ namespace osu.Game.Screens.Play.HUD Height = (float)((HitWindows.Meh - HitWindows.Good) / (HitWindows.Meh * 2)) } }); + + type.BindValueChanged(onTypeChanged, true); + } + + private void onTypeChanged(ValueChangedEvent type) + { + switch (type.NewValue) + { + case ScoreMeterType.None: + this.FadeOut(200, Easing.OutQuint); + break; + case ScoreMeterType.HitError: + this.FadeIn(200, Easing.OutQuint); + break; + } } public void OnNewJudgement(JudgementResult newJudgement) From 8740ebd13f2fcac9a46151cf0991031e3edbdf32 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 18 Aug 2019 15:45:18 +0300 Subject: [PATCH 2231/2854] Simplify layout --- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 72 +++++++++++++------- osu.Game/Screens/Play/HUDOverlay.cs | 12 ++-- 2 files changed, 53 insertions(+), 31 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index 9a5198b7cc..1a4d2a45c5 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -21,10 +21,11 @@ using osu.Game.Configuration; namespace osu.Game.Screens.Play.HUD { - public class HitErrorDisplay : Container + public class HitErrorDisplay : CompositeDrawable { private const int stored_judgements_amount = 5; private const int bar_width = 3; + private const int judgement_line_width = 8; private const int bar_height = 200; private const int spacing = 3; @@ -36,36 +37,57 @@ namespace osu.Game.Screens.Play.HUD [Resolved] private OsuColour colours { get; set; } - private readonly bool mirrored; private readonly SpriteIcon arrow; private readonly FillFlowContainer bar; + private readonly Container judgementsContainer; private readonly Queue judgementOffsets = new Queue(); private readonly Bindable type = new Bindable(); - public HitErrorDisplay(bool mirrored = false) + public HitErrorDisplay(bool reversed = false) { - this.mirrored = mirrored; + AutoSizeAxes = Axes.Both; - Size = new Vector2(bar_width, bar_height); - - Children = new Drawable[] + AddInternal(new FillFlowContainer { - bar = new FillFlowContainer + AutoSizeAxes = Axes.X, + Height = bar_height, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(spacing, 0), + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - }, - arrow = new SpriteIcon - { - Anchor = mirrored ? Anchor.CentreLeft : Anchor.CentreRight, - Origin = mirrored ? Anchor.CentreRight : Anchor.CentreLeft, - X = mirrored ? -spacing : spacing, - RelativePositionAxes = Axes.Y, - Icon = mirrored ? FontAwesome.Solid.ChevronRight : FontAwesome.Solid.ChevronLeft, - Size = new Vector2(8), + judgementsContainer = new Container + { + Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Width = judgement_line_width, + RelativeSizeAxes = Axes.Y, + }, + bar = new FillFlowContainer + { + Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Width = bar_width, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + }, + new Container + { + Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Child = arrow = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.Y, + Icon = reversed ? FontAwesome.Solid.ChevronRight : FontAwesome.Solid.ChevronLeft, + Size = new Vector2(8), + } + }, } - }; + }); } [BackgroundDependencyLoader] @@ -136,7 +158,7 @@ namespace osu.Game.Screens.Play.HUD Container judgementLine; - Add(judgementLine = CreateJudgementLine(newJudgement)); + judgementsContainer.Add(judgementLine = CreateJudgementLine(newJudgement)); judgementLine.FadeOut(10000, Easing.OutQuint); judgementLine.Expire(); @@ -146,13 +168,13 @@ namespace osu.Game.Screens.Play.HUD protected virtual Container CreateJudgementLine(JudgementResult judgement) => new CircularContainer { - Anchor = mirrored ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = mirrored ? Anchor.CentreLeft : Anchor.CentreRight, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Masking = true, - Size = new Vector2(8, 2), + RelativeSizeAxes = Axes.X, + Height = 2, RelativePositionAxes = Axes.Y, Y = getRelativeJudgementPosition(judgement.TimeOffset), - X = mirrored ? spacing : -spacing, Child = new Box { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index a9a469486e..50480c001c 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -86,8 +86,8 @@ namespace osu.Game.Screens.Play HealthDisplay = CreateHealthDisplay(), Progress = CreateProgress(), ModDisplay = CreateModsContainer(), - LeftHitErrorDisplay = CreateAccuracyBar(false), - RightHitErrorDisplay = CreateAccuracyBar(), + LeftHitErrorDisplay = CreateHitErrorDisplay(false), + RightHitErrorDisplay = CreateHitErrorDisplay(), } }, PlayerSettingsOverlay = CreatePlayerSettingsOverlay(), @@ -260,11 +260,11 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20, Right = 10 }, }; - protected virtual HitErrorDisplay CreateAccuracyBar(bool mirrored = true) => new HitErrorDisplay(mirrored) + protected virtual HitErrorDisplay CreateHitErrorDisplay(bool reversed = true) => new HitErrorDisplay(reversed) { - Anchor = mirrored ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = mirrored ? Anchor.CentreRight : Anchor.CentreLeft, - Margin = new MarginPadding { Horizontal = 30 } + Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Margin = new MarginPadding { Horizontal = 20 } }; protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); From 55cd1cecdfb57c6ba8794ce66946b24be2d8fe4d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 18 Aug 2019 15:53:42 +0300 Subject: [PATCH 2232/2854] Add missing blank line --- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index 1a4d2a45c5..2959a9aca7 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -145,6 +145,7 @@ namespace osu.Game.Screens.Play.HUD case ScoreMeterType.None: this.FadeOut(200, Easing.OutQuint); break; + case ScoreMeterType.HitError: this.FadeIn(200, Easing.OutQuint); break; From 6c60db550ff9c3e76abdc89add98d6f26e415b28 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 18 Aug 2019 16:24:13 +0300 Subject: [PATCH 2233/2854] Fix crash if ruleset has no Meh hit windows --- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 21 ++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index 2959a9aca7..2ed06209de 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -107,37 +107,46 @@ namespace osu.Game.Screens.Play.HUD { RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(colours.Yellow.Opacity(0), colours.Yellow), - Height = (float)((HitWindows.Meh - HitWindows.Good) / (HitWindows.Meh * 2)) + Height = (float)((getMehHitWindows() - HitWindows.Good) / (getMehHitWindows() * 2)) }, new Box { RelativeSizeAxes = Axes.Both, Colour = colours.Green, - Height = (float)((HitWindows.Good - HitWindows.Great) / (HitWindows.Meh * 2)) + Height = (float)((HitWindows.Good - HitWindows.Great) / (getMehHitWindows() * 2)) }, new Box { RelativeSizeAxes = Axes.Both, Colour = colours.BlueLight, - Height = (float)(HitWindows.Great / HitWindows.Meh) + Height = (float)(HitWindows.Great / getMehHitWindows()) }, new Box { RelativeSizeAxes = Axes.Both, Colour = colours.Green, - Height = (float)((HitWindows.Good - HitWindows.Great) / (HitWindows.Meh * 2)) + Height = (float)((HitWindows.Good - HitWindows.Great) / (getMehHitWindows() * 2)) }, new Box { RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(colours.Yellow, colours.Yellow.Opacity(0)), - Height = (float)((HitWindows.Meh - HitWindows.Good) / (HitWindows.Meh * 2)) + Height = (float)((getMehHitWindows() - HitWindows.Good) / (getMehHitWindows() * 2)) } }); type.BindValueChanged(onTypeChanged, true); } + private double getMehHitWindows() + { + // In case if ruleset has no Meh hit windows (like Taiko) + if (HitWindows.Meh == 0) + return HitWindows.Good + 40; + + return HitWindows.Meh; + } + private void onTypeChanged(ValueChangedEvent type) { switch (type.NewValue) @@ -183,7 +192,7 @@ namespace osu.Game.Screens.Play.HUD } }; - private float getRelativeJudgementPosition(double value) => (float)(value / HitWindows.Meh); + private float getRelativeJudgementPosition(double value) => (float)(value / getMehHitWindows()); private float calculateArrowPosition(JudgementResult newJudgement) { From dd6351b8caa83e917152a74220ed1a04fe56f3f3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 18 Aug 2019 16:51:16 +0300 Subject: [PATCH 2234/2854] Apply suggested changes --- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 16 +++++++++------- osu.Game/Screens/Play/HUDOverlay.cs | 18 +++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index 2ed06209de..1a3682351b 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -27,6 +27,9 @@ namespace osu.Game.Screens.Play.HUD private const int bar_width = 3; private const int judgement_line_width = 8; private const int bar_height = 200; + private const int fade_duration = 200; + private const int arrow_move_duration = 500; + private const int judgement_life_time = 10000; private const int spacing = 3; public HitWindows HitWindows { get; set; } @@ -152,11 +155,11 @@ namespace osu.Game.Screens.Play.HUD switch (type.NewValue) { case ScoreMeterType.None: - this.FadeOut(200, Easing.OutQuint); + this.FadeOut(fade_duration, Easing.OutQuint); break; case ScoreMeterType.HitError: - this.FadeIn(200, Easing.OutQuint); + this.FadeIn(fade_duration, Easing.OutQuint); break; } } @@ -166,14 +169,13 @@ namespace osu.Game.Screens.Play.HUD if (!newJudgement.IsHit) return; - Container judgementLine; + var judgementLine = CreateJudgementLine(newJudgement); - judgementsContainer.Add(judgementLine = CreateJudgementLine(newJudgement)); + judgementsContainer.Add(judgementLine); - judgementLine.FadeOut(10000, Easing.OutQuint); - judgementLine.Expire(); + judgementLine.FadeOut(judgement_life_time, Easing.OutQuint).Expire(); - arrow.MoveToY(calculateArrowPosition(newJudgement), 500, Easing.OutQuint); + arrow.MoveToY(calculateArrowPosition(newJudgement), arrow_move_duration, Easing.OutQuint); } protected virtual Container CreateJudgementLine(JudgementResult judgement) => new CircularContainer diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 50480c001c..114dc86757 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -276,17 +277,16 @@ namespace osu.Game.Screens.Play ComboCounter?.Current.BindTo(processor.Combo); HealthDisplay?.Current.BindTo(processor.Health); - if (LeftHitErrorDisplay != null) - { - processor.NewJudgement += LeftHitErrorDisplay.OnNewJudgement; - LeftHitErrorDisplay.HitWindows = processor.CreateHitWindows(); - } + var hitWindows = processor.CreateHitWindows(); - if (RightHitErrorDisplay != null) + visibilityContainer.ForEach(drawable => { - processor.NewJudgement += RightHitErrorDisplay.OnNewJudgement; - RightHitErrorDisplay.HitWindows = processor.CreateHitWindows(); - } + if (drawable is HitErrorDisplay) + { + processor.NewJudgement += (drawable as HitErrorDisplay).OnNewJudgement; + (drawable as HitErrorDisplay).HitWindows = hitWindows; + } + }); if (HealthDisplay is StandardHealthDisplay shd) processor.NewJudgement += shd.Flash; From 4c817b18b7f217ef9c95c01c867d8e4442c7ad18 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 18 Aug 2019 17:03:11 +0300 Subject: [PATCH 2235/2854] Use direct cast --- osu.Game/Screens/Play/HUDOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 114dc86757..be291003b5 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -283,8 +283,8 @@ namespace osu.Game.Screens.Play { if (drawable is HitErrorDisplay) { - processor.NewJudgement += (drawable as HitErrorDisplay).OnNewJudgement; - (drawable as HitErrorDisplay).HitWindows = hitWindows; + processor.NewJudgement += ((HitErrorDisplay)drawable).OnNewJudgement; + ((HitErrorDisplay)drawable).HitWindows = hitWindows; } }); From 3fcd786198a60b61cd6179f56294b0b12d209808 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 18 Aug 2019 19:32:56 +0200 Subject: [PATCH 2236/2854] Take lease on WorkingBeatmap during intro screens to prevent weird interactions with Playback control. --- osu.Game/Screens/Menu/IntroScreen.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 27f3c9a45b..a621e29cf8 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -28,11 +28,18 @@ namespace osu.Game.Screens.Menu private Bindable menuVoice; + private LeasedBindable beatmap; + + public new Bindable Beatmap { get => beatmap; } + protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack(); [BackgroundDependencyLoader] private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game) { + //we take a lease on the beatmap bindable to prevent music playback from changing / pausing music during intros, as it is causing weird interactions with certains intros + beatmap = base.Beatmap.BeginLease(false); + menuVoice = config.GetBindable(OsuSetting.MenuVoice); seeya = audio.Samples.Get(@"seeya"); } @@ -108,6 +115,8 @@ namespace osu.Game.Screens.Menu protected void LoadMenu() { DidLoadMenu = true; + beatmap.Return(); //we return the lease to the beatmap bindable as we're pushing the main menu. + this.Push(mainMenu); } } From 2393bbc69b15e1f3421f3d4b140393bbaa8462ac Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 18 Aug 2019 21:27:53 +0300 Subject: [PATCH 2237/2854] Expand APIKudosuHistory --- .../Requests/GetUserKudosuHistoryRequest.cs | 10 ++++ .../Requests/Responses/APIKudosuHistory.cs | 46 +++++++++++++++++-- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs index e90e297672..af37bd4b51 100644 --- a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs @@ -18,4 +18,14 @@ namespace osu.Game.Online.API.Requests protected override string Target => $"users/{userId}/kudosu"; } + + public enum KudosuAction + { + Give, + VoteGive, + Reset, + VoteReset, + DenyKudosuReset, + Revoke, + } } diff --git a/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs index 271dcc320e..d02f71c339 100644 --- a/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs +++ b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs @@ -2,19 +2,59 @@ // See the LICENCE file in the repository root for full licence text. using System; +using Humanizer; using Newtonsoft.Json; namespace osu.Game.Online.API.Requests.Responses { public class APIKudosuHistory { - [JsonProperty("id")] - public int ID; - [JsonProperty("created_at")] public DateTimeOffset CreatedAt; [JsonProperty("amount")] + private int amount + { + set => Amount = Math.Abs(value); + } + public int Amount; + + [JsonProperty("post")] + public ModdingPost Post; + + public class ModdingPost + { + [JsonProperty("url")] + public string Url; + + [JsonProperty("title")] + public string Title; + } + + [JsonProperty("giver")] + public KudosuGiver Giver; + + public class KudosuGiver + { + [JsonProperty("url")] + public string Url; + + [JsonProperty("username")] + public string Username; + } + + [JsonProperty("action")] + private string action + { + set + { + string parsed = value.Contains(".") ? value.Split('.')[0].Pascalize() + value.Split('.')[1].Pascalize() : value.Pascalize(); + + Action = (KudosuAction)Enum.Parse(typeof(KudosuAction), parsed); + } + } + + public KudosuAction Action; } } From be97804180f00c93818ea91b9afd58037e27c7dd Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 18 Aug 2019 21:28:07 +0300 Subject: [PATCH 2238/2854] Implement text formatting --- .../Kudosu/DrawableKudosuHistoryItem.cs | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index 1671073242..9b68131515 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.API.Requests; using osu.Game.Online.Chat; namespace osu.Game.Overlays.Profile.Sections.Kudosu @@ -51,6 +52,45 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu AutoSizeAxes = Axes.X, }; - private MessageFormatter.MessageFormatterResult createMessage() => MessageFormatter.FormatText($@"{historyItem.Amount}"); + private MessageFormatter.MessageFormatterResult createMessage() + { + string postLinkTemplate() => $"[{historyItem.Post.Url} {historyItem.Post.Title}]"; + string userLinkTemplate() => $"[{historyItem.Giver?.Url} {historyItem.Giver?.Username}]"; + + string message; + + switch (historyItem.Action) + { + case KudosuAction.Give: + message = $"Received {historyItem.Amount} kudosu from {userLinkTemplate()} for a post at {postLinkTemplate()}"; + break; + + case KudosuAction.VoteGive: + message = $"Received {historyItem.Amount} kudosu from obtaining votes in modding post of {postLinkTemplate()}"; + break; + + case KudosuAction.Reset: + message = $"Kudosu reset by {userLinkTemplate()} for the post {postLinkTemplate()}"; + break; + + case KudosuAction.VoteReset: + message = $"Lost {historyItem.Amount} kudosu from losing votes in modding post of {postLinkTemplate()}"; + break; + + case KudosuAction.DenyKudosuReset: + message = $"Denied {historyItem.Amount} kudosu from modding post {postLinkTemplate()}"; + break; + + case KudosuAction.Revoke: + message = $"Denied kudosu by {userLinkTemplate()} for the post {postLinkTemplate()}"; + break; + + default: + message = string.Empty; + break; + } + + return MessageFormatter.FormatText(message); + } } } From 4c9b621f43d9a702be1bd3149758a9e030f781bf Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 18 Aug 2019 22:01:36 +0300 Subject: [PATCH 2239/2854] Fix some user links can't be opened inside the game --- osu.Game/Online/Chat/MessageFormatter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 4aaffdd161..db26945ef3 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -122,6 +122,7 @@ namespace osu.Game.Online.Chat return new LinkDetails(LinkAction.OpenBeatmapSet, args[3]); case "u": + case "users": return new LinkDetails(LinkAction.OpenUserProfile, args[3]); } } From 015406f4d2b7256ee0f7c0c9d2e0bb5445ab86ea Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 18 Aug 2019 22:02:59 +0300 Subject: [PATCH 2240/2854] Fix link parser --- osu.Game/Online/Chat/MessageFormatter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 4aaffdd161..db26945ef3 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -122,6 +122,7 @@ namespace osu.Game.Online.Chat return new LinkDetails(LinkAction.OpenBeatmapSet, args[3]); case "u": + case "users": return new LinkDetails(LinkAction.OpenUserProfile, args[3]); } } From a51fbfa31b52aefb18a8ec9d78f93f475b724de5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Aug 2019 10:46:28 +0900 Subject: [PATCH 2241/2854] Fix osu! default tooltips not displaying --- .../Graphics/Cursor/OsuTooltipContainer.cs | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs index cfcda892fd..57f39bb8c7 100644 --- a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs @@ -30,22 +30,24 @@ namespace osu.Game.Graphics.Cursor private readonly OsuSpriteText text; private bool instantMovement = true; - public override string TooltipText + public override bool SetContent(object content) { - set + if (!(content is string contentString)) + return false; + + if (contentString == text.Text) return true; + + text.Text = contentString; + + if (IsPresent) { - if (value == text.Text) return; - - text.Text = value; - - if (IsPresent) - { - AutoSizeDuration = 250; - background.FlashColour(OsuColour.Gray(0.4f), 1000, Easing.OutQuint); - } - else - AutoSizeDuration = 0; + AutoSizeDuration = 250; + background.FlashColour(OsuColour.Gray(0.4f), 1000, Easing.OutQuint); } + else + AutoSizeDuration = 0; + + return true; } public OsuTooltip() From 1f00793891e1488151e7817edd99cbd9a362dfd7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Aug 2019 11:30:04 +0900 Subject: [PATCH 2242/2854] Unpause music when changing selection at song select --- osu.Game/Overlays/MusicController.cs | 9 +++++++++ osu.Game/Screens/Select/SongSelect.cs | 13 +++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index f6208c46cb..6ad147735b 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -92,6 +92,15 @@ namespace osu.Game.Overlays }); } + /// + /// Start playing the current track (if not already playing). + /// + public void Play() + { + if (!IsPlaying) + TogglePause(); + } + /// /// Toggle pause / play. /// diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 8340814db9..edb0e6deb8 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -359,6 +359,7 @@ namespace osu.Game.Screens.Select return; beatmapNoDebounce = beatmap; + performUpdateSelected(); } @@ -586,10 +587,18 @@ namespace osu.Game.Screens.Select { Track track = Beatmap.Value.Track; - if ((!track.IsRunning || restart) && music?.IsUserPaused != true) + if (!track.IsRunning || restart) { track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; - track.Restart(); + + if (music != null) + { + // use the global music controller (when available) to cancel a potential local user paused state. + music.SeekTo(track.RestartPoint); + music.Play(); + } + else + track.Restart(); } } From 7143497441a95db5f98d81797ed2966058671cf4 Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Mon, 19 Aug 2019 10:32:01 +0700 Subject: [PATCH 2243/2854] Match up tooltip background color with OsuTooltipContainer --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 962cb33a83..3c3a7c056e 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -153,7 +153,7 @@ namespace osu.Game.Beatmaps.Drawables private void load(OsuColour colours) { this.colours = colours; - background.Colour = colours.GreyCarmineDark; + background.Colour = colours.Gray3; } public bool SetContent(object content) From be51fde2925a674d0fd31b6b24bb9e8aeca015ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Aug 2019 13:22:32 +0900 Subject: [PATCH 2244/2854] Centre beatmap panels in profile overlay --- .../Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index 1b6c1c99a6..8a6b52b7ee 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -46,8 +46,11 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps if (!s.OnlineBeatmapSetID.HasValue) continue; - var panel = new DirectGridPanel(s.ToBeatmapSet(Rulesets)); - ItemsContainer.Add(panel); + ItemsContainer.Add(new DirectGridPanel(s.ToBeatmapSet(Rulesets)) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }); } }); From 4356f2ef9fdf072fbacdc7e196edff4659bd9582 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Aug 2019 17:45:54 +0900 Subject: [PATCH 2245/2854] Add sample usage of looping skinnable --- osu.Game/Skinning/LegacySkin.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index b560fd4f84..a41812bad0 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -102,6 +102,10 @@ namespace osu.Game.Skinning return null; + case "Play/osu/sliderfollowcircle": + animatable = true; + break; + case "Play/Miss": componentName = "hit0"; animatable = true; @@ -149,8 +153,6 @@ namespace osu.Game.Skinning texture = GetTexture($"{componentName}-{i}"); } - // This comment can be removed once we have components which are looping - // ReSharper disable once ConditionIsAlwaysTrueOrFalse animation.Repeat = looping; return animation; From d224405bc611aa7f1c4be3df83b7b51136a74716 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Aug 2019 17:46:05 +0900 Subject: [PATCH 2246/2854] Rename const and make more available --- osu.Game/Skinning/LegacySkin.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index a41812bad0..2b9ed2aa8b 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -76,11 +76,12 @@ namespace osu.Game.Skinning Samples?.Dispose(); } + private const double default_frame_time = 1000 / 60d; + public override Drawable GetDrawableComponent(string componentName) { bool animatable = false; bool looping = true; - const double frametime = 1000 / 60d; switch (componentName) { @@ -145,7 +146,7 @@ namespace osu.Game.Skinning if (texture != null && animatable) { - var animation = new TextureAnimation { DefaultFrameLength = frametime }; + var animation = new TextureAnimation { DefaultFrameLength = default_frame_time }; for (int i = 1; texture != null; i++) { From d02b8d14f7e18ebc7292cf30e2243e4268d498e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Aug 2019 17:47:59 +0900 Subject: [PATCH 2247/2854] Avoid unnecessary texture retrieval for non-animated sprites --- osu.Game/Skinning/LegacySkin.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 2b9ed2aa8b..d47e100d0e 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -142,9 +142,9 @@ namespace osu.Game.Skinning }; } - var texture = GetTexture($"{componentName}-0"); + Texture texture; - if (texture != null && animatable) + if (animatable && (texture = GetTexture($"{componentName}-0")) != null) { var animation = new TextureAnimation { DefaultFrameLength = default_frame_time }; From 539a27a557738923c4805b5dadfc89bc2be4853f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Aug 2019 19:23:54 +0900 Subject: [PATCH 2248/2854] Refactor texture lookup code --- osu.Game/Skinning/LegacySkin.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index d47e100d0e..94421b1251 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -142,28 +142,31 @@ namespace osu.Game.Skinning }; } + return getAnimation(componentName, animatable, looping); + } + + private Drawable getAnimation(string componentName, bool animatable, bool looping, string animationSeparator = "-") + { Texture texture; - if (animatable && (texture = GetTexture($"{componentName}-0")) != null) + Texture getFrameTexture(int frame) => GetTexture($"{componentName}{animationSeparator}{frame}"); + + if (animatable && (texture = getFrameTexture(0)) != null) { var animation = new TextureAnimation { DefaultFrameLength = default_frame_time }; for (int i = 1; texture != null; i++) { animation.AddFrame(texture); - texture = GetTexture($"{componentName}-{i}"); + texture = getFrameTexture(i); } animation.Repeat = looping; return animation; } - else - { - texture = GetTexture(componentName); - return texture == null ? null : new Sprite { Texture = texture }; - } + return (texture = GetTexture(componentName)) == null ? null : new Sprite { Texture = texture }; } public class LegacySliderBall : Sprite From d0766fa1cdaa15c1a35a096e1364de4455dcefb5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Aug 2019 19:52:53 +0900 Subject: [PATCH 2249/2854] Add slider ball animation support --- .../Objects/Drawables/Pieces/SliderBall.cs | 12 +++++- osu.Game/Skinning/LegacySkin.cs | 42 +++++++++++++------ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 8b72b23ca3..332e25750f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -55,7 +55,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Child = new Container { RelativeSizeAxes = Axes.Both, - // TODO: support skin filename animation (sliderb0, sliderb1...) Child = new SkinnableDrawable("Play/osu/sliderball", _ => new DefaultSliderBall()), } } @@ -168,9 +167,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces return action == OsuAction.LeftButton || action == OsuAction.RightButton; } + private Vector2? lastPosition; + public void UpdateProgress(double completionProgress) { - Position = slider.CurvePositionAt(completionProgress); + var newPos = slider.CurvePositionAt(completionProgress); + + var diff = lastPosition.HasValue ? lastPosition.Value - newPos : newPos - slider.CurvePositionAt(completionProgress + 0.01f); + + Position = newPos; + Rotation = 90 + (float)(-Math.Atan2(diff.X, diff.Y) * 180 / Math.PI); + + lastPosition = newPos; } private class FollowCircleContainer : Container diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 94421b1251..883e0ce3fc 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -92,8 +92,20 @@ namespace osu.Game.Skinning return null; case "Play/osu/sliderball": - if (GetTexture("sliderb") != null) - return new LegacySliderBall(); + var sliderBallContent = getAnimation("sliderb", true, true, ""); + + if (sliderBallContent != null) + { + var size = sliderBallContent.Size; + + sliderBallContent.RelativeSizeAxes = Axes.Both; + sliderBallContent.Size = Vector2.One; + + return new LegacySliderBall(sliderBallContent) + { + Size = size + }; + } return null; @@ -169,16 +181,6 @@ namespace osu.Game.Skinning return (texture = GetTexture(componentName)) == null ? null : new Sprite { Texture = texture }; } - public class LegacySliderBall : Sprite - { - [BackgroundDependencyLoader] - private void load(ISkinSource skin) - { - Texture = skin.GetTexture("sliderb"); - Colour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White; - } - } - public override Texture GetTexture(string componentName) { float ratio = 2; @@ -333,6 +335,20 @@ namespace osu.Game.Skinning } } + public class LegacySliderBall : CompositeDrawable + { + public LegacySliderBall(Drawable content) + { + InternalChild = content; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + Colour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White; + } + } + public class LegacyMainCirclePiece : CompositeDrawable { public LegacyMainCirclePiece() From b3556403aac6af43614e429054a4b6672583b700 Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Mon, 19 Aug 2019 16:18:25 +0200 Subject: [PATCH 2250/2854] Make GetNextObject() a virtual method --- .../Replays/ManiaAutoGenerator.cs | 24 ++++++++--------- .../Replays/TaikoAutoGenerator.cs | 26 +++++++++++++++---- osu.Game/Rulesets/Replays/AutoGenerator.cs | 9 +++++++ 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 43150958d0..b9dd32208e 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Replays; using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Replays; @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Mania.Replays for (int i = 0; i < Beatmap.HitObjects.Count; i++) { var currentObject = Beatmap.HitObjects[i]; - var nextObjectInTheSameColumn = getNextObjectInTheSameColumn(i); + var nextObjectInTheSameColumn = GetNextObject(i); double endTime = (currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime; @@ -94,19 +94,19 @@ namespace osu.Game.Rulesets.Mania.Replays yield return new ReleasePoint { Time = endTime + calculatedDelay, Column = currentObject.Column }; } + } - ManiaHitObject getNextObjectInTheSameColumn(int currentIndex) + protected override HitObject GetNextObject(int currentIndex) + { + int desiredColumn = Beatmap.HitObjects[currentIndex++].Column; + + for (; currentIndex < Beatmap.HitObjects.Count; currentIndex++) { - int desiredColumn = Beatmap.HitObjects[currentIndex++].Column; - - for (; currentIndex < Beatmap.HitObjects.Count; currentIndex++) - { - if (Beatmap.HitObjects[currentIndex].Column == desiredColumn) - return Beatmap.HitObjects[currentIndex]; - } - - return null; + if (Beatmap.HitObjects[currentIndex].Column == desiredColumn) + return Beatmap.HitObjects[currentIndex]; } + + return null; } private interface IActionPoint diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index 1d35393de0..67f93a0259 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Taiko.Beatmaps; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Taiko.Replays { @@ -113,16 +114,17 @@ namespace osu.Game.Rulesets.Taiko.Replays else throw new InvalidOperationException("Unknown hit object type."); - TaikoHitObject nextHitObject = i < Beatmap.HitObjects.Count - 1 ? Beatmap.HitObjects[i + 1] : null; + var nextHitObject = GetNextObject(i); bool canDelayKeyUp = nextHitObject == null || nextHitObject.StartTime > endTime + KEY_UP_DELAY; - if (canDelayKeyUp) - Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY)); + double calculatedDelay = canDelayKeyUp ? KEY_UP_DELAY : nextHitObject.StartTime - endTime; - if (nextHitObject != null) + Frames.Add(new TaikoReplayFrame(endTime + calculatedDelay)); + + if (i < Beatmap.HitObjects.Count - 1) { - double waitTime = nextHitObject.StartTime - 1000; + double waitTime = Beatmap.HitObjects[i + 1].StartTime - 1000; if (waitTime > endTime) Frames.Add(new TaikoReplayFrame(waitTime)); } @@ -132,5 +134,19 @@ namespace osu.Game.Rulesets.Taiko.Replays return Replay; } + + protected override HitObject GetNextObject(int currentIndex) + { + Type desiredType = Beatmap.HitObjects[currentIndex++].GetType(); + + for (; currentIndex < Beatmap.HitObjects.Count; currentIndex++) + { + var currentObj = Beatmap.HitObjects[currentIndex]; + if (currentObj.GetType().Equals(desiredType) || currentObj is DrumRoll) + return Beatmap.HitObjects[currentIndex]; + } + + return null; + } } } diff --git a/osu.Game/Rulesets/Replays/AutoGenerator.cs b/osu.Game/Rulesets/Replays/AutoGenerator.cs index 1d4cdbf04c..3319f30a6f 100644 --- a/osu.Game/Rulesets/Replays/AutoGenerator.cs +++ b/osu.Game/Rulesets/Replays/AutoGenerator.cs @@ -3,6 +3,7 @@ using osu.Game.Beatmaps; using osu.Game.Replays; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Replays { @@ -34,5 +35,13 @@ namespace osu.Game.Rulesets.Replays protected const double KEY_UP_DELAY = 50; #endregion + + protected virtual HitObject GetNextObject(int currentIndex) + { + if (currentIndex >= Beatmap.HitObjects.Count - 1) + return null; + + return Beatmap.HitObjects[currentIndex + 1]; + } } } From daeefc449c30785ba9db860ddaaf7f7fc7c92c18 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 19 Aug 2019 17:49:53 +0300 Subject: [PATCH 2251/2854] Use another link format to avoid representation issues --- .../Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index 9b68131515..ac5801f989 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu private MessageFormatter.MessageFormatterResult createMessage() { - string postLinkTemplate() => $"[{historyItem.Post.Url} {historyItem.Post.Title}]"; + string postLinkTemplate() => $"({historyItem.Post.Title})[{historyItem.Post.Url}]"; string userLinkTemplate() => $"[{historyItem.Giver?.Url} {historyItem.Giver?.Username}]"; string message; From 6b8fbf0eb1ead2c25e04b8a62ca70aaaf8e42b2c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 19 Aug 2019 17:59:55 +0300 Subject: [PATCH 2252/2854] Change link format back Due to unavaliability to handle round brackets --- .../Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index ac5801f989..9b68131515 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu private MessageFormatter.MessageFormatterResult createMessage() { - string postLinkTemplate() => $"({historyItem.Post.Title})[{historyItem.Post.Url}]"; + string postLinkTemplate() => $"[{historyItem.Post.Url} {historyItem.Post.Title}]"; string userLinkTemplate() => $"[{historyItem.Giver?.Url} {historyItem.Giver?.Username}]"; string message; From eb83e36e0f85dc3b0ca6f4f6ce1c138b6ecc938c Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Mon, 19 Aug 2019 17:05:25 +0200 Subject: [PATCH 2253/2854] Make TaikoAutoGenerator unpress sooner --- osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index 67f93a0259..463bb84d07 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Taiko.Replays bool canDelayKeyUp = nextHitObject == null || nextHitObject.StartTime > endTime + KEY_UP_DELAY; - double calculatedDelay = canDelayKeyUp ? KEY_UP_DELAY : nextHitObject.StartTime - endTime; + double calculatedDelay = canDelayKeyUp ? KEY_UP_DELAY : (nextHitObject.StartTime - endTime) * 0.9; Frames.Add(new TaikoReplayFrame(endTime + calculatedDelay)); From 9732f5d6226f739c51e6cae81b706581d5c0291d Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Mon, 19 Aug 2019 17:28:32 +0200 Subject: [PATCH 2254/2854] Add Swell to GetNextObject() check --- osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index 463bb84d07..fbc7b75854 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -142,7 +142,8 @@ namespace osu.Game.Rulesets.Taiko.Replays for (; currentIndex < Beatmap.HitObjects.Count; currentIndex++) { var currentObj = Beatmap.HitObjects[currentIndex]; - if (currentObj.GetType().Equals(desiredType) || currentObj is DrumRoll) + if (currentObj.GetType().Equals(desiredType) || + currentObj is DrumRoll || currentObj is Swell) // It's best to unpress any remaining keys before DrumRoll or Swell return Beatmap.HitObjects[currentIndex]; } From 70084b5553102e93fb5d592fb7d24d4672c070b4 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 19 Aug 2019 20:28:03 +0300 Subject: [PATCH 2255/2854] Move HitErrorDisplay outside of the HUD --- osu.Game/Screens/Play/HUDOverlay.cs | 22 ------ .../HitErrorDisplay.cs | 66 +++++------------- .../HitErrorDisplay/HitErrorDisplayOverlay.cs | 69 +++++++++++++++++++ osu.Game/Screens/Play/Player.cs | 4 ++ 4 files changed, 90 insertions(+), 71 deletions(-) rename osu.Game/Screens/Play/{HUD => HitErrorDisplay}/HitErrorDisplay.cs (77%) create mode 100644 osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index be291003b5..1124c8f5f0 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -36,8 +36,6 @@ namespace osu.Game.Screens.Play public readonly ModDisplay ModDisplay; public readonly HoldForMenuButton HoldToQuit; public readonly PlayerSettingsOverlay PlayerSettingsOverlay; - public readonly HitErrorDisplay LeftHitErrorDisplay; - public readonly HitErrorDisplay RightHitErrorDisplay; public Bindable ShowHealthbar = new Bindable(true); @@ -87,8 +85,6 @@ namespace osu.Game.Screens.Play HealthDisplay = CreateHealthDisplay(), Progress = CreateProgress(), ModDisplay = CreateModsContainer(), - LeftHitErrorDisplay = CreateHitErrorDisplay(false), - RightHitErrorDisplay = CreateHitErrorDisplay(), } }, PlayerSettingsOverlay = CreatePlayerSettingsOverlay(), @@ -261,13 +257,6 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20, Right = 10 }, }; - protected virtual HitErrorDisplay CreateHitErrorDisplay(bool reversed = true) => new HitErrorDisplay(reversed) - { - Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, - Margin = new MarginPadding { Horizontal = 20 } - }; - protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); protected virtual void BindProcessor(ScoreProcessor processor) @@ -277,17 +266,6 @@ namespace osu.Game.Screens.Play ComboCounter?.Current.BindTo(processor.Combo); HealthDisplay?.Current.BindTo(processor.Health); - var hitWindows = processor.CreateHitWindows(); - - visibilityContainer.ForEach(drawable => - { - if (drawable is HitErrorDisplay) - { - processor.NewJudgement += ((HitErrorDisplay)drawable).OnNewJudgement; - ((HitErrorDisplay)drawable).HitWindows = hitWindows; - } - }); - if (HealthDisplay is StandardHealthDisplay shd) processor.NewJudgement += shd.Flash; } diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs similarity index 77% rename from osu.Game/Screens/Play/HUD/HitErrorDisplay.cs rename to osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs index 1a3682351b..1a776bc78d 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs @@ -10,16 +10,13 @@ using osuTK; using osu.Framework.Graphics.Sprites; using System.Collections.Generic; using osu.Game.Rulesets.Objects; -using osu.Game.Beatmaps; -using osu.Framework.Bindables; using osu.Framework.Allocation; using osu.Framework.Graphics.Colour; using osu.Game.Graphics; using osu.Framework.Extensions.Color4Extensions; using System.Linq; -using osu.Game.Configuration; -namespace osu.Game.Screens.Play.HUD +namespace osu.Game.Screens.Play.HitErrorDisplay { public class HitErrorDisplay : CompositeDrawable { @@ -27,29 +24,24 @@ namespace osu.Game.Screens.Play.HUD private const int bar_width = 3; private const int judgement_line_width = 8; private const int bar_height = 200; - private const int fade_duration = 200; private const int arrow_move_duration = 500; private const int judgement_life_time = 10000; private const int spacing = 3; - public HitWindows HitWindows { get; set; } - - [Resolved] - private Bindable beatmap { get; set; } - - [Resolved] - private OsuColour colours { get; set; } - + private readonly HitWindows hitWindows; private readonly SpriteIcon arrow; private readonly FillFlowContainer bar; private readonly Container judgementsContainer; private readonly Queue judgementOffsets = new Queue(); - private readonly Bindable type = new Bindable(); - - public HitErrorDisplay(bool reversed = false) + public HitErrorDisplay(float overallDifficulty, HitWindows hitWindows, bool reversed = false) { + this.hitWindows = hitWindows; + hitWindows.SetDifficulty(overallDifficulty); + AutoSizeAxes = Axes.Both; + Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft; + Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft; AddInternal(new FillFlowContainer { @@ -94,74 +86,50 @@ namespace osu.Game.Screens.Play.HUD } [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private void load(OsuColour colours) { - config.BindWith(OsuSetting.ScoreMeter, type); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - HitWindows.SetDifficulty(beatmap.Value.BeatmapInfo.BaseDifficulty.OverallDifficulty); - bar.AddRange(new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(colours.Yellow.Opacity(0), colours.Yellow), - Height = (float)((getMehHitWindows() - HitWindows.Good) / (getMehHitWindows() * 2)) + Height = (float)((getMehHitWindows() - hitWindows.Good) / (getMehHitWindows() * 2)) }, new Box { RelativeSizeAxes = Axes.Both, Colour = colours.Green, - Height = (float)((HitWindows.Good - HitWindows.Great) / (getMehHitWindows() * 2)) + Height = (float)((hitWindows.Good - hitWindows.Great) / (getMehHitWindows() * 2)) }, new Box { RelativeSizeAxes = Axes.Both, Colour = colours.BlueLight, - Height = (float)(HitWindows.Great / getMehHitWindows()) + Height = (float)(hitWindows.Great / getMehHitWindows()) }, new Box { RelativeSizeAxes = Axes.Both, Colour = colours.Green, - Height = (float)((HitWindows.Good - HitWindows.Great) / (getMehHitWindows() * 2)) + Height = (float)((hitWindows.Good - hitWindows.Great) / (getMehHitWindows() * 2)) }, new Box { RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(colours.Yellow, colours.Yellow.Opacity(0)), - Height = (float)((getMehHitWindows() - HitWindows.Good) / (getMehHitWindows() * 2)) + Height = (float)((getMehHitWindows() - hitWindows.Good) / (getMehHitWindows() * 2)) } }); - - type.BindValueChanged(onTypeChanged, true); } private double getMehHitWindows() { // In case if ruleset has no Meh hit windows (like Taiko) - if (HitWindows.Meh == 0) - return HitWindows.Good + 40; + if (hitWindows.Meh == 0) + return hitWindows.Good + 40; - return HitWindows.Meh; - } - - private void onTypeChanged(ValueChangedEvent type) - { - switch (type.NewValue) - { - case ScoreMeterType.None: - this.FadeOut(fade_duration, Easing.OutQuint); - break; - - case ScoreMeterType.HitError: - this.FadeIn(fade_duration, Easing.OutQuint); - break; - } + return hitWindows.Meh; } public void OnNewJudgement(JudgementResult newJudgement) diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs new file mode 100644 index 0000000000..09ecbdfc66 --- /dev/null +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs @@ -0,0 +1,69 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Scoring; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Framework.Extensions.IEnumerableExtensions; +using System.Linq; + +namespace osu.Game.Screens.Play.HitErrorDisplay +{ + public class HitErrorDisplayOverlay : Container + { + private const int fade_duration = 200; + private const int margin = 10; + + private readonly Bindable type = new Bindable(); + + public HitErrorDisplayOverlay(ScoreProcessor processor, WorkingBeatmap workingBeatmap) + { + float overallDifficulty = workingBeatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty; + + RelativeSizeAxes = Axes.Both; + Children = new[] + { + new HitErrorDisplay(overallDifficulty, processor.CreateHitWindows()) + { + Margin = new MarginPadding { Left = margin } + }, + new HitErrorDisplay(overallDifficulty, processor.CreateHitWindows(), true) + { + Margin = new MarginPadding { Right = margin } + }, + }; + + Children.ForEach(t => processor.NewJudgement += t.OnNewJudgement); + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.BindWith(OsuSetting.ScoreMeter, type); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + type.BindValueChanged(onTypeChanged, true); + } + + private void onTypeChanged(ValueChangedEvent type) + { + switch (type.NewValue) + { + case ScoreMeterType.None: + InternalChildren.ForEach(t => t.FadeOut(fade_duration, Easing.OutQuint)); + break; + + default: + InternalChildren.ForEach(t => t.FadeIn(fade_duration, Easing.OutQuint)); + break; + } + } + } +} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e7398be176..9ef8cc4509 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -24,6 +24,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; +using osu.Game.Screens.Play.HitErrorDisplay; using osu.Game.Screens.Ranking; using osu.Game.Skinning; using osu.Game.Users; @@ -73,6 +74,8 @@ namespace osu.Game.Screens.Play protected HUDOverlay HUDOverlay { get; private set; } + protected HitErrorDisplayOverlay HitErrorDisplayOverlay { get; private set; } + public bool LoadedBeatmapSuccessfully => DrawableRuleset?.Objects.Any() == true; protected GameplayClockContainer GameplayClockContainer { get; private set; } @@ -157,6 +160,7 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Origin = Anchor.Centre }, + HitErrorDisplayOverlay = new HitErrorDisplayOverlay(ScoreProcessor, working), new SkipOverlay(DrawableRuleset.GameplayStartTime) { RequestSeek = GameplayClockContainer.Seek From 6d3aa0520b6cc6bc999809b123b600414314260f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 19 Aug 2019 20:44:06 +0300 Subject: [PATCH 2256/2854] Make HitErrorDisplay an abstract class --- .../HitErrorDisplay/DefaultHitErrorDisplay.cs | 174 ++++++++++++++++++ .../Play/HitErrorDisplay/HitErrorDisplay.cs | 167 +---------------- .../HitErrorDisplay/HitErrorDisplayOverlay.cs | 4 +- 3 files changed, 182 insertions(+), 163 deletions(-) create mode 100644 osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs diff --git a/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs new file mode 100644 index 0000000000..6cf80b209a --- /dev/null +++ b/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs @@ -0,0 +1,174 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Judgements; +using osuTK.Graphics; +using osuTK; +using osu.Framework.Graphics.Sprites; +using System.Collections.Generic; +using osu.Game.Rulesets.Objects; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Colour; +using osu.Game.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using System.Linq; + +namespace osu.Game.Screens.Play.HitErrorDisplay +{ + public class DefaultHitErrorDisplay : HitErrorDisplay + { + private const int stored_judgements_amount = 5; + private const int bar_width = 3; + private const int judgement_line_width = 8; + private const int bar_height = 200; + private const int arrow_move_duration = 500; + private const int judgement_life_time = 10000; + private const int spacing = 3; + + private readonly SpriteIcon arrow; + private readonly FillFlowContainer bar; + private readonly Container judgementsContainer; + private readonly Queue judgementOffsets = new Queue(); + + public DefaultHitErrorDisplay(float overallDifficulty, HitWindows hitWindows, bool reversed = false) + : base(overallDifficulty, hitWindows) + { + AutoSizeAxes = Axes.Both; + Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft; + Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft; + + AddInternal(new FillFlowContainer + { + AutoSizeAxes = Axes.X, + Height = bar_height, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(spacing, 0), + Children = new Drawable[] + { + judgementsContainer = new Container + { + Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Width = judgement_line_width, + RelativeSizeAxes = Axes.Y, + }, + bar = new FillFlowContainer + { + Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Width = bar_width, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + }, + new Container + { + Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Child = arrow = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.Y, + Icon = reversed ? FontAwesome.Solid.ChevronRight : FontAwesome.Solid.ChevronLeft, + Size = new Vector2(8), + } + }, + } + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + bar.AddRange(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(colours.Yellow.Opacity(0), colours.Yellow), + Height = (float)((getMehHitWindows() - HitWindows.Good) / (getMehHitWindows() * 2)) + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Green, + Height = (float)((HitWindows.Good - HitWindows.Great) / (getMehHitWindows() * 2)) + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.BlueLight, + Height = (float)(HitWindows.Great / getMehHitWindows()) + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Green, + Height = (float)((HitWindows.Good - HitWindows.Great) / (getMehHitWindows() * 2)) + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(colours.Yellow, colours.Yellow.Opacity(0)), + Height = (float)((getMehHitWindows() - HitWindows.Good) / (getMehHitWindows() * 2)) + } + }); + } + + private double getMehHitWindows() + { + // In case if ruleset has no Meh hit windows (like Taiko) + if (HitWindows.Meh == 0) + return HitWindows.Good + 40; + + return HitWindows.Meh; + } + + public override void OnNewJudgement(JudgementResult newJudgement) + { + if (!newJudgement.IsHit) + return; + + var judgementLine = CreateJudgementLine(newJudgement); + + judgementsContainer.Add(judgementLine); + + judgementLine.FadeOut(judgement_life_time, Easing.OutQuint).Expire(); + + arrow.MoveToY(calculateArrowPosition(newJudgement), arrow_move_duration, Easing.OutQuint); + } + + protected virtual Container CreateJudgementLine(JudgementResult judgement) => new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Masking = true, + RelativeSizeAxes = Axes.X, + Height = 2, + RelativePositionAxes = Axes.Y, + Y = getRelativeJudgementPosition(judgement.TimeOffset), + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + } + }; + + private float getRelativeJudgementPosition(double value) => (float)(value / getMehHitWindows()); + + private float calculateArrowPosition(JudgementResult newJudgement) + { + if (judgementOffsets.Count > stored_judgements_amount) + judgementOffsets.Dequeue(); + + judgementOffsets.Enqueue(newJudgement.TimeOffset); + + return getRelativeJudgementPosition(judgementOffsets.Average()); + } + } +} diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs index 1a776bc78d..fb22b35736 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs @@ -1,177 +1,22 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Judgements; -using osuTK.Graphics; -using osuTK; -using osu.Framework.Graphics.Sprites; -using System.Collections.Generic; using osu.Game.Rulesets.Objects; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Colour; -using osu.Game.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using System.Linq; namespace osu.Game.Screens.Play.HitErrorDisplay { - public class HitErrorDisplay : CompositeDrawable + public abstract class HitErrorDisplay : CompositeDrawable { - private const int stored_judgements_amount = 5; - private const int bar_width = 3; - private const int judgement_line_width = 8; - private const int bar_height = 200; - private const int arrow_move_duration = 500; - private const int judgement_life_time = 10000; - private const int spacing = 3; + protected readonly HitWindows HitWindows; - private readonly HitWindows hitWindows; - private readonly SpriteIcon arrow; - private readonly FillFlowContainer bar; - private readonly Container judgementsContainer; - private readonly Queue judgementOffsets = new Queue(); - - public HitErrorDisplay(float overallDifficulty, HitWindows hitWindows, bool reversed = false) + public HitErrorDisplay(float overallDifficulty, HitWindows hitWindows) { - this.hitWindows = hitWindows; - hitWindows.SetDifficulty(overallDifficulty); - - AutoSizeAxes = Axes.Both; - Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft; - Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft; - - AddInternal(new FillFlowContainer - { - AutoSizeAxes = Axes.X, - Height = bar_height, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(spacing, 0), - Children = new Drawable[] - { - judgementsContainer = new Container - { - Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, - Width = judgement_line_width, - RelativeSizeAxes = Axes.Y, - }, - bar = new FillFlowContainer - { - Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, - Width = bar_width, - RelativeSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - }, - new Container - { - Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Child = arrow = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativePositionAxes = Axes.Y, - Icon = reversed ? FontAwesome.Solid.ChevronRight : FontAwesome.Solid.ChevronLeft, - Size = new Vector2(8), - } - }, - } - }); + HitWindows = hitWindows; + HitWindows.SetDifficulty(overallDifficulty); } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - bar.AddRange(new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(colours.Yellow.Opacity(0), colours.Yellow), - Height = (float)((getMehHitWindows() - hitWindows.Good) / (getMehHitWindows() * 2)) - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Green, - Height = (float)((hitWindows.Good - hitWindows.Great) / (getMehHitWindows() * 2)) - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.BlueLight, - Height = (float)(hitWindows.Great / getMehHitWindows()) - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Green, - Height = (float)((hitWindows.Good - hitWindows.Great) / (getMehHitWindows() * 2)) - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(colours.Yellow, colours.Yellow.Opacity(0)), - Height = (float)((getMehHitWindows() - hitWindows.Good) / (getMehHitWindows() * 2)) - } - }); - } - - private double getMehHitWindows() - { - // In case if ruleset has no Meh hit windows (like Taiko) - if (hitWindows.Meh == 0) - return hitWindows.Good + 40; - - return hitWindows.Meh; - } - - public void OnNewJudgement(JudgementResult newJudgement) - { - if (!newJudgement.IsHit) - return; - - var judgementLine = CreateJudgementLine(newJudgement); - - judgementsContainer.Add(judgementLine); - - judgementLine.FadeOut(judgement_life_time, Easing.OutQuint).Expire(); - - arrow.MoveToY(calculateArrowPosition(newJudgement), arrow_move_duration, Easing.OutQuint); - } - - protected virtual Container CreateJudgementLine(JudgementResult judgement) => new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Masking = true, - RelativeSizeAxes = Axes.X, - Height = 2, - RelativePositionAxes = Axes.Y, - Y = getRelativeJudgementPosition(judgement.TimeOffset), - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, - } - }; - - private float getRelativeJudgementPosition(double value) => (float)(value / getMehHitWindows()); - - private float calculateArrowPosition(JudgementResult newJudgement) - { - if (judgementOffsets.Count > stored_judgements_amount) - judgementOffsets.Dequeue(); - - judgementOffsets.Enqueue(newJudgement.TimeOffset); - - return getRelativeJudgementPosition(judgementOffsets.Average()); - } + public abstract void OnNewJudgement(JudgementResult newJudgement); } } diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs index 09ecbdfc66..35b883ff27 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs @@ -27,11 +27,11 @@ namespace osu.Game.Screens.Play.HitErrorDisplay RelativeSizeAxes = Axes.Both; Children = new[] { - new HitErrorDisplay(overallDifficulty, processor.CreateHitWindows()) + new DefaultHitErrorDisplay(overallDifficulty, processor.CreateHitWindows()) { Margin = new MarginPadding { Left = margin } }, - new HitErrorDisplay(overallDifficulty, processor.CreateHitWindows(), true) + new DefaultHitErrorDisplay(overallDifficulty, processor.CreateHitWindows(), true) { Margin = new MarginPadding { Right = margin } }, From 9bd844bf4d41c6dd9b82aa7cbc27079b738b35d2 Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Mon, 19 Aug 2019 19:54:22 +0200 Subject: [PATCH 2257/2854] Make comment more informative --- osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index fbc7b75854..4d6730c0e6 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Taiko.Replays { var currentObj = Beatmap.HitObjects[currentIndex]; if (currentObj.GetType().Equals(desiredType) || - currentObj is DrumRoll || currentObj is Swell) // It's best to unpress any remaining keys before DrumRoll or Swell + currentObj is DrumRoll || currentObj is Swell) // Unpress all keys before DrumRoll or Swell return Beatmap.HitObjects[currentIndex]; } From 6d84523bc0d87ba814ddddf1edcdff53812a48be Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 19 Aug 2019 21:10:12 +0300 Subject: [PATCH 2258/2854] Add testing --- .../Gameplay/TestSceneHitErrorDisplay.cs | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs new file mode 100644 index 0000000000..c5e5e96ad9 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs @@ -0,0 +1,110 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Screens.Play.HitErrorDisplay; +using System; +using System.Collections.Generic; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; +using osu.Framework.MathUtils; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneHitErrorDisplay : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(HitErrorDisplay), + }; + + private HitErrorDisplay display; + + public TestSceneHitErrorDisplay() + { + recreateDisplay(new OsuHitWindows(), 5); + AddStep("New random judgement", () => newJudgement()); + AddStep("New fixed judgement (50ms)", () => newJudgement(50)); + } + + [Test] + public void TestOsu() + { + AddStep("OD 1", () => recreateDisplay(new OsuHitWindows(), 1)); + AddStep("OD 2", () => recreateDisplay(new OsuHitWindows(), 2)); + AddStep("OD 3", () => recreateDisplay(new OsuHitWindows(), 3)); + AddStep("OD 4", () => recreateDisplay(new OsuHitWindows(), 4)); + AddStep("OD 5", () => recreateDisplay(new OsuHitWindows(), 5)); + AddStep("OD 6", () => recreateDisplay(new OsuHitWindows(), 6)); + AddStep("OD 7", () => recreateDisplay(new OsuHitWindows(), 7)); + AddStep("OD 8", () => recreateDisplay(new OsuHitWindows(), 8)); + AddStep("OD 9", () => recreateDisplay(new OsuHitWindows(), 9)); + AddStep("OD 10", () => recreateDisplay(new OsuHitWindows(), 10)); + } + + [Test] + public void TestTaiko() + { + AddStep("OD 1", () => recreateDisplay(new TaikoHitWindows(), 1)); + AddStep("OD 2", () => recreateDisplay(new TaikoHitWindows(), 2)); + AddStep("OD 3", () => recreateDisplay(new TaikoHitWindows(), 3)); + AddStep("OD 4", () => recreateDisplay(new TaikoHitWindows(), 4)); + AddStep("OD 5", () => recreateDisplay(new TaikoHitWindows(), 5)); + AddStep("OD 6", () => recreateDisplay(new TaikoHitWindows(), 6)); + AddStep("OD 7", () => recreateDisplay(new TaikoHitWindows(), 7)); + AddStep("OD 8", () => recreateDisplay(new TaikoHitWindows(), 8)); + AddStep("OD 9", () => recreateDisplay(new TaikoHitWindows(), 9)); + AddStep("OD 10", () => recreateDisplay(new TaikoHitWindows(), 10)); + } + + [Test] + public void TestMania() + { + AddStep("OD 1", () => recreateDisplay(new ManiaHitWindows(), 1)); + AddStep("OD 2", () => recreateDisplay(new ManiaHitWindows(), 2)); + AddStep("OD 3", () => recreateDisplay(new ManiaHitWindows(), 3)); + AddStep("OD 4", () => recreateDisplay(new ManiaHitWindows(), 4)); + AddStep("OD 5", () => recreateDisplay(new ManiaHitWindows(), 5)); + AddStep("OD 6", () => recreateDisplay(new ManiaHitWindows(), 6)); + AddStep("OD 7", () => recreateDisplay(new ManiaHitWindows(), 7)); + AddStep("OD 8", () => recreateDisplay(new ManiaHitWindows(), 8)); + AddStep("OD 9", () => recreateDisplay(new ManiaHitWindows(), 9)); + AddStep("OD 10", () => recreateDisplay(new ManiaHitWindows(), 10)); + } + + [Test] + public void TestCatch() + { + AddStep("OD 1", () => recreateDisplay(new CatchHitWindows(), 1)); + AddStep("OD 2", () => recreateDisplay(new CatchHitWindows(), 2)); + AddStep("OD 3", () => recreateDisplay(new CatchHitWindows(), 3)); + AddStep("OD 4", () => recreateDisplay(new CatchHitWindows(), 4)); + AddStep("OD 5", () => recreateDisplay(new CatchHitWindows(), 5)); + AddStep("OD 6", () => recreateDisplay(new CatchHitWindows(), 6)); + AddStep("OD 7", () => recreateDisplay(new CatchHitWindows(), 7)); + AddStep("OD 8", () => recreateDisplay(new CatchHitWindows(), 8)); + AddStep("OD 9", () => recreateDisplay(new CatchHitWindows(), 9)); + AddStep("OD 10", () => recreateDisplay(new CatchHitWindows(), 10)); + } + + private void recreateDisplay(HitWindows hitWindows, float overallDifficulty) + { + Clear(); + Add(display = new DefaultHitErrorDisplay(overallDifficulty, hitWindows)); + } + + private void newJudgement(float offset = 0) + { + display?.OnNewJudgement(new JudgementResult(new Judgement()) + { + TimeOffset = offset == 0 ? RNG.Next(-70, 70) : offset, + Type = HitResult.Perfect, + }); + } + } +} From 1bff103d3244aff12c6faf83454cb43bbfd2d852 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 19 Aug 2019 21:25:14 +0300 Subject: [PATCH 2259/2854] CI fixes --- .../Visual/Gameplay/TestSceneHitErrorDisplay.cs | 7 ++++++- osu.Game/Screens/Play/HUDOverlay.cs | 1 - .../Play/HitErrorDisplay/DefaultHitErrorDisplay.cs | 2 -- osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs | 2 +- .../Play/HitErrorDisplay/HitErrorDisplayOverlay.cs | 9 ++++++--- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs index c5e5e96ad9..e86606e4fc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs @@ -13,6 +13,7 @@ using System.Collections.Generic; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Framework.MathUtils; +using osu.Framework.Graphics; namespace osu.Game.Tests.Visual.Gameplay { @@ -95,7 +96,11 @@ namespace osu.Game.Tests.Visual.Gameplay private void recreateDisplay(HitWindows hitWindows, float overallDifficulty) { Clear(); - Add(display = new DefaultHitErrorDisplay(overallDifficulty, hitWindows)); + Add(display = new DefaultHitErrorDisplay(overallDifficulty, hitWindows) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); } private void newJudgement(float offset = 0) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 1124c8f5f0..43b9491750 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; diff --git a/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs index 6cf80b209a..a0b9787c5f 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs @@ -37,8 +37,6 @@ namespace osu.Game.Screens.Play.HitErrorDisplay : base(overallDifficulty, hitWindows) { AutoSizeAxes = Axes.Both; - Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft; - Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft; AddInternal(new FillFlowContainer { diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs index fb22b35736..10b5e5b1cb 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs @@ -11,7 +11,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay { protected readonly HitWindows HitWindows; - public HitErrorDisplay(float overallDifficulty, HitWindows hitWindows) + protected HitErrorDisplay(float overallDifficulty, HitWindows hitWindows) { HitWindows = hitWindows; HitWindows.SetDifficulty(overallDifficulty); diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs index 35b883ff27..fd118a26e5 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs @@ -9,7 +9,6 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Framework.Extensions.IEnumerableExtensions; -using System.Linq; namespace osu.Game.Screens.Play.HitErrorDisplay { @@ -29,11 +28,15 @@ namespace osu.Game.Screens.Play.HitErrorDisplay { new DefaultHitErrorDisplay(overallDifficulty, processor.CreateHitWindows()) { - Margin = new MarginPadding { Left = margin } + Margin = new MarginPadding { Left = margin }, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, }, new DefaultHitErrorDisplay(overallDifficulty, processor.CreateHitWindows(), true) { - Margin = new MarginPadding { Right = margin } + Margin = new MarginPadding { Right = margin }, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, }, }; From 77e9e89fecdcac72fd34e607fe4f909b2b83cbf0 Mon Sep 17 00:00:00 2001 From: Desconocidosmh Date: Mon, 19 Aug 2019 20:45:23 +0200 Subject: [PATCH 2260/2854] Refactoring --- osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs | 8 ++++---- osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index b9dd32208e..fd0a876775 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -81,14 +81,14 @@ namespace osu.Game.Rulesets.Mania.Replays for (int i = 0; i < Beatmap.HitObjects.Count; i++) { var currentObject = Beatmap.HitObjects[i]; - var nextObjectInTheSameColumn = GetNextObject(i); + var nextObjectInColumn = GetNextObject(i); // Get the next object that requires pressing the same button double endTime = (currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime; - bool canDelayKeyUp = nextObjectInTheSameColumn == null || - nextObjectInTheSameColumn.StartTime > endTime + RELEASE_DELAY; + bool canDelayKeyUp = nextObjectInColumn == null || + nextObjectInColumn.StartTime > endTime + RELEASE_DELAY; - double calculatedDelay = canDelayKeyUp ? RELEASE_DELAY : (nextObjectInTheSameColumn.StartTime - endTime) * 0.9; + double calculatedDelay = canDelayKeyUp ? RELEASE_DELAY : (nextObjectInColumn.StartTime - endTime) * 0.9; yield return new HitPoint { Time = currentObject.StartTime, Column = currentObject.Column }; diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index 4d6730c0e6..6720d8b8bf 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Taiko.Replays else throw new InvalidOperationException("Unknown hit object type."); - var nextHitObject = GetNextObject(i); + var nextHitObject = GetNextObject(i); // Get the next object that requires pressing the same button bool canDelayKeyUp = nextHitObject == null || nextHitObject.StartTime > endTime + KEY_UP_DELAY; @@ -142,7 +142,7 @@ namespace osu.Game.Rulesets.Taiko.Replays for (; currentIndex < Beatmap.HitObjects.Count; currentIndex++) { var currentObj = Beatmap.HitObjects[currentIndex]; - if (currentObj.GetType().Equals(desiredType) || + if (currentObj.GetType() == desiredType || currentObj is DrumRoll || currentObj is Swell) // Unpress all keys before DrumRoll or Swell return Beatmap.HitObjects[currentIndex]; } From f1c3a60660d035128c8f3e938f88b216d4ab8669 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 19 Aug 2019 22:04:27 +0300 Subject: [PATCH 2261/2854] Add ability to select side --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- osu.Game/Configuration/ScoreMeterType.cs | 10 ++- .../HitErrorDisplay/HitErrorDisplayOverlay.cs | 66 +++++++++++++------ 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index bffbce2a52..fbb17fa7f9 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -79,7 +79,7 @@ namespace osu.Game.Configuration Set(OsuSetting.ShowInterface, true); Set(OsuSetting.ShowHealthDisplayWhenCantFail, true); Set(OsuSetting.KeyOverlay, false); - Set(OsuSetting.ScoreMeter, ScoreMeterType.HitError); + Set(OsuSetting.ScoreMeter, ScoreMeterType.HitErrorBoth); Set(OsuSetting.FloatingComments, false); diff --git a/osu.Game/Configuration/ScoreMeterType.cs b/osu.Game/Configuration/ScoreMeterType.cs index e78220c9c9..b85ef9309d 100644 --- a/osu.Game/Configuration/ScoreMeterType.cs +++ b/osu.Game/Configuration/ScoreMeterType.cs @@ -10,7 +10,13 @@ namespace osu.Game.Configuration [Description("None")] None, - [Description("Hit Error")] - HitError + [Description("Hit Error (left)")] + HitErrorLeft, + + [Description("Hit Error (right)")] + HitErrorRight, + + [Description("Hit Error (both)")] + HitErrorBoth, } } diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs index fd118a26e5..bbed2b1618 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs @@ -9,6 +9,7 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Rulesets.Objects; namespace osu.Game.Screens.Play.HitErrorDisplay { @@ -18,29 +19,18 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private const int margin = 10; private readonly Bindable type = new Bindable(); + private readonly HitWindows hitWindows; + private readonly ScoreProcessor processor; + private readonly float overallDifficulty; public HitErrorDisplayOverlay(ScoreProcessor processor, WorkingBeatmap workingBeatmap) { - float overallDifficulty = workingBeatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty; + this.processor = processor; + + overallDifficulty = workingBeatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty; + hitWindows = processor.CreateHitWindows(); RelativeSizeAxes = Axes.Both; - Children = new[] - { - new DefaultHitErrorDisplay(overallDifficulty, processor.CreateHitWindows()) - { - Margin = new MarginPadding { Left = margin }, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - new DefaultHitErrorDisplay(overallDifficulty, processor.CreateHitWindows(), true) - { - Margin = new MarginPadding { Right = margin }, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - }, - }; - - Children.ForEach(t => processor.NewJudgement += t.OnNewJudgement); } [BackgroundDependencyLoader] @@ -57,16 +47,50 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private void onTypeChanged(ValueChangedEvent type) { + clear(); + switch (type.NewValue) { case ScoreMeterType.None: - InternalChildren.ForEach(t => t.FadeOut(fade_duration, Easing.OutQuint)); break; - default: - InternalChildren.ForEach(t => t.FadeIn(fade_duration, Easing.OutQuint)); + case ScoreMeterType.HitErrorBoth: + createNew(); + createNew(true); + break; + + case ScoreMeterType.HitErrorLeft: + createNew(); + break; + + case ScoreMeterType.HitErrorRight: + createNew(true); break; } } + + private void clear() + { + Children.ForEach(t => + { + processor.NewJudgement -= t.OnNewJudgement; + t.FadeOut(fade_duration, Easing.OutQuint).Expire(); + }); + } + + private void createNew(bool reversed = false) + { + var display = new DefaultHitErrorDisplay(overallDifficulty, hitWindows, reversed) + { + Margin = new MarginPadding(margin), + Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Alpha = 0, + }; + + processor.NewJudgement += display.OnNewJudgement; + Add(display); + display.FadeInFromZero(fade_duration, Easing.OutQuint); + } } } From 50c47568e434f5a68002d40610d1618f221fbd39 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 19 Aug 2019 22:45:27 +0300 Subject: [PATCH 2262/2854] Don't present Meh hit windows if it has no value --- .../Gameplay/TestSceneHitErrorDisplay.cs | 19 +++++++ .../HitErrorDisplay/DefaultHitErrorDisplay.cs | 54 ++++++++++--------- 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs index e86606e4fc..006773a091 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs @@ -14,6 +14,8 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Framework.MathUtils; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; namespace osu.Game.Tests.Visual.Gameplay { @@ -95,7 +97,24 @@ namespace osu.Game.Tests.Visual.Gameplay private void recreateDisplay(HitWindows hitWindows, float overallDifficulty) { + hitWindows.SetDifficulty(overallDifficulty); + Clear(); + + Add(new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Children = new[] + { + new SpriteText { Text = $@"Great: {hitWindows.Great}" }, + new SpriteText { Text = $@"Good: {hitWindows.Good}" }, + new SpriteText { Text = $@"Meh: {hitWindows.Meh}" }, + } + }); + Add(display = new DefaultHitErrorDisplay(overallDifficulty, hitWindows) { Anchor = Anchor.Centre, diff --git a/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs index a0b9787c5f..11de0696d3 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs @@ -32,12 +32,14 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private readonly FillFlowContainer bar; private readonly Container judgementsContainer; private readonly Queue judgementOffsets = new Queue(); + private readonly double maxHitWindows; public DefaultHitErrorDisplay(float overallDifficulty, HitWindows hitWindows, bool reversed = false) : base(overallDifficulty, hitWindows) { - AutoSizeAxes = Axes.Both; + maxHitWindows = HitWindows.Meh == 0 ? HitWindows.Good : HitWindows.Meh; + AutoSizeAxes = Axes.Both; AddInternal(new FillFlowContainer { AutoSizeAxes = Axes.X, @@ -83,48 +85,52 @@ namespace osu.Game.Screens.Play.HitErrorDisplay [BackgroundDependencyLoader] private void load(OsuColour colours) { - bar.AddRange(new Drawable[] - { - new Box + Box topGreenBox; + Box bottomGreenBox; + + if (HitWindows.Meh != 0) + bar.Add(new Box { RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(colours.Yellow.Opacity(0), colours.Yellow), - Height = (float)((getMehHitWindows() - HitWindows.Good) / (getMehHitWindows() * 2)) - }, - new Box + Height = (float)((maxHitWindows - HitWindows.Good) / (maxHitWindows * 2)) + }); + + bar.AddRange(new Drawable[] + { + topGreenBox = new Box { RelativeSizeAxes = Axes.Both, Colour = colours.Green, - Height = (float)((HitWindows.Good - HitWindows.Great) / (getMehHitWindows() * 2)) + Height = (float)((HitWindows.Good - HitWindows.Great) / (maxHitWindows * 2)) }, new Box { RelativeSizeAxes = Axes.Both, Colour = colours.BlueLight, - Height = (float)(HitWindows.Great / getMehHitWindows()) + Height = (float)(HitWindows.Great / maxHitWindows) }, - new Box + bottomGreenBox = new Box { RelativeSizeAxes = Axes.Both, Colour = colours.Green, - Height = (float)((HitWindows.Good - HitWindows.Great) / (getMehHitWindows() * 2)) - }, - new Box + Height = (float)((HitWindows.Good - HitWindows.Great) / (maxHitWindows * 2)) + } + });; + + if (HitWindows.Meh != 0) + bar.Add(new Box { RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(colours.Yellow, colours.Yellow.Opacity(0)), - Height = (float)((getMehHitWindows() - HitWindows.Good) / (getMehHitWindows() * 2)) - } - }); - } + Height = (float)((maxHitWindows - HitWindows.Good) / (maxHitWindows * 2)) + }); - private double getMehHitWindows() - { - // In case if ruleset has no Meh hit windows (like Taiko) if (HitWindows.Meh == 0) - return HitWindows.Good + 40; - - return HitWindows.Meh; + { + topGreenBox.Colour = ColourInfo.GradientVertical(colours.Green.Opacity(0), colours.Green); + bottomGreenBox.Colour = ColourInfo.GradientVertical(colours.Green, colours.Green.Opacity(0)); + } } public override void OnNewJudgement(JudgementResult newJudgement) @@ -157,7 +163,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay } }; - private float getRelativeJudgementPosition(double value) => (float)(value / getMehHitWindows()); + private float getRelativeJudgementPosition(double value) => (float)(value / maxHitWindows); private float calculateArrowPosition(JudgementResult newJudgement) { From 415f1802614dbcd96541746a02a8a61500ba07bb Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 19 Aug 2019 22:53:28 +0300 Subject: [PATCH 2263/2854] Delete extra semicolon --- osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs index 11de0696d3..2d05dc8aba 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs @@ -116,7 +116,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay Colour = colours.Green, Height = (float)((HitWindows.Good - HitWindows.Great) / (maxHitWindows * 2)) } - });; + }); if (HitWindows.Meh != 0) bar.Add(new Box From 62a24bf16d38ffbf9460a8025748c012b20d1939 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Aug 2019 13:18:34 +0900 Subject: [PATCH 2264/2854] Fix a couple of rotation-related issues --- osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 332e25750f..02505c3ec0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -174,9 +174,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces var newPos = slider.CurvePositionAt(completionProgress); var diff = lastPosition.HasValue ? lastPosition.Value - newPos : newPos - slider.CurvePositionAt(completionProgress + 0.01f); + if (diff == Vector2.Zero) + return; Position = newPos; - Rotation = 90 + (float)(-Math.Atan2(diff.X, diff.Y) * 180 / Math.PI); + Rotation = -90 + (float)(-Math.Atan2(diff.X, diff.Y) * 180 / Math.PI); lastPosition = newPos; } From 550311698b74769f95f35f130f72ae84e17c775d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Aug 2019 13:18:59 +0900 Subject: [PATCH 2265/2854] Update slider test scene --- .../TestSceneSlider.cs | 160 ++++++++++-------- 1 file changed, 86 insertions(+), 74 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index c5a27205d6..29c71a8903 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs @@ -10,7 +10,6 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Tests.Visual; using osuTK; using osuTK.Graphics; using osu.Game.Rulesets.Mods; @@ -27,83 +26,96 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Osu.Tests { [TestFixture] - public class TestSceneSlider : OsuTestScene + public class TestSceneSlider : SkinnableTestScene { public override IReadOnlyList RequiredTypes => new[] { + typeof(Slider), + typeof(SliderTick), + typeof(SliderTailCircle), typeof(SliderBall), typeof(SliderBody), - typeof(SliderTick), + typeof(SnakingSliderBody), typeof(DrawableSlider), typeof(DrawableSliderTick), + typeof(DrawableSliderTail), + typeof(DrawableSliderHead), typeof(DrawableRepeatPoint), typeof(DrawableOsuHitObject) }; - private readonly Container content; - protected override Container Content => content; + private Container content; + + protected override Container Content + { + get + { + if (content == null) + base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); + + return content; + } + } private int depthIndex; public TestSceneSlider() { - base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); + AddStep("Big Single", () => SetContents(() => testSimpleBig())); + AddStep("Medium Single", () => SetContents(() => testSimpleMedium())); + AddStep("Small Single", () => SetContents(() => testSimpleSmall())); + AddStep("Big 1 Repeat", () => SetContents(() => testSimpleBig(1))); + AddStep("Medium 1 Repeat", () => SetContents(() => testSimpleMedium(1))); + AddStep("Small 1 Repeat", () => SetContents(() => testSimpleSmall(1))); + AddStep("Big 2 Repeats", () => SetContents(() => testSimpleBig(2))); + AddStep("Medium 2 Repeats", () => SetContents(() => testSimpleMedium(2))); + AddStep("Small 2 Repeats", () => SetContents(() => testSimpleSmall(2))); - AddStep("Big Single", () => testSimpleBig()); - AddStep("Medium Single", () => testSimpleMedium()); - AddStep("Small Single", () => testSimpleSmall()); - AddStep("Big 1 Repeat", () => testSimpleBig(1)); - AddStep("Medium 1 Repeat", () => testSimpleMedium(1)); - AddStep("Small 1 Repeat", () => testSimpleSmall(1)); - AddStep("Big 2 Repeats", () => testSimpleBig(2)); - AddStep("Medium 2 Repeats", () => testSimpleMedium(2)); - AddStep("Small 2 Repeats", () => testSimpleSmall(2)); + AddStep("Slow Slider", () => SetContents(testSlowSpeed)); // slow long sliders take ages already so no repeat steps + AddStep("Slow Short Slider", () => SetContents(() => testShortSlowSpeed())); + AddStep("Slow Short Slider 1 Repeats", () => SetContents(() => testShortSlowSpeed(1))); + AddStep("Slow Short Slider 2 Repeats", () => SetContents(() => testShortSlowSpeed(2))); - AddStep("Slow Slider", testSlowSpeed); // slow long sliders take ages already so no repeat steps - AddStep("Slow Short Slider", () => testShortSlowSpeed()); - AddStep("Slow Short Slider 1 Repeats", () => testShortSlowSpeed(1)); - AddStep("Slow Short Slider 2 Repeats", () => testShortSlowSpeed(2)); + AddStep("Fast Slider", () => SetContents(() => testHighSpeed())); + AddStep("Fast Slider 1 Repeat", () => SetContents(() => testHighSpeed(1))); + AddStep("Fast Slider 2 Repeats", () => SetContents(() => testHighSpeed(2))); + AddStep("Fast Short Slider", () => SetContents(() => testShortHighSpeed())); + AddStep("Fast Short Slider 1 Repeat", () => SetContents(() => testShortHighSpeed(1))); + AddStep("Fast Short Slider 2 Repeats", () => SetContents(() => testShortHighSpeed(2))); + AddStep("Fast Short Slider 6 Repeats", () => SetContents(() => testShortHighSpeed(6))); - AddStep("Fast Slider", () => testHighSpeed()); - AddStep("Fast Slider 1 Repeat", () => testHighSpeed(1)); - AddStep("Fast Slider 2 Repeats", () => testHighSpeed(2)); - AddStep("Fast Short Slider", () => testShortHighSpeed()); - AddStep("Fast Short Slider 1 Repeat", () => testShortHighSpeed(1)); - AddStep("Fast Short Slider 2 Repeats", () => testShortHighSpeed(2)); - AddStep("Fast Short Slider 6 Repeats", () => testShortHighSpeed(6)); + AddStep("Perfect Curve", () => SetContents(() => testPerfect())); + AddStep("Perfect Curve 1 Repeat", () => SetContents(() => testPerfect(1))); + AddStep("Perfect Curve 2 Repeats", () => SetContents(() => testPerfect(2))); - AddStep("Perfect Curve", () => testPerfect()); - AddStep("Perfect Curve 1 Repeat", () => testPerfect(1)); - AddStep("Perfect Curve 2 Repeats", () => testPerfect(2)); + AddStep("Linear Slider", () => SetContents(() => testLinear())); + AddStep("Linear Slider 1 Repeat", () => SetContents(() => testLinear(1))); + AddStep("Linear Slider 2 Repeats", () => SetContents(() => testLinear(2))); - AddStep("Linear Slider", () => testLinear()); - AddStep("Linear Slider 1 Repeat", () => testLinear(1)); - AddStep("Linear Slider 2 Repeats", () => testLinear(2)); + AddStep("Bezier Slider", () => SetContents(() => testBezier())); + AddStep("Bezier Slider 1 Repeat", () => SetContents(() => testBezier(1))); + AddStep("Bezier Slider 2 Repeats", () => SetContents(() => testBezier(2))); - AddStep("Bezier Slider", () => testBezier()); - AddStep("Bezier Slider 1 Repeat", () => testBezier(1)); - AddStep("Bezier Slider 2 Repeats", () => testBezier(2)); + AddStep("Linear Overlapping", () => SetContents(() => testLinearOverlapping())); + AddStep("Linear Overlapping 1 Repeat", () => SetContents(() => testLinearOverlapping(1))); + AddStep("Linear Overlapping 2 Repeats", () => SetContents(() => testLinearOverlapping(2))); - AddStep("Linear Overlapping", () => testLinearOverlapping()); - AddStep("Linear Overlapping 1 Repeat", () => testLinearOverlapping(1)); - AddStep("Linear Overlapping 2 Repeats", () => testLinearOverlapping(2)); + AddStep("Catmull Slider", () => SetContents(() => testCatmull())); + AddStep("Catmull Slider 1 Repeat", () => SetContents(() => testCatmull(1))); + AddStep("Catmull Slider 2 Repeats", () => SetContents(() => testCatmull(2))); - AddStep("Catmull Slider", () => testCatmull()); - AddStep("Catmull Slider 1 Repeat", () => testCatmull(1)); - AddStep("Catmull Slider 2 Repeats", () => testCatmull(2)); + AddStep("Big Single, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset())); + AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset(1))); - AddStep("Big Single, Large StackOffset", () => testSimpleBigLargeStackOffset()); - AddStep("Big 1 Repeat, Large StackOffset", () => testSimpleBigLargeStackOffset(1)); - - AddStep("Distance Overflow", () => testDistanceOverflow()); - AddStep("Distance Overflow 1 Repeat", () => testDistanceOverflow(1)); + AddStep("Distance Overflow", () => SetContents(() => testDistanceOverflow())); + AddStep("Distance Overflow 1 Repeat", () => SetContents(() => testDistanceOverflow(1))); } - private void testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats); + private Drawable testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats); - private void testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10); + private Drawable testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10); - private void testDistanceOverflow(int repeats = 0) + private Drawable testDistanceOverflow(int repeats = 0) { var slider = new Slider { @@ -120,22 +132,22 @@ namespace osu.Game.Rulesets.Osu.Tests StackHeight = 10 }; - addSlider(slider, 2, 2); + return createDrawable(slider, 2, 2); } - private void testSimpleMedium(int repeats = 0) => createSlider(5, repeats: repeats); + private Drawable testSimpleMedium(int repeats = 0) => createSlider(5, repeats: repeats); - private void testSimpleSmall(int repeats = 0) => createSlider(7, repeats: repeats); + private Drawable testSimpleSmall(int repeats = 0) => createSlider(7, repeats: repeats); - private void testSlowSpeed() => createSlider(speedMultiplier: 0.5); + private Drawable testSlowSpeed() => createSlider(speedMultiplier: 0.5); - private void testShortSlowSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 0.5); + private Drawable testShortSlowSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 0.5); - private void testHighSpeed(int repeats = 0) => createSlider(repeats: repeats, speedMultiplier: 15); + private Drawable testHighSpeed(int repeats = 0) => createSlider(repeats: repeats, speedMultiplier: 15); - private void testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15); + private Drawable testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15); - private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2, int stackHeight = 0) + private Drawable createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2, int stackHeight = 0) { var slider = new Slider { @@ -151,10 +163,10 @@ namespace osu.Game.Rulesets.Osu.Tests StackHeight = stackHeight }; - addSlider(slider, circleSize, speedMultiplier); + return createDrawable(slider, circleSize, speedMultiplier); } - private void testPerfect(int repeats = 0) + private Drawable testPerfect(int repeats = 0) { var slider = new Slider { @@ -170,12 +182,12 @@ namespace osu.Game.Rulesets.Osu.Tests NodeSamples = createEmptySamples(repeats) }; - addSlider(slider, 2, 3); + return createDrawable(slider, 2, 3); } - private void testLinear(int repeats = 0) => createLinear(repeats); + private Drawable testLinear(int repeats = 0) => createLinear(repeats); - private void createLinear(int repeats) + private Drawable createLinear(int repeats) { var slider = new Slider { @@ -194,12 +206,12 @@ namespace osu.Game.Rulesets.Osu.Tests NodeSamples = createEmptySamples(repeats) }; - addSlider(slider, 2, 3); + return createDrawable(slider, 2, 3); } - private void testBezier(int repeats = 0) => createBezier(repeats); + private Drawable testBezier(int repeats = 0) => createBezier(repeats); - private void createBezier(int repeats) + private Drawable createBezier(int repeats) { var slider = new Slider { @@ -217,12 +229,12 @@ namespace osu.Game.Rulesets.Osu.Tests NodeSamples = createEmptySamples(repeats) }; - addSlider(slider, 2, 3); + return createDrawable(slider, 2, 3); } - private void testLinearOverlapping(int repeats = 0) => createOverlapping(repeats); + private Drawable testLinearOverlapping(int repeats = 0) => createOverlapping(repeats); - private void createOverlapping(int repeats) + private Drawable createOverlapping(int repeats) { var slider = new Slider { @@ -241,12 +253,12 @@ namespace osu.Game.Rulesets.Osu.Tests NodeSamples = createEmptySamples(repeats) }; - addSlider(slider, 2, 3); + return createDrawable(slider, 2, 3); } - private void testCatmull(int repeats = 0) => createCatmull(repeats); + private Drawable testCatmull(int repeats = 0) => createCatmull(repeats); - private void createCatmull(int repeats = 0) + private Drawable createCatmull(int repeats = 0) { var repeatSamples = new List>(); for (int i = 0; i < repeats; i++) @@ -267,7 +279,7 @@ namespace osu.Game.Rulesets.Osu.Tests NodeSamples = repeatSamples }; - addSlider(slider, 3, 1); + return createDrawable(slider, 3, 1); } private List> createEmptySamples(int repeats) @@ -278,7 +290,7 @@ namespace osu.Game.Rulesets.Osu.Tests return repeatSamples; } - private void addSlider(Slider slider, float circleSize, double speedMultiplier) + private Drawable createDrawable(Slider slider, float circleSize, double speedMultiplier) { var cpi = new ControlPointInfo(); cpi.DifficultyPoints.Add(new DifficultyControlPoint { SpeedMultiplier = speedMultiplier }); @@ -296,7 +308,7 @@ namespace osu.Game.Rulesets.Osu.Tests drawable.OnNewResult += onNewResult; - Add(drawable); + return drawable; } private float judgementOffsetDirection = 1; From b5cb59faf2a3f17602e7c2d81dc1c8970c705cb8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Aug 2019 13:19:23 +0900 Subject: [PATCH 2266/2854] Add default skin sprites and specular/diffuse layer support --- .../Resources/default-skin/sliderb-nd@2x.png | Bin 0 -> 14258 bytes .../default-skin/sliderb-spec@2x.png | Bin 0 -> 13141 bytes .../Resources/default-skin/sliderb0@2x.png | Bin 0 -> 17053 bytes .../Resources/default-skin/sliderb1@2x.png | Bin 0 -> 17792 bytes .../Resources/default-skin/sliderb2@2x.png | Bin 0 -> 18268 bytes .../Resources/default-skin/sliderb3@2x.png | Bin 0 -> 18182 bytes .../Resources/default-skin/sliderb4@2x.png | Bin 0 -> 18062 bytes .../Resources/default-skin/sliderb5@2x.png | Bin 0 -> 16895 bytes .../Resources/default-skin/sliderb6@2x.png | Bin 0 -> 16702 bytes .../Resources/default-skin/sliderb7@2x.png | Bin 0 -> 17139 bytes .../Resources/default-skin/sliderb8@2x.png | Bin 0 -> 17084 bytes .../Resources/default-skin/sliderb9@2x.png | Bin 0 -> 17067 bytes osu.Game/Skinning/LegacySkin.cs | 23 +++++++++++++++--- 13 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-nd@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-spec@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb0@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb1@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb2@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb3@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb4@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb5@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb6@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb7@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb8@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb9@2x.png diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-nd@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-nd@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..626fd91e386796be54e3ff952006082d90361319 GIT binary patch literal 14258 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-spec@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-spec@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..76fd9ab16807c2ef61dd2aa0db47c78262db03b2 GIT binary patch literal 13141 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 O@*I3?M71L9&Hn(Rv4%VV literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb1@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb1@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e99f076947aa7ca1614fe825839dd3d7b8dcc303 GIT binary patch literal 17792 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb2@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb2@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cd36a0ae169e1d876012402b2b7aa34a13a179d6 GIT binary patch literal 18268 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 SqgKcO0000l1{&5@md}*`-!Dp9 zDmuz42LJajFTVg*7DIOzM<-t;tN-`>)ZX36(~${+DhEyp`FBcXJAWsTu%M8LfZ)Gx z79mL~5kaYElCl+kw7`4F{=LV@#ls0CC@A&6@27&#XxFurfYp>nz|f#lOP_z(;cO{r@lWR7i5* zXznWr!~#-RRx}D)I0y^=OgYCl&S=^H$a3A%E z-%3DC$Ress%X)7(5IUf>FU)OR zQ;$DNVCMT()zoOZy1V~y5~lS=5W_#qIF6F_c6QF2^@qz|twocXwcMULvs*-cX)k^r zNAg9)#h{d3onqtok|~Y-Ll(OWsXiqhg_7KMet+uN*2&E+?%TX;-CET5cm=wn%lndKM~OX73OMThN$RMOZ>_PTO4%6#+0y;d4u zjpsU^x~N1Rc$NS7BHOKEJX@-)zoctC8_iu7$DJtrf^t}~iSKzDyPn2LOzIWL)71Fv zz^$QI?By6u@bNGWVHAr9U2G#M>t+&&enqkTFrajD=cA^7I`&tOFu#OLiBbg|1yUqa0Dl8rK8~~;`N(*4 zTx;GOq}vx~>D6oO?&ZZi0+C)7Ol}UaUX?AJc(di(nIfVc%9}UdSD?lD&>48J5a5Qa zoo5Q$ePkP&SrD>Y6*{zWyn(HFnOMSQXy1s650W8K(eJ|G0b?rbW0z4zvG_-D#B73o zP+|Yt)_mIewe`!&oL9unA}&m@+z-(YgGigq1(;8ZY@P&vop#C6sjxh`x}DPEJh5W? zba_*vk|c%|Z~qGnJ!}8y{3UoBIKP~6)|R1@`InMvS4}?ZXudHrl}Bmp0ik*~D%=Ns zU{;V|3Yo7g`t6>0-|>g8#r+I}+tr7=RgC9+}n=|F*rmhr4->H>EIvmQR{ zatg*<>@@sg9IarvQVI84AWSH6!F{qkBItpJYnVQa( z5>oR8(JF{7xIVHS6T0d-LF6%{j0hx*Y&;p>IHoo>?jC+z5xJ=r%AYFLk7-7<_MJjU z;umbCGE0iAr5{?lQzXZI!i zdItkkk9#BwH_I2o-D?V6wD*_H9%IeX0;Z#0hci8;wY^>bbiaARGd|D(8@2w&D2u-T z{;Yv{b!NuDxVrioJsq8q^a1g@e%;8h!tUEIe)lYqZsl4zQ+p1b!$~xx+xe^CB_HU} zRxL8Mj2+2Bzq?lMX8(2te&Wg0050RH&6!Tx(%QP*?)_^}^yi;skem3{l%{}eS7YOG zNsvrnf^vyyjuv9?_wNq3|Dtf1f$)M3{PmvZBeTfsNsj93a8ag|L+Yeq z7WJv8_T*(Pn-a4)mDG%1@Yd)MS8db(EX%q==_t3;pTilR8>=+;FfCUvz+8DpSdUG z39CS0y}HkS8Q#f@>CW7KcfJTLi_P>DVEtr|*f$CQ4^`>2Gm4CSq3T5k`GH)Ub5@Y4 z58{p1XTf(DtpPS|?(<}zc8p6phh$$wQdAp6J1`2F=tGf!t;I>4p4%Px?7DqTU!8Gj4Hmbp{(%nDU$4uBk>avb6T>1uBic0m;B1V+?PP z{tNT{&$QM|=#$?)2>n)`5E{7F^>fo+(u%Mpx11H&)&)91O7iC*Pf(*s81f*MzdZ_l zWP9@wf6mHpoE2D|b6knSYR0plE%WJGj{Y0VyAC#?=y(8mGojy3W8g7NI%}Q+>n-A^ z)zdeb>L2p*ZN$K@`5X4I>$g0ncSO3nx{$yiSuQRt+&9_&7gZ02_M-Z(#=XRnAwui! z>S=wO3$ zd!Y>_gyOmRV_T`?uYS(X4yS$Fq>v8WzdU=KBJ(#88)ah;izSAgAH2`{@o@2QziI=l zB+aZXO<=%SoO9td603FToblugAm|>LwIZ?Pa^?5WQpxLgbvC^r)RCc*=;sG(e7F|k zYf^g|2d=(Pvzsg4Avx2(JT0nl8K-SplL* z7T?$$PogmLZ>0%E=gYf>J0A#vmDZ!c;6KD5XCOkR#1YX>6&@u9>>(3k?QWO$i!+x% zdpJw>MFH^EbMg@_&S+(}V;7#xuAOo5)gI^hCZ{SeGyoI%^8RnO_HocHrg>JxSFZ0& z=a}&e@+$x2t{{@QgoVb9)4O_9N0BE- zixZgr=hAQf^ju9u+@ExTp|ZA53jE2xgq@p%20nt=lNu-sMFIs7~{z9a}Wlt9tWZ`rp0{IqG_pcf8f^7|q5 zwXW_1fSh762I|^Zq|A(G_t@31%lXE%hp&wfHk70N=Yxi;?WzPPtrI_kyc5{fQ$)qZ z{Z9QBf!vJ^()C5CQJp}`xJEs%EJCf^f-RcSZK4G_4{Xn&EzEZv9o$%>Lim?{i&s-M|)kS(oS7I5B?SNm`L{Z$B7=uN{p?`sHQ-i_~A0 zsZHtpy};XdwU+H4u?865Ed4Cu`u;{m{}x!n(FH;^mg&K%swV{1{~-R3Uw2l|`ioG7FZH#tB@A3+5XW^VY3rhf3-u&s_yWeNAm=FLKi4Rbx z)V8mNhnALb(Y#1IeVJH76j)>9JGE}VlW(w&WZ`DXK75ujRhBW;g0nsf(%9kA*fCH7 z`%UBJrxX-s&pA0;BELYa$R7v zU)@HW=5+d@;YP-?hG}1U9nG26RHEGu%zJ+eM(R*@`3aAYk3TZdrk8sgWPfvhkm8M~ zFfWifA5;ItFZBa*LRroivd>|k(-mdiVSvv%mrN(B(Q!0L81d$?_abnn24b8YSs?60 z;BS5SRJ`X5Y3cI(IgCM{vLV|k*pUp1TSr*WMup27Q<@sFyfQgyt1K8W55kBqM>$~~07Z0KzDAz3{fe9Y% z)-(jli+isPxTLXL^QuqDDH2@XS#0p&n6!`EMvz3Rg3S?<6Us;aRE2VO$+8Y6a_V+H zwXJY!Z);CnMLA~lhN{vi>G`~KVLc%bh`)p$_KMSBARp=%tvyjQGcmwOF+ELOZ%t!o z)O$)y@{Fqv(-w!7ozRw>mA&AbV^Othj6ohToyh2GFRurB)=dM%71@>Ww-n_DWoWj8$g7A5scHS|o$ecOHq%MM4>l)4YG z6!kO_%e1cznrR{f=JX6L$4iG=c^WhLp_2kw1M(P~lWHMCH4xlhCGb49Z8Bqqq6*hu zO?E{9ybUqp371@D9uS?b&|Piw-Ab3Wnm+B}OilYOTMzpj4Pf0t$`=l$}!(Xwt=4DO6#_CkY z)LBew4-5Dak8EvmF{>^(_`jZSSDBQ!efTsEwss0N)jS`D?fs05dpDisZ_NGrvG;{W z7@`3TJ9hF93<1R^f=b0Fkt=bgMh>NEMiBCbWFss($aG{w48&}xQF`kpXmQI~=I@3| zY+_jva}UU97S=GHRW&viaB+3LUI22pCKV`_gmFRM9wiXAqY0#3B?pJcCiSKoYmWL!Cq$Bk~ z$~8x;+?@08x!lb1Ecx6btP&+31mGy9vO)zQLK3Hj# zO{2!*PN}qvCUNu(7_Y2T@uY58^>Ya1d9fMW`$}834E3}O z&Y0T*vcW-~^RkddmQz%C&S}tv8Q=LG1{SUsZX_|i*xF}@k2^P!1@aMn?1}4TQ7jD1 zEzUqeF>G%l&^qUKWC6PFPAV0|sW|5f=l?d=rwUfbKCZVZ^r=d03hVDYn_V#4JZ|E9 z8F(@--u}6mY3C#o*eqO%WPMK)OUPp|Z+=<`SZqFF4>neO-$F!y1v~T2{2op>;>jLU z+nmTXxCzrNgL|@OO`A&?%54|BR>18Wr@eZPRd@bTJpZuAzml(gN-9ehs7?5zSgdRf z>wqHgj#T91*0qk!Cgvyh8zYF*;M`eszPy=^ z$)eBTee*LjEtms~50@kI-fQph&UHvbhaRbc)fjwY_8uNaAE)Rkl6@otLX{$!k;V?1 zlRnEp7(!he?&?FEg*8bx+`xf_6m&~E6BIY?4{FPgB6N%B3bp{^Lz>f-W&h3cAuiTH zo5!N}^-jfwhK5xJDSYGj9q^Ia!AePskSSj&@7r~x{rd+*PP&V3*D<|U&#x{!=CD(J zp2T(9C>AY3iveb3jPpEh2LF2R95|>}d;`T5$-mr&6+}AV8AB!n<_~P!n%{x$bjpkK z%&LFtt%S0GyZs4;;4F!35QkK!U_pm{VK|BaiE z4HsiC?|s9tOw!od)YFXiS81MFK}s{ifq>B%#WGo>CXagtEj#ZtnIr-|V5X*ESNv7a zt-{M7vJg0v*DA}mwAxGitIw|9r2tTM7;9VEFI7cJ4(hpV)l!(olZHK0fN@#XCr%c|ltIe-Do+N^gCeJ=fE%-J7ua(eB)uF-j$`mC1|(O@psP&)-=)JDlg|Ynw25K?E0P+L_>Sc(~g_-9dqME?mUKZZD{VWEuP zWCQ_7v`_vqc+gv8CnsYnND_^_%p^N!F{`^gi+;gcUs?D3S0#n6UMzn(_^|?&!&|xD z7VQ$Q@H^6oOPFT>J&gLK9^gSuuoWeY$ztxJwBor`aP}4H_Hj`!ysPtDG0i9<<+9)6 z%=;-4@LFYTsQ(+X6NEG`U0~K(ApljzA4ucZ|71*g>1p7_)|vbrgCQT8@oU^ZDxq7~ zSkR#)(Emt`xlq)C(s_Rn`6kJ^*WO%OwC!~xjulK0mp0Z1gWzg2Wp@z9NsbSRZ5+eK zABkdo&b?ei))m66Lxr6mklJ;OxqtreR*oD&W*;J>LEq=46@5Ecd+S&d*ReHr^xrH8 zWb4sHmSwP|bB80Q0f1IyprKowK?0V-FPZZ1h|R*71a|8BxedgcWt8@Xo!eOSP?L&+ zv;tYoiKF?-a!42egT99DH{w-E+CI_A2WwA5-(LC7MMT_Rp{rQHae5m4ueo92hQ!g7 zZ|1noa@1GZ-go>x7mCz-QfkUm&hPZOaC(d&$~%Syyo}Ysp}vlVOH?UqTlWVi1!Y{8 z@kN81k?s|?3r9>8Q#@;xdR+Q;4)l{TVnKVdNh3`yBtY6ye73rSf^hyrgk!>;JyPEBQ~?1bdl{CUrQy2sM>WXcbiAD*KKbRNN!FA zs>LLm^>kw+J1S=sb|p8E4Y1c*0Wd)ZY>>B;MT)3SKW7gIlzp4yH*(s6PZ?9aTxCkB zSL%uxzlwa+Nr5D62vWjECKBq>5mYf65ST?1MUsZdywH2}og-`cIay4@(wo(=@hr1N z6e>RKe7-^sG$u)dt@wF2>pORA2=@BFYKCtAt1a?jJm@3-w@cV<4j_ zaW5~1So8Dqzjkq{A{|I9k%%P|{$Rj!88B~T!1_ng;gM~PYo5L;j=5PIwa5jt@7rCl z(r63-wN1r_pNuvwokUu9`By1{C8G_bz8cufq`+t1ne+HyqrNwb7EKx*(|$Kvi~hsk zP)KaK@cYGaz7p64GjeMz+hIIcw3u|ZTEZ5e^?AWcD`g|Md(Tf;IR=0;c8tcRKyuvZ zScobXK2J<+f|vH14ghyqZrlh$DP2@By8U+y-jV&q(b##RQDsI#OhPazbquuzNr74Q ze=2V=g7hR*eqm*pXON$6zjVD91Ic=R%|Lt>_xeIW`SB9f=3zO3k!4U#rc`dAfk&?N z0_7kS@@aJQD~uUqR5=<}gKNP!5SQ77fsNl7waa5;gXH)~#a-5nqxhS;uv|Njx{|Yh zJu+;kEPFtb255QcYtZxPv;5RtH!bo$GB!GWX?^pCLnO-JdA){_W=;{KHdyi!K+`lJ zg)FDvT^~z7N>#T-7KnM!VJkwOgL90$>swrv3>o$9KId`0my$56O6EQ7)wyd`^^Hl5_+$=8f! z{?%^yMn~Wbd!|;akbSDfg`WLBpxePbcmi-K6hN_4#c2C7{K|<;>0E@$p8IwC2b$%& z=~Hc?0na3mrR=le9&CcRWMhLLJT=&D$qaQ$;GDC`=cJ-$4P{kT`2V|4oln8BCCS0TEAbYRrL4kg1cW_n%e!YHFLWOg&A|xE;wfciD}Er$#GA zZSaUhA{m?MbNqUTLjpsvx>{r%{%i4)!C`L2a~lqYJzwI-G5gNFAD?v=AlhWvaF=Vaz_r?N;7S7+H%TrvZ?m(-e&!ZC8w&u~;by+aBBG8$js5{ls z;|(pz;~UE$6HX%&PBzehecuXy(!d4c4aq5ZS&*`PUOr2@ zhig(Jk1VD()YvIbV;3#`Oi!-LM04Qu%#TjYc^7;{&bvz~&hR-V0K&~twNo}~hi|V$BJW$SMV~DCbIG8lSE(F#0)JYgU>kCq1)EI5$<)wmQQ5Tf2d(VCJQrt^NG7O}0 z$%I?@2AJL~`Me7c21vaUe6uoxu)2VS!`&^rMG&;ou%heLA3|8N-Fw&QR)bCy2)oK5HCM_zK`d8+!f{fHub)Y*wg1bYRv{jH0q1Krr|#h3D=sqvqiu?V5T*vY|W} z;-KcL#va3``YP4mfS#wQ1B@q3q zH9ML>@1t2D3#;QLj8tAr2@7eo{#Lw*yZKNZoN(29X(Rlijbti*QMPx?Xq*~5*r%DvFm zHW7ly!MH^+8`4oxvkmnUTnivvYeNZ6t9gydE{YEKqdd?x93I!tgX#L3&{(R^Q9%|Xhsi84`_u%`WT7^v zG3wQEnv@Y0&|`d712WgOk&E8-C8>5Re(coIz@NlvtwhfUXTX=4TOpfHRG{t$pv%DV zPPw8qMUD>i+Rd%z(HgNH=y_6s7}KlEH>c}`cK@FiK%jh35GM+NDzY4ZDyj4iFw^gD zSORbqGwc@+{Xke+WfF&bc6qtEWd0{Uh|Zn9OcvYIllorDmZc?`Pisb&y3U_V49D+Z zHJXXUKRLHAI3Ia+{F5n$Ofog@ZZlo?h28)p)(#v6m1k8Z{(c7ob!9_T6dQji3%QTi zjl`~WmIXvfu@p*}aK`N87m{@SiC6-XXCwN{f91w6(nLT5FF+xmGjvkL=f-y9r5|bM z{l?#`#%adSUEo%I7foRFia`^fHT3F(^Z{-y3pjq*UQzZvrY)3qEQ>Pz9OJ))BEbo6 z@&Z>c;|W++m+l=l59y>I=rMO078XWZO$}i|!F}<6-!pkJ6xd=!U}c(5M(KARN2OyZ z;1sF$LPgu%>$n?NZHNuN|3(+!vvxmH!o^LrJGqhp*nw7-C<-C*Y6HIC#Ocuo!*Kl< zd&z3n(m%9jZi7T$Q#Cdf+zmk^@{{L)GBUnylx&I#MijWdv7!g`e)Q+ zJjDJ&W$n37ZSSRP-uXaJFrbtgNgW(3j{d@H`eoiabPN$xba8Qm@N@bce1io8RCiEJu6CWl(rW8A{ z@q_mfXklUDg`KjYE$Jer!l|qDE?!A`jMC2->au_kY^W$z!BTZz58XU$%2ksaS8!+! z_K`s{?GWHU?N*}c4rqUK<(F_OYyOn4B^Q6yK){t3xAlbyne2nL+$e#jD>L%#COrMC z6mP)gBjQowtW|TN_mz@}Oo<)Dmp5K6i_!i1XNkhYuS6aF`o=z%6+#a;Aek|7eP@H+PejQKqDqgwb^)fHs zx=dsT3%I%>4q(29R8M+-3xdd=kuI+G5~|JgtW5uUk%EEHj;SV#-#B{RNP$t**(s?< zbW?b&txQ1CefI0KSjmuV`=3s^%RVqcSR*h+`W9?WHm@kdEDJ7h-Pt}t!@0m5xM-P~ zD}}hQ##5!L{S6`@r>rH9p+hitc`c! z1mVGE&D^q+5!Eiah060=yEG~O-p-DDEOj+p#HrqrLu(Q!7X#2_aQGVg&kLt%JY_~H@FZg9^7gx>~4iOQ&u@qPf$uhDQ*ZrndM?w{TqbkcLw;iWEI1Wn%UubbDQ{XvC32vOaEdW%*9 zfQx%~kNT>f{#gYF7Bt5~S(1%`DuL-+RGd@Sh$_JTXt%HWNeR&0j>e=KnVM#|$Kga~ zWl;;eOnog9;3RIod$PY=mZHA_w9p*vb?<%pdo6zu;@Os9E8Gtf3d9K50yW1QPbSG7 z7hcnPc2HohRrgovH2U_#_n@{!wG!6~x$siHZT{##2lk+VtDI!hpF`+BMgU#D4G1j2 z*GyBOrgANP;r&~EnjbSdWENI2%@^C+4&xlJK4~mH&iFh-mv2*ay08eyCS=tl&;{X<2j|d+ zzH8SaYjKw;_J7R`Nf6_!zW6NiuNUvu@jVlnePar97Y8hc8_U3zc0;Ck-gBg6vq9kHgsV9yh zV**3}H6WrsN{K$4w0d5#HRQW7e;xV|PX}^k6ep+LwokXf5FPbXj4={G4@*u6!sW{3 zL2rt#-T(L^}gPRvmWGvIs{=0Mc#-Le3K4#@F#2I3<568;&-l%yrfC8DIi9Sq0X*<^n$i52gQwDyimA z!rZEx#GLC`Oj{#r+ir8~$;(07!HuPHxw9LlV-{=sO4c*vxQ0`+=OG7t7 zMa>X`+TnBm&m$ipS$+t4)yQAJw}S9+*PbJS#YXeMz7;#~Gd`E%g+9Bf5-r9x)={Kd zXStbwPTR(WmOvlwb=k-c6J)UGG6*rzX+oc^UGtvX+pYqEBKQHNatMX)C;xxdR4 z!iAgBL>@R7hKbeC*%HHSL2bT>H@OA4@7B%w*^+F;o+N(GkoXI2ZN`|jI)s+R+FkS{ zWs6Fn=|SvKJ#us6avlpJb_*>oU-~=3??ON?fF3Xk$bginy^k}5vin6~sAy~MjMvR@ zp`wf%h=&N#3733{kQ88qDvGTIqYL8R$`xWCF`=!%Y%=eSQ6cZ}L9uK+nzdyg6sY`G zZ9?Y=zp}0yOH<)GXszWp*d>?!yXx9qS+)&<$eT-mh_(Z`3Nwclj;}=`1HtA2Lx@~^ zH=*5*K9p-?l0C|TFR=J1qNVfCS2|n%ZDZKvQqh1YMhyna8)@#0GssXkkawFZgxnh0 zi~czF+ewHIN01?sL9}r$rbG;c!ZMcm(FwWZcP6F5s=2y+{H!dx8Y}SE;Cm4Cz2Ax} zzOG$o@5_8Wv;x$kcL;1?Z1Wp)EKN@@!KL*-I`)tJb7R8rzp-{bud0W(FwSpJCTI}y z4{s;l=ZLydOQ5auPi8s!19>|M_*8@W%^0x)K_$467UoMcM6YGvi4vuHH1a|mBoSAO zU>~Rz2ba$mzrGopV@m9nVdYAjtd+(osh^)eFPKqOfBk-gU${l7ZUzbn4#z{^9yBAQ zc#M8(g|2t^29Jn;CzA{iY+1_ z@X-_4xn&d>F~TlFxKTLl`yKD?&o>{>80rG(Yx^X)CbDF_HsSQ`Gn+51H1I}b3qH(9 z@NrH3fr;+18(FF}Pi~h0Oi;qXy{z2D1L=18-L%gjOhX0`SrmEO>@RZ4KIJC6DCG#y z`e3XKLsh%BO@uj?;q(=S)(=)O2*Us7pJc9H!o`z;4)lb54HwauN;cbZ`3!V^|98m= z>QYQQjqWHDjoAeCO5aYa2yx2}p)L#Y7+)NYhOonRyBs?Ry(HQVv))3&Hib zZosz{MO;aj+gm-_>81%b$F%jvM(u`}0l><(P(wHeYRIOcwY>iqU^oXMmOx9-9YzLm zFl%|Oe}*CL^qmqR`H&&h_D<*(rQz@(yt#L1%m4&q8!%ahS*TD@;kk26iRz?MQ{>g& z@}h5Z{1B2A(;@soeLexq^qGU%BBT8i^eu;5T1j610j|BkWXSUIE)n!G8I`WlA{}qA z15xfngL03ntQZljYh^azPJ8cA_lpc|teq{8&0DedGq?@?5m)wz;Hi{}xNmc@+>iGD zg9JLpR8%i|a&6kGmx1WTIEe|ZfQTDs2MGokd8HRqA^bpk1b7s)o}k9Vz4}U1+vGZq zn4ag{1wg!H#m+ET72iI8hAg3W!>ugsalhaW4Eyca7t*rK+xk*dT_9F{KFB)OZWH1# z2J*)WTSnEh#WjT9w%JR%v918gx)nM}=RyBXAee$R;Gd)&^9?EOBtDaDOpK=bBA ziApUQxAO(;lIz-SlIr)06)g4tS8ONy-Fv}NswP~a3-4js^xa5SZ+rsL`o_wIDpy+% zOmEP421>ne^?5A*DDoWli2y-3b^e=`U~>>e$Q~Qn@G_;MFuZGoO9PIgb!W4Vj!n`0B2 zt8vFE0C8owy^L*R(?(9&sZ%MWVkD#Q@n6+`h7QNNX+uf4norD$+mv83jqMj`_>#}a zua1q6Fl3!&C^a<8;)mozcV0GsHq#upXd9=la#-VRqg{EW+IJjRj`th_7W+SXcf0yB z{qA?K(1nE$I*dQ3 zOrfZhWxpl6eR7bRbY$!!4dG~;#VP9z*BQbeVri`1vJSAGppQL{0yanOT&wGBb(-w+ zVg5{wElG64Gq^=kFKfg4Z{IYOtRoUbn{Uv>3FnF7sB}k0uf?~)ID;Y1yf+?B$^8FR z6cixu0Sr7_Kp&}BL|xNeOR4<_LmK@4Z|5iv zuLLo!w{7ch@6Wv^S_=GWgFDoDM6jSwLTw8n+K2rQeWG*Sff<+06LKfmz_`2UQ!+ax zkJ~4Q{Z|6>G(vqK$J@yaC>@fOpbLQf*h$crYbz1Mem|vaL7=Cp9}!*d(^ClzaLGk} zgIEqh-0&i?NM%w%EP&d>M%}&Ek#ukFwX_4A2oAZSaoA^6d-{DAhFc7?v61|dBWFxr z5L-0)gQ>W+NsR8j3?Dur(qLeRs{HDw9UgIP{a&mc#LEw{+IP90^iOL?4$#@Cp5SM^ z&ZU`-4gL30_KlE_^7oe|^3TGqPuU&zT|&1EKby6YyZRFIe^h!L3NXYA9fCJ4Qv}|r z3~r66S;6F|FhM5!WRE-2u_rA+?JA1L2igb34yjJ_&HV9x_oQ%Cm%R;g0v7AxysJ2A zK>3PNt*YRAf(~Zn^Qc(QGI*s;=tRnW2xx9n#DmIVKQrlR7ng&S`ux6(;3W1xY%l10-*LE~RX4?#8&izv?87zs4MB{mJK%seV)hgaKX15_` zWFrO|(VeEH!Fokq`gYF(J%Ht}h`2&(Jbj@uCG}5X1~d|_`Lf1^;rDQeOWY zuM6pv1A%RX(Q#3E`0K}7|0NhD;o>d(g)4}j)SX|RX~P7+^lqkUP6gnh+{KcRNE2NF zHwe5m*6wSs0=)==AB|Pn6vs%YkcgPC`g{;Q_!?sH)U4NaAv(xHp49E67KmW9z=+Lv z_&D%_@_pd6XE-QFVSEjjpJK&8drJr=`SLXczHMM{-q1*27WiQF4fau~_z_FfcNDvT zcF~f0tc_pxV328?Fp>4#s(mv5-E2enFox~@?G4`(WJ?ja9TmNhSbB=ZY(=4Ci zn1Og5$%oKa(*$Qjo=%%u-oI2JyIwL>bs>;Yt=b*w)(VtUl{0A ztq3NM5W!wty+dlY5>{WIfn>M2{121|4X*+P2^_RtBry$9QFl-q58peujpzpjd<1BiAh(Qo^Jps@VOku9%% zdcht9*?fKQ!QMXIG7PHLLts7kX#(AxTYkq;IMMI)ZdQ;;=6HzWjMOY0-jRq?WwaXx zM;#KwUUVc+qRNiNlP@Ik4b|C+QxKL_7ELrB1acB?9wu)N93P90%_a;7tDd|$ajDtssn z%y4KjsQ)M;PqOAm>ZzLvC{4kyc-RaQ$C?$nHd}^SaBjnGiKn}F#geK%znK*o|6701 zRln*1fyXKQ8Xa|AhK4$RO{m{v1QeVqb7vEY{5n;V3xXd;hrbBGzy!0B(T~eI_Adh< z=zLuDVeAC+Xh-I#a?kxDfNRm3Zprqyj=28L+wh~RIuVc{*#m0sFg$2QL0~Q%5^6km z#u01x8uq$T^8uH zr#*Yu6M5S(27bjce+)j^Humrcaes{h;8(ja{0}mqclJQXglNDf@;QPDouRDV%zwhYb@58-)_z|pv$f)=5R&-USJ-#^b-fM|49fUzp{7X$Xy48Vm)yWP z((t2bT@0W@st{*>=1NKk+VKxbs;=$_S|7S#TKz-`@eejY!O$A$wHF9k3T4? zsEfyLW7jmFz;gzFiV)AcBLQwFVmjc?3s8@1zXHZVsS0a0gxL0Ny$6M-6tz~pf`7~TM-%l>rPX{(>EC1=+-m61n&)<5{ zpT=K(8r!m&6*;&teHnJqjE%+pkt@3A2Tbr4aje}EuzW^z2D*lu z6zTAyzJ^DM&|%A}ue>5BDB$$Ph7Dk{|H_}RO-@iu{Cg*ivo(V)?cxm>X`W_3f117c zc>?Wq_Y3IC%YlS_H(&6*>|+R{gCdW_^eGEw%%(*SRIKNiXGHj2__O?2lkoM+w%wz* zucFEm-4_PzxG43MsR#aE-M&CvQOY+~rR~s(aKHIP-evDw0QufQv?e?GgD0R<%iJj-@iBabqj;I0=oS`WlSStq zv5za~9CUYX%Hvp+zC{fk;@?+bA zX{u_-N#hTQbfO=VvCLJ4pQ;f2<3$NfQKeK+ApomN_SN{S=FwNpW4@hBUg6i%g`V|s zt-C%0zK9&IL^vQSU%FfRhZ4V`Pa=RB0v5h7qDin}CdG0%aSo48B%B!2YQ*7_pBwDA6McdSL@o+QU^iwTc*gJSMLqK zh1J`Do>I3<;cyxft4bBDPW{6q}%_HpzAd?WZaN^ zkl1&CWQGLfeX;zeriAMYptSvvSd!di=n&d9qLUP@w*lSxV$aQ#B0`D>O(gTxkYQS7 z`0wPT&0*x@vIIKf8R7kV!JRx75mbZVAWrQ!f2lA(XJ_<5`;xC_sBArL-3U#q_b;X2 zT_kzF`RahV$tCwZ&+2JAO#Q=hPc4npQ|i^rpY`OqzwyyM?}dcHP|*F^-phxDcv}l75{djT|71@R zeIo>q5qzu>jk(Ah*!s5?n1-FGWuTjtwYmzhH-*I?rE7QZ>L(78E(#9cRy@lazh})J zyW`Fq4>U{i><`6F@VMJe=M}1k2M$Z1fra?tk(NB#n$#g#+YX3HGOlDk-|9eved4uu z_L_?TzK7vf@kj;QkF`k(?ZQWJgzw+YsUYMHxWKg%%8aKv=7(BM>q<< zC^Mi>|F%h7W7-hD3517<)AH86fM#IaF0A%ZjisUCh<`55Wxy6hs-oZXLOw?o((gsJ%M{*S2ag{ zV`?v-c41Hdc0rynbOu!0Xusfw4eG}OCWu#v{Dil1DIA&vbqV6j{?lZ}#{Ga_=Ymp& zb`#H{U#2cQo0z~V+pO&anJ)&b<4-`VSbkwTQ2(<3-W4S7jd0l7Hcw(K_=WApncg_i z#rNsO?Iz$3G>fv4a;q`bttfovtaA}_wtBlQ&JSqndDN%gP^r*zs*^jK-!rHL@-@CddD%G9*;ZG_apv7V-p{2#{*`h60E;zK!*eB0c0)B zKzImWS{>-JgW;CAED(djUzc}b>f81Y;tTkE(^_=bV}AjmdyXSs9gbD|H>$0`E9|e|3P63&x$pKyKRv#o;7}Jx8%<5ke$f8zY>hLhBim1o zO=|&O0~s{8e;qsv7^ZyKK6M0az&}LT0L)wAG{N=lr+8EAfZ0gEN*#eP= z`Es=6R_Et~VZau(Ijq^6M?abF2c}jMj@q?@#rA%K(7Qt5eO#2ArR1jgl;+#{HTfqh zerUwOv}>bzR1mAfK7F!KrjTls{~~2+-_yrdsV~?kS61$Kfau`01c-N29-Wao z78*zx`v4q&?&|5;CrF(XW@$WIY}Zpay^&?;h*4BP_Los(X9Kv;z2;0^fVh?zqRb^} zlk$zCd@g~;m3~6I(Bmowm_z|DWV;LyEe=oPWS0(g@E>+f`|0RqLrz7EjT`hRw zsm!zNQoLX@Y$BmjjUW(C&A5c@NWeqrSV0grIrvZSJ-_$tZrHZ~Sp66$jnESi; zJ5KH7N(exl`i(V?!TwYTO3+pR662pq6eHYug|c)=N1r=WqqwHV%@FW_8(LZ}7W}I- zEh84&@2H_5!Pk>4J3j!yM+;z1Tdc1BLYFdNQMbe^mCc4m{qq20RI>uLIZpS2kNSO`%%M{J){kAt5~2V zxTHK%2LN*xO`%4HMhjoGIGGQA|GtE600pRL#A?kWTMs~=TOmqHFSb7f=->H)ST-{` zslZ>opX_yzX<##OcG3c3VvBkE0JI_Ik&AO)$LquV`6n+iFT2VJZhpS3AaFmaEChyo z_?zQj#0cQ@uY3JQHS!Ge4-m~`^BIzcp91!7wc!mPZtA21@>SeN<+nZj1c}F)Q7kf2 zPTwka1q=jP((o%0vHugQ16BMsHrvIipj6o}1q9%ELdeC_r%(T+H?Gsdg$rd()4szw z|1sx$sGp!@iAkEKA(P1XP)_9y}$W;?zyLhbN)R5modhM6cdunfoDjiQlF&L=@&aYJD+&|`R8BJ zG%XQrp$M=&Vq?dSRVPfCa7a8JpQb3v35uc|s;X+U<#~gaJenfZJOJ08ciwrQ*6aJ7 zJ9q8@0KNy{Y{M`bG)*IRpqh2QZK+ghQ!bZ#Igv=bwr$(CWpBLkM&wN+z}DZIo_gx3 z;~E3uQFc+7ov9&0NCv=MQ50{w=%S12>d1&c|M|~idwcsd z&iVHY!;plg(zgXqvV}*Y&@3baX6VuwX$4fK;?4BEa?KCQu$GpVy<@T1 z!IC6R7X;x;ilXeMC`vbgonjRsq>~VG7l7Yib=6fpK2AoKWn6dNb>j@fxWF)suNj81 zXD*l1a=F}g!!TaXX0t1k$>f$yCiCi|MT;UY8UeQN*vy$Thc!1h?FNQA#->){LjggC)ae!-`BqOHKM9&gCGd|a?YnxN)Od_ zeW<2sTL3(lOeR0*>gw9|;)^dvDVPYb{p&NBI(6#M0Rsl?BZ}gI@p!yZ6vh1sA>$=U ziW5RM>AJpvF}5_5$+Q}Vk=?$1JGVx>mSe{se>?!7xw#oTcI+T6EiIswilQirhG7gK zgs6fbsJUE@Ynrxg;lhPG(M!jJMu3g1+D6BY9V;JjzyZyoC^krvlujm-MlP32=(=uj z&h=a_hh@u_l?_-z2y(d`m1UU?95|3Z@x&8s|NZyJzyJO3=u?O+vP#*QF=L`d=x6r- Y0Z!VEjc(%0RR91007*qoM6N<$f_m)to&W#< literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb4@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb4@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a5b19887d64f0040da387029545dea15d85669c9 GIT binary patch literal 18062 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 Hu0mjf{m45K literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb5@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4bb01f0e88229c6433c6363d7f7c725776372683 GIT binary patch literal 16895 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

{E0 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 E0O3MVi~s-t literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb6@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb6@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..859e0aa4c1f0d548eda3fe4c6c02f7fc4af1831f GIT binary patch literal 16702 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb8@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb8@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fcdf4ed4a4a76808dbfe41e54a45d4433dc1320b GIT binary patch literal 17084 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb9@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb9@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c990cf0fe6784265e2419070a27459fa6f7b2fa0 GIT binary patch literal 17067 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 => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White; + + InternalChildren = new[] + { + new Sprite + { + Texture = skin.GetTexture("sliderb-nd"), + Colour = new Color4(5, 5, 5, 255), + }, + animationContent, + new Sprite + { + Texture = skin.GetTexture("sliderb-spec"), + Blending = BlendingMode.Additive, + }, + }; } } From 3b7cee2d45ef027b93d71950ff4ccc24843323b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Aug 2019 13:50:16 +0900 Subject: [PATCH 2267/2854] Fix LegacySkin not checking for @2x hitcircle when deciding sizing --- osu.Game/Skinning/LegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 3eda76e40f..7c621ee87f 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -59,7 +59,7 @@ namespace osu.Game.Skinning Samples = audioManager.GetSampleStore(storage); Textures = new TextureStore(new TextureLoaderStore(storage)); - using (var testStream = storage.GetStream("hitcircle")) + using (var testStream = storage.GetStream("hitcircle@2x") ?? storage.GetStream("hitcircle")) hasHitCircle |= testStream != null; if (hasHitCircle) From c4251d512e069cfee758bafe8f85dd7113d360e3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 20 Aug 2019 08:00:09 +0300 Subject: [PATCH 2268/2854] Simplify bar building --- .../HitErrorDisplay/DefaultHitErrorDisplay.cs | 69 ++++++++----------- 1 file changed, 27 insertions(+), 42 deletions(-) diff --git a/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs index 2d05dc8aba..a9dd7ffcca 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private const int spacing = 3; private readonly SpriteIcon arrow; - private readonly FillFlowContainer bar; + private readonly FillFlowContainer bar; private readonly Container judgementsContainer; private readonly Queue judgementOffsets = new Queue(); private readonly double maxHitWindows; @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay Width = judgement_line_width, RelativeSizeAxes = Axes.Y, }, - bar = new FillFlowContainer + bar = new FillFlowContainer { Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, @@ -85,54 +85,39 @@ namespace osu.Game.Screens.Play.HitErrorDisplay [BackgroundDependencyLoader] private void load(OsuColour colours) { - Box topGreenBox; - Box bottomGreenBox; - if (HitWindows.Meh != 0) - bar.Add(new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(colours.Yellow.Opacity(0), colours.Yellow), - Height = (float)((maxHitWindows - HitWindows.Good) / (maxHitWindows * 2)) - }); - - bar.AddRange(new Drawable[] { - topGreenBox = new Box + bar.AddRange(new[] { - RelativeSizeAxes = Axes.Both, - Colour = colours.Green, - Height = (float)((HitWindows.Good - HitWindows.Great) / (maxHitWindows * 2)) - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.BlueLight, - Height = (float)(HitWindows.Great / maxHitWindows) - }, - bottomGreenBox = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Green, - Height = (float)((HitWindows.Good - HitWindows.Great) / (maxHitWindows * 2)) - } - }); - - if (HitWindows.Meh != 0) - bar.Add(new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(colours.Yellow, colours.Yellow.Opacity(0)), - Height = (float)((maxHitWindows - HitWindows.Good) / (maxHitWindows * 2)) + createColoredPiece(ColourInfo.GradientVertical(colours.Yellow.Opacity(0), colours.Yellow), + (maxHitWindows - HitWindows.Good) / (maxHitWindows * 2)), + createColoredPiece(colours.Green, (HitWindows.Good - HitWindows.Great) / (maxHitWindows * 2)), + createColoredPiece(colours.BlueLight, HitWindows.Great / maxHitWindows), + createColoredPiece(colours.Green, (HitWindows.Good - HitWindows.Great) / (maxHitWindows * 2)), + createColoredPiece(ColourInfo.GradientVertical(colours.Yellow, colours.Yellow.Opacity(0)), + (maxHitWindows - HitWindows.Good) / (maxHitWindows * 2)) }); - - if (HitWindows.Meh == 0) + } + else { - topGreenBox.Colour = ColourInfo.GradientVertical(colours.Green.Opacity(0), colours.Green); - bottomGreenBox.Colour = ColourInfo.GradientVertical(colours.Green, colours.Green.Opacity(0)); + bar.AddRange(new[] + { + createColoredPiece(ColourInfo.GradientVertical(colours.Green.Opacity(0), colours.Green), + (HitWindows.Good - HitWindows.Great) / (maxHitWindows * 2)), + createColoredPiece(colours.BlueLight, HitWindows.Great / maxHitWindows), + createColoredPiece(ColourInfo.GradientVertical(colours.Green, colours.Green.Opacity(0)), + (HitWindows.Good - HitWindows.Great) / (maxHitWindows * 2)), + }); } } + private Box createColoredPiece(ColourInfo colour, double height) => new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colour, + Height = (float)height + }; + public override void OnNewJudgement(JudgementResult newJudgement) { if (!newJudgement.IsHit) From d337f9b482a9144d85d8d12682b0a0461a8f3bff Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 20 Aug 2019 08:03:17 +0300 Subject: [PATCH 2269/2854] DefaultHitErrorDisplay -> BarHitErrorDisplay --- osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs | 2 +- .../{DefaultHitErrorDisplay.cs => BarHitErrorDisplay.cs} | 4 ++-- .../Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename osu.Game/Screens/Play/HitErrorDisplay/{DefaultHitErrorDisplay.cs => BarHitErrorDisplay.cs} (97%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs index 006773a091..02773e0561 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs @@ -115,7 +115,7 @@ namespace osu.Game.Tests.Visual.Gameplay } }); - Add(display = new DefaultHitErrorDisplay(overallDifficulty, hitWindows) + Add(display = new BarHitErrorDisplay(overallDifficulty, hitWindows) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs similarity index 97% rename from osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs rename to osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs index a9dd7ffcca..26f9e3a5e0 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/DefaultHitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs @@ -18,7 +18,7 @@ using System.Linq; namespace osu.Game.Screens.Play.HitErrorDisplay { - public class DefaultHitErrorDisplay : HitErrorDisplay + public class BarHitErrorDisplay : HitErrorDisplay { private const int stored_judgements_amount = 5; private const int bar_width = 3; @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private readonly Queue judgementOffsets = new Queue(); private readonly double maxHitWindows; - public DefaultHitErrorDisplay(float overallDifficulty, HitWindows hitWindows, bool reversed = false) + public BarHitErrorDisplay(float overallDifficulty, HitWindows hitWindows, bool reversed = false) : base(overallDifficulty, hitWindows) { maxHitWindows = HitWindows.Meh == 0 ? HitWindows.Good : HitWindows.Meh; diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs index bbed2b1618..e4a630245d 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs @@ -80,7 +80,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private void createNew(bool reversed = false) { - var display = new DefaultHitErrorDisplay(overallDifficulty, hitWindows, reversed) + var display = new BarHitErrorDisplay(overallDifficulty, hitWindows, reversed) { Margin = new MarginPadding(margin), Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, From 9f64e0962534b6a4aa149ad24d2d0d18c1815ee3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 20 Aug 2019 08:45:51 +0300 Subject: [PATCH 2270/2854] Move HitErrorDisplayOverlay back to the HUD --- .../Visual/Gameplay/TestSceneHitErrorDisplay.cs | 2 +- osu.Game/Screens/Play/HUDOverlay.cs | 8 ++++++++ .../Play/HitErrorDisplay/BarHitErrorDisplay.cs | 4 ++-- .../Screens/Play/HitErrorDisplay/HitErrorDisplay.cs | 3 +-- .../Play/HitErrorDisplay/HitErrorDisplayOverlay.cs | 12 ++++-------- osu.Game/Screens/Play/Player.cs | 4 ---- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs index 02773e0561..a148bdad67 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs @@ -115,7 +115,7 @@ namespace osu.Game.Tests.Visual.Gameplay } }); - Add(display = new BarHitErrorDisplay(overallDifficulty, hitWindows) + Add(display = new BarHitErrorDisplay(hitWindows) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 43b9491750..02432cf64e 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -15,6 +15,7 @@ using osu.Game.Overlays.Notifications; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play.HitErrorDisplay; using osu.Game.Screens.Play.HUD; using osuTK; using osuTK.Input; @@ -33,6 +34,7 @@ namespace osu.Game.Screens.Play public readonly HealthDisplay HealthDisplay; public readonly SongProgress Progress; public readonly ModDisplay ModDisplay; + public readonly HitErrorDisplayOverlay HitErrorDisplayOverlay; public readonly HoldForMenuButton HoldToQuit; public readonly PlayerSettingsOverlay PlayerSettingsOverlay; @@ -84,6 +86,7 @@ namespace osu.Game.Screens.Play HealthDisplay = CreateHealthDisplay(), Progress = CreateProgress(), ModDisplay = CreateModsContainer(), + HitErrorDisplayOverlay = CreateHitErrorDisplayOverlay(), } }, PlayerSettingsOverlay = CreatePlayerSettingsOverlay(), @@ -256,6 +259,11 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20, Right = 10 }, }; + protected virtual HitErrorDisplayOverlay CreateHitErrorDisplayOverlay() => new HitErrorDisplayOverlay(scoreProcessor) + { + RelativeSizeAxes = Axes.Both, + }; + protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); protected virtual void BindProcessor(ScoreProcessor processor) diff --git a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs index 26f9e3a5e0..6539ce38a8 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs @@ -34,8 +34,8 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private readonly Queue judgementOffsets = new Queue(); private readonly double maxHitWindows; - public BarHitErrorDisplay(float overallDifficulty, HitWindows hitWindows, bool reversed = false) - : base(overallDifficulty, hitWindows) + public BarHitErrorDisplay(HitWindows hitWindows, bool reversed = false) + : base(hitWindows) { maxHitWindows = HitWindows.Meh == 0 ? HitWindows.Good : HitWindows.Meh; diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs index 10b5e5b1cb..422e151d8a 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs @@ -11,10 +11,9 @@ namespace osu.Game.Screens.Play.HitErrorDisplay { protected readonly HitWindows HitWindows; - protected HitErrorDisplay(float overallDifficulty, HitWindows hitWindows) + protected HitErrorDisplay(HitWindows hitWindows) { HitWindows = hitWindows; - HitWindows.SetDifficulty(overallDifficulty); } public abstract void OnNewJudgement(JudgementResult newJudgement); diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs index e4a630245d..1c61904461 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs @@ -21,22 +21,18 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private readonly Bindable type = new Bindable(); private readonly HitWindows hitWindows; private readonly ScoreProcessor processor; - private readonly float overallDifficulty; - public HitErrorDisplayOverlay(ScoreProcessor processor, WorkingBeatmap workingBeatmap) + public HitErrorDisplayOverlay(ScoreProcessor processor) { this.processor = processor; - - overallDifficulty = workingBeatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty; hitWindows = processor.CreateHitWindows(); - - RelativeSizeAxes = Axes.Both; } [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private void load(OsuConfigManager config, Bindable workingBeatmap) { config.BindWith(OsuSetting.ScoreMeter, type); + hitWindows.SetDifficulty(workingBeatmap.Value.BeatmapInfo.BaseDifficulty.OverallDifficulty); } protected override void LoadComplete() @@ -80,7 +76,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private void createNew(bool reversed = false) { - var display = new BarHitErrorDisplay(overallDifficulty, hitWindows, reversed) + var display = new BarHitErrorDisplay(hitWindows, reversed) { Margin = new MarginPadding(margin), Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 9ef8cc4509..e7398be176 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -24,7 +24,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; -using osu.Game.Screens.Play.HitErrorDisplay; using osu.Game.Screens.Ranking; using osu.Game.Skinning; using osu.Game.Users; @@ -74,8 +73,6 @@ namespace osu.Game.Screens.Play protected HUDOverlay HUDOverlay { get; private set; } - protected HitErrorDisplayOverlay HitErrorDisplayOverlay { get; private set; } - public bool LoadedBeatmapSuccessfully => DrawableRuleset?.Objects.Any() == true; protected GameplayClockContainer GameplayClockContainer { get; private set; } @@ -160,7 +157,6 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Origin = Anchor.Centre }, - HitErrorDisplayOverlay = new HitErrorDisplayOverlay(ScoreProcessor, working), new SkipOverlay(DrawableRuleset.GameplayStartTime) { RequestSeek = GameplayClockContainer.Seek From 596ee150c6e2db6fc3963ca237afba868432543a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 20 Aug 2019 08:51:41 +0300 Subject: [PATCH 2271/2854] Add xmldoc for not obvious const --- .../Play/HitErrorDisplay/BarHitErrorDisplay.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs index 6539ce38a8..f68e17c908 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs @@ -20,12 +20,15 @@ namespace osu.Game.Screens.Play.HitErrorDisplay { public class BarHitErrorDisplay : HitErrorDisplay { + ///

+ /// The amount of which will be stored to calculate arrow position. + /// private const int stored_judgements_amount = 5; - private const int bar_width = 3; + private const int judgement_fade_duration = 10000; + private const int arrow_move_duration = 500; private const int judgement_line_width = 8; private const int bar_height = 200; - private const int arrow_move_duration = 500; - private const int judgement_life_time = 10000; + private const int bar_width = 3; private const int spacing = 3; private readonly SpriteIcon arrow; @@ -127,7 +130,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay judgementsContainer.Add(judgementLine); - judgementLine.FadeOut(judgement_life_time, Easing.OutQuint).Expire(); + judgementLine.FadeOut(judgement_fade_duration, Easing.OutQuint).Expire(); arrow.MoveToY(calculateArrowPosition(newJudgement), arrow_move_duration, Easing.OutQuint); } From 84530b7839dce1db5481ce52ca86a9803529444f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Aug 2019 15:02:07 +0900 Subject: [PATCH 2272/2854] Colour the correct layer --- osu.Game/Skinning/LegacySkin.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index d6446625a5..26ee434bb4 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -43,6 +43,7 @@ namespace osu.Game.Skinning public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) : this(skin, new LegacySkinResourceStore(skin, storage), audioManager, "skin.ini") { + if (!Configuration.CustomColours.ContainsKey("SliderBall")) Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); } private readonly bool hasHitCircle; @@ -347,7 +348,7 @@ namespace osu.Game.Skinning [BackgroundDependencyLoader] private void load(ISkinSource skin, DrawableHitObject drawableObject) { - Colour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White; + animationContent.Colour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White; InternalChildren = new[] { From f72edb8bf8840139adf03a8a125a09373167a20f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 20 Aug 2019 09:03:31 +0300 Subject: [PATCH 2273/2854] Add missing blank line --- osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs index f68e17c908..d8ae3dd9b0 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs @@ -24,6 +24,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay /// The amount of which will be stored to calculate arrow position. ///
private const int stored_judgements_amount = 5; + private const int judgement_fade_duration = 10000; private const int arrow_move_duration = 500; private const int judgement_line_width = 8; From 9fbc8440fc069ce732f6a7961366269062fdcd53 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Aug 2019 17:39:16 +0900 Subject: [PATCH 2274/2854] Add actual testing of animations --- .../Resources/special-skin/hit0-0@2x.png | Bin 4020 -> 3009 bytes .../Resources/special-skin/hit0-1@2x.png | Bin 0 -> 8378 bytes .../Resources/special-skin/hit100-0@2x.png | Bin 5653 -> 3461 bytes .../Resources/special-skin/hit100-1@2x.png | Bin 0 -> 9934 bytes .../Resources/special-skin/hit300-0@2x.png | Bin 5004 -> 3928 bytes .../Resources/special-skin/hit300-1@2x.png | Bin 0 -> 13096 bytes .../Resources/special-skin/hit50-0@2x.png | Bin 3861 -> 2966 bytes .../Resources/special-skin/hit50-1@2x.png | Bin 0 -> 9138 bytes .../SkinnableTestScene.cs | 45 +++++++++++------- 9 files changed, 27 insertions(+), 18 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit0-1@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit100-1@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit300-1@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit50-1@2x.png diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit0-0@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit0-0@2x.png index 9d2ed3a7fa3d5bc0aa43098e9247fa3f6a7c6f80..37e7e9143f63e30bcfd3de38c46dac951451f43d 100644 GIT binary patch literal 3009 zcmb_eX;c$g7OpI^4Ja}_dKeWlp;b6-_5~7_u*o9BP9vy|#H4^INhKkW00Pl=T5a*r z9vwhI!*nA|;|Qp@MoHm5fMA2qd|^`~yVy7#;He&2od z>R?1zppDfZtpEV92@dj)0ss_Zq5umsyFeLk2be4ndE&>^ z8az(G_rz~zgpxzWeqf3qC`$@PXNAS^vUc%Ud_2bs=b>OD076j4#VLfTqBOR`6aNmE zjo7AT5+3)?MYhWm|GucW&1*a7LmfF(OIkwI4YUqMk2eBC^Q0v z$)=OpR4VSng-5bU`AO_3|9}sH)Gj; zQjjZyq%jbbx-bP1DUb|GOM%2VKR=V|ESz&FmnRUJ7F|q2z3feV(1Aw_M*xx5c(fLbHL1b6ZDwsR8&Sqv4Zmrwe zV!JYIHP#umNonV68E6@SJ|-sm3?42jJ!;=Fms~E_d(cjiblcE}&gDM+s!7^I4_&fB z>o|(G1-wlk11#{Vj8BKOSkZrjR4PwY9LzjZbzz-N-tDe3!?~*Z0*_O#R_G?H2HWc= z?#n$+m)OHK=09h_9NiW3bJzoC+kHxtoYcnns|H);la{P`^+okk)B%V9SipNR52s2R ze+Qszd*hV=4hXUK&V2!!;n1}Y?cw8UYm{?fJEn8W5#=l}dnj>=0Bn^vQ?bgmGkwID z8tdXLOl@`5{+)Rknvzn4THi?mP|hiqiE{-2_PlU*I+oyy?)s+hj-O37pv~^=A+np% z4uMq@w8xPN-=`@5_MBuV^A>{W+Xdsjc0e{nNq}ec6Z|F~J!-uUYuhDnrPceST z*E+y;P0C9lN=k+C8Romnhc#;o!*j}$pJXivGQ)tQR?BLyyy1to!h!RZJm4V5Bbpm z+ALAu@LA1E8~A#~=v-MCXYj%3;8`7{HX76A>Q&@pXYB5}0FzpfH<}9DB?h<759%TF z$QztPqe;{Epf5GH39DIa-A#FuvLI|m_te2TEAMvmN#0w|xhCVt0G9GCwWVXrb3O2= z?s|vueAnvcl2ck^_1}&kt*#uXue;LfI1w*P4yq0r!E`XjwB(qA8zbYhA_ zfX>d{b?eEG+6#!D7wQM(p4ItU>#{oFjiPn~A0xj1L>o*QdO&S^HZ{9%*K=ot&Z|RL zmtWau1E1^DPYN%X4>m(*j7!h&UtSm#c~@X*yL&~gSE=QOL^E-f;_OpZj{I`oiMPW0 zvr~}>J3pz0CP;b9S1B`ZP10VIpDwE#oN_Q!o5vkPXt!^bl~lbY&Ht!03|!zqiAR^! zcKP0O0UFw`ebq8$rAcabWK-`@2d*{!LH<1=$iOU47kf)*A2)1`yIT6vka43g%!D%z zdKX<3SngRZ6}^dADI~%6AVO6*FTDK_8|AEFdbKsQ&FwpB>SZDs2tVE9Guz8nC7wOG zwCc&iVgyGQMCuBVXQ@?(yq+epoEJby?{05P>4nz3g&?2?3juZ-SpbvbvN+qq9- zd%{XP0vsxV4Lq~)Sa^>LJ=5KksGnL6CjatJV zr#)3Kd)bh>(hfn07^Nm|9p0*(Y}Fs%{3KX6xV49J)8tOMUOF=tm=9kbo>F%UoJMx( ziUaH`0SoWj7Ul6R+tr-;_z|5u=dmoiMDtXow^%uqPi={RD=Mit$QqZqn6J}!sb4)( z6`W)n6t>OxTC;|Jq203|QCO`4V7rf=NsFh#r!zhs(&AbAKSL_DhRsS8aNZ)>X>IhT R0@F`*@WwFzYTv}Y{{lUSa}NLj literal 4020 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 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit0-1@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit0-1@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b75c71927c6671c72bc8ad92d25235067d5a2743 GIT binary patch literal 8378 zcmd6McTkhvw)c}56$A@Sx?mJRAq43VdhcC82p}ayN)mbrEh8N)1v%uQ#6azUTYqn=|*1Z|=;wnc1^T*7~irSK0IIy&??s)X$$|I|l&3c})!! zBLDzVh9H3I45dSQ& zAn|gMh5l11b6o>&Wt<0!TTD*m&Whjct z;C{Al@Y{kya17?pzWzb&>1BlaKWhA2YEKh?Hx%3m<%#q5u&4OL@!H?a6x;pxg8mRv zw2{{LaH2TH)>Q>(?~OrWy);#1p_G3F9h@Ab#YKh0B*h%WVJJ}v5tyi$h$zg~Rzwo! zAS5IvA|dP`B_byAH_pG~S5sCI7giJ&S5guYyM0?#N=ZUZR8?F`SVTlfRY_Dy{BK@O ztf!YP)*kh@Y$uBBzj(#|TV82p50tGJ&cg(UbNxFC4DRE+aGv*ZZrsYsf4nZm&8KT? z?}Yucc=L~={<&`zl!ucK%0bNohvEK*rKO$zjTRz8q9V8LMQ_7|B_!-<8kDNjiv$ z!W>2H>?K8n#e}3p9iV^lJN*Az5Kd7F{wG}iukiVMi4v24hW|Mg?r4PB;)!+Zr3o@`w@NgjhydhYAV?{xR>a))03@^wzVfhBr* zYT2r*vvV_T=l6*394SQt=Qt_gD>L=XOO`k}B7vT)CfCfT2dw%(#@FZpgof;JV*r3q z4gkuLqCn{`QQCiK%1seXMQLCFAW%APO8XB@6)2+rGK7$1LiMKt1C2u)a&_#+foXrr zAyVI8_Z6)FRKQYUr_t9PS8tLu()LQ;&{bfi?LmZj`$J^Fm`wpPokm`!u`0hh6nXQ)untg4bWd?F zeD}(`^LiNk7W^mKd~?h zT_*s$q{Er}O+_)e^QKgBcKQx`@r?Chaab$NCsNt;7wSDH9E4#E@< zT;hGZ3kiJpg!r~2Vl05+#lfBWy_Zpse6kr>7Ms0U|t(% zWD%)*uk~o+&XVVal=0ToMaELs^Wg3jzIru)rf~}CrdXN_edaT8XCpX=6~vd~UVWM1 z&I|B{h4@b^$d!%b-wzAhrH1I#tlr;^WXHL#4j=AQv1%>8)~d5^nHFLK5Wh@KnX$Ej zYmYWa(GXCvF;@5tS{=zg+GHmxmtKAcDjGM%VrVB6*Hq%&nW7cg1guvyYx3)INWeke#$n-<+_Y8$@Q1Rg2Z%Jc zhcXE3$HRE*I;?A~hJ)^|8E?k;`&IRKI}YMX?*MHnj;hq@!%F}ysG12}0~WFwgalV( zIpr-cm-@b?1#N;kf;-yQR);>UCISn2DphU8W2=*bch6h`;AwTS+~7U(>x-?#Ptc?3UOKqR8W#5E;e&S2bqt|3qfaj${XW+nWub|MEv zA&G0bLv8Ew)BX;8@;foACAC0l@6zBB2Jke09MyQW|4mhzKpz%RF#>qA9)hDMY(MPZ z0)26>iUDp>gAH&qI;vVlt+8*96^)J_J7NZV&aJ3kvmT=cEie~V$1U*N{6Ndpo#aPC z98|L=AF`hZ;o||<%b;)g(EFHr?;+K~pj)@%is=YGw=4ryha9@asCt*U`sr1^5Ar4( z$G%N`G!P%v?1$!sWM-Iu(n^Qj>6FV76mmbFdN+9kSdlcbNeEn@Y#9m5wCZkfmpHRB z6$1=EyjFJMQF>f|YmaaTOfBxGd8JT9QP8~<*tbSmj4r|~a<*>P$0#ELfMohZ*UA&f z&#tQg+5xG64vqG&nOWUaJCK#!OxU~o=ov7NxiE4XQ(s-qe*eZeQC(RS|sqM-rtwx=ZWTJH7 z5i7dXC%BW&bz-!TDx0T5>m9Fvp}lIRQT&=j-nV?2q4u=5)8Q}$k4VJD!12DDqgrWS zQxp#piHn7qvTE)R=tlx>7m)fZsAQL4ON7PvIiqdD>!+#eJ-eEp&8Me`(S7c;qGkHU zVYIy*zV@l*%Git%UL=^L1H`q3M8(@os9u=<8XEc%S&S$4L5D6^bp;xA2g~0_`>fTr zBN>hb?(3gf0VQeimJODidx!os*2L^*hc=pg63G0ZfsVjz9nNJ*tzZvz6IBFE(Rx4` zxwf@aU$p#C(kAeQ*TUh?kRV8#~-ND4jo4{@2`ckVloJV>t zd9bJZ+B20do1i-?+d)OLbft(#WodJRH)6YOAPP@-XVXWV!koi_YTMPxh4rQPd2eUG zM%4ep(n=NLH7^rpo2JrVzV`Ir9{JkR*%U%Y051+iAg5=^8C}8IA406&oo{zW%ILaR zlw93;$z1glii_U=QZT$$QFXYX5DY$IWp`OMc7tTU$z1-xTe&KiWr?M>i;F=^HK4iz zlG&D-+WK=6`1y^IEX}+K3^|xjD47TpJ^{Hy-QGq3D!?Sukq>HlXMg<}J69F^!Noik z2dS`C>p1&LNabe1_?s)7rSiNteSO9-DjA0k!BeLYbL#S1n-4Btng~Ypd>Sfp&qzeW zj#K86lt1KCsWt{@kovuu4xnI`@8^W*lX7^#a4JNct;FR7t-3f@i3M*1a*C2s{CSl=tSl!Hv0{587PYWctc0X+P(QO z7$Gxx+@JF^hn!c!k{@FtxbaOd$n4$KrFboE3R+cDNV%(K$erqjc?}VWImVv58m{5@=igBxeE|%uuS!sL%g2tY-Gfd6!3O zn;Y#a@6FJN8~S}}+8?{T49y0R1Rl@ zg9e|^R#Az~Zw<|+KFSzYdXGQN-80iX@^oY24WqQKS8%Wt@08MJfoCbhVtPO?Gi;H` z?_g&+ug{Zj^td1lCO6uu{j5%Xn703H6QLvJMd|ok=CHjMQ5i>8&*PuG8?Iq3^H*<{ z_?hgc%sy@R3H9Sk6zzZhwyKU+d3(8t{bJ5i@V;?Y(7_CuQOZ$d_eS1{*1EGo1Tv5q zzs!Fq(kym)gKzvCXn~Hw7B}hqNKd8B64RZWXozqL~ln0HG@ zGp|L|pJ%^LH>?RR?p2?Ed-P?XnYo>~^&7vRxc?pX3u!sN>W@e9tG3^NDUsT@ESQ9y_0}VPD}UP8k!{4IOZSY zfgA?cQ@vodh0v;Ko(6J#yh^GAGLZ`GNqyjcGP?af~4LehJCs%dyYV+c`aZ@7DX zO#`r)e-R@Pxb9I=JWv`Gu)Qc|9qKvh-=HDOk{`0-LVvWNl^3U5D9LND?mr@>XQZ0W zCN?-0e|tarBl#pehySS9Q}4Bbk}U8c&mbqFzKIva*ZBcQ3w`=lRe-(PaIP=oDH}ZZ z#|kl3VOGFjX0;QF&+;gXTz()?)jgK944w5ddjISMo1^Zty%KZh>|XF%mG0+s9f1ql z7n&fMzxRHrU4^sIxp0lhwd;LlM>n3^R}477WLe^3EWECn`iKhFy0NyHG8QVvGQIGz z*PR&Ncj+RPwi6It?_im;uRLx$|}u#y_(ywAzrtqwlp^}dk>hL4{q%TB#k9X(dG`TVVGp_(=($tf8UVirH;R7*In6*|p?TGqt&-_;m0%hKB zczx1!W16+q_-a1igw&s+1d(*;PiuLXyQ{|Nsv{7km;zjYPxXTbz<@i_(n(m8D*fF{md8JfJ!QdtJZ>}a2KGI ziX+?aHO-X!Lc|)`jk7w9{JG_A$xJ6o5BSPg^w7i`ToSy@n?_-LLoP(-0gJ039j1;d!|x&M#YQ{&$~niy&Y_o86B2aBP6gco&KsIDdC+`VGB_<(LRG) zYUSyjE{So{Y1pl0E_Ym^WosEkWkwL?!(pFTTDDtR_$;jDNqg-4~r$~XLl6B=$1PW|TY66mKBnVo3n{tV5f$uyZYbU0$dRq-! zIx(Ag)jtPi6z7b)1kZ7}equkR{;A)qtG!vIVji<|9L{h0eVfSmp^V+Ij8T^8c~fkP zpKv{s*M2g;?NoJ=HdIItz?#jT+!C7{990ML-3)utJ61_sS$j1yeH1EYlI)d{f{WjJ zlR3dr8j^iqdu4DfF*SAew$!thG1e%!jbPW)tj_|Ui+Zc;l@VdDBPCzJ0>XX$_SOQTa7S0^v4X_iKhGd zg{<9L|Jlw4KZ#$jGRv(L)j4`vSE|lwWjmTUpjLu%R&?4+?omxg02)7@JfvZ`7}|C; zXqh$-O2@aU0e#LoD*+Ip0(t_;92t4wH}5KQcFOLo$>&Q?lvmUThLIiuMr|%(h%Wxd zjWOgCMAYyVN$##&j8p8HzpqTqllc7|=CdP34e7oagJS(xA66AxUp$@Jr!xH|^g3bJ ze9UCmW+#w}8&#&3%7RqxnechlEKhu#`jJ z<%rr@Yylg1WHI{jq%G&A#rOO9-h+X?sR&$Bz!8ebKoYhM3~>87EqE%jUAeV}gqC3j zA^^Q)?_+i>x5cMh_&Z69!g{F_UJO&hX{-HYPu~k(obo|ws;>hEUz=821$^?iwe;7p!$jZmDt1_4QlX@arC|FE1#H$VL-*(Fagx7&yP8GbFuJ5P_^S9^z_I3V@suF9)fF6F9jNM%^_f}(uFDK-CbSrBCle8gY`C4vd$h0=&)ODpO1)j6K1G3&?Vc)Vw{HJMlKr`8VFGea4 ze*4a`&u>`EoaJgr3D}l=fT9~-4V``<n^_0tq}LcLCuv|yU5B9(t#RV zrcj`_>SN!;HBME46C_GJ{ri=?jRd)(>kpxWb<8r0;x78kZGJJw`;nL-|Ufr#`6c6X0y_G{R_*8|LR8g~^tF8nV8$0q{ z=ouFSV)W*ocWpfa+~P5rA1$KlI@lljXr`$Dg8#<#GxJc6vd%Uz`Gw-a8}X-mu?%lu zZHB*;n5s^0-G5Q$#Lrt_tK0Kibfnh8tZc*ov)p~I-_xWG|K0p$O_Zd`ofa;idHi#F zD=cy=iN-7amz-I-PKD6EI(GNdGd7VU@z0OM)q@c{ty84O#m=@r^Q^r zV%$R@og|H^YjTRWvM(47Nr(3Dr1A0y?>~$KGkCmU4!~93^wbFg_21Fw>4_Q_RbnoT zeYGwYpO9i;yPB=V_nhv_668g<$j%6j)mzL&%DTY5h<;!XI5Jhaar z@ifS;*f}Q7+eWfUz!$HWs??`51YtJ=k_OO%e4yTp53kmwa&(M*+9dl+-E)>=ezr;B|{c$|@4@^4d3hVBh@=GT3XtjPV6m)|crAeqNu`|Z2N0!=pM@l|ar zTUm5xdkpo;59lSK1|MH|4f7h!+8Bqg=^up7tOs%XM%oF1XOmvJlbn^FDN)yG z5u*O38hAo9#K9yOZb=kB>ErR!xD=+czAp5svhC6O;%aRTf5{Hu*c8MSeA FzW}dVh$;X8 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit100-0@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit100-0@2x.png index 777af3bd415263824913549c5c4c708c4d6e450a..79326674086568e677792fe57141e52e9944c33b 100644 GIT binary patch literal 3461 zcmb_fc|6qVAOFterbxXgM+RHAtugnBMg}>SY&X*b(Jj?!Ji z5`~tfQ;yP5iAI+~W|T6f5j)!2-@abEzy0I4`~34f&*%I3yg%>fb9}$gHz&Y<&0GTu z0{{T#dV9GC0sus_gaCRvnkk%jI$AR=7I=gTf;iCv5se1|s~Maq5a!LI#ejhzjggda z3v>p6IbvpTs36quTQZ%)a-_}bIEq^%-)IM8GF>|*x`m#kUOiV=3O zPbz{q7rWm`h57}+s2m;$!#U#NbQBf`Baj?Xcnp?AS_wlVQA7lih(KZBC_EX9B%{%= z&%;jRjmL;42fBNH_NCdm*u@G2TrvV75{Vo|7)K5-27w}xNC+evfkwkM8gPCRTR;=T z*?jvi2<{-C&SP=~Ob#11i%5&&2n8;78m7M;fyMRn`yH6g{~Rby$Pi*07lCp_B3P{1 zbA8h03j)Fa()delesB^OL3g7a7QShNSx zo#2LZbHkEQC=Zew!4vC&C!sMIq=y^U4gbZ~o6Q%{*mUrVZ>GlgXIt#w+LEa}kS5^p zf;pV{FI5l_%Moz+u^cXpN}bI*3HG%gjm~7x3YX6&>bG;dgFI#;$nfNGSg=o#CNuwn z1qO-5py*f>98Dn5;hI9C!HEnU79Nd>q7yM_9Fl}(*nPHV{J#-IXhv&6p)s{sK0Qg8Rw!Q$T6{oB^r6Bg;*S-M~6i0LKM)TOSv z+f4VGmS}}T9U-UYgu8_|U*5sjX9(>(iADqOed@hNl+n(Yzv50k=&J3yus-F@hY!8L z%OPV9L=SKU04QJrWB`B`0Q>Z6_{XAu3Fc3f{s+N~tsgh~*qWQ0Kb*eM7*U|!E}1JS z5>D25sC%zbIMbJ7d-8Ru;uNr1t#>K}>wOq4GXTZ!gA-~?J5-VLNQk%xtI3j?$2-J zJdOpgLg$#`wB7FS*3(62!-nOkQNv%x+1KyR)C*e>xJuHR)Lu{`e_ex9hxDl;@)PhR{nCPiRw86iuyS?43v7Yvg5>@;&yRz>3yd@TSofLF zy0Z!p>5Yoc5_Ua4#1vTGGQ$E3B|XPCkKTMSoo6d#32&4(9-UdN<;pr&?NisSN@x~Z zey~~B6*!Q0T<#3@3T?XE-4b)|fk8vJ>YF2*DTz5{HFeKoJ%C^=lxTit4?0{jJv~@6 z_(BiYyyz(FHZt8#RkqDsm~!#0Ho9nSNN3e~TEcq`6+ z>JRK2|5{Beb$+qz(B#E5ixs07MM?eJYqM<=*Q2$)CYu(pS()C}-JK`^+BrxCq!vKJ!x#QQlh`K(c@uxsKHHL7 zcb;rWd3Nk}g!m~2dRl@_diZVZVGb)O1NOpm*O^tvQhKUvCGSlx_0&1#)UGkiXbUVa zJJ1j`a_Hct=*4m8E9I!IWny%`)|+x>T#X7y*zh2TJe}q|J|i{aOIFLcP9UMkW{z|{y+7@g zBZXYOzu@HI>&;b9p|E?{NJ^@){kGytdZ$bULp+-?yq7h$@9NQF*uC0yTZH;mSF`W%UqTPtk>c1@Vx{;{ue>f~$@{=LEbw8)&idtelbUdWHaPqXO z0@3tdSEBIUU-RU2_V&a@+B-;nU*W?iBX{1r?`u`oCfglVlwW;g3&J5()Oz_ilBZIR zS$XQ^O}-mGw;XrfZVm8bF0{_syrfz-@~Cn_46diYXR0qx84uWJEbxTvf0$Z1aJT)Z z+Z)GyMz#>7i-F3!E?8k%`xe1NZGz4weNjmPEYHU@v~bW6lm7K@w>-Yqbe1Wo zBEHB^VhPu>3>8&I8JAi|E)1Ab?d$h$@+f|r0?&6o=%nrySd~e)DEDm=E3}%3wkE5t zWpvG@h3H3WSf&MO4k<$JcvnV?vdz>xn@hss;HF z6U)a6n$o}#S8VuK7QbfC}}ocX1LoO*M{$v6CIPT_W$ zgW$Z%!8&Ghmm+}LUwIvAAy(iAxLrvHop$>S@jbKwdKfXWw`NN-4`|sUO j{)ql>-g}6H6o?KWI_G)Bc-VCS&C9aPtiuX literal 5653 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 Wr=9MwmK5XvL>cIqJgU`pj`<%3F8Egf diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit100-1@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit100-1@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0b0ae85972b22118b1d3116dfc4f2e0915076d73 GIT binary patch literal 9934 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�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 S;=}F{0ZMY}vT$jO@c#lO1J)@3 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit300-0@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit300-0@2x.png index 7ee45a7057ca5f1e11d9bea30375c92e139cf01e..441c3ec21f8a88762c00ea80eda09a7a32faa334 100644 GIT binary patch literal 3928 zcmcImdpOi-`~Qxj5LvBNgh@$Z9Oh^S!y=*vWi(M}%#567hM8fU&Ay7>EOx6mOGNFK zMC8=OIBh~IhZZU0EFp(+Cd14-+S>iSziY4ekKgIPP1I;zAQG8K>@xc(|ssl2P*MA z$hEszj)&O8`86Hd|1yWdGAIf=unuWH2 zSE~*7CtGL}5IAF8unon}|7awG;u`6MCq)L5&}3~(EZ968BLSdNSVV9*HHgN-jG(p1PXtV(sYHVU^WNd0= z0)vqrH6bk-4v?bmcWM9llTf5J`Bs&XjKNc$(V`Ri;vkloWLptNUkqH`&HZq19 zL7@Xlz8KZl3FtHj{Od@+XbI)Mnx%f+DGZ7*M#IV4 zpY6&2*9aO(NEvOW%l}B9FCs}!ZZ7{Q2Fd1+a-z^AHN=nb%hzRGv~kD!V-TUeWQP;4wnysm=xE>&nBD?{wpSY4HI&vD#on zE73Wu^w-P3L@Xu4VQN$^e}}2F_mq?pzyW}*0Ql3J09XNl6!4#m{!1{M&(yzA`q$l` zob4xzYGT{{-~oac-|*k&$H~`frmec}ayukKLVlZ2_t>$8y!H-GhqQ;>@X5-Zs$e#}v8bjo6j+u&RhQURyNV>SksZt9Mmv zLH;d+UDKza0mSK4{p=etBCy$>A>d{2ks742&mmj!9@y;qBU8m>_R``E2w(LfF7EJ; zYp596_=e?4!>u>7%FBYWsS62~xR8aK1CL6re-^WI8ojq$3hgy*+uubi-Vw#+Aw^|+ zoN&@Rd+jr>_0*x;+AMmP72%%3JHwuj5>E<)y&Yjb*J*OhpDl zGa*uKV~zZlA{yYUy~(|vg>a*0cu$~;Ld)^}F zGvmTUyKvXYWv|SzkL4F1pbi2mb#ek zGwlcZQ`2kizZ8Al+R&ELbnaPRhftc>sFUB^&DSizGfvb_f}YyPS*o7`P{~fB2ITsB z$(GbpAJ4l+zWSkI**@8EXr7QWZEvT!mJ-@8?a}pSTZ5E-X+qQR#Arw8ZWb5(5;`Dq zYo31mtZe|>8zgSVc$eQ7$PHEw$#R2+o^#i2RA$%oiuVTNI9fXT$v&@#yB!rC_Jw|P z*C@N;5l`A}IQn~cdm$#l@$5FA*c8_~p(4jYJ{c8w7`Pt6*3Mc2jWl~vw5aoPcV4m+ z1u;|i)JwfxM)~7`$Aq`rLihQw1B$0*YQk8B=duy&9uV{*39_JH>2@!9La2S5=6eX)m=gD%pSIwo0nANxS)!Lvh35nax*%o0NgcUEX;sl}o@SLh&ud+v42H4QmU&u=a})l_aej1s%qFv9stb>MTTyLqQjmJMaK;DuY1Jb$gFRv z!)k3o9=3l$0Qyp2XJ|#<>Bs>oshYZE zIzJdc&TR%4Oym+eR->-k3E#2#8r+!vdvY!yV~(jo#Xb1)t}x$ig`yD|!f%$f`G#29 zj;ndr+t=^C&EmE6J3aHy$E;Ur%hz(>6qZJ&>PTBzD-36K zC^Z_BX%*Y1QFkY0_!}-hJVL~FJhfd_P@}sy@x(FD)_9|BEux9o+cyoZBC*-m=0A?K zS1L>tE{gYLV>|a;3*DG-i}Ws3&4$*8ci`NjgMm(m<`b*Ju?C zW}X9roYvpb0&fxfb$Xo6?14$$ z5cN4N$n92-xmGDEP#!A$G;?Llq(!sRs44HxfX>&ogA+`Qz=0 z&<-DAc4J(midD#!ZtAn45T|rvwpL=G=p#9QK84e%&uNi`i`uzyod(XuQkOC;#<0@_ zR`yE9HX-E&|J2K`GXrPp2>?#OO4NUvRsTAG^UK4UZ`c}M=3Uk~o~A1*8^6Q@=IY>{#L?mgl`|a`%>MOqd}x0D*P2Cr z$n~g1)gRBkFO?J0*407nbQPDXg!{4EFX%f}bwp#@T1WowJx0#lQMom&w2NaMU#sW- zRf<`!pb}8IAU9Yu1UlT0WfDdje#$k!YyiK2x#~?T)k8He)2+N}K16oLP*y{yYbUuU z{-GX~r?HT(?YTBDL=T@` zyRtU$UZ6HvIhycr>abrW6x#+Ia^x1Rai|ZkpQmI<6=XNSs)+}+GD?v7zg2HaI zd;CiS{p~3$cV`w1Upo*0DgjjWKq})JkLw=Y+w&et#cby=m^Riqp#uYjQTvVe=4RKv z?XGr{u7}92g{15Dye)cRP)*1SoeQTePjn>BM$I|)G#$A+UCUox+ycH?urT$wU%2wa zvwIcLE;qfKJEuxRqCo{S08Q^0~X0Gt5@opD_eil9MDpn!PSpeCdw8JL3XAnT=I-(C5EL% zee0*nx5TCbZ@i07R#hf!iHf+!-0AaBvBEijef?T}=vjuhr98)1cgn_RpOSBL-`*vc zw#UwierIo_6?(pnQQh)rk~@p->Q$vA9_ChO8@1{K%eqrI2$WT9j4a4=Ho69(lj7>BHutJm?2h0V5#A;z)k@#@ z{e!?DQh{2}PH|!y$)mO?){`h4lgi236?Rp38TWca;r3WnZ>{+g^%%G13G;z$&x?HX z?DUo1bL!n6k&i-jAeH+T5@MF-OLayR+RQ-hz6hlbp`LU1qe8_a9)htSv*RCI7R_HR zOFjpA!tCt9?ns8W?)eGTd$(3RyY;ryGiUuEamk&WX-f13tsg_S&(Q>@yW$u%` z0aH5#GcXsr_l&S2X1ZyEA9pu#W5P-87 zT`d3q)6f0y?(WZi?my~5|4T5O&9*qWPAs(pNIDKR>x|l24oHT>5hvS1>vMnmCmytB AIRF3v literal 5004 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 QCoF)HoVsi+&@%l00vK~Sa{vGU literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit50-0@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit50-0@2x.png index 7be5374a4679af322dd3d4c4b331afc8951a1bda..6f92db28d3c20066a8a74ec8c6d8d143945a450a 100644 GIT binary patch literal 2966 zcmdT`X;@Qd7XAX1%^=hk3o7Iqw73wm6A}y{n=FC|h@fB+k{gJS+?WItlqHIVQdFD~ z!O;jhwWxJm!y{0OpaaE%3swbW)u1A>DX1*6%tgh{52w#O^YqW$Ki~PzJ@0$Ycb5Cz ztl*$kW~TE@0RU!xzFr{!U{DbQj1AExN_J9+F4hX~2t_C&R492e7_1Z^F)-vO=EcGx zFi()Q6h#BD!c8UR@#A3y6a&YKBy9Y^>4Bs`sV5vK^4%tbL$?Km@wdhEnF73X4mBwT`Kok<$nMiUblBoo;6N^S- zQ7F)d2akG_352W=FP{&-&>b5er%*^)M50oubW~Cu5m_ve%w#f&Bnpv2A)pond6GoI zQxPO`yBP*ASk9M;qzVxtfuME)UBXv&Bxo|H&-BoW2p z>2tlamMcQw-^2K%wLC0I3KK(MIg%*jqxBHl&4AI|{jsBILo^!J8kqNz&igSmgOmfc?v`p zh9C(uT@W0HC=huZB85CXr>o9{mIU(nBFVIJ>2#sqpW6$TiMGN59~mNs-esC4`a~2| z5{*jc)5ru0gTW`Djl?533+Ob0kQ&2xrc&r6CQX30sCBx zb0HVYgLAx0LQIMbLOqu3X#IM*`7e28E!+FrQaZ3%><0ay-n7fhZHIe5CI{bo1yDv#cEY!S> zU#GU$9`ghl$YNkzxl2EIb=OyVYH;u~W1t6tin(cWuD%84;4>_@d*4FPxi-=L@T48! zAHsyS8h;54xPk`MI>K=LuV(HB+~alXyf?GlflsjkX<`*Hd>l5|Id?HbuP#IPu>NkQ za`si_CgGDpEzWTG%CW_j9d#rZBN4!?ioRW~cWBI_g}rfRMpiM`HgXSmr7u-C?X zvTJtElVw4tzOre(l$jArt1De(0Rq&UF}_WpSJ2$?^5K-kzin{XxMoZ~i=(8ziByBA zth+DyN5BnA3)rpHov<~JZ<4DLm_VG;W`sF!9!v*iV&HmfEG=SCcyxaJFj_aHU}1Y{ zXsi}v)i_b6skSvzaU%94Swp~WJJhHyc`~`hS$divMZOq_6H97 zwFNeF3CTIR+O$&JQ}ZJ(zYrdNbaU0Y;U(Aj6WMi{x*Vl8a&XeSMXT;xSA2K}{V-`Ua8IPy~-&Yr|xQARRCEV6HEVa4)o?WA?0F!X8&^O&0n9XjU?F7>Aak5AIc7S?aartQ_@Skvv zciW6PT5j!bzs4skhvS>o@Z&eJ-8^${)3xGqE2|TRMc1QUW6soWJi;+O*AO~psrasQ za6#5B@?>Q-Lznz>>8@eZ@#p=EfTsq7g~z7GdQ0!c5n>EnIhhc4+^~H^ zYk)jb4;$2`RpQS1XbbPRJTSjImU+56IocjW(znt$r<@z=spOkCCNb&;PF=*cq`J@y zpqfZ`&U_A&eR1H~Uo!Ma)j%`WJi1q_j@sH*((f^?c*QoG+IRIrOHzUwV56VA_rHE} zva|R5+ghZf1BZ!AfPP5e+E7dTd3|j){o9>44T#50DkF|-ZfYaaYlg>rD0&N%)52BN zn+&*$I(2A}dCH#8pT*cuS{PMiPMB7nT(Dts#1U`N(3y5g)pl)Gb}MtJ>$V-n-b!t? z<9u$JOWN4LozzEuo{5}U5cbWHjv@29@nqMWp^TedsXtj(+m9GLDg%e-<0kvoW`tV3 z`rgK#U(Qprf4^)j!2kE{^WWT>N3W!PW(AgCFFNzMzVQJ1qYV7KgS^gqaCiO-X=E-p literal 3861 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 F_J2M}_?iF! diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit50-1@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit50-1@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b28503e9f2eb683cac80a4b5853c6fa884d75964 GIT binary patch literal 9138 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, "Resources/metrics_skin"), audio, true); + defaultSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore(dllStore, "Resources/default_skin"), audio, false); + specialSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore(dllStore, "Resources/special_skin"), audio, true); } public void SetContents(Func creationFunction) @@ -43,23 +43,32 @@ namespace osu.Game.Rulesets.Osu.Tests Cell(3).Child = new LocalSkinOverrideContainer(specialSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction()); } - private static Skin getSkinFromResources(SkinManager skins, string name) + private class TestLegacySkin : LegacySkin { - using (var storage = new DllResourceStore("osu.Game.Rulesets.Osu.Tests.dll")) + private readonly bool extrapolateAnimations; + + public TestLegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager, bool extrapolateAnimations) + : base(skin, storage, audioManager, "skin.ini") { - var tempName = Path.GetTempFileName(); + this.extrapolateAnimations = extrapolateAnimations; + } - File.Delete(tempName); - Directory.CreateDirectory(tempName); + public override Texture GetTexture(string componentName) + { + // extrapolate frames to test longer animations + if (extrapolateAnimations) + { + var match = Regex.Match(componentName, "-([0-9]*)"); - var files = storage.GetAvailableResources().Where(f => f.StartsWith($"Resources/{name}")); + if (match.Length > 0) + { + var number = int.Parse(match.Groups[1].Value); + if (number < 60) + return base.GetTexture(componentName.Replace($"-{number}", $"-{number % 2}")); + } + } - foreach (var file in files) - using (var stream = storage.GetStream(file)) - using (var newFile = File.Create(Path.Combine(tempName, Path.GetFileName(file)))) - stream.CopyTo(newFile); - - return skins.GetSkin(skins.Import(tempName).Result); + return base.GetTexture(componentName); } } } From 6dd638b32793acb6dd0d2db08f92ec00cae3db39 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Aug 2019 17:39:24 +0900 Subject: [PATCH 2275/2854] Further improve legibility of texture lookup --- osu.Game/Skinning/LegacySkin.cs | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 94421b1251..6e983fe771 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -151,22 +151,33 @@ namespace osu.Game.Skinning Texture getFrameTexture(int frame) => GetTexture($"{componentName}{animationSeparator}{frame}"); - if (animatable && (texture = getFrameTexture(0)) != null) + TextureAnimation animation = null; + + if (animatable) { - var animation = new TextureAnimation { DefaultFrameLength = default_frame_time }; - - for (int i = 1; texture != null; i++) + for (int i = 0;; i++) { + if ((texture = getFrameTexture(i)) == null) + break; + + if (animation == null) + animation = new TextureAnimation + { + DefaultFrameLength = default_frame_time, + Repeat = looping + }; + animation.AddFrame(texture); - texture = getFrameTexture(i); } - - animation.Repeat = looping; - - return animation; } - return (texture = GetTexture(componentName)) == null ? null : new Sprite { Texture = texture }; + if (animation != null) + return animation; + + if ((texture = GetTexture(componentName)) != null) + return new Sprite { Texture = texture }; + + return null; } public class LegacySliderBall : Sprite From 9f0a0b2fcb0055ca5b4cc47334aef18fa0260402 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Aug 2019 17:42:33 +0900 Subject: [PATCH 2276/2854] Fix file extension case #1 --- .../Resources/metrics-skin/hit0@2x.PNG | Bin 9492 -> 0 bytes .../Resources/metrics-skin/hit100@2x.PNG | Bin 8371 -> 0 bytes .../Resources/metrics-skin/hit300@2x.PNG | Bin 9589 -> 0 bytes .../Resources/metrics-skin/hit50@2x.PNG | Bin 9299 -> 0 bytes 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit0@2x.PNG delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit100@2x.PNG delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit300@2x.PNG delete mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit50@2x.PNG diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit0@2x.PNG b/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit0@2x.PNG deleted file mode 100644 index a91072eb5b84a8204add39df625c54b703e0d873..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9492 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 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit50@2x.PNG b/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit50@2x.PNG deleted file mode 100644 index f64feded0cc2fc38672c081da38fe214d2546df3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9299 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 Date: Tue, 20 Aug 2019 17:42:57 +0900 Subject: [PATCH 2277/2854] Fix file extension case #2 --- .../Resources/metrics-skin/hit0@2x.png | Bin 0 -> 9492 bytes .../Resources/metrics-skin/hit100@2x.png | Bin 0 -> 8371 bytes .../Resources/metrics-skin/hit300@2x.png | Bin 0 -> 9589 bytes .../Resources/metrics-skin/hit50@2x.png | Bin 0 -> 9299 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit0@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit100@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit300@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit50@2x.png diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit0@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit0@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a91072eb5b84a8204add39df625c54b703e0d873 GIT binary patch literal 9492 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit50@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit50@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f64feded0cc2fc38672c081da38fe214d2546df3 GIT binary patch literal 9299 zcmZ`&pup=OL6CLN(;pu4%fq-7AfxT&H=@>SaCSq9g2H#cP;Mjy!`&(-LenQSKe=rYnE6#xL{{Rjgxfh+X(=`{QlpD zfeIH{005K#8IY)&`_hU2hhKQJNkW{XpBaLXIkKw)KHmetc<>VNYGJ>|+2|w_pp)iY zEzCF++6vTSuEyQ~xTdD`GU86Nm0%YI*zk+1law-;Ug%~)8{P!yybCbw^r-%{-qKiP zg;-e9T2zwtHttrOc6xYP;xy)W%6rEsh6OMB|1Qu$cP_FlNyCCHg`bec{{KS^$EK+597m6ieVe3=py3L`$qiNbIRH-sxbN@O@J*zu&l&r;7FM=cz ztmGWa$)`o@$o|TQP}0+k(7jCg$C-7)=A=)s6MMlP`IaxE5>KWq&`=v`0IOhb9}u=O zxQ4>{s2uBq_DYPv8E}4!;$EuCPXVs4D~ZF$Zp3_b=-9s3?{x82&uJ&MEc0Sri9E zft2kT0V-a|DmlsZ;2xb)2XB&I*iC-B>J0mK$~$y=>=2qT=!`M4LBMp4S}^-P)|e&c zaH%`yn215wvR}Usx9XEKAW7CUmd-c(Z)%;}NTPaslOIgbHe@{=x9)XOBWx z%iD09WAuZdw#by?azil|m12M8QP%yOzLdADdHpVNrcm{7=aS(E9%8_BFq>=-$ zT8EDxVuEQ%qBuZ&bM9@yGm<5Gnq>oiKOMYb54usgKH->(dg`V@mT+2^lkX+*mem__ z1OgoyD~_}SY7UCCR^z%F=4!%iCfHSLYPDssfo3Z%4YanB+S8%eXPs4M3pj%K^J-uf z*2|J9c5n@t-El;INO@?gA}>FTy7I($`+Hr7n1w6R1zp>kx%BuF3k@&dST=a1LLz!p zr=i-3K`QqbTEj=qnJ7MMn=f-O3>}?T+va6tt|i-bEM%!ID#5+z?j6hpAiQCor%tS* z9!j^S?%AaN&tA%60VA!D6z_(#b7`oeSjrO zr0qvI3!C`sON|m3ZZ&-Vh%qyF63kYPTR5j_!OmXbDb@NXD(18qgng*CTiou->ibw3*w*<8A9Z@VNUOQa|v(Uy^{ zyT{%L_Zvg0Uin|9tYf*M#4?)wQkVVHg7By!ldy)+DH6n*35n-|29Q#o$*@Gn?_~yP%U;ohV|cX0K=-k>vB+ES)#Jcp&ypR5EHIh z(wqt2iVVAev@WS3ESux}j|~^uR{fi#ru>QW7R)yc3|C5$#g^a3B@$6LZ8$D|>a~mc zsd0gd6MqZ}FU|g|C5JA-oda!Pyv4z5o#JbLbjQs0hNw{S{ld|+u^!@@?%)SA^~w0ohF#S$Ta(BXV|6WP zIOm^|*;^lBT7y0bUEWEA-4>>vSEoG)VFyOt%s2d7SRm00Ei=cP_NGp}{9~m>54pnm z&I`jCd5>Uhb+&m%l_3pyxf2<8T~<5XvNDHsKOXVZDgF|KmIp6ipV+# zc)1Th)1A+uFW2kvN#^&E@FdG{@nv;!A~KkeUtZp+oY>_PFrFZ1pd-Veh|PfpD2BIy zva+L-_B@g(eb4DvtNohl^9-t1lhq_ix84Qi+B^B;-X=VQzvZ+#q6MN=VTXO;n`WKa4cYa#h5et z$E~m~(jmiAh#p^{ci77_2xYM%iJWBzjbwKvfCXVeMybVq@C{b#-HUn8Y{mB;zYUh| zhDJB}oNEE_JmsH75bb8J#WrG>mo}vx6BdnVz%t?>+5X6XUht+SkAp2@Xo8Wt84ic5tT? zejMtCXeIlO;m9Z9K#a~)*iU5~iW|d@ehX_ApRhiqgRuBe3HJ_B&-BOY-^8x6?^I9V zCnHEO32j+eG}*Y*6p+!8K(Z(uIjj!f5`o=-G%M zW6VR6(=!|FNc>5S1!~1FIHc13kOWU9oq?p`BVl0;b( zkztl}4aJu&q@-QRk zG<1N?!?%A{iTawn&Arhv#t@@rWK)ESLha@+p0#lf-A(K%&%M>q-z;{`rgiWoot450 zI6rhBW$a^ZwZdLE-$G2Nd``Bc38QSrH#FHr)-x}bbWn|${9wcmiF!IC$=qT z8%A-l#Hh7mqA`2M25sP58zniwnjzh^!*Ids6c^-sM1#9)$qVJq{kQb6B>+REAh#Dm6s7uv66!GJTtHvBVcv|tQ(tvp)sVv6uN4#0 zSrFFuOIFup3oBh!TJ9Nv8Rr)pqiqV&oxJWSySWT2p<-ke{}Q>;@)tIeH)QJTubmot zR-^6Ro;0;x->=n5Ae5I~t^3Fcj&=;@Gf^Z&w%u=Tgs-?D1Lie1g+Bf=e@6el^DIi# zG(@csIk>5GV)keHFxUbZEB;-1M@(^I5Kg-NV~%t_8x25gAP^_ct$^b7j1dJ~D(VuH zbkgI%9ep-Zei>kjVpFhf0m!}U)yN=6c;)o>({+6Kt=POScIoYO$ue}7SElU7`{L|48W!odUp&N&q?5U9zokAMoAu-_9)^- zLNr{gqXPhX;T3RS(*L02ufF6xc)+#742d@z>)|^;H_kXk!aVBi**%8q&A_7g`Gz5- zt$6ulD(fhlD#2maWx0l$_`H->8G`G+`uh|8q&d4HT$;VRLQ%>^#@xnWJ;_AmL!)6K zu{XMx)}p|$JTE3vlG`on0X?EDD7kAWvS8Ds48NzV#KFTEkbJ>4hr`r4uaFV~S7k;u z-UzCHBK>%&G@C1cZpM+(pAZ*7gt5dc0nomKr9A62!!hG2!qJvQxHobqihXmMxP1%z zeR%@G++gqxIa&ujUU92^$=a;{bN_VZ?n6D+kruE@7Em+zM>ABLd*oAEECC)8?%@ua zJx5?2C3XoMRyexE27t|`yekIGYtWk-w8BC)kaErOH^Fj|u_R@{E(Q`KayjG}`MlNw zS|Z-GbjmpU8nNW~VHmTw6niPK$NXdcm)(Lo+HqI^%m&)Ez(U2Dl|+0he*ZzjRWDN4CgIY!%VwZ~hI<jM`xQla~^#D=iCyYhqQIfczF9km+nFDmL_;lf$TD_aVSe&cEi}6BO?w1;)fa( z@mgeM^AhX8W093jBU)twH4+2wsuq_suMbUP zN(b<1Obash*D>+B z+r3*=rjRO=*`ZgaKY>SHByr09Jx3v>fj8mb$<7OCR^N?ijs9n}^op_m?q7&#$~ zvRk`wqf7-hSNFlIBY8N1)&0Z&*=Plg#{gsM-* zON8sg^$%PaXim1JdODq4*(zQW(hoU`3-V2kx(DH}WtOotf)(!bj{$82!*)v8`2Dtb z(2!7`)r4Luc3Qd7W@Q@&OLldAWDLq5mVVz)&Pbvj#sDm97|v>(m#VnpZsSB zSY2Kc?s(hx856dC`0a#faK2=RIKv?F+!Y?QGIUX^F>-U@ zBuCH+Uh7(%9)l%o93FvDN+i;n!RRc6J3o(BSo#)!lj=+T6R%U2lVt+NeQ}TFAhf5N zoby-0+cL*LEv$#{<{M|!V+>7TZ61@Gm-V0u!(MtrCygVNPp~}014gJv0NQ)}=n-TE zn;Vz0QL zd&3RdNTsIRC{`!0FPUiW=6<$LBj^d2K`n?vqZw4 zNCB#3M7pcZZht(l3Y8TH!fN!-nGfc>HPsTN|9G+P*up5+LUh+c?1cj7J=4R!2k)OW zwwPjv|9!9PBWdw{RjW+J7Y9nnt1wV<^w!C6*cSJEZO*y|@!V!~O*j_-r!zokfiXzm zaTvaY-wwudZJ>-|Nb1j+DI|l{GD=8}eG#4-mR;ClJbAiIcTAF61kAog`O4Bm{s3Rmu1}o*W!j>Cjy8PrJl7^q(k_v0>nbMfEi_M1 zB-wC_BaefaIo5wu3qcWv-QlacHJmxpq&1DdwI>WA%sphahrMZ6FxHV&?CPNTZ?Pt* zp>ViXns^3BI~9$Q99Wy7pTQAM^o&Mti1DpMsT+iZr3@b%1^11_U-+4{Q!4wJ$12U) zT?%+)_|FbnK8{n*l_h-o*PN4V^>l-bW(P@zLar>g_}@AV)+?jG&gAZVu36oQfHbd6 z^8}TxvM{%V>oz}IfARVjb&p9y?Xznkm5JAW{QH&P6j|(SuuWsEmjJnvUxr(c^Y$@h z1`o<}UnCDO2$ub;{3tWT6I{|+FrT@LhWNaa<-6?eZIB&MhK;hWX?Jny@gQ)}TsUMV zh4FrvuhTFlGGog-stH99{uj3_0T#$mN4REQHY@I|rSKdX`DXn;To^46CjjJumRepM z&JnPf572J>jN{?>)zj?v+jVo9LCa$unAQhv0K>=Xe8$moEt;+yIh)L6RW&6FZm=aF zV9@HR=ghlO=FzsgzIMX~aFq$2S^k7s1jk?kFM^~T*`z*mt4#5z$Jx2()8Rp$BL%3L zz2g?DwKYQB*#0k_RXzmtw}8ha2m$*6LVmZ+f4!Jas%zwWojR3m_S2ozo-o{Ae=XT! zGTQ2RiP-~O=YFDNc?@u6k*-a{{0fj!_|3a3NHsoC89)Az&qo0sJwc`CG{x0*+liM|p)L!=EmLYXX^5f)ymog%`mt4KTWq?;_}kYiDT2ua5YZKnxc3*lZ?|$>7#Q>06Wz_Ydt1RM|lqSgKtlD z4Y}rt4R>ahDa-H11Q$qc1h~pEcnMsbtNQQvL+N)-dbW z9&fXekcp+s$Z>pJ*bX1TZu>x`;B)Hoa>0Ba3!6zjbRpQ->(e~!1~^iA`wqqq!y2<- zj)<{8y00)0y9e{fc_P0}n1|A^y|C7B_vCFjpD=Al@>k(8@joMF6pmQ25I1Y{(Mfw! z{{!lpP~{Dzkd2OuX0h^lKNp6MRhd7PIKnZ^u2hm9Uw?*47I4IPEk*r1w#Cyssr)E& zlYAERm0hVE-C}f7nbg02>UmHZ^*LMTiVVM$Kzm2I z0-JT#mYe5=-H;9b<6mc7H-O)|x$`?a&O@hr;(rYTtwt&ZZz`-XbWY7_<|XnPneeh* zbQD^97Uzj|qbcNRjoAFke6*KJM`G_jIqhog(fg;}*b1I!{M*pD&k`-U_z?xo1plvlm}B38v zKFU(g5-op{A(?Uu%_IDvP<->&K@~rl;adP5DJ$;!n!&pqrOVD1BsY7$(Wj_r$SPN^ zJH`LZuovPiYGcnw^zSE;_dE=UH!t?7GFRfEn6JXwiFMyQ2pIPy<_ehHJm<07%CkB* zK1Mgs`no69q9k`)%20}(l10c~Tzzq){niBJ)>X~84g2<6;@^F3M%ulXw(qsL84Tu# zMG{2YswF;CV5{Z$xD%exXk#{BM?%Q31~QNLBflX`DK0>ADE{tqGw8{W{e zfvXIIIlJZSLx8Z{sUjtT_j-%M?o=TAmNDkJ0|Z*35`GP!sFi)Fv4!izgr*&+p9tXO z6~kg`Dj4UTSgyon4PFUB9qyjo9mcWKrF{S+}e z_btMCqjO-r!#E@ir0B|ij0=)(jkClDqair_M!C^eQS5YGq6sueJJQ$MUW}c8sY@$s zGUqX89r`!jY>V+2@i2*^CW6@8EPm^sF)!VF(C`#8bQaVphx|CO)yg)eshPU?+Z#J0 zO^0xY{NO&iD=X)(MoqP?!XkcLh$I?P0`JhLMi|nuPB{9r{lK<70F?_O*|DAmfe+mRh~3sK_qEWRQFTWumIJF?vnx~f22u26Pq-`(z1tzS z8uCK^(^Y!xpXhRB<{UX+ifsvcWyKJI|U9+TAUs3r}K$bJ`ebk_Tn z&s>7ciqb>^t)6P+BiLt)vu3zOB4TcF$;n|IOceDbjCi+($>!2i{o!>jqy~47e}hG% zQ=;wkl$zFYcf*tiD!E(K*;TpoFo3(>hrYlNql^y^#zJ}rV!jzUQgz25ZUY)N+aeAr z!`dz@QCFUnoj%v&X|35&Hz=-)yyF+al$S&8`^cm13ro@7N>Wy*eT9shM`|ddJGiVF zx_Z)VUsv*B&M48Up?HCVa`$!tPG@QRURpVJCN$H4KnCgij+{JvJBSLP{Sj)@0w|Y* z691={wY7x1SOy@C=e8ubobp$YheM`j1K<4^ZQf0Y*a^k4l9y(WW}*)J{P2-~dLCXbH|f^Pan828o6w-6_wRnA1#7>+B^AydQuzL_BXNl~TS0Lx|% z|1B3#Pmrp!QTJjAaX+>nN?H$A9H4{dZVfXoM%>bNi0!E=0Ag=g?R#}UsL$p&2Yf*t8FM( zAabzgfR!Sn?V^6r^QlPDg^tll--!^VXV*k}F<&4uEJjKv z^ksS4o5k$L3RZzAsD;)+Fi~oZq9*E3rlBfagyVTp$h8;jTPV4Hk+P*9>eQb`r8sv1a61l>`U1{DC~weJEj@g$-$XVh3E{Y z2EtF;nieNN>HO$_R$?kb`nhk>9gW+RQSuIUhQ4Yx70yTvA8#6MN5S0#Fy}MhF;I;p z6Sq-x`a9twJ;4>RZTQooVU%2aZt~A7T3TZU72M=iL+;JhYW$)+lNUVVuXKHJj(5(Q z1zeoHJ+3*A;i#A|t@+%GQHI(mm}NSAS=@>npV9!}&H6X}(C2Y=c}ybmx~@p)h*Dpr zG#GH|PE7O^slDVdm)b%PJsr__BQ~a?oD(Gz%XOuKT^UJ70}evANhpMk>bTw$dO2E+y` z3qfJ*G;f^!4R00JnK-=8OrMEiBMwhu@@N>ba|Mq1_*9nbYEJfv62pqBa1!)u5f>Sx z6Fp6q4I6Lyr@iS_sb67Lx8Y7XV0A}}dq615j&3$RiXpBP1U*A~_L-Db!h~Jz*~BP} z__@8-O&r#D62?7B)st*qFmv5rf4;-te{QD-8-b5$=puZA#8;gN@|Vo7eWvaBf^ev0 zdu)w46#8y{*L;`F7w@(f`XcDa^d$+0Dq`6HcN*=XG3Z&GAxLo-1GUnmaB0E+xq-6-Hbd>A#2pUU!& zV?9|~z3+aDJTQfDpf4dW!*p!fpi2UjV@}0y#P7yo&(nI}g{@v;>rj4$iq4*NJHUee zSS#IJH9Ft;3}}i;53cYg-HjHD0&T$5qGtc8v!pd_>6&77H zk!&x#8o9t)e%ySK1S2)oU`;xtJ8E3=Zlq0(X!}TS{r9|$ z#j6T>U5+i77!mkJe)??a*!((y1r_dS%DHtU8}!7CxF?lR$4oI-72c99SX2LwE)i)o3ooE%?D)J>DG|DuLk@$tzJSNx!6*C9m~ak0pCd4NAkMW;~6#|5TP+J-R^W_g?&*yHlNcR!=|0MOdI} z^JJ%bg@)m*jXQ!>V9o3eenrLif-B}?D={J|JHj3@&GM?5t_Pt_Jl2P>WmAN0^$zcj+qfRU1j zaa2F1g5DneNu9^<_O`J4`zkNl>BQ@f zUJ!y%upiia^S+0<^_;sl>7;5Fx1*jLV9GMsd)!UbsI%K16j)60RmBtrhlvsW&?Nka z{<#tE9vVLAUp|iChGAV^SWKh=A4}>%hz|{?N(UPf-EKm0qB)?s`8qr5rqaTyBZI6Q zKyjd4YjixdY!#Q4kc<+qO%gTqGUq_OsSIb}E6X&!eHH}aWJW3Hu! Date: Tue, 20 Aug 2019 17:47:24 +0900 Subject: [PATCH 2278/2854] Don't show judgement type "none" --- .../TestSceneDrawableJudgement.cs | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs index 6f6520dafa..82a8d0e5e6 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; @@ -21,19 +22,13 @@ namespace osu.Game.Rulesets.Osu.Tests public TestSceneDrawableJudgement() { - foreach (HitResult result in Enum.GetValues(typeof(HitResult))) - { - JudgementResult judgement = new JudgementResult(null) - { - Type = result, - }; - - AddStep("Show " + result.GetDescription(), () => SetContents(() => new DrawableOsuJudgement(judgement, null) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - })); - } + foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType().Skip(1)) + AddStep("Show " + result.GetDescription(), () => SetContents(() => + new DrawableOsuJudgement(new JudgementResult(null) { Type = result }, null) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + })); } } } From 7d668c81fcbbeb1cd2ccd7dba538864c764d81a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Aug 2019 18:13:00 +0900 Subject: [PATCH 2279/2854] Avoid test failures This is a bit of a workaround (I'm pretty sure these tests are not running correctly on appveyor) but I think it will do for now. Cannot repro failures locally at all. --- osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs b/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs index 8b37ef5876..02716dc1d5 100644 --- a/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs +++ b/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs @@ -60,12 +60,8 @@ namespace osu.Game.Rulesets.Osu.Tests { var match = Regex.Match(componentName, "-([0-9]*)"); - if (match.Length > 0) - { - var number = int.Parse(match.Groups[1].Value); - if (number < 60) - return base.GetTexture(componentName.Replace($"-{number}", $"-{number % 2}")); - } + if (match.Length > 0 && int.TryParse(match.Groups[1].Value, out var number) && number < 60) + return base.GetTexture(componentName.Replace($"-{number}", $"-{number % 2}")); } return base.GetTexture(componentName); From 33946f045812e67b22681cdedf408008807ac739 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 20 Aug 2019 11:18:41 +0200 Subject: [PATCH 2280/2854] Fix CI issues and update comments. --- osu.Game/Screens/Menu/IntroScreen.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index a621e29cf8..6a98c78d5f 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -30,14 +30,14 @@ namespace osu.Game.Screens.Menu private LeasedBindable beatmap; - public new Bindable Beatmap { get => beatmap; } + public new Bindable Beatmap => beatmap; protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack(); [BackgroundDependencyLoader] private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game) { - //we take a lease on the beatmap bindable to prevent music playback from changing / pausing music during intros, as it is causing weird interactions with certains intros + // we take a lease on the beatmap bindable to prevent music playback from changing / pausing during intros, as it is causing weird interactions with certains intros. beatmap = base.Beatmap.BeginLease(false); menuVoice = config.GetBindable(OsuSetting.MenuVoice); @@ -115,7 +115,7 @@ namespace osu.Game.Screens.Menu protected void LoadMenu() { DidLoadMenu = true; - beatmap.Return(); //we return the lease to the beatmap bindable as we're pushing the main menu. + beatmap.Return(); this.Push(mainMenu); } From 0de219dda48cb8d7a370e0424ebba7a5e9025c0a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 21 Jun 2019 20:47:43 +0900 Subject: [PATCH 2281/2854] Update with spritetext text builder changes --- osu.Game/Skinning/LegacySkin.cs | 50 +++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 3eda76e40f..ee88d3254e 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; +using osu.Framework.Text; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -234,37 +235,50 @@ namespace osu.Game.Skinning private class LegacySpriteText : OsuSpriteText { - private readonly TextureStore textures; - private readonly string font; + private readonly LegacyGlyphStore glyphStore; public LegacySpriteText(TextureStore textures, string font) { - this.textures = textures; - this.font = font; - Shadow = false; UseFullGlyphHeight = false; + + Font = new FontUsage(font, 16); + glyphStore = new LegacyGlyphStore(textures); } - protected override Texture GetTextureForCharacter(char c) + protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore); + + private class LegacyGlyphStore : ITexturedGlyphLookupStore { - string textureName = $"{font}-{c}"; + private readonly TextureStore textures; - // Approximate value that brings character sizing roughly in-line with stable - float ratio = 36; - - var texture = textures.Get($"{textureName}@2x"); - - if (texture == null) + public LegacyGlyphStore(TextureStore textures) { - ratio = 18; - texture = textures.Get(textureName); + this.textures = textures; } - if (texture != null) - texture.ScaleAdjust = ratio; + public ITexturedCharacterGlyph Get(string fontName, char character) + { + string textureName = $"{fontName}-{character}"; - return texture; + // Approximate value that brings character sizing roughly in-line with stable + float ratio = 36; + + var texture = textures.Get($"{textureName}@2x"); + + if (texture == null) + { + ratio = 18; + texture = textures.Get(textureName); + } + + if (texture != null) + texture.ScaleAdjust = ratio; + + return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture?.Width ?? 0, null), texture, 1f / ratio); + } + + public Task GetAsync(string fontName, char character) => Task.Run(() => Get(fontName, character)); } } From f1d02d8169a6b38b6f8019c83a3fbaea8bae2d81 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 20 Aug 2019 15:00:14 +0300 Subject: [PATCH 2282/2854] Update design in line with web --- .../Kudosu/DrawableKudosuHistoryItem.cs | 105 +++++++++++++----- 1 file changed, 78 insertions(+), 27 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index 9b68131515..7c120e60f3 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -12,49 +12,100 @@ using osu.Game.Online.Chat; namespace osu.Game.Overlays.Profile.Sections.Kudosu { - public class DrawableKudosuHistoryItem : DrawableProfileRow + public class DrawableKudosuHistoryItem : CompositeDrawable { + private const int height = 25; + + [Resolved] + private OsuColour colours { get; set; } + private readonly APIKudosuHistory historyItem; - private LinkFlowContainer content; + private readonly LinkFlowContainer linkFlowContainer; + private readonly DrawableDate date; public DrawableKudosuHistoryItem(APIKudosuHistory historyItem) { this.historyItem = historyItem; + + Height = height; + RelativeSizeAxes = Axes.X; + AddRangeInternal(new Drawable[] + { + linkFlowContainer = new LinkFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + }, + date = new DrawableDate(historyItem.CreatedAt) + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + } + }); } [BackgroundDependencyLoader] private void load() { - LeftFlowContainer.Padding = new MarginPadding { Left = 10 }; + date.Colour = colours.GreySeafoamLighter; - LeftFlowContainer.Add(content = new LinkFlowContainer + switch (historyItem.Action) { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - }); + case KudosuAction.VoteGive: + case KudosuAction.Give: + linkFlowContainer.AddText($@"Received "); + addKudosuPart(); + addMainPart(); + addPostPart(); + break; - RightFlowContainer.Add(new DrawableDate(historyItem.CreatedAt) - { - Font = OsuFont.GetFont(size: 13), - Colour = OsuColour.Gray(0xAA), - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - }); + case KudosuAction.Reset: + addMainPart(); + addPostPart(); + break; - var formatted = createMessage(); + case KudosuAction.VoteReset: + linkFlowContainer.AddText($@"Lost "); + addKudosuPart(); + addMainPart(); + addPostPart(); + break; - content.AddLinks(formatted.Text, formatted.Links); + case KudosuAction.DenyKudosuReset: + linkFlowContainer.AddText($@"Denied "); + addKudosuPart(); + addMainPart(); + addPostPart(); + break; + + case KudosuAction.Revoke: + addMainPart(); + addPostPart(); + break; + } } - protected override Drawable CreateLeftVisual() => new Container + private void addKudosuPart() { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - }; + linkFlowContainer.AddText($@"{historyItem.Amount} kudosu", t => + { + t.Font = t.Font.With(italics: true); + t.Colour = colours.Blue; + }); + } + + private void addMainPart() + { + var text = createMessage(); + + linkFlowContainer.AddLinks(text.Text, text.Links); + } + + private void addPostPart() => linkFlowContainer.AddLink(historyItem.Post.Title, historyItem.Post.Url); private MessageFormatter.MessageFormatterResult createMessage() { - string postLinkTemplate() => $"[{historyItem.Post.Url} {historyItem.Post.Title}]"; string userLinkTemplate() => $"[{historyItem.Giver?.Url} {historyItem.Giver?.Username}]"; string message; @@ -62,27 +113,27 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu switch (historyItem.Action) { case KudosuAction.Give: - message = $"Received {historyItem.Amount} kudosu from {userLinkTemplate()} for a post at {postLinkTemplate()}"; + message = $" from {userLinkTemplate()} for a post at "; break; case KudosuAction.VoteGive: - message = $"Received {historyItem.Amount} kudosu from obtaining votes in modding post of {postLinkTemplate()}"; + message = $" from obtaining votes in modding post of "; break; case KudosuAction.Reset: - message = $"Kudosu reset by {userLinkTemplate()} for the post {postLinkTemplate()}"; + message = $"Kudosu reset by {userLinkTemplate()} for the post "; break; case KudosuAction.VoteReset: - message = $"Lost {historyItem.Amount} kudosu from losing votes in modding post of {postLinkTemplate()}"; + message = $" from losing votes in modding post of "; break; case KudosuAction.DenyKudosuReset: - message = $"Denied {historyItem.Amount} kudosu from modding post {postLinkTemplate()}"; + message = $" from modding post "; break; case KudosuAction.Revoke: - message = $"Denied kudosu by {userLinkTemplate()} for the post {postLinkTemplate()}"; + message = $"Denied kudosu by {userLinkTemplate()} for the post "; break; default: From 832b365bd08ac76da29707aea9d2d466d745497c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 20 Aug 2019 15:17:31 +0300 Subject: [PATCH 2283/2854] Add testing --- .../Visual/Online/TestSceneKudosuHistory.cs | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs b/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs new file mode 100644 index 0000000000..6424db89fa --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs @@ -0,0 +1,156 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Overlays.Profile.Sections.Kudosu; +using System.Collections.Generic; +using System; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.API.Requests; +using osu.Framework.Extensions.IEnumerableExtensions; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneKudosuHistory : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(DrawableKudosuHistoryItem), + }; + + private readonly Box background; + + public TestSceneKudosuHistory() + { + FillFlowContainer content; + + AddRange(new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + content = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + Width = 0.7f, + AutoSizeAxes = Axes.Y, + } + }); + + items.ForEach(t => content.Add(new DrawableKudosuHistoryItem(t))); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.GreySeafoam; + } + + private IEnumerable items = new[] + { + new APIKudosuHistory + { + Amount = 10, + CreatedAt = new DateTimeOffset(new DateTime(2011, 11, 11)), + Action = KudosuAction.DenyKudosuReset, + Post = new APIKudosuHistory.ModdingPost + { + Title = @"Random post 1", + Url = @"https://osu.ppy.sh/b/1234", + }, + Giver = new APIKudosuHistory.KudosuGiver + { + Username = @"Username1", + Url = @"https://osu.ppy.sh/u/1234" + } + }, + new APIKudosuHistory + { + Amount = 5, + CreatedAt = new DateTimeOffset(new DateTime(2012, 10, 11)), + Action = KudosuAction.Give, + Post = new APIKudosuHistory.ModdingPost + { + Title = @"Random post 2", + Url = @"https://osu.ppy.sh/b/1234", + }, + Giver = new APIKudosuHistory.KudosuGiver + { + Username = @"Username2", + Url = @"https://osu.ppy.sh/u/1234" + } + }, + new APIKudosuHistory + { + Amount = 8, + CreatedAt = new DateTimeOffset(new DateTime(2013, 9, 11)), + Action = KudosuAction.Reset, + Post = new APIKudosuHistory.ModdingPost + { + Title = @"Random post 3", + Url = @"https://osu.ppy.sh/b/1234", + }, + Giver = new APIKudosuHistory.KudosuGiver + { + Username = @"Username3", + Url = @"https://osu.ppy.sh/u/1234" + } + }, + new APIKudosuHistory + { + Amount = 7, + CreatedAt = new DateTimeOffset(new DateTime(2014, 8, 11)), + Action = KudosuAction.Revoke, + Post = new APIKudosuHistory.ModdingPost + { + Title = @"Random post 4", + Url = @"https://osu.ppy.sh/b/1234", + }, + Giver = new APIKudosuHistory.KudosuGiver + { + Username = @"Username4", + Url = @"https://osu.ppy.sh/u/1234" + } + }, + new APIKudosuHistory + { + Amount = 100, + CreatedAt = new DateTimeOffset(new DateTime(2015, 7, 11)), + Action = KudosuAction.VoteGive, + Post = new APIKudosuHistory.ModdingPost + { + Title = @"Random post 5", + Url = @"https://osu.ppy.sh/b/1234", + }, + Giver = new APIKudosuHistory.KudosuGiver + { + Username = @"Username5", + Url = @"https://osu.ppy.sh/u/1234" + } + }, + new APIKudosuHistory + { + Amount = 20, + CreatedAt = new DateTimeOffset(new DateTime(2016, 6, 11)), + Action = KudosuAction.VoteReset, + Post = new APIKudosuHistory.ModdingPost + { + Title = @"Random post 6", + Url = @"https://osu.ppy.sh/b/1234", + }, + Giver = new APIKudosuHistory.KudosuGiver + { + Username = @"Username6", + Url = @"https://osu.ppy.sh/u/1234" + } + } + }; + } +} From c4344f3f7cc985b5f40ad4e1603c59ab910b3e2b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 20 Aug 2019 15:29:32 +0300 Subject: [PATCH 2284/2854] CI fixes --- .../Visual/Online/TestSceneKudosuHistory.cs | 2 +- .../Kudosu/DrawableKudosuHistoryItem.cs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs b/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs index 6424db89fa..dcf2bec239 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Online background.Colour = colours.GreySeafoam; } - private IEnumerable items = new[] + private readonly IEnumerable items = new[] { new APIKudosuHistory { diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index 7c120e60f3..d6dfdc84ec 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { case KudosuAction.VoteGive: case KudosuAction.Give: - linkFlowContainer.AddText($@"Received "); + linkFlowContainer.AddText(@"Received "); addKudosuPart(); addMainPart(); addPostPart(); @@ -66,14 +66,14 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu break; case KudosuAction.VoteReset: - linkFlowContainer.AddText($@"Lost "); + linkFlowContainer.AddText(@"Lost "); addKudosuPart(); addMainPart(); addPostPart(); break; case KudosuAction.DenyKudosuReset: - linkFlowContainer.AddText($@"Denied "); + linkFlowContainer.AddText(@"Denied "); addKudosuPart(); addMainPart(); addPostPart(); @@ -113,27 +113,27 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu switch (historyItem.Action) { case KudosuAction.Give: - message = $" from {userLinkTemplate()} for a post at "; + message = $@" from {userLinkTemplate()} for a post at "; break; case KudosuAction.VoteGive: - message = $" from obtaining votes in modding post of "; + message = @" from obtaining votes in modding post of "; break; case KudosuAction.Reset: - message = $"Kudosu reset by {userLinkTemplate()} for the post "; + message = $@"Kudosu reset by {userLinkTemplate()} for the post "; break; case KudosuAction.VoteReset: - message = $" from losing votes in modding post of "; + message = @" from losing votes in modding post of "; break; case KudosuAction.DenyKudosuReset: - message = $" from modding post "; + message = @" from modding post "; break; case KudosuAction.Revoke: - message = $"Denied kudosu by {userLinkTemplate()} for the post "; + message = $@"Denied kudosu by {userLinkTemplate()} for the post "; break; default: From 5c7cb4dc21ecb4a3b5961a4bed7c7f4b1562aa1a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 20 Aug 2019 16:11:59 +0300 Subject: [PATCH 2285/2854] Simplify text creation --- .../Kudosu/DrawableKudosuHistoryItem.cs | 64 +++++-------------- 1 file changed, 16 insertions(+), 48 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index d6dfdc84ec..1b0a501db2 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -50,37 +50,45 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { date.Colour = colours.GreySeafoamLighter; + string userLinkTemplate() => $"[{historyItem.Giver?.Url} {historyItem.Giver?.Username}]"; + switch (historyItem.Action) { case KudosuAction.VoteGive: + linkFlowContainer.AddText(@"Received "); + addKudosuPart(); + addMainPart($@" from {userLinkTemplate()} for a post at "); + addPostPart(); + break; + case KudosuAction.Give: linkFlowContainer.AddText(@"Received "); addKudosuPart(); - addMainPart(); + addMainPart(@" from obtaining votes in modding post of "); addPostPart(); break; case KudosuAction.Reset: - addMainPart(); + addMainPart($@"Kudosu reset by {userLinkTemplate()} for the post "); addPostPart(); break; case KudosuAction.VoteReset: linkFlowContainer.AddText(@"Lost "); addKudosuPart(); - addMainPart(); + addMainPart(@" from losing votes in modding post of "); addPostPart(); break; case KudosuAction.DenyKudosuReset: linkFlowContainer.AddText(@"Denied "); addKudosuPart(); - addMainPart(); + addMainPart(@" from modding post "); addPostPart(); break; case KudosuAction.Revoke: - addMainPart(); + addMainPart($@"Denied kudosu by {userLinkTemplate()} for the post "); addPostPart(); break; } @@ -95,53 +103,13 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu }); } - private void addMainPart() + private void addMainPart(string text) { - var text = createMessage(); + var formatted = MessageFormatter.FormatText(text); - linkFlowContainer.AddLinks(text.Text, text.Links); + linkFlowContainer.AddLinks(formatted.Text, formatted.Links); } private void addPostPart() => linkFlowContainer.AddLink(historyItem.Post.Title, historyItem.Post.Url); - - private MessageFormatter.MessageFormatterResult createMessage() - { - string userLinkTemplate() => $"[{historyItem.Giver?.Url} {historyItem.Giver?.Username}]"; - - string message; - - switch (historyItem.Action) - { - case KudosuAction.Give: - message = $@" from {userLinkTemplate()} for a post at "; - break; - - case KudosuAction.VoteGive: - message = @" from obtaining votes in modding post of "; - break; - - case KudosuAction.Reset: - message = $@"Kudosu reset by {userLinkTemplate()} for the post "; - break; - - case KudosuAction.VoteReset: - message = @" from losing votes in modding post of "; - break; - - case KudosuAction.DenyKudosuReset: - message = @" from modding post "; - break; - - case KudosuAction.Revoke: - message = $@"Denied kudosu by {userLinkTemplate()} for the post "; - break; - - default: - message = string.Empty; - break; - } - - return MessageFormatter.FormatText(message); - } } } From 426c7a48989302453ca7e05b8c91738e06d6ad7e Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 20 Aug 2019 16:19:21 +0300 Subject: [PATCH 2286/2854] Fix incorrect templates --- .../Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index 1b0a501db2..e5f5b720c2 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -57,14 +57,14 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu case KudosuAction.VoteGive: linkFlowContainer.AddText(@"Received "); addKudosuPart(); - addMainPart($@" from {userLinkTemplate()} for a post at "); + addMainPart(@" from obtaining votes in modding post of "); addPostPart(); break; case KudosuAction.Give: linkFlowContainer.AddText(@"Received "); addKudosuPart(); - addMainPart(@" from obtaining votes in modding post of "); + addMainPart($@" from {userLinkTemplate()} for a post at "); addPostPart(); break; From 439d825dd1beecf4a9c447974bcacdee82df3c6b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 20 Aug 2019 18:39:29 +0300 Subject: [PATCH 2287/2854] Disallow adding bonus judgements' result to statistics --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index c8858233aa..e47df6b473 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -325,9 +325,6 @@ namespace osu.Game.Rulesets.Scoring JudgedHits++; - if (result.Type != HitResult.None) - scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1; - if (result.Judgement.AffectsCombo) { switch (result.Type) @@ -352,6 +349,9 @@ namespace osu.Game.Rulesets.Scoring } else { + if (result.HasResult) + scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1; + baseScore += result.Judgement.NumericResultFor(result); rollingMaxBaseScore += result.Judgement.MaxNumericResult; } @@ -371,9 +371,6 @@ namespace osu.Game.Rulesets.Scoring JudgedHits--; - if (result.Type != HitResult.None) - scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) - 1; - if (result.Judgement.IsBonus) { if (result.IsHit) @@ -381,6 +378,9 @@ namespace osu.Game.Rulesets.Scoring } else { + if (result.HasResult) + scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) - 1; + baseScore -= result.Judgement.NumericResultFor(result); rollingMaxBaseScore -= result.Judgement.MaxNumericResult; } From 48716f8f2b6c57639cf2a2223bb78610ecccf403 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Aug 2019 13:29:50 +0900 Subject: [PATCH 2288/2854] Update framework --- osu.Android.props | 2 +- .../Objects/Drawable/DrawableFruit.cs | 2 +- .../Objects/Drawable/Pieces/Pulp.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- .../Objects/Drawables/Pieces/BodyPiece.cs | 4 ++-- .../Objects/Drawables/Pieces/LaneGlowPiece.cs | 4 ++-- .../UI/Components/ColumnBackground.cs | 2 +- .../Objects/Drawables/Connections/FollowPoint.cs | 2 +- .../Objects/Drawables/DrawableRepeatPoint.cs | 2 +- .../Objects/Drawables/Pieces/CirclePiece.cs | 2 +- .../Objects/Drawables/Pieces/ExplodePiece.cs | 4 ++-- .../Objects/Drawables/Pieces/FlashPiece.cs | 2 +- .../Objects/Drawables/Pieces/GlowPiece.cs | 2 +- .../Objects/Drawables/Pieces/SliderBall.cs | 4 ++-- .../Objects/Drawables/DrawableSwell.cs | 4 ++-- .../Objects/Drawables/Pieces/CirclePiece.cs | 2 +- osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 4 ++-- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 6 +++--- .../Visual/UserInterface/TestSceneCursors.cs | 2 +- .../Components/TournamentBeatmapPanel.cs | 2 +- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 9 --------- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 2 +- osu.Game/Graphics/Containers/OsuScrollContainer.cs | 2 +- osu.Game/Graphics/Cursor/MenuCursor.cs | 2 +- osu.Game/Graphics/Sprites/GlowingSpriteText.cs | 2 +- osu.Game/Graphics/UserInterface/DialogButton.cs | 2 +- osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs | 2 +- osu.Game/Graphics/UserInterface/OsuButton.cs | 2 +- .../Overlays/Profile/Header/Components/RankGraph.cs | 9 --------- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 2 +- .../Overlays/Toolbar/ToolbarOverlayToggleButton.cs | 2 +- .../Components/RadioButtons/DrawableRadioButton.cs | 2 +- .../Edit/Compose/Components/BeatDivisorControl.cs | 2 +- osu.Game/Screens/Menu/Button.cs | 2 +- osu.Game/Screens/Menu/IntroTriangles.cs | 6 +++--- osu.Game/Screens/Menu/LogoVisualisation.cs | 2 +- osu.Game/Screens/Menu/MenuSideFlashes.cs | 4 ++-- osu.Game/Screens/Menu/OsuLogo.cs | 4 ++-- .../Screens/Multi/Match/Components/HeaderButton.cs | 2 +- osu.Game/Screens/ScreenWhiteBox.cs | 2 +- .../Screens/Select/Carousel/DrawableCarouselItem.cs | 2 +- .../Screens/Select/Options/BeatmapOptionsButton.cs | 2 +- osu.Game/Storyboards/CommandTimelineGroup.cs | 4 ++-- osu.Game/Storyboards/Drawables/DrawablesExtensions.cs | 10 +++++----- osu.Game/Storyboards/StoryboardSprite.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 47 files changed, 63 insertions(+), 81 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index bb283dc0c5..fe6420ead8 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -61,6 +61,6 @@ - + diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs index ce2daebbf1..1af77b75fc 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable Anchor = Anchor.Centre, Origin = Anchor.Centre, AccentColour = Color4.Red, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Alpha = 0.5f, Scale = new Vector2(1.333f) }); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs index b9b6d5b924..1e9daf18db 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces Anchor = Anchor.Centre; Origin = Anchor.Centre; - Blending = BlendingMode.Additive; + Blending = BlendingParameters.Additive; Colour = Color4.White.Opacity(0.9f); } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 0b06e958e6..62abe53559 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -201,7 +201,7 @@ namespace osu.Game.Rulesets.Catch.UI additive.Scale = Scale; additive.Colour = HyperDashing ? Color4.Red : Color4.White; additive.RelativePositionAxes = RelativePositionAxes; - additive.Blending = BlendingMode.Additive; + additive.Blending = BlendingParameters.Additive; AdditiveTarget.Add(additive); diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs index a92e56d3c3..31a4857805 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs @@ -26,14 +26,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces public BodyPiece() { - Blending = BlendingMode.Additive; + Blending = BlendingParameters.Additive; Children = new[] { Background = new Box { RelativeSizeAxes = Axes.Both }, Foreground = new BufferedContainer { - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, RelativeSizeAxes = Axes.Both, CacheDrawnFrameBuffer = true, Children = new Drawable[] diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/LaneGlowPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/LaneGlowPiece.cs index 9e0307c5c2..48c7ea7b7f 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/LaneGlowPiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/LaneGlowPiece.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces Name = "Top", RelativeSizeAxes = Axes.Both, Height = 0.5f, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Colour = ColourInfo.GradientVertical(Color4.Transparent, Color4.White.Opacity(alpha)) }, new Box @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.Both, Height = 0.5f, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Colour = ColourInfo.GradientVertical(Color4.White.Opacity(alpha), Color4.Transparent) } }; diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs index b4e29ae9f9..5ee78aa496 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components Name = "Background Gradient Overlay", RelativeSizeAxes = Axes.Both, Height = 0.5f, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Alpha = 0 } }; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs index a2a23e9ff7..523e911434 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections Child = new Box { Size = new Vector2(width), - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Origin = Anchor.Centre, Anchor = Anchor.Centre, Alpha = 0.5f, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index f75b62eecf..1db1eec33e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); - Blending = BlendingMode.Additive; + Blending = BlendingParameters.Additive; Origin = Anchor.Centre; InternalChild = scaleContainer = new SkinnableDrawable("Play/osu/reversearrow", _ => new SpriteIcon diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index c92937ef09..a59cfc1123 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces new TrianglesPiece { RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Alpha = 0.5f, } }; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs index 8ff16f8b84..1d21347cba 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs @@ -17,12 +17,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Anchor = Anchor.Centre; Origin = Anchor.Centre; - Blending = BlendingMode.Additive; + Blending = BlendingParameters.Additive; Alpha = 0; Child = new SkinnableDrawable("Play/osu/hitcircle-explode", _ => new TrianglesPiece { - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, RelativeSizeAxes = Axes.Both, Alpha = 0.2f, }, s => s.GetTexture("Play/osu/hitcircle") == null); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs index c22073f56c..1e3af567fe 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Anchor = Anchor.Centre; Origin = Anchor.Centre; - Blending = BlendingMode.Additive; + Blending = BlendingParameters.Additive; Alpha = 0; Child = new SkinnableDrawable("Play/osu/hitcircle-flash", name => new CircularContainer diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs index 917695c790..a36d9e96c8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Anchor = Anchor.Centre, Origin = Anchor.Centre, Texture = textures.Get(name), - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Alpha = 0.5f }, s => s.GetTexture("Play/osu/hitcircle") == null); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 8b72b23ca3..dbab83d24a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces this.drawableSlider = drawableSlider; this.slider = slider; - Blending = BlendingMode.Additive; + Blending = BlendingParameters.Additive; Origin = Anchor.Centre; Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); @@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Masking = true, BorderThickness = 5, BorderColour = Color4.Orange, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Child = new Box { Colour = Color4.Orange, diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 5ec9dc61e2..82448ec7d5 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Origin = Anchor.Centre, Alpha = 0, RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Masking = true, Children = new[] { @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables RelativeSizeAxes = Axes.Both, Masking = true, BorderThickness = target_ring_thick_border, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Children = new Drawable[] { new Box diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs index b7db819717..d9c0664ecd 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, Colour = Color4.White, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Alpha = 0, AlwaysPresent = true } diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index aa37ff7008..d6866c7d25 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Taiko.UI Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, Alpha = 0, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, }, centre = new Sprite { @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Taiko.UI RelativeSizeAxes = Axes.Both, Size = new Vector2(0.7f), Alpha = 0, - Blending = BlendingMode.Additive + Blending = BlendingParameters.Additive } }; } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 7427a3235d..e62dc45cab 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.UI { RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, }, HitTarget = new HitTarget { @@ -127,14 +127,14 @@ namespace osu.Game.Rulesets.Taiko.UI RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Blending = BlendingMode.Additive + Blending = BlendingParameters.Additive }, judgementContainer = new JudgementContainer { Name = "Judgements", RelativeSizeAxes = Axes.Y, Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Blending = BlendingMode.Additive + Blending = BlendingParameters.Additive }, } }, diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs index 23d9112b25..e95f4c09c6 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs @@ -249,7 +249,7 @@ namespace osu.Game.Tests.Visual.UserInterface Size = new Vector2(50); Masking = true; - Blending = BlendingMode.Additive; + Blending = BlendingParameters.Additive; Alpha = 0.5f; Child = new Box { RelativeSizeAxes = Axes.Both }; diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index d5e28c1e3e..f6c1be0e36 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -125,7 +125,7 @@ namespace osu.Game.Tournament.Components { RelativeSizeAxes = Axes.Both, Colour = Color4.Gray, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Alpha = 0, }, }); diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 3c3a7c056e..5cce29d609 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -86,11 +86,6 @@ namespace osu.Game.Beatmaps.Drawables private readonly FillFlowContainer difficultyFlow; - public string TooltipText - { - set { } - } - public DifficultyIconTooltip() { AutoSizeAxes = Axes.Both; @@ -168,10 +163,6 @@ namespace osu.Game.Beatmaps.Drawables return true; } - public void Refresh() - { - } - public void Move(Vector2 pos) => Position = pos; protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 3ae1c3ef12..17df9ccc7e 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -246,7 +246,7 @@ namespace osu.Game.Beatmaps.Formats switch (type) { case "A": - timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); + timelineGroup?.BlendingParameters.Add(easing, startTime, endTime, BlendingParameters.Additive, startTime == endTime ? BlendingParameters.Additive : BlendingParameters.Inherit); break; case "H": diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs index 8fc8dec9fd..2721ce55dc 100644 --- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs +++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs @@ -98,7 +98,7 @@ namespace osu.Game.Graphics.Containers public OsuScrollbar(Direction scrollDir) : base(scrollDir) { - Blending = BlendingMode.Additive; + Blending = BlendingParameters.Additive; CornerRadius = 5; diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index 092a23e787..e103798355 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -150,7 +150,7 @@ namespace osu.Game.Graphics.Cursor }, AdditiveLayer = new Sprite { - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Colour = colour.Pink, Alpha = 0, Texture = textures.Get(@"Cursor/menu-cursor-additive"), diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs index 74e387d60e..24816deeb5 100644 --- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs @@ -56,7 +56,7 @@ namespace osu.Game.Graphics.Sprites BlurSigma = new Vector2(4), CacheDrawnFrameBuffer = true, RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Size = new Vector2(3f), Children = new[] { diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index b50bf14bab..927ad13829 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -254,7 +254,7 @@ namespace osu.Game.Graphics.UserInterface colourContainer.Add(flash); flash.Colour = ButtonColour; - flash.Blending = BlendingMode.Additive; + flash.Blending = BlendingParameters.Additive; flash.Alpha = 0.3f; flash.FadeOutFromOne(click_duration); flash.Expire(); diff --git a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs index 1a8fea4ff9..660bd7979f 100644 --- a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs @@ -64,7 +64,7 @@ namespace osu.Game.Graphics.UserInterface { RelativeSizeAxes = Axes.Both, Colour = HoverColour, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Alpha = 0, }, } diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index 7a27f825f6..c1810800a0 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -39,7 +39,7 @@ namespace osu.Game.Graphics.UserInterface hover = new Box { RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Colour = Color4.White.Opacity(0.1f), Alpha = 0, Depth = -1 diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index de760eedfd..24ed0cc022 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -296,15 +296,6 @@ namespace osu.Game.Overlays.Profile.Header.Components this.MoveTo(pos, 200, Easing.OutQuint); } - public void Refresh() - { - } - - public string TooltipText - { - set => throw new InvalidOperationException(); - } - protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 2b2b19b73a..d6b810366d 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -79,7 +79,7 @@ namespace osu.Game.Overlays.Toolbar { RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(80).Opacity(180), - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Alpha = 0, }, Flow = new FillFlowContainer diff --git a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs index b286cbfb1d..36387bb00d 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Toolbar { RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(150).Opacity(180), - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Depth = 2, Alpha = 0, }); diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs index 70c0cf623e..5854d66aa8 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons Scale = new Vector2(0.5f), X = 10, Masking = true, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Child = new Box { RelativeSizeAxes = Axes.Both } }; } diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index c615656d60..0d16d8474b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -360,7 +360,7 @@ namespace osu.Game.Screens.Edit.Compose.Components Origin = Anchor.BottomCentre, Anchor = Anchor.BottomCentre, Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0.2f), Color4.White), - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, }, new EquilateralTriangle { diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index badd1e0549..1bf25a2504 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -92,7 +92,7 @@ namespace osu.Game.Screens.Menu { EdgeSmoothness = new Vector2(1.5f, 0), RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Colour = Color4.White, Alpha = 0, }, diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index ba0d624959..f5a72c2c1d 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -296,7 +296,7 @@ namespace osu.Game.Screens.Menu { Colour = Color4.White; RelativeSizeAxes = Axes.Both; - Blending = BlendingMode.Additive; + Blending = BlendingParameters.Additive; } protected override void LoadComplete() @@ -399,11 +399,11 @@ namespace osu.Game.Screens.Menu Origin = Anchor.Centre, Colour = Color4.Black, Size = new Vector2(size - 5), - Blending = BlendingMode.None, + Blending = BlendingParameters.None, }); } - Blending = BlendingMode.Additive; + Blending = BlendingParameters.Additive; CacheDrawnFrameBuffer = true; } } diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 39bda799b5..9d0a5cd05b 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -76,7 +76,7 @@ namespace osu.Game.Screens.Menu public LogoVisualisation() { texture = Texture.WhitePixel; - Blending = BlendingMode.Additive; + Blending = BlendingParameters.Additive; } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Menu/MenuSideFlashes.cs b/osu.Game/Screens/Menu/MenuSideFlashes.cs index 95d0bf04b4..393964561c 100644 --- a/osu.Game/Screens/Menu/MenuSideFlashes.cs +++ b/osu.Game/Screens/Menu/MenuSideFlashes.cs @@ -70,7 +70,7 @@ namespace osu.Game.Screens.Menu // align off-screen to make sure our edges don't become visible during parallax. X = -box_width, Alpha = 0, - Blending = BlendingMode.Additive + Blending = BlendingParameters.Additive }, rightBox = new Box { @@ -81,7 +81,7 @@ namespace osu.Game.Screens.Menu Height = 1.5f, X = box_width, Alpha = 0, - Blending = BlendingMode.Additive + Blending = BlendingParameters.Additive } }; diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 479b3d80b6..0c5bf12bdb 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -124,7 +124,7 @@ namespace osu.Game.Screens.Menu { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Alpha = 0 } } @@ -185,7 +185,7 @@ namespace osu.Game.Screens.Menu flashLayer = new Box { RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Colour = Color4.White, Alpha = 0, }, diff --git a/osu.Game/Screens/Multi/Match/Components/HeaderButton.cs b/osu.Game/Screens/Multi/Match/Components/HeaderButton.cs index f3412d0be7..de6ece6a05 100644 --- a/osu.Game/Screens/Multi/Match/Components/HeaderButton.cs +++ b/osu.Game/Screens/Multi/Match/Components/HeaderButton.cs @@ -31,7 +31,7 @@ namespace osu.Game.Screens.Multi.Match.Components { RelativeSizeAxes = Axes.Both, Alpha = 0.15f, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, }, }); } diff --git a/osu.Game/Screens/ScreenWhiteBox.cs b/osu.Game/Screens/ScreenWhiteBox.cs index 5648dd997b..6c5854d17e 100644 --- a/osu.Game/Screens/ScreenWhiteBox.cs +++ b/osu.Game/Screens/ScreenWhiteBox.cs @@ -95,7 +95,7 @@ namespace osu.Game.Screens Colour = getColourFor(GetType()), Alpha = 0.2f, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, }, textContainer = new FillFlowContainer { diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs index b906bd935c..6118191302 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs @@ -64,7 +64,7 @@ namespace osu.Game.Screens.Select.Carousel { RelativeSizeAxes = Axes.Both, Alpha = 0, - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, }, } }; diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs index a8b5bbbd00..ff9beafb23 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs @@ -121,7 +121,7 @@ namespace osu.Game.Screens.Select.Options { RelativeSizeAxes = Axes.Both, EdgeSmoothness = new Vector2(1.5f, 0), - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, Colour = Color4.White, Alpha = 0, }, diff --git a/osu.Game/Storyboards/CommandTimelineGroup.cs b/osu.Game/Storyboards/CommandTimelineGroup.cs index b1cc0436de..461ee762e9 100644 --- a/osu.Game/Storyboards/CommandTimelineGroup.cs +++ b/osu.Game/Storyboards/CommandTimelineGroup.cs @@ -20,7 +20,7 @@ namespace osu.Game.Storyboards public CommandTimeline Rotation = new CommandTimeline(); public CommandTimeline Colour = new CommandTimeline(); public CommandTimeline Alpha = new CommandTimeline(); - public CommandTimeline BlendingMode = new CommandTimeline(); + public CommandTimeline BlendingParameters = new CommandTimeline(); public CommandTimeline FlipH = new CommandTimeline(); public CommandTimeline FlipV = new CommandTimeline(); @@ -35,7 +35,7 @@ namespace osu.Game.Storyboards yield return Rotation; yield return Colour; yield return Alpha; - yield return BlendingMode; + yield return BlendingParameters; yield return FlipH; yield return FlipV; } diff --git a/osu.Game/Storyboards/Drawables/DrawablesExtensions.cs b/osu.Game/Storyboards/Drawables/DrawablesExtensions.cs index 7e31e1135e..bbc55a336d 100644 --- a/osu.Game/Storyboards/Drawables/DrawablesExtensions.cs +++ b/osu.Game/Storyboards/Drawables/DrawablesExtensions.cs @@ -12,19 +12,19 @@ namespace osu.Game.Storyboards.Drawables /// Adjusts after a delay. ///

/// A to which further transforms can be added. - public static TransformSequence TransformBlendingMode(this T drawable, BlendingMode newValue, double delay = 0) + public static TransformSequence TransformBlendingMode(this T drawable, BlendingParameters newValue, double delay = 0) where T : Drawable - => drawable.TransformTo(drawable.PopulateTransform(new TransformBlendingMode(), newValue, delay)); + => drawable.TransformTo(drawable.PopulateTransform(new TransformBlendingParameters(), newValue, delay)); } - public class TransformBlendingMode : Transform + public class TransformBlendingParameters : Transform { - private BlendingMode valueAt(double time) + private BlendingParameters valueAt(double time) => time < EndTime ? StartValue : EndValue; public override string TargetMember => nameof(Drawable.Blending); protected override void Apply(Drawable d, double time) => d.Blending = valueAt(time); - protected override void ReadIntoStartValue(Drawable d) => StartValue = d.Blending.Mode; + protected override void ReadIntoStartValue(Drawable d) => StartValue = d.Blending; } } diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 8f8ec22aae..37c3ff495f 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -69,7 +69,7 @@ namespace osu.Game.Storyboards applyCommands(drawable, getCommands(g => g.Rotation, triggeredGroups), (d, value) => d.Rotation = value, (d, value, duration, easing) => d.RotateTo(value, duration, easing)); applyCommands(drawable, getCommands(g => g.Colour, triggeredGroups), (d, value) => d.Colour = value, (d, value, duration, easing) => d.FadeColour(value, duration, easing)); applyCommands(drawable, getCommands(g => g.Alpha, triggeredGroups), (d, value) => d.Alpha = value, (d, value, duration, easing) => d.FadeTo(value, duration, easing)); - applyCommands(drawable, getCommands(g => g.BlendingMode, triggeredGroups), (d, value) => d.Blending = value, (d, value, duration, easing) => d.TransformBlendingMode(value, duration), false); + applyCommands(drawable, getCommands(g => g.BlendingParameters, triggeredGroups), (d, value) => d.Blending = value, (d, value, duration, easing) => d.TransformBlendingMode(value, duration), false); if (drawable is IFlippable flippable) { diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 758c4dda4c..8176b61fca 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index d6ad35b663..332d891416 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -118,8 +118,8 @@ - - + + From cca64771dd73030f81a6f9b1019223aacb746294 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Aug 2019 13:56:49 +0900 Subject: [PATCH 2289/2854] Add comment about placement of default loading --- osu.Game/Skinning/LegacySkin.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 5a05420e75..2be31c5ee0 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -43,6 +43,7 @@ namespace osu.Game.Skinning public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) : this(skin, new LegacySkinResourceStore(skin, storage), audioManager, "skin.ini") { + // defaults should only be applied for non-beatmap skins (which are parsed via this constructor). if (!Configuration.CustomColours.ContainsKey("SliderBall")) Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); } From 724365c6d12e4b1f24824dfc7a20448839066f32 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Aug 2019 14:01:07 +0900 Subject: [PATCH 2290/2854] Minor tidying --- osu.Game/Screens/Menu/IntroScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 6a98c78d5f..4d0f7ff87a 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader] private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game) { - // we take a lease on the beatmap bindable to prevent music playback from changing / pausing during intros, as it is causing weird interactions with certains intros. + // prevent user from changing beatmap while the intro is still runnning. beatmap = base.Beatmap.BeginLease(false); menuVoice = config.GetBindable(OsuSetting.MenuVoice); @@ -114,9 +114,9 @@ namespace osu.Game.Screens.Menu protected void LoadMenu() { - DidLoadMenu = true; beatmap.Return(); + DidLoadMenu = true; this.Push(mainMenu); } } From ae7bedacc1a05d9ea4d70eae3c4faee003f6e497 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2019 05:20:49 +0000 Subject: [PATCH 2291/2854] Bump ppy.osu.Framework.iOS from 2019.816.0 to 2019.821.0 Bumps [ppy.osu.Framework.iOS](https://github.com/ppy/osu-framework) from 2019.816.0 to 2019.821.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.816.0...2019.821.0) Signed-off-by: dependabot-preview[bot] --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index d6ad35b663..6e459bdd23 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -119,7 +119,7 @@ - + From 9ce783fbf284d8b6e84798a96e336b454bbc367c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2019 05:21:25 +0000 Subject: [PATCH 2292/2854] Bump ppy.osu.Framework.Android from 2019.816.0 to 2019.821.0 Bumps [ppy.osu.Framework.Android](https://github.com/ppy/osu-framework) from 2019.816.0 to 2019.821.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.816.0...2019.821.0) Signed-off-by: dependabot-preview[bot] --- osu.Android.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android.props b/osu.Android.props index bb283dc0c5..fe6420ead8 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -61,6 +61,6 @@ - + From 444f2b9387bf6c850dd95e49ddd7ce3ed09cfbd6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Aug 2019 14:31:19 +0900 Subject: [PATCH 2293/2854] Specify font size in a saner way --- osu.Game/Skinning/LegacySkin.cs | 35 +++++++++++++-------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 21e8995981..de6346c9dc 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -135,7 +135,7 @@ namespace osu.Game.Skinning case "Play/osu/number-text": return !hasFont(Configuration.HitCircleFont) ? null - : new LegacySpriteText(Textures, Configuration.HitCircleFont) + : new LegacySpriteText(this, Configuration.HitCircleFont) { Scale = new Vector2(0.96f), // Spacing value was reverse-engineered from the ratio of the rendered sprite size in the visual inspector vs the actual texture size @@ -282,45 +282,38 @@ namespace osu.Game.Skinning { private readonly LegacyGlyphStore glyphStore; - public LegacySpriteText(TextureStore textures, string font) + public LegacySpriteText(ISkin skin, string font) { Shadow = false; UseFullGlyphHeight = false; - Font = new FontUsage(font, 16); - glyphStore = new LegacyGlyphStore(textures); + Font = new FontUsage(font, OsuFont.DEFAULT_FONT_SIZE); + glyphStore = new LegacyGlyphStore(skin); } protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore); private class LegacyGlyphStore : ITexturedGlyphLookupStore { - private readonly TextureStore textures; + private readonly ISkin skin; - public LegacyGlyphStore(TextureStore textures) + public LegacyGlyphStore(ISkin skin) { - this.textures = textures; + this.skin = skin; } public ITexturedCharacterGlyph Get(string fontName, char character) { - string textureName = $"{fontName}-{character}"; - - // Approximate value that brings character sizing roughly in-line with stable - float ratio = 36; - - var texture = textures.Get($"{textureName}@2x"); - - if (texture == null) - { - ratio = 18; - texture = textures.Get(textureName); - } + var texture = skin.GetTexture($"{fontName}-{character}"); if (texture != null) - texture.ScaleAdjust = ratio; + // Approximate value that brings character sizing roughly in-line with stable + texture.ScaleAdjust *= 18; - return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture?.Width ?? 0, null), texture, 1f / ratio); + if (texture == null) + return null; + + return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture?.Width ?? 0, null), texture, 1f / texture.ScaleAdjust); } public Task GetAsync(string fontName, char character) => Task.Run(() => Get(fontName, character)); From 4186d2566a4a85d09507804fa90b0a9a67228cea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Aug 2019 15:11:33 +0900 Subject: [PATCH 2294/2854] Remove unnecessary null checks --- osu.Game/Skinning/LegacySkin.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index e2ef3183da..a81e8c2b67 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -317,7 +317,7 @@ namespace osu.Game.Skinning if (texture == null) return null; - return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture?.Width ?? 0, null), texture, 1f / texture.ScaleAdjust); + return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, null), texture, 1f / texture.ScaleAdjust); } public Task GetAsync(string fontName, char character) => Task.Run(() => Get(fontName, character)); From 28dfe072a50f5998b5d31ea9b5bba01c0f9846a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Aug 2019 15:11:46 +0900 Subject: [PATCH 2295/2854] Update one more blending mode change post-master merge --- osu.Game/Skinning/LegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index a81e8c2b67..48310cf027 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -380,7 +380,7 @@ namespace osu.Game.Skinning new Sprite { Texture = skin.GetTexture("sliderb-spec"), - Blending = BlendingMode.Additive, + Blending = BlendingParameters.Additive, }, }; } From 71cbc3525d98e3177c60c60f0677dece66ad9105 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 21 Aug 2019 09:16:09 +0300 Subject: [PATCH 2296/2854] Add/remove displays only if necessary --- .../HitErrorDisplay/HitErrorDisplayOverlay.cs | 62 ++++++++++++++----- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs index 1c61904461..fae1b68e4a 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs @@ -8,7 +8,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Configuration; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Objects; namespace osu.Game.Screens.Play.HitErrorDisplay @@ -22,6 +21,9 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private readonly HitWindows hitWindows; private readonly ScoreProcessor processor; + private BarHitErrorDisplay leftDisplay; + private BarHitErrorDisplay rightDisplay; + public HitErrorDisplayOverlay(ScoreProcessor processor) { this.processor = processor; @@ -43,38 +45,69 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private void onTypeChanged(ValueChangedEvent type) { - clear(); - switch (type.NewValue) { case ScoreMeterType.None: + removeLeftDisplay(); + removeRightDisplay(); break; case ScoreMeterType.HitErrorBoth: - createNew(); - createNew(true); + addLeftDisplay(); + addRightDisplay(); break; case ScoreMeterType.HitErrorLeft: - createNew(); + addLeftDisplay(); + removeRightDisplay(); break; case ScoreMeterType.HitErrorRight: - createNew(true); + addRightDisplay(); + removeLeftDisplay(); break; } } - private void clear() + private void addLeftDisplay() { - Children.ForEach(t => - { - processor.NewJudgement -= t.OnNewJudgement; - t.FadeOut(fade_duration, Easing.OutQuint).Expire(); - }); + if (leftDisplay != null) + return; + + leftDisplay = createNew(); } - private void createNew(bool reversed = false) + private void addRightDisplay() + { + if (rightDisplay != null) + return; + + rightDisplay = createNew(true); + } + + private void removeRightDisplay() + { + if (rightDisplay == null) + return; + + processor.NewJudgement -= rightDisplay.OnNewJudgement; + + rightDisplay.FadeOut(fade_duration, Easing.OutQuint).Expire(); + rightDisplay = null; + } + + private void removeLeftDisplay() + { + if (leftDisplay == null) + return; + + processor.NewJudgement -= leftDisplay.OnNewJudgement; + + leftDisplay.FadeOut(fade_duration, Easing.OutQuint).Expire(); + leftDisplay = null; + } + + private BarHitErrorDisplay createNew(bool reversed = false) { var display = new BarHitErrorDisplay(hitWindows, reversed) { @@ -87,6 +120,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay processor.NewJudgement += display.OnNewJudgement; Add(display); display.FadeInFromZero(fade_duration, Easing.OutQuint); + return display; } } } From a994ad9c84047e583471443c0d68f4ecc0e94dc4 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 21 Aug 2019 09:40:15 +0300 Subject: [PATCH 2297/2854] Use moving average to calculate arrow position --- .../HitErrorDisplay/BarHitErrorDisplay.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs index d8ae3dd9b0..d24982635b 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private readonly SpriteIcon arrow; private readonly FillFlowContainer bar; private readonly Container judgementsContainer; - private readonly Queue judgementOffsets = new Queue(); + private readonly List judgementOffsets = new List(); private readonly double maxHitWindows; public BarHitErrorDisplay(HitWindows hitWindows, bool reversed = false) @@ -154,14 +154,23 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private float getRelativeJudgementPosition(double value) => (float)(value / maxHitWindows); + private double sum = 0; + private float calculateArrowPosition(JudgementResult newJudgement) { + var offset = newJudgement.TimeOffset; + + sum += offset; + + judgementOffsets.Add(offset); + if (judgementOffsets.Count > stored_judgements_amount) - judgementOffsets.Dequeue(); + { + sum -= judgementOffsets[0]; + judgementOffsets.RemoveAt(0); + } - judgementOffsets.Enqueue(newJudgement.TimeOffset); - - return getRelativeJudgementPosition(judgementOffsets.Average()); + return getRelativeJudgementPosition(sum / stored_judgements_amount); } } } From a5acc913eab464a2f0e313df630a1a1a6736e87b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 21 Aug 2019 09:58:47 +0300 Subject: [PATCH 2298/2854] CI fixes --- osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs index d24982635b..d2bef75fd7 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs @@ -14,7 +14,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Colour; using osu.Game.Graphics; using osu.Framework.Extensions.Color4Extensions; -using System.Linq; namespace osu.Game.Screens.Play.HitErrorDisplay { @@ -154,7 +153,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private float getRelativeJudgementPosition(double value) => (float)(value / maxHitWindows); - private double sum = 0; + private double sum; private float calculateArrowPosition(JudgementResult newJudgement) { From 0ccfaeb8d9896ae901e09a8eb284477514cd5fde Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 21 Aug 2019 10:13:59 +0300 Subject: [PATCH 2299/2854] Simplify moving average --- .../Play/HitErrorDisplay/BarHitErrorDisplay.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs index d2bef75fd7..85d017073a 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs @@ -14,6 +14,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Colour; using osu.Game.Graphics; using osu.Framework.Extensions.Color4Extensions; +using System.Linq; namespace osu.Game.Screens.Play.HitErrorDisplay { @@ -153,21 +154,17 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private float getRelativeJudgementPosition(double value) => (float)(value / maxHitWindows); - private double sum; - private float calculateArrowPosition(JudgementResult newJudgement) { - var offset = newJudgement.TimeOffset; + judgementOffsets.Add(newJudgement.TimeOffset); - sum += offset; + if (judgementOffsets.Count < stored_judgements_amount) + return getRelativeJudgementPosition(judgementOffsets.Average()); - judgementOffsets.Add(offset); + double sum = 0; - if (judgementOffsets.Count > stored_judgements_amount) - { - sum -= judgementOffsets[0]; - judgementOffsets.RemoveAt(0); - } + for (int i = judgementOffsets.Count - stored_judgements_amount; i < judgementOffsets.Count; i++) + sum += judgementOffsets[i]; return getRelativeJudgementPosition(sum / stored_judgements_amount); } From fb8d8f9438d8b4b704a3201ccc371c07211b80be Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2019 08:27:56 +0000 Subject: [PATCH 2300/2854] Bump SharpCompress from 0.23.0 to 0.24.0 Bumps [SharpCompress](https://github.com/adamhathcock/sharpcompress) from 0.23.0 to 0.24.0. - [Release notes](https://github.com/adamhathcock/sharpcompress/releases) - [Commits](https://github.com/adamhathcock/sharpcompress/compare/0.23...0.24) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 758c4dda4c..9318468c78 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index d6ad35b663..aebef4dc3a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -120,7 +120,7 @@ - + From 727a6abaf775ff392b4ac135c1f2a188ab1d4d14 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 21 Aug 2019 12:16:30 +0300 Subject: [PATCH 2301/2854] Simplify caching --- .../Graphics/Containers/OsuFocusedOverlayContainer.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 5606328575..0f7b26835b 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -15,6 +15,7 @@ using osu.Game.Overlays; namespace osu.Game.Graphics.Containers { + [Cached(typeof(IPreviewTrackOwner))] public abstract class OsuFocusedOverlayContainer : FocusedOverlayContainer, IPreviewTrackOwner, IKeyBindingHandler { private SampleChannel samplePopIn; @@ -38,13 +39,6 @@ namespace osu.Game.Graphics.Containers protected readonly Bindable OverlayActivationMode = new Bindable(OverlayActivation.All); - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - dependencies.CacheAs(this); - return dependencies; - } - [BackgroundDependencyLoader(true)] private void load(AudioManager audio) { From 87f3184fccee86fe7d3dc94c5ce38d98108ae8a6 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 21 Aug 2019 15:14:33 +0300 Subject: [PATCH 2302/2854] Try retrieving samples without bank names Fallback for spinner bonus samples --- osu.Game/Skinning/SkinnableSound.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 8e2b5cec98..b50cc19482 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -55,8 +55,16 @@ namespace osu.Game.Skinning foreach (var lookup in info.LookupNames) { var ch = getSampleFunction($"Gameplay/{lookup}"); + if (ch == null) - continue; + { + // Try fallback to non-bank samples. + var bank = lookup.Split('/').Last().Split('-')[0] + '-'; + ch = getSampleFunction($"Gameplay/{lookup.Replace(bank, "")}"); + + if (ch == null) + continue; + } ch.Volume.Value = info.Volume / 100.0; return ch; From 7bf430afd5be08dec4dd0aefb4734a42cd1ebc52 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Aug 2019 21:16:06 +0900 Subject: [PATCH 2303/2854] Add padding back to difficulty icons on carousel --- osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 4ceb82d4cc..0259f3cd81 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -98,6 +98,7 @@ namespace osu.Game.Screens.Select.Carousel new FillFlowContainer { AutoSizeAxes = Axes.Both, + Spacing = new Vector2(3), Children = ((CarouselBeatmapSet)Item).Beatmaps.Select(b => new FilterableDifficultyIcon(b)).ToList() }, } From acc07c1d6548cd2a26882b73a8fb4ad3db8599c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Aug 2019 22:18:29 +0900 Subject: [PATCH 2304/2854] Remove mod icon offset --- osu.Game/Rulesets/UI/ModIcon.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 5bb1de7a38..88a2338b94 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -52,7 +52,6 @@ namespace osu.Game.Rulesets.UI Anchor = Anchor.Centre, Size = new Vector2(size), Icon = OsuIcon.ModBg, - Y = -6.5f, Shadow = true, }, modIcon = new SpriteIcon From 4f98361da3b3bcf7b5764522f683eb4b116bebdf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Aug 2019 22:22:03 +0900 Subject: [PATCH 2305/2854] One more offset removed --- osu.Game/Overlays/SettingsSubPanel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/SettingsSubPanel.cs b/osu.Game/Overlays/SettingsSubPanel.cs index 7f794e2927..5000156e97 100644 --- a/osu.Game/Overlays/SettingsSubPanel.cs +++ b/osu.Game/Overlays/SettingsSubPanel.cs @@ -57,7 +57,6 @@ namespace osu.Game.Overlays { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Y = -15, Size = new Vector2(15), Shadow = true, Icon = FontAwesome.Solid.ChevronLeft From db1ff6d211e1d482cea28b922c8a04ac79efab4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Aug 2019 13:48:27 +0900 Subject: [PATCH 2306/2854] Fix video decoding loop running permanently in the background --- osu.Game/Screens/Menu/IntroTriangles.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index f5a72c2c1d..bb3a8f0013 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -139,7 +139,7 @@ namespace osu.Game.Screens.Menu private RulesetFlow rulesets; private Container rulesetsScale; private Drawable logoContainerSecondary; - private Drawable logoContainer; + private Drawable lazerLogo; private GlitchingTriangles triangles; @@ -191,7 +191,7 @@ namespace osu.Game.Screens.Menu RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Child = logoContainer = new LazerLogo(textures.GetStream("Menu/logo-triangles.mp4")) + Child = lazerLogo = new LazerLogo(textures.GetStream("Menu/logo-triangles.mp4")) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -222,7 +222,7 @@ namespace osu.Game.Screens.Menu const float scale_adjust = 0.8f; rulesets.Hide(); - logoContainer.Hide(); + lazerLogo.Hide(); background.Hide(); using (BeginAbsoluteSequence(0, true)) @@ -269,14 +269,17 @@ namespace osu.Game.Screens.Menu rulesets.FadeOut(); // matching flyte curve y = 0.25x^2 + (max(0, x - 0.7) / 0.3) ^ 5 - logoContainer.FadeIn().ScaleTo(scale_start).Then().Delay(logo_scale_duration * 0.7f).ScaleTo(scale_start - scale_adjust, logo_scale_duration * 0.3f, Easing.InQuint); + lazerLogo.FadeIn().ScaleTo(scale_start).Then().Delay(logo_scale_duration * 0.7f).ScaleTo(scale_start - scale_adjust, logo_scale_duration * 0.3f, Easing.InQuint); logoContainerSecondary.ScaleTo(scale_start).Then().ScaleTo(scale_start - scale_adjust * 0.25f, logo_scale_duration, Easing.InQuad); } using (BeginDelayedSequence(logo_2, true)) { - logoContainer.FadeOut().OnComplete(_ => + lazerLogo.FadeOut().OnComplete(_ => { + lazerLogo.Expire(); + lazerLogo.Dispose(); // explicit disposal as we are pushing a new screen and the expire may not get run. + logo.FadeIn(); background.FadeIn(); From 66d84401678a10e3692c95da2042f7c6a71bda94 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 22 Aug 2019 12:50:47 +0300 Subject: [PATCH 2307/2854] Move non-bank samples logic to LegacySkin --- osu.Game/Skinning/LegacySkin.cs | 14 +++++++++++++- osu.Game/Skinning/SkinnableSound.cs | 10 +--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 48310cf027..73957a203e 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -212,7 +212,19 @@ namespace osu.Game.Skinning return texture; } - public override SampleChannel GetSample(string sampleName) => Samples.Get(sampleName); + public override SampleChannel GetSample(string sampleName) + { + var sample = Samples.Get(sampleName); + + if (sample == null) + { + // Try fallback to non-bank samples. + var bank = sampleName.Split('/').Last().Split('-')[0] + '-'; + sample = Samples.Get($"Gameplay/{sampleName.Replace(bank, "")}"); + } + + return sample; + } private bool hasFont(string fontName) => GetTexture($"{fontName}-0") != null; diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index b50cc19482..8e2b5cec98 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -55,16 +55,8 @@ namespace osu.Game.Skinning foreach (var lookup in info.LookupNames) { var ch = getSampleFunction($"Gameplay/{lookup}"); - if (ch == null) - { - // Try fallback to non-bank samples. - var bank = lookup.Split('/').Last().Split('-')[0] + '-'; - ch = getSampleFunction($"Gameplay/{lookup.Replace(bank, "")}"); - - if (ch == null) - continue; - } + continue; ch.Volume.Value = info.Volume / 100.0; return ch; From f4d2bb036b3bcd9ed7819ad1380e1310bf8a2984 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 22 Aug 2019 16:50:54 +0300 Subject: [PATCH 2308/2854] Expand KudosuAction list --- .../Visual/Online/TestSceneKudosuHistory.cs | 80 +++++++++++++++++++ .../Requests/GetUserKudosuHistoryRequest.cs | 5 ++ .../Kudosu/DrawableKudosuHistoryItem.cs | 35 ++++++++ 3 files changed, 120 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs b/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs index dcf2bec239..8badfeaa23 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs @@ -150,6 +150,86 @@ namespace osu.Game.Tests.Visual.Online Username = @"Username6", Url = @"https://osu.ppy.sh/u/1234" } + }, + new APIKudosuHistory + { + Amount = 11, + CreatedAt = new DateTimeOffset(new DateTime(2016, 6, 11)), + Action = KudosuAction.AllowKudosuGive, + Post = new APIKudosuHistory.ModdingPost + { + Title = @"Random post 7", + Url = @"https://osu.ppy.sh/b/1234", + }, + Giver = new APIKudosuHistory.KudosuGiver + { + Username = @"Username7", + Url = @"https://osu.ppy.sh/u/1234" + } + }, + new APIKudosuHistory + { + Amount = 24, + CreatedAt = new DateTimeOffset(new DateTime(2014, 6, 11)), + Action = KudosuAction.DeleteReset, + Post = new APIKudosuHistory.ModdingPost + { + Title = @"Random post 8", + Url = @"https://osu.ppy.sh/b/1234", + }, + Giver = new APIKudosuHistory.KudosuGiver + { + Username = @"Username8", + Url = @"https://osu.ppy.sh/u/1234" + } + }, + new APIKudosuHistory + { + Amount = 12, + CreatedAt = new DateTimeOffset(new DateTime(2016, 6, 11)), + Action = KudosuAction.RestoreGive, + Post = new APIKudosuHistory.ModdingPost + { + Title = @"Random post 9", + Url = @"https://osu.ppy.sh/b/1234", + }, + Giver = new APIKudosuHistory.KudosuGiver + { + Username = @"Username9", + Url = @"https://osu.ppy.sh/u/1234" + } + }, + new APIKudosuHistory + { + Amount = 2, + CreatedAt = new DateTimeOffset(new DateTime(2012, 6, 11)), + Action = KudosuAction.RecalculateGive, + Post = new APIKudosuHistory.ModdingPost + { + Title = @"Random post 10", + Url = @"https://osu.ppy.sh/b/1234", + }, + Giver = new APIKudosuHistory.KudosuGiver + { + Username = @"Username10", + Url = @"https://osu.ppy.sh/u/1234" + } + }, + new APIKudosuHistory + { + Amount = 32, + CreatedAt = new DateTimeOffset(new DateTime(2019, 8, 11)), + Action = KudosuAction.RecalculateReset, + Post = new APIKudosuHistory.ModdingPost + { + Title = @"Random post 11", + Url = @"https://osu.ppy.sh/b/1234", + }, + Giver = new APIKudosuHistory.KudosuGiver + { + Username = @"Username11", + Url = @"https://osu.ppy.sh/u/1234" + } } }; } diff --git a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs index af37bd4b51..dd6f2ccf22 100644 --- a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs @@ -27,5 +27,10 @@ namespace osu.Game.Online.API.Requests VoteReset, DenyKudosuReset, Revoke, + AllowKudosuGive, + DeleteReset, + RestoreGive, + RecalculateGive, + RecalculateReset } } diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index e5f5b720c2..3cc39f0e73 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -91,6 +91,41 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu addMainPart($@"Denied kudosu by {userLinkTemplate()} for the post "); addPostPart(); break; + + case KudosuAction.AllowKudosuGive: + linkFlowContainer.AddText(@"Received "); + addKudosuPart(); + addMainPart(@" from kudosu deny repeal of modding post "); + addPostPart(); + break; + + case KudosuAction.DeleteReset: + linkFlowContainer.AddText(@"Lost "); + addKudosuPart(); + addMainPart(@" from modding post deletion of "); + addPostPart(); + break; + + case KudosuAction.RestoreGive: + linkFlowContainer.AddText(@"Received "); + addKudosuPart(); + addMainPart(@" from modding post restoration of "); + addPostPart(); + break; + + case KudosuAction.RecalculateGive: + linkFlowContainer.AddText(@"Received "); + addKudosuPart(); + addMainPart(@" from votes recalculation in modding post of "); + addPostPart(); + break; + + case KudosuAction.RecalculateReset: + linkFlowContainer.AddText(@"Lost "); + addKudosuPart(); + addMainPart(@" from votes recalculation in modding post of "); + addPostPart(); + break; } } From a30567394ed1973230716b6d6b55090a1097d407 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 23 Aug 2019 12:39:24 +0900 Subject: [PATCH 2309/2854] Remove bottom margin from show more button --- osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs index 5ed546c62b..cf4e1c0dde 100644 --- a/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs +++ b/osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs @@ -124,14 +124,12 @@ namespace osu.Game.Overlays.Profile.Sections private class ChevronIcon : SpriteIcon { - private const int bottom_margin = 2; private const int icon_size = 8; public ChevronIcon() { Anchor = Anchor.Centre; Origin = Anchor.Centre; - Margin = new MarginPadding { Bottom = bottom_margin }; Size = new Vector2(icon_size); Icon = FontAwesome.Solid.ChevronDown; } From 900df5d72f576a0f31d0840b3d1672314eb5e02a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Aug 2019 13:02:50 +0900 Subject: [PATCH 2310/2854] Fix crash on closing game --- osu.Game/Screens/Menu/IntroTriangles.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index bb3a8f0013..db970dd76e 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -138,7 +138,7 @@ namespace osu.Game.Screens.Menu private RulesetFlow rulesets; private Container rulesetsScale; - private Drawable logoContainerSecondary; + private Container logoContainerSecondary; private Drawable lazerLogo; private GlitchingTriangles triangles; @@ -158,7 +158,7 @@ namespace osu.Game.Screens.Menu { this.game = game; - InternalChildren = new[] + InternalChildren = new Drawable[] { triangles = new GlitchingTriangles { @@ -277,7 +277,7 @@ namespace osu.Game.Screens.Menu { lazerLogo.FadeOut().OnComplete(_ => { - lazerLogo.Expire(); + logoContainerSecondary.Remove(lazerLogo); lazerLogo.Dispose(); // explicit disposal as we are pushing a new screen and the expire may not get run. logo.FadeIn(); From c41b1e9eb4c900906c3d092c09c29142eb3bd314 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 22 Aug 2019 21:36:21 -0700 Subject: [PATCH 2311/2854] Fix alignment and size of mute button --- osu.Game/Overlays/Volume/MuteButton.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Volume/MuteButton.cs b/osu.Game/Overlays/Volume/MuteButton.cs index a4884dc2c1..0f69f11985 100644 --- a/osu.Game/Overlays/Volume/MuteButton.cs +++ b/osu.Game/Overlays/Volume/MuteButton.cs @@ -65,13 +65,14 @@ namespace osu.Game.Overlays.Volume { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(20), } }); Current.ValueChanged += muted => { icon.Icon = muted.NewValue ? FontAwesome.Solid.VolumeMute : FontAwesome.Solid.VolumeUp; + icon.Size = new Vector2(muted.NewValue ? 18 : 20); + icon.Margin = new MarginPadding { Right = muted.NewValue ? 2 : 0 }; }; Current.TriggerChange(); From c55d237db6e2db0fc3e468d98ae74bc9a7f6adf3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Aug 2019 17:42:40 +0900 Subject: [PATCH 2312/2854] Use BindValueChanged --- osu.Game/Overlays/Volume/MuteButton.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Volume/MuteButton.cs b/osu.Game/Overlays/Volume/MuteButton.cs index 0f69f11985..6d876a77b1 100644 --- a/osu.Game/Overlays/Volume/MuteButton.cs +++ b/osu.Game/Overlays/Volume/MuteButton.cs @@ -68,14 +68,12 @@ namespace osu.Game.Overlays.Volume } }); - Current.ValueChanged += muted => + Current.BindValueChanged(muted => { icon.Icon = muted.NewValue ? FontAwesome.Solid.VolumeMute : FontAwesome.Solid.VolumeUp; icon.Size = new Vector2(muted.NewValue ? 18 : 20); icon.Margin = new MarginPadding { Right = muted.NewValue ? 2 : 0 }; - }; - - Current.TriggerChange(); + }, true); } protected override bool OnHover(HoverEvent e) From cb54fbee875d7806c3bbc0867242cc2c9e7e1211 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2019 09:29:35 +0000 Subject: [PATCH 2313/2854] Bump ppy.osu.Framework.Android from 2019.821.0 to 2019.823.0 Bumps [ppy.osu.Framework.Android](https://github.com/ppy/osu-framework) from 2019.821.0 to 2019.823.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.821.0...2019.823.0) Signed-off-by: dependabot-preview[bot] --- osu.Android.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android.props b/osu.Android.props index fe6420ead8..9c4a308271 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -61,6 +61,6 @@ - + From 4bc05818716e94dc4bc63b8122c127ac29c520f5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2019 09:33:38 +0000 Subject: [PATCH 2314/2854] Bump ppy.osu.Framework.iOS from 2019.821.0 to 2019.823.0 Bumps [ppy.osu.Framework.iOS](https://github.com/ppy/osu-framework) from 2019.821.0 to 2019.823.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.821.0...2019.823.0) Signed-off-by: dependabot-preview[bot] --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index 05910f846a..39066cb440 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -119,7 +119,7 @@ - + From 11a61b953b15e669a1124c52b2a1e12140b0d327 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2019 09:43:34 +0000 Subject: [PATCH 2315/2854] Bump ppy.osu.Framework from 2019.821.0 to 2019.823.0 Bumps [ppy.osu.Framework](https://github.com/ppy/osu-framework) from 2019.821.0 to 2019.823.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.821.0...2019.823.0) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 905fa57f22..8c692132ad 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 05910f846a..3007a9cb86 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -118,7 +118,7 @@ - + From c836ef57217d086c22df6aa0d72738e2cc076706 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2019 10:04:00 +0000 Subject: [PATCH 2316/2854] Bump ppy.osu.Game.Resources from 2019.809.0 to 2019.823.0 Bumps [ppy.osu.Game.Resources](https://github.com/ppy/osu-resources) from 2019.809.0 to 2019.823.0. - [Release notes](https://github.com/ppy/osu-resources/releases) - [Commits](https://github.com/ppy/osu-resources/compare/2019.809.0...2019.823.0) Signed-off-by: dependabot-preview[bot] --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index fe6420ead8..232d89b375 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -60,7 +60,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 905fa57f22..22af0a5f6d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,7 +14,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 05910f846a..3fe4d1d613 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -117,7 +117,7 @@ - + From 050130e1591e6ec6991b9027fa51c0d085498307 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 23 Aug 2019 14:11:21 +0300 Subject: [PATCH 2317/2854] Refactor PaginatedContainer to centralise repetitive logic --- .../Beatmaps/PaginatedBeatmapContainer.cs | 48 +++---------- .../PaginatedMostPlayedBeatmapContainer.cs | 41 +++-------- .../Kudosu/PaginatedKudosuHistoryContainer.cs | 38 ++-------- .../Profile/Sections/PaginatedContainer.cs | 72 +++++++++++++++---- .../Sections/Ranks/PaginatedScoreContainer.cs | 65 ++++++----------- .../PaginatedRecentActivityContainer.cs | 39 ++-------- 6 files changed, 110 insertions(+), 193 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index 8a6b52b7ee..3fbd1dacd9 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -1,21 +1,22 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; +using System.Collections.Generic; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Direct; using osu.Game.Users; using osuTK; namespace osu.Game.Overlays.Profile.Sections.Beatmaps { - public class PaginatedBeatmapContainer : PaginatedContainer + public class PaginatedBeatmapContainer : PaginatedContainer { private const float panel_padding = 10f; private readonly BeatmapSetType type; - private GetUserBeatmapsRequest request; public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, string header, string missing = "None... yet.") : base(user, header, missing) @@ -27,40 +28,13 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps ItemsContainer.Spacing = new Vector2(panel_padding); } - protected override void ShowMore() + protected override APIRequest> CreateRequest() + => new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); + + protected override Drawable CreateDrawableItem(APIBeatmapSet item) => new DirectGridPanel(item.ToBeatmapSet(Rulesets)) { - request = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); - request.Success += sets => Schedule(() => - { - MoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0); - MoreButton.IsLoading = false; - - if (!sets.Any() && VisiblePages == 1) - { - MissingText.Show(); - return; - } - - foreach (var s in sets) - { - if (!s.OnlineBeatmapSetID.HasValue) - continue; - - ItemsContainer.Add(new DirectGridPanel(s.ToBeatmapSet(Rulesets)) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }); - } - }); - - Api.Queue(request); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - request?.Cancel(); - } + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }; } } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index 23072f8d90..e444363e52 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -1,19 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; +using System.Collections.Generic; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Users; namespace osu.Game.Overlays.Profile.Sections.Historical { - public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer + public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer { - private GetUserMostPlayedBeatmapsRequest request; - public PaginatedMostPlayedBeatmapContainer(Bindable user) : base(user, "Most Played Beatmaps", "No records. :(") { @@ -22,35 +22,10 @@ namespace osu.Game.Overlays.Profile.Sections.Historical ItemsContainer.Direction = FillDirection.Vertical; } - protected override void ShowMore() - { - request = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, ItemsPerPage); - request.Success += beatmaps => Schedule(() => - { - MoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0); - MoreButton.IsLoading = false; + protected override APIRequest> CreateRequest() + => new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, ItemsPerPage); - if (!beatmaps.Any() && VisiblePages == 1) - { - MissingText.Show(); - return; - } - - MissingText.Hide(); - - foreach (var beatmap in beatmaps) - { - ItemsContainer.Add(new DrawableMostPlayedBeatmap(beatmap.GetBeatmapInfo(Rulesets), beatmap.PlayCount)); - } - }); - - Api.Queue(request); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - request?.Cancel(); - } + protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap item) + => new DrawableMostPlayedBeatmap(item.GetBeatmapInfo(Rulesets), item.PlayCount); } } diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs index 29b1d3c5aa..0e7cfc37c0 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs @@ -4,48 +4,24 @@ using osu.Framework.Graphics; using osu.Game.Online.API.Requests; using osu.Game.Users; -using System.Linq; using osu.Framework.Bindables; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.API; +using System.Collections.Generic; namespace osu.Game.Overlays.Profile.Sections.Kudosu { - public class PaginatedKudosuHistoryContainer : PaginatedContainer + public class PaginatedKudosuHistoryContainer : PaginatedContainer { - private GetUserKudosuHistoryRequest request; - public PaginatedKudosuHistoryContainer(Bindable user, string header, string missing) : base(user, header, missing) { ItemsPerPage = 5; } - protected override void ShowMore() - { - request = new GetUserKudosuHistoryRequest(User.Value.Id, VisiblePages++, ItemsPerPage); - request.Success += items => Schedule(() => - { - MoreButton.FadeTo(items.Count == ItemsPerPage ? 1 : 0); - MoreButton.IsLoading = false; + protected override APIRequest> CreateRequest() + => new GetUserKudosuHistoryRequest(User.Value.Id, VisiblePages++, ItemsPerPage); - if (!items.Any() && VisiblePages == 1) - { - MissingText.Show(); - return; - } - - MissingText.Hide(); - - foreach (var item in items) - ItemsContainer.Add(new DrawableKudosuHistoryItem(item)); - }); - - Api.Queue(request); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - request?.Cancel(); - } + protected override Drawable CreateDrawableItem(APIKudosuHistory item) => new DrawableKudosuHistoryItem(item); } } diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index b459afcb49..e329fce67b 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -11,22 +11,25 @@ using osu.Game.Graphics.Sprites; using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Users; +using System.Collections.Generic; +using System.Linq; namespace osu.Game.Overlays.Profile.Sections { - public abstract class PaginatedContainer : FillFlowContainer + public abstract class PaginatedContainer : FillFlowContainer { - protected readonly FillFlowContainer ItemsContainer; - protected readonly ShowMoreButton MoreButton; - protected readonly OsuSpriteText MissingText; + private readonly ShowMoreButton moreButton; + private readonly OsuSpriteText missingText; + private APIRequest> retrievalRequest; + + [Resolved] + private IAPIProvider api { get; set; } protected int VisiblePages; protected int ItemsPerPage; protected readonly Bindable User = new Bindable(); - - protected IAPIProvider Api; - protected APIRequest RetrievalRequest; + protected readonly FillFlowContainer ItemsContainer; protected RulesetStore Rulesets; protected PaginatedContainer(Bindable user, string header, string missing) @@ -51,15 +54,15 @@ namespace osu.Game.Overlays.Profile.Sections RelativeSizeAxes = Axes.X, Spacing = new Vector2(0, 2), }, - MoreButton = new ShowMoreButton + moreButton = new ShowMoreButton { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Alpha = 0, Margin = new MarginPadding { Top = 10 }, - Action = ShowMore, + Action = showMore, }, - MissingText = new OsuSpriteText + missingText = new OsuSpriteText { Font = OsuFont.GetFont(size: 15), Text = missing, @@ -69,9 +72,8 @@ namespace osu.Game.Overlays.Profile.Sections } [BackgroundDependencyLoader] - private void load(IAPIProvider api, RulesetStore rulesets) + private void load(RulesetStore rulesets) { - Api = api; Rulesets = rulesets; User.ValueChanged += onUserChanged; @@ -84,9 +86,51 @@ namespace osu.Game.Overlays.Profile.Sections ItemsContainer.Clear(); if (e.NewValue != null) - ShowMore(); + showMore(); } - protected abstract void ShowMore(); + private void showMore() + { + retrievalRequest = CreateRequest(); + retrievalRequest.Success += items => UpdateItems(items); + + api.Queue(retrievalRequest); + } + + protected virtual void UpdateItems(List items) + { + Schedule(() => + { + moreButton.FadeTo(items.Count == ItemsPerPage ? 1 : 0); + moreButton.IsLoading = false; + + if (!items.Any() && VisiblePages == 1) + { + moreButton.Hide(); + moreButton.IsLoading = false; + missingText.Show(); + return; + } + + LoadComponentsAsync(items.Select(item => CreateDrawableItem(item)), i => + { + missingText.Hide(); + moreButton.FadeTo(items.Count == ItemsPerPage ? 1 : 0); + moreButton.IsLoading = false; + + ItemsContainer.AddRange(i); + }); + }); + } + + protected abstract APIRequest> CreateRequest(); + + protected abstract Drawable CreateDrawableItem(T item); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + retrievalRequest?.Cancel(); + } } } diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 4a9ac6e5c7..dfe300b069 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -5,18 +5,18 @@ using osu.Framework.Graphics.Containers; using osu.Game.Online.API.Requests; using osu.Game.Users; using System; -using System.Collections.Generic; -using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Online.API.Requests.Responses; +using System.Collections.Generic; +using osu.Game.Online.API; namespace osu.Game.Overlays.Profile.Sections.Ranks { - public class PaginatedScoreContainer : PaginatedContainer + public class PaginatedScoreContainer : PaginatedContainer { private readonly bool includeWeight; private readonly ScoreType type; - private GetUserScoresRequest request; public PaginatedScoreContainer(ScoreType type, Bindable user, string header, string missing, bool includeWeight = false) : base(user, header, missing) @@ -29,52 +29,27 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks ItemsContainer.Direction = FillDirection.Vertical; } - protected override void ShowMore() + protected override void UpdateItems(List items) { - request = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); - request.Success += scores => Schedule(() => - { - foreach (var s in scores) - s.Ruleset = Rulesets.GetRuleset(s.RulesetID); + foreach (var item in items) + item.Ruleset = Rulesets.GetRuleset(item.RulesetID); - if (!scores.Any() && VisiblePages == 1) - { - MoreButton.Hide(); - MoreButton.IsLoading = false; - MissingText.Show(); - return; - } - - IEnumerable drawableScores; - - switch (type) - { - default: - drawableScores = scores.Select(score => new DrawablePerformanceScore(score, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null)); - break; - - case ScoreType.Recent: - drawableScores = scores.Select(score => new DrawableTotalScore(score)); - break; - } - - LoadComponentsAsync(drawableScores, s => - { - MissingText.Hide(); - MoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0); - MoreButton.IsLoading = false; - - ItemsContainer.AddRange(s); - }); - }); - - Api.Queue(request); + base.UpdateItems(items); } - protected override void Dispose(bool isDisposing) + protected override APIRequest> CreateRequest() + => new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); + + protected override Drawable CreateDrawableItem(APILegacyScoreInfo item) { - base.Dispose(isDisposing); - request?.Cancel(); + switch (type) + { + default: + return new DrawablePerformanceScore(item, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null); + + case ScoreType.Recent: + return new DrawableTotalScore(item); + } } } } diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index f2a778a874..0251dd0740 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -4,51 +4,24 @@ using osu.Framework.Graphics; using osu.Game.Online.API.Requests; using osu.Game.Users; -using System.Linq; using osu.Framework.Bindables; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.API; +using System.Collections.Generic; namespace osu.Game.Overlays.Profile.Sections.Recent { - public class PaginatedRecentActivityContainer : PaginatedContainer + public class PaginatedRecentActivityContainer : PaginatedContainer { - private GetUserRecentActivitiesRequest request; - public PaginatedRecentActivityContainer(Bindable user, string header, string missing) : base(user, header, missing) { ItemsPerPage = 5; } - protected override void ShowMore() - { - request = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++, ItemsPerPage); - request.Success += activities => Schedule(() => - { - MoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0); - MoreButton.IsLoading = false; + protected override APIRequest> CreateRequest() + => new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++, ItemsPerPage); - if (!activities.Any() && VisiblePages == 1) - { - MissingText.Show(); - return; - } - - MissingText.Hide(); - - foreach (APIRecentActivity activity in activities) - { - ItemsContainer.Add(new DrawableRecentActivity(activity)); - } - }); - - Api.Queue(request); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - request?.Cancel(); - } + protected override Drawable CreateDrawableItem(APIRecentActivity item) => new DrawableRecentActivity(item); } } From 45c0826314fb89ec07026037aa93c7e50979cb80 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 23 Aug 2019 14:14:39 +0300 Subject: [PATCH 2318/2854] Remove repetitive code --- osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index e329fce67b..6c444b2e26 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -101,9 +101,6 @@ namespace osu.Game.Overlays.Profile.Sections { Schedule(() => { - moreButton.FadeTo(items.Count == ItemsPerPage ? 1 : 0); - moreButton.IsLoading = false; - if (!items.Any() && VisiblePages == 1) { moreButton.Hide(); From 6bf31e8f91111d762d657dd1f93dddfeb0577b2a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 23 Aug 2019 13:15:38 +0200 Subject: [PATCH 2319/2854] Make song select grouping & sorting filters persistent --- osu.Game/Configuration/OsuConfigManager.cs | 6 +++ osu.Game/Screens/Select/FilterControl.cs | 54 +++++++--------------- 2 files changed, 23 insertions(+), 37 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 19f46c1d6a..6ebacc642d 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -8,6 +8,7 @@ using osu.Framework.Platform; using osu.Game.Overlays; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Select; +using osu.Game.Screens.Select.Filter; namespace osu.Game.Configuration { @@ -25,6 +26,9 @@ namespace osu.Game.Configuration Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1); Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10, 0.1); + Set(OsuSetting.SelectGroupingMode, GroupMode.All); + Set(OsuSetting.SelectSortingMode, SortMode.Title); + Set(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation); Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1); @@ -150,6 +154,8 @@ namespace osu.Game.Configuration SaveUsername, DisplayStarsMinimum, DisplayStarsMaximum, + SelectGroupingMode, + SelectSortingMode, RandomSelectAlgorithm, ShowFpsDisplay, ChatDisplayHeight, diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 84e8e90f54..b97d64f013 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -29,40 +29,14 @@ namespace osu.Game.Screens.Select private readonly TabControl groupTabs; - private SortMode sort = SortMode.Title; + public readonly Bindable SortMode = new Bindable(Filter.SortMode.Title); - public SortMode Sort - { - get => sort; - set - { - if (sort != value) - { - sort = value; - FilterChanged?.Invoke(CreateCriteria()); - } - } - } - - private GroupMode group = GroupMode.All; - - public GroupMode Group - { - get => group; - set - { - if (group != value) - { - group = value; - FilterChanged?.Invoke(CreateCriteria()); - } - } - } + public readonly Bindable GroupMode = new Bindable(Filter.GroupMode.All); public FilterCriteria CreateCriteria() => new FilterCriteria { - Group = group, - Sort = sort, + Group = GroupMode.Value, + Sort = SortMode.Value, SearchText = searchTextBox.Text, AllowConvertedBeatmaps = showConverted.Value, Ruleset = ruleset.Value @@ -122,7 +96,7 @@ namespace osu.Game.Screens.Select Height = 24, Width = 0.5f, AutoSort = true, - Current = { Value = GroupMode.Title } + Current = GroupMode }, //spriteText = new OsuSpriteText //{ @@ -141,7 +115,7 @@ namespace osu.Game.Screens.Select Width = 0.5f, Height = 24, AutoSort = true, - Current = { Value = SortMode.Title } + Current = SortMode } } }, @@ -151,10 +125,8 @@ namespace osu.Game.Screens.Select searchTextBox.Current.ValueChanged += _ => FilterChanged?.Invoke(CreateCriteria()); - groupTabs.PinItem(GroupMode.All); - groupTabs.PinItem(GroupMode.RecentlyPlayed); - groupTabs.Current.ValueChanged += group => Group = group.NewValue; - sortTabs.Current.ValueChanged += sort => Sort = sort.NewValue; + groupTabs.PinItem(Filter.GroupMode.All); + groupTabs.PinItem(Filter.GroupMode.RecentlyPlayed); } public void Deactivate() @@ -184,7 +156,15 @@ namespace osu.Game.Screens.Select showConverted.ValueChanged += _ => updateCriteria(); ruleset.BindTo(parentRuleset); - ruleset.BindValueChanged(_ => updateCriteria(), true); + ruleset.BindValueChanged(_ => updateCriteria()); + + config.BindWith(OsuSetting.SelectGroupingMode, GroupMode); + config.BindWith(OsuSetting.SelectSortingMode, SortMode); + + GroupMode.BindValueChanged(_ => updateCriteria()); + SortMode.BindValueChanged(_ => updateCriteria()); + + updateCriteria(); } private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria()); From 6ea10ada3451d0096c9e4da34f3e5ac51e50aaae Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 23 Aug 2019 13:31:45 +0200 Subject: [PATCH 2320/2854] Fix visual tests. --- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 680250a226..2dbe53709b 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -111,13 +111,13 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap); - AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; }); - AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; }); - AddStep(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; }); - AddStep(@"Sort by DateAdded", delegate { songSelect.FilterControl.Sort = SortMode.DateAdded; }); - AddStep(@"Sort by BPM", delegate { songSelect.FilterControl.Sort = SortMode.BPM; }); - AddStep(@"Sort by Length", delegate { songSelect.FilterControl.Sort = SortMode.Length; }); - AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; }); + AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.SortMode.Value = SortMode.Artist; }); + AddStep(@"Sort by Title", delegate { songSelect.FilterControl.SortMode.Value = SortMode.Title; }); + AddStep(@"Sort by Author", delegate { songSelect.FilterControl.SortMode.Value = SortMode.Author; }); + AddStep(@"Sort by DateAdded", delegate { songSelect.FilterControl.SortMode.Value = SortMode.DateAdded; }); + AddStep(@"Sort by BPM", delegate { songSelect.FilterControl.SortMode.Value = SortMode.BPM; }); + AddStep(@"Sort by Length", delegate { songSelect.FilterControl.SortMode.Value = SortMode.Length; }); + AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.SortMode.Value = SortMode.Difficulty; }); } [Test] From d8535574d10b26413c18639ccedc93ff15e00a4c Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 23 Aug 2019 14:32:43 +0300 Subject: [PATCH 2321/2854] Pass sample info to Skin.GetSample --- .../TestSceneCatcher.cs | 2 +- .../Gameplay/TestSceneSkinnableDrawable.cs | 7 ++++--- osu.Game/Skinning/DefaultSkin.cs | 3 ++- osu.Game/Skinning/ISkin.cs | 3 ++- osu.Game/Skinning/LegacySkin.cs | 20 +++++++++++-------- .../Skinning/LocalSkinOverrideContainer.cs | 7 ++++--- osu.Game/Skinning/Skin.cs | 3 ++- osu.Game/Skinning/SkinManager.cs | 3 ++- 8 files changed, 29 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 33f93cdb4a..72646e656e 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Catch.Tests return null; } - public SampleChannel GetSample(string sampleName) => + public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); public Texture GetTexture(string componentName) => diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs index 0b5978e3eb..6c003e62ec 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; +using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Skinning; @@ -253,7 +254,7 @@ namespace osu.Game.Tests.Visual.Gameplay public Texture GetTexture(string componentName) => throw new NotImplementedException(); - public SampleChannel GetSample(string sampleName) => throw new NotImplementedException(); + public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); } @@ -264,7 +265,7 @@ namespace osu.Game.Tests.Visual.Gameplay public Texture GetTexture(string componentName) => throw new NotImplementedException(); - public SampleChannel GetSample(string sampleName) => throw new NotImplementedException(); + public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); } @@ -275,7 +276,7 @@ namespace osu.Game.Tests.Visual.Gameplay public Texture GetTexture(string componentName) => throw new NotImplementedException(); - public SampleChannel GetSample(string sampleName) => throw new NotImplementedException(); + public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); } diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index c7556dddd5..6072bb64ed 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -4,6 +4,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; +using osu.Game.Audio; namespace osu.Game.Skinning { @@ -19,6 +20,6 @@ namespace osu.Game.Skinning public override Texture GetTexture(string componentName) => null; - public override SampleChannel GetSample(string sampleName) => null; + public override SampleChannel GetSample(ISampleInfo sampleInfo) => null; } } diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index 0e67a1897c..4867aba0a9 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; +using osu.Game.Audio; namespace osu.Game.Skinning { @@ -17,7 +18,7 @@ namespace osu.Game.Skinning Texture GetTexture(string componentName); - SampleChannel GetSample(string sampleName); + SampleChannel GetSample(ISampleInfo sampleInfo); TValue GetValue(Func query) where TConfiguration : SkinConfiguration; } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 73957a203e..bf2f382527 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -17,6 +17,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Framework.Text; +using osu.Game.Audio; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -212,18 +213,21 @@ namespace osu.Game.Skinning return texture; } - public override SampleChannel GetSample(string sampleName) + public override SampleChannel GetSample(ISampleInfo sampleInfo) { - var sample = Samples.Get(sampleName); - - if (sample == null) + foreach (var lookup in sampleInfo.LookupNames) { - // Try fallback to non-bank samples. - var bank = sampleName.Split('/').Last().Split('-')[0] + '-'; - sample = Samples.Get($"Gameplay/{sampleName.Replace(bank, "")}"); + var sample = Samples.Get(lookup); + + if (sample != null) + return sample; } - return sample; + if (sampleInfo is HitSampleInfo hsi) + // Try fallback to non-bank samples. + return Samples.Get($"{hsi.Name}"); + + return null; } private bool hasFont(string fontName) => GetTexture($"{fontName}-0") != null; diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs index 7882e0f31b..fc36d1c8da 100644 --- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs +++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs @@ -8,6 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; +using osu.Game.Audio; using osu.Game.Configuration; namespace osu.Game.Skinning @@ -49,13 +50,13 @@ namespace osu.Game.Skinning return fallbackSource.GetTexture(componentName); } - public SampleChannel GetSample(string sampleName) + public SampleChannel GetSample(ISampleInfo sampleInfo) { SampleChannel sourceChannel; - if (beatmapHitsounds.Value && (sourceChannel = skin?.GetSample(sampleName)) != null) + if (beatmapHitsounds.Value && (sourceChannel = skin?.GetSample(sampleInfo)) != null) return sourceChannel; - return fallbackSource?.GetSample(sampleName); + return fallbackSource?.GetSample(sampleInfo); } public TValue GetValue(Func query) where TConfiguration : SkinConfiguration diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 09c0d3d0bc..027d9df8b8 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; +using osu.Game.Audio; namespace osu.Game.Skinning { @@ -16,7 +17,7 @@ namespace osu.Game.Skinning public abstract Drawable GetDrawableComponent(string componentName); - public abstract SampleChannel GetSample(string sampleName); + public abstract SampleChannel GetSample(ISampleInfo sampleInfo); public abstract Texture GetTexture(string componentName); diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 19997e8844..e747a8b1ce 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -15,6 +15,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Framework.Platform; +using osu.Game.Audio; using osu.Game.Database; using osu.Game.IO.Archives; @@ -120,7 +121,7 @@ namespace osu.Game.Skinning public Texture GetTexture(string componentName) => CurrentSkin.Value.GetTexture(componentName); - public SampleChannel GetSample(string sampleName) => CurrentSkin.Value.GetSample(sampleName); + public SampleChannel GetSample(ISampleInfo sampleInfo) => CurrentSkin.Value.GetSample(sampleInfo); public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => CurrentSkin.Value.GetValue(query); } From 7e34afeab874911ef0fd3edb1ad94388fc681d9d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 23 Aug 2019 14:38:18 +0300 Subject: [PATCH 2322/2854] Conver to method group --- osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 6c444b2e26..0e2b4c986a 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -82,6 +82,8 @@ namespace osu.Game.Overlays.Profile.Sections private void onUserChanged(ValueChangedEvent e) { + retrievalRequest?.Cancel(); + VisiblePages = 0; ItemsContainer.Clear(); @@ -92,7 +94,7 @@ namespace osu.Game.Overlays.Profile.Sections private void showMore() { retrievalRequest = CreateRequest(); - retrievalRequest.Success += items => UpdateItems(items); + retrievalRequest.Success += UpdateItems; api.Queue(retrievalRequest); } @@ -109,13 +111,13 @@ namespace osu.Game.Overlays.Profile.Sections return; } - LoadComponentsAsync(items.Select(item => CreateDrawableItem(item)), i => + LoadComponentsAsync(items.Select(CreateDrawableItem), drawables => { missingText.Hide(); moreButton.FadeTo(items.Count == ItemsPerPage ? 1 : 0); moreButton.IsLoading = false; - ItemsContainer.AddRange(i); + ItemsContainer.AddRange(drawables); }); }); } From 0cde0982e595da7dc28240b78ed534e83f171507 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 23 Aug 2019 14:52:26 +0300 Subject: [PATCH 2323/2854] Use cansellation token --- osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 0e2b4c986a..75601041e8 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets; using osu.Game.Users; using System.Collections.Generic; using System.Linq; +using System.Threading; namespace osu.Game.Overlays.Profile.Sections { @@ -21,6 +22,7 @@ namespace osu.Game.Overlays.Profile.Sections private readonly ShowMoreButton moreButton; private readonly OsuSpriteText missingText; private APIRequest> retrievalRequest; + private CancellationTokenSource loadCancellation; [Resolved] private IAPIProvider api { get; set; } @@ -82,6 +84,7 @@ namespace osu.Game.Overlays.Profile.Sections private void onUserChanged(ValueChangedEvent e) { + loadCancellation?.Cancel(); retrievalRequest?.Cancel(); VisiblePages = 0; @@ -93,6 +96,8 @@ namespace osu.Game.Overlays.Profile.Sections private void showMore() { + loadCancellation = new CancellationTokenSource(); + retrievalRequest = CreateRequest(); retrievalRequest.Success += UpdateItems; @@ -118,7 +123,7 @@ namespace osu.Game.Overlays.Profile.Sections moreButton.IsLoading = false; ItemsContainer.AddRange(drawables); - }); + }, loadCancellation.Token); }); } From 7b04fb1690fa32ccdd2f75d6bb4b4b33f5b6806f Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 23 Aug 2019 14:54:39 +0300 Subject: [PATCH 2324/2854] StoryboardSample -> StoryboardSampleInfo --- .../Formats/LegacyStoryboardDecoder.cs | 2 +- .../Drawables/DrawableStoryboardSample.cs | 24 +++++++++---------- osu.Game/Storyboards/StoryboardSample.cs | 10 ++++---- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 17df9ccc7e..14c6ea5c8e 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -121,7 +121,7 @@ namespace osu.Game.Beatmaps.Formats var layer = parseLayer(split[2]); var path = cleanFilename(split[3]); var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; - storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); + storyboard.GetLayer(layer).Add(new StoryboardSampleInfo(path, time, (int)volume)); break; } } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs index ffd238d4e1..b04f1d4518 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.IO; using osu.Framework.Allocation; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; @@ -17,25 +16,24 @@ namespace osu.Game.Storyboards.Drawables ///
private const double allowable_late_start = 100; - private readonly StoryboardSample sample; + private readonly StoryboardSampleInfo sampleInfo; private SampleChannel channel; public override bool RemoveWhenNotAlive => false; - public DrawableStoryboardSample(StoryboardSample sample) + public DrawableStoryboardSample(StoryboardSampleInfo sampleInfo) { - this.sample = sample; - LifetimeStart = sample.StartTime; + this.sampleInfo = sampleInfo; + LifetimeStart = sampleInfo.StartTime; } [BackgroundDependencyLoader] private void load(IBindable beatmap) { - // Try first with the full name, then attempt with no path - channel = beatmap.Value.Skin.GetSample(sample.Path) ?? beatmap.Value.Skin.GetSample(Path.ChangeExtension(sample.Path, null)); + channel = beatmap.Value.Skin.GetSample(sampleInfo); if (channel != null) - channel.Volume.Value = sample.Volume / 100; + channel.Volume.Value = sampleInfo.Volume / 100.0; } protected override void Update() @@ -43,27 +41,27 @@ namespace osu.Game.Storyboards.Drawables base.Update(); // TODO: this logic will need to be consolidated with other game samples like hit sounds. - if (Time.Current < sample.StartTime) + if (Time.Current < sampleInfo.StartTime) { // We've rewound before the start time of the sample channel?.Stop(); // In the case that the user fast-forwards to a point far beyond the start time of the sample, // we want to be able to fall into the if-conditional below (therefore we must not have a life time end) - LifetimeStart = sample.StartTime; + LifetimeStart = sampleInfo.StartTime; LifetimeEnd = double.MaxValue; } - else if (Time.Current - Time.Elapsed < sample.StartTime) + else if (Time.Current - Time.Elapsed < sampleInfo.StartTime) { // We've passed the start time of the sample. We only play the sample if we're within an allowable range // from the sample's start, to reduce layering if we've been fast-forwarded far into the future - if (Time.Current - sample.StartTime < allowable_late_start) + if (Time.Current - sampleInfo.StartTime < allowable_late_start) channel?.Play(); // In the case that the user rewinds to a point far behind the start time of the sample, // we want to be able to fall into the if-conditional above (therefore we must not have a life time start) LifetimeStart = double.MinValue; - LifetimeEnd = sample.StartTime; + LifetimeEnd = sampleInfo.StartTime; } } } diff --git a/osu.Game/Storyboards/StoryboardSample.cs b/osu.Game/Storyboards/StoryboardSample.cs index 24231cdca6..4635109d51 100644 --- a/osu.Game/Storyboards/StoryboardSample.cs +++ b/osu.Game/Storyboards/StoryboardSample.cs @@ -1,21 +1,23 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Graphics; +using osu.Game.Audio; using osu.Game.Storyboards.Drawables; namespace osu.Game.Storyboards { - public class StoryboardSample : IStoryboardElement + public class StoryboardSampleInfo : IStoryboardElement, ISampleInfo { - public string Path { get; set; } + public string Path { get; } public bool IsDrawable => true; public double StartTime { get; } - public float Volume; + public int Volume { get; } - public StoryboardSample(string path, double time, float volume) + public StoryboardSampleInfo(string path, double time, int volume) { Path = path; StartTime = time; From b6b050d5e9662155924c3dfa5c41a22a7fbeb1cd Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 23 Aug 2019 14:55:06 +0300 Subject: [PATCH 2325/2854] Add sample path to the lookup names --- osu.Game/Storyboards/StoryboardSample.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Storyboards/StoryboardSample.cs b/osu.Game/Storyboards/StoryboardSample.cs index 4635109d51..5d6ce215f5 100644 --- a/osu.Game/Storyboards/StoryboardSample.cs +++ b/osu.Game/Storyboards/StoryboardSample.cs @@ -17,6 +17,13 @@ namespace osu.Game.Storyboards public int Volume { get; } + public IEnumerable LookupNames => new[] + { + // Try first with the full name, then attempt with no path + Path, + System.IO.Path.ChangeExtension(Path, null), + }; + public StoryboardSampleInfo(string path, double time, int volume) { Path = path; From da72806693cc9688f189f61daa17f2f45ead6145 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 23 Aug 2019 14:55:38 +0300 Subject: [PATCH 2326/2854] Remove lookup logic from SkinnableSound --- osu.Game/Skinning/SkinnableSound.cs | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 8e2b5cec98..23093d9bd9 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -43,28 +42,21 @@ namespace osu.Game.Skinning { channels = hitSamples.Select(s => { - var ch = loadChannel(s, skin.GetSample); + var ch = skin.GetSample(s); + if (ch == null && allowFallback) - ch = loadChannel(s, audio.Samples.Get); + if (s is HitSampleInfo hsi) + ch = audio.Samples.Get(string.IsNullOrEmpty(hsi.Namespace) + ? $"Gameplay/{hsi.Namespace}/{hsi.Bank}-{hsi.Name}" + : $"Gameplay/{hsi.Bank}-{hsi.Name}"); + + if (ch != null) + ch.Volume.Value = s.Volume / 100.0; + return ch; }).Where(c => c != null).ToArray(); } - private SampleChannel loadChannel(ISampleInfo info, Func getSampleFunction) - { - foreach (var lookup in info.LookupNames) - { - var ch = getSampleFunction($"Gameplay/{lookup}"); - if (ch == null) - continue; - - ch.Volume.Value = info.Volume / 100.0; - return ch; - } - - return null; - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From ce5ee095b96111f95bb191dda6e3ea04342c8393 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 23 Aug 2019 14:03:56 +0200 Subject: [PATCH 2327/2854] Fetch config bindables in BDL. --- osu.Game/Screens/Select/FilterControl.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index b97d64f013..44bc3235ac 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -29,9 +29,9 @@ namespace osu.Game.Screens.Select private readonly TabControl groupTabs; - public readonly Bindable SortMode = new Bindable(Filter.SortMode.Title); + public Bindable SortMode; - public readonly Bindable GroupMode = new Bindable(Filter.GroupMode.All); + public Bindable GroupMode; public FilterCriteria CreateCriteria() => new FilterCriteria { @@ -96,7 +96,6 @@ namespace osu.Game.Screens.Select Height = 24, Width = 0.5f, AutoSort = true, - Current = GroupMode }, //spriteText = new OsuSpriteText //{ @@ -115,7 +114,6 @@ namespace osu.Game.Screens.Select Width = 0.5f, Height = 24, AutoSort = true, - Current = SortMode } } }, @@ -158,8 +156,11 @@ namespace osu.Game.Screens.Select ruleset.BindTo(parentRuleset); ruleset.BindValueChanged(_ => updateCriteria()); - config.BindWith(OsuSetting.SelectGroupingMode, GroupMode); - config.BindWith(OsuSetting.SelectSortingMode, SortMode); + SortMode = config.GetBindable(OsuSetting.SelectSortingMode); + GroupMode = config.GetBindable(OsuSetting.SelectGroupingMode); + + sortTabs.Current.BindTo(SortMode); + groupTabs.Current.BindTo(GroupMode); GroupMode.BindValueChanged(_ => updateCriteria()); SortMode.BindValueChanged(_ => updateCriteria()); From 94b5caf7404e072353884b2b278ddea8adce6baf Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 23 Aug 2019 15:18:56 +0300 Subject: [PATCH 2328/2854] Fix build issues --- osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs | 1 + osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 72646e656e..406c0af28d 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics.Sprites; using osuTK.Graphics; using osu.Framework.Audio.Sample; using osu.Framework.Graphics.Textures; +using osu.Game.Audio; namespace osu.Game.Rulesets.Catch.Tests { diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 971518909d..953763c95d 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Beatmaps.Formats int spriteCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSprite)); int animationCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardAnimation)); - int sampleCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSample)); + int sampleCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSampleInfo)); Assert.AreEqual(15, spriteCount); Assert.AreEqual(1, animationCount); From 72a644996c830cd5e1479024a02b81d58fe1e6f0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2019 13:41:27 +0000 Subject: [PATCH 2329/2854] Bump NUnit3TestAdapter from 3.14.0 to 3.15.0 Bumps [NUnit3TestAdapter](https://github.com/nunit/nunit3-vs-adapter) from 3.14.0 to 3.15.0. - [Release notes](https://github.com/nunit/nunit3-vs-adapter/releases) - [Commits](https://github.com/nunit/nunit3-vs-adapter/compare/V3.14...V3.15) Signed-off-by: dependabot-preview[bot] --- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 4100404da6..7c282f449b 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index 013d2a71d4..4dcfc1b81f 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index 92c5c77aac..197309c7c4 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index 82055ecaee..a5db1625d9 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 50530088c2..4a9d88f3a6 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index 257db89a20..2a8bd393da 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -7,7 +7,7 @@ - + WinExe From 1295ec490f7c1e5aedba2d349cfc181ef4710114 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 23 Aug 2019 17:57:31 +0300 Subject: [PATCH 2330/2854] Fix difficulty icon not wrapping correctly in beatmap set overlay --- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index 104315f1c2..28947b6f22 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -91,7 +91,8 @@ namespace osu.Game.Overlays.BeatmapSet { difficulties = new DifficultiesContainer { - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Margin = new MarginPadding { Left = -(tile_icon_padding + tile_spacing / 2) }, OnLostHover = () => { From 67acf20805e083264d039d5c063f8bdd94026979 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 23 Aug 2019 18:31:53 +0300 Subject: [PATCH 2331/2854] Add test beatmap set with many difficulties --- .../Online/TestSceneBeatmapSetOverlay.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index daee419b52..8f19df65a9 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -135,6 +135,9 @@ namespace osu.Game.Tests.Visual.Online }); downloadAssert(true); + + AddStep("show many difficulties", () => overlay.ShowBeatmapSet(createManyDifficultiesBeatmapSet())); + downloadAssert(true); } [Test] @@ -222,6 +225,56 @@ namespace osu.Game.Tests.Visual.Online AddStep(@"show without reload", overlay.Show); } + private BeatmapSetInfo createManyDifficultiesBeatmapSet() + { + var beatmaps = new List(); + + for (int i = 1; i < 41; i++) + { + beatmaps.Add(new BeatmapInfo + { + OnlineBeatmapID = i * 10, + Version = $"Test #{i}", + Ruleset = Ruleset.Value, + StarDifficulty = 2 + i * 0.1, + BaseDifficulty = new BeatmapDifficulty + { + OverallDifficulty = 3.5f, + }, + OnlineInfo = new BeatmapOnlineInfo(), + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(), + }, + }); + } + + return new BeatmapSetInfo + { + OnlineBeatmapSetID = 123, + Metadata = new BeatmapMetadata + { + Title = @"many difficulties beatmap", + Artist = @"none", + Author = new User + { + Username = @"BanchoBot", + Id = 3, + }, + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Preview = @"https://b.ppy.sh/preview/123.mp3", + HasVideo = true, + HasStoryboard = true, + Covers = new BeatmapSetOnlineCovers(), + }, + Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, + Beatmaps = beatmaps, + }; + } + private void downloadAssert(bool shown) { AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.DownloadButtonsVisible == shown); From f639df849fa3094a27a35fa08763e6eb501a0def Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 23 Aug 2019 23:11:36 +0300 Subject: [PATCH 2332/2854] Allow for difficulty icon to contain content --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 5cce29d609..81f517dd86 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -19,23 +19,33 @@ using osuTK.Graphics; namespace osu.Game.Beatmaps.Drawables { - public class DifficultyIcon : Container, IHasCustomTooltip + public class DifficultyIcon : CompositeDrawable, IHasCustomTooltip { private readonly BeatmapInfo beatmap; private readonly RulesetInfo ruleset; + private readonly Container iconContainer; + + /// + /// Size of this difficulty icon. + /// + public new Vector2 Size + { + get => iconContainer.Size; + set => iconContainer.Size = value; + } + public DifficultyIcon(BeatmapInfo beatmap, RulesetInfo ruleset = null, bool shouldShowTooltip = true) { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - - this.beatmap = beatmap; + this.beatmap = beatmap ?? throw new ArgumentNullException(nameof(beatmap)); this.ruleset = ruleset ?? beatmap.Ruleset; if (shouldShowTooltip) TooltipContent = beatmap; - Size = new Vector2(20); + AutoSizeAxes = Axes.Both; + + InternalChild = iconContainer = new Container { Size = new Vector2(20f) }; } public string TooltipText { get; set; } @@ -47,7 +57,7 @@ namespace osu.Game.Beatmaps.Drawables [BackgroundDependencyLoader] private void load(OsuColour colours) { - Children = new Drawable[] + iconContainer.Children = new Drawable[] { new CircularContainer { From d4236c574f99853de259925ebdd44e9dc004200b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 23 Aug 2019 23:30:54 +0300 Subject: [PATCH 2333/2854] Allow difficulty icon to be updateable --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 93 ++++++++++++------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 81f517dd86..6d9d0afc78 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -1,7 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; +using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -21,10 +22,22 @@ namespace osu.Game.Beatmaps.Drawables { public class DifficultyIcon : CompositeDrawable, IHasCustomTooltip { - private readonly BeatmapInfo beatmap; - private readonly RulesetInfo ruleset; + private BeatmapInfo beatmap; private readonly Container iconContainer; + private readonly Box iconBg; + + protected BeatmapInfo Beatmap + { + get => beatmap; + set + { + beatmap = value; + + if (IsLoaded) + updateIconColour(); + } + } /// /// Size of this difficulty icon. @@ -37,15 +50,46 @@ namespace osu.Game.Beatmaps.Drawables public DifficultyIcon(BeatmapInfo beatmap, RulesetInfo ruleset = null, bool shouldShowTooltip = true) { - this.beatmap = beatmap ?? throw new ArgumentNullException(nameof(beatmap)); + this.beatmap = beatmap; - this.ruleset = ruleset ?? beatmap.Ruleset; if (shouldShowTooltip) TooltipContent = beatmap; AutoSizeAxes = Axes.Both; - InternalChild = iconContainer = new Container { Size = new Vector2(20f) }; + InternalChild = iconContainer = new Container + { + Size = new Vector2(20f), + Children = new Drawable[] + { + new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Scale = new Vector2(0.84f), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.08f), + Type = EdgeEffectType.Shadow, + Radius = 5, + }, + Child = iconBg = new Box + { + RelativeSizeAxes = Axes.Both, + }, + }, + new ConstrainedIconContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) + Icon = (ruleset ?? beatmap?.Ruleset)?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } + } + } + }; } public string TooltipText { get; set; } @@ -54,41 +98,18 @@ namespace osu.Game.Beatmaps.Drawables public object TooltipContent { get; set; } + private OsuColour colours; + [BackgroundDependencyLoader] private void load(OsuColour colours) { - iconContainer.Children = new Drawable[] - { - new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Scale = new Vector2(0.84f), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.Black.Opacity(0.08f), - Type = EdgeEffectType.Shadow, - Radius = 5, - }, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.ForDifficultyRating(beatmap.DifficultyRating), - }, - }, - new ConstrainedIconContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) - Icon = ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } - } - }; + this.colours = colours; + + updateIconColour(); } + private void updateIconColour() => iconBg.Colour = colours.ForDifficultyRating(beatmap.DifficultyRating); + private class DifficultyIconTooltip : VisibilityContainer, ITooltip { private readonly OsuSpriteText difficultyName, starRating; From 83b6e0f30c674b19607cac50b79d899e036721a5 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 23 Aug 2019 23:36:23 +0300 Subject: [PATCH 2334/2854] Implement grouped difficulty icon --- .../Drawables/GroupedDifficultyIcon.cs | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs diff --git a/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs new file mode 100644 index 0000000000..17f2bf719d --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs @@ -0,0 +1,58 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets; +using osuTK.Graphics; + +namespace osu.Game.Beatmaps.Drawables +{ + public class GroupedDifficultyIcon : DifficultyIcon + { + private readonly OsuSpriteText counter; + + private List beatmaps; + + protected List Beatmaps + { + get => beatmaps; + set + { + beatmaps = value; + + updateDisplay(); + } + } + + public GroupedDifficultyIcon(List beatmaps, RulesetInfo ruleset, Color4 counterColour) + : base(null, ruleset, false) + { + this.beatmaps = beatmaps; + + AddInternal(counter = new OsuSpriteText + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Padding = new MarginPadding { Left = Size.X }, + Margin = new MarginPadding { Left = 2, Right = 5 }, + Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold), + Colour = counterColour, + }); + + updateDisplay(); + } + + private void updateDisplay() + { + if (beatmaps == null || beatmaps.Count == 0) + return; + + Beatmap = beatmaps.OrderBy(b => b.StarDifficulty).Last(); + counter.Text = beatmaps.Count.ToString(); + } + } +} From 63e6aca61b6f4a246bc05d17d1fbc1cf6e9c2b57 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Fri, 23 Aug 2019 23:40:41 +0300 Subject: [PATCH 2335/2854] Add logic to create grouped difficulty icons in direct panel --- osu.Game/Overlays/Direct/DirectPanel.cs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 8199d80528..b2f3c6befb 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -18,6 +18,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -28,10 +29,12 @@ namespace osu.Game.Overlays.Direct public readonly BeatmapSetInfo SetInfo; private const double hover_transition_time = 400; + private const int maximum_difficulty_icons = 15; private Container content; private BeatmapSetOverlay beatmapSetOverlay; + private RulesetStore rulesets; public PreviewTrack Preview => PlayButton.Preview; public Bindable PreviewPlaying => PlayButton.Playing; @@ -67,9 +70,10 @@ namespace osu.Game.Overlays.Direct }; [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay) + private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay, RulesetStore rulesets) { this.beatmapSetOverlay = beatmapSetOverlay; + this.rulesets = rulesets; AddInternal(content = new Container { @@ -142,8 +146,18 @@ namespace osu.Game.Overlays.Direct { var icons = new List(); - foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty)) - icons.Add(new DifficultyIcon(b)); + if (SetInfo.Beatmaps.Count > maximum_difficulty_icons) + { + foreach (var ruleset in rulesets.AvailableRulesets) + { + List list; + if ((list = SetInfo.Beatmaps.FindAll(b => b.Ruleset.Equals(ruleset))).Count > 0) + icons.Add(new GroupedDifficultyIcon(list, ruleset, this is DirectListPanel ? Color4.White : Color4.Black)); + } + } + else + foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty)) + icons.Add(new DifficultyIcon(b)); return icons; } From 87340061e16d6d41c59802c5c9f9e3b1f0e90e01 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 24 Aug 2019 00:06:28 +0300 Subject: [PATCH 2336/2854] Add logic to create grouped difficulty icons in carousel beatmap set --- .../Carousel/DrawableCarouselBeatmapSet.cs | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 0259f3cd81..c6bd726632 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -19,6 +19,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; +using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -39,7 +40,7 @@ namespace osu.Game.Screens.Select.Carousel } [BackgroundDependencyLoader(true)] - private void load(BeatmapManager manager, BeatmapSetOverlay beatmapOverlay, DialogOverlay overlay) + private void load(BeatmapManager manager, BeatmapSetOverlay beatmapOverlay, DialogOverlay overlay, RulesetStore rulesets) { restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore); dialogOverlay = overlay; @@ -95,11 +96,11 @@ namespace osu.Game.Screens.Select.Carousel TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, Status = beatmapSet.Status }, - new FillFlowContainer + new FillFlowContainer { AutoSizeAxes = Axes.Both, Spacing = new Vector2(3), - Children = ((CarouselBeatmapSet)Item).Beatmaps.Select(b => new FilterableDifficultyIcon(b)).ToList() + Children = getDifficultyIcons(rulesets), }, } } @@ -108,6 +109,27 @@ namespace osu.Game.Screens.Select.Carousel }; } + private const int maximum_difficulty_icons = 18; + + private List getDifficultyIcons(RulesetStore rulesets) + { + var beatmaps = ((CarouselBeatmapSet)Item).Beatmaps.ToList(); + var icons = new List(); + + if (beatmaps.Count > maximum_difficulty_icons) + { + foreach (var ruleset in rulesets.AvailableRulesets.OrderBy(r => r.ID)) + { + List list; + if ((list = beatmaps.FindAll(b => b.Beatmap.Ruleset.Equals(ruleset))).Count > 0) + icons.Add(new FilterableGroupedDifficultyIcon(list, ruleset)); + } + } + else beatmaps.ForEach(b => icons.Add(new FilterableDifficultyIcon(b))); + + return icons; + } + public MenuItem[] ContextMenuItems { get @@ -205,5 +227,21 @@ namespace osu.Game.Screens.Select.Carousel filtered.TriggerChange(); } } + + public class FilterableGroupedDifficultyIcon : GroupedDifficultyIcon + { + public FilterableGroupedDifficultyIcon(List items, RulesetInfo ruleset) + : base(items.Select(i => i.Beatmap).ToList(), ruleset, Color4.White) + { + items.ForEach(item => item.Filtered.ValueChanged += _ => + { + var hiddenItems = items.FindAll(i => !i.Filtered.Value); + var hasHidden = hiddenItems.Count > 0; + + this.FadeTo(hasHidden ? 1 : 0.1f, 100); + Beatmaps = (hasHidden ? hiddenItems : items).Select(i => i.Beatmap).ToList(); + }); + } + } } } From 8584d3ba231e6b8c67d7ea3f85bf3c5075c996c1 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 24 Aug 2019 00:31:36 +0300 Subject: [PATCH 2337/2854] Add many difficulties beatmap direct panel to the tests --- .../Visual/Online/TestSceneDirectPanel.cs | 62 +++++++++++++++---- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs index 53dbaeddda..731cb62518 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Overlays.Direct; using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; using osu.Game.Users; using osuTK; @@ -24,7 +23,7 @@ namespace osu.Game.Tests.Visual.Online typeof(IconPill) }; - private BeatmapSetInfo getUndownloadableBeatmapSet(RulesetInfo ruleset) => new BeatmapSetInfo + private BeatmapSetInfo getUndownloadableBeatmapSet() => new BeatmapSetInfo { OnlineBeatmapSetID = 123, Metadata = new BeatmapMetadata @@ -56,23 +55,62 @@ namespace osu.Game.Tests.Visual.Online { new BeatmapInfo { - Ruleset = ruleset, + Ruleset = Ruleset.Value, Version = "Test", StarDifficulty = 6.42, } } }; - [BackgroundDependencyLoader] - private void load() + private BeatmapSetInfo getManyDifficultiesBeatmapSet(RulesetStore rulesets) { - var ruleset = new OsuRuleset().RulesetInfo; + var beatmaps = new List(); - var normal = CreateWorkingBeatmap(ruleset).BeatmapSetInfo; + for (int i = 0; i < 100; i++) + { + beatmaps.Add(new BeatmapInfo + { + Ruleset = rulesets.GetRuleset(i % 4), + StarDifficulty = 2 + i % 4 * 2, + BaseDifficulty = new BeatmapDifficulty + { + OverallDifficulty = 3.5f, + } + }); + } + + return new BeatmapSetInfo + { + OnlineBeatmapSetID = 1, + Metadata = new BeatmapMetadata + { + Title = "many difficulties beatmap", + Artist = "test", + Author = new User + { + Username = "BanchoBot", + Id = 3, + } + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + HasVideo = true, + HasStoryboard = true, + Covers = new BeatmapSetOnlineCovers(), + }, + Beatmaps = beatmaps, + }; + } + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + var normal = CreateWorkingBeatmap(Ruleset.Value).BeatmapSetInfo; normal.OnlineInfo.HasVideo = true; normal.OnlineInfo.HasStoryboard = true; - var undownloadable = getUndownloadableBeatmapSet(ruleset); + var undownloadable = getUndownloadableBeatmapSet(); + var manyDifficulties = getManyDifficultiesBeatmapSet(rulesets); Child = new BasicScrollContainer { @@ -81,15 +119,17 @@ namespace osu.Game.Tests.Visual.Online { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, + Direction = FillDirection.Full, Padding = new MarginPadding(20), - Spacing = new Vector2(0, 20), + Spacing = new Vector2(5, 20), Children = new Drawable[] { new DirectGridPanel(normal), - new DirectListPanel(normal), new DirectGridPanel(undownloadable), + new DirectGridPanel(manyDifficulties), + new DirectListPanel(normal), new DirectListPanel(undownloadable), + new DirectListPanel(manyDifficulties), }, }, }; From f6feef6b5661cb3b40276311ac2745ee5f3b1053 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 24 Aug 2019 00:33:14 +0300 Subject: [PATCH 2338/2854] Remove redundant using directive --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 6d9d0afc78..7732aa96f8 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; From 8ccbe84f67b87ba97b45576c495a53e509fa878f Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 24 Aug 2019 01:30:33 +0300 Subject: [PATCH 2339/2854] Loop on distinct rulesets of beatmap instead of all --- osu.Game/Overlays/Direct/DirectPanel.cs | 12 +++--------- .../Select/Carousel/DrawableCarouselBeatmapSet.cs | 14 +++++--------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index b2f3c6befb..d6258061e1 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -34,7 +34,6 @@ namespace osu.Game.Overlays.Direct private Container content; private BeatmapSetOverlay beatmapSetOverlay; - private RulesetStore rulesets; public PreviewTrack Preview => PlayButton.Preview; public Bindable PreviewPlaying => PlayButton.Playing; @@ -70,10 +69,9 @@ namespace osu.Game.Overlays.Direct }; [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay, RulesetStore rulesets) + private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay) { this.beatmapSetOverlay = beatmapSetOverlay; - this.rulesets = rulesets; AddInternal(content = new Container { @@ -148,12 +146,8 @@ namespace osu.Game.Overlays.Direct if (SetInfo.Beatmaps.Count > maximum_difficulty_icons) { - foreach (var ruleset in rulesets.AvailableRulesets) - { - List list; - if ((list = SetInfo.Beatmaps.FindAll(b => b.Ruleset.Equals(ruleset))).Count > 0) - icons.Add(new GroupedDifficultyIcon(list, ruleset, this is DirectListPanel ? Color4.White : Color4.Black)); - } + foreach (var ruleset in SetInfo.Beatmaps.Select(b => b.Ruleset).Distinct()) + icons.Add(new GroupedDifficultyIcon(SetInfo.Beatmaps.FindAll(b => b.Ruleset.Equals(ruleset)), ruleset, this is DirectListPanel ? Color4.White : Color4.Black)); } else foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty)) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index c6bd726632..69b9aa399a 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -40,7 +40,7 @@ namespace osu.Game.Screens.Select.Carousel } [BackgroundDependencyLoader(true)] - private void load(BeatmapManager manager, BeatmapSetOverlay beatmapOverlay, DialogOverlay overlay, RulesetStore rulesets) + private void load(BeatmapManager manager, BeatmapSetOverlay beatmapOverlay, DialogOverlay overlay) { restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore); dialogOverlay = overlay; @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Select.Carousel { AutoSizeAxes = Axes.Both, Spacing = new Vector2(3), - Children = getDifficultyIcons(rulesets), + Children = getDifficultyIcons(), }, } } @@ -111,19 +111,15 @@ namespace osu.Game.Screens.Select.Carousel private const int maximum_difficulty_icons = 18; - private List getDifficultyIcons(RulesetStore rulesets) + private List getDifficultyIcons() { var beatmaps = ((CarouselBeatmapSet)Item).Beatmaps.ToList(); var icons = new List(); if (beatmaps.Count > maximum_difficulty_icons) { - foreach (var ruleset in rulesets.AvailableRulesets.OrderBy(r => r.ID)) - { - List list; - if ((list = beatmaps.FindAll(b => b.Beatmap.Ruleset.Equals(ruleset))).Count > 0) - icons.Add(new FilterableGroupedDifficultyIcon(list, ruleset)); - } + foreach (var ruleset in beatmaps.Select(b => b.Beatmap.Ruleset).Distinct()) + icons.Add(new FilterableGroupedDifficultyIcon(beatmaps.FindAll(b => b.Beatmap.Ruleset.Equals(ruleset)), ruleset)); } else beatmaps.ForEach(b => icons.Add(new FilterableDifficultyIcon(b))); From 830ddd6ed9a99c42bac7ba4dfa9714751b46076b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 24 Aug 2019 01:31:53 +0300 Subject: [PATCH 2340/2854] Use all rulesets for many difficulties test --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 7c9b7c7815..6669ec7da3 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -516,6 +516,7 @@ namespace osu.Game.Tests.Visual.SongSelect OnlineBeatmapID = b * 10, Path = $"extra{b}.osu", Version = $"Extra {b}", + Ruleset = rulesets.GetRuleset((b - 1) % 4), StarDifficulty = 2, BaseDifficulty = new BeatmapDifficulty { From 008e39b7381c0b0b7ffcee28df42019ab967ba49 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 24 Aug 2019 01:40:40 +0300 Subject: [PATCH 2341/2854] Remove redundant using directive --- osu.Game/Overlays/Direct/DirectPanel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index d6258061e1..7e16daee75 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -18,7 +18,6 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; From 0bcd323d17b12d1c48a13bbc7de2d674a156d513 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 24 Aug 2019 09:30:43 +0300 Subject: [PATCH 2342/2854] Remove unnecessary string literal --- osu.Game/Skinning/LegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index bf2f382527..d699b1f3c6 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -225,7 +225,7 @@ namespace osu.Game.Skinning if (sampleInfo is HitSampleInfo hsi) // Try fallback to non-bank samples. - return Samples.Get($"{hsi.Name}"); + return Samples.Get(hsi.Name); return null; } From af4adb6339e807ee71be35766c1366fb67fcbf38 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sat, 24 Aug 2019 09:43:55 +0300 Subject: [PATCH 2343/2854] Add xmldoc --- osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs index 17f2bf719d..b48b966918 100644 --- a/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs @@ -11,6 +11,12 @@ using osuTK.Graphics; namespace osu.Game.Beatmaps.Drawables { + /// + /// A difficulty icon that contains a counter on the right-side of it. + /// + /// + /// Used in cases when there are too many difficulty icons to show. + /// public class GroupedDifficultyIcon : DifficultyIcon { private readonly OsuSpriteText counter; From b204e4419a9dab314f49a4dc52682b95fe8e66e2 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Aug 2019 10:34:54 +0200 Subject: [PATCH 2344/2854] Rename SelectSortingMode & SelectGroupingMode -> SongSelectSortingMode & SongSelectGroupingMode --- osu.Game/Configuration/OsuConfigManager.cs | 8 ++++---- osu.Game/Screens/Select/FilterControl.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 6ebacc642d..b13e115387 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -26,8 +26,8 @@ namespace osu.Game.Configuration Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1); Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10, 0.1); - Set(OsuSetting.SelectGroupingMode, GroupMode.All); - Set(OsuSetting.SelectSortingMode, SortMode.Title); + Set(OsuSetting.SongSelectGroupingMode, GroupMode.All); + Set(OsuSetting.SongSelectSortingMode, SortMode.Title); Set(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation); @@ -154,8 +154,8 @@ namespace osu.Game.Configuration SaveUsername, DisplayStarsMinimum, DisplayStarsMaximum, - SelectGroupingMode, - SelectSortingMode, + SongSelectGroupingMode, + SongSelectSortingMode, RandomSelectAlgorithm, ShowFpsDisplay, ChatDisplayHeight, diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 44bc3235ac..06b6cccf96 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -156,8 +156,8 @@ namespace osu.Game.Screens.Select ruleset.BindTo(parentRuleset); ruleset.BindValueChanged(_ => updateCriteria()); - SortMode = config.GetBindable(OsuSetting.SelectSortingMode); - GroupMode = config.GetBindable(OsuSetting.SelectGroupingMode); + SortMode = config.GetBindable(OsuSetting.SongSelectSortingMode); + GroupMode = config.GetBindable(OsuSetting.SongSelectGroupingMode); sortTabs.Current.BindTo(SortMode); groupTabs.Current.BindTo(GroupMode); From a19a9b90ede7f812335433b8f171a56fab578a29 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 25 Aug 2019 11:38:26 +0900 Subject: [PATCH 2345/2854] Simplify group filter display --- .../Drawables/GroupedDifficultyIcon.cs | 33 ++----------------- .../Carousel/DrawableCarouselBeatmapSet.cs | 7 ++-- 2 files changed, 5 insertions(+), 35 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs index b48b966918..fbad113caa 100644 --- a/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs @@ -19,46 +19,19 @@ namespace osu.Game.Beatmaps.Drawables /// public class GroupedDifficultyIcon : DifficultyIcon { - private readonly OsuSpriteText counter; - - private List beatmaps; - - protected List Beatmaps - { - get => beatmaps; - set - { - beatmaps = value; - - updateDisplay(); - } - } - public GroupedDifficultyIcon(List beatmaps, RulesetInfo ruleset, Color4 counterColour) - : base(null, ruleset, false) + : base(beatmaps.OrderBy(b => b.StarDifficulty).Last(), ruleset, false) { - this.beatmaps = beatmaps; - - AddInternal(counter = new OsuSpriteText + AddInternal(new OsuSpriteText { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Padding = new MarginPadding { Left = Size.X }, Margin = new MarginPadding { Left = 2, Right = 5 }, Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold), + Text = beatmaps.Count.ToString(), Colour = counterColour, }); - - updateDisplay(); - } - - private void updateDisplay() - { - if (beatmaps == null || beatmaps.Count == 0) - return; - - Beatmap = beatmaps.OrderBy(b => b.StarDifficulty).Last(); - counter.Text = beatmaps.Count.ToString(); } } } diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 69b9aa399a..79e5debdd2 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -231,11 +231,8 @@ namespace osu.Game.Screens.Select.Carousel { items.ForEach(item => item.Filtered.ValueChanged += _ => { - var hiddenItems = items.FindAll(i => !i.Filtered.Value); - var hasHidden = hiddenItems.Count > 0; - - this.FadeTo(hasHidden ? 1 : 0.1f, 100); - Beatmaps = (hasHidden ? hiddenItems : items).Select(i => i.Beatmap).ToList(); + // for now, fade the whole group based on the ratio of hidden items. + this.FadeTo(1 - 0.9f * ((float)items.Count(i => i.Filtered.Value) / items.Count), 100); }); } } From 2e21fbcf6a2ce843172a1f58be10dbf01ffa758f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 25 Aug 2019 11:44:56 +0900 Subject: [PATCH 2346/2854] Fix incorrect colour usage --- osu.Game/Overlays/Direct/DirectGridPanel.cs | 2 +- osu.Game/Overlays/Direct/DirectListPanel.cs | 2 +- osu.Game/Overlays/Direct/DirectPanel.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index 243e79eb9b..7bf94c1483 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -151,7 +151,7 @@ namespace osu.Game.Overlays.Direct AutoSizeAxes = Axes.X, Height = 20, Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, - Children = GetDifficultyIcons(), + Children = GetDifficultyIcons(colours), }, }, }, diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index 5757e1445b..158ff648dd 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -129,7 +129,7 @@ namespace osu.Game.Overlays.Direct AutoSizeAxes = Axes.X, Height = 20, Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, - Children = GetDifficultyIcons(), + Children = GetDifficultyIcons(colours), }, }, }, diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 7e16daee75..641423f21f 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -139,14 +139,14 @@ namespace osu.Game.Overlays.Direct }; } - protected List GetDifficultyIcons() + protected List GetDifficultyIcons(OsuColour colours) { var icons = new List(); if (SetInfo.Beatmaps.Count > maximum_difficulty_icons) { foreach (var ruleset in SetInfo.Beatmaps.Select(b => b.Ruleset).Distinct()) - icons.Add(new GroupedDifficultyIcon(SetInfo.Beatmaps.FindAll(b => b.Ruleset.Equals(ruleset)), ruleset, this is DirectListPanel ? Color4.White : Color4.Black)); + icons.Add(new GroupedDifficultyIcon(SetInfo.Beatmaps.FindAll(b => b.Ruleset.Equals(ruleset)), ruleset, this is DirectListPanel ? Color4.White : colours.Gray5)); } else foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty)) From 1d34124667666f291957df354b362873c534fce1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 25 Aug 2019 11:56:07 +0900 Subject: [PATCH 2347/2854] Revert all DifficultyIcon changes --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 91 ++++++++----------- 1 file changed, 36 insertions(+), 55 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 7732aa96f8..81f517dd86 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -20,22 +21,10 @@ namespace osu.Game.Beatmaps.Drawables { public class DifficultyIcon : CompositeDrawable, IHasCustomTooltip { - private BeatmapInfo beatmap; + private readonly BeatmapInfo beatmap; + private readonly RulesetInfo ruleset; private readonly Container iconContainer; - private readonly Box iconBg; - - protected BeatmapInfo Beatmap - { - get => beatmap; - set - { - beatmap = value; - - if (IsLoaded) - updateIconColour(); - } - } /// /// Size of this difficulty icon. @@ -48,46 +37,15 @@ namespace osu.Game.Beatmaps.Drawables public DifficultyIcon(BeatmapInfo beatmap, RulesetInfo ruleset = null, bool shouldShowTooltip = true) { - this.beatmap = beatmap; + this.beatmap = beatmap ?? throw new ArgumentNullException(nameof(beatmap)); + this.ruleset = ruleset ?? beatmap.Ruleset; if (shouldShowTooltip) TooltipContent = beatmap; AutoSizeAxes = Axes.Both; - InternalChild = iconContainer = new Container - { - Size = new Vector2(20f), - Children = new Drawable[] - { - new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Scale = new Vector2(0.84f), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.Black.Opacity(0.08f), - Type = EdgeEffectType.Shadow, - Radius = 5, - }, - Child = iconBg = new Box - { - RelativeSizeAxes = Axes.Both, - }, - }, - new ConstrainedIconContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) - Icon = (ruleset ?? beatmap?.Ruleset)?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } - } - } - }; + InternalChild = iconContainer = new Container { Size = new Vector2(20f) }; } public string TooltipText { get; set; } @@ -96,18 +54,41 @@ namespace osu.Game.Beatmaps.Drawables public object TooltipContent { get; set; } - private OsuColour colours; - [BackgroundDependencyLoader] private void load(OsuColour colours) { - this.colours = colours; - - updateIconColour(); + iconContainer.Children = new Drawable[] + { + new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Scale = new Vector2(0.84f), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.08f), + Type = EdgeEffectType.Shadow, + Radius = 5, + }, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.ForDifficultyRating(beatmap.DifficultyRating), + }, + }, + new ConstrainedIconContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) + Icon = ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } + } + }; } - private void updateIconColour() => iconBg.Colour = colours.ForDifficultyRating(beatmap.DifficultyRating); - private class DifficultyIconTooltip : VisibilityContainer, ITooltip { private readonly OsuSpriteText difficultyName, starRating; From ef397434f68b3e6ccd4fca8dc8ddead3f31928f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 25 Aug 2019 12:00:06 +0900 Subject: [PATCH 2348/2854] use GroupBy instead of Distinct+FindAll --- .../Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 79e5debdd2..c9419107a2 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -118,8 +118,8 @@ namespace osu.Game.Screens.Select.Carousel if (beatmaps.Count > maximum_difficulty_icons) { - foreach (var ruleset in beatmaps.Select(b => b.Beatmap.Ruleset).Distinct()) - icons.Add(new FilterableGroupedDifficultyIcon(beatmaps.FindAll(b => b.Beatmap.Ruleset.Equals(ruleset)), ruleset)); + foreach (var group in beatmaps.GroupBy(b => b.Beatmap.Ruleset)) + icons.Add(new FilterableGroupedDifficultyIcon(group.ToList(), group.Key)); } else beatmaps.ForEach(b => icons.Add(new FilterableDifficultyIcon(b))); From 9881d3677df5896d28bb6e95d5ba7b4e65b0744d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 25 Aug 2019 12:05:46 +0900 Subject: [PATCH 2349/2854] Simplify icon creation --- .../Carousel/DrawableCarouselBeatmapSet.cs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index c9419107a2..0a8c61e3d2 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Select.Carousel { AutoSizeAxes = Axes.Both, Spacing = new Vector2(3), - Children = getDifficultyIcons(), + ChildrenEnumerable = getDifficultyIcons(), }, } } @@ -111,19 +111,13 @@ namespace osu.Game.Screens.Select.Carousel private const int maximum_difficulty_icons = 18; - private List getDifficultyIcons() + private IEnumerable getDifficultyIcons() { var beatmaps = ((CarouselBeatmapSet)Item).Beatmaps.ToList(); - var icons = new List(); - if (beatmaps.Count > maximum_difficulty_icons) - { - foreach (var group in beatmaps.GroupBy(b => b.Beatmap.Ruleset)) - icons.Add(new FilterableGroupedDifficultyIcon(group.ToList(), group.Key)); - } - else beatmaps.ForEach(b => icons.Add(new FilterableDifficultyIcon(b))); - - return icons; + return beatmaps.Count > maximum_difficulty_icons + ? (IEnumerable)beatmaps.GroupBy(b => b.Beatmap.Ruleset).Select(group => new FilterableGroupedDifficultyIcon(group.ToList(), group.Key)) + : beatmaps.Select(b => new FilterableDifficultyIcon(b)); } public MenuItem[] ContextMenuItems From de2c6aa23d30e0480755f1d592f18c864f8b4316 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Aug 2019 12:21:11 +0900 Subject: [PATCH 2350/2854] Rename and expand usability of SkinProvidingContainer --- .../Skinning/BeatmapSkinProvidingContainer.cs | 38 ++++++++++++++++ ...Container.cs => SkinProvidingContainer.cs} | 44 ++++++++----------- 2 files changed, 57 insertions(+), 25 deletions(-) create mode 100644 osu.Game/Skinning/BeatmapSkinProvidingContainer.cs rename osu.Game/Skinning/{LocalSkinOverrideContainer.cs => SkinProvidingContainer.cs} (60%) diff --git a/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs b/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs new file mode 100644 index 0000000000..dc25a33f1e --- /dev/null +++ b/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs @@ -0,0 +1,38 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Configuration; + +namespace osu.Game.Skinning +{ + /// + /// A container which overrides existing skin options with beatmap-local values. + /// + public class BeatmapSkinProvidingContainer : SkinProvidingContainer + { + private readonly Bindable beatmapSkins = new Bindable(); + private readonly Bindable beatmapHitsounds = new Bindable(); + + protected override bool AllowConfigurationLookup => beatmapSkins.Value; + protected override bool AllowDrawableLookup(string componentName) => beatmapSkins.Value; + protected override bool AllowTextureLookup(string componentName) => beatmapSkins.Value; + protected override bool AllowSampleLookup(string componentName) => beatmapHitsounds.Value; + + public BeatmapSkinProvidingContainer(ISkin skin) + : base(skin) + { + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.BindWith(OsuSetting.BeatmapSkins, beatmapSkins); + config.BindWith(OsuSetting.BeatmapHitsounds, beatmapHitsounds); + + beatmapSkins.BindValueChanged(_ => TriggerSourceChanged()); + beatmapHitsounds.BindValueChanged(_ => TriggerSourceChanged()); + } + } +} diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs similarity index 60% rename from osu.Game/Skinning/LocalSkinOverrideContainer.cs rename to osu.Game/Skinning/SkinProvidingContainer.cs index 7882e0f31b..a92a0ae0d1 100644 --- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -4,29 +4,32 @@ using System; using osu.Framework.Allocation; using osu.Framework.Audio.Sample; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; -using osu.Game.Configuration; namespace osu.Game.Skinning { /// - /// A container which overrides existing skin options with beatmap-local values. + /// A container which adds a local to the hierarchy. /// - public class LocalSkinOverrideContainer : Container, ISkinSource + public class SkinProvidingContainer : Container, ISkinSource { public event Action SourceChanged; - private readonly Bindable beatmapSkins = new Bindable(); - private readonly Bindable beatmapHitsounds = new Bindable(); - private readonly ISkin skin; private ISkinSource fallbackSource; - public LocalSkinOverrideContainer(ISkin skin) + protected virtual bool AllowDrawableLookup(string componentName) => true; + + protected virtual bool AllowTextureLookup(string componentName) => true; + + protected virtual bool AllowSampleLookup(string componentName) => true; + + protected virtual bool AllowConfigurationLookup => true; + + public SkinProvidingContainer(ISkin skin) { this.skin = skin; } @@ -34,7 +37,7 @@ namespace osu.Game.Skinning public Drawable GetDrawableComponent(string componentName) { Drawable sourceDrawable; - if (beatmapSkins.Value && (sourceDrawable = skin?.GetDrawableComponent(componentName)) != null) + if (AllowDrawableLookup(componentName) && (sourceDrawable = skin?.GetDrawableComponent(componentName)) != null) return sourceDrawable; return fallbackSource?.GetDrawableComponent(componentName); @@ -43,7 +46,7 @@ namespace osu.Game.Skinning public Texture GetTexture(string componentName) { Texture sourceTexture; - if (beatmapSkins.Value && (sourceTexture = skin?.GetTexture(componentName)) != null) + if (AllowTextureLookup(componentName) && (sourceTexture = skin?.GetTexture(componentName)) != null) return sourceTexture; return fallbackSource.GetTexture(componentName); @@ -52,7 +55,7 @@ namespace osu.Game.Skinning public SampleChannel GetSample(string sampleName) { SampleChannel sourceChannel; - if (beatmapHitsounds.Value && (sourceChannel = skin?.GetSample(sampleName)) != null) + if (AllowSampleLookup(sampleName) && (sourceChannel = skin?.GetSample(sampleName)) != null) return sourceChannel; return fallbackSource?.GetSample(sampleName); @@ -61,14 +64,13 @@ namespace osu.Game.Skinning public TValue GetValue(Func query) where TConfiguration : SkinConfiguration { TValue val; - if ((skin as Skin)?.Configuration is TConfiguration conf) - if (beatmapSkins.Value && (val = query.Invoke(conf)) != null) - return val; + if (AllowConfigurationLookup && skin != null && (val = skin.GetValue(query)) != null) + return val; return fallbackSource == null ? default : fallbackSource.GetValue(query); } - private void onSourceChanged() => SourceChanged?.Invoke(); + protected virtual void TriggerSourceChanged() => SourceChanged?.Invoke(); protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { @@ -76,18 +78,10 @@ namespace osu.Game.Skinning fallbackSource = dependencies.Get(); if (fallbackSource != null) - fallbackSource.SourceChanged += onSourceChanged; + fallbackSource.SourceChanged += TriggerSourceChanged; dependencies.CacheAs(this); - var config = dependencies.Get(); - - config.BindWith(OsuSetting.BeatmapSkins, beatmapSkins); - config.BindWith(OsuSetting.BeatmapHitsounds, beatmapHitsounds); - - beatmapSkins.BindValueChanged(_ => onSourceChanged()); - beatmapHitsounds.BindValueChanged(_ => onSourceChanged()); - return dependencies; } @@ -99,7 +93,7 @@ namespace osu.Game.Skinning base.Dispose(isDisposing); if (fallbackSource != null) - fallbackSource.SourceChanged -= onSourceChanged; + fallbackSource.SourceChanged -= TriggerSourceChanged; } } } From 5e362d10b1c26443bb6c95c3f61c0b5984e81b7d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Aug 2019 12:21:49 +0900 Subject: [PATCH 2351/2854] Add ruleset-specific legacy skin providers This moves implementation of osu! skinnables to OsuLegacySkin. --- osu.Game.Rulesets.Osu/OsuLegacySkin.cs | 228 +++++++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 3 + osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 2 +- osu.Game/Rulesets/Ruleset.cs | 3 + osu.Game/Screens/Play/Player.cs | 41 ++-- osu.Game/Skinning/LegacySkin.cs | 137 -------------- 6 files changed, 261 insertions(+), 153 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/OsuLegacySkin.cs diff --git a/osu.Game.Rulesets.Osu/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/OsuLegacySkin.cs new file mode 100644 index 0000000000..0085d64353 --- /dev/null +++ b/osu.Game.Rulesets.Osu/OsuLegacySkin.cs @@ -0,0 +1,228 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Animations; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Skinning; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Osu +{ + public class OsuLegacySkin : ISkin + { + private readonly ISkin source; + + private Lazy configuration; + + private Lazy hasHitCircle; + + /// + /// On osu-stable, hitcircles have 5 pixels of transparent padding on each side to allow for shadows etc. + /// Their hittable area is 128px, but the actual circle portion is 118px. + /// We must account for some gameplay elements such as slider bodies, where this padding is not present. + /// + private const float legacy_circle_radius = 64 - 5; + + public OsuLegacySkin(ISkinSource source) + { + this.source = source; + + source.SourceChanged += sourceChanged; + sourceChanged(); + } + + private void sourceChanged() + { + configuration = new Lazy(() => + { + var config = new SkinConfiguration(); + if (hasHitCircle.Value) + config.SliderPathRadius = legacy_circle_radius; + return config; + }); + + hasHitCircle = new Lazy(() => source.GetTexture("hitcircle") != null); + } + + private const double default_frame_time = 1000 / 60d; + + public Drawable GetDrawableComponent(string componentName) + { + switch (componentName) + { + case "Play/osu/sliderball": + var sliderBallContent = getAnimation("sliderb", true, true, ""); + + if (sliderBallContent != null) + { + var size = sliderBallContent.Size; + + sliderBallContent.RelativeSizeAxes = Axes.Both; + sliderBallContent.Size = Vector2.One; + + return new LegacySliderBall(sliderBallContent) + { + Size = size + }; + } + + return null; + + case "Play/osu/hitcircle": + if (hasHitCircle.Value) + return new LegacyMainCirclePiece(); + + return null; + } + + return null; + } + + private Drawable getAnimation(string componentName, bool animatable, bool looping, string animationSeparator = "-") + { + Texture texture; + + Texture getFrameTexture(int frame) => source.GetTexture($"{componentName}{animationSeparator}{frame}"); + + TextureAnimation animation = null; + + if (animatable) + { + for (int i = 0;; i++) + { + if ((texture = getFrameTexture(i)) == null) + break; + + if (animation == null) + animation = new TextureAnimation + { + DefaultFrameLength = default_frame_time, + Repeat = looping + }; + + animation.AddFrame(texture); + } + } + + if (animation != null) + return animation; + + if ((texture = source.GetTexture(componentName)) != null) + return new Sprite { Texture = texture }; + + return null; + } + + public Texture GetTexture(string componentName) => null; + + public SampleChannel GetSample(string sampleName) => null; + + public TValue GetValue(Func query) where TConfiguration : SkinConfiguration + => configuration.Value is TConfiguration conf ? query.Invoke(conf) : default; + + public class LegacySliderBall : CompositeDrawable + { + private readonly Drawable animationContent; + + public LegacySliderBall(Drawable animationContent) + { + this.animationContent = animationContent; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin, DrawableHitObject drawableObject) + { + animationContent.Colour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White; + + InternalChildren = new[] + { + new Sprite + { + Texture = skin.GetTexture("sliderb-nd"), + Colour = new Color4(5, 5, 5, 255), + }, + animationContent, + new Sprite + { + Texture = skin.GetTexture("sliderb-spec"), + Blending = BlendingParameters.Additive, + }, + }; + } + } + + public class LegacyMainCirclePiece : CompositeDrawable + { + public LegacyMainCirclePiece() + { + Size = new Vector2(128); + } + + private readonly IBindable state = new Bindable(); + + private readonly Bindable accentColour = new Bindable(); + + [BackgroundDependencyLoader] + private void load(DrawableHitObject drawableObject, ISkinSource skin) + { + Sprite hitCircleSprite; + + InternalChildren = new Drawable[] + { + hitCircleSprite = new Sprite + { + Texture = skin.GetTexture("hitcircle"), + Colour = drawableObject.AccentColour.Value, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new SkinnableSpriteText("Play/osu/number-text", _ => new OsuSpriteText + { + Font = OsuFont.Numeric.With(size: 40), + UseFullGlyphHeight = false, + }, confineMode: ConfineMode.NoScaling) + { + Text = (((IHasComboInformation)drawableObject.HitObject).IndexInCurrentCombo + 1).ToString() + }, + new Sprite + { + Texture = skin.GetTexture("hitcircleoverlay"), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }; + + state.BindTo(drawableObject.State); + state.BindValueChanged(updateState, true); + + accentColour.BindTo(drawableObject.AccentColour); + accentColour.BindValueChanged(colour => hitCircleSprite.Colour = colour.NewValue, true); + } + + private void updateState(ValueChangedEvent state) + { + const double legacy_fade_duration = 240; + + switch (state.NewValue) + { + case ArmedState.Hit: + this.FadeOut(legacy_fade_duration, Easing.Out); + this.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + break; + } + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index d50d4f401c..4211ae253c 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -24,6 +24,7 @@ using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Difficulty; using osu.Game.Scoring; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu { @@ -163,6 +164,8 @@ namespace osu.Game.Rulesets.Osu public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this); + public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkin(source); + public override int? LegacyID => 0; public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame(); diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 9037faf606..178183bd22 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.UI }, // Todo: This should not exist, but currently helps to reduce LOH allocations due to unbinding skin source events on judgement disposal // Todo: Remove when hitobjects are properly pooled - new LocalSkinOverrideContainer(null) + new SkinProvidingContainer(null) { RelativeSizeAxes = Axes.Both, Child = HitObjectContainer, diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 42b1322cae..2fbbd74d7a 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -18,6 +18,7 @@ using osu.Game.Configuration; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; using osu.Game.Scoring; +using osu.Game.Skinning; namespace osu.Game.Rulesets { @@ -44,6 +45,8 @@ namespace osu.Game.Rulesets public ModAutoplay GetAutoplayMod() => GetAllMods().OfType().First(); + public virtual ISkin CreateLegacySkinProvider(ISkinSource source) => null; + protected Ruleset(RulesetInfo rulesetInfo = null) { RulesetInfo = rulesetInfo ?? createRulesetInfo(); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e7398be176..deed17a049 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -60,7 +60,9 @@ namespace osu.Game.Screens.Play [Resolved] private ScoreManager scoreManager { get; set; } - private RulesetInfo ruleset; + private RulesetInfo rulesetInfo; + + private Ruleset ruleset; private IAPIProvider api; @@ -121,20 +123,29 @@ namespace osu.Game.Screens.Play InternalChild = GameplayClockContainer = new GameplayClockContainer(working, Mods.Value, DrawableRuleset.GameplayStartTime); + SkinProvidingContainer skinProvidingContainer = new BeatmapSkinProvidingContainer(working.Skin) + { + RelativeSizeAxes = Axes.Both, + }; + GameplayClockContainer.Children = new[] { DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both }, new ScalingContainer(ScalingMode.Gameplay) { - Child = new LocalSkinOverrideContainer(working.Skin) - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + Child = skinProvidingContainer.WithChild( + // the skinProvidingContainer is used as the fallback source here to allow the ruleset-specific skin implementation + // full access to all skin sources. + new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(skinProvidingContainer)) { - DrawableRuleset, - new ComboEffects(ScoreProcessor) + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + DrawableRuleset, + new ComboEffects(ScoreProcessor) + } } - } + ) }, breakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) { @@ -222,20 +233,20 @@ namespace osu.Game.Screens.Play if (beatmap == null) throw new InvalidOperationException("Beatmap was not loaded"); - ruleset = Ruleset.Value ?? beatmap.BeatmapInfo.Ruleset; - var rulesetInstance = ruleset.CreateInstance(); + rulesetInfo = Ruleset.Value ?? beatmap.BeatmapInfo.Ruleset; + ruleset = rulesetInfo.CreateInstance(); try { - DrawableRuleset = rulesetInstance.CreateDrawableRulesetWith(working, Mods.Value); + DrawableRuleset = ruleset.CreateDrawableRulesetWith(working, Mods.Value); } catch (BeatmapInvalidForRulesetException) { // we may fail to create a DrawableRuleset if the beatmap cannot be loaded with the user's preferred ruleset // let's try again forcing the beatmap's ruleset. - ruleset = beatmap.BeatmapInfo.Ruleset; - rulesetInstance = ruleset.CreateInstance(); - DrawableRuleset = rulesetInstance.CreateDrawableRulesetWith(Beatmap.Value, Mods.Value); + rulesetInfo = beatmap.BeatmapInfo.Ruleset; + ruleset = rulesetInfo.CreateInstance(); + DrawableRuleset = ruleset.CreateDrawableRulesetWith(Beatmap.Value, Mods.Value); } if (!DrawableRuleset.Objects.Any()) @@ -313,7 +324,7 @@ namespace osu.Game.Screens.Play var score = DrawableRuleset.ReplayScore?.ScoreInfo ?? new ScoreInfo { Beatmap = Beatmap.Value.BeatmapInfo, - Ruleset = ruleset, + Ruleset = rulesetInfo, Mods = Mods.Value.ToArray(), User = api.LocalUser.Value, }; diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 48310cf027..785daba878 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; @@ -20,8 +19,6 @@ using osu.Framework.Text; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.UI; using osuTK; using osuTK.Graphics; @@ -34,13 +31,6 @@ namespace osu.Game.Skinning protected IResourceStore Samples; - /// - /// On osu-stable, hitcircles have 5 pixels of transparent padding on each side to allow for shadows etc. - /// Their hittable area is 128px, but the actual circle portion is 118px. - /// We must account for some gameplay elements such as slider bodies, where this padding is not present. - /// - private const float legacy_circle_radius = 64 - 5; - public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) : this(skin, new LegacySkinResourceStore(skin, storage), audioManager, "skin.ini") { @@ -48,8 +38,6 @@ namespace osu.Game.Skinning if (!Configuration.CustomColours.ContainsKey("SliderBall")) Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); } - private readonly bool hasHitCircle; - protected LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager, string filename) : base(skin) { @@ -62,14 +50,6 @@ namespace osu.Game.Skinning Samples = audioManager.GetSampleStore(storage); Textures = new TextureStore(new TextureLoaderStore(storage)); - - using (var testStream = storage.GetStream("hitcircle@2x") ?? storage.GetStream("hitcircle")) - hasHitCircle |= testStream != null; - - if (hasHitCircle) - { - Configuration.SliderPathRadius = legacy_circle_radius; - } } protected override void Dispose(bool isDisposing) @@ -94,30 +74,6 @@ namespace osu.Game.Skinning return null; - case "Play/osu/sliderball": - var sliderBallContent = getAnimation("sliderb", true, true, ""); - - if (sliderBallContent != null) - { - var size = sliderBallContent.Size; - - sliderBallContent.RelativeSizeAxes = Axes.Both; - sliderBallContent.Size = Vector2.One; - - return new LegacySliderBall(sliderBallContent) - { - Size = size - }; - } - - return null; - - case "Play/osu/hitcircle": - if (hasHitCircle) - return new LegacyMainCirclePiece(); - - return null; - case "Play/osu/sliderfollowcircle": animatable = true; break; @@ -355,99 +311,6 @@ namespace osu.Game.Skinning } } - public class LegacySliderBall : CompositeDrawable - { - private readonly Drawable animationContent; - - public LegacySliderBall(Drawable animationContent) - { - this.animationContent = animationContent; - } - - [BackgroundDependencyLoader] - private void load(ISkinSource skin, DrawableHitObject drawableObject) - { - animationContent.Colour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White; - - InternalChildren = new[] - { - new Sprite - { - Texture = skin.GetTexture("sliderb-nd"), - Colour = new Color4(5, 5, 5, 255), - }, - animationContent, - new Sprite - { - Texture = skin.GetTexture("sliderb-spec"), - Blending = BlendingParameters.Additive, - }, - }; - } - } - - public class LegacyMainCirclePiece : CompositeDrawable - { - public LegacyMainCirclePiece() - { - Size = new Vector2(128); - } - - private readonly IBindable state = new Bindable(); - - private readonly Bindable accentColour = new Bindable(); - - [BackgroundDependencyLoader] - private void load(DrawableHitObject drawableObject, ISkinSource skin) - { - Sprite hitCircleSprite; - - InternalChildren = new Drawable[] - { - hitCircleSprite = new Sprite - { - Texture = skin.GetTexture("hitcircle"), - Colour = drawableObject.AccentColour.Value, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - new SkinnableSpriteText("Play/osu/number-text", _ => new OsuSpriteText - { - Font = OsuFont.Numeric.With(size: 40), - UseFullGlyphHeight = false, - }, confineMode: ConfineMode.NoScaling) - { - Text = (((IHasComboInformation)drawableObject.HitObject).IndexInCurrentCombo + 1).ToString() - }, - new Sprite - { - Texture = skin.GetTexture("hitcircleoverlay"), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } - }; - - state.BindTo(drawableObject.State); - state.BindValueChanged(updateState, true); - - accentColour.BindTo(drawableObject.AccentColour); - accentColour.BindValueChanged(colour => hitCircleSprite.Colour = colour.NewValue, true); - } - - private void updateState(ValueChangedEvent state) - { - const double legacy_fade_duration = 240; - - switch (state.NewValue) - { - case ArmedState.Hit: - this.FadeOut(legacy_fade_duration, Easing.Out); - this.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); - break; - } - } - } - /// /// A sprite which is displayed within the playfield, but historically was not considered part of the playfield. /// Performs scale adjustment to undo the scale applied by (osu! ruleset specifically). From 6e3a63dae8a9dbc59d47993d884b97b2414de7df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Aug 2019 12:31:51 +0900 Subject: [PATCH 2352/2854] Update tests --- .../SkinnableTestScene.cs | 20 +++++++++++++++---- .../Gameplay/TestSceneSkinnableDrawable.cs | 8 ++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs b/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs index 02716dc1d5..c3a64f8b54 100644 --- a/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs +++ b/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs @@ -37,10 +37,22 @@ namespace osu.Game.Rulesets.Osu.Tests public void SetContents(Func creationFunction) { - Cell(0).Child = new LocalSkinOverrideContainer(null) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction()); - Cell(1).Child = new LocalSkinOverrideContainer(metricsSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction()); - Cell(2).Child = new LocalSkinOverrideContainer(defaultSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction()); - Cell(3).Child = new LocalSkinOverrideContainer(specialSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction()); + Cell(0).Child = createProvider(null, creationFunction); + Cell(1).Child = createProvider(metricsSkin, creationFunction); + Cell(2).Child = createProvider(defaultSkin, creationFunction); + Cell(3).Child = createProvider(specialSkin, creationFunction); + } + + private Drawable createProvider(Skin skin, Func creationFunction) + { + var mainProvider = new SkinProvidingContainer(skin) { RelativeSizeAxes = Axes.Both }; + + return mainProvider + .WithChild(new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider)) + { + RelativeSizeAxes = Axes.Both, + Child = creationFunction() + }); } private class TestLegacySkin : LegacySkin diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs index 0b5978e3eb..b18eca0600 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("setup layout larger source", () => { - Child = new LocalSkinOverrideContainer(new SizedSource(50)) + Child = new SkinProvidingContainer(new SizedSource(50)) { RelativeSizeAxes = Axes.Both, Child = fill = new FillFlowContainer @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("setup layout larger source", () => { - Child = new LocalSkinOverrideContainer(new SizedSource(30)) + Child = new SkinProvidingContainer(new SizedSource(30)) { RelativeSizeAxes = Axes.Both, Child = fill = new FillFlowContainer @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Gameplay Child = new SkinSourceContainer { RelativeSizeAxes = Axes.Both, - Child = new LocalSkinOverrideContainer(secondarySource) + Child = new SkinProvidingContainer(secondarySource) { RelativeSizeAxes = Axes.Both, Child = consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true) @@ -120,7 +120,7 @@ namespace osu.Game.Tests.Visual.Gameplay Child = new SkinSourceContainer { RelativeSizeAxes = Axes.Both, - Child = target = new LocalSkinOverrideContainer(secondarySource) + Child = target = new SkinProvidingContainer(secondarySource) { RelativeSizeAxes = Axes.Both, } From d99c60adc7cf97d11de29631ec46c1c652a1b62e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Aug 2019 12:51:13 +0900 Subject: [PATCH 2353/2854] Provide a way to scale beat lengths relative to each other --- .../Rulesets/Timing/MultiplierControlPoint.cs | 8 +++- .../UI/Scrolling/DrawableScrollingRuleset.cs | 38 +++++++++++++++---- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs index 9bab065d1e..ffa35c24cd 100644 --- a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs +++ b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs @@ -20,7 +20,13 @@ namespace osu.Game.Rulesets.Timing /// /// The aggregate multiplier which this provides. /// - public double Multiplier => Velocity * DifficultyPoint.SpeedMultiplier * 1000 / TimingPoint.BeatLength; + public double Multiplier => Velocity * DifficultyPoint.SpeedMultiplier * BaseBeatLength / TimingPoint.BeatLength; + + /// + /// The base beat length to scale the provided multiplier relative to. + /// + /// For a of 1000, a with a beat length of 500 will increase the multiplier by 2. + public double BaseBeatLength = 1000; /// /// The velocity multiplier. diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index 42ec0b79b9..385f824ef5 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -69,6 +69,11 @@ namespace osu.Game.Rulesets.UI.Scrolling /// protected virtual bool UserScrollSpeedAdjustment => true; + /// + /// Whether beat lengths should scale relative to the most common beat length in the . + /// + protected virtual bool RelativeScaleBeatLengths => false; + /// /// Provides the default s that adjust the scrolling rate of s /// inside this . @@ -107,16 +112,35 @@ namespace osu.Game.Rulesets.UI.Scrolling [BackgroundDependencyLoader] private void load() { - // Calculate default multiplier control points + double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; + double baseBeatLength = 1000; + + if (RelativeScaleBeatLengths) + { + IReadOnlyList timingPoints = Beatmap.ControlPointInfo.TimingPoints; + double maxDuration = 0; + + for (int i = 0; i < timingPoints.Count; i++) + { + double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time : lastObjectTime; + double duration = endTime - timingPoints[i].Time; + + if (duration > maxDuration) + { + maxDuration = duration; + baseBeatLength = timingPoints[i].BeatLength; + } + } + } + + // Merge sequences of timing and difficulty control points to create the aggregate "multiplier" control point var lastTimingPoint = new TimingControlPoint(); var lastDifficultyPoint = new DifficultyControlPoint(); - - // Merge timing + difficulty points var allPoints = new SortedList(Comparer.Default); allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints); allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints); - // Generate the timing points, making non-timing changes use the previous timing change + // Generate the timing points, making non-timing changes use the previous timing change and vice-versa var timingChanges = allPoints.Select(c => { var timingPoint = c as TimingControlPoint; @@ -131,14 +155,13 @@ namespace osu.Game.Rulesets.UI.Scrolling return new MultiplierControlPoint(c.Time) { Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier, + BaseBeatLength = baseBeatLength, TimingPoint = lastTimingPoint, DifficultyPoint = lastDifficultyPoint }; }); - double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; - - // Perform some post processing of the timing changes + // Trim unwanted sequences of timing changes timingChanges = timingChanges // Collapse sections after the last hit object .Where(s => s.StartTime <= lastObjectTime) @@ -147,7 +170,6 @@ namespace osu.Game.Rulesets.UI.Scrolling controlPoints.AddRange(timingChanges); - // If we have no control points, add a default one if (controlPoints.Count == 0) controlPoints.Add(new MultiplierControlPoint { Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier }); } From e30ae57ea66325491eb1f94c7314d6270da5d3c8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Aug 2019 12:51:23 +0900 Subject: [PATCH 2354/2854] Scale mania beat lengths relative to each other --- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index c8aeda8fe4..0718de2c7d 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -36,6 +36,8 @@ namespace osu.Game.Rulesets.Mania.UI public IEnumerable BarLines; + protected override bool RelativeScaleBeatLengths => true; + protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config; private readonly Bindable configDirection = new Bindable(); From 7b82121b8503f81de85013e9c82435d4b584d34c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Aug 2019 13:03:00 +0900 Subject: [PATCH 2355/2854] Add comment about lazy usage --- osu.Game.Rulesets.Osu/OsuLegacySkin.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/OsuLegacySkin.cs index 0085d64353..988eb6a439 100644 --- a/osu.Game.Rulesets.Osu/OsuLegacySkin.cs +++ b/osu.Game.Rulesets.Osu/OsuLegacySkin.cs @@ -45,6 +45,7 @@ namespace osu.Game.Rulesets.Osu private void sourceChanged() { + // these need to be lazy in order to ensure they aren't called before the dependencies have been loaded into our source. configuration = new Lazy(() => { var config = new SkinConfiguration(); From 289bd8e6b0c9367ae4cb9bbb15a4f3be7c424213 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Aug 2019 14:25:35 +0900 Subject: [PATCH 2356/2854] Don't return DefaultSkin on beatmap skin parsing failure --- osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 5657b8fb8a..2d8a0b1249 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -138,19 +138,15 @@ namespace osu.Game.Beatmaps protected override Skin GetSkin() { - Skin skin; - try { - skin = new LegacyBeatmapSkin(BeatmapInfo, store, AudioManager); + return new LegacyBeatmapSkin(BeatmapInfo, store, AudioManager); } catch (Exception e) { Logger.Error(e, "Skin failed to load"); - skin = new DefaultSkin(); + return null; } - - return skin; } } } From db987c6077153102848ffe9f16c79de8480c39e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Aug 2019 14:24:48 +0900 Subject: [PATCH 2357/2854] Move SliderBall colour logic to OsuLegacySkinProvider --- osu.Game.Rulesets.Osu/OsuLegacySkin.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Osu/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/OsuLegacySkin.cs index 988eb6a439..1957156918 100644 --- a/osu.Game.Rulesets.Osu/OsuLegacySkin.cs +++ b/osu.Game.Rulesets.Osu/OsuLegacySkin.cs @@ -51,6 +51,12 @@ namespace osu.Game.Rulesets.Osu var config = new SkinConfiguration(); if (hasHitCircle.Value) config.SliderPathRadius = legacy_circle_radius; + + // defaults should only be applied for non-beatmap skins (which are parsed via this constructor). + config.CustomColours["SliderBall"] = + source.GetValue(s => s.CustomColours.TryGetValue("SliderBall", out var val) ? val : (Color4?)null) + ?? new Color4(2, 170, 255, 255); + return config; }); From 1cfe2b7de8388ade173269519e7947400ef18778 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Aug 2019 16:25:23 +0900 Subject: [PATCH 2358/2854] Fix timing points beyond the end time potentially becoming dominant --- osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index 385f824ef5..65a22b10b7 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -122,6 +122,9 @@ namespace osu.Game.Rulesets.UI.Scrolling for (int i = 0; i < timingPoints.Count; i++) { + if (timingPoints[i].Time > lastObjectTime) + break; + double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time : lastObjectTime; double duration = endTime - timingPoints[i].Time; From fb8b5ee1060dfd0ec8467ed15ddcc4c84806603f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Aug 2019 16:31:46 +0900 Subject: [PATCH 2359/2854] Add test --- .../TestSceneDrawableScrollingRuleset.cs | 305 ++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs new file mode 100644 index 0000000000..ee11fc0d06 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -0,0 +1,305 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Framework.MathUtils; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Configuration; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.UI.Scrolling; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneDrawableScrollingRuleset : OsuTestScene + { + /// + /// The amount of time visible by the "view window" of the playfield. + /// All hitobjects added through are spaced apart by this value, such that for a beat length of 1000, + /// there will be at most 2 hitobjects visible in the "view window". + /// + private const double time_range = 1000; + + private readonly ManualClock testClock = new ManualClock(); + private TestDrawableScrollingRuleset drawableRuleset; + + [SetUp] + public void Setup() => Schedule(() => testClock.CurrentTime = 0); + + [Test] + public void TestRelativeBeatLengthScaleSingleTimingPoint() + { + var beatmap = createBeatmap(new TimingControlPoint { BeatLength = time_range / 2 }); + + createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true); + + assertPosition(0, 0f); + + // The single timing point is 1x speed relative to itself, such that the hitobject occurring time_range milliseconds later should appear + // at the bottom of the view window regardless of the timing point's beat length + assertPosition(1, 1f); + } + + [Test] + public void TestRelativeBeatLengthScaleTimingPointBeyondEndDoesNotBecomeDominant() + { + var beatmap = createBeatmap( + new TimingControlPoint { BeatLength = time_range / 2 }, + new TimingControlPoint { Time = 12000, BeatLength = time_range }, + new TimingControlPoint { Time = 100000, BeatLength = time_range }); + + createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true); + + assertPosition(0, 0f); + assertPosition(1, 1f); + } + + [Test] + public void TestRelativeBeatLengthScaleFromSecondTimingPoint() + { + var beatmap = createBeatmap( + new TimingControlPoint { BeatLength = time_range }, + new TimingControlPoint { Time = 3 * time_range, BeatLength = time_range / 2 }); + + createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true); + + // The first timing point should have a relative velocity of 2 + assertPosition(0, 0f); + assertPosition(1, 0.5f); + assertPosition(2, 1f); + + // Move to the second timing point + setTime(3 * time_range); + assertPosition(3, 0f); + + // As above, this is the timing point that is 1x speed relative to itself, so the hitobject occurring time_range milliseconds later should be at the bottom of the view window + assertPosition(4, 1f); + } + + [Test] + public void TestNonRelativeScale() + { + var beatmap = createBeatmap( + new TimingControlPoint { BeatLength = time_range }, + new TimingControlPoint { Time = 3 * time_range, BeatLength = time_range / 2 }); + + createTest(beatmap); + + assertPosition(0, 0f); + assertPosition(1, 1); + + // Move to the second timing point + setTime(3 * time_range); + assertPosition(3, 0f); + + // For a beat length of 500, the view window of this timing point is elongated 2x (1000 / 500), such that the second hitobject is two TimeRanges away (offscreen) + // To bring it on-screen, half TimeRange is added to the current time, bringing the second half of the view window into view, and the hitobject should appear at the bottom + setTime(3 * time_range + time_range / 2); + assertPosition(4, 1f); + } + + private void assertPosition(int index, float relativeY) => AddAssert($"hitobject {index} at {relativeY}", + () => Precision.AlmostEquals(drawableRuleset.Playfield.AllHitObjects.ElementAt(index).DrawPosition.Y, drawableRuleset.Playfield.HitObjectContainer.DrawHeight * relativeY)); + + private void setTime(double time) + { + AddStep($"set time = {time}", () => testClock.CurrentTime = time); + } + + /// + /// Creates an , containing 10 hitobjects and user-provided timing points. + /// The hitobjects are spaced milliseconds apart. + /// + /// The timing points to add to the beatmap. + /// The . + private IBeatmap createBeatmap(params TimingControlPoint[] timingControlPoints) + { + var beatmap = new Beatmap { BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo } }; + + beatmap.ControlPointInfo.TimingPoints.AddRange(timingControlPoints); + + for (int i = 0; i < 10; i++) + beatmap.HitObjects.Add(new HitObject { StartTime = i * time_range }); + + return beatmap; + } + + private void createTest(IBeatmap beatmap, Action overrideAction = null) => AddStep("create test", () => + { + var ruleset = new TestScrollingRuleset(); + + drawableRuleset = (TestDrawableScrollingRuleset)ruleset.CreateDrawableRulesetWith(CreateWorkingBeatmap(beatmap), Array.Empty()); + drawableRuleset.FrameStablePlayback = false; + + overrideAction?.Invoke(drawableRuleset); + + Child = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Height = 0.75f, + Width = 400, + Masking = true, + Clock = new FramedClock(testClock), + Child = drawableRuleset + }; + }); + + #region Ruleset + + private class TestScrollingRuleset : Ruleset + { + public TestScrollingRuleset(RulesetInfo rulesetInfo = null) + : base(rulesetInfo) + { + } + + public override IEnumerable GetModsFor(ModType type) => throw new NotImplementedException(); + + public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods) => new TestDrawableScrollingRuleset(this, beatmap, mods); + + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TestBeatmapConverter(beatmap); + + public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException(); + + public override string Description { get; } = string.Empty; + + public override string ShortName { get; } = string.Empty; + } + + private class TestDrawableScrollingRuleset : DrawableScrollingRuleset + { + public bool RelativeScaleBeatLengthsOverride { get; set; } + + protected override bool RelativeScaleBeatLengths => RelativeScaleBeatLengthsOverride; + + protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping; + + public TestDrawableScrollingRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) + : base(ruleset, beatmap, mods) + { + TimeRange.Value = time_range; + } + + public override DrawableHitObject CreateDrawableRepresentation(TestHitObject h) => new DrawableTestHitObject(h); + + protected override PassThroughInputManager CreateInputManager() => new PassThroughInputManager(); + + protected override Playfield CreatePlayfield() => new TestPlayfield(); + } + + private class TestPlayfield : ScrollingPlayfield + { + public TestPlayfield() + { + AddInternal(new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.2f, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 150 }, + Children = new Drawable[] + { + new Box + { + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + Height = 2, + Colour = Color4.Green + }, + HitObjectContainer + } + } + } + }); + } + } + + private class TestBeatmapConverter : BeatmapConverter + { + public TestBeatmapConverter(IBeatmap beatmap) + : base(beatmap) + { + } + + protected override IEnumerable ValidConversionTypes => new[] { typeof(HitObject) }; + + protected override IEnumerable ConvertHitObject(HitObject original, IBeatmap beatmap) + { + yield return new TestHitObject + { + StartTime = original.StartTime, + EndTime = (original as IHasEndTime)?.EndTime ?? (original.StartTime + 100) + }; + } + } + + #endregion + + #region HitObject + + private class TestHitObject : HitObject, IHasEndTime + { + public double EndTime { get; set; } + + public double Duration => EndTime - StartTime; + } + + private class DrawableTestHitObject : DrawableHitObject + { + public DrawableTestHitObject(TestHitObject hitObject) + : base(hitObject) + { + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; + + Size = new Vector2(100, 25); + + AddRangeInternal(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.LightPink + }, + new Box + { + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.X, + Height = 2, + Colour = Color4.Red + } + }); + } + } + + #endregion + } +} From 6596d7fc463d3a73cbf5605d1074135db9c230f8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Aug 2019 16:33:24 +0900 Subject: [PATCH 2360/2854] Add nullref safety to FrameStablePlayback boolean --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index eb14bd1f24..ccfd89adca 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -62,13 +62,20 @@ namespace osu.Game.Rulesets.UI public override GameplayClock FrameStableClock => frameStabilityContainer.GameplayClock; + private bool frameStablePlayback = true; + /// /// Whether to enable frame-stable playback. /// internal bool FrameStablePlayback { - get => frameStabilityContainer.FrameStablePlayback; - set => frameStabilityContainer.FrameStablePlayback = value; + get => frameStablePlayback; + set + { + frameStablePlayback = false; + if (frameStabilityContainer != null) + frameStabilityContainer.FrameStablePlayback = value; + } } /// @@ -156,6 +163,7 @@ namespace osu.Game.Rulesets.UI { frameStabilityContainer = new FrameStabilityContainer(GameplayStartTime) { + FrameStablePlayback = FrameStablePlayback, Child = KeyBindingInputManager .WithChild(CreatePlayfieldAdjustmentContainer() .WithChild(Playfield) From 6dfe95db74b6d12d1ed5b9567a0652eb6303d589 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Aug 2019 16:34:12 +0900 Subject: [PATCH 2361/2854] Allow RulesetConfigCache to return null configs --- osu.Game/Rulesets/RulesetConfigCache.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/RulesetConfigCache.cs b/osu.Game/Rulesets/RulesetConfigCache.cs index 9a5a4d4acd..8c9e3c94e2 100644 --- a/osu.Game/Rulesets/RulesetConfigCache.cs +++ b/osu.Game/Rulesets/RulesetConfigCache.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets public IRulesetConfigManager GetConfigFor(Ruleset ruleset) { if (ruleset.RulesetInfo.ID == null) - throw new InvalidOperationException("The provided ruleset doesn't have a valid id."); + return null; return configCache.GetOrAdd(ruleset.RulesetInfo.ID.Value, _ => ruleset.CreateConfig(settingsStore)); } From fb1cd9e5e7a9050a8b2af1882ecf342f4928baf8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Aug 2019 16:47:23 +0900 Subject: [PATCH 2362/2854] Add a sane default lifetime end for scrolling hitobjects --- .../UI/Scrolling/ScrollingHitObjectContainer.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 19247d8a37..14a4869a98 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -12,8 +12,13 @@ namespace osu.Game.Rulesets.UI.Scrolling { public class ScrollingHitObjectContainer : HitObjectContainer { - private readonly IBindable timeRange = new BindableDouble(); + /// + /// A multiplier applied to the length of the scrolling area to determine a safe default lifetime end for hitobjects. + /// This is only used to limit the lifetime end within reason, as proper lifetime management should be implemented on hitobjects themselves. + /// + private const float safe_lifetime_end_multiplier = 2; + private readonly IBindable timeRange = new BindableDouble(); private readonly IBindable direction = new Bindable(); [Resolved] @@ -92,6 +97,8 @@ namespace osu.Game.Rulesets.UI.Scrolling if (hitObject.HitObject is IHasEndTime endTime) { + hitObject.LifetimeEnd = scrollingInfo.Algorithm.TimeAt(scrollLength * safe_lifetime_end_multiplier, endTime.EndTime, timeRange.Value, scrollLength); + switch (direction.Value) { case ScrollingDirection.Up: @@ -105,6 +112,8 @@ namespace osu.Game.Rulesets.UI.Scrolling break; } } + else + hitObject.LifetimeEnd = scrollingInfo.Algorithm.TimeAt(scrollLength * safe_lifetime_end_multiplier, hitObject.HitObject.StartTime, timeRange.Value, scrollLength); foreach (var obj in hitObject.NestedHitObjects) { From d4a296f9116fc9ce35695e9f2c89bfebd3cd8041 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Aug 2019 17:45:12 +0900 Subject: [PATCH 2363/2854] Slight refactoring --- .../Replays/ManiaAutoGenerator.cs | 8 ++++---- .../Replays/TaikoAutoGenerator.cs | 14 +++++++++----- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index fd0a876775..7b8bbc2095 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -98,12 +98,12 @@ namespace osu.Game.Rulesets.Mania.Replays protected override HitObject GetNextObject(int currentIndex) { - int desiredColumn = Beatmap.HitObjects[currentIndex++].Column; + int desiredColumn = Beatmap.HitObjects[currentIndex].Column; - for (; currentIndex < Beatmap.HitObjects.Count; currentIndex++) + for (int i = currentIndex + 1; i < Beatmap.HitObjects.Count; i++) { - if (Beatmap.HitObjects[currentIndex].Column == desiredColumn) - return Beatmap.HitObjects[currentIndex]; + if (Beatmap.HitObjects[i].Column == desiredColumn) + return Beatmap.HitObjects[i]; } return null; diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index 6720d8b8bf..299679b2c1 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -137,14 +137,18 @@ namespace osu.Game.Rulesets.Taiko.Replays protected override HitObject GetNextObject(int currentIndex) { - Type desiredType = Beatmap.HitObjects[currentIndex++].GetType(); + Type desiredType = Beatmap.HitObjects[currentIndex].GetType(); - for (; currentIndex < Beatmap.HitObjects.Count; currentIndex++) + for (int i = currentIndex + 1; i < Beatmap.HitObjects.Count; i++) { - var currentObj = Beatmap.HitObjects[currentIndex]; + var currentObj = Beatmap.HitObjects[i]; + if (currentObj.GetType() == desiredType || - currentObj is DrumRoll || currentObj is Swell) // Unpress all keys before DrumRoll or Swell - return Beatmap.HitObjects[currentIndex]; + // Un-press all keys before a DrumRoll or Swell + currentObj is DrumRoll || currentObj is Swell) + { + return Beatmap.HitObjects[i]; + } } return null; From 62641c149d96d33ec56542764b3d2a8ddd7258cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Aug 2019 18:18:14 +0900 Subject: [PATCH 2364/2854] Fx component lookups being incorrect for non-databased legacy skins --- .../metrics-skin/approachcircle@2x.png | Bin 0 -> 13816 bytes osu.Game/Skinning/LegacySkin.cs | 10 ++++++---- 2 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/approachcircle@2x.png diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/approachcircle@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/approachcircle@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..72ef665478b758c364197aa79f238230e229b3c4 GIT binary patch literal 13816 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 X;|*6ltxe$d|7PiG>1$T1+eQB$HM;D+ literal 0 HcmV?d00001 diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 48310cf027..7bffe5d321 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -155,6 +155,11 @@ namespace osu.Game.Skinning // Spacing value was reverse-engineered from the ratio of the rendered sprite size in the visual inspector vs the actual texture size Spacing = new Vector2(-Configuration.HitCircleOverlap * 0.89f, 0) }; + + default: + string lastPiece = componentName.Split('/').Last(); + componentName = componentName.StartsWith("Gameplay/taiko/") ? "taiko-" + lastPiece : lastPiece; + break; } return getAnimation(componentName, animatable, looping); @@ -226,11 +231,8 @@ namespace osu.Game.Skinning { bool hasExtension = filename.Contains('.'); - string lastPiece = filename.Split('/').Last(); - var legacyName = filename.StartsWith("Gameplay/taiko/") ? "taiko-" + lastPiece : lastPiece; - var file = source.Files.Find(f => - string.Equals(hasExtension ? f.Filename : Path.ChangeExtension(f.Filename, null), legacyName, StringComparison.InvariantCultureIgnoreCase)); + string.Equals(hasExtension ? f.Filename : Path.ChangeExtension(f.Filename, null), filename, StringComparison.InvariantCultureIgnoreCase)); return file?.FileInfo.StoragePath; } From 0422c19c2fc524d03f1d159f780f01ae6457365a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Aug 2019 19:06:23 +0900 Subject: [PATCH 2365/2854] Group lifetime setters together --- .../UI/Scrolling/ScrollingHitObjectContainer.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 14a4869a98..1df8c8218f 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -93,27 +93,28 @@ namespace osu.Game.Rulesets.UI.Scrolling private void computeInitialStateRecursive(DrawableHitObject hitObject) { - hitObject.LifetimeStart = scrollingInfo.Algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, timeRange.Value); + double endTime = hitObject.HitObject.StartTime; - if (hitObject.HitObject is IHasEndTime endTime) + if (hitObject.HitObject is IHasEndTime e) { - hitObject.LifetimeEnd = scrollingInfo.Algorithm.TimeAt(scrollLength * safe_lifetime_end_multiplier, endTime.EndTime, timeRange.Value, scrollLength); + endTime = e.EndTime; switch (direction.Value) { case ScrollingDirection.Up: case ScrollingDirection.Down: - hitObject.Height = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, endTime.EndTime, timeRange.Value, scrollLength); + hitObject.Height = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, endTime, timeRange.Value, scrollLength); break; case ScrollingDirection.Left: case ScrollingDirection.Right: - hitObject.Width = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, endTime.EndTime, timeRange.Value, scrollLength); + hitObject.Width = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, endTime, timeRange.Value, scrollLength); break; } } - else - hitObject.LifetimeEnd = scrollingInfo.Algorithm.TimeAt(scrollLength * safe_lifetime_end_multiplier, hitObject.HitObject.StartTime, timeRange.Value, scrollLength); + + hitObject.LifetimeStart = scrollingInfo.Algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, timeRange.Value); + hitObject.LifetimeEnd = scrollingInfo.Algorithm.TimeAt(scrollLength * safe_lifetime_end_multiplier, endTime, timeRange.Value, scrollLength); foreach (var obj in hitObject.NestedHitObjects) { From fd0f42eee4ffd88e1b65e69e8187d906b596b25e Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 26 Aug 2019 14:11:24 +0300 Subject: [PATCH 2366/2854] Fix filtered grouped difficulty items in DrawableCarouselBeatmapSet aren't hidden on first load --- .../Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 0a8c61e3d2..137fce7acf 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -223,11 +223,9 @@ namespace osu.Game.Screens.Select.Carousel public FilterableGroupedDifficultyIcon(List items, RulesetInfo ruleset) : base(items.Select(i => i.Beatmap).ToList(), ruleset, Color4.White) { - items.ForEach(item => item.Filtered.ValueChanged += _ => - { - // for now, fade the whole group based on the ratio of hidden items. - this.FadeTo(1 - 0.9f * ((float)items.Count(i => i.Filtered.Value) / items.Count), 100); - }); + // for now, fade the whole group based on the ratio of hidden items. + items.ForEach(item => item.Filtered.BindValueChanged(_ + => this.FadeTo(1 - 0.9f * ((float)items.Count(i => i.Filtered.Value) / items.Count), 100), true)); } } } From 9321f208841a9b66a8f7a1441f9812a5f7d4c157 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 26 Aug 2019 14:32:27 +0300 Subject: [PATCH 2367/2854] Move arrow to the first line --- .../Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 137fce7acf..5ef20bd0f2 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -224,8 +224,8 @@ namespace osu.Game.Screens.Select.Carousel : base(items.Select(i => i.Beatmap).ToList(), ruleset, Color4.White) { // for now, fade the whole group based on the ratio of hidden items. - items.ForEach(item => item.Filtered.BindValueChanged(_ - => this.FadeTo(1 - 0.9f * ((float)items.Count(i => i.Filtered.Value) / items.Count), 100), true)); + items.ForEach(item => item.Filtered.BindValueChanged(_ => + this.FadeTo(1 - 0.9f * ((float)items.Count(i => i.Filtered.Value) / items.Count), 100), true)); } } } From fb69755869775abfbbb7ae79c4ca8087696c6aa8 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 26 Aug 2019 15:15:23 +0300 Subject: [PATCH 2368/2854] Use transform management for catch hit objects --- .../Objects/Drawable/DrawableCatchHitObject.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs index a1279e8443..f4218061d4 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -58,14 +58,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss); } - protected override bool UseTransformStateManagement => false; + protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt; - protected override void UpdateState(ArmedState state) + protected override void UpdateInitialTransforms() => this.FadeIn(200); + + protected override void UpdateStateTransforms(ArmedState state) { - // TODO: update to use new state management. - using (BeginAbsoluteSequence(HitObject.StartTime - HitObject.TimePreempt)) - this.FadeIn(200); - var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; using (BeginAbsoluteSequence(endTime, true)) From 3f93780306769c6a6bee07c57065052178f41bef Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 26 Aug 2019 15:15:48 +0300 Subject: [PATCH 2369/2854] Override transform update functions in bar line --- .../Objects/Drawables/DrawableBarLine.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs index 9c3197504f..dee7bddcb3 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs @@ -69,7 +69,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Alpha = 0.2f; } - protected override void UpdateState(ArmedState state) + protected override void UpdateInitialTransforms() + { + } + + protected override void UpdateStateTransforms(ArmedState state) { } } From 62edfe7327672f2b56de7bfaf797555a3676927f Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 26 Aug 2019 15:16:01 +0300 Subject: [PATCH 2370/2854] Correct comment --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 62abe53559..ceda643335 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.UI if (lastPlateableFruit == null) return; - // this is required to make this run after the last caught fruit runs UpdateState at least once. + // this is required to make this run after the last caught fruit runs updateState() at least once. // TODO: find a better alternative if (lastPlateableFruit.IsLoaded) action(); From 24e7146a976297fabe3d56093cda75d16e412a4c Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 26 Aug 2019 15:26:19 +0300 Subject: [PATCH 2371/2854] Revert unintended change --- .../Objects/Drawables/DrawableBarLine.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs index dee7bddcb3..9c3197504f 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs @@ -69,11 +69,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Alpha = 0.2f; } - protected override void UpdateInitialTransforms() - { - } - - protected override void UpdateStateTransforms(ArmedState state) + protected override void UpdateState(ArmedState state) { } } From c11f5084004520256a56663d7fdb5d7c3e80869b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 26 Aug 2019 22:06:30 +0300 Subject: [PATCH 2372/2854] Use transform management for mania hit objects --- .../Objects/Drawables/DrawableBarLine.cs | 2 +- .../Drawables/DrawableManiaHitObject.cs | 29 +++++++++---------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs index 9c3197504f..e9c352c97e 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Alpha = 0.2f; } - protected override void UpdateState(ArmedState state) + protected override void UpdateStateTransforms(ArmedState state) { } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index db6b53e76d..e5b114ca81 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -45,24 +45,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { Anchor = Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre; } - } - public abstract class DrawableManiaHitObject : DrawableManiaHitObject - where TObject : ManiaHitObject - { - public new readonly TObject HitObject; - - protected DrawableManiaHitObject(TObject hitObject) - : base(hitObject) + protected override void UpdateStateTransforms(ArmedState state) { - HitObject = hitObject; - } - - protected override bool UseTransformStateManagement => false; - - protected override void UpdateState(ArmedState state) - { - // TODO: update to use new state management. switch (state) { case ArmedState.Miss: @@ -75,4 +60,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables } } } + + public abstract class DrawableManiaHitObject : DrawableManiaHitObject + where TObject : ManiaHitObject + { + public new readonly TObject HitObject; + + protected DrawableManiaHitObject(TObject hitObject) + : base(hitObject) + { + HitObject = hitObject; + } + } } From 843da26dbafc7c1605981bcf15f9c3006d5b5361 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 27 Aug 2019 05:03:56 +0300 Subject: [PATCH 2373/2854] Use transform management for taiko hit objects --- .../Objects/Drawables/DrawableDrumRoll.cs | 4 +- .../Objects/Drawables/DrawableDrumRollTick.cs | 2 +- .../Objects/Drawables/DrawableHit.cs | 64 ++++++++----------- .../Objects/Drawables/DrawableSwell.cs | 21 +++--- .../Drawables/DrawableTaikoHitObject.cs | 2 - 5 files changed, 41 insertions(+), 52 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 9b4df74a61..f4407a7b54 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -88,13 +88,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ApplyResult(r => r.Type = HitResult.Miss); } - protected override void UpdateState(ArmedState state) + protected override void UpdateStateTransforms(ArmedState state) { switch (state) { case ArmedState.Hit: case ArmedState.Miss: - this.FadeOut(100).Expire(); + this.Delay(HitObject.Duration).FadeOut(100).Expire(); break; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index 9259c693d9..cef9a53deb 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ApplyResult(r => r.Type = HitResult.Great); } - protected override void UpdateState(ArmedState state) + protected override void UpdateStateTransforms(ArmedState state) { switch (state) { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 34ae7db984..fa45067210 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -92,56 +92,42 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Size = BaseSize * Parent.RelativeChildSize; } - protected override void UpdateState(ArmedState state) + protected override void UpdateStateTransforms(ArmedState state) { - // TODO: update to use new state management. - var circlePiece = MainPiece as CirclePiece; - circlePiece?.FlashBox.FinishTransforms(); - - var offset = !AllJudged ? 0 : Time.Current - HitObject.StartTime; - - using (BeginDelayedSequence(HitObject.StartTime - Time.Current + offset, true)) + switch (state) { - switch (State.Value) - { - case ArmedState.Idle: - validActionPressed = false; + case ArmedState.Idle: + validActionPressed = false; - UnproxyContent(); - this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire(); - break; + UnproxyContent(); + this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire(); + break; - case ArmedState.Miss: - this.FadeOut(100) - .Expire(); - break; + case ArmedState.Miss: + this.FadeOut(100) + .Expire(); + break; - case ArmedState.Hit: - // If we're far enough away from the left stage, we should bring outselves in front of it - ProxyContent(); + case ArmedState.Hit: + // If we're far enough away from the left stage, we should bring outselves in front of it + ProxyContent(); - var flash = circlePiece?.FlashBox; + var flash = (MainPiece as CirclePiece)?.FlashBox; + flash?.FadeTo(0.9f).FadeOut(300); - if (flash != null) - { - flash.FadeTo(0.9f); - flash.FadeOut(300); - } + const float gravity_time = 300; + const float gravity_travel_height = 200; - const float gravity_time = 300; - const float gravity_travel_height = 200; + this.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad); - this.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad); + this.MoveToY(-gravity_travel_height, gravity_time, Easing.Out) + .Then() + .MoveToY(gravity_travel_height * 2, gravity_time * 2, Easing.In); - this.MoveToY(-gravity_travel_height, gravity_time, Easing.Out) - .Then() - .MoveToY(gravity_travel_height * 2, gravity_time * 2, Easing.In); + this.FadeOut(800) + .Expire(); - this.FadeOut(800) - .Expire(); - - break; - } + break; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 82448ec7d5..88769bfff6 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -179,26 +179,31 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } - protected override void UpdateState(ArmedState state) + protected override double InitialLifetimeOffset => 100; + + protected override void UpdateInitialTransforms() => targetRing.ScaleTo(target_ring_scale, InitialLifetimeOffset * 4, Easing.OutQuint); + + protected override void UpdateStateTransforms(ArmedState state) { - const float preempt = 100; - const float out_transition_time = 300; + const double transition_duration = 300; switch (state) { case ArmedState.Idle: UnproxyContent(); expandingRing.FadeTo(0); - using (BeginAbsoluteSequence(HitObject.StartTime - preempt, true)) - targetRing.ScaleTo(target_ring_scale, preempt * 4, Easing.OutQuint); break; case ArmedState.Miss: case ArmedState.Hit: - this.FadeOut(out_transition_time, Easing.Out); - bodyContainer.ScaleTo(1.4f, out_transition_time); + using (BeginAbsoluteSequence(Time.Current, true)) + { + this.FadeOut(transition_duration, Easing.Out); + bodyContainer.ScaleTo(1.4f, transition_duration); + + Expire(); + } - Expire(); break; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index b46738c69a..bd45b52d7b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -121,8 +121,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } - protected override bool UseTransformStateManagement => false; - // Normal and clap samples are handled by the drum protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP); From cd68d030bd186fe3e9ef98489d876f258b76a4e1 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 27 Aug 2019 05:04:34 +0300 Subject: [PATCH 2374/2854] Remove unnecessary UpdateState overrides --- osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs | 4 ---- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs | 4 ---- .../Objects/Drawables/DrawableStrongNestedHit.cs | 4 ---- .../Objects/Drawables/DrawableSwellTick.cs | 4 ---- 4 files changed, 16 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs index 6f9856df83..3c84d900a6 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs @@ -247,10 +247,6 @@ namespace osu.Game.Rulesets.Taiko.Tests : base(hitObject) { } - - protected override void UpdateState(ArmedState state) - { - } } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs index f8909fb98c..bf89f7e15b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs @@ -53,9 +53,5 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Alpha = 0.75f }); } - - protected override void UpdateState(ArmedState state) - { - } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs index 98a2e8a721..108e42eea5 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs @@ -18,9 +18,5 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { MainObject = mainObject; } - - protected override void UpdateState(ArmedState state) - { - } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs index 41a8fd9a75..fb80a9a5b4 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs @@ -21,10 +21,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { } - protected override void UpdateState(ArmedState state) - { - } - public override bool OnPressed(TaikoAction action) => false; } } From 0eef398ca74ebf8d860ae81b86eab3ee0f0d9636 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 27 Aug 2019 05:19:21 +0300 Subject: [PATCH 2375/2854] Remove redundant using directive --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs index fb80a9a5b4..8b27d78101 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.Objects.Drawables From 6368189d4660c188ae2360c856268d029ea5de91 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 27 Aug 2019 11:59:25 +0900 Subject: [PATCH 2376/2854] Refactor --- .../Select/Carousel/DrawableCarouselBeatmapSet.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 5ef20bd0f2..97b6a78804 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -220,12 +220,23 @@ namespace osu.Game.Screens.Select.Carousel public class FilterableGroupedDifficultyIcon : GroupedDifficultyIcon { + private readonly List items; + public FilterableGroupedDifficultyIcon(List items, RulesetInfo ruleset) : base(items.Select(i => i.Beatmap).ToList(), ruleset, Color4.White) + { + this.items = items; + + foreach (var item in items) + item.Filtered.BindValueChanged(_ => Scheduler.AddOnce(updateFilteredDisplay)); + + updateFilteredDisplay(); + } + + private void updateFilteredDisplay() { // for now, fade the whole group based on the ratio of hidden items. - items.ForEach(item => item.Filtered.BindValueChanged(_ => - this.FadeTo(1 - 0.9f * ((float)items.Count(i => i.Filtered.Value) / items.Count), 100), true)); + this.FadeTo(1 - 0.9f * ((float)items.Count(i => i.Filtered.Value) / items.Count), 100); } } } From 6014a6680411e2d553a8b09ef2beb86d2f0ee928 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 27 Aug 2019 12:22:31 +0900 Subject: [PATCH 2377/2854] Add initial fade transform to fix rewind --- .../Objects/Drawables/DrawableManiaHitObject.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index e5b114ca81..ce1484d460 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -46,6 +46,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Anchor = Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre; } + protected override void UpdateInitialTransforms() => this.FadeIn(); + protected override void UpdateStateTransforms(ArmedState state) { switch (state) From fff2da728926758c7ce6502ab0558b888289f979 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 27 Aug 2019 12:59:57 +0900 Subject: [PATCH 2378/2854] Fix hold notes disappearing instantaneously --- .../Objects/Drawables/DrawableHoldNote.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 952c6e128e..fc3b6885d7 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI.Scrolling; @@ -104,6 +105,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2; } + protected override void UpdateStateTransforms(ArmedState state) + { + using (BeginDelayedSequence(HitObject.Duration, true)) + base.UpdateStateTransforms(state); + } + protected void BeginHold() { holdStartTime = Time.Current; From 7885c79467aa732110d1cc6be57daf5a7521336a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Aug 2019 14:31:34 +0900 Subject: [PATCH 2379/2854] Make bindables private --- .../SongSelect/TestScenePlaySongSelect.cs | 21 ++++++++++------ osu.Game/Screens/Select/FilterControl.cs | 24 +++++++++---------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 2dbe53709b..263eada07c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -15,6 +15,7 @@ using osu.Framework.MathUtils; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; @@ -79,8 +80,12 @@ namespace osu.Game.Tests.Visual.SongSelect Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default)); Beatmap.SetDefault(); + + Dependencies.Cache(config = new OsuConfigManager(LocalStorage)); } + private OsuConfigManager config; + [SetUp] public virtual void SetUp() => Schedule(() => { @@ -111,13 +116,15 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap); - AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.SortMode.Value = SortMode.Artist; }); - AddStep(@"Sort by Title", delegate { songSelect.FilterControl.SortMode.Value = SortMode.Title; }); - AddStep(@"Sort by Author", delegate { songSelect.FilterControl.SortMode.Value = SortMode.Author; }); - AddStep(@"Sort by DateAdded", delegate { songSelect.FilterControl.SortMode.Value = SortMode.DateAdded; }); - AddStep(@"Sort by BPM", delegate { songSelect.FilterControl.SortMode.Value = SortMode.BPM; }); - AddStep(@"Sort by Length", delegate { songSelect.FilterControl.SortMode.Value = SortMode.Length; }); - AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.SortMode.Value = SortMode.Difficulty; }); + var sortMode = config.GetBindable(OsuSetting.SongSelectSortingMode); + + AddStep(@"Sort by Artist", delegate { sortMode.Value = SortMode.Artist; }); + AddStep(@"Sort by Title", delegate { sortMode.Value = SortMode.Title; }); + AddStep(@"Sort by Author", delegate { sortMode.Value = SortMode.Author; }); + AddStep(@"Sort by DateAdded", delegate { sortMode.Value = SortMode.DateAdded; }); + AddStep(@"Sort by BPM", delegate { sortMode.Value = SortMode.BPM; }); + AddStep(@"Sort by Length", delegate { sortMode.Value = SortMode.Length; }); + AddStep(@"Sort by Difficulty", delegate { sortMode.Value = SortMode.Difficulty; }); } [Test] diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 06b6cccf96..ed74b01fc9 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -29,14 +29,14 @@ namespace osu.Game.Screens.Select private readonly TabControl groupTabs; - public Bindable SortMode; + private Bindable sortMode; - public Bindable GroupMode; + private Bindable groupMode; public FilterCriteria CreateCriteria() => new FilterCriteria { - Group = GroupMode.Value, - Sort = SortMode.Value, + Group = groupMode.Value, + Sort = sortMode.Value, SearchText = searchTextBox.Text, AllowConvertedBeatmaps = showConverted.Value, Ruleset = ruleset.Value @@ -123,8 +123,8 @@ namespace osu.Game.Screens.Select searchTextBox.Current.ValueChanged += _ => FilterChanged?.Invoke(CreateCriteria()); - groupTabs.PinItem(Filter.GroupMode.All); - groupTabs.PinItem(Filter.GroupMode.RecentlyPlayed); + groupTabs.PinItem(GroupMode.All); + groupTabs.PinItem(GroupMode.RecentlyPlayed); } public void Deactivate() @@ -156,14 +156,14 @@ namespace osu.Game.Screens.Select ruleset.BindTo(parentRuleset); ruleset.BindValueChanged(_ => updateCriteria()); - SortMode = config.GetBindable(OsuSetting.SongSelectSortingMode); - GroupMode = config.GetBindable(OsuSetting.SongSelectGroupingMode); + sortMode = config.GetBindable(OsuSetting.SongSelectSortingMode); + groupMode = config.GetBindable(OsuSetting.SongSelectGroupingMode); - sortTabs.Current.BindTo(SortMode); - groupTabs.Current.BindTo(GroupMode); + sortTabs.Current.BindTo(sortMode); + groupTabs.Current.BindTo(groupMode); - GroupMode.BindValueChanged(_ => updateCriteria()); - SortMode.BindValueChanged(_ => updateCriteria()); + groupMode.BindValueChanged(_ => updateCriteria()); + sortMode.BindValueChanged(_ => updateCriteria()); updateCriteria(); } From b85e62a6e2da4f861788a67da8ae64495f30038b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 27 Aug 2019 14:19:55 +0900 Subject: [PATCH 2380/2854] Fix taiko proxies being removed on rewind --- .../Drawables/DrawableTaikoHitObject.cs | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index b46738c69a..ea3ea4a9ad 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -78,10 +78,29 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public abstract bool OnPressed(TaikoAction action); public virtual bool OnReleased(TaikoAction action) => false; + public override double LifetimeStart + { + get => base.LifetimeStart; + set + { + base.LifetimeStart = value; + proxiedContent.LifetimeStart = value; + } + } + + public override double LifetimeEnd + { + get => base.LifetimeEnd; + set + { + base.LifetimeEnd = value; + proxiedContent.LifetimeEnd = value; + } + } + private class ProxiedContentContainer : Container { - public override double LifetimeStart => Parent?.LifetimeStart ?? base.LifetimeStart; - public override double LifetimeEnd => Parent?.LifetimeEnd ?? base.LifetimeEnd; + public override bool RemoveWhenNotAlive => false; } } From dba367981759de0db52b7b54bfc2046bfc15a154 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 27 Aug 2019 15:19:16 +0900 Subject: [PATCH 2381/2854] Fix DrawableSwell not proxying its content in time/correctly --- .../Objects/Drawables/DrawableSwell.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 88769bfff6..7d5b928b4f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -179,9 +179,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } - protected override double InitialLifetimeOffset => 100; + protected override void UpdateInitialTransforms() + { + base.UpdateInitialTransforms(); - protected override void UpdateInitialTransforms() => targetRing.ScaleTo(target_ring_scale, InitialLifetimeOffset * 4, Easing.OutQuint); + using (BeginAbsoluteSequence(HitObject.StartTime - 100, true)) + targetRing.ScaleTo(target_ring_scale, 400, Easing.OutQuint); + } protected override void UpdateStateTransforms(ArmedState state) { @@ -190,7 +194,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables switch (state) { case ArmedState.Idle: - UnproxyContent(); expandingRing.FadeTo(0); break; @@ -217,9 +220,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables // Make the swell stop at the hit target X = Math.Max(0, X); - double t = Math.Min(HitObject.StartTime, Time.Current); - if (t == HitObject.StartTime) + if (Time.Current >= HitObject.StartTime - 100) ProxyContent(); + else + UnproxyContent(); } private bool? lastWasCentre; From bc7a81e733c21adb600120065e78eff7f65ac5da Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 27 Aug 2019 15:19:29 +0900 Subject: [PATCH 2382/2854] Add initial fade transform --- .../Objects/Drawables/DrawableTaikoHitObject.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index bd45b52d7b..a0a71a4c42 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -78,6 +78,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public abstract bool OnPressed(TaikoAction action); public virtual bool OnReleased(TaikoAction action) => false; + protected override void UpdateInitialTransforms() => this.FadeIn(); + private class ProxiedContentContainer : Container { public override double LifetimeStart => Parent?.LifetimeStart ?? base.LifetimeStart; From 2b22fd799dd4f16393f746aa9ea39819025ac9c0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 27 Aug 2019 15:21:54 +0900 Subject: [PATCH 2383/2854] Use constant value for offset --- .../Objects/Drawables/DrawableSwell.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 7d5b928b4f..094ad1230f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -25,6 +25,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private const float target_ring_scale = 5f; private const float inner_ring_alpha = 0.65f; + /// + /// Offset away from the start time of the swell at which the ring starts appearing. + /// + private const double ring_appear_offset = 100; + private readonly List ticks = new List(); private readonly Container bodyContainer; @@ -183,7 +188,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { base.UpdateInitialTransforms(); - using (BeginAbsoluteSequence(HitObject.StartTime - 100, true)) + using (BeginAbsoluteSequence(HitObject.StartTime - ring_appear_offset, true)) targetRing.ScaleTo(target_ring_scale, 400, Easing.OutQuint); } @@ -220,7 +225,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables // Make the swell stop at the hit target X = Math.Max(0, X); - if (Time.Current >= HitObject.StartTime - 100) + if (Time.Current >= HitObject.StartTime - ring_appear_offset) ProxyContent(); else UnproxyContent(); From 9e926d44c0b68c86dadc031940a440ae86ccbabd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 27 Aug 2019 16:09:47 +0900 Subject: [PATCH 2384/2854] Obsolete legacy UpdateState() method --- .../Visual/Gameplay/TestSceneScrollingHitObjects.cs | 8 -------- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 6 ++++++ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs index 0a9cdc6a8e..aa80819694 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs @@ -200,10 +200,6 @@ namespace osu.Game.Tests.Visual.Gameplay break; } } - - protected override void UpdateState(ArmedState state) - { - } } private class TestDrawableHitObject : DrawableHitObject @@ -216,10 +212,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddInternal(new Box { Size = new Vector2(75) }); } - - protected override void UpdateState(ArmedState state) - { - } } } } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index b72a55b9ed..80e70589bd 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -132,6 +132,8 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public event Action ApplyCustomUpdateState; +#pragma warning disable 618 // (legacy state management) - can be removed 20200227 + /// /// Enables automatic transform management of this hitobject. Implementation of transforms should be done in and only. Rewinding and removing previous states is done automatically. /// @@ -139,6 +141,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// Going forward, this is the preferred way of implementing s. Previous functionality /// is offered as a compatibility layer until all rulesets have been migrated across. /// + [Obsolete("Use UpdateInitialTransforms()/UpdateStateTransforms() instead")] // can be removed 20200227 protected virtual bool UseTransformStateManagement => true; protected override void ClearInternal(bool disposeChildren = true) => throw new InvalidOperationException($"Should never clear a {nameof(DrawableHitObject)}"); @@ -219,10 +222,13 @@ namespace osu.Game.Rulesets.Objects.Drawables /// Should generally not be used when is true; use instead. /// /// The new armed state. + [Obsolete("Use UpdateInitialTransforms()/UpdateStateTransforms() instead")] // can be removed 20200227 protected virtual void UpdateState(ArmedState state) { } +#pragma warning restore 618 + #endregion protected override void SkinChanged(ISkinSource skin, bool allowFallback) From 0ea10a49223ab178cd4fb520ffd7fc2ad87c1464 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Aug 2019 17:18:32 +0900 Subject: [PATCH 2385/2854] Fix skin sample reading failing --- osu.Game/Skinning/LegacySkin.cs | 55 +++++++++++++++++---------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 7bffe5d321..37a3059160 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -155,16 +155,40 @@ namespace osu.Game.Skinning // Spacing value was reverse-engineered from the ratio of the rendered sprite size in the visual inspector vs the actual texture size Spacing = new Vector2(-Configuration.HitCircleOverlap * 0.89f, 0) }; - - default: - string lastPiece = componentName.Split('/').Last(); - componentName = componentName.StartsWith("Gameplay/taiko/") ? "taiko-" + lastPiece : lastPiece; - break; } return getAnimation(componentName, animatable, looping); } + public override Texture GetTexture(string componentName) + { + componentName = getFallbackName(componentName); + + float ratio = 2; + var texture = Textures.Get($"{componentName}@2x"); + + if (texture == null) + { + ratio = 1; + texture = Textures.Get(componentName); + } + + if (texture != null) + texture.ScaleAdjust = ratio; + + return texture; + } + + public override SampleChannel GetSample(string sampleName) => Samples.Get(getFallbackName(sampleName)); + + private bool hasFont(string fontName) => GetTexture($"{fontName}-0") != null; + + private string getFallbackName(string componentName) + { + string lastPiece = componentName.Split('/').Last(); + return componentName.StartsWith("Gameplay/taiko/") ? "taiko-" + lastPiece : lastPiece; + } + private Drawable getAnimation(string componentName, bool animatable, bool looping, string animationSeparator = "-") { Texture texture; @@ -200,27 +224,6 @@ namespace osu.Game.Skinning return null; } - public override Texture GetTexture(string componentName) - { - float ratio = 2; - var texture = Textures.Get($"{componentName}@2x"); - - if (texture == null) - { - ratio = 1; - texture = Textures.Get(componentName); - } - - if (texture != null) - texture.ScaleAdjust = ratio; - - return texture; - } - - public override SampleChannel GetSample(string sampleName) => Samples.Get(sampleName); - - private bool hasFont(string fontName) => GetTexture($"{fontName}-0") != null; - protected class LegacySkinResourceStore : IResourceStore where T : INamedFileInfo { From 7aeeb65ae7fe6df164155f31414997c8c64629ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Aug 2019 18:27:21 +0900 Subject: [PATCH 2386/2854] Tidy up Player's container loading logic Fixes drawable ruleset being loaded before skin sources are finished, by loading them as a separate operation (to avoid children being loaded first). --- .../SkinnableTestScene.cs | 3 +- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 1 - osu.Game/Screens/Play/Player.cs | 81 +++++++++++-------- osu.Game/Skinning/SkinProvidingContainer.cs | 2 + 4 files changed, 49 insertions(+), 38 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs b/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs index c3a64f8b54..29e5146ff1 100644 --- a/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs +++ b/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs @@ -45,12 +45,11 @@ namespace osu.Game.Rulesets.Osu.Tests private Drawable createProvider(Skin skin, Func creationFunction) { - var mainProvider = new SkinProvidingContainer(skin) { RelativeSizeAxes = Axes.Both }; + var mainProvider = new SkinProvidingContainer(skin); return mainProvider .WithChild(new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider)) { - RelativeSizeAxes = Axes.Both, Child = creationFunction() }); } diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 178183bd22..ea7eee8bb8 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -44,7 +44,6 @@ namespace osu.Game.Rulesets.Osu.UI // Todo: Remove when hitobjects are properly pooled new SkinProvidingContainer(null) { - RelativeSizeAxes = Axes.Both, Child = HitObjectContainer, }, approachCircles = new ApproachCircleProxyContainer diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index deed17a049..b487f3e61b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -123,30 +123,53 @@ namespace osu.Game.Screens.Play InternalChild = GameplayClockContainer = new GameplayClockContainer(working, Mods.Value, DrawableRuleset.GameplayStartTime); - SkinProvidingContainer skinProvidingContainer = new BeatmapSkinProvidingContainer(working.Skin) - { - RelativeSizeAxes = Axes.Both, - }; + addUnderlayComponents(GameplayClockContainer); + addGameplayComponents(GameplayClockContainer, working); + addOverlayComponents(GameplayClockContainer, working); - GameplayClockContainer.Children = new[] + DrawableRuleset.HasReplayLoaded.BindValueChanged(e => HUDOverlay.HoldToQuit.PauseOnFocusLost = !e.NewValue && PauseOnFocusLost, true); + + // bind clock into components that require it + DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused); + + // Bind ScoreProcessor to ourselves + ScoreProcessor.AllJudged += onCompletion; + ScoreProcessor.Failed += onFail; + + foreach (var mod in Mods.Value.OfType()) + mod.ApplyToScoreProcessor(ScoreProcessor); + } + + private void addUnderlayComponents(Container target) + { + target.Add(DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both }); + } + + private void addGameplayComponents(Container target, WorkingBeatmap working) + { + var beatmapSkinProvider = new BeatmapSkinProvidingContainer(working.Skin); + + // the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation + // full access to all skin sources. + var rulesetSkinProvider = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider)); + + // load the skinning hierarchy first. + // this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources. + target.Add(new ScalingContainer(ScalingMode.Gameplay) + .WithChild(beatmapSkinProvider + .WithChild(target = rulesetSkinProvider))); + + target.AddRange(new Drawable[] + { + DrawableRuleset, + new ComboEffects(ScoreProcessor) + }); + } + + private void addOverlayComponents(Container target, WorkingBeatmap working) + { + target.AddRange(new[] { - DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both }, - new ScalingContainer(ScalingMode.Gameplay) - { - Child = skinProvidingContainer.WithChild( - // the skinProvidingContainer is used as the fallback source here to allow the ruleset-specific skin implementation - // full access to all skin sources. - new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(skinProvidingContainer)) - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - DrawableRuleset, - new ComboEffects(ScoreProcessor) - } - } - ) - }, breakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) { Anchor = Anchor.Centre, @@ -205,19 +228,7 @@ namespace osu.Game.Screens.Play }, }, failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, } - }; - - DrawableRuleset.HasReplayLoaded.BindValueChanged(e => HUDOverlay.HoldToQuit.PauseOnFocusLost = !e.NewValue && PauseOnFocusLost, true); - - // bind clock into components that require it - DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused); - - // Bind ScoreProcessor to ourselves - ScoreProcessor.AllJudged += onCompletion; - ScoreProcessor.Failed += onFail; - - foreach (var mod in Mods.Value.OfType()) - mod.ApplyToScoreProcessor(ScoreProcessor); + }); } private WorkingBeatmap loadBeatmap() diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index a92a0ae0d1..9d6fbfdc74 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -32,6 +32,8 @@ namespace osu.Game.Skinning public SkinProvidingContainer(ISkin skin) { this.skin = skin; + + RelativeSizeAxes = Axes.Both; } public Drawable GetDrawableComponent(string componentName) From b59973c712a6213b103ad2cb4ecda79b267d610e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Aug 2019 18:42:49 +0900 Subject: [PATCH 2387/2854] Update in line with framework changes --- osu.Game/Graphics/ScreenshotManager.cs | 2 +- osu.Game/Input/Bindings/GlobalActionContainer.cs | 2 +- osu.Game/Input/IdleTracker.cs | 2 +- osu.Game/Overlays/Volume/VolumeControlReceptor.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 524a4742c0..f532302de2 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -22,7 +22,7 @@ using SixLabors.ImageSharp; namespace osu.Game.Graphics { - public class ScreenshotManager : Container, IKeyBindingHandler, IHandleGlobalInput + public class ScreenshotManager : Container, IKeyBindingHandler, IHandleGlobalKeyboardInput { private readonly BindableBool cursorVisibility = new BindableBool(true); diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index b70072a222..bf758e21d9 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -10,7 +10,7 @@ using osu.Framework.Input.Bindings; namespace osu.Game.Input.Bindings { - public class GlobalActionContainer : DatabasedKeyBindingContainer, IHandleGlobalInput + public class GlobalActionContainer : DatabasedKeyBindingContainer, IHandleGlobalKeyboardInput { private readonly Drawable handler; diff --git a/osu.Game/Input/IdleTracker.cs b/osu.Game/Input/IdleTracker.cs index cbc446a126..39ccf9fe1c 100644 --- a/osu.Game/Input/IdleTracker.cs +++ b/osu.Game/Input/IdleTracker.cs @@ -12,7 +12,7 @@ namespace osu.Game.Input /// /// Track whether the end-user is in an idle state, based on their last interaction with the game. /// - public class IdleTracker : Component, IKeyBindingHandler, IHandleGlobalInput + public class IdleTracker : Component, IKeyBindingHandler, IHandleGlobalKeyboardInput { private readonly double timeToIdle; diff --git a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs index 26235fa280..9cd3aac2cb 100644 --- a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs +++ b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs @@ -9,7 +9,7 @@ using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Volume { - public class VolumeControlReceptor : Container, IScrollBindingHandler, IHandleGlobalInput + public class VolumeControlReceptor : Container, IScrollBindingHandler, IHandleGlobalKeyboardInput { public Func ActionRequested; public Func ScrollActionRequested; From f49b58c102e641ada62971df2413b8e83e1aefb6 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 27 Aug 2019 15:30:41 +0300 Subject: [PATCH 2388/2854] Simplify text building --- .../Kudosu/DrawableKudosuHistoryItem.cs | 46 ++++++------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index 3cc39f0e73..4dba07713f 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -55,83 +55,67 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu switch (historyItem.Action) { case KudosuAction.VoteGive: - linkFlowContainer.AddText(@"Received "); - addKudosuPart(); + addKudosuPart(@"Received"); addMainPart(@" from obtaining votes in modding post of "); - addPostPart(); break; case KudosuAction.Give: - linkFlowContainer.AddText(@"Received "); - addKudosuPart(); + addKudosuPart(@"Received"); addMainPart($@" from {userLinkTemplate()} for a post at "); - addPostPart(); break; case KudosuAction.Reset: addMainPart($@"Kudosu reset by {userLinkTemplate()} for the post "); - addPostPart(); break; case KudosuAction.VoteReset: - linkFlowContainer.AddText(@"Lost "); - addKudosuPart(); + addKudosuPart(@"Lost"); addMainPart(@" from losing votes in modding post of "); - addPostPart(); break; case KudosuAction.DenyKudosuReset: - linkFlowContainer.AddText(@"Denied "); - addKudosuPart(); + addKudosuPart(@"Denied"); addMainPart(@" from modding post "); - addPostPart(); break; case KudosuAction.Revoke: addMainPart($@"Denied kudosu by {userLinkTemplate()} for the post "); - addPostPart(); break; case KudosuAction.AllowKudosuGive: - linkFlowContainer.AddText(@"Received "); - addKudosuPart(); + addKudosuPart(@"Received"); addMainPart(@" from kudosu deny repeal of modding post "); - addPostPart(); break; case KudosuAction.DeleteReset: - linkFlowContainer.AddText(@"Lost "); - addKudosuPart(); + addKudosuPart(@"Lost"); addMainPart(@" from modding post deletion of "); - addPostPart(); break; case KudosuAction.RestoreGive: - linkFlowContainer.AddText(@"Received "); - addKudosuPart(); + addKudosuPart(@"Received"); addMainPart(@" from modding post restoration of "); - addPostPart(); break; case KudosuAction.RecalculateGive: - linkFlowContainer.AddText(@"Received "); - addKudosuPart(); + addKudosuPart(@"Received"); addMainPart(@" from votes recalculation in modding post of "); - addPostPart(); break; case KudosuAction.RecalculateReset: - linkFlowContainer.AddText(@"Lost "); - addKudosuPart(); + addKudosuPart(@"Lost"); addMainPart(@" from votes recalculation in modding post of "); - addPostPart(); break; } + + addPostPart(); } - private void addKudosuPart() + private void addKudosuPart(string prefix) { - linkFlowContainer.AddText($@"{historyItem.Amount} kudosu", t => + linkFlowContainer.AddText(prefix); + + linkFlowContainer.AddText($@" {historyItem.Amount} kudosu", t => { t.Font = t.Font.With(italics: true); t.Colour = colours.Blue; From ed827d514f42d2d774f9b592dfee85c962036b94 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 27 Aug 2019 15:36:08 +0300 Subject: [PATCH 2389/2854] Add comments --- osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs index d02f71c339..19ce11aa13 100644 --- a/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs +++ b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs @@ -15,6 +15,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty("amount")] private int amount { + //We can receive negative values. However "action" is enough to build needed items set => Amount = Math.Abs(value); } @@ -49,6 +50,7 @@ namespace osu.Game.Online.API.Requests.Responses { set { + //We will receive something like "foo.bar" or just "foo" string parsed = value.Contains(".") ? value.Split('.')[0].Pascalize() + value.Split('.')[1].Pascalize() : value.Pascalize(); Action = (KudosuAction)Enum.Parse(typeof(KudosuAction), parsed); From 22ee7db805c1be3876c6d625d6107e98d4425cbf Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 27 Aug 2019 15:47:37 +0300 Subject: [PATCH 2390/2854] Refactor PaginatedContainer to avoid code duplication --- .../Beatmaps/PaginatedBeatmapContainer.cs | 48 +++--------- .../PaginatedMostPlayedBeatmapContainer.cs | 41 ++-------- .../Profile/Sections/PaginatedContainer.cs | 76 +++++++++++++++---- .../Sections/Ranks/PaginatedScoreContainer.cs | 65 +++++----------- .../PaginatedRecentActivityContainer.cs | 39 ++-------- 5 files changed, 107 insertions(+), 162 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index 8a6b52b7ee..d0ba3e6ba5 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -1,21 +1,22 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; +using System.Collections.Generic; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Direct; using osu.Game.Users; using osuTK; namespace osu.Game.Overlays.Profile.Sections.Beatmaps { - public class PaginatedBeatmapContainer : PaginatedContainer + public class PaginatedBeatmapContainer : PaginatedContainer { private const float panel_padding = 10f; private readonly BeatmapSetType type; - private GetUserBeatmapsRequest request; public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, string header, string missing = "None... yet.") : base(user, header, missing) @@ -27,40 +28,13 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps ItemsContainer.Spacing = new Vector2(panel_padding); } - protected override void ShowMore() + protected override APIRequest> CreateRequest() => + new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); + + protected override Drawable CreateDrawableItem(APIBeatmapSet item) => new DirectGridPanel(item.ToBeatmapSet(Rulesets)) { - request = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); - request.Success += sets => Schedule(() => - { - MoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0); - MoreButton.IsLoading = false; - - if (!sets.Any() && VisiblePages == 1) - { - MissingText.Show(); - return; - } - - foreach (var s in sets) - { - if (!s.OnlineBeatmapSetID.HasValue) - continue; - - ItemsContainer.Add(new DirectGridPanel(s.ToBeatmapSet(Rulesets)) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }); - } - }); - - Api.Queue(request); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - request?.Cancel(); - } + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }; } } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index 23072f8d90..10aa31225f 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -1,19 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; +using System.Collections.Generic; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Users; namespace osu.Game.Overlays.Profile.Sections.Historical { - public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer + public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer { - private GetUserMostPlayedBeatmapsRequest request; - public PaginatedMostPlayedBeatmapContainer(Bindable user) : base(user, "Most Played Beatmaps", "No records. :(") { @@ -22,35 +22,10 @@ namespace osu.Game.Overlays.Profile.Sections.Historical ItemsContainer.Direction = FillDirection.Vertical; } - protected override void ShowMore() - { - request = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, ItemsPerPage); - request.Success += beatmaps => Schedule(() => - { - MoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0); - MoreButton.IsLoading = false; + protected override APIRequest> CreateRequest() => + new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, ItemsPerPage); - if (!beatmaps.Any() && VisiblePages == 1) - { - MissingText.Show(); - return; - } - - MissingText.Hide(); - - foreach (var beatmap in beatmaps) - { - ItemsContainer.Add(new DrawableMostPlayedBeatmap(beatmap.GetBeatmapInfo(Rulesets), beatmap.PlayCount)); - } - }); - - Api.Queue(request); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - request?.Cancel(); - } + protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap item) => + new DrawableMostPlayedBeatmap(item.GetBeatmapInfo(Rulesets), item.PlayCount); } } diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index b459afcb49..75601041e8 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -11,22 +11,27 @@ using osu.Game.Graphics.Sprites; using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Users; +using System.Collections.Generic; +using System.Linq; +using System.Threading; namespace osu.Game.Overlays.Profile.Sections { - public abstract class PaginatedContainer : FillFlowContainer + public abstract class PaginatedContainer : FillFlowContainer { - protected readonly FillFlowContainer ItemsContainer; - protected readonly ShowMoreButton MoreButton; - protected readonly OsuSpriteText MissingText; + private readonly ShowMoreButton moreButton; + private readonly OsuSpriteText missingText; + private APIRequest> retrievalRequest; + private CancellationTokenSource loadCancellation; + + [Resolved] + private IAPIProvider api { get; set; } protected int VisiblePages; protected int ItemsPerPage; protected readonly Bindable User = new Bindable(); - - protected IAPIProvider Api; - protected APIRequest RetrievalRequest; + protected readonly FillFlowContainer ItemsContainer; protected RulesetStore Rulesets; protected PaginatedContainer(Bindable user, string header, string missing) @@ -51,15 +56,15 @@ namespace osu.Game.Overlays.Profile.Sections RelativeSizeAxes = Axes.X, Spacing = new Vector2(0, 2), }, - MoreButton = new ShowMoreButton + moreButton = new ShowMoreButton { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Alpha = 0, Margin = new MarginPadding { Top = 10 }, - Action = ShowMore, + Action = showMore, }, - MissingText = new OsuSpriteText + missingText = new OsuSpriteText { Font = OsuFont.GetFont(size: 15), Text = missing, @@ -69,9 +74,8 @@ namespace osu.Game.Overlays.Profile.Sections } [BackgroundDependencyLoader] - private void load(IAPIProvider api, RulesetStore rulesets) + private void load(RulesetStore rulesets) { - Api = api; Rulesets = rulesets; User.ValueChanged += onUserChanged; @@ -80,13 +84,57 @@ namespace osu.Game.Overlays.Profile.Sections private void onUserChanged(ValueChangedEvent e) { + loadCancellation?.Cancel(); + retrievalRequest?.Cancel(); + VisiblePages = 0; ItemsContainer.Clear(); if (e.NewValue != null) - ShowMore(); + showMore(); } - protected abstract void ShowMore(); + private void showMore() + { + loadCancellation = new CancellationTokenSource(); + + retrievalRequest = CreateRequest(); + retrievalRequest.Success += UpdateItems; + + api.Queue(retrievalRequest); + } + + protected virtual void UpdateItems(List items) + { + Schedule(() => + { + if (!items.Any() && VisiblePages == 1) + { + moreButton.Hide(); + moreButton.IsLoading = false; + missingText.Show(); + return; + } + + LoadComponentsAsync(items.Select(CreateDrawableItem), drawables => + { + missingText.Hide(); + moreButton.FadeTo(items.Count == ItemsPerPage ? 1 : 0); + moreButton.IsLoading = false; + + ItemsContainer.AddRange(drawables); + }, loadCancellation.Token); + }); + } + + protected abstract APIRequest> CreateRequest(); + + protected abstract Drawable CreateDrawableItem(T item); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + retrievalRequest?.Cancel(); + } } } diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 4a9ac6e5c7..96291f5a32 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -5,18 +5,18 @@ using osu.Framework.Graphics.Containers; using osu.Game.Online.API.Requests; using osu.Game.Users; using System; -using System.Collections.Generic; -using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Online.API.Requests.Responses; +using System.Collections.Generic; +using osu.Game.Online.API; namespace osu.Game.Overlays.Profile.Sections.Ranks { - public class PaginatedScoreContainer : PaginatedContainer + public class PaginatedScoreContainer : PaginatedContainer { private readonly bool includeWeight; private readonly ScoreType type; - private GetUserScoresRequest request; public PaginatedScoreContainer(ScoreType type, Bindable user, string header, string missing, bool includeWeight = false) : base(user, header, missing) @@ -29,52 +29,27 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks ItemsContainer.Direction = FillDirection.Vertical; } - protected override void ShowMore() + protected override void UpdateItems(List items) { - request = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); - request.Success += scores => Schedule(() => - { - foreach (var s in scores) - s.Ruleset = Rulesets.GetRuleset(s.RulesetID); + foreach (var item in items) + item.Ruleset = Rulesets.GetRuleset(item.RulesetID); - if (!scores.Any() && VisiblePages == 1) - { - MoreButton.Hide(); - MoreButton.IsLoading = false; - MissingText.Show(); - return; - } - - IEnumerable drawableScores; - - switch (type) - { - default: - drawableScores = scores.Select(score => new DrawablePerformanceScore(score, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null)); - break; - - case ScoreType.Recent: - drawableScores = scores.Select(score => new DrawableTotalScore(score)); - break; - } - - LoadComponentsAsync(drawableScores, s => - { - MissingText.Hide(); - MoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0); - MoreButton.IsLoading = false; - - ItemsContainer.AddRange(s); - }); - }); - - Api.Queue(request); + base.UpdateItems(items); } - protected override void Dispose(bool isDisposing) + protected override APIRequest> CreateRequest() => + new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); + + protected override Drawable CreateDrawableItem(APILegacyScoreInfo item) { - base.Dispose(isDisposing); - request?.Cancel(); + switch (type) + { + default: + return new DrawablePerformanceScore(item, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null); + + case ScoreType.Recent: + return new DrawableTotalScore(item); + } } } } diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index f2a778a874..8cc762e3a7 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -4,51 +4,24 @@ using osu.Framework.Graphics; using osu.Game.Online.API.Requests; using osu.Game.Users; -using System.Linq; using osu.Framework.Bindables; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.API; +using System.Collections.Generic; namespace osu.Game.Overlays.Profile.Sections.Recent { - public class PaginatedRecentActivityContainer : PaginatedContainer + public class PaginatedRecentActivityContainer : PaginatedContainer { - private GetUserRecentActivitiesRequest request; - public PaginatedRecentActivityContainer(Bindable user, string header, string missing) : base(user, header, missing) { ItemsPerPage = 5; } - protected override void ShowMore() - { - request = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++, ItemsPerPage); - request.Success += activities => Schedule(() => - { - MoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0); - MoreButton.IsLoading = false; + protected override APIRequest> CreateRequest() => + new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++, ItemsPerPage); - if (!activities.Any() && VisiblePages == 1) - { - MissingText.Show(); - return; - } - - MissingText.Hide(); - - foreach (APIRecentActivity activity in activities) - { - ItemsContainer.Add(new DrawableRecentActivity(activity)); - } - }); - - Api.Queue(request); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - request?.Cancel(); - } + protected override Drawable CreateDrawableItem(APIRecentActivity item) => new DrawableRecentActivity(item); } } From 9a383eee1a802f85819504e31da54852816df45d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 27 Aug 2019 15:58:57 +0300 Subject: [PATCH 2391/2854] Add AllowCreate function --- .../Sections/Beatmaps/PaginatedBeatmapContainer.cs | 2 ++ osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index d0ba3e6ba5..0b424057ae 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -31,6 +31,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps protected override APIRequest> CreateRequest() => new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); + protected override bool AllowCreate(APIBeatmapSet item) => item.OnlineBeatmapSetID.HasValue; + protected override Drawable CreateDrawableItem(APIBeatmapSet item) => new DirectGridPanel(item.ToBeatmapSet(Rulesets)) { Anchor = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 75601041e8..adc1107b32 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -116,7 +116,7 @@ namespace osu.Game.Overlays.Profile.Sections return; } - LoadComponentsAsync(items.Select(CreateDrawableItem), drawables => + LoadComponentsAsync(items.Where(item => AllowCreate(item)).Select(CreateDrawableItem), drawables => { missingText.Hide(); moreButton.FadeTo(items.Count == ItemsPerPage ? 1 : 0); @@ -127,6 +127,13 @@ namespace osu.Game.Overlays.Profile.Sections }); } + /// + /// Used to check whether the item is suitable for drawable creation. + /// + /// An item to check + /// + protected virtual bool AllowCreate(T item) => true; + protected abstract APIRequest> CreateRequest(); protected abstract Drawable CreateDrawableItem(T item); From 5fd43d42f441c54787ba132012297f8e6de9e956 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 27 Aug 2019 16:09:37 +0300 Subject: [PATCH 2392/2854] CI fixes --- osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs | 2 +- .../Profile/Sections/Ranks/PaginatedScoreContainer.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index adc1107b32..a3a22d6248 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -116,7 +116,7 @@ namespace osu.Game.Overlays.Profile.Sections return; } - LoadComponentsAsync(items.Where(item => AllowCreate(item)).Select(CreateDrawableItem), drawables => + LoadComponentsAsync(items.Where(AllowCreate).Select(CreateDrawableItem), drawables => { missingText.Hide(); moreButton.FadeTo(items.Count == ItemsPerPage ? 1 : 0); diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 96291f5a32..5f023cfa4b 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -45,10 +45,10 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks switch (type) { default: - return new DrawablePerformanceScore(item, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null); + return new DrawablePerformanceScore(item, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null); case ScoreType.Recent: - return new DrawableTotalScore(item); + return new DrawableTotalScore(item); } } } From 4ef991b97309868a507a63097caa9de1611da9be Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 27 Aug 2019 19:42:17 +0300 Subject: [PATCH 2393/2854] Loop over lookup names on default-samples retrieval --- osu.Game/Skinning/SkinnableSound.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 23093d9bd9..0c20241c4c 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -45,10 +45,9 @@ namespace osu.Game.Skinning var ch = skin.GetSample(s); if (ch == null && allowFallback) - if (s is HitSampleInfo hsi) - ch = audio.Samples.Get(string.IsNullOrEmpty(hsi.Namespace) - ? $"Gameplay/{hsi.Namespace}/{hsi.Bank}-{hsi.Name}" - : $"Gameplay/{hsi.Bank}-{hsi.Name}"); + foreach (var lookup in s.LookupNames) + if ((ch = audio.Samples.Get($"Gameplay/{lookup}")) != null) + break; if (ch != null) ch.Volume.Value = s.Volume / 100.0; From 4add1727b7458b9e36584fafdb753793c41ee447 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Aug 2019 03:25:57 +0900 Subject: [PATCH 2394/2854] Fix hitsounds not updating immediately after switching skins --- osu.Game/Skinning/SkinnableSound.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 8e2b5cec98..aa1c596a51 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -37,7 +37,7 @@ namespace osu.Game.Skinning public void Play() => channels?.ForEach(c => c.Play()); - public override bool IsPresent => false; // We don't need to receive updates. + public override bool IsPresent => Scheduler.HasPendingTasks; protected override void SkinChanged(ISkinSource skin, bool allowFallback) { From 7cc92e2fad4cdfbeefd12d718d73e3cf0ad27af8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 28 Aug 2019 13:20:28 +0900 Subject: [PATCH 2395/2854] Make taiko proxy containers use LifetimeManagementContainer --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index e62dc45cab..a10f70a344 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -44,9 +44,8 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly JudgementContainer judgementContainer; internal readonly HitTarget HitTarget; - private readonly Container topLevelHitContainer; - - private readonly Container barlineContainer; + private readonly ProxyContainer topLevelHitContainer; + private readonly ProxyContainer barlineContainer; private readonly Container overlayBackgroundContainer; private readonly Container backgroundContainer; @@ -108,7 +107,7 @@ namespace osu.Game.Rulesets.Taiko.UI } } }, - barlineContainer = new Container + barlineContainer = new ProxyContainer { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = HIT_TARGET_OFFSET } @@ -183,7 +182,7 @@ namespace osu.Game.Rulesets.Taiko.UI } } }, - topLevelHitContainer = new Container + topLevelHitContainer = new ProxyContainer { Name = "Top level hit objects", RelativeSizeAxes = Axes.Both, @@ -256,5 +255,15 @@ namespace osu.Game.Rulesets.Taiko.UI break; } } + + private class ProxyContainer : LifetimeManagementContainer + { + public new MarginPadding Padding + { + set => base.Padding = value; + } + + public void Add(Drawable proxy) => AddInternal(proxy); + } } } From f18b5a3c02ff44dd78db54fc9eae2f2c4979fac0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Aug 2019 13:27:44 +0900 Subject: [PATCH 2396/2854] Remove "AllowCreate" function by instead handling nulls --- .../Sections/Beatmaps/PaginatedBeatmapContainer.cs | 14 +++++++------- .../Profile/Sections/PaginatedContainer.cs | 9 +-------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index 0b424057ae..fe6822440f 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -31,12 +31,12 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps protected override APIRequest> CreateRequest() => new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); - protected override bool AllowCreate(APIBeatmapSet item) => item.OnlineBeatmapSetID.HasValue; - - protected override Drawable CreateDrawableItem(APIBeatmapSet item) => new DirectGridPanel(item.ToBeatmapSet(Rulesets)) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }; + protected override Drawable CreateDrawableItem(APIBeatmapSet item) => !item.OnlineBeatmapSetID.HasValue + ? null + : new DirectGridPanel(item.ToBeatmapSet(Rulesets)) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }; } } diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index a3a22d6248..4a10946ec2 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -116,7 +116,7 @@ namespace osu.Game.Overlays.Profile.Sections return; } - LoadComponentsAsync(items.Where(AllowCreate).Select(CreateDrawableItem), drawables => + LoadComponentsAsync(items.Select(CreateDrawableItem).Where(d => d != null), drawables => { missingText.Hide(); moreButton.FadeTo(items.Count == ItemsPerPage ? 1 : 0); @@ -127,13 +127,6 @@ namespace osu.Game.Overlays.Profile.Sections }); } - /// - /// Used to check whether the item is suitable for drawable creation. - /// - /// An item to check - /// - protected virtual bool AllowCreate(T item) => true; - protected abstract APIRequest> CreateRequest(); protected abstract Drawable CreateDrawableItem(T item); From 27633c8dbd28b50a9f7796617d426c563d4dd5e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Aug 2019 13:28:21 +0900 Subject: [PATCH 2397/2854] T -> TModel --- .../Overlays/Profile/Sections/PaginatedContainer.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 4a10946ec2..b91f22d1e7 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -17,11 +17,11 @@ using System.Threading; namespace osu.Game.Overlays.Profile.Sections { - public abstract class PaginatedContainer : FillFlowContainer + public abstract class PaginatedContainer : FillFlowContainer { private readonly ShowMoreButton moreButton; private readonly OsuSpriteText missingText; - private APIRequest> retrievalRequest; + private APIRequest> retrievalRequest; private CancellationTokenSource loadCancellation; [Resolved] @@ -104,7 +104,7 @@ namespace osu.Game.Overlays.Profile.Sections api.Queue(retrievalRequest); } - protected virtual void UpdateItems(List items) + protected virtual void UpdateItems(List items) { Schedule(() => { @@ -127,9 +127,9 @@ namespace osu.Game.Overlays.Profile.Sections }); } - protected abstract APIRequest> CreateRequest(); + protected abstract APIRequest> CreateRequest(); - protected abstract Drawable CreateDrawableItem(T item); + protected abstract Drawable CreateDrawableItem(TModel item); protected override void Dispose(bool isDisposing) { From 3942c83c187034750131d527e5b839106e973160 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Aug 2019 13:28:57 +0900 Subject: [PATCH 2398/2854] Simplify schedule layout --- .../Profile/Sections/PaginatedContainer.cs | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index b91f22d1e7..e26f7f3601 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -104,28 +104,25 @@ namespace osu.Game.Overlays.Profile.Sections api.Queue(retrievalRequest); } - protected virtual void UpdateItems(List items) + protected virtual void UpdateItems(List items) => Schedule(() => { - Schedule(() => + if (!items.Any() && VisiblePages == 1) { - if (!items.Any() && VisiblePages == 1) - { - moreButton.Hide(); - moreButton.IsLoading = false; - missingText.Show(); - return; - } + moreButton.Hide(); + moreButton.IsLoading = false; + missingText.Show(); + return; + } - LoadComponentsAsync(items.Select(CreateDrawableItem).Where(d => d != null), drawables => - { - missingText.Hide(); - moreButton.FadeTo(items.Count == ItemsPerPage ? 1 : 0); - moreButton.IsLoading = false; + LoadComponentsAsync(items.Select(CreateDrawableItem).Where(d => d != null), drawables => + { + missingText.Hide(); + moreButton.FadeTo(items.Count == ItemsPerPage ? 1 : 0); + moreButton.IsLoading = false; - ItemsContainer.AddRange(drawables); - }, loadCancellation.Token); - }); - } + ItemsContainer.AddRange(drawables); + }, loadCancellation.Token); + }); protected abstract APIRequest> CreateRequest(); From bef44b8e58581a6e5d51871c8ef5acd13aca2502 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Aug 2019 13:31:12 +0900 Subject: [PATCH 2399/2854] item -> model --- .../Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs | 4 ++-- .../Historical/PaginatedMostPlayedBeatmapContainer.cs | 4 ++-- osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs | 2 +- .../Profile/Sections/Ranks/PaginatedScoreContainer.cs | 6 +++--- .../Sections/Recent/PaginatedRecentActivityContainer.cs | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index fe6822440f..919f8a2fa0 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -31,9 +31,9 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps protected override APIRequest> CreateRequest() => new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); - protected override Drawable CreateDrawableItem(APIBeatmapSet item) => !item.OnlineBeatmapSetID.HasValue + protected override Drawable CreateDrawableItem(APIBeatmapSet model) => !model.OnlineBeatmapSetID.HasValue ? null - : new DirectGridPanel(item.ToBeatmapSet(Rulesets)) + : new DirectGridPanel(model.ToBeatmapSet(Rulesets)) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index 10aa31225f..6e6d6272c7 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected override APIRequest> CreateRequest() => new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, ItemsPerPage); - protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap item) => - new DrawableMostPlayedBeatmap(item.GetBeatmapInfo(Rulesets), item.PlayCount); + protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap model) => + new DrawableMostPlayedBeatmap(model.GetBeatmapInfo(Rulesets), model.PlayCount); } } diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index e26f7f3601..bb221bd43a 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -126,7 +126,7 @@ namespace osu.Game.Overlays.Profile.Sections protected abstract APIRequest> CreateRequest(); - protected abstract Drawable CreateDrawableItem(TModel item); + protected abstract Drawable CreateDrawableItem(TModel model); protected override void Dispose(bool isDisposing) { diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 5f023cfa4b..853b9db0a7 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -40,15 +40,15 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks protected override APIRequest> CreateRequest() => new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); - protected override Drawable CreateDrawableItem(APILegacyScoreInfo item) + protected override Drawable CreateDrawableItem(APILegacyScoreInfo model) { switch (type) { default: - return new DrawablePerformanceScore(item, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null); + return new DrawablePerformanceScore(model, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null); case ScoreType.Recent: - return new DrawableTotalScore(item); + return new DrawableTotalScore(model); } } } diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index 8cc762e3a7..3f9d4dc93e 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -22,6 +22,6 @@ namespace osu.Game.Overlays.Profile.Sections.Recent protected override APIRequest> CreateRequest() => new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++, ItemsPerPage); - protected override Drawable CreateDrawableItem(APIRecentActivity item) => new DrawableRecentActivity(item); + protected override Drawable CreateDrawableItem(APIRecentActivity model) => new DrawableRecentActivity(model); } } From 70da25cfca5664965951c27bbd41600cb13898eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Aug 2019 17:03:54 +0900 Subject: [PATCH 2400/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 215a9a8090..0f6e32d664 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -61,6 +61,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4fe9119cef..d791909372 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 82301549d7..9fc472bf40 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -118,8 +118,8 @@ - - + + From bb22c2d6e48b8039b967bd7c7320f84e382af794 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Aug 2019 17:29:18 +0900 Subject: [PATCH 2401/2854] Tidy up text construction --- .../Kudosu/DrawableKudosuHistoryItem.cs | 116 ++++++++++-------- 1 file changed, 66 insertions(+), 50 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index 4dba07713f..fb7d597012 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -50,85 +50,101 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { date.Colour = colours.GreySeafoamLighter; - string userLinkTemplate() => $"[{historyItem.Giver?.Url} {historyItem.Giver?.Username}]"; + string prefix = getPrefix(historyItem); + var formattedSource = MessageFormatter.FormatText(getSource(historyItem)); + + if (!string.IsNullOrEmpty(prefix)) + { + linkFlowContainer.AddText(prefix); + linkFlowContainer.AddText($@" {historyItem.Amount} kudosu", t => + { + t.Font = t.Font.With(italics: true); + t.Colour = colours.Blue; + }); + } + + linkFlowContainer.AddLinks(formattedSource.Text + " ", formattedSource.Links); + linkFlowContainer.AddLink(historyItem.Post.Title, historyItem.Post.Url); + } + + private string getSource(APIKudosuHistory historyItem) + { + string userLink() => $"[{historyItem.Giver?.Url} {historyItem.Giver?.Username}]"; switch (historyItem.Action) { case KudosuAction.VoteGive: - addKudosuPart(@"Received"); - addMainPart(@" from obtaining votes in modding post of "); - break; + return @" from obtaining votes in modding post of"; case KudosuAction.Give: - addKudosuPart(@"Received"); - addMainPart($@" from {userLinkTemplate()} for a post at "); - break; + return $@" from {userLink()} for a post at"; case KudosuAction.Reset: - addMainPart($@"Kudosu reset by {userLinkTemplate()} for the post "); - break; + return $@"Kudosu reset by {userLink()} for the post"; case KudosuAction.VoteReset: - addKudosuPart(@"Lost"); - addMainPart(@" from losing votes in modding post of "); - break; + return @" from losing votes in modding post of"; case KudosuAction.DenyKudosuReset: - addKudosuPart(@"Denied"); - addMainPart(@" from modding post "); - break; + return @" from modding post"; case KudosuAction.Revoke: - addMainPart($@"Denied kudosu by {userLinkTemplate()} for the post "); - break; + return $@"Denied kudosu by {userLink()} for the post"; case KudosuAction.AllowKudosuGive: - addKudosuPart(@"Received"); - addMainPart(@" from kudosu deny repeal of modding post "); - break; + return @" from kudosu deny repeal of modding post"; case KudosuAction.DeleteReset: - addKudosuPart(@"Lost"); - addMainPart(@" from modding post deletion of "); - break; + return @" from modding post deletion of"; case KudosuAction.RestoreGive: - addKudosuPart(@"Received"); - addMainPart(@" from modding post restoration of "); - break; + return @" from modding post restoration of"; case KudosuAction.RecalculateGive: - addKudosuPart(@"Received"); - addMainPart(@" from votes recalculation in modding post of "); - break; + return @" from votes recalculation in modding post of"; case KudosuAction.RecalculateReset: - addKudosuPart(@"Lost"); - addMainPart(@" from votes recalculation in modding post of "); - break; + return @" from votes recalculation in modding post of"; + + default: + return @" from unknown event "; } - - addPostPart(); } - private void addKudosuPart(string prefix) + private string getPrefix(APIKudosuHistory historyItem) { - linkFlowContainer.AddText(prefix); - - linkFlowContainer.AddText($@" {historyItem.Amount} kudosu", t => + switch (historyItem.Action) { - t.Font = t.Font.With(italics: true); - t.Colour = colours.Blue; - }); + case KudosuAction.VoteGive: + return @"Received"; + + case KudosuAction.Give: + return @"Received"; + + case KudosuAction.VoteReset: + return @"Lost"; + + case KudosuAction.DenyKudosuReset: + return @"Denied"; + + case KudosuAction.AllowKudosuGive: + return @"Received"; + + case KudosuAction.DeleteReset: + return @"Lost"; + + case KudosuAction.RestoreGive: + return @"Received"; + + case KudosuAction.RecalculateGive: + return @"Received"; + + case KudosuAction.RecalculateReset: + return @"Lost"; + + default: + return null; + } } - - private void addMainPart(string text) - { - var formatted = MessageFormatter.FormatText(text); - - linkFlowContainer.AddLinks(formatted.Text, formatted.Links); - } - - private void addPostPart() => linkFlowContainer.AddLink(historyItem.Post.Title, historyItem.Post.Url); } } From 9752dbf9505ba614d6609c42fa874aeb73aad026 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Aug 2019 18:10:58 +0900 Subject: [PATCH 2402/2854] Fix osu! approach circles fading in too late --- .../Objects/Drawables/DrawableHitCircle.cs | 6 +++++- .../Objects/Drawables/DrawableOsuHitObject.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 7 +++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index ca124e9214..0af278f6a4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -29,6 +29,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly HitArea hitArea; + private readonly SkinnableDrawable mainContent; + public DrawableHitCircle(HitCircle h) : base(h) { @@ -56,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables return true; }, }, - new SkinnableDrawable("Play/osu/hitcircle", _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)), + mainContent = new SkinnableDrawable("Play/osu/hitcircle", _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)), ApproachCircle = new ApproachCircle { Alpha = 0, @@ -108,6 +110,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateInitialTransforms(); + mainContent.FadeInFromZero(HitObject.TimeFadeIn); + ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt)); ApproachCircle.ScaleTo(1f, HitObject.TimePreempt); ApproachCircle.Expire(true); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index a89fb8b682..17efefa959 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt; - protected override void UpdateInitialTransforms() => this.FadeIn(HitObject.TimeFadeIn); + protected override void UpdateInitialTransforms() => this.FadeInFromZero(); private OsuInputManager osuActionInputManager; internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index a0626707af..1749ea1f60 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -93,6 +93,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } + protected override void UpdateInitialTransforms() + { + base.UpdateInitialTransforms(); + + Body.FadeInFromZero(HitObject.TimeFadeIn); + } + [BackgroundDependencyLoader] private void load() { From 9a98f39f06623a181bf4298e8ffb1c8f80e238a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Aug 2019 18:12:47 +0900 Subject: [PATCH 2403/2854] Share logic with other rulesets (and make default) --- .../Objects/Drawable/DrawableCatchHitObject.cs | 2 +- .../Objects/Drawables/DrawableManiaHitObject.cs | 2 -- .../Objects/Drawables/DrawableOsuHitObject.cs | 2 -- .../Objects/Drawables/DrawableTaikoHitObject.cs | 2 -- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 4 ++++ 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs index f4218061d4..00734810b3 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt; - protected override void UpdateInitialTransforms() => this.FadeIn(200); + protected override void UpdateInitialTransforms() => this.FadeInFromZero(200); protected override void UpdateStateTransforms(ArmedState state) { diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index ce1484d460..e5b114ca81 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -46,8 +46,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Anchor = Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre; } - protected override void UpdateInitialTransforms() => this.FadeIn(); - protected override void UpdateStateTransforms(ArmedState state) { switch (state) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 17efefa959..b4f5642f45 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -36,8 +36,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt; - protected override void UpdateInitialTransforms() => this.FadeInFromZero(); - private OsuInputManager osuActionInputManager; internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 5f3bab5b28..5424ccb4de 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -78,8 +78,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public abstract bool OnPressed(TaikoAction action); public virtual bool OnReleased(TaikoAction action) => false; - protected override void UpdateInitialTransforms() => this.FadeIn(); - public override double LifetimeStart { get => base.LifetimeStart; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 80e70589bd..4a6f261905 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; +using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osu.Game.Audio; using osu.Game.Rulesets.Judgements; @@ -186,6 +187,8 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// Apply (generally fade-in) transforms leading into the start time. /// The local drawable hierarchy is recursively delayed to for convenience. + /// + /// By default this will fade in the object from zero with no duration. /// /// /// This is called once before every . This is to ensure a good state in the case @@ -193,6 +196,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// protected virtual void UpdateInitialTransforms() { + this.FadeInFromZero(); } /// From cb8fe89b8bf9d82008db4853cbd6ba680a194de7 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 28 Aug 2019 13:09:53 +0300 Subject: [PATCH 2404/2854] Allow setting looping to existing channels --- osu.Game/Skinning/SkinnableSound.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index ac2cb16a6a..1491f2a989 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -35,7 +35,18 @@ namespace osu.Game.Skinning this.audio = audio; } - public bool Looping; + private bool looping; + + public bool Looping + { + get => looping; + set + { + looping = value; + + channels.ForEach(c => c.Looping = looping); + } + } public void Play() => channels?.ForEach(c => c.Play()); From 2f3fb8cf88823e906df851d9da5d5ca9c1eb3f9a Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 28 Aug 2019 13:10:11 +0300 Subject: [PATCH 2405/2854] Add Stop + RemoveAdjustment functions --- osu.Game/Skinning/SkinnableSound.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 1491f2a989..74804d5a06 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -49,8 +49,10 @@ namespace osu.Game.Skinning } public void Play() => channels?.ForEach(c => c.Play()); + public void Stop() => channels?.ForEach(c => c.Stop()); public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => channels?.ForEach(c => c.AddAdjustment(type, adjustBindable)); + public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => channels?.ForEach(c => c.RemoveAdjustment(type, adjustBindable)); public override bool IsPresent => Scheduler.HasPendingTasks; From 01aede3e299f53f0fd2c86f00a5c5087559ace88 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Aug 2019 19:57:17 +0900 Subject: [PATCH 2406/2854] Add comprehensive skin fallback integration testing --- .../TestSceneOsuPlayer.cs | 140 ++++++++++++++++++ .../Beatmaps/BeatmapManager_WorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 8 +- osu.Game/Skinning/SkinnableDrawable.cs | 2 +- osu.Game/Tests/Visual/PlayerTestScene.cs | 7 +- osu.Game/Tests/Visual/TestPlayer.cs | 3 + 6 files changed, 153 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs index 0a33b09ba8..7c84ae80c8 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs @@ -1,7 +1,23 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Timing; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Screens.Play; +using osu.Game.Skinning; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Osu.Tests @@ -9,9 +25,133 @@ namespace osu.Game.Rulesets.Osu.Tests [TestFixture] public class TestSceneOsuPlayer : PlayerTestScene { + private readonly TestSource testUserSkin; + private readonly TestSource testBeatmapSkin; + public TestSceneOsuPlayer() : base(new OsuRuleset()) { + testUserSkin = new TestSource("user"); + testBeatmapSkin = new TestSource("beatmap"); + } + + [Test] + public void TestBeatmapSkinDefault() + { + AddStep("enable user provider", () => testUserSkin.Enabled = true); + + AddStep("enable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, true)); + checkNextHitObject("beatmap"); + + AddStep("disable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, false)); + checkNextHitObject("user"); + + AddStep("disable user provider", () => testUserSkin.Enabled = false); + checkNextHitObject(null); + } + + private void checkNextHitObject(string skin) => + AddUntilStep($"check skin from {skin}", () => + { + var firstObject = ((TestPlayer)Player).DrawableRuleset.Playfield.AllHitObjects.OfType().FirstOrDefault(); + + if (firstObject == null) + return false; + + var skinnable = firstObject?.ApproachCircle.Child as SkinnableDrawable; + + if (skin == null && skinnable?.Drawable is Sprite) + // check for default skin provider + return true; + + var text = skinnable?.Drawable as SpriteText; + + return text?.Text == skin; + }); + + [Resolved] + private AudioManager audio { get; set; } + + protected override Player CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(testUserSkin); + + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) => new CustomSkinWorkingBeatmap(beatmap, Clock, audio, testBeatmapSkin); + + public class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap + { + private readonly ISkinSource skin; + + public CustomSkinWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock frameBasedClock, AudioManager audio, ISkinSource skin) + : base(beatmap, frameBasedClock, audio) + { + this.skin = skin; + } + + protected override ISkin GetSkin() => skin; + } + + public class SkinProvidingPlayer : TestPlayer + { + private readonly TestSource userSkin; + + public SkinProvidingPlayer(TestSource userSkin) + { + this.userSkin = userSkin; + } + + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + + dependencies.CacheAs(userSkin); + + return dependencies; + } + } + + public class TestSource : ISkinSource + { + private readonly string identifier; + + public TestSource(string identifier) + { + this.identifier = identifier; + } + + public Drawable GetDrawableComponent(string componentName) + { + if (!enabled) return null; + + return new SpriteText + { + Text = identifier, + Font = OsuFont.Default.With(size: 30), + }; + } + + public Texture GetTexture(string componentName) => null; + + public SampleChannel GetSample(ISampleInfo sampleInfo) => null; + + public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => default; + + public event Action SourceChanged; + + private bool enabled = true; + + public bool Enabled + { + get => enabled; + set + { + if (value == enabled) + return; + + enabled = value; + SourceChanged?.Invoke(); + } + } } } } diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 2d8a0b1249..5bbffc2f77 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -136,7 +136,7 @@ namespace osu.Game.Beatmaps return storyboard; } - protected override Skin GetSkin() + protected override ISkin GetSkin() { try { diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 8605caa5fe..9addcfbdd7 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -46,7 +46,7 @@ namespace osu.Game.Beatmaps background = new RecyclableLazy(GetBackground, BackgroundStillValid); waveform = new RecyclableLazy(GetWaveform); storyboard = new RecyclableLazy(GetStoryboard); - skin = new RecyclableLazy(GetSkin); + skin = new RecyclableLazy(GetSkin); total_count.Value++; } @@ -214,10 +214,10 @@ namespace osu.Game.Beatmaps private readonly RecyclableLazy storyboard; public bool SkinLoaded => skin.IsResultAvailable; - public Skin Skin => skin.Value; + public ISkin Skin => skin.Value; - protected virtual Skin GetSkin() => new DefaultSkin(); - private readonly RecyclableLazy skin; + protected virtual ISkin GetSkin() => new DefaultSkin(); + private readonly RecyclableLazy skin; /// /// Transfer pieces of a beatmap to a new one, where possible, to save on loading. diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index 0c635a3d2f..07f802944b 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -16,7 +16,7 @@ namespace osu.Game.Skinning /// /// The displayed component. /// - protected Drawable Drawable { get; private set; } + public Drawable Drawable { get; private set; } private readonly string componentName; diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index 03e17a819c..1ab20ecd48 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -22,12 +22,13 @@ namespace osu.Game.Tests.Visual this.ruleset = ruleset; } + protected OsuConfigManager LocalConfig; + [BackgroundDependencyLoader] private void load() { - OsuConfigManager manager; - Dependencies.Cache(manager = new OsuConfigManager(LocalStorage)); - manager.GetBindable(OsuSetting.DimLevel).Value = 1.0; + Dependencies.Cache(LocalConfig = new OsuConfigManager(LocalStorage)); + LocalConfig.GetBindable(OsuSetting.DimLevel).Value = 1.0; } [SetUpSteps] diff --git a/osu.Game/Tests/Visual/TestPlayer.cs b/osu.Game/Tests/Visual/TestPlayer.cs index b93a1466e0..31f6edadec 100644 --- a/osu.Game/Tests/Visual/TestPlayer.cs +++ b/osu.Game/Tests/Visual/TestPlayer.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; namespace osu.Game.Tests.Visual @@ -9,6 +10,8 @@ namespace osu.Game.Tests.Visual { protected override bool PauseOnFocusLost => false; + public new DrawableRuleset DrawableRuleset => base.DrawableRuleset; + public TestPlayer(bool allowPause = true, bool showResults = true) : base(allowPause, showResults) { From c7e20b34bae62458749b973acb1e0cd54f1e8c7b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Aug 2019 20:15:28 +0900 Subject: [PATCH 2407/2854] Cleanup --- osu.Game/Utils/HumanizerUtils.cs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/osu.Game/Utils/HumanizerUtils.cs b/osu.Game/Utils/HumanizerUtils.cs index 398c76a09f..5b7c3630d9 100644 --- a/osu.Game/Utils/HumanizerUtils.cs +++ b/osu.Game/Utils/HumanizerUtils.cs @@ -10,27 +10,21 @@ namespace osu.Game.Utils public static class HumanizerUtils { /// - /// Humanizes a string using the system culture, then falls back if one cannot be found. - /// - /// A localization lookup failure will throw an exception of type - /// + /// Turns the current or provided date into a human readable sentence /// - /// The time to humanize. - /// A humanized string of the given time. - public static string Humanize(DateTimeOffset dateTimeOffset) + /// The date to be humanized + /// distance of time in words + public static string Humanize(DateTimeOffset input) { - string offset; - + // this works around https://github.com/xamarin/xamarin-android/issues/2012 and https://github.com/Humanizr/Humanizer/issues/690#issuecomment-368536282 try { - offset = dateTimeOffset.Humanize(); + return input.Humanize(); } catch (ArgumentException) { - offset = dateTimeOffset.Humanize(culture: new CultureInfo("en-US")); + return input.Humanize(culture: new CultureInfo("en-US")); } - - return offset; } } } From 8b42890644d503b32e663e028bd711434b1969c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Aug 2019 20:19:19 +0900 Subject: [PATCH 2408/2854] Fix unnecessary null check --- osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs index 7c84ae80c8..d61378f4f8 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Tests if (firstObject == null) return false; - var skinnable = firstObject?.ApproachCircle.Child as SkinnableDrawable; + var skinnable = firstObject.ApproachCircle.Child as SkinnableDrawable; if (skin == null && skinnable?.Drawable is Sprite) // check for default skin provider From 348d88846da7d530b992a651512843d123f07bb7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 28 Aug 2019 20:19:22 +0900 Subject: [PATCH 2409/2854] Add IBeatmap interface for typed hitobject retrieval --- osu.Game/Beatmaps/Beatmap.cs | 12 ++++-------- osu.Game/Beatmaps/IBeatmap.cs | 9 +++++++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index a09a1bb9cb..5435e86dfd 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -14,7 +14,7 @@ namespace osu.Game.Beatmaps /// /// A Beatmap containing converted HitObjects. /// - public class Beatmap : IBeatmap + public class Beatmap : IBeatmap where T : HitObject { public BeatmapInfo BeatmapInfo { get; set; } = new BeatmapInfo @@ -36,17 +36,13 @@ namespace osu.Game.Beatmaps public List Breaks { get; set; } = new List(); - /// - /// Total amount of break time in the beatmap. - /// [JsonIgnore] public double TotalBreakTime => Breaks.Sum(b => b.Duration); - /// - /// The HitObjects this Beatmap contains. - /// [JsonConverter(typeof(TypedListConverter))] - public List HitObjects = new List(); + public List HitObjects { get; set; } = new List(); + + IReadOnlyList IBeatmap.HitObjects => HitObjects; IReadOnlyList IBeatmap.HitObjects => HitObjects; diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index 512fe25809..8f27e0b0e9 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -53,4 +53,13 @@ namespace osu.Game.Beatmaps /// The shallow-cloned beatmap. IBeatmap Clone(); } + + public interface IBeatmap : IBeatmap + where T : HitObject + { + /// + /// The hitobjects contained by this beatmap. + /// + new IReadOnlyList HitObjects { get; } + } } From f6ad95018adda03061931a74039c8a375ae887df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Aug 2019 20:22:16 +0900 Subject: [PATCH 2410/2854] Centralise default beat length specification --- osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs | 4 +++- .../Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs | 2 +- osu.Game/Rulesets/Timing/MultiplierControlPoint.cs | 2 +- osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index e5815a3f3b..ccb8a92b3a 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -14,6 +14,8 @@ namespace osu.Game.Beatmaps.ControlPoints /// public TimeSignatures TimeSignature = TimeSignatures.SimpleQuadruple; + public const double DEFAULT_BEAT_LENGTH = 1000; + /// /// The beat length at this control point. /// @@ -23,7 +25,7 @@ namespace osu.Game.Beatmaps.ControlPoints set => beatLength = MathHelper.Clamp(value, 6, 60000); } - private double beatLength = 1000; + private double beatLength = DEFAULT_BEAT_LENGTH; public bool Equals(TimingControlPoint other) => base.Equals(other) diff --git a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs index 540f616ea9..8775c15f17 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs @@ -31,7 +31,7 @@ namespace osu.Game.Beatmaps.Formats private class LegacyDifficultyCalculatorControlPoint : TimingControlPoint { - public override double BeatLength { get; set; } = 1000; + public override double BeatLength { get; set; } = TimingControlPoint.DEFAULT_BEAT_LENGTH; } } } diff --git a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs index ffa35c24cd..4b3c3f90f0 100644 --- a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs +++ b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Timing /// The base beat length to scale the provided multiplier relative to. /// /// For a of 1000, a with a beat length of 500 will increase the multiplier by 2. - public double BaseBeatLength = 1000; + public double BaseBeatLength = TimingControlPoint.DEFAULT_BEAT_LENGTH; /// /// The velocity multiplier. diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index 65a22b10b7..c1a4c9520e 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.UI.Scrolling private void load() { double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; - double baseBeatLength = 1000; + double baseBeatLength = TimingControlPoint.DEFAULT_BEAT_LENGTH; if (RelativeScaleBeatLengths) { From 3d6200338263bdaca6e39410bd52df1a0dab7e79 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 28 Aug 2019 15:39:45 +0300 Subject: [PATCH 2411/2854] Add null check --- osu.Game/Skinning/SkinnableSound.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 74804d5a06..cb511fc775 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -44,7 +44,7 @@ namespace osu.Game.Skinning { looping = value; - channels.ForEach(c => c.Looping = looping); + channels?.ForEach(c => c.Looping = looping); } } From c6e757fdae6c89b70927a26a83db61ed2cb8e572 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Aug 2019 22:11:23 +0900 Subject: [PATCH 2412/2854] Remove redundant qualifier --- .../Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs index 8775c15f17..2c493254e0 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs @@ -31,7 +31,7 @@ namespace osu.Game.Beatmaps.Formats private class LegacyDifficultyCalculatorControlPoint : TimingControlPoint { - public override double BeatLength { get; set; } = TimingControlPoint.DEFAULT_BEAT_LENGTH; + public override double BeatLength { get; set; } = DEFAULT_BEAT_LENGTH; } } } From 73fd3cf03ca93a0a73b9ae4d55e726d3e3c8089f Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 28 Aug 2019 19:00:01 -0700 Subject: [PATCH 2413/2854] Fix gameplay menu button initial hover animation --- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index c7e762714c..f93d5d8b02 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -304,8 +304,6 @@ namespace osu.Game.Screens.Play private class Button : DialogButton { - protected override bool OnHover(HoverEvent e) => true; - protected override bool OnMouseMove(MouseMoveEvent e) { Selected.Value = true; From 03a4acaf4ca0071c9885e5176e67296bb7b68073 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Aug 2019 12:07:58 +0900 Subject: [PATCH 2414/2854] Fix drags outside of overlay container bounds not hiding overlay --- .../Containers/OsuFocusedOverlayContainer.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 0f7b26835b..9c948d6f90 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -62,15 +62,23 @@ namespace osu.Game.Graphics.Containers protected override bool OnClick(ClickEvent e) { - if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)) - { - Hide(); - return true; - } + closeIfOutside(e); return base.OnClick(e); } + protected override bool OnDragEnd(DragEndEvent e) + { + closeIfOutside(e); + return base.OnDragEnd(e); + } + + private void closeIfOutside(MouseEvent e) + { + if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)) + Hide(); + } + public virtual bool OnPressed(GlobalAction action) { switch (action) From 6949c96aaa51952258a335de43b73fc0ba7a793f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Aug 2019 12:43:43 +0900 Subject: [PATCH 2415/2854] Add initial EditorBeatmap structure --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 14 ++++ osu.Game/Screens/Edit/EditorBeatmap.cs | 79 +++++++++++++++++++++ osu.Game/Screens/Edit/IEditorBeatmap.cs | 17 +++++ 3 files changed, 110 insertions(+) create mode 100644 osu.Game/Screens/Edit/EditorBeatmap.cs create mode 100644 osu.Game/Screens/Edit/IEditorBeatmap.cs diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 38ec09535d..6d98f45187 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -18,6 +18,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Components.RadioButtons; using osu.Game.Screens.Edit.Compose.Components; @@ -185,6 +186,19 @@ namespace osu.Game.Rulesets.Edit { } + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var workingBeatmap = parent.Get>(); + var playableBeatmap = (Beatmap)workingBeatmap.Value.GetPlayableBeatmap(Ruleset.RulesetInfo, Array.Empty()); + var editorBeatmap = new EditorBeatmap(playableBeatmap); + + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + dependencies.CacheAs(editorBeatmap); + dependencies.CacheAs(editorBeatmap); + + return dependencies; + } + internal override DrawableEditRuleset CreateDrawableRuleset() => new DrawableEditRuleset(CreateDrawableRuleset(Ruleset, Beatmap.Value, Array.Empty())); diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs new file mode 100644 index 0000000000..99dd441578 --- /dev/null +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -0,0 +1,79 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Screens.Edit +{ + public class EditorBeatmap : IBeatmap, IEditorBeatmap + where T : HitObject + { + public event Action HitObjectRemoved; + public event Action HitObjectAdded; + + event Action IEditorBeatmap.HitObjectAdded + { + add => HitObjectAdded += value; + remove => HitObjectAdded -= value; + } + + event Action IEditorBeatmap.HitObjectRemoved + { + add => HitObjectRemoved += value; + remove => HitObjectRemoved -= value; + } + + private readonly Beatmap beatmap; + + public EditorBeatmap(Beatmap beatmap) + { + this.beatmap = beatmap; + } + + public BeatmapInfo BeatmapInfo + { + get => beatmap.BeatmapInfo; + set => beatmap.BeatmapInfo = value; + } + + public BeatmapMetadata Metadata => beatmap.Metadata; + + public ControlPointInfo ControlPointInfo => beatmap.ControlPointInfo; + + public List Breaks => beatmap.Breaks; + + public double TotalBreakTime => beatmap.TotalBreakTime; + + IReadOnlyList IBeatmap.HitObjects => beatmap.HitObjects; + + IReadOnlyList IBeatmap.HitObjects => beatmap.HitObjects; + + public IEnumerable GetStatistics() => beatmap.GetStatistics(); + + public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone(); + + public void Add(T hitObject) + { + // Preserve existing sorting order in the beatmap + var insertionIndex = beatmap.HitObjects.FindLastIndex(h => h.StartTime <= hitObject.StartTime); + beatmap.HitObjects.Insert(insertionIndex + 1, hitObject); + + HitObjectAdded?.Invoke(hitObject); + } + + public void Remove(T hitObject) + { + if (beatmap.HitObjects.Remove(hitObject)) + HitObjectRemoved?.Invoke(hitObject); + } + + public void Add(HitObject hitObject) => Add((T)hitObject); + + public void Remove(HitObject hitObject) => Remove((T)hitObject); + } +} diff --git a/osu.Game/Screens/Edit/IEditorBeatmap.cs b/osu.Game/Screens/Edit/IEditorBeatmap.cs new file mode 100644 index 0000000000..602a096d65 --- /dev/null +++ b/osu.Game/Screens/Edit/IEditorBeatmap.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Screens.Edit +{ + public interface IEditorBeatmap + { + event Action HitObjectAdded; + event Action HitObjectRemoved; + + void Add(HitObject hitObject); + void Remove(HitObject hitObject); + } +} From 840f2246199d7818dc29771de2dd81628f689294 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Aug 2019 16:05:44 +0900 Subject: [PATCH 2416/2854] Remove typed events to reduce complexity --- osu.Game/Screens/Edit/EditorBeatmap.cs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 99dd441578..d9f17abfa5 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -13,20 +13,8 @@ namespace osu.Game.Screens.Edit public class EditorBeatmap : IBeatmap, IEditorBeatmap where T : HitObject { - public event Action HitObjectRemoved; - public event Action HitObjectAdded; - - event Action IEditorBeatmap.HitObjectAdded - { - add => HitObjectAdded += value; - remove => HitObjectAdded -= value; - } - - event Action IEditorBeatmap.HitObjectRemoved - { - add => HitObjectRemoved += value; - remove => HitObjectRemoved -= value; - } + public event Action HitObjectAdded; + public event Action HitObjectRemoved; private readonly Beatmap beatmap; From 7927b684d3c0ace143ae033f20b944c37c9f1f41 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Aug 2019 16:06:40 +0900 Subject: [PATCH 2417/2854] Hook up + use editor beatmap --- .../Editor/TestSceneHitObjectComposer.cs | 16 +--- osu.Game/Rulesets/Edit/DrawableEditRuleset.cs | 63 ++++----------- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 66 ++++++++++++---- .../Compose/Components/BlueprintContainer.cs | 76 +++++++++++++------ .../Screens/Edit/Compose/ComposeScreen.cs | 12 +-- 5 files changed, 118 insertions(+), 115 deletions(-) diff --git a/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs index 7accbe2fa8..0ea73fb3de 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs @@ -16,15 +16,13 @@ using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Compose.Components; using osuTK; namespace osu.Game.Tests.Visual.Editor { [TestFixture] - [Cached(Type = typeof(IPlacementHandler))] - public class TestSceneHitObjectComposer : OsuTestScene, IPlacementHandler + public class TestSceneHitObjectComposer : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { @@ -39,8 +37,6 @@ namespace osu.Game.Tests.Visual.Editor typeof(HitCirclePlacementBlueprint), }; - private HitObjectComposer composer; - [BackgroundDependencyLoader] private void load() { @@ -67,15 +63,7 @@ namespace osu.Game.Tests.Visual.Editor Dependencies.CacheAs(clock); Dependencies.CacheAs(clock); - Child = composer = new OsuHitObjectComposer(new OsuRuleset()); + Child = new OsuHitObjectComposer(new OsuRuleset()); } - - public void BeginPlacement(HitObject hitObject) - { - } - - public void EndPlacement(HitObject hitObject) => composer.Add(hitObject); - - public void Delete(HitObject hitObject) => composer.Remove(hitObject); } } diff --git a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs b/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs index e85ebb5f3a..c9d7b2cd81 100644 --- a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs +++ b/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs @@ -5,10 +5,9 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Edit; namespace osu.Game.Rulesets.Edit { @@ -25,20 +24,6 @@ namespace osu.Game.Rulesets.Edit { RelativeSizeAxes = Axes.Both; } - - /// - /// Adds a to the and displays a visual representation of it. - /// - /// The to add. - /// The visual representation of . - internal abstract DrawableHitObject Add(HitObject hitObject); - - /// - /// Removes a from the and the display. - /// - /// The to remove. - /// The visual representation of the removed . - internal abstract DrawableHitObject Remove(HitObject hitObject); } public class DrawableEditRuleset : DrawableEditRuleset @@ -48,11 +33,11 @@ namespace osu.Game.Rulesets.Edit public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => drawableRuleset.CreatePlayfieldAdjustmentContainer(); - private Ruleset ruleset => drawableRuleset.Ruleset; - private Beatmap beatmap => drawableRuleset.Beatmap; - private readonly DrawableRuleset drawableRuleset; + [Resolved] + private EditorBeatmap beatmap { get; set; } + public DrawableEditRuleset(DrawableRuleset drawableRuleset) { this.drawableRuleset = drawableRuleset; @@ -67,50 +52,28 @@ namespace osu.Game.Rulesets.Edit Playfield.DisplayJudgements.Value = false; } - internal override DrawableHitObject Add(HitObject hitObject) + protected override void LoadComplete() { - var tObject = (TObject)hitObject; + base.LoadComplete(); - // Add to beatmap, preserving sorting order - var insertionIndex = beatmap.HitObjects.FindLastIndex(h => h.StartTime <= hitObject.StartTime); - beatmap.HitObjects.Insert(insertionIndex + 1, tObject); + beatmap.HitObjectAdded += addHitObject; + beatmap.HitObjectRemoved += removeHitObject; + } - // Process object - var processor = ruleset.CreateBeatmapProcessor(beatmap); - - processor?.PreProcess(); - tObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); - processor?.PostProcess(); - - // Add visual representation - var drawableObject = drawableRuleset.CreateDrawableRepresentation(tObject); + private void addHitObject(HitObject hitObject) + { + var drawableObject = drawableRuleset.CreateDrawableRepresentation((TObject)hitObject); drawableRuleset.Playfield.Add(drawableObject); drawableRuleset.Playfield.PostProcess(); - - return drawableObject; } - internal override DrawableHitObject Remove(HitObject hitObject) + private void removeHitObject(HitObject hitObject) { - var tObject = (TObject)hitObject; - - // Remove from beatmap - beatmap.HitObjects.Remove(tObject); - - // Process the beatmap - var processor = ruleset.CreateBeatmapProcessor(beatmap); - - processor?.PreProcess(); - processor?.PostProcess(); - - // Remove visual representation var drawableObject = Playfield.AllHitObjects.Single(d => d.HitObject == hitObject); drawableRuleset.Playfield.Remove(drawableObject); drawableRuleset.Playfield.PostProcess(); - - return drawableObject; } } } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 6d98f45187..fe81f6747d 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -20,6 +20,7 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Components.RadioButtons; +using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Compose.Components; namespace osu.Game.Rulesets.Edit @@ -154,14 +155,6 @@ namespace osu.Game.Rulesets.Edit /// public virtual bool CursorInPlacementArea => DrawableRuleset.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position); - /// - /// Adds a to the and visualises it. - /// - /// The to add. - public void Add(HitObject hitObject) => blueprintContainer.AddBlueprintFor(DrawableRuleset.Add(hitObject)); - - public void Remove(HitObject hitObject) => blueprintContainer.RemoveBlueprintFor(DrawableRuleset.Remove(hitObject)); - internal abstract DrawableEditRuleset CreateDrawableRuleset(); protected abstract IReadOnlyList CompositionTools { get; } @@ -178,9 +171,16 @@ namespace osu.Game.Rulesets.Edit public virtual SelectionHandler CreateSelectionHandler() => new SelectionHandler(); } - public abstract class HitObjectComposer : HitObjectComposer + [Cached(Type = typeof(IPlacementHandler))] + public abstract class HitObjectComposer : HitObjectComposer, IPlacementHandler where TObject : HitObject { + private Beatmap playableBeatmap; + + [Cached] + [Cached(typeof(IEditorBeatmap))] + private EditorBeatmap editorBeatmap; + protected HitObjectComposer(Ruleset ruleset) : base(ruleset) { @@ -189,19 +189,55 @@ namespace osu.Game.Rulesets.Edit protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { var workingBeatmap = parent.Get>(); - var playableBeatmap = (Beatmap)workingBeatmap.Value.GetPlayableBeatmap(Ruleset.RulesetInfo, Array.Empty()); - var editorBeatmap = new EditorBeatmap(playableBeatmap); + playableBeatmap = (Beatmap)workingBeatmap.Value.GetPlayableBeatmap(Ruleset.RulesetInfo, Array.Empty()); - var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - dependencies.CacheAs(editorBeatmap); - dependencies.CacheAs(editorBeatmap); + editorBeatmap = new EditorBeatmap(playableBeatmap); + editorBeatmap.HitObjectAdded += addHitObject; + editorBeatmap.HitObjectRemoved += removeHitObject; - return dependencies; + return base.CreateChildDependencies(parent); + } + + private void addHitObject(HitObject hitObject) + { + // Process object + var processor = Ruleset.CreateBeatmapProcessor(playableBeatmap); + + processor?.PreProcess(); + hitObject.ApplyDefaults(playableBeatmap.ControlPointInfo, playableBeatmap.BeatmapInfo.BaseDifficulty); + processor?.PostProcess(); + } + + private void removeHitObject(HitObject hitObject) + { + var processor = Ruleset.CreateBeatmapProcessor(playableBeatmap); + + processor?.PreProcess(); + processor?.PostProcess(); } internal override DrawableEditRuleset CreateDrawableRuleset() => new DrawableEditRuleset(CreateDrawableRuleset(Ruleset, Beatmap.Value, Array.Empty())); protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods); + + public void BeginPlacement(HitObject hitObject) + { + } + + public void EndPlacement(HitObject hitObject) => editorBeatmap.Add(hitObject); + + public void Delete(HitObject hitObject) => editorBeatmap.Remove(hitObject); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (editorBeatmap != null) + { + editorBeatmap.HitObjectAdded -= addHitObject; + editorBeatmap.HitObjectRemoved -= removeHitObject; + } + } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index a1e62cd38b..7d25fd5283 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -11,6 +11,7 @@ using osu.Framework.Input.Events; using osu.Framework.Input.States; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Screens.Edit.Compose.Components @@ -29,6 +30,9 @@ namespace osu.Game.Screens.Edit.Compose.Components [Resolved] private HitObjectComposer composer { get; set; } + [Resolved] + private IEditorBeatmap beatmap { get; set; } + public BlueprintContainer() { RelativeSizeAxes = Axes.Both; @@ -53,7 +57,15 @@ namespace osu.Game.Screens.Edit.Compose.Components }; foreach (var obj in composer.HitObjects) - AddBlueprintFor(obj); + addBlueprintFor(obj); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + beatmap.HitObjectAdded += addBlueprintFor; + beatmap.HitObjectRemoved += removeBlueprintFor; } private HitObjectCompositionTool currentTool; @@ -75,11 +87,32 @@ namespace osu.Game.Screens.Edit.Compose.Components } } - /// - /// Adds a blueprint for a which adds movement support. - /// - /// The to create a blueprint for. - public void AddBlueprintFor(DrawableHitObject hitObject) + private void addBlueprintFor(HitObject hitObject) + { + var drawable = composer.HitObjects.FirstOrDefault(d => d.HitObject == hitObject); + if (drawable == null) + return; + + addBlueprintFor(drawable); + } + + private void removeBlueprintFor(HitObject hitObject) + { + var blueprint = selectionBlueprints.Single(m => m.HitObject.HitObject == hitObject); + if (blueprint == null) + return; + + blueprint.Deselect(); + + blueprint.Selected -= onBlueprintSelected; + blueprint.Deselected -= onBlueprintDeselected; + blueprint.SelectionRequested -= onSelectionRequested; + blueprint.DragRequested -= onDragRequested; + + selectionBlueprints.Remove(blueprint); + } + + private void addBlueprintFor(DrawableHitObject hitObject) { refreshTool(); @@ -95,25 +128,7 @@ namespace osu.Game.Screens.Edit.Compose.Components selectionBlueprints.Add(blueprint); } - /// - /// Removes a blueprint for a . - /// - /// The for which to remove the blueprint. - public void RemoveBlueprintFor(DrawableHitObject hitObject) - { - var blueprint = selectionBlueprints.Single(m => m.HitObject == hitObject); - if (blueprint == null) - return; - - blueprint.Deselect(); - - blueprint.Selected -= onBlueprintSelected; - blueprint.Deselected -= onBlueprintDeselected; - blueprint.SelectionRequested -= onSelectionRequested; - blueprint.DragRequested -= onDragRequested; - - selectionBlueprints.Remove(blueprint); - } + private void removeBlueprintFor(DrawableHitObject hitObject) => removeBlueprintFor(hitObject.HitObject); protected override bool OnClick(ClickEvent e) { @@ -183,6 +198,17 @@ namespace osu.Game.Screens.Edit.Compose.Components private void onDragRequested(SelectionBlueprint blueprint, DragEvent dragEvent) => selectionHandler.HandleDrag(blueprint, dragEvent); + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (beatmap != null) + { + beatmap.HitObjectAdded -= addBlueprintFor; + beatmap.HitObjectRemoved -= removeBlueprintFor; + } + } + private class SelectionBlueprintContainer : Container { protected override int Compare(Drawable x, Drawable y) diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index 5699ef0a84..ec4dda5c23 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -9,15 +9,13 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Edit.Compose.Components.Timeline; using osuTK.Graphics; namespace osu.Game.Screens.Edit.Compose { - [Cached(Type = typeof(IPlacementHandler))] - public class ComposeScreen : EditorScreen, IPlacementHandler + public class ComposeScreen : EditorScreen { private const float vertical_margins = 10; private const float horizontal_margins = 20; @@ -119,13 +117,5 @@ namespace osu.Game.Screens.Edit.Compose composerContainer.Child = composer; } - - public void BeginPlacement(HitObject hitObject) - { - } - - public void EndPlacement(HitObject hitObject) => composer.Add(hitObject); - - public void Delete(HitObject hitObject) => composer.Remove(hitObject); } } From 5db813b7a411b94d61676cb325e883f929861755 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Aug 2019 16:26:39 +0900 Subject: [PATCH 2418/2854] Add secondary interface for further abstraction --- osu.Game/Rulesets/Edit/DrawableEditRuleset.cs | 2 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 9 ++++--- osu.Game/Screens/Edit/EditorBeatmap.cs | 2 +- osu.Game/Screens/Edit/IEditorBeatmap.cs | 27 ++++++++++++++++--- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs b/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs index c9d7b2cd81..95a1492a45 100644 --- a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs +++ b/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Edit private readonly DrawableRuleset drawableRuleset; [Resolved] - private EditorBeatmap beatmap { get; set; } + private IEditorBeatmap beatmap { get; set; } public DrawableEditRuleset(DrawableRuleset drawableRuleset) { diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index fe81f6747d..fb7021bfa8 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -176,9 +176,6 @@ namespace osu.Game.Rulesets.Edit where TObject : HitObject { private Beatmap playableBeatmap; - - [Cached] - [Cached(typeof(IEditorBeatmap))] private EditorBeatmap editorBeatmap; protected HitObjectComposer(Ruleset ruleset) @@ -195,7 +192,11 @@ namespace osu.Game.Rulesets.Edit editorBeatmap.HitObjectAdded += addHitObject; editorBeatmap.HitObjectRemoved += removeHitObject; - return base.CreateChildDependencies(parent); + var dependencies = new DependencyContainer(parent); + dependencies.CacheAs(editorBeatmap); + dependencies.CacheAs>(editorBeatmap); + + return base.CreateChildDependencies(dependencies); } private void addHitObject(HitObject hitObject) diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index d9f17abfa5..2261f6f45c 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Objects; namespace osu.Game.Screens.Edit { - public class EditorBeatmap : IBeatmap, IEditorBeatmap + public class EditorBeatmap : IEditorBeatmap where T : HitObject { public event Action HitObjectAdded; diff --git a/osu.Game/Screens/Edit/IEditorBeatmap.cs b/osu.Game/Screens/Edit/IEditorBeatmap.cs index 602a096d65..2f250ba446 100644 --- a/osu.Game/Screens/Edit/IEditorBeatmap.cs +++ b/osu.Game/Screens/Edit/IEditorBeatmap.cs @@ -2,16 +2,35 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; namespace osu.Game.Screens.Edit { - public interface IEditorBeatmap + /// + /// Interface for the contained by the see . + /// Children of may resolve the beatmap via or . + /// + public interface IEditorBeatmap : IBeatmap { + /// + /// Invoked when a is added to this . + /// event Action HitObjectAdded; - event Action HitObjectRemoved; - void Add(HitObject hitObject); - void Remove(HitObject hitObject); + /// + /// Invoked when a is removed from this . + /// + event Action HitObjectRemoved; + } + + /// + /// Interface for the contained by the see . + /// Children of may resolve the beatmap via or . + /// + public interface IEditorBeatmap : IEditorBeatmap, IBeatmap + where T : HitObject + { } } From dad0fa2dca16f672569e1a59bf4d16c54456346b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Aug 2019 16:31:40 +0900 Subject: [PATCH 2419/2854] Bind disposal --- osu.Game/Rulesets/Edit/DrawableEditRuleset.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs b/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs index 95a1492a45..a12e4ba3ab 100644 --- a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs +++ b/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs @@ -75,5 +75,16 @@ namespace osu.Game.Rulesets.Edit drawableRuleset.Playfield.Remove(drawableObject); drawableRuleset.Playfield.PostProcess(); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (beatmap != null) + { + beatmap.HitObjectAdded -= addHitObject; + beatmap.HitObjectRemoved -= removeHitObject; + } + } } } From b04a8ae8560e467ca8f3172e0261b9f3f4b05591 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Aug 2019 16:31:43 +0900 Subject: [PATCH 2420/2854] Add xmldocs --- osu.Game/Screens/Edit/EditorBeatmap.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 2261f6f45c..f0b6c62154 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -45,6 +45,10 @@ namespace osu.Game.Screens.Edit public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone(); + /// + /// Adds a to this . + /// + /// The to add. public void Add(T hitObject) { // Preserve existing sorting order in the beatmap @@ -54,14 +58,26 @@ namespace osu.Game.Screens.Edit HitObjectAdded?.Invoke(hitObject); } + /// + /// Removes a from this . + /// + /// The to add. public void Remove(T hitObject) { if (beatmap.HitObjects.Remove(hitObject)) HitObjectRemoved?.Invoke(hitObject); } + /// + /// Adds a to this . + /// + /// The to add. public void Add(HitObject hitObject) => Add((T)hitObject); + /// + /// Removes a from this . + /// + /// The to add. public void Remove(HitObject hitObject) => Remove((T)hitObject); } } From 40c1c6072ed587b0f9f3c8ec6d720117788fd62b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Aug 2019 16:38:39 +0900 Subject: [PATCH 2421/2854] Add "osu!classic" as a bundled skin choice --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- osu.Game/OsuGame.cs | 21 +++++++++++++++++++- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Skinning/DefaultLegacySkin.cs | 23 ++++++++++++++++++++++ osu.Game/Skinning/LegacySkin.cs | 3 +++ osu.Game/Skinning/SkinInfo.cs | 6 +++++- osu.Game/Skinning/SkinManager.cs | 18 +++++++++++++++-- 7 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 osu.Game/Skinning/DefaultLegacySkin.cs diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index b13e115387..fb472f3f89 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -18,7 +18,7 @@ namespace osu.Game.Configuration { // UI/selection defaults Set(OsuSetting.Ruleset, 0, 0, int.MaxValue); - Set(OsuSetting.Skin, 0, 0, int.MaxValue); + Set(OsuSetting.Skin, 0, -1, int.MaxValue); Set(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Details); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 0e804ecbaf..8fa8ffaf9b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -182,7 +182,26 @@ namespace osu.Game // bind config int to database SkinInfo configSkin = LocalConfig.GetBindable(OsuSetting.Skin); SkinManager.CurrentSkinInfo.ValueChanged += skin => configSkin.Value = skin.NewValue.ID; - configSkin.ValueChanged += skinId => SkinManager.CurrentSkinInfo.Value = SkinManager.Query(s => s.ID == skinId.NewValue) ?? SkinInfo.Default; + configSkin.ValueChanged += skinId => + { + var skinInfo = SkinManager.Query(s => s.ID == skinId.NewValue); + + if (skinInfo == null) + { + switch (skinId.NewValue) + { + case -1: + skinInfo = DefaultLegacySkin.Info; + break; + + default: + skinInfo = SkinInfo.Default; + break; + } + } + + SkinManager.CurrentSkinInfo.Value = skinInfo; + }; configSkin.TriggerChange(); IsActive.BindValueChanged(active => updateActiveState(active.NewValue), true); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 076c9ada78..de8f316b06 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -158,7 +158,7 @@ namespace osu.Game runMigrations(); - dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio)); + dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio, new NamespacedResourceStore(Resources, "Skins/Legacy"))); dependencies.CacheAs(SkinManager); API = new APIAccess(LocalConfig); diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs new file mode 100644 index 0000000000..b35c9c7b97 --- /dev/null +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Audio; +using osu.Framework.IO.Stores; + +namespace osu.Game.Skinning +{ + public class DefaultLegacySkin : LegacySkin + { + public DefaultLegacySkin(IResourceStore storage, AudioManager audioManager) + : base(Info, storage, audioManager, string.Empty) + { + } + + public static SkinInfo Info { get; } = new SkinInfo + { + ID = -1, // this is temporary until database storage is decided upon. + Name = "osu!classic", + Creator = "team osu!" + }; + } +} diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 570ba1ced7..0cc5e9c9b6 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -248,6 +248,9 @@ namespace osu.Game.Skinning private string getPathForFile(string filename) { + if (source.Files == null) + return null; + bool hasExtension = filename.Contains('.'); var file = source.Files.Find(f => diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index 187ea910a7..6b9627188e 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -26,7 +26,11 @@ namespace osu.Game.Skinning public string FullName => $"\"{Name}\" by {Creator}"; - public static SkinInfo Default { get; } = new SkinInfo { Name = "osu!lazer", Creator = "team osu!" }; + public static SkinInfo Default { get; } = new SkinInfo + { + Name = "osu!lazer", + Creator = "team osu!" + }; public bool Equals(SkinInfo other) => other != null && ID == other.ID; diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index e747a8b1ce..0e40eb5376 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -14,6 +14,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Game.Audio; using osu.Game.Database; @@ -25,6 +26,8 @@ namespace osu.Game.Skinning { private readonly AudioManager audio; + private readonly IResourceStore legacyDefaultResources; + public readonly Bindable CurrentSkin = new Bindable(new DefaultSkin()); public readonly Bindable CurrentSkinInfo = new Bindable(SkinInfo.Default) { Default = SkinInfo.Default }; @@ -34,10 +37,11 @@ namespace osu.Game.Skinning protected override string ImportFromStablePath => "Skins"; - public SkinManager(Storage storage, DatabaseContextFactory contextFactory, IIpcHost importHost, AudioManager audio) + public SkinManager(Storage storage, DatabaseContextFactory contextFactory, IIpcHost importHost, AudioManager audio, IResourceStore legacyDefaultResources) : base(storage, contextFactory, new SkinStore(contextFactory, storage), importHost) { this.audio = audio; + this.legacyDefaultResources = legacyDefaultResources; ItemRemoved += removedInfo => { @@ -56,6 +60,9 @@ namespace osu.Game.Skinning }; } + private Skin createIfNotExisting(SkinInfo skinInfo) => + GetSkin(Query(s => s.Name == skinInfo.Name) ?? Import(skinInfo).Result); + protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osk"; /// @@ -66,6 +73,7 @@ namespace osu.Game.Skinning { var userSkins = GetAllUserSkins(); userSkins.Insert(0, SkinInfo.Default); + userSkins.Insert(1, DefaultLegacySkin.Info); return userSkins; } @@ -91,7 +99,7 @@ namespace osu.Game.Skinning else { model.Name = model.Name.Replace(".osk", ""); - model.Creator = "Unknown"; + model.Creator = model.Creator ?? "Unknown"; } } @@ -102,9 +110,15 @@ namespace osu.Game.Skinning /// A instance correlating to the provided . public Skin GetSkin(SkinInfo skinInfo) { + if (skinInfo == null) + return null; + if (skinInfo == SkinInfo.Default) return new DefaultSkin(); + if (skinInfo == DefaultLegacySkin.Info) + return new DefaultLegacySkin(legacyDefaultResources, audio); + return new LegacySkin(skinInfo, Files.Store, audio); } From b40143cb73d8d5b8a0eaf8605b81b40ee30c05e7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Aug 2019 17:18:34 +0900 Subject: [PATCH 2422/2854] Remove unnecessary comment --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index fb7021bfa8..d5a0e052a5 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -201,7 +201,6 @@ namespace osu.Game.Rulesets.Edit private void addHitObject(HitObject hitObject) { - // Process object var processor = Ruleset.CreateBeatmapProcessor(playableBeatmap); processor?.PreProcess(); From 0fbdcabb6faa374e20fd43f1a762c839b73d4ced Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Aug 2019 17:21:52 +0900 Subject: [PATCH 2423/2854] Re-use a single beatmap processor --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index d5a0e052a5..ed2ef5d9f8 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -177,6 +177,7 @@ namespace osu.Game.Rulesets.Edit { private Beatmap playableBeatmap; private EditorBeatmap editorBeatmap; + private IBeatmapProcessor beatmapProcessor; protected HitObjectComposer(Ruleset ruleset) : base(ruleset) @@ -188,6 +189,8 @@ namespace osu.Game.Rulesets.Edit var workingBeatmap = parent.Get>(); playableBeatmap = (Beatmap)workingBeatmap.Value.GetPlayableBeatmap(Ruleset.RulesetInfo, Array.Empty()); + beatmapProcessor = Ruleset.CreateBeatmapProcessor(playableBeatmap); + editorBeatmap = new EditorBeatmap(playableBeatmap); editorBeatmap.HitObjectAdded += addHitObject; editorBeatmap.HitObjectRemoved += removeHitObject; @@ -201,19 +204,15 @@ namespace osu.Game.Rulesets.Edit private void addHitObject(HitObject hitObject) { - var processor = Ruleset.CreateBeatmapProcessor(playableBeatmap); - - processor?.PreProcess(); + beatmapProcessor?.PreProcess(); hitObject.ApplyDefaults(playableBeatmap.ControlPointInfo, playableBeatmap.BeatmapInfo.BaseDifficulty); - processor?.PostProcess(); + beatmapProcessor?.PostProcess(); } private void removeHitObject(HitObject hitObject) { - var processor = Ruleset.CreateBeatmapProcessor(playableBeatmap); - - processor?.PreProcess(); - processor?.PostProcess(); + beatmapProcessor?.PreProcess(); + beatmapProcessor?.PostProcess(); } internal override DrawableEditRuleset CreateDrawableRuleset() From 59296d12f3af1e2d107c0b0c3206e47cd1feab08 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Aug 2019 18:02:50 +0900 Subject: [PATCH 2424/2854] Refactor HitObjectComposer --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 150 +++++++++----------- 1 file changed, 70 insertions(+), 80 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index ed2ef5d9f8..239ec572b2 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -25,40 +25,40 @@ using osu.Game.Screens.Edit.Compose.Components; namespace osu.Game.Rulesets.Edit { - public abstract class HitObjectComposer : CompositeDrawable + [Cached(Type = typeof(IPlacementHandler))] + public abstract class HitObjectComposer : HitObjectComposer, IPlacementHandler + where TObject : HitObject { - public IEnumerable HitObjects => DrawableRuleset.Playfield.AllHitObjects; + protected IRulesetConfigManager Config { get; private set; } + protected DrawableEditRuleset DrawableRuleset { get; private set; } protected readonly Ruleset Ruleset; - protected readonly IBindable Beatmap = new Bindable(); - - protected IRulesetConfigManager Config { get; private set; } - - private readonly List layerContainers = new List(); - - protected DrawableEditRuleset DrawableRuleset { get; private set; } + private IBindable workingBeatmap; + private Beatmap playableBeatmap; + private EditorBeatmap editorBeatmap; + private IBeatmapProcessor beatmapProcessor; private BlueprintContainer blueprintContainer; + private readonly List layerContainers = new List(); private InputManager inputManager; - internal HitObjectComposer(Ruleset ruleset) + protected HitObjectComposer(Ruleset ruleset) { Ruleset = ruleset; - RelativeSizeAxes = Axes.Both; } [BackgroundDependencyLoader] - private void load(IBindable beatmap, IFrameBasedClock framedClock) + private void load(IFrameBasedClock framedClock) { - Beatmap.BindTo(beatmap); - try { - DrawableRuleset = CreateDrawableRuleset(); - DrawableRuleset.Clock = framedClock; + DrawableRuleset = new DrawableEditRuleset(CreateDrawableRuleset(Ruleset, workingBeatmap.Value, Array.Empty())) + { + Clock = framedClock + }; } catch (Exception e) { @@ -120,6 +120,26 @@ namespace osu.Game.Rulesets.Edit toolboxCollection.Items[0].Select(); } + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + workingBeatmap = parent.Get>().GetBoundCopy(); + playableBeatmap = (Beatmap)workingBeatmap.Value.GetPlayableBeatmap(Ruleset.RulesetInfo, Array.Empty()); + + beatmapProcessor = Ruleset.CreateBeatmapProcessor(playableBeatmap); + + editorBeatmap = new EditorBeatmap(playableBeatmap); + editorBeatmap.HitObjectAdded += addHitObject; + editorBeatmap.HitObjectRemoved += removeHitObject; + + var dependencies = new DependencyContainer(parent); + dependencies.CacheAs(editorBeatmap); + dependencies.CacheAs>(editorBeatmap); + + Config = dependencies.Get().GetConfigFor(Ruleset); + + return base.CreateChildDependencies(dependencies); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -127,16 +147,6 @@ namespace osu.Game.Rulesets.Edit inputManager = GetContainingInputManager(); } - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - - dependencies.CacheAs(this); - Config = dependencies.Get().GetConfigFor(Ruleset); - - return dependencies; - } - protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -150,58 +160,6 @@ namespace osu.Game.Rulesets.Edit }); } - /// - /// Whether the user's cursor is currently in an area of the that is valid for placement. - /// - public virtual bool CursorInPlacementArea => DrawableRuleset.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position); - - internal abstract DrawableEditRuleset CreateDrawableRuleset(); - - protected abstract IReadOnlyList CompositionTools { get; } - - /// - /// Creates a for a specific . - /// - /// The to create the overlay for. - public virtual SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => null; - - /// - /// Creates a which outlines s and handles movement of selections. - /// - public virtual SelectionHandler CreateSelectionHandler() => new SelectionHandler(); - } - - [Cached(Type = typeof(IPlacementHandler))] - public abstract class HitObjectComposer : HitObjectComposer, IPlacementHandler - where TObject : HitObject - { - private Beatmap playableBeatmap; - private EditorBeatmap editorBeatmap; - private IBeatmapProcessor beatmapProcessor; - - protected HitObjectComposer(Ruleset ruleset) - : base(ruleset) - { - } - - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var workingBeatmap = parent.Get>(); - playableBeatmap = (Beatmap)workingBeatmap.Value.GetPlayableBeatmap(Ruleset.RulesetInfo, Array.Empty()); - - beatmapProcessor = Ruleset.CreateBeatmapProcessor(playableBeatmap); - - editorBeatmap = new EditorBeatmap(playableBeatmap); - editorBeatmap.HitObjectAdded += addHitObject; - editorBeatmap.HitObjectRemoved += removeHitObject; - - var dependencies = new DependencyContainer(parent); - dependencies.CacheAs(editorBeatmap); - dependencies.CacheAs>(editorBeatmap); - - return base.CreateChildDependencies(dependencies); - } - private void addHitObject(HitObject hitObject) { beatmapProcessor?.PreProcess(); @@ -215,8 +173,10 @@ namespace osu.Game.Rulesets.Edit beatmapProcessor?.PostProcess(); } - internal override DrawableEditRuleset CreateDrawableRuleset() - => new DrawableEditRuleset(CreateDrawableRuleset(Ruleset, Beatmap.Value, Array.Empty())); + public override IEnumerable HitObjects => DrawableRuleset.Playfield.AllHitObjects; + public override bool CursorInPlacementArea => DrawableRuleset.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position); + + protected abstract IReadOnlyList CompositionTools { get; } protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods); @@ -239,4 +199,34 @@ namespace osu.Game.Rulesets.Edit } } } + + [Cached(typeof(HitObjectComposer))] + public abstract class HitObjectComposer : CompositeDrawable + { + internal HitObjectComposer() + { + RelativeSizeAxes = Axes.Both; + } + + /// + /// All the s. + /// + public abstract IEnumerable HitObjects { get; } + + /// + /// Whether the user's cursor is currently in an area of the that is valid for placement. + /// + public abstract bool CursorInPlacementArea { get; } + + /// + /// Creates a for a specific . + /// + /// The to create the overlay for. + public virtual SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => null; + + /// + /// Creates a which outlines s and handles movement of selections. + /// + public virtual SelectionHandler CreateSelectionHandler() => new SelectionHandler(); + } } From 87e28ab1f97869de9fef85deac8ec2be1072f0e0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Aug 2019 18:12:29 +0900 Subject: [PATCH 2425/2854] Remove non-generic DrawableEditRuleset --- .../Edit/ManiaHitObjectComposer.cs | 12 ++++----- osu.Game/Rulesets/Edit/DrawableEditRuleset.cs | 25 +++++-------------- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 22 ++++++++-------- 3 files changed, 23 insertions(+), 36 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 2729621ab3..3a28149946 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Edit [Cached(Type = typeof(IManiaHitObjectComposer))] public class ManiaHitObjectComposer : HitObjectComposer, IManiaHitObjectComposer { - protected new DrawableManiaEditRuleset DrawableRuleset { get; private set; } + private DrawableManiaEditRuleset drawableRuleset; public ManiaHitObjectComposer(Ruleset ruleset) : base(ruleset) @@ -33,23 +33,23 @@ namespace osu.Game.Rulesets.Mania.Edit /// /// The screen-space position. /// The column which intersects with . - public Column ColumnAt(Vector2 screenSpacePosition) => DrawableRuleset.GetColumnByPosition(screenSpacePosition); + public Column ColumnAt(Vector2 screenSpacePosition) => drawableRuleset.GetColumnByPosition(screenSpacePosition); private DependencyContainer dependencies; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - public int TotalColumns => ((ManiaPlayfield)DrawableRuleset.Playfield).TotalColumns; + public int TotalColumns => ((ManiaPlayfield)drawableRuleset.Playfield).TotalColumns; protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) { - DrawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods); + drawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods); // This is the earliest we can cache the scrolling info to ourselves, before masks are added to the hierarchy and inject it - dependencies.CacheAs(DrawableRuleset.ScrollingInfo); + dependencies.CacheAs(drawableRuleset.ScrollingInfo); - return DrawableRuleset; + return drawableRuleset; } protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] diff --git a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs b/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs index a12e4ba3ab..a9f0dd4197 100644 --- a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs +++ b/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs @@ -11,27 +11,10 @@ using osu.Game.Screens.Edit; namespace osu.Game.Rulesets.Edit { - public abstract class DrawableEditRuleset : CompositeDrawable - { - /// - /// The contained by this . - /// - public abstract Playfield Playfield { get; } - - public abstract PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer(); - - internal DrawableEditRuleset() - { - RelativeSizeAxes = Axes.Both; - } - } - - public class DrawableEditRuleset : DrawableEditRuleset + public class DrawableEditRuleset : CompositeDrawable where TObject : HitObject { - public override Playfield Playfield => drawableRuleset.Playfield; - - public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => drawableRuleset.CreatePlayfieldAdjustmentContainer(); + public Playfield Playfield => drawableRuleset.Playfield; private readonly DrawableRuleset drawableRuleset; @@ -42,6 +25,8 @@ namespace osu.Game.Rulesets.Edit { this.drawableRuleset = drawableRuleset; + RelativeSizeAxes = Axes.Both; + InternalChild = drawableRuleset; } @@ -76,6 +61,8 @@ namespace osu.Game.Rulesets.Edit drawableRuleset.Playfield.PostProcess(); } + public PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => drawableRuleset.CreatePlayfieldAdjustmentContainer(); + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 239ec572b2..35c5a63ef1 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -30,7 +30,6 @@ namespace osu.Game.Rulesets.Edit where TObject : HitObject { protected IRulesetConfigManager Config { get; private set; } - protected DrawableEditRuleset DrawableRuleset { get; private set; } protected readonly Ruleset Ruleset; @@ -39,6 +38,7 @@ namespace osu.Game.Rulesets.Edit private EditorBeatmap editorBeatmap; private IBeatmapProcessor beatmapProcessor; + private DrawableEditRuleset drawableRuleset; private BlueprintContainer blueprintContainer; private readonly List layerContainers = new List(); @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Edit { try { - DrawableRuleset = new DrawableEditRuleset(CreateDrawableRuleset(Ruleset, workingBeatmap.Value, Array.Empty())) + drawableRuleset = new DrawableEditRuleset(CreateDrawableRuleset(Ruleset, workingBeatmap.Value, Array.Empty())) { Clock = framedClock }; @@ -66,10 +66,10 @@ namespace osu.Game.Rulesets.Edit return; } - var layerBelowRuleset = DrawableRuleset.CreatePlayfieldAdjustmentContainer(); + var layerBelowRuleset = drawableRuleset.CreatePlayfieldAdjustmentContainer(); layerBelowRuleset.Child = new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both }; - var layerAboveRuleset = DrawableRuleset.CreatePlayfieldAdjustmentContainer(); + var layerAboveRuleset = drawableRuleset.CreatePlayfieldAdjustmentContainer(); layerAboveRuleset.Child = blueprintContainer = new BlueprintContainer(); layerContainers.Add(layerBelowRuleset); @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Edit Children = new Drawable[] { layerBelowRuleset, - DrawableRuleset, + drawableRuleset, layerAboveRuleset } } @@ -153,10 +153,10 @@ namespace osu.Game.Rulesets.Edit layerContainers.ForEach(l => { - l.Anchor = DrawableRuleset.Playfield.Anchor; - l.Origin = DrawableRuleset.Playfield.Origin; - l.Position = DrawableRuleset.Playfield.Position; - l.Size = DrawableRuleset.Playfield.Size; + l.Anchor = drawableRuleset.Playfield.Anchor; + l.Origin = drawableRuleset.Playfield.Origin; + l.Position = drawableRuleset.Playfield.Position; + l.Size = drawableRuleset.Playfield.Size; }); } @@ -173,8 +173,8 @@ namespace osu.Game.Rulesets.Edit beatmapProcessor?.PostProcess(); } - public override IEnumerable HitObjects => DrawableRuleset.Playfield.AllHitObjects; - public override bool CursorInPlacementArea => DrawableRuleset.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position); + public override IEnumerable HitObjects => drawableRuleset.Playfield.AllHitObjects; + public override bool CursorInPlacementArea => drawableRuleset.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position); protected abstract IReadOnlyList CompositionTools { get; } From 714ee312da0338bb768ac543ad5ffb4cf43328f0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Aug 2019 18:20:43 +0900 Subject: [PATCH 2426/2854] Rename DrawableEditRuleset -> DrawableEditRulesetWrapper --- ...leset.cs => DrawableEditRulesetWrapper.cs} | 7 ++++-- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 22 +++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) rename osu.Game/Rulesets/Edit/{DrawableEditRuleset.cs => DrawableEditRulesetWrapper.cs} (85%) diff --git a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs b/osu.Game/Rulesets/Edit/DrawableEditRulesetWrapper.cs similarity index 85% rename from osu.Game/Rulesets/Edit/DrawableEditRuleset.cs rename to osu.Game/Rulesets/Edit/DrawableEditRulesetWrapper.cs index a9f0dd4197..af565f8896 100644 --- a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs +++ b/osu.Game/Rulesets/Edit/DrawableEditRulesetWrapper.cs @@ -11,7 +11,10 @@ using osu.Game.Screens.Edit; namespace osu.Game.Rulesets.Edit { - public class DrawableEditRuleset : CompositeDrawable + /// + /// A wrapper for a . Handles adding visual representations of s to the underlying . + /// + internal class DrawableEditRulesetWrapper : CompositeDrawable where TObject : HitObject { public Playfield Playfield => drawableRuleset.Playfield; @@ -21,7 +24,7 @@ namespace osu.Game.Rulesets.Edit [Resolved] private IEditorBeatmap beatmap { get; set; } - public DrawableEditRuleset(DrawableRuleset drawableRuleset) + public DrawableEditRulesetWrapper(DrawableRuleset drawableRuleset) { this.drawableRuleset = drawableRuleset; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 35c5a63ef1..d7ee63d4a4 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Edit private EditorBeatmap editorBeatmap; private IBeatmapProcessor beatmapProcessor; - private DrawableEditRuleset drawableRuleset; + private DrawableEditRulesetWrapper drawableRulesetWrapper; private BlueprintContainer blueprintContainer; private readonly List layerContainers = new List(); @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Edit { try { - drawableRuleset = new DrawableEditRuleset(CreateDrawableRuleset(Ruleset, workingBeatmap.Value, Array.Empty())) + drawableRulesetWrapper = new DrawableEditRulesetWrapper(CreateDrawableRuleset(Ruleset, workingBeatmap.Value, Array.Empty())) { Clock = framedClock }; @@ -66,10 +66,10 @@ namespace osu.Game.Rulesets.Edit return; } - var layerBelowRuleset = drawableRuleset.CreatePlayfieldAdjustmentContainer(); + var layerBelowRuleset = drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer(); layerBelowRuleset.Child = new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both }; - var layerAboveRuleset = drawableRuleset.CreatePlayfieldAdjustmentContainer(); + var layerAboveRuleset = drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer(); layerAboveRuleset.Child = blueprintContainer = new BlueprintContainer(); layerContainers.Add(layerBelowRuleset); @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Edit Children = new Drawable[] { layerBelowRuleset, - drawableRuleset, + drawableRulesetWrapper, layerAboveRuleset } } @@ -153,10 +153,10 @@ namespace osu.Game.Rulesets.Edit layerContainers.ForEach(l => { - l.Anchor = drawableRuleset.Playfield.Anchor; - l.Origin = drawableRuleset.Playfield.Origin; - l.Position = drawableRuleset.Playfield.Position; - l.Size = drawableRuleset.Playfield.Size; + l.Anchor = drawableRulesetWrapper.Playfield.Anchor; + l.Origin = drawableRulesetWrapper.Playfield.Origin; + l.Position = drawableRulesetWrapper.Playfield.Position; + l.Size = drawableRulesetWrapper.Playfield.Size; }); } @@ -173,8 +173,8 @@ namespace osu.Game.Rulesets.Edit beatmapProcessor?.PostProcess(); } - public override IEnumerable HitObjects => drawableRuleset.Playfield.AllHitObjects; - public override bool CursorInPlacementArea => drawableRuleset.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position); + public override IEnumerable HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects; + public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position); protected abstract IReadOnlyList CompositionTools { get; } From d3030831793dc68f7d8e04fce2d05209e6fab796 Mon Sep 17 00:00:00 2001 From: StanR Date: Thu, 29 Aug 2019 12:29:31 +0300 Subject: [PATCH 2427/2854] Update to match api --- .../Online/TestSceneBeatmapSetOverlay.cs | 4 +- osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs | 51 +++---------------- .../API/Requests/Responses/APIBeatmapSet.cs | 4 +- osu.Game/Overlays/BeatmapSet/Info.cs | 4 +- 4 files changed, 12 insertions(+), 51 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 2d918442eb..ee9e088dcc 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -176,8 +176,8 @@ namespace osu.Game.Tests.Visual.Online HasVideo = true, HasStoryboard = true, Covers = new BeatmapSetOnlineCovers(), - Language = BeatmapSetOnlineLanguage.English, - Genre = BeatmapSetOnlineGenre.Rock, + Language = new BeatmapSetOnlineLanguage { Id = 3, Name = "English" }, + Genre = new BeatmapSetOnlineGenre { Id = 4, Name = "Rock" }, }, Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, Beatmaps = new List diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index decf2d10db..500e42096c 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -88,55 +88,16 @@ namespace osu.Game.Beatmaps public BeatmapSetOnlineLanguage Language { get; set; } } - public enum BeatmapSetOnlineGenre + public class BeatmapSetOnlineGenre { - [Description("Any")] - Any = 0, - - [Description("Unspecified")] - Unspecified = 1, - - [Description("Video Game")] - VideoGame = 2, - - [Description("Anime")] - Anime = 3, - - [Description("Rock")] - Rock = 4, - - [Description("Pop")] - Pop = 5, - - [Description("Other")] - Other = 6, - - [Description("Novelty")] - Novelty = 7, - - // genre_id 8 doesn't exist - - [Description("Hip-Hop")] - HipHop = 9, - - [Description("Electronic")] - Electronic = 10 + public int Id { get; set; } + public string Name { get; set; } } - public enum BeatmapSetOnlineLanguage + public class BeatmapSetOnlineLanguage { - Any, - Other, - English, - Japanese, - Chinese, - Instrumental, - Korean, - French, - German, - Swedish, - Spanish, - Italian + public int Id { get; set; } + public string Name { get; set; } } public class BeatmapSetOnlineCovers diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 1526cccf31..1ca14256e5 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -69,10 +69,10 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"availability")] private BeatmapSetOnlineAvailability availability { get; set; } - [JsonProperty(@"genre_id")] + [JsonProperty(@"genre")] private BeatmapSetOnlineGenre genre { get; set; } - [JsonProperty(@"language_id")] + [JsonProperty(@"language")] private BeatmapSetOnlineLanguage language { get; set; } [JsonProperty(@"beatmaps")] diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index 0e4e9db948..9c5cce89f9 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -133,8 +133,8 @@ namespace osu.Game.Overlays.BeatmapSet { source.Text = b.NewValue?.Metadata.Source ?? string.Empty; tags.Text = b.NewValue?.Metadata.Tags ?? string.Empty; - genre.Text = (b.NewValue?.OnlineInfo.Genre ?? BeatmapSetOnlineGenre.Unspecified).GetDescription(); - language.Text = (b.NewValue?.OnlineInfo.Language ?? BeatmapSetOnlineLanguage.Other).ToString(); + genre.Text = b.NewValue?.OnlineInfo.Genre.Name ?? "Unspecified"; + language.Text = b.NewValue?.OnlineInfo.Language.Name ?? "Other"; }; } From 68ee7346b213b099e6eceed2cd8478181e15b484 Mon Sep 17 00:00:00 2001 From: StanR Date: Thu, 29 Aug 2019 12:49:44 +0300 Subject: [PATCH 2428/2854] Remove usings --- osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs | 1 - osu.Game/Overlays/BeatmapSet/Info.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index 500e42096c..06dee4d3f5 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.ComponentModel; using Newtonsoft.Json; namespace osu.Game.Beatmaps diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index 9c5cce89f9..f17b44c8f7 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -3,7 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; From c1c1c7874bab0786ffeaa6f3a99e63c9b2317853 Mon Sep 17 00:00:00 2001 From: StanR Date: Thu, 29 Aug 2019 13:25:05 +0300 Subject: [PATCH 2429/2854] Nullcheck --- osu.Game/Overlays/BeatmapSet/Info.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index f17b44c8f7..72db03a5a6 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -132,8 +132,8 @@ namespace osu.Game.Overlays.BeatmapSet { source.Text = b.NewValue?.Metadata.Source ?? string.Empty; tags.Text = b.NewValue?.Metadata.Tags ?? string.Empty; - genre.Text = b.NewValue?.OnlineInfo.Genre.Name ?? "Unspecified"; - language.Text = b.NewValue?.OnlineInfo.Language.Name ?? "Other"; + genre.Text = b.NewValue?.OnlineInfo?.Genre?.Name ?? "Unspecified"; + language.Text = b.NewValue?.OnlineInfo?.Language?.Name ?? "Other"; }; } From 6ab2b20c70dcea4dd98ec7c7ebc9f477170ea95d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Aug 2019 19:38:44 +0900 Subject: [PATCH 2430/2854] Add an interface for working beatmaps --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 +- .../UI/DrawableCatchRuleset.cs | 4 +- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- .../UI/DrawableManiaRuleset.cs | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- .../UI/DrawableOsuRuleset.cs | 2 +- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- .../UI/DrawableTaikoRuleset.cs | 2 +- .../TestSceneDrawableScrollingRuleset.cs | 4 +- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/IWorkingBeatmap.cs | 61 +++++++++++++++++++ osu.Game/Beatmaps/WorkingBeatmap.cs | 16 +---- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 12 ++-- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Rulesets/UI/DrawableRuleset.cs | 2 +- .../UI/Scrolling/DrawableScrollingRuleset.cs | 2 +- 16 files changed, 85 insertions(+), 34 deletions(-) create mode 100644 osu.Game/Beatmaps/IWorkingBeatmap.cs diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 6f1a7873ec..71e05083be 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch { public class CatchRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableCatchRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableCatchRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap); diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index f48b84e344..6b7f00c5d0 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -25,11 +25,11 @@ namespace osu.Game.Rulesets.Catch.UI protected override bool UserScrollSpeedAdjustment => false; - public DrawableCatchRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) + public DrawableCatchRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { Direction.Value = ScrollingDirection.Down; - TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450); + TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450); } public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this); diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 8966b5058f..0de86c2149 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania { public class ManiaRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableManiaRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableManiaRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap); public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score); diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 0718de2c7d..f26526fe70 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.UI private readonly Bindable configDirection = new Bindable(); - public DrawableManiaRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) + public DrawableManiaRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { // Generate the bar lines diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index d50d4f401c..3bbfc25d22 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu { public class OsuRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableOsuRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableOsuRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap); diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index d185d7d4c9..aa61fb6922 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.UI { protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config; - public DrawableOsuRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) + public DrawableOsuRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 83356b77c2..6d0a5eb1e1 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko { public class TaikoRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableTaikoRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableTaikoRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap); public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index ec3a56e9c7..b03bea578e 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.UI protected override bool UserScrollSpeedAdjustment => false; - public DrawableTaikoRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) + public DrawableTaikoRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { Direction.Value = ScrollingDirection.Left; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index ee11fc0d06..60ace8ea69 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -174,7 +174,7 @@ namespace osu.Game.Tests.Visual.Gameplay public override IEnumerable GetModsFor(ModType type) => throw new NotImplementedException(); - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods) => new TestDrawableScrollingRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new TestDrawableScrollingRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TestBeatmapConverter(beatmap); @@ -193,7 +193,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping; - public TestDrawableScrollingRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) + public TestDrawableScrollingRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { TimeRange.Value = time_range; diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 3a4c677bd1..29ade24328 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -54,7 +54,7 @@ namespace osu.Game.Beatmaps { public override IEnumerable GetModsFor(ModType type) => new Mod[] { }; - public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods) + public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) { throw new NotImplementedException(); } diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs new file mode 100644 index 0000000000..aea3751bb5 --- /dev/null +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -0,0 +1,61 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Textures; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.UI; +using osu.Game.Skinning; +using osu.Game.Storyboards; + +namespace osu.Game.Beatmaps +{ + public interface IWorkingBeatmap + { + /// + /// Retrieves the which this represents. + /// + IBeatmap Beatmap { get; } + + /// + /// Retrieves the background for this . + /// + Texture Background { get; } + + /// + /// Retrieves the audio track for this . + /// + Track Track { get; } + + /// + /// Retrieves the for the of this . + /// + Waveform Waveform { get; } + + /// + /// Retrieves the which this provides. + /// + Storyboard Storyboard { get; } + + /// + /// Retrieves the which this provides. + /// + Skin Skin { get; } + + /// + /// Constructs a playable from using the applicable converters for a specific . + /// + /// The returned is in a playable state - all and s + /// have been applied, and s have been fully constructed. + /// + /// + /// The to create a playable for. + /// The s to apply to the . + /// The converted . + /// If could not be converted to . + IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods); + } +} diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 8605caa5fe..1cce3dc5fe 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -16,14 +16,13 @@ using osu.Framework.Audio; using osu.Framework.Statistics; using osu.Game.IO.Serialization; using osu.Game.Rulesets; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.UI; using osu.Game.Skinning; namespace osu.Game.Beatmaps { - public abstract class WorkingBeatmap : IDisposable + public abstract class WorkingBeatmap : IWorkingBeatmap, IDisposable { public readonly BeatmapInfo BeatmapInfo; @@ -97,18 +96,7 @@ namespace osu.Game.Beatmaps /// The applicable . protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap); - /// - /// Constructs a playable from using the applicable converters for a specific . - /// - /// The returned is in a playable state - all and s - /// have been applied, and s have been fully constructed. - /// - /// - /// The to create a playable for. - /// The s to apply to the . - /// The converted . - /// If could not be converted to . - public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods) + public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods) { var rulesetInstance = ruleset.CreateInstance(); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index d7ee63d4a4..fc324d7021 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Edit protected readonly Ruleset Ruleset; - private IBindable workingBeatmap; + private IWorkingBeatmap workingBeatmap; private Beatmap playableBeatmap; private EditorBeatmap editorBeatmap; private IBeatmapProcessor beatmapProcessor; @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Edit { try { - drawableRulesetWrapper = new DrawableEditRulesetWrapper(CreateDrawableRuleset(Ruleset, workingBeatmap.Value, Array.Empty())) + drawableRulesetWrapper = new DrawableEditRulesetWrapper(CreateDrawableRuleset(Ruleset, workingBeatmap, Array.Empty())) { Clock = framedClock }; @@ -122,8 +122,10 @@ namespace osu.Game.Rulesets.Edit protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - workingBeatmap = parent.Get>().GetBoundCopy(); - playableBeatmap = (Beatmap)workingBeatmap.Value.GetPlayableBeatmap(Ruleset.RulesetInfo, Array.Empty()); + var parentWorkingBeatmap = parent.Get>().Value; + + playableBeatmap = (Beatmap)parentWorkingBeatmap.GetPlayableBeatmap(Ruleset.RulesetInfo, Array.Empty()); + workingBeatmap = new EditorWorkingBeatmap(playableBeatmap, parentWorkingBeatmap); beatmapProcessor = Ruleset.CreateBeatmapProcessor(playableBeatmap); @@ -178,7 +180,7 @@ namespace osu.Game.Rulesets.Edit protected abstract IReadOnlyList CompositionTools { get; } - protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods); + protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods); public void BeginPlacement(HitObject hitObject) { diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 42b1322cae..b089840f1e 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets /// The s to apply. /// Unable to successfully load the beatmap to be usable with this ruleset. /// - public abstract DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods); + public abstract DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods); /// /// Creates a to convert a to one that is applicable for this . diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index ccfd89adca..021bd515b5 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.UI /// The ruleset being represented. /// The beatmap to create the hit renderer for. /// The s to apply. - protected DrawableRuleset(Ruleset ruleset, WorkingBeatmap workingBeatmap, IReadOnlyList mods) + protected DrawableRuleset(Ruleset ruleset, IWorkingBeatmap workingBeatmap, IReadOnlyList mods) : base(ruleset) { if (workingBeatmap == null) diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index c1a4c9520e..64e491858b 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.UI.Scrolling [Cached(Type = typeof(IScrollingInfo))] private readonly LocalScrollingInfo scrollingInfo; - protected DrawableScrollingRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) + protected DrawableScrollingRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { scrollingInfo = new LocalScrollingInfo(); From 6641811125b938aa1511325938504f4be4e876a0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Aug 2019 19:40:56 +0900 Subject: [PATCH 2431/2854] Add EditorWorkingBeatmap for reuse of the playable beatmap --- .../Edit/DrawableManiaEditRuleset.cs | 2 +- .../Edit/ManiaHitObjectComposer.cs | 2 +- .../Edit/DrawableOsuEditRuleset.cs | 2 +- .../Edit/OsuHitObjectComposer.cs | 2 +- osu.Game/Screens/Edit/EditorWorkingBeatmap.cs | 42 +++++++++++++++++++ 5 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Screens/Edit/EditorWorkingBeatmap.cs diff --git a/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs b/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs index e5f379f608..97d8aaa052 100644 --- a/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs +++ b/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Edit { public new IScrollingInfo ScrollingInfo => base.ScrollingInfo; - public DrawableManiaEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) + public DrawableManiaEditRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 3a28149946..0bfe6f9517 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Edit public int TotalColumns => ((ManiaPlayfield)drawableRuleset.Playfield).TotalColumns; - protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) + protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) { drawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods); diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs index bcb6099cfb..cc08d356f9 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Edit { public class DrawableOsuEditRuleset : DrawableOsuRuleset { - public DrawableOsuEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) + public DrawableOsuEditRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index c5452ae0aa..1c040e9dee 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit { } - protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList mods) + protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableOsuEditRuleset(ruleset, beatmap, mods); protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] diff --git a/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs b/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs new file mode 100644 index 0000000000..8bec68596c --- /dev/null +++ b/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Skinning; +using osu.Game.Storyboards; + +namespace osu.Game.Screens.Edit +{ + public class EditorWorkingBeatmap : IWorkingBeatmap + where TObject : HitObject + { + private readonly Beatmap playableBeatmap; + private readonly WorkingBeatmap workingBeatmap; + + public EditorWorkingBeatmap(Beatmap playableBeatmap, WorkingBeatmap workingBeatmap) + { + this.playableBeatmap = playableBeatmap; + this.workingBeatmap = workingBeatmap; + } + + public IBeatmap Beatmap => workingBeatmap.Beatmap; + + public Texture Background => workingBeatmap.Background; + + public Track Track => workingBeatmap.Track; + + public Waveform Waveform => workingBeatmap.Waveform; + + public Storyboard Storyboard => workingBeatmap.Storyboard; + + public Skin Skin => workingBeatmap.Skin; + + public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods) => playableBeatmap; + } +} From ae0a5504d705fb4ed1dd95751e71d581f40cf636 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Aug 2019 19:43:33 +0900 Subject: [PATCH 2432/2854] Revert unnecessary change --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 1cce3dc5fe..90dde4239c 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -96,7 +96,7 @@ namespace osu.Game.Beatmaps /// The applicable . protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap); - public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods) + public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods) { var rulesetInstance = ruleset.CreateInstance(); From ec6a40af339a78b2d172b69089ea77b6afca8c5e Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 29 Aug 2019 15:32:21 +0300 Subject: [PATCH 2433/2854] Add adjustments on channel creation if there is --- osu.Game/Skinning/SkinnableSound.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index cb511fc775..e1d9b231dc 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -15,6 +15,9 @@ namespace osu.Game.Skinning public class SkinnableSound : SkinReloadableDrawable { private readonly ISampleInfo[] hitSamples; + + private readonly List<(AdjustableProperty, BindableDouble)> adjustments = new List<(AdjustableProperty, BindableDouble)>(); + private SampleChannel[] channels; private AudioManager audio; @@ -51,8 +54,17 @@ namespace osu.Game.Skinning public void Play() => channels?.ForEach(c => c.Play()); public void Stop() => channels?.ForEach(c => c.Stop()); - public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => channels?.ForEach(c => c.AddAdjustment(type, adjustBindable)); - public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => channels?.ForEach(c => c.RemoveAdjustment(type, adjustBindable)); + public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) + { + adjustments.Add((type, adjustBindable)); + channels?.ForEach(c => c.AddAdjustment(type, adjustBindable)); + } + + public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) + { + adjustments.Remove((type, adjustBindable)); + channels?.ForEach(c => c.RemoveAdjustment(type, adjustBindable)); + } public override bool IsPresent => Scheduler.HasPendingTasks; @@ -71,6 +83,9 @@ namespace osu.Game.Skinning { ch.Looping = looping; ch.Volume.Value = s.Volume / 100.0; + + foreach (var adjustment in adjustments) + ch.AddAdjustment(adjust.Item1, adjust.Item2); } return ch; From 06224a7d4ee9921180946040f62811d75689a406 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 29 Aug 2019 15:38:33 +0300 Subject: [PATCH 2434/2854] Fix build issue --- osu.Game/Skinning/SkinnableSound.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index e1d9b231dc..bf647baeec 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -85,7 +85,7 @@ namespace osu.Game.Skinning ch.Volume.Value = s.Volume / 100.0; foreach (var adjustment in adjustments) - ch.AddAdjustment(adjust.Item1, adjust.Item2); + ch.AddAdjustment(adjustment.Item1, adjustment.Item2); } return ch; From 3f500131d483b5ebbdc60fb4c905de2e754965d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 11:33:50 +0900 Subject: [PATCH 2435/2854] Add basic xmldoc --- osu.Game/Screens/Edit/EditorWorkingBeatmap.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs b/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs index 8bec68596c..45fca493a2 100644 --- a/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs @@ -13,6 +13,10 @@ using osu.Game.Storyboards; namespace osu.Game.Screens.Edit { + /// + /// Encapsulates a while providing an overridden . + /// + /// public class EditorWorkingBeatmap : IWorkingBeatmap where TObject : HitObject { From bfbec067b13ba7546b1ece8f460b4c433200564c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 11:54:36 +0900 Subject: [PATCH 2436/2854] Remove remnants of user skin PR --- osu.Game/Skinning/SkinManager.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 0e40eb5376..a713933c6e 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -60,9 +60,6 @@ namespace osu.Game.Skinning }; } - private Skin createIfNotExisting(SkinInfo skinInfo) => - GetSkin(Query(s => s.Name == skinInfo.Name) ?? Import(skinInfo).Result); - protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osk"; /// @@ -110,9 +107,6 @@ namespace osu.Game.Skinning /// A instance correlating to the provided . public Skin GetSkin(SkinInfo skinInfo) { - if (skinInfo == null) - return null; - if (skinInfo == SkinInfo.Default) return new DefaultSkin(); From 8fe37d0c43aec1182f9b437ba16cce2103a6a526 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 12:35:44 +0900 Subject: [PATCH 2437/2854] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 0f6e32d664..3854cab34f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -60,7 +60,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d791909372..5563f9efeb 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,7 +14,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 9fc472bf40..a430ff59ab 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -117,7 +117,7 @@ - + From 9c622680e32a5f21777ffb4aee13acbee1a7fe86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 12:35:53 +0900 Subject: [PATCH 2438/2854] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 3854cab34f..2c3c8bcaad 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -61,6 +61,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5563f9efeb..8e6ce2d1ba 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index a430ff59ab..47cc6ec97a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -118,8 +118,8 @@ - - + + From 84e474826817f394594a0973016492d3bda740d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 12:59:58 +0900 Subject: [PATCH 2439/2854] Remove duplicate getAnimation function and improve namespacing --- osu.Game.Rulesets.Osu/OsuLegacySkin.cs | 236 ------------------ osu.Game.Rulesets.Osu/OsuRuleset.cs | 1 + .../Skinning/LegacyMainCirclePiece.cs | 81 ++++++ .../Skinning/LegacySliderBall.cs | 44 ++++ .../Skinning/OsuLegacySkin.cs | 97 +++++++ osu.Game/Beatmaps/IWorkingBeatmap.cs | 2 +- osu.Game/Screens/Edit/EditorWorkingBeatmap.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 40 +-- osu.Game/Skinning/LegacySkinExtensions.cs | 53 ++++ 9 files changed, 279 insertions(+), 277 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/OsuLegacySkin.cs create mode 100644 osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs create mode 100644 osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs create mode 100644 osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs create mode 100644 osu.Game/Skinning/LegacySkinExtensions.cs diff --git a/osu.Game.Rulesets.Osu/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/OsuLegacySkin.cs deleted file mode 100644 index d4b00ab911..0000000000 --- a/osu.Game.Rulesets.Osu/OsuLegacySkin.cs +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osu.Framework.Allocation; -using osu.Framework.Audio.Sample; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Animations; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Game.Audio; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Skinning; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Rulesets.Osu -{ - public class OsuLegacySkin : ISkin - { - private readonly ISkin source; - - private Lazy configuration; - - private Lazy hasHitCircle; - - /// - /// On osu-stable, hitcircles have 5 pixels of transparent padding on each side to allow for shadows etc. - /// Their hittable area is 128px, but the actual circle portion is 118px. - /// We must account for some gameplay elements such as slider bodies, where this padding is not present. - /// - private const float legacy_circle_radius = 64 - 5; - - public OsuLegacySkin(ISkinSource source) - { - this.source = source; - - source.SourceChanged += sourceChanged; - sourceChanged(); - } - - private void sourceChanged() - { - // these need to be lazy in order to ensure they aren't called before the dependencies have been loaded into our source. - configuration = new Lazy(() => - { - var config = new SkinConfiguration(); - if (hasHitCircle.Value) - config.SliderPathRadius = legacy_circle_radius; - - // defaults should only be applied for non-beatmap skins (which are parsed via this constructor). - config.CustomColours["SliderBall"] = - source.GetValue(s => s.CustomColours.TryGetValue("SliderBall", out var val) ? val : (Color4?)null) - ?? new Color4(2, 170, 255, 255); - - return config; - }); - - hasHitCircle = new Lazy(() => source.GetTexture("hitcircle") != null); - } - - private const double default_frame_time = 1000 / 60d; - - public Drawable GetDrawableComponent(string componentName) - { - switch (componentName) - { - case "Play/osu/sliderball": - var sliderBallContent = getAnimation("sliderb", true, true, ""); - - if (sliderBallContent != null) - { - var size = sliderBallContent.Size; - - sliderBallContent.RelativeSizeAxes = Axes.Both; - sliderBallContent.Size = Vector2.One; - - return new LegacySliderBall(sliderBallContent) - { - Size = size - }; - } - - return null; - - case "Play/osu/hitcircle": - if (hasHitCircle.Value) - return new LegacyMainCirclePiece(); - - return null; - } - - return null; - } - - private Drawable getAnimation(string componentName, bool animatable, bool looping, string animationSeparator = "-") - { - Texture texture; - - Texture getFrameTexture(int frame) => source.GetTexture($"{componentName}{animationSeparator}{frame}"); - - TextureAnimation animation = null; - - if (animatable) - { - for (int i = 0;; i++) - { - if ((texture = getFrameTexture(i)) == null) - break; - - if (animation == null) - animation = new TextureAnimation - { - DefaultFrameLength = default_frame_time, - Repeat = looping - }; - - animation.AddFrame(texture); - } - } - - if (animation != null) - return animation; - - if ((texture = source.GetTexture(componentName)) != null) - return new Sprite { Texture = texture }; - - return null; - } - - public Texture GetTexture(string componentName) => null; - - public SampleChannel GetSample(ISampleInfo sample) => null; - - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration - => configuration.Value is TConfiguration conf ? query.Invoke(conf) : default; - - public class LegacySliderBall : CompositeDrawable - { - private readonly Drawable animationContent; - - public LegacySliderBall(Drawable animationContent) - { - this.animationContent = animationContent; - } - - [BackgroundDependencyLoader] - private void load(ISkinSource skin, DrawableHitObject drawableObject) - { - animationContent.Colour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White; - - InternalChildren = new[] - { - new Sprite - { - Texture = skin.GetTexture("sliderb-nd"), - Colour = new Color4(5, 5, 5, 255), - }, - animationContent, - new Sprite - { - Texture = skin.GetTexture("sliderb-spec"), - Blending = BlendingParameters.Additive, - }, - }; - } - } - - public class LegacyMainCirclePiece : CompositeDrawable - { - public LegacyMainCirclePiece() - { - Size = new Vector2(128); - } - - private readonly IBindable state = new Bindable(); - - private readonly Bindable accentColour = new Bindable(); - - [BackgroundDependencyLoader] - private void load(DrawableHitObject drawableObject, ISkinSource skin) - { - Sprite hitCircleSprite; - - InternalChildren = new Drawable[] - { - hitCircleSprite = new Sprite - { - Texture = skin.GetTexture("hitcircle"), - Colour = drawableObject.AccentColour.Value, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - new SkinnableSpriteText("Play/osu/number-text", _ => new OsuSpriteText - { - Font = OsuFont.Numeric.With(size: 40), - UseFullGlyphHeight = false, - }, confineMode: ConfineMode.NoScaling) - { - Text = (((IHasComboInformation)drawableObject.HitObject).IndexInCurrentCombo + 1).ToString() - }, - new Sprite - { - Texture = skin.GetTexture("hitcircleoverlay"), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } - }; - - state.BindTo(drawableObject.State); - state.BindValueChanged(updateState, true); - - accentColour.BindTo(drawableObject.AccentColour); - accentColour.BindValueChanged(colour => hitCircleSprite.Colour = colour.NewValue, true); - } - - private void updateState(ValueChangedEvent state) - { - const double legacy_fade_duration = 240; - - switch (state.NewValue) - { - case ArmedState.Hit: - this.FadeOut(legacy_fade_duration, Easing.Out); - this.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); - break; - } - } - } - } -} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index e2c64bbedf..49676933e1 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -23,6 +23,7 @@ using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Difficulty; +using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Scoring; using osu.Game.Skinning; diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs new file mode 100644 index 0000000000..a7906ddd24 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -0,0 +1,81 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Skinning; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Skinning +{ + public class LegacyMainCirclePiece : CompositeDrawable + { + public LegacyMainCirclePiece() + { + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); + } + + private readonly IBindable state = new Bindable(); + + private readonly Bindable accentColour = new Bindable(); + + [BackgroundDependencyLoader] + private void load(DrawableHitObject drawableObject, ISkinSource skin) + { + Sprite hitCircleSprite; + + InternalChildren = new Drawable[] + { + hitCircleSprite = new Sprite + { + Texture = skin.GetTexture("hitcircle"), + Colour = drawableObject.AccentColour.Value, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new SkinnableSpriteText("Play/osu/number-text", _ => new OsuSpriteText + { + Font = OsuFont.Numeric.With(size: 40), + UseFullGlyphHeight = false, + }, confineMode: ConfineMode.NoScaling) + { + Text = (((IHasComboInformation)drawableObject.HitObject).IndexInCurrentCombo + 1).ToString() + }, + new Sprite + { + Texture = skin.GetTexture("hitcircleoverlay"), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }; + + state.BindTo(drawableObject.State); + state.BindValueChanged(updateState, true); + + accentColour.BindTo(drawableObject.AccentColour); + accentColour.BindValueChanged(colour => hitCircleSprite.Colour = colour.NewValue, true); + } + + private void updateState(ValueChangedEvent state) + { + const double legacy_fade_duration = 240; + + switch (state.NewValue) + { + case ArmedState.Hit: + this.FadeOut(legacy_fade_duration, Easing.Out); + this.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + break; + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs new file mode 100644 index 0000000000..ec838c596d --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Skinning; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Skinning +{ + public class LegacySliderBall : CompositeDrawable + { + private readonly Drawable animationContent; + + public LegacySliderBall(Drawable animationContent) + { + this.animationContent = animationContent; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin, DrawableHitObject drawableObject) + { + animationContent.Colour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White; + + InternalChildren = new[] + { + new Sprite + { + Texture = skin.GetTexture("sliderb-nd"), + Colour = new Color4(5, 5, 5, 255), + }, + animationContent, + new Sprite + { + Texture = skin.GetTexture("sliderb-spec"), + Blending = BlendingParameters.Additive, + }, + }; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs new file mode 100644 index 0000000000..927cbc5d2f --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs @@ -0,0 +1,97 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; +using osu.Game.Audio; +using osu.Game.Skinning; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Skinning +{ + public class OsuLegacySkin : ISkin + { + private readonly ISkin source; + + private Lazy configuration; + + private Lazy hasHitCircle; + + /// + /// On osu-stable, hitcircles have 5 pixels of transparent padding on each side to allow for shadows etc. + /// Their hittable area is 128px, but the actual circle portion is 118px. + /// We must account for some gameplay elements such as slider bodies, where this padding is not present. + /// + private const float legacy_circle_radius = 64 - 5; + + public OsuLegacySkin(ISkinSource source) + { + this.source = source; + + source.SourceChanged += sourceChanged; + sourceChanged(); + } + + private void sourceChanged() + { + // these need to be lazy in order to ensure they aren't called before the dependencies have been loaded into our source. + configuration = new Lazy(() => + { + var config = new SkinConfiguration(); + if (hasHitCircle.Value) + config.SliderPathRadius = legacy_circle_radius; + + // defaults should only be applied for non-beatmap skins (which are parsed via this constructor). + config.CustomColours["SliderBall"] = + source.GetValue(s => s.CustomColours.TryGetValue("SliderBall", out var val) ? val : (Color4?)null) + ?? new Color4(2, 170, 255, 255); + + return config; + }); + + hasHitCircle = new Lazy(() => source.GetTexture("hitcircle") != null); + } + + public Drawable GetDrawableComponent(string componentName) + { + switch (componentName) + { + case "Play/osu/sliderball": + var sliderBallContent = this.GetAnimation("sliderb", true, true, ""); + + if (sliderBallContent != null) + { + var size = sliderBallContent.Size; + + sliderBallContent.RelativeSizeAxes = Axes.Both; + sliderBallContent.Size = Vector2.One; + + return new LegacySliderBall(sliderBallContent) + { + Size = size + }; + } + + return null; + + case "Play/osu/hitcircle": + if (hasHitCircle.Value) + return new LegacyMainCirclePiece(); + + return null; + } + + return null; + } + + public Texture GetTexture(string componentName) => null; + + public SampleChannel GetSample(ISampleInfo sample) => null; + + public TValue GetValue(Func query) where TConfiguration : SkinConfiguration + => configuration.Value is TConfiguration conf ? query.Invoke(conf) : default; + } +} diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index aea3751bb5..44071d9cc1 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -43,7 +43,7 @@ namespace osu.Game.Beatmaps /// /// Retrieves the which this provides. /// - Skin Skin { get; } + ISkin Skin { get; } /// /// Constructs a playable from using the applicable converters for a specific . diff --git a/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs b/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs index 45fca493a2..299059407c 100644 --- a/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Edit public Storyboard Storyboard => workingBeatmap.Storyboard; - public Skin Skin => workingBeatmap.Skin; + public ISkin Skin => workingBeatmap.Skin; public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods) => playableBeatmap; } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 8dfefcfa1b..d567e48d9a 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -10,7 +10,6 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; -using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; @@ -60,8 +59,6 @@ namespace osu.Game.Skinning Samples?.Dispose(); } - private const double default_frame_time = 1000 / 60d; - public override Drawable GetDrawableComponent(string componentName) { bool animatable = false; @@ -114,7 +111,7 @@ namespace osu.Game.Skinning }; } - return getAnimation(componentName, animatable, looping); + return this.GetAnimation(componentName, animatable, looping); } public override Texture GetTexture(string componentName) @@ -161,41 +158,6 @@ namespace osu.Game.Skinning return componentName.StartsWith("Gameplay/taiko/") ? "taiko-" + lastPiece : lastPiece; } - private Drawable getAnimation(string componentName, bool animatable, bool looping, string animationSeparator = "-") - { - Texture texture; - - Texture getFrameTexture(int frame) => GetTexture($"{componentName}{animationSeparator}{frame}"); - - TextureAnimation animation = null; - - if (animatable) - { - for (int i = 0;; i++) - { - if ((texture = getFrameTexture(i)) == null) - break; - - if (animation == null) - animation = new TextureAnimation - { - DefaultFrameLength = default_frame_time, - Repeat = looping - }; - - animation.AddFrame(texture); - } - } - - if (animation != null) - return animation; - - if ((texture = GetTexture(componentName)) != null) - return new Sprite { Texture = texture }; - - return null; - } - protected class LegacySkinResourceStore : IResourceStore where T : INamedFileInfo { diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs new file mode 100644 index 0000000000..c5582af836 --- /dev/null +++ b/osu.Game/Skinning/LegacySkinExtensions.cs @@ -0,0 +1,53 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Animations; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; + +namespace osu.Game.Skinning +{ + public static class LegacySkinExtensions + { + public static Drawable GetAnimation(this ISkin source, string componentName, bool animatable, bool looping, string animationSeparator = "-") + { + const double default_frame_time = 1000 / 60d; + + Texture texture; + + Texture getFrameTexture(int frame) => source.GetTexture($"{componentName}{animationSeparator}{frame}"); + + TextureAnimation animation = null; + + if (animatable) + { + for (int i = 0;; i++) + { + if ((texture = getFrameTexture(i)) == null) + break; + + if (animation == null) + animation = new TextureAnimation + { + DefaultFrameLength = default_frame_time, + Repeat = looping + }; + + animation.AddFrame(texture); + } + } + + if (animation != null) + return animation; + + if ((texture = source.GetTexture(componentName)) != null) + return new Sprite + { + Texture = texture + }; + + return null; + } + } +} From 7bba8ca14bb92bea3f1d6a707ba1c85cc8f8f9a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 13:04:11 +0900 Subject: [PATCH 2440/2854] Split out nested classes --- osu.Game/Skinning/LegacySkin.cs | 112 ------------------- osu.Game/Skinning/LegacySkinResourceStore.cs | 76 +++++++++++++ osu.Game/Skinning/LegacySpriteText.cs | 53 +++++++++ 3 files changed, 129 insertions(+), 112 deletions(-) create mode 100644 osu.Game/Skinning/LegacySkinResourceStore.cs create mode 100644 osu.Game/Skinning/LegacySpriteText.cs diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index d567e48d9a..0151a211d3 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -1,11 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -14,11 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; -using osu.Framework.Text; using osu.Game.Audio; -using osu.Game.Database; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.UI; using osuTK; using osuTK.Graphics; @@ -158,111 +151,6 @@ namespace osu.Game.Skinning return componentName.StartsWith("Gameplay/taiko/") ? "taiko-" + lastPiece : lastPiece; } - protected class LegacySkinResourceStore : IResourceStore - where T : INamedFileInfo - { - private readonly IHasFiles source; - private readonly IResourceStore underlyingStore; - - private string getPathForFile(string filename) - { - bool hasExtension = filename.Contains('.'); - - var file = source.Files.Find(f => - string.Equals(hasExtension ? f.Filename : Path.ChangeExtension(f.Filename, null), filename, StringComparison.InvariantCultureIgnoreCase)); - return file?.FileInfo.StoragePath; - } - - public LegacySkinResourceStore(IHasFiles source, IResourceStore underlyingStore) - { - this.source = source; - this.underlyingStore = underlyingStore; - } - - public Stream GetStream(string name) - { - string path = getPathForFile(name); - return path == null ? null : underlyingStore.GetStream(path); - } - - public IEnumerable GetAvailableResources() => source.Files.Select(f => f.Filename); - - byte[] IResourceStore.Get(string name) => GetAsync(name).Result; - - public Task GetAsync(string name) - { - string path = getPathForFile(name); - return path == null ? Task.FromResult(null) : underlyingStore.GetAsync(path); - } - - #region IDisposable Support - - private bool isDisposed; - - protected virtual void Dispose(bool disposing) - { - if (!isDisposed) - { - isDisposed = true; - } - } - - ~LegacySkinResourceStore() - { - Dispose(false); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - #endregion - } - - private class LegacySpriteText : OsuSpriteText - { - private readonly LegacyGlyphStore glyphStore; - - public LegacySpriteText(ISkin skin, string font) - { - Shadow = false; - UseFullGlyphHeight = false; - - Font = new FontUsage(font, OsuFont.DEFAULT_FONT_SIZE); - glyphStore = new LegacyGlyphStore(skin); - } - - protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore); - - private class LegacyGlyphStore : ITexturedGlyphLookupStore - { - private readonly ISkin skin; - - public LegacyGlyphStore(ISkin skin) - { - this.skin = skin; - } - - public ITexturedCharacterGlyph Get(string fontName, char character) - { - var texture = skin.GetTexture($"{fontName}-{character}"); - - if (texture != null) - // Approximate value that brings character sizing roughly in-line with stable - texture.ScaleAdjust *= 18; - - if (texture == null) - return null; - - return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, null), texture, 1f / texture.ScaleAdjust); - } - - public Task GetAsync(string fontName, char character) => Task.Run(() => Get(fontName, character)); - } - } - public class LegacyCursor : CompositeDrawable { public LegacyCursor() diff --git a/osu.Game/Skinning/LegacySkinResourceStore.cs b/osu.Game/Skinning/LegacySkinResourceStore.cs new file mode 100644 index 0000000000..c8912aeb1f --- /dev/null +++ b/osu.Game/Skinning/LegacySkinResourceStore.cs @@ -0,0 +1,76 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using osu.Framework.IO.Stores; +using osu.Game.Database; + +namespace osu.Game.Skinning +{ + public class LegacySkinResourceStore : IResourceStore + where T : INamedFileInfo + { + private readonly IHasFiles source; + private readonly IResourceStore underlyingStore; + + private string getPathForFile(string filename) + { + bool hasExtension = filename.Contains('.'); + + var file = source.Files.Find(f => + string.Equals(hasExtension ? f.Filename : Path.ChangeExtension(f.Filename, null), filename, StringComparison.InvariantCultureIgnoreCase)); + return file?.FileInfo.StoragePath; + } + + public LegacySkinResourceStore(IHasFiles source, IResourceStore underlyingStore) + { + this.source = source; + this.underlyingStore = underlyingStore; + } + + public Stream GetStream(string name) + { + string path = getPathForFile(name); + return path == null ? null : underlyingStore.GetStream(path); + } + + public IEnumerable GetAvailableResources() => source.Files.Select(f => f.Filename); + + byte[] IResourceStore.Get(string name) => GetAsync(name).Result; + + public Task GetAsync(string name) + { + string path = getPathForFile(name); + return path == null ? Task.FromResult(null) : underlyingStore.GetAsync(path); + } + + #region IDisposable Support + + private bool isDisposed; + + protected virtual void Dispose(bool disposing) + { + if (!isDisposed) + { + isDisposed = true; + } + } + + ~LegacySkinResourceStore() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} diff --git a/osu.Game/Skinning/LegacySpriteText.cs b/osu.Game/Skinning/LegacySpriteText.cs new file mode 100644 index 0000000000..dbcec019d6 --- /dev/null +++ b/osu.Game/Skinning/LegacySpriteText.cs @@ -0,0 +1,53 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading.Tasks; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Text; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Skinning +{ + public class LegacySpriteText : OsuSpriteText + { + private readonly LegacyGlyphStore glyphStore; + + public LegacySpriteText(ISkin skin, string font) + { + Shadow = false; + UseFullGlyphHeight = false; + + Font = new FontUsage(font, OsuFont.DEFAULT_FONT_SIZE); + glyphStore = new LegacyGlyphStore(skin); + } + + protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore); + + private class LegacyGlyphStore : ITexturedGlyphLookupStore + { + private readonly ISkin skin; + + public LegacyGlyphStore(ISkin skin) + { + this.skin = skin; + } + + public ITexturedCharacterGlyph Get(string fontName, char character) + { + var texture = skin.GetTexture($"{fontName}-{character}"); + + if (texture != null) + // Approximate value that brings character sizing roughly in-line with stable + texture.ScaleAdjust *= 18; + + if (texture == null) + return null; + + return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, null), texture, 1f / texture.ScaleAdjust); + } + + public Task GetAsync(string fontName, char character) => Task.Run(() => Get(fontName, character)); + } + } +} From c389a5c798974d33f1b8728b09b99ebdd7e23137 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 13:42:29 +0900 Subject: [PATCH 2441/2854] Move remaining osu-specific implementations to OsuLegacySkin --- .../Skinning/LegacyCursor.cs | 42 +++++++++++ .../Skinning/NonPlayfieldSprite.cs | 28 +++++++ .../Skinning/OsuLegacySkin.cs | 22 ++++++ osu.Game/Skinning/LegacySkin.cs | 73 ------------------- 4 files changed, 92 insertions(+), 73 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs create mode 100644 osu.Game.Rulesets.Osu/Skinning/NonPlayfieldSprite.cs diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs new file mode 100644 index 0000000000..470ba3acae --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Skinning +{ + public class LegacyCursor : CompositeDrawable + { + public LegacyCursor() + { + Size = new Vector2(50); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + InternalChildren = new Drawable[] + { + new NonPlayfieldSprite + { + Texture = skin.GetTexture("cursormiddle"), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new NonPlayfieldSprite + { + Texture = skin.GetTexture("cursor"), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/NonPlayfieldSprite.cs b/osu.Game.Rulesets.Osu/Skinning/NonPlayfieldSprite.cs new file mode 100644 index 0000000000..55257106e2 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/NonPlayfieldSprite.cs @@ -0,0 +1,28 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Osu.Skinning +{ + /// + /// A sprite which is displayed within the playfield, but historically was not considered part of the playfield. + /// Performs scale adjustment to undo the scale applied by (osu! ruleset specifically). + /// + public class NonPlayfieldSprite : Sprite + { + public override Texture Texture + { + get => base.Texture; + set + { + if (value != null) + // stable "magic ratio". see OsuPlayfieldAdjustmentContainer for full explanation. + value.ScaleAdjust *= 1.6f; + base.Texture = value; + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs index 927cbc5d2f..27bfdc315b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs @@ -82,6 +82,26 @@ namespace osu.Game.Rulesets.Osu.Skinning return new LegacyMainCirclePiece(); return null; + + case "Play/osu/cursor": + if (GetTexture("cursor") != null) + return new LegacyCursor(); + + return null; + + case "Play/osu/number-text": + + string font = GetValue(config => config.HitCircleFont); + var overlap = GetValue(config => config.HitCircleOverlap); + + return !hasFont(font) + ? null + : new LegacySpriteText(this, font) + { + // Spacing value was reverse-engineered from the ratio of the rendered sprite size in the visual inspector vs the actual texture size + Scale = new Vector2(0.96f), + Spacing = new Vector2(-overlap * 0.89f, 0) + }; } return null; @@ -93,5 +113,7 @@ namespace osu.Game.Rulesets.Osu.Skinning public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => configuration.Value is TConfiguration conf ? query.Invoke(conf) : default; + + private bool hasFont(string fontName) => GetTexture($"{fontName}-0") != null; } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 0151a211d3..9a47e01f4e 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -3,17 +3,12 @@ using System.IO; using System.Linq; -using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Game.Audio; -using osu.Game.Rulesets.UI; -using osuTK; using osuTK.Graphics; namespace osu.Game.Skinning @@ -59,12 +54,6 @@ namespace osu.Game.Skinning switch (componentName) { - case "Play/osu/cursor": - if (GetTexture("cursor") != null) - return new LegacyCursor(); - - return null; - case "Play/osu/sliderfollowcircle": animatable = true; break; @@ -92,16 +81,6 @@ namespace osu.Game.Skinning animatable = true; looping = false; break; - - case "Play/osu/number-text": - return !hasFont(Configuration.HitCircleFont) - ? null - : new LegacySpriteText(this, Configuration.HitCircleFont) - { - Scale = new Vector2(0.96f), - // Spacing value was reverse-engineered from the ratio of the rendered sprite size in the visual inspector vs the actual texture size - Spacing = new Vector2(-Configuration.HitCircleOverlap * 0.89f, 0) - }; } return this.GetAnimation(componentName, animatable, looping); @@ -143,62 +122,10 @@ namespace osu.Game.Skinning return null; } - private bool hasFont(string fontName) => GetTexture($"{fontName}-0") != null; - private string getFallbackName(string componentName) { string lastPiece = componentName.Split('/').Last(); return componentName.StartsWith("Gameplay/taiko/") ? "taiko-" + lastPiece : lastPiece; } - - public class LegacyCursor : CompositeDrawable - { - public LegacyCursor() - { - Size = new Vector2(50); - - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - } - - [BackgroundDependencyLoader] - private void load(ISkinSource skin) - { - InternalChildren = new Drawable[] - { - new NonPlayfieldSprite - { - Texture = skin.GetTexture("cursormiddle"), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - new NonPlayfieldSprite - { - Texture = skin.GetTexture("cursor"), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } - }; - } - } - - /// - /// A sprite which is displayed within the playfield, but historically was not considered part of the playfield. - /// Performs scale adjustment to undo the scale applied by (osu! ruleset specifically). - /// - private class NonPlayfieldSprite : Sprite - { - public override Texture Texture - { - get => base.Texture; - set - { - if (value != null) - // stable "magic ratio". see OsuPlayfieldAdjustmentContainer for full explanation. - value.ScaleAdjust *= 1.6f; - base.Texture = value; - } - } - } } } From 22e3ad8b9c388ee6b4ff43c9acb3f186a7070264 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 13:58:17 +0900 Subject: [PATCH 2442/2854] Add skinning support to cursor test --- .../Resources/default-skin/cursor@2x.png | Bin 0 -> 28063 bytes .../default-skin/cursormiddle@2x.png | Bin 0 -> 7676 bytes .../TestSceneGameplayCursor.cs | 18 ++++++------------ .../Skinning/OsuLegacySkin.cs | 2 +- 4 files changed, 7 insertions(+), 13 deletions(-) create mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursor@2x.png create mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursormiddle@2x.png diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursor@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursor@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..75f9ba5ea6637636912004f8a889f5575d830436 GIT binary patch literal 28063 zcmV*6Ky$x|P)1^@s67{VYS000U^X+uL$Nkc;* zP;zf(X>4Tx07wm;mUmPX*B8g%%xo{TU6vwc>AklFq%OTkl_mFQv@x1^BM1TV}0C2duqR=S6Xn?LjUp6xrb&~O43j*Nv zEr418u3H3zGns$s|L;SQD-ufpfWpxLJ03rmi*g~#S@{x?OrJ!Vo{}kJ7$ajbnjp%m zGEV!%=70KpVow?KvV}a4moSaFCQKV= zXBIPnpP$8-NG!rR+)R#`$7JVZi#Wn10DSspSrkx`)s~4C+0n+?(b2-z5-tDd^^cpM zz5W?wz5V3zGUCskL5!X++LzcbT23thtSPiMTfS&1I{|204}j|3FPi>70OSh+Xzlyz zdl<5LNtZ}OE>>3g`T3RtKG#xK(9i3CI(+v0d-&=+OWAp!Ysd8Ar*foO5~i%E+?=c& zshF87;&Ay)i~kOm zCIB-Z!^JGdti+UJsxgN!t(Y#%b<8kk67vyD#cE*9urAm@Y#cTXn~yERR$}Y1E!Yd# zo7hq8Ya9;8z!~A3Z~?e@Tn26#t`xT$*Ni)h>&K1Yrto;Y8r}@=h7ZGY@Dh9xekcA2 z{tSKqKZ<`tAQQ9+wgf*y0zpVvOQ<9qCY&Y=5XJ~ILHOG0j2XwBQ%7jM`P2tv~{#P+6CGu9Y;5!2hua>CG_v;z4S?CC1rc%807-x z8s$^ULkxsr$OvR)G0GUn7`GVjR5Vq*RQM{JRGL%DRgX~5SKp(4L49HleU9rK?wsN|$L8GCfHh1tA~lw29MI^|n9|hJ z^w$(=?$kW5IibbS^3=-Es?a*EHLgw5cGnhYS7@Kne#%s4dNH$@Rm?8tq>hG8fR0pW zzfP~tjINRHeBHIW&AJctNO~;2RJ{tlPQ6KeZT(RF<@$~KcMXUJEQ54|9R}S7(}qTd zv4$HA+YFx=sTu_uEj4O1x^GN1_Ap*-Tx)#81ZToB$u!w*a?KPrbudjgtugI0gUuYx z1ZKO<`pvQC&gMe%TJu2*iiMX&o<*a@uqDGX#B!}=o8@yWeX9hktybMuAFUm%v#jf^ z@7XBX1lg>$>9G0T*3_13TVs2}j%w#;x5}>F?uEUXJ>Pzh{cQ)DL#V?BhfaqNj!uqZ z$0o;dCw-@6r(I5iEIKQkRm!^LjCJ;QUgdn!`K^nii^S!a%Wtk0u9>cfU7yS~n#-SC zH+RHM*Nx-0-)+d9>7MMq&wa>4$AjZh>+#4_&y(j_?>XjW;+5fb#Ot}YwYS*2#e16V z!d}5X>x20C`xN{1`YQR(_pSDQ=%?$K=GW*q>F?mb%>QfvHXt})YrtTjW*|4PA#gIt zDQHDdS1=_wD!4lMQHW`XIHV&K4h;(37J7f4!93x-wlEMD7`83!LAX));_x3Ma1r4V zH4%>^Z6cRPc1O{olA;bry^i*dE{nc5-*~=serJq)Okzw!%yg_zYWi`#ol25V;v^kU#wN!mA5MPH z3FFjqrcwe^cBM>m+1wr6XFN|{1#g`1#xLiOrMjh-r#?w@OWT$Wgg6&&5F%x&L(6hXP*!%2{VOVIa)adIsGCtQITk9vCHD^izmgw;`&@D zcVTY3gpU49^+=7S>!rha?s+wNZ}MaEj~6Hw2n%|am@e70WNfM5(r=exmT{MLF4tMU zX8G_6uNC`OLMu~NcCOM}Rk&(&wg2ivYe;J{*Zj2BdTsgISLt?eJQu}$~QLORDCnMIdyYynPb_W zEx0YhEw{FMY&}%2SiZD;WLxOA)(U1tamB0cN!u@1+E?z~LE0hRF;o>&)xJ}I=a!xC ztJAA*)_B)6@6y<{Y1i~_-tK`to_m`1YVIxB`);3L-|hYW`&(-bYby`n4&)tpTo+T< z{VnU;hI;k-lKKw^g$IWYMIP#EaB65ctZ}%k5pI+=jvq-pa_u{x@7kLzn)Wv{noEv? zqtc^Kzfb=D*0JDYoyS?nn|?6(VOI;SrMMMpUD7()mfkkh9^c-7BIrbChiga6kCs0k zJgIZC=9KcOveTr~g{NoFEIl)IR&;jaT-v#j&ZN$J=i|=b=!)p-y%2oi(nY_E=exbS z&s=i5bn>#xz3Ke>~2=f&N;yEFGz-^boBexUH6@}b7V+Mi8+ZXR+R zIyLMw-18{v(Y+Dw$g^K^e|bMz_?Y^*a!h-y;fd{&ljDBl*PbqTI{HlXY-Xb9SH)j< zJvV;-!*8Cy^-RW1j=m7TnEk!VQ9anCQ#Irqek8|^R*&0nRzYJrE<0@H_e;78`TZQGW*j2F^3eLan}X_~CoxBCij zzcm^xK!Toa0A0U6O7vRcZ7gEt_170-FCYG}6!-o2^?SeZ!ylF!UC)okg=^O~*EDLv zpKpm^9#6ndf~f{ch*sbMoGWjH2#dI{HqDRUEbFh00KC0leLilxpW@|%0?wP`mtJ{| z^*5GBxN(*B3Pb_C?#J<`X*PiuHR8`>2;JiYm{4u9CIJ*A@qYBi| z~{wP%}V=d%LWt!XKr8iCnXd^&z`aaQ0tevBa{p1ZkFSm6*#WDM;W$953o(+(J8zKwa1? zfT)djfk}||C~pGJ)6W3X=WTOq(l$G{*za=FOj*9e@)J)q&B5*R-APwU8M=G0Tz383 zegVPdd0&?IFAU8WUo_46i$gP;v+eVyIcE7H%g2|8=97=9E5IEB+vTT+=1AxO>^+Wg z`)~+tR|9u*G_Uu-cHLiHHK$QK{ybuu?a={jD_GYm^Ou_H>Jq9ncl&^&nOgwG`W{+o zhteD{0nyPx*X&Rp0M8U?CM+MaeBnaVOjvihyNjgGH_Z{{{yyblh%ba%|JmIenuCL; z**ynLcbcZB+yk~b@ZG-Mlx5(WvA)mtW0rUK`zQlCU{m8AvRzFlaGF^)+yU#&XjW_O zmauIzqmLM9-#lR3%-}V^3KN)6Az97eQ==1p5zVv?)h-|%-0T|7n-CoXlbR`(54&da z1W*CcjOC7Uf_<4Ecg>Mdu{=4VWciroJ$}#93N)9oa50!Kb~slNxd?*ZPA_>E8T-(FcWqXFCXRgGqSt^PNx z(K_q#jU>VHA8o%##T2&9tFN|nwD66tkSQ+#){W=7=EcihbNfT;{Q0DrlaIcbG*>Q9 zn#pY1bjQ=?_9r_{cWK(}H#^PVK<;*$VY1uw-EMR4;%?I&f(^Mn-fL#Fa|rVsYHP2V z&d){erstY&a;}+B_B;F34fTa8>-+u4$4BQIbFTZD`?#;)@w*-)k9W**{SAN1-}JYw zgLSb^*3CLvSL>THU|p<}bsH}1MqRD5b@v=R7thIa^Bg@_ z&)IXg4QvbB#I_MM+iDwVwsC^?HN)l4eZ7(VG>0o-U28^-MemBM(YVyhm*{n23O~dQ zUeSO07&C^0-dt{*XFGf-eWhK@-@HQubofnOGi$nL_X3&n&hZ3u)geT-&k+pEvuW21 zEKezWzBlP98??iWWqt=k*vwnL!@q&gwJ$xUF+Sn9E$4Ip!pLN3;J`hbo=pR0PE$1R z`etbPY@p=md_tx?V!fL;(_RyaY2@?W**uopVcxWCJDB!hId6_G^v%w!=bU;p8{eB# zOTPaU*^a^?KpkOX=lE+^p6O|$fzO79CX2Rv<;oCYwpU*rLu=QI=`3x|d05}tZ1BB@ zn7%$AECe^6NoXv<;(z#Ag4JBD9QV<`5-fXw>02G{b>Ua-1J^Nr)ZWer&J3VNfabu~ z^b^bvS3Tt<*T?1KaeD_$onQjvgyo(*0NoS`46&{-$Urrx>Z@OQ_<*I>%DM~Jx&xoidpPdz z;b4ECaKb0Rh4#{EUjYlK_S8avfz82*FPdqX0u>snhHHjDGaZ2%IzAiN2hebqVp+)C zCR{ELXu2o?%}ZV25HlK!!>X#P)OBbPnLtni#h0U;A?OKU2$YaPPAprZIty4Vw=4_P zjDt*fXR5~liw#k>V>v^Ep{jw2ngHt#ux$eE($j$WQ+l5-6wtCWu+r*Xsys#fHeTsE^rCfutTPVpr$>X z7L5f7Eq;qcw7`{s%~_rRRgdE=Xp^=87Qj`oskuC6OV3ldtbZ6n%61K~0l;WIJ}(+f z$ih@oPAaI{5v%|VbJzn`H=Uum&{plN07iK{f;JtO)o8;W;0YRlwZ{N?0%*6;X!{4k zM~K9j8k+36=lXKTfL`Z~N}FA)wAVw1?BM~n$t1q&P31A{u%fj_KTC6X7oZMrb0~m&j=f4utm#^S}VW;s1>j#00Th6)%7gnIS#Cwg>9HE*99#; z>t=ifFj=ob>(FM0pP}8Jo;SN_wL6&4y^DQw4)bX|tu~wM+UzZFz+Yc#GtbQPc?jv@ zIQi>vrAd4XU+Z@`3wu7ljmG*zG*^Sx`UvOzMb@-u>u}f$uJnt!=%_)IsXz7i$fCgBD+t{yXnP?bb@ErkTS+g2u6HhBl zWSUDr>xTlFu+=FXd}3OfNGL}$ViKuMIQM9+K@+GZ6DB}v0R@6M1Taw!g3}z6XMF1z z>nT9Tm`XmM98+KR5x_&X?;K-6rE2bi*}=J!VQj>n_?!jkO0{#{kjpklCiD5-hcpP|>f9^=+kq5pG53cKtY}-xaJH zGBsELho%!7;DQQ-QUKfJgQfQ3z-YP=E_#B{!Pm$DQkDxW0;LACoTI@kaRLIcO{JgD z*x%30snG^N+hO}5odHefIbc1+N9!=5`e#q=&YL@@X|pt$4_A|YwSaAei)M6)Ol=Zx z5zATewE%1Yu5(U<4$=vg`zFglcyz1x%oZmY`)WN05STlmgWcl*AKaxi8y< zORZ+9_7b$7V|F84LRQQrbpbHXDIklI#W9uZ-oKbfYJeI9GNCGf^+47Efm%q=gr!Gw zS;n$ucLbZj(^AIvqkxS-MC=pWCM?f@i_eAZXar8!dO+J1E0 zH@kR+b$qniWd4vNUw?RzaTR|>X$3Btr$pn+_$y7Knk%C4n8eGN#Ji|=`(Qmm!zpm> zo^u<52d+JPb_jg{gl+;LXrS&`Ab@5{Ip4*MsF8$*$QO{gjaEfKWj!DxkO{4^P00K_ zpk#Rg8d{AS#P11L$8XaQ1eQ`i;ZmCwXSkTjmROhmy;=(uMK0RQm=?i_&{~?sL6Zmw z0u_L?r5uJMzJoUFfk((#7pgwC^BLtJSgvc@Stjs>B|i$tIxj-Zrfz~pddlxiG3AFW zt9>t=o8gCn{dwv_%&_O4>SfI4=M3+#1Mn0k)3}-A)Ly@89J0jBu5mQarpWRuF_&MB z2D=BY%9$4~M>+`Cua4n|+9WpTFikYrK44jfqmIU!9|0Er)IvMWfCISHSi%%#Z**RW zDBV7AD+Z`~N|zg>_y9MsEO5{Z6bOt`STnbw(a>gUFEl1nXCF;wF_o#s_(GKw2HH!= z!m+nRprdKjw+g@nsS_qN7MhB#Fn&}4iU=(Ga~BiPq7NjUNoAT&EDn#+RU=KJ6|!GhBN+*aK;57inEHWxeXWp@$IoM3$h>Vm!WQKS8V()XJuV;vSUCG=vVkmIYOtj*R?K8+cH3GZ zkWpjldkxup98IETQNYw*#7@|EECGgb695B5!sTsp%2;|;7tOeUcWgR~cK;`ooDjGGS+vM&eN3S9I^ z;)d%OhEQ__T)$Rs_1HtK;Zk#*C$6O&NLCDCi2WTueX1^0-BTA_@Q>%r>11<*JkOfTO)Lv+=FOx!xX&M1a6R8Fg zG68W}V9dfKT$X^8Wy|s~+y&gb0h(po^Evy@`CdTBcL+w%6D^u8%&U+cV>SVKvWGSU zGGVg=aEFkFnrz{hC1lB?G#R~1)0fm_4-QxzL|sJZsg4f)J#dYIQ={+a zo@GSD=Liy$=ndV4NmN@gs^ADyI8|efTC42<1Hk&88`LZdkRbJA*)R^FWqIJHb(BKq zHcGP)CTKATCtxg)6-PhX@uB`HJ{VeO1S6z7a4<1VujwOX3Y9>^L@=7q~>vybR)0$e)(5ieVV>>kG-${z6k z<%0qCKyaPMzrmR|1R+=gMrS*Eh?>LUxH#Z5U>c@HYpH#@Q42ZpL0AGJmNC3ExT7G1 ziDie@upY~-H>?-nC^?2;#`pQ=wsanrpMXcpeH7a{lp2aPvfm*L7l{xQzZWv&o*~Ks z1$BUOx`Q&hiQI3ioVF%A21P@usT#9Us`h}o1Cz@G%dE#T>n-aAG`9m98qM#j&3f{} zbk2^r->G)PN8153N_(19N&yozLm6ta?#|Z&*@FRA6em582-GcaSf@jO1-~nNuGCx! z*B)>YFbvHFWcpb4>Wp|vnqnnS?~zp5+RY7Q(D01+m~9JIpZ(3nn;D9vsMu)*yD zW;sGvOdy-8#4XI`nTrI%&5UM!Cc0JC4qtNd<6GQ>7_-aXQoJ{5LJK^lyaQx+fJfjM zBI__&%iew14s?taDc=!HAsm4dHbD!siTMm|hDH;%&}P6TU_EyeIs&!#K!#6tqjvl^ z#L4&pe*3=C{R6h*chMZ){;7kg_7G&-AN7a6-jd6jW_zq8TJPTA;7<>P9rW=ga7qs_31hVV^Ofj7sAscVGX61cL>b?!U{grU{gU#->ynvm(Esnr59V4DJ(+AMpT_dv$& zrSQps?1k^o7a6pFehgfN{pr77NqJy2ExIS?TQ`BlHGQ=Ldux2t{0? zr>H~UohxQB;Tmw{!-?OaXL!sIo3f`ce z8`#95Z!wDrm%dhLEHsyJ2~|HY=CA22)mXqJNCMT81FBpH(lI@OW&G9eG|ewz3LnSh z1!4XLKH8tX&x64F^%%dV)mg-Sz=eoA5VRj709f}vD(!3BE=KKgVKdab1c*g|FtH;+ zWYojh^JTeb1g=(jhmjx+!>!qgyCKBvv8v}8`hACTpU3Z%sR8mg+4mLl>#y@?jE(UP ze#cvAE}fXproryfUR2%(4f6hCIQ8@x8#Gsg=DLV!w5NCsP=+EHXo8PWdWy3#^bn4` z+R7duy+BQ$rcjNfwqisRH7`(MY*{A{Pw>k$cr^O*#D3$uP4m5Hm+Rk#CH=EM`T5$J z3}0~mUtl^dZ{mxo(NuXiSx1{`QqgpsnoJW)4>bHTwHhYVkj9jrraoCmW|(7yFm@=_ zWVbMt4IzvMP3GXm+UbAlwSL7^o>7aPuEDlxFCWbCp$x}!IZ$wd_VUK{9R~7w`)5L) zdb8TwKisGmFdcfs57^9w=1Md2M8$2F&gMIE#i=7TwYC(N3Zv~}ke9?Q1&y>M}d-`1t z6hwPz68Fh7RXA|jV>HgCY3%U1rbgope2$t+CtkQTg+rRcQA>rk8ZNUhptY7oa6SjC z)_U<7Tjk3n0+9GCB?cu}ncZFkhGdjdWD12BS?Se)Sw8{ObeFiDX8%Ef^TK)USDNNI zuB$&rn-y6YKxPmC7qCN(yg^?mqAmiZS+sgoH!cUh6!+~}n`#O%jXk45j>wKFX@RU` zgvBn;65MixE}tLr#Ck`@TJlua5V!o^C48Z&bg<&}x8MxWk_|R*ODCJi74608yiU$u zW5&C@zc|A*-Xwr}0n>QI)ipc7ED{Ef_|}ZZUUTMcbi)kMBOKw$^c$izN0qn+j0G3YRzi!jFq=YLw_WixI^O!tqQU1DXo zF_o7H$WAepw<=Rv_bUB;lQ5s)i=Cpu$}u8hpW4e_;{nenrfG~v_0d=t9nTd}di<~P z#`VsgjcHJo2G2NG?a+@tmzt}^ztXp2mIfM4xPU6a61I}Qi^lrR@8#P4mPiQyMSrCL zrsgsWR|1xB)qn}tNr2T_teo#si>b-9-x(Yp{Yo|TT3Ce*Cnhhb-}$uy+0XIMyx`hs zBYxlEMXYHSL*P_5M5pl4NRD6gQ~#k-xbR)_sZe($){h43bodz+dlxNs4z2bn!_}QJ zXty$zPBLTjX)50uwHOCFDQ(wao3vLt_IWGFCyotCOvl?lI`va}i)yapF@Wm5#Lh6p zgof6c7pe&k{d{!bqi0ya5-y>NhO_bfY)opc?>>+tFI0JjCtSiNTz1uJxYG9$uv&Ya z1u(UkfYrX(x~WVp=6QJ$nU@z3D`!dqrZ!uoaP&Vz)@ZZ#Gulth#yQg&=>3e`O(^F$ z|HjUA{^tjn+2Z&c{ofhG7&As=q|-n1wh~&byETvDJ>E((0Gkd!2aYP7pv6wmU?Z8e z@0E_d&n@2gf~$sc^gf``YveJHF%lL3V8%H47;?jC(zycy9U?U&!Zg;}D@~(NC1B~?3s&vm=jF)XmduSG%1pd7*jZ{ms7^_a84r6cfomMO*14sj<|+ zLp z(g5lh=PIFyv7z<3z7$+GtPV=TrS^Idze^3K_R6dTQ;IA`sx_FPr6yB@r50P)WL|7$ z+dy-@o;BSG73mnpSa0%j>(sma!z{_Itz zHqA19!ql#o&yoH9$tfZG!#^MQGgc@-K;lzQ0@QLV$ma!oHgn){IiGam-V54{&v_!r zeV7IY#9Rh7zq_lsv5;7Ug#r%&b<|#@L+lp5q&Dlo6`- z92eRPxc0d%(Fqq1#fsFCkCyrL585lj6OQZB{{S+_b9HL3Nx5+y_YX0XM(9h=kw%Fg zW2uWimkoEeq;NhU3s-8dO>ikR;Yyz?O`~uMS(-%4b-1!Iv#bGIhpaZ20%9AdskBVU ztj{K}@&d4)&y&@5Cnwv=He2{)VF?OY0TDyD2ViS`njFZS2V>5`6F?m0Gv*jjrLR2>*K zvEy(Yoc!a6Dwl};;DZ!6_8QKa_Zo5R9W3bRQ-|E>io_9^ezoy(5CTpIp-G-p9mSls9grK7=*~O&u>my=Q;5> zfabFYuwykIn#>_r$2kA9+dQc6gk*Q-W$njh~J;(6BfGh4T=Isx@ zW$)8WS%4n;NcKXlWJ&MZhc*tSoJ`%q+&T<-bc-3zZ9^eAnL=@^{Ja zkOeP>X^^wq49w4`EsdDnGOI0;oG3G|nl>wsY9ew_^{{OTI}{TsTt4JWIl2X! z$_bCu^=xwhJ99-LN1&rUCXCx3&|)n^+%=o~n9S5-vT)J4+#CmZ)kob*N_MJh-V2x? z*3NzNT$u*^qa#MDa@YQN)Ly&Xq95?RoGSvo%8hw$U-;ajaM6=2Py39{eMIidXw%SO zI`{@x!{^fJ#rvXAdz~#Q%=gK{<+1|Sv9F!_li(7n1Zy3xgsLvteUTDCHwc+o;a?&D z`nWD&|1bGplC6`F35)GrYtU`W3D$ZY^~+992BJShF_wtGqf~T1INtayrjN{7ml(G% zY-FD}SF?4-y?|^gWZsI>M+3B7%434Lsm0z!bIs!cUVO1P%00?O$YQm6tAN$!G4{L4 z-?#7rxalcq&MfHT7M=5R5vC9@CnCwm`Qzzo{f&s) z;{M?JOO92WMggn+u1#<$v~|HHES3V5C5y3MmoA$Uyt20Z>vmzYe3JaP$^SO_J@P*% z|CF3yTV{PWmAPLHzsEJ50kZEl%|H7wXA@NdFrbR3$*Tu`9T(=)NYwvJ9qZLQ z-M2Ff(wo>J%IBmcYA+vO?}>k@l6{=NC%EbZvOa(4qh)2hmvEK)kBh;ARUp-??rirK zHH&JlZKhGPc#F&>ytoCFTA~(OEx3eAX`~RdWYc1s%j>D$FX3_@x99pVv-}^Et@}SE zbH#>9wm#OQE_2!Kbsh6}Jf@~sjk!J9mJSB^e}0zxU@d%P4)g2;14BGMSK7s=%pJPp zQ|B@5f={92?g-fq8cpk@77JgD!H6x-TkK5;b-;vEkIKl6cp`lKt*mNUT?H&Z%K?RN zqqQey?3sNc(R%aZsQTLcw9{FMRv@*4!*;3qW4G- zlcaO%nEBI_8Y;okEZ!j3rm==Ao#t|d7HyG2TNj*XooAjUn-<$#K1<5)3V|ut%Noxu zvG5;||0enO$kt!DH`UASHTbLsu(e-zvg7YalMm^Cl%YPfwZU!SVqBQx_2ZdJN}nhn zm>(mw8Pgl2f7UXERL_IZEtBBIq^DxP>$H`^Oee4$bA{Zw6Q68Al-_{>40d(SwZM7BR4x=6eyIP=gh)RpX}*7SwFmsI}Iz z!EJa2pfJ>4HCU;+{BD|^Z9uV0Bmwv;Qy$L?HkUoV%WnId6naE%52`c?@57Is!~3CevWKodX}+ zoj65l1ZfFb5c-e>Ll*Wd8yHD5>94FMD_xS#ndutYo`KO?$7MTO)mO0`<9|tT%;u~Q zhVSKL|1q;5mC^kRCm+a+;K!gs9_P<{kuhWiGg+>na`I;z=A2-yYc0bOg0&9Uy4Fe) zr0_D=LadkB_xGz&GP&_S1%@e%BjsXv9`_0$u?eI#I%3N z01bvutZbZeA?Ec!W`yrUhRRaQ6s`|4R@R22l)Vhdf=tMUj6`G~X0DA@NtRe$0+ts| z37nNZ(>%sxyaw4gcuZyK!yF@EIm4jgi7=Cx$I^==M1hkZkMpMwRz;_A#=`rx0pIz*5&O;xQmS`TG#xgaS6^lvViVs%ig5V0_YB5Za z55@Xu+38RJctzJ*%Z9X((sl+}(%(wWwap}EZ*g6a)#;yJiHER9*2hEVa+ZIaZTeIrGJ&As4-tK@hj4tXg%XNyb?rfijQ$Xu(1>}5a}WI{G%L{?-* zcGl9GlYedWzX(rU;LT&7bATP5P?_)`!7=pi@XX^3(-`IvCtt#KGL3^JWHGpKe6Yhn zYfL~Aa23FOQs(p<*=ncL;2NFPFe&RMQMgiQS$gU&f7$d72Y8D7G#NkTfs=Lds$Z|u z@3Kv(el&jWKD4=lj^ER{$DG-=(XKS;IP|HMtZqoQxouybAV(JVA|2i0ePT>xK!z66 zMA~T!AIy8q{BD>@{cc@Cpm$O9sG6~F%X}ASUz<$R$&#eYixa^s+{jzwCCt6 zSG>91`VJqT7&7+74`V#3&^SED=_xgjR4`@|j7n?UJpr{?Ik1oOpPsxj&0eLN1g~77 z^D0iwC176V30K`)RA_Z(^&UaG?LpE%9KYlVVSAdF$S;$BjjXWmCs{|2V_B!a%Zlgz zmt?JsfSCoF_6@kwDkNY}u>B%=d$R2pQxqr@G?+?2zyez3$MFr8t1yfV2$y$j8ir%} z1e0cRz)bdc0gc||-fd!~RPnv>XRnRrSXg;ZaqK~clXjJAVpFxP+F0MW7`;cdmvH&+ zA79U6_QzQY+MUhW33pMN9I-h1Vc$84Vj^iG4sm)=RTGe4rAb_et2T=nipUxD zLobrQ;YIL&lm7|%zasx5@{h^uz_4j+%6;oP3*CQ6R#Tm1F4s+_?X^AG_R1hq_+r2% ztUCDoR>)2nB1<)xvQ@?@?|ko5STdabco!#(d_4ML&DAmj$K#Jo<*&J^Bu7uA|0Q4s z)qIKPT;~qBgv{*gS=3@N2Ysm0;)i*QC^VRjlUj`ZOsB);UTQ4!NpP(LRy*~zpgdj* z(B+4jtizv@|9kR(Lw<*R)|7Ru>#k<{dt@)5^v68!^vTrZUU)TRw%f^RDFZbUaHaOL z2Wdt@Xd+Xt$~a^_%3TIzK_+A)!xb_kJ8NkKjPuGKq|Vw)FCk*yaqVk}?UL?zKr~oS z>C2?zeLud6#h@#WpcN|GYlLjPBD^w+`Yk`}6iO#2O<=-h<89JhHgcN9ZD7>`JNXwM z&ZioT|BkF?v%Y6Zb)6Hmm-*tqK^8L4*<;m^*28}ZQ2=k~fHkwK2L=6?pk2?9=>65@pUcLr{VoC9WE$DbFMqSi z-~S`>e@%Y>0jiRa{X2YD->Wv2wuNny+H4&(KH8F$fwC|uo0zv2%@s03cKXoq%r`Ps zw#ryp<9LPaQ-jHbY{-bL$c&EuT2Hb77NWRTdXWC`3ooL0F^|4LT~+8it9yMfvhQ0= zErxSO50W3}VGk>%CX0FK0W3@r!X@Dmk^7ZD6i7ne9|VW?ODgDT^|v1+?kbsJ-a0DZ`>HM%f}? zUmT{)mAwqef=tMUjL3@2n7j*QNPj$oH4VlN-n{(ygSL748Lk`X^abicE8SD42MQld zwMmb{x0czBwL**6vv?JZS}Y)opDpvDj*D9g!CHsQD|M6Rvau7cbn5f4DV$t?6iR{k zhva{ChRS69Ei?ahmh~@c$OJ6gCjm>yY{%`%xoI;M85No3djnFI%CWr55g0&ARpM6 zQ6p4sF_op~a=Ll6acro}*?4)yT6$$}f-42V$B%6Cg7_cFKRZJWJ)b(E6`H?At^vz- z$#zO}nXuL0UtLxfh%-{i$k389(*lheOqp^6a+EP}DQ`x#%784$gj!5SWJP8g*fA|w z0?V=`|6VN%&r|5UO&P&DX;Gx)-T)f}FCdfXBwI5{g(N@x1+~~%Q5Ir0XUpwLa1t;Z zDjUtKG|gjzwXV5pxYh-;{TIKPPdv~6fvhWVmXzma`RinZ)t)h=UP|Fvn`@^LYl-g=4BV;kPqna$V86S~W zPo`{J{kslVYAqWu^Cq~m@mG)b=&${hDjJ}@pX9mz201lYwo5{`Nt3o;=aG9oK7BRgwpsloi3uNVL~L&KIc1 zn_-1aQ%ULj7IpgLWl~W&wOL82j&uFqGOxaqz}jRQ^GG)SwKA~UihLseP@Z1KUf zxZ0XXeK2l%YaYWyLar}M_x*|SPHQV+wnE-3(^ST?^pi;$bBUFWf7DC~RyLBQS7vIh zY|u1~*>kkvv+-Gctdc#wKe&$?=efSZcd|XUL1z2erncYqw2)26N-Z|ZGRslEbIOo8 zvNuGLJjjNO$jX-IP*wqpKpFMf`HZLO=xM0_>?ufV98@D18V$hW!H+Vlal(b-1i(>? z1!xSX^*-u}$%aV45-J;J8(i7YCm!@M`S71pPcN2}Qhfor{4&|^BwX26S*FQM$hIGQ zAsb|+_EL7rFyu+;jMeehU%*m}Df{>r3=}qGL{?~gtrRz{g8Xs-e>9i2l{QSBv%xI0 z5iM=tIcojNDF4lYW}sdn^W`>QiJjS=Z^WW^WdSu_yI_ZN^!#qQJ3s;fHof1SK^(VvO(%b zvhmgdt06o2h>zE&KfTZD3Yp#fZGdGvX1i`bd@Y-h*_s>$P8lorl)1~YFoOv-*b$7# z%9gYlu&2ho_q`29@}R24Se`?&V2wY#SazL%;S5H_V)tgR`1)mKF#w@1 z4$D8A0n5*i4xx3Va|X4{wGs#Co+r*9KgzIKe<&+qC0UcmJOtx~7K>XJYOu1AvhixD zwtW3tvH8O@oJF>UyApU>x!`QKY_n{`1kL?7rxn>CXa5y%mXa;kp+`% z$jAw4J79O|e%6WCXoqWQA0xmLF8&4G<8ooKY|HPi9woQdWj5TlGWX5TbN$;?-j4>e z9SA-xQnt%_nb0kEyl0`7P3ub>kf$w{ zDzg5pULK;7ZYle_B>5+&^@hpy{MPz5p0 zMiBIDnDz2^`RrA)mC4@GNdoy(_WMWV$0_{_Dr-I|WjiSZHQ3mF+uCl~rrVEM%cjVT z$ugB8@>|GMK_Xv+P`;BvVq_s6i5(9uE79S<_oq(QV7p9T-H)}HqQ$(SFo7b;_YD$@ zN=O!;b-q1ZnHi06!>9yg`DF zY-Dw%sAb|Uai=63!JLg!m)6f7mJMjivaD_XYZt!m#&6#1@!XnlB}iG;ZI;W~uG`PF zmQ9gaB}Zf$vK1_3oicY>7Gy#;WJFfBq|Jc&8n;LcG_`-zbD)IFH>I)?KiH11CMg;# z!4keDUJB|Hj1MRd@Xj@O-2d*H@&bX+rM!(^j5jevE^hGlU9nai8f_cv{PjJMU0 z)nL_dWxHiNTAn=8U_~~{4B08e@(P+FTV<@QYuSgpgyK+x$%w3&lAUVS=?XA*@Fhp4 z<0psa3lzs5VCl#$G#G-OaZvLx!dA!^s4-JfZYC7a*m~3F5l&1tLgs9oY%EI~*QuNm zuxxBLZ3XSu`E2#v-23rP_4y}QbvwD7?U(I&GGGhY09ME>6a#bPhXpESSSe(^ko#yd zJ7$`e4H=OYnK30pv9eVN+_^Y^5!*`lbIzMkQ!VW#g&I-Y3@$Ttmi3_oPc~dF7t1kKcy+oP{dEO6_i$?PQzPZD^ai zZF^eCCS;|&%By7-3SyLT%3ImXK=L3PG9oKm?$@tgu&w+!dpVVGv ziEW)99lHB5t4evyBtR3cj*3R*)L^_pBGbpG-U)Hot9ct}@9~*+%?4*Nmg~~xI{mGB z<0JPMP`hJa$+0w@bE{aENbqM4s~_{*c!DaghShky`ZJB{s7B{3-nfM+xodsKz zU%2&$ZV-?z0Ridm6cALphpwT9W~94OBowKkrMpWSB!}+qlxFCB=YP(5zrnutHP7CA z{qD8aYWk7R9!XZ#@YNX4XQkJ z&dSeJf5lqk0pmvHR=)K?!bb0)50OGVxDwYHfrxR#kH+qzNjc%w)n&p&w51Cx2v*fXK&dka+IQ{Wq66A;&)9&u9K0rgx#(LtlNZPZZriOE7oI1aj<0Jq zZl^(5P`y&T2Sw1K5zd?B1?nZv!|j`A;+lT{>4d$XgFEg`vl;i(23W~^DaR@O&5dPN z8lW(7Vy+QecLn{3sZ*S41yj-~O2<;G#-n zgYThZ-uY?&Cdu?h!#P&(TE86mOBxj!vyb??3@M8;t=~@+Ck&mT?jiG0InwY^-Kn@C zTc>uLo&UXr`Q2yzklf8pn%yWFs}za>Uxby;8e}Gr6}T)!EG6&(QGpq~A1R7))v<>v zTARN7VnINNA%p)uKy}~nP=fzq5XA3i=kx-65p1NzquiFe_hwXFLKl!I<}99rK4CJy zyrSV-|7iQjLqeYIr+Pr8jp5KlIjr}$mwn66*hoe<;PQ*~aKuG$g!}j;XtC+#oASC9 zl0I@06CGzA6y{D;^Ahry;j<;&+KR828CkPX(56{i8EUoke|Ut#Du4J_2A_7)koJNH zBWTEolNqJP1sC+cin_)UJ88%+CMfavTv(1oQS9DNLslnzBl0~rt;x2R<R!D(Y~QnailS+aI*20z6Z8*_eW&Vv zmc7+D~Q7<-UKVx zm^)FebEuQv)aoLbYx?V_7^~b=t+aA`KB|{MM&D0tzfc{GZT-HmiLBXVA!6O!gMrD9 zl?fzAJs)Ss>P*ze=xZrw;Zu9Bxggz%qkeeF$J^J(gF>dQ3e1I(&II3x$anHdaio>Q z-#4HXeow6D`(GKK^L6^=CrRocEl3AP4aC_NTYK#`)q0EEa0Q|E#~(Ki(H_W{jNZ>_ z%5Yf3DAFf0C?m_lp1JUbHcU z!?;0IHf@Bgo7Ovx*l+9lS$@#czim(&x7H5~_+~b)g0%90h~kM*grH{V-Rg0v z>PSlnw_s%+@pvGKM5XX=skroCbf?*azd?{uQdx(p1~Xr?{1taQVxx){z03FkensW< zX!1RaBSsyyY*%yrG$Am{FVUz_CH~Gk-L7pb$?8UnKjrZ4_zg}<|IoJEuA@u-?&l^= zdv7F%kVBB8y&<=(d?@J-KA9AshS0kpTpjs-Tz(2Jg1vc0LrZh%5RA;P80044x01d9R3`sUb#5@ zXxURSZADWi@U>pwC=v#_UUp2FHfVs|qjeV4*GhnuXXG5?V)B4_ec7FR4%c;cZCwLY%4<`Vz{`g9}-s~DX^ zl5X{ukaW|d^rE3r2{^U=6xc0Jezc&XXOZ(dR!1AmgN3XR{| zBI(KQHAMdSx3%XMlIUKSiIr?nG;5TZuxG8>1WvF`n-@Y@U*;PSDi~c$h@Nqo z!ip4MicjF^wG=s}G6lQ6RmFef9X<(q#)H7YSXO&ZbntFYBO#?6BJA;)ozYRRe zyS@MbjM)Em0bG>?WTt0hr7p1RjSEm=Eo9X`cKk#N+~Ap$br%hHv>Uprxt@76miBm# z=*Vi<#Y=K=JY=0UfWcL(9Eg{!eTg`hn%+Lza5)*d>5lZuvoZefjq%r?o9{!zke+@d zy4-)ntt%`CiyTY$vZEr|wB*^RyOfpjeuS!Ne2u*k5Su+s+hhD#$%;DWa(USk5lh#P zm5!CVh%-aEuo^{z-z1_`kyQ1N2~?vEE6dAzWJp$JYlAwf*JkGnmC_Rn_>;6P1S&tL zq<1UFB!yHcc|`wOa(fDZJwE%`Q8^=*dTbMOjZHz$j3(DgRDSPe9-lmy)$t*eUdgKDzu$@OE@JS9m`w=8xrRQ(XvU$5!@!&wevJzP2HK9r zGVPDM<9_>1_6+sDWCwz_%+9|DU9M@uW{K+UJ*%chf1FY^7tN1rX_zQYUK>%gzOO&5 zhqrU7CVs~{qX{?17QpZwF%9^fV2+e@$_s+`f#eS73n9)bDy-wL^YUe0xVP6BUYJF1 zss|WCI!Ob%bJyYHCDTSOt4yyopd4jcFWs{vt4hVd*hxdX(hmo zFB{RKkf-caGY~HMns|YM%=d@CCm99FF-eze=nnrDbeRjehSfz7pD!Q7+beA;99WBG zaI{Zq^nLgIxD=O)~Ci)9u)cW zR|cS6M{d8QK2nH=r&)1&sV%!J)v_i-+8j6o569d@JDgKHe!S6lngn{NPs>BS4u=y- zt)>0T-K!)#m)5sIhKmI-dBf5!hllv!nBxy#JVX0Dpn{`c<^!6k45kRRn&>TZ!$tu_ zcnuKmx@IT48quUdV$*`n^y*08jv7<<59aR6SP%IcO41<=Dx0vB)hF#}vplO?Sf z?XD~|Lxpuc^tWJa!h&WY!b%;Z!?0o&7<=PW>-4E-aq4L+OL*k;k+QUqxe}CM^P5L2 zn|1gi?L#(K;QQySfy($7bp7Cx-pBU2#e(##^K(hoKz?bLwX5_8&U zhhVbHPV=6#Gnb>%Qr+9pKmd;bIeLr=T1D4c7+J@2jX6_>pD}fr`rf!*jqo3gM}%Lq zMoK1B^nAn^c=q)--FXbI8Y=(XynE~MfMS78=TO7W0CuN?RIf7S7yk?DncEO5>{k-K z_)9-ldX3OFT93CdH_PWyk7q7+qey3{oJ%ok%vD4A1mm19E)2`LO^~^6;Q1F|DjH3y zUEz+jdXeI~95rGS?1{;~M;ty{J2>B+V9jW4_6&1vL`)pAl#W5cK zXXR?H1iGO`2fj0^PbAm=nPq)a#*k{QI`eBv$y1Np5Fsu~hc^-v;Y`gvcJKvMoY2IB z%jCA+$fMnqK8l+3S9_Q5<5iDIG<(1Y%a*hqFED@j!b*-e!YBz59IZkQF}>Khm--&BETQHz57y zVMQgo+8Zee#vEs7iT!g>Go_}it0rJ}#G~RcMYYXZLM6{yvuSuNUb17_r0~J$i2B%u zN&y+a{${)Yot?QRrB8ey+9WT44_brRB0 zUU!tqfw_?+BoD^zkv?yl+w~3Ccg}1cmUIck(clL-+rT`?QOanN3YNX!hpJ5Ibcc7( z_ufE>)lXf{Jn@qj4@qyRrVSNYR4K!a|dm+uaQ|k$~7dUQz$+3EKTI0J$y9lX(I*CSZ#6ev zH^5L%Xu{({BAzusTp1x{LaI$hQieoaOW+@8%}~-J12f8u2YkF);=u2|-9c}aW50Zz z-~!C>N!X~KBHAx5Ecmaj#;T{fI>OLf4E-uej(48^%`MQtG0mw_OinlKOVMlp2xS)M zlMS&$){dZs`x>(bWzqy6|8|JgEjU`3Xgw8sPgvkV9b}?YNj^uDZlMJO`gO!Heh)HL zB*7eVT_-@jH`(SGe9+PDUyC-89PQn7zBM6TB-HV5g3&XN%<&JHIafI7B^bRR2*VMWv4m!+6)h=Rq>b8TbUmZMSVI5W#q^#0#5?4Hks;u>c z@;{jm#2ZO;$wyb}oZS1|Ia$26>xs4S#N_);DKhi4qMWF&SE--KL+y+h{meeMn)tM9qHN%^!JSurBpXvf=6SCbjo05IA7BSk-tjZ314h0~*mLy=Bi9k~)$1=!j0Z7; zJdnQ$5!d7c+8=PU{l}n^A2sc<3}urFGzX^5>Pd;K1QKn~I7V{Kn2ZiQ^G%DAK2H^; zw!XXtMyrRJs%7C+eIqvQq`d!KPgJqaLuB?4LV{jxIm7lF=w{Hu;nw^5hPdc zHNw{Ku;vO^?odux0P{`gDpQlhb2_JCrctV_Pm}wFFleVq-kDu&V0PCIYJ<6^$V!p4 z5;F3-ri~B@o?}m}6qnR7g!=4HeBBrrWKSWlV_B5Q8?Et&Lpy@`-GssF&#AZnoD(%( z_wG${D0ssV$+s7-LZ}Z(JBnX-bu;&8q4?^dy{bX{TMB3D^e55 zPfF9GA%=C9N)D+VXtaM6c7Mrl_fFn_wsHz<#E|#%?p;FaYK{&Pg}b--rYz8)!_U|# zO~D7^&VIp~-o~GJyUe@n*v}{4dVc?TNoNm=h52F1o6J{Gtb9d(wvpz79$ivxr;REh zNef!PUj(rcSGjZQ(y2|Q?DE#@LHpf14!g34Onq;*_>Qa(hLTe~TNHD*0_g;+JeJDW7Ve=l#eWBbw85 zdMO`k0xk!|e3Ie&#-wX+eN#LD=;8p7ph2{?eRwpj4R5j-jEUm=Qx`W$dD2q55i78` zH;f$#wwgDd)=E8zLChrx?9TUobdzxn`Uqx^T;@>x(3BcuPAYiA^qPDi6XbYWk`uZcmDYGeY8`GH$Z$ zIsNuz9fG$^2|ci<&&UkMRu%G$Ej!I$bC2|m!K+zTv}85xU+RF3NxRpl^hGV2ZrLn& ze7mlhEVQqhw~r=$;5Cn4V-~umCww-fn<{M;w;Lty9;dTE>u3HY_Cy@!K^8b%6Pr+f zYemRT<(tw6i^%y4^Xd-cfA_2puG`lsS^4c*TwBJ<_G#@%uLH6e`nLHd*YG|l!g4>k zJ;}Wu1zGtT6Nyd9pCh!0_j8I&2{Uh||2Sgs-|JNZTY7(QXkAAZnw18Tv1SEAsFUn1UxYmoQpDCzq}sb%*-|a`Y>gpYa|KrVKZ=8KJ{sS0a5>@lgdVpg?aeJ6Cf>5 z(Dg(Zn%Ac;3{?3m*WR>942qbm8qJi`fwqb!TRz$S&FoxXpDCfhR3z0QX_S_-7FYdR zI|^oNB2~fOHf#sX@=bnF<$j&7b9w`l3ZM!Tw^oY_m+R=;z`8_onNb+?xAZS zji}3~^xDEKNF&2wJgAOYZc#ro>6NaEP;O)~X|1Q|@PK$r(E&tx1n85x zs_<#8?64{M({S0r*#v`!LR^9g@D{8w$;dy<3;yF?d;Qw=WF7ZU4c>?-KBnnq|FZou3wcXFXa0l{GiZ5ZQ{MF%KC7v8nT6lk84qs#1K_l< zUIiobsn`@^(OB9NDn+O5_x?gRJKBOIyHAe!5)SyG!-x3tbWbnpxgWm&u3g@@afLYx z_G$q?q?OKC;yy7916+zTf>`OK4}r5p@@f+rM6svcbCYjXCyefm5XquVWUkO6-1?fE z(z#-X@ar}&isD-Z|uWWI(_QMDIL8T)2@;@)MZ$VVwYca&(kg~w?nzk zh_5BGy8K&YpT(x|vhP#M;_B10N-Mg^Z}aEK){9~kQv8NFy4cJ~q}eMz+yZ3~7H_#6 z>?VP)K_qd6$VvZIQ&oJwp$LZ+hViZLeEb>{K-OU(`_a8k%}A<^toixs0;N}*%O|l? zc!d3^Xm~Z^Ihrmh(U8aWhXS6|mllPr^aDI7jn8*48(TJXzR6II)9k;2@UKj!rknCT zQowGLBqo_};KH1m4ZmSFT7OSa(Ae$9JYTWU1N6>y12;x@bc&2%{#dwlA+xJnO42&H zV`y6-adg|kV`1u#FaV7bw~lxsI9Wi+Gx!p70bToi-66e2OCX-Ynm8_# zSIojX-TsJ^GIeebyEX;NzERU3CDTgsCFsbnkM~rNtIub`USNgAC}8JV5a?HWqBHwE zPql^Ts=V`Dxgj`SD65(qQg8KK*4<(3nJiDc5tv>nIjlolh9E z+or4}s1GYbRk39k0SKTAzmKyb|qbek%J6TZZ3s0pL9B&-429JuKok9J$@Dk7&x{)QxM% z`YFm{NQxpa_tq1H>zSF%S~Gj)1z0dN9&`b8TUmbSWB^C_xZ^LRJoT9Ph6q7q-C>^o z>(S1+A48#omH;wx3$Cc@FZglPY4zzG3IHVRC}i2p_!}%vfA*VHOY|*W0(Cp0PA-)x(8iz zT@8Lu$iQf7k(tF>;EJaaTgF&(z!-@*^GE(PZU8t0o&@)ZBT1PqAi-YA1(TN(jyJX0 zHlczwV$IH@_^N)0CbgSlmHfdy>l}#@O;Qxx!IJt&8QLAKL6O4#R)rP`j-*!`M6qUo zfgct2S!KuTj>liBaiAU2PWIzgw4aEp=0tXsiB}Qg{x}OFXli=eLJ{2yI*c6?jLnJi zDzLb07P8~Y-515}pVEU!2iaYg7I0$#sdFN}TRAhWpioFEN8aRZWyNPKnNtNFTM7}- zXrY6;lW2lCw9-_97*q;j=mx$)So=X7%KvN)Fy>GSH2-AAjS8M?C&-X+?!u8ofjqRT z=6Q}we41q6F%%cvUg~yK<(Gx)XB^xf6{J85EqL`~MW#A6YC9YSI%lN6s&>kY0HvLz z2_4N&=+JkHO+&tm=)5Fu0YK+l;zsd)&n1BWkI{^$xMv#Cvo6dN=s@C4r!V zS*-bKYbiBEWtJP#eA94%Wcv&#@+s_dKTXjl5?;`^!56<_2Twa93bfBvzkJk7nlq8P zP}cXn7>*{Vo)}(NyX;rGRTnk48#EQ2>Y)OuzcL%9C?(Aa>dSfr6OzUO~jLFh}82JfU4G~WV`pLr~!zDknCAHXG?n(=844K%eG)?r@X)Z8tMN; zyW}sVwZ^VA@>@;vypAPni|jW6xpZd(aB1v!%(B00dl2$Uqkqvs!bwMLg$JOSXcPqUIv%DGx3;7MP)b zFUV3Ts;G8uJ8j_U73&_iB(1RD=6(G1Vav!A#mHhq=V9ipj$PkCMYZ2kxff}+_Y+%x zU@opnz|bpn!k01Ubtt@GI^xrbScrFIYx8r;L&}~PLEWx(#JV-6Yg`5NRN-HW@mMN} zKYi!x+MYrgbe>VuoiAvZ*6NH{dH`*if5D}ADuN;5StOLMe)v;xJ>NT?&xNNX&&dYb z(j0>1HdbUqwYB70%jty;W{R|e~sT?ASy=Ia( zNW=aXniN47zEZybt?7<$)4m+~qoBdst?5H;rx`6acSxG#7TwA^1gyuMf z!x;-L0ZIc0nY81PX4M&iOCkRXO@6j_$6-7A6Cdq93ny}p*+%qDzKwP1t%Xt-%6w?% ziOIeG#*!XZ2ACC3a+J__bwmAl(7=ehwqbe1CDX$ZQ!qJ44Q8XkstIzKwjGt@Hi9;d zf2R5@ee~K9N0jZ>30b+}}35d~2lP@GEunF)3HsJq8+*5?#)?3?U zn;gn3X{$uoRFdap@KPHS1NPX+NaXyl&tcE;Ua8D*E@t?Lpju*ePk=x&zYKpmFJPh5 zi%uM=yJ5?SJVj;1Mp*;kAX$aAlI;Q87P^oM>n~Q$g&*(qASzG@k@ATq_ z=dz$pfKerKK?#x)*L$#Hv8$C61Ke9H-dc^Y*jBMjfWriiP>Uu|)y7d(V}vY4#V{;I z>2hF>8`yw&C`ac-dy$MCk>bb>Ka`6AFpHFw?_x(gWAa>@pyyNGOYMD#GtIe$ALc^% zy;?)fg+9fLUQR3FCEa zQ~NL5jo|W9Bivm6Q{$A#qUM@nT3WMbS(@+ePhzqmwJEkDr>59{fuY!L%l$JwrBp)5pNr#??q<#>(C#59I-pYVK&ha5-l5~Wcai_enzQOH-Y8)bdHeN#aJADlyLEvRXyDD z-g`l=Smn%36#0GlVPPTx8BgF7?LbAGoPUYe!Z#t`IIo8ripozSmY#nE5X*1Cs$&%< znesEhHu}}=p$#BzdS5lIKAOSHg_F4CQ=b%+Ls9rx;G%KCL|6Ig zi)nsyvlazc_m&p2*HTOcqhy~xpZB68TT?)Cz}E`!WNRMl1bItG6eY-ho4JoI({~6Lk^aU{{?E zuY7kUXe$>Px(gwT@lag1w#_5tq8BIP8DeeDA{)PV^(L#8s2aIydZhpdDubFuB;rJ? zQki9}kjpi(p+R=ccY6=74NaCs%_?@=50~BFTv%ncJ8KmA+3R;RX}7=cNv7U671i+? z-W|U6%J5tp+&}s`dgMn0#DRDv`Z%7|mvPJu^$2{Opq-(6OswS1|D)`fq`J#st>8Bt ziR0AXWkfy=E9I53wEj>(zxv<^=W?xua95R80S@CymoPA2Xx7kwvH&rX&K80kBfskJ z{@UX}vQ@~edI4mYov|}&|04c=Ra1# zA2}0YF@e|bVw^M>yCDE{34TGULvipQ!gr<|f@cfu>N)v-gGfl7QAo7ob}2};klz8H z4~9cjpCh1u)8V(z8GPZlEz|u?NsfDAZ>1W6i@z+5Yx_*z?u2CfnA+U?Ai?-?klu(a zcD+Qe4jWRWm&>$-{1GlA%qHATdB}t9VbYaDC^o|G{u+sS$YS_^9$49*L@jLXP~yNV zw|@QoATT?`>J7AXWuMh!HrU?=pq=y~%h7?1NA~x5+yKS~`ZY*8L8f`+4;5cVY-3E+ zT}0AF0tEm_Nk@#1-WeWoAvwo|Gh>{DmlTF>X=*yQNhQ$K@>$1wo4o3>GTmhkP&}n8 zR-4_E+IHIzymKl>pUw&%&T4If%~l?t3ZtYHCbN{iftP%a(G4Y4>B{b9H_Dly@T$P{ zMmFnqM=9T}izP;GfCT+>n&dTmhz-J|wpO8WCxeK+Z|M!~a3K7P^+5hp#6rK5S%PC2 zxq!X93MsYY)RV#6=3Cw>KNgNDV->A%(5*@xg$$kGC8>|2yau1M zO01UX_hu}S(PlhfZhewy0W{yeLs{7}*o-eRg z@bVo>ocH(c7LW}Hr{DEy7I*OQJl*Wzo&6>u z`8)l-3U!P~bCgxsN*Y3U6LWfI95=yVGi~YP5ZJSJA^dcl(v$QD$Z}{+Q(o%caFQ9C z(CV$4nYOpze1Ds==k$MJ<8dgmZq9(vYmUa94@Zl_zuHY$UN~5a6>=RPJ3dYvf>i2H zVSgB6wnm|)u+_q)c<#eBl{6j^ zCu&j!6}F6Ph%DpT5t0<#&pXHGZ-vRb-7(AZOY}-NKRKe;60^ypHaLE*a0OCgn~VqdsYzLm z+t!s%(Q+M6XoZB&!327lQ>9AP-JOpzXM4ipNMfmsB8xfNpDd&?0*kS65AHVYb#}vX zdD}LOPYM(KGp~9ZvUm!|Zie>2g+T{`vup3x&d@;Y216rML2htH{6wAM%UeK_V8b#< z0S=#b@ff@75GX!T58b<;LC%UswykOW!})Z7+ZESv-088wixq#6h|Mr(A8%BNl2b8f zJ?5@89VH!?a>t%1es{WsHGeOP=HevZ@S*k4_d9fSM6;1Y7m0%oEvnhebjNn`-zjE1 zmX-EoQx(=*&yOia0l2|6i|+Rx0Nt#KdpEqSEJqZVCtm`4Nx;SWR(B)dcLMa^62bf@ jn-{$-$xG7P7bN*RROT)j#xBGkuK_-NP?fKcH4FKF4Lr@_ literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursormiddle@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursormiddle@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..ebf59c18ba9f62390f49be4bed696a997a51f05c GIT binary patch literal 7676 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 q!@f`~Q0000 RequiredTypes => new[] { typeof(CursorTrail) }; - public CursorContainer Cursor => cursorContainer; - - public bool ProvidingUserCursor => true; - [BackgroundDependencyLoader] private void load() { - Add(cursorContainer = new OsuCursorContainer { RelativeSizeAxes = Axes.Both }); + SetContents(() => new OsuCursorContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + }); } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs index 27bfdc315b..0295a419ac 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Skinning return null; case "Play/osu/cursor": - if (GetTexture("cursor") != null) + if (source.GetTexture("cursor") != null) return new LegacyCursor(); return null; From 8f9fe9923746dfd62231d30dc198bf2458bfa36d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 14:40:36 +0900 Subject: [PATCH 2443/2854] Move SliderFollowCircle implementation --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs | 3 +++ osu.Game/Skinning/LegacySkin.cs | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs index 0295a419ac..3c508f34e0 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs @@ -59,6 +59,9 @@ namespace osu.Game.Rulesets.Osu.Skinning { switch (componentName) { + case "Play/osu/sliderfollowcircle": + return this.GetAnimation(componentName, true, true); + case "Play/osu/sliderball": var sliderBallContent = this.GetAnimation("sliderb", true, true, ""); diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 9a47e01f4e..1572c588e8 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -54,10 +54,6 @@ namespace osu.Game.Skinning switch (componentName) { - case "Play/osu/sliderfollowcircle": - animatable = true; - break; - case "Play/Miss": componentName = "hit0"; animatable = true; From 493fc5d400d616135e94291f6ec6f498cbd971da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 14:44:36 +0900 Subject: [PATCH 2444/2854] Bring back OsuPlayer test --- .../TestSceneOsuPlayer.cs | 148 +---------------- .../TestSceneSkinFallbacks.cs | 157 ++++++++++++++++++ 2 files changed, 161 insertions(+), 144 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs index d61378f4f8..1ba027ea4b 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs @@ -1,157 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Framework.Timing; -using osu.Game.Audio; -using osu.Game.Beatmaps; -using osu.Game.Configuration; -using osu.Game.Graphics; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Screens.Play; -using osu.Game.Skinning; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Osu.Tests { - [TestFixture] - public class TestSceneOsuPlayer : PlayerTestScene - { - private readonly TestSource testUserSkin; - private readonly TestSource testBeatmapSkin; - - public TestSceneOsuPlayer() - : base(new OsuRuleset()) + [TestFixture] + public class TestSceneOsuPlayer : PlayerTestScene { - testUserSkin = new TestSource("user"); - testBeatmapSkin = new TestSource("beatmap"); - } - - [Test] - public void TestBeatmapSkinDefault() - { - AddStep("enable user provider", () => testUserSkin.Enabled = true); - - AddStep("enable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, true)); - checkNextHitObject("beatmap"); - - AddStep("disable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, false)); - checkNextHitObject("user"); - - AddStep("disable user provider", () => testUserSkin.Enabled = false); - checkNextHitObject(null); - } - - private void checkNextHitObject(string skin) => - AddUntilStep($"check skin from {skin}", () => + public TestSceneOsuPlayer() + : base(new OsuRuleset()) { - var firstObject = ((TestPlayer)Player).DrawableRuleset.Playfield.AllHitObjects.OfType().FirstOrDefault(); - - if (firstObject == null) - return false; - - var skinnable = firstObject.ApproachCircle.Child as SkinnableDrawable; - - if (skin == null && skinnable?.Drawable is Sprite) - // check for default skin provider - return true; - - var text = skinnable?.Drawable as SpriteText; - - return text?.Text == skin; - }); - - [Resolved] - private AudioManager audio { get; set; } - - protected override Player CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(testUserSkin); - - protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) => new CustomSkinWorkingBeatmap(beatmap, Clock, audio, testBeatmapSkin); - - public class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap - { - private readonly ISkinSource skin; - - public CustomSkinWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock frameBasedClock, AudioManager audio, ISkinSource skin) - : base(beatmap, frameBasedClock, audio) - { - this.skin = skin; - } - - protected override ISkin GetSkin() => skin; - } - - public class SkinProvidingPlayer : TestPlayer - { - private readonly TestSource userSkin; - - public SkinProvidingPlayer(TestSource userSkin) - { - this.userSkin = userSkin; - } - - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - - dependencies.CacheAs(userSkin); - - return dependencies; } } - - public class TestSource : ISkinSource - { - private readonly string identifier; - - public TestSource(string identifier) - { - this.identifier = identifier; - } - - public Drawable GetDrawableComponent(string componentName) - { - if (!enabled) return null; - - return new SpriteText - { - Text = identifier, - Font = OsuFont.Default.With(size: 30), - }; - } - - public Texture GetTexture(string componentName) => null; - - public SampleChannel GetSample(ISampleInfo sampleInfo) => null; - - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => default; - - public event Action SourceChanged; - - private bool enabled = true; - - public bool Enabled - { - get => enabled; - set - { - if (value == enabled) - return; - - enabled = value; - SourceChanged?.Invoke(); - } - } - } - } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs new file mode 100644 index 0000000000..24d0b39bed --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs @@ -0,0 +1,157 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Timing; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Screens.Play; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestSceneSkinFallbacks : PlayerTestScene + { + private readonly TestSource testUserSkin; + private readonly TestSource testBeatmapSkin; + + public TestSceneSkinFallbacks() + : base(new OsuRuleset()) + { + testUserSkin = new TestSource("user"); + testBeatmapSkin = new TestSource("beatmap"); + } + + [Test] + public void TestBeatmapSkinDefault() + { + AddStep("enable user provider", () => testUserSkin.Enabled = true); + + AddStep("enable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, true)); + checkNextHitObject("beatmap"); + + AddStep("disable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, false)); + checkNextHitObject("user"); + + AddStep("disable user provider", () => testUserSkin.Enabled = false); + checkNextHitObject(null); + } + + private void checkNextHitObject(string skin) => + AddUntilStep($"check skin from {skin}", () => + { + var firstObject = ((TestPlayer)Player).DrawableRuleset.Playfield.AllHitObjects.OfType().FirstOrDefault(); + + if (firstObject == null) + return false; + + var skinnable = firstObject.ApproachCircle.Child as SkinnableDrawable; + + if (skin == null && skinnable?.Drawable is Sprite) + // check for default skin provider + return true; + + var text = skinnable?.Drawable as SpriteText; + + return text?.Text == skin; + }); + + [Resolved] + private AudioManager audio { get; set; } + + protected override Player CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(testUserSkin); + + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) => new CustomSkinWorkingBeatmap(beatmap, Clock, audio, testBeatmapSkin); + + public class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap + { + private readonly ISkinSource skin; + + public CustomSkinWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock frameBasedClock, AudioManager audio, ISkinSource skin) + : base(beatmap, frameBasedClock, audio) + { + this.skin = skin; + } + + protected override ISkin GetSkin() => skin; + } + + public class SkinProvidingPlayer : TestPlayer + { + private readonly TestSource userSkin; + + public SkinProvidingPlayer(TestSource userSkin) + { + this.userSkin = userSkin; + } + + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + + dependencies.CacheAs(userSkin); + + return dependencies; + } + } + + public class TestSource : ISkinSource + { + private readonly string identifier; + + public TestSource(string identifier) + { + this.identifier = identifier; + } + + public Drawable GetDrawableComponent(string componentName) + { + if (!enabled) return null; + + return new SpriteText + { + Text = identifier, + Font = OsuFont.Default.With(size: 30), + }; + } + + public Texture GetTexture(string componentName) => null; + + public SampleChannel GetSample(ISampleInfo sampleInfo) => null; + + public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => default; + + public event Action SourceChanged; + + private bool enabled = true; + + public bool Enabled + { + get => enabled; + set + { + if (value == enabled) + return; + + enabled = value; + SourceChanged?.Invoke(); + } + } + } + } +} From c3fb4b9099c9f7dd7e0bd71f6ae14a61d66c6f3d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 14:51:47 +0900 Subject: [PATCH 2445/2854] Fix test failing --- osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs index 24d0b39bed..731b0a84e9 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void checkNextHitObject(string skin) => AddUntilStep($"check skin from {skin}", () => { - var firstObject = ((TestPlayer)Player).DrawableRuleset.Playfield.AllHitObjects.OfType().FirstOrDefault(); + var firstObject = ((TestPlayer)Player).DrawableRuleset.Playfield.HitObjectContainer.AliveObjects.OfType().FirstOrDefault(); if (firstObject == null) return false; From ae05faa6d2e96d49c0de9d2791d0bbb4d12c4e21 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 14:54:46 +0900 Subject: [PATCH 2446/2854] Fix indentation --- osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs index 1ba027ea4b..0a33b09ba8 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs @@ -6,12 +6,12 @@ using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Osu.Tests { - [TestFixture] - public class TestSceneOsuPlayer : PlayerTestScene + [TestFixture] + public class TestSceneOsuPlayer : PlayerTestScene + { + public TestSceneOsuPlayer() + : base(new OsuRuleset()) { - public TestSceneOsuPlayer() - : base(new OsuRuleset()) - { - } } + } } From a15828ab25d6b727618fe4159fbadacb90fc9de5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 14:39:02 +0900 Subject: [PATCH 2447/2854] Introduce the concept of SkinComponents Removes reliance on string lookups and better defines elements for introduction into database --- .../TestSceneCatcher.cs | 6 ++-- osu.Game.Rulesets.Catch/CatchRuleset.cs | 4 ++- osu.Game.Rulesets.Catch/CatchSkinComponent.cs | 19 ++++++++++++ .../CatchSkinComponents.cs | 10 +++++++ osu.Game.Rulesets.Catch/UI/CatcherSprite.cs | 2 +- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 4 ++- osu.Game.Rulesets.Mania/ManiaSkinComponent.cs | 19 ++++++++++++ .../ManiaSkinComponents.cs | 9 ++++++ .../TestSceneSkinFallbacks.cs | 2 +- .../Drawables/Connections/FollowPoint.cs | 2 +- .../Objects/Drawables/DrawableHitCircle.cs | 2 +- .../Objects/Drawables/DrawableRepeatPoint.cs | 2 +- .../Objects/Drawables/DrawableSliderTick.cs | 2 +- .../Drawables/Pieces/ApproachCircle.cs | 6 ++-- .../Objects/Drawables/Pieces/ExplodePiece.cs | 5 ++-- .../Objects/Drawables/Pieces/FlashPiece.cs | 5 ++-- .../Objects/Drawables/Pieces/GlowPiece.cs | 7 ++--- .../Objects/Drawables/Pieces/NumberPiece.cs | 6 ++-- .../Objects/Drawables/Pieces/RingPiece.cs | 5 ++-- .../Objects/Drawables/Pieces/SliderBall.cs | 4 +-- osu.Game.Rulesets.Osu/OsuRuleset.cs | 4 ++- osu.Game.Rulesets.Osu/OsuSkinComponent.cs | 19 ++++++++++++ osu.Game.Rulesets.Osu/OsuSkinComponents.cs | 18 +++++++++++ .../Skinning/LegacyMainCirclePiece.cs | 2 +- .../Skinning/OsuLegacySkin.cs | 22 +++++++------- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 2 +- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 4 ++- osu.Game.Rulesets.Taiko/TaikoSkinComponent.cs | 19 ++++++++++++ .../TaikoSkinComponents.cs | 9 ++++++ .../Gameplay/TestSceneSkinnableDrawable.cs | 30 ++++++++++++++----- .../Rulesets/Judgements/DrawableJudgement.cs | 2 +- .../Skinning/BeatmapSkinProvidingContainer.cs | 2 +- osu.Game/Skinning/DefaultSkin.cs | 4 +-- osu.Game/Skinning/ISkin.cs | 2 +- osu.Game/Skinning/ISkinComponent.cs | 10 +++++++ osu.Game/Skinning/LegacySkin.cs | 29 +++++------------- osu.Game/Skinning/PlaySkinComponent.cs | 23 ++++++++++++++ osu.Game/Skinning/Skin.cs | 4 +-- osu.Game/Skinning/SkinManager.cs | 4 +-- osu.Game/Skinning/SkinProvidingContainer.cs | 8 ++--- osu.Game/Skinning/SkinnableDrawable.cs | 20 ++++++------- osu.Game/Skinning/SkinnableSprite.cs | 6 ++-- osu.Game/Skinning/SkinnableSpriteText.cs | 4 +-- 43 files changed, 264 insertions(+), 104 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/CatchSkinComponent.cs create mode 100644 osu.Game.Rulesets.Catch/CatchSkinComponents.cs create mode 100644 osu.Game.Rulesets.Mania/ManiaSkinComponent.cs create mode 100644 osu.Game.Rulesets.Mania/ManiaSkinComponents.cs create mode 100644 osu.Game.Rulesets.Osu/OsuSkinComponent.cs create mode 100644 osu.Game.Rulesets.Osu/OsuSkinComponents.cs create mode 100644 osu.Game.Rulesets.Taiko/TaikoSkinComponent.cs create mode 100644 osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs create mode 100644 osu.Game/Skinning/ISkinComponent.cs create mode 100644 osu.Game/Skinning/PlaySkinComponent.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 406c0af28d..13286f4524 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; @@ -82,9 +82,9 @@ namespace osu.Game.Rulesets.Catch.Tests remove { } } - public Drawable GetDrawableComponent(string componentName) + public Drawable GetDrawableComponent(ISkinComponent component) { - switch (componentName) + switch (component.LookupName) { case "Play/Catch/fruit-catcher-idle": return new CatcherCustomSkin(); diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 71e05083be..cd2f8d56af 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -27,6 +27,8 @@ namespace osu.Game.Rulesets.Catch public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap); + public const string SHORT_NAME = "catch"; + public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] { new KeyBinding(InputKey.Z, CatchAction.MoveLeft), @@ -117,7 +119,7 @@ namespace osu.Game.Rulesets.Catch public override string Description => "osu!catch"; - public override string ShortName => "fruits"; + public override string ShortName => SHORT_NAME; public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetCatch }; diff --git a/osu.Game.Rulesets.Catch/CatchSkinComponent.cs b/osu.Game.Rulesets.Catch/CatchSkinComponent.cs new file mode 100644 index 0000000000..620720310f --- /dev/null +++ b/osu.Game.Rulesets.Catch/CatchSkinComponent.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Catch +{ + public class CatchSkinComponent : PlaySkinComponent + { + public CatchSkinComponent(CatchSkinComponents component) + : base(component) + { + } + + protected override string RulesetPrefix => CatchRuleset.SHORT_NAME; + + protected override string ComponentName => Component.ToString().ToLower(); + } +} diff --git a/osu.Game.Rulesets.Catch/CatchSkinComponents.cs b/osu.Game.Rulesets.Catch/CatchSkinComponents.cs new file mode 100644 index 0000000000..c03fe42af7 --- /dev/null +++ b/osu.Game.Rulesets.Catch/CatchSkinComponents.cs @@ -0,0 +1,10 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Catch +{ + public enum CatchSkinComponents + { + Catcher + } +} diff --git a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs index c0c1952064..1c2fe3517a 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.UI [BackgroundDependencyLoader] private void load() { - InternalChild = new SkinnableSprite(@"Play/Catch/fruit-catcher-idle") + InternalChild = new SkinnableSprite(new CatchSkinComponent(CatchSkinComponents.Catcher)) { RelativeSizeAxes = Axes.Both, Anchor = Anchor.TopCentre, diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 0de86c2149..0c4e7d4858 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -35,6 +35,8 @@ namespace osu.Game.Rulesets.Mania public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap); public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score); + public const string SHORT_NAME = "mania"; + public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this); public override IEnumerable ConvertLegacyMods(LegacyMods mods) @@ -163,7 +165,7 @@ namespace osu.Game.Rulesets.Mania public override string Description => "osu!mania"; - public override string ShortName => "mania"; + public override string ShortName => SHORT_NAME; public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetMania }; diff --git a/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs b/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs new file mode 100644 index 0000000000..72a3ce7ad5 --- /dev/null +++ b/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Mania +{ + public class ManiaSkinComponent : PlaySkinComponent + { + public ManiaSkinComponent(ManiaSkinComponents component) + : base(component) + { + } + + protected override string RulesetPrefix => ManiaRuleset.SHORT_NAME; + + protected override string ComponentName => Component.ToString().ToLower(); + } +} diff --git a/osu.Game.Rulesets.Mania/ManiaSkinComponents.cs b/osu.Game.Rulesets.Mania/ManiaSkinComponents.cs new file mode 100644 index 0000000000..6d85816e5a --- /dev/null +++ b/osu.Game.Rulesets.Mania/ManiaSkinComponents.cs @@ -0,0 +1,9 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Mania +{ + public enum ManiaSkinComponents + { + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs index 731b0a84e9..fe73e7c861 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs @@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.Osu.Tests this.identifier = identifier; } - public Drawable GetDrawableComponent(string componentName) + public Drawable GetDrawableComponent(ISkinComponent component) { if (!enabled) return null; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs index 523e911434..89ffddf4cb 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections { Origin = Anchor.Centre; - Child = new SkinnableDrawable("Play/osu/followpoint", _ => new Container + Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.FollowPoint), _ => new Container { Masking = true, AutoSizeAxes = Axes.Both, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 0af278f6a4..22b12be030 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables return true; }, }, - mainContent = new SkinnableDrawable("Play/osu/hitcircle", _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)), + mainContent = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.HitCircle), _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)), ApproachCircle = new ApproachCircle { Alpha = 0, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 1db1eec33e..50187781f6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Blending = BlendingParameters.Additive; Origin = Anchor.Centre; - InternalChild = scaleContainer = new SkinnableDrawable("Play/osu/reversearrow", _ => new SpriteIcon + InternalChild = scaleContainer = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ReverseArrow), _ => new SpriteIcon { RelativeSizeAxes = Axes.Both, Icon = FontAwesome.Solid.ChevronRight, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 653e73ac3f..c5fa5f0af5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); Origin = Anchor.Centre; - InternalChild = scaleContainer = new SkinnableDrawable("Play/osu/sliderscorepoint", _ => new CircularContainer + InternalChild = scaleContainer = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderScorePoint), _ => new CircularContainer { Masking = true, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs index 5813197336..c17c276205 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs @@ -31,13 +31,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private class SkinnableApproachCircle : SkinnableSprite { public SkinnableApproachCircle() - : base("Play/osu/approachcircle") + : base(new OsuSkinComponent(OsuSkinComponents.ApproachCircle)) { } - protected override Drawable CreateDefault(string name) + protected override Drawable CreateDefault(ISkinComponent component) { - var drawable = base.CreateDefault(name); + var drawable = base.CreateDefault(component); // account for the sprite being used for the default approach circle being taken from stable, // when hitcircles have 5px padding on each size. this should be removed if we update the sprite. diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs index 1d21347cba..6381ddca69 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs @@ -3,7 +3,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Skinning; using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces @@ -20,12 +19,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Blending = BlendingParameters.Additive; Alpha = 0; - Child = new SkinnableDrawable("Play/osu/hitcircle-explode", _ => new TrianglesPiece + Child = new TrianglesPiece { Blending = BlendingParameters.Additive, RelativeSizeAxes = Axes.Both, Alpha = 0.2f, - }, s => s.GetTexture("Play/osu/hitcircle") == null); + }; } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs index 1e3af567fe..038a2299e9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs @@ -5,7 +5,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osuTK; using osu.Framework.Graphics.Shapes; -using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -21,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Blending = BlendingParameters.Additive; Alpha = 0; - Child = new SkinnableDrawable("Play/osu/hitcircle-flash", name => new CircularContainer + Child = new CircularContainer { Masking = true, RelativeSizeAxes = Axes.Both, @@ -29,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { RelativeSizeAxes = Axes.Both } - }, s => s.GetTexture("Play/osu/hitcircle") == null); + }; } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs index a36d9e96c8..00188689dd 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -22,14 +21,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces [BackgroundDependencyLoader] private void load(TextureStore textures) { - Child = new SkinnableDrawable("Play/osu/ring-glow", name => new Sprite + Child = new Sprite { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Texture = textures.Get(name), + Texture = textures.Get("ring-glow"), Blending = BlendingParameters.Additive, Alpha = 0.5f - }, s => s.GetTexture("Play/osu/hitcircle") == null); + }; } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs index e8dc63abca..62c4ba5ee3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Children = new Drawable[] { - new SkinnableDrawable("Play/osu/number-glow", name => new CircularContainer + new CircularContainer { Masking = true, Origin = Anchor.Centre, @@ -41,8 +41,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Colour = Color4.White.Opacity(0.5f), }, Child = new Box() - }, s => s.GetTexture("Play/osu/hitcircle") == null), - number = new SkinnableSpriteText("Play/osu/number-text", _ => new OsuSpriteText + }, + number = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText { Font = OsuFont.Numeric.With(size: 40), UseFullGlyphHeight = false, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs index 575f2c92c5..c97b74756a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics.Containers; using osuTK; using osuTK.Graphics; using osu.Framework.Graphics.Shapes; -using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -19,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Anchor = Anchor.Centre; Origin = Anchor.Centre; - InternalChild = new SkinnableDrawable("Play/osu/hitcircleoverlay", _ => new Container + InternalChild = new Container { Masking = true, CornerRadius = Size.X / 2, @@ -35,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces RelativeSizeAxes = Axes.Both } } - }); + }; } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 329aed7b81..7c871c6ccd 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Anchor = Anchor.Centre, RelativeSizeAxes = Axes.Both, Alpha = 0, - Child = new SkinnableDrawable("Play/osu/sliderfollowcircle", _ => new DefaultFollowCircle()), + Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderFollowCircle), _ => new DefaultFollowCircle()), }, new CircularContainer { @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Child = new Container { RelativeSizeAxes = Axes.Both, - Child = new SkinnableDrawable("Play/osu/sliderball", _ => new DefaultSliderBall()), + Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBall), _ => new DefaultSliderBall()), } } }; diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 49676933e1..27899ab56e 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -35,6 +35,8 @@ namespace osu.Game.Rulesets.Osu public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap); + public const string SHORT_NAME = "osu"; + public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] { new KeyBinding(InputKey.Z, OsuAction.LeftButton), @@ -161,7 +163,7 @@ namespace osu.Game.Rulesets.Osu public override string Description => "osu!"; - public override string ShortName => "osu"; + public override string ShortName => SHORT_NAME; public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this); diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponent.cs b/osu.Game.Rulesets.Osu/OsuSkinComponent.cs new file mode 100644 index 0000000000..ef0df6cbda --- /dev/null +++ b/osu.Game.Rulesets.Osu/OsuSkinComponent.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Osu +{ + public class OsuSkinComponent : PlaySkinComponent + { + public OsuSkinComponent(OsuSkinComponents component) + : base(component) + { + } + + protected override string RulesetPrefix => OsuRuleset.SHORT_NAME; + + protected override string ComponentName => Component.ToString().ToLower(); + } +} diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs new file mode 100644 index 0000000000..5971f053c2 --- /dev/null +++ b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Osu +{ + public enum OsuSkinComponents + { + HitCircle, + FollowPoint, + Cursor, + SliderScorePoint, + ApproachCircle, + ReverseArrow, + HitCircleText, + SliderFollowCircle, + SliderBall + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index a7906ddd24..83d507f64b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Skinning Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - new SkinnableSpriteText("Play/osu/number-text", _ => new OsuSpriteText + new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText { Font = OsuFont.Numeric.With(size: 40), UseFullGlyphHeight = false, diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs index 3c508f34e0..002b3f8cda 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs @@ -55,14 +55,17 @@ namespace osu.Game.Rulesets.Osu.Skinning hasHitCircle = new Lazy(() => source.GetTexture("hitcircle") != null); } - public Drawable GetDrawableComponent(string componentName) + public Drawable GetDrawableComponent(ISkinComponent component) { - switch (componentName) - { - case "Play/osu/sliderfollowcircle": - return this.GetAnimation(componentName, true, true); + if (!(component is OsuSkinComponent osuComponent)) + return null; - case "Play/osu/sliderball": + switch (osuComponent.Component) + { + case OsuSkinComponents.SliderFollowCircle: + return this.GetAnimation("sliderfollowcircle", true, true); + + case OsuSkinComponents.SliderBall: var sliderBallContent = this.GetAnimation("sliderb", true, true, ""); if (sliderBallContent != null) @@ -80,20 +83,19 @@ namespace osu.Game.Rulesets.Osu.Skinning return null; - case "Play/osu/hitcircle": + case OsuSkinComponents.HitCircle: if (hasHitCircle.Value) return new LegacyMainCirclePiece(); return null; - case "Play/osu/cursor": + case OsuSkinComponents.Cursor: if (source.GetTexture("cursor") != null) return new LegacyCursor(); return null; - case "Play/osu/number-text": - + case OsuSkinComponents.HitCircleText: string font = GetValue(config => config.HitCircleFont); var overlap = GetValue(config => config.HitCircleOverlap); diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index eb1977a13d..869c27dcac 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, Anchor = Anchor.Centre, - Child = scaleTarget = new SkinnableDrawable("Play/osu/cursor", _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling) + Child = scaleTarget = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.Cursor), _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling) { Origin = Anchor.Centre, Anchor = Anchor.Centre, diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 6d0a5eb1e1..7fdb823388 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Taiko public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableTaikoRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap); + public const string SHORT_NAME = "taiko"; + public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] { new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre), @@ -116,7 +118,7 @@ namespace osu.Game.Rulesets.Taiko public override string Description => "osu!taiko"; - public override string ShortName => "taiko"; + public override string ShortName => SHORT_NAME; public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetTaiko }; diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponent.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponent.cs new file mode 100644 index 0000000000..474154279c --- /dev/null +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponent.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Taiko +{ + public class TaikoSkinComponent : PlaySkinComponent + { + public TaikoSkinComponent(TaikoSkinComponents component) + : base(component) + { + } + + protected override string RulesetPrefix => TaikoRuleset.SHORT_NAME; + + protected override string ComponentName => Component.ToString().ToLower(); + } +} diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs new file mode 100644 index 0000000000..04aca534c6 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs @@ -0,0 +1,9 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Taiko +{ + public enum TaikoSkinComponents + { + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs index 96dc864577..ee5552c6e0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs @@ -137,8 +137,8 @@ namespace osu.Game.Tests.Visual.Gameplay { public new Drawable Drawable => base.Drawable; - public ExposedSkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) - : base(name, defaultImplementation, allowFallback, confineMode) + public ExposedSkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + : base(new TestSkinComponent(name), defaultImplementation, allowFallback, confineMode) { } } @@ -206,8 +206,8 @@ namespace osu.Game.Tests.Visual.Gameplay public new Drawable Drawable => base.Drawable; public int SkinChangedCount { get; private set; } - public SkinConsumer(string name, Func defaultImplementation, Func allowFallback = null) - : base(name, defaultImplementation, allowFallback) + public SkinConsumer(string name, Func defaultImplementation, Func allowFallback = null) + : base(new TestSkinComponent(name), defaultImplementation, allowFallback) { } @@ -243,8 +243,8 @@ namespace osu.Game.Tests.Visual.Gameplay this.size = size; } - public Drawable GetDrawableComponent(string componentName) => - componentName == "available" + public Drawable GetDrawableComponent(ISkinComponent componentName) => + componentName.LookupName == "available" ? new DrawWidthBox { Colour = Color4.Yellow, @@ -261,7 +261,7 @@ namespace osu.Game.Tests.Visual.Gameplay private class SecondarySource : ISkin { - public Drawable GetDrawableComponent(string componentName) => new SecondarySourceBox(); + public Drawable GetDrawableComponent(ISkinComponent componentName) => new SecondarySourceBox(); public Texture GetTexture(string componentName) => throw new NotImplementedException(); @@ -272,7 +272,7 @@ namespace osu.Game.Tests.Visual.Gameplay private class SkinSourceContainer : Container, ISkin { - public Drawable GetDrawableComponent(string componentName) => new BaseSourceBox(); + public Drawable GetDrawableComponent(ISkinComponent componentName) => new BaseSourceBox(); public Texture GetTexture(string componentName) => throw new NotImplementedException(); @@ -280,5 +280,19 @@ namespace osu.Game.Tests.Visual.Gameplay public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); } + + private class TestSkinComponent : ISkinComponent + { + private readonly string name; + + public TestSkinComponent(string name) + { + this.name = name; + } + + public string ComponentGroup => string.Empty; + + public string LookupName => name; + } } } diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 61c2644c6f..ecbdc53493 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Judgements Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Child = new SkinnableDrawable($"Play/{Result.Type}", _ => JudgementText = new OsuSpriteText + Child = new SkinnableDrawable(new PlaySkinComponent(Result.Type), _ => JudgementText = new OsuSpriteText { Text = Result.Type.GetDescription().ToUpperInvariant(), Font = OsuFont.Numeric.With(size: 12), diff --git a/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs b/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs index 345df35b12..40335db697 100644 --- a/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs +++ b/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Skinning private readonly Bindable beatmapHitsounds = new Bindable(); protected override bool AllowConfigurationLookup => beatmapSkins.Value; - protected override bool AllowDrawableLookup(string componentName) => beatmapSkins.Value; + protected override bool AllowDrawableLookup(ISkinComponent component) => beatmapSkins.Value; protected override bool AllowTextureLookup(string componentName) => beatmapSkins.Value; protected override bool AllowSampleLookup(ISampleInfo componentName) => beatmapHitsounds.Value; diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 6072bb64ed..f917514877 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Audio.Sample; @@ -16,7 +16,7 @@ namespace osu.Game.Skinning Configuration = new SkinConfiguration(); } - public override Drawable GetDrawableComponent(string componentName) => null; + public override Drawable GetDrawableComponent(ISkinComponent component) => null; public override Texture GetTexture(string componentName) => null; diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index 4867aba0a9..bc1ae634c9 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.cs @@ -14,7 +14,7 @@ namespace osu.Game.Skinning /// public interface ISkin { - Drawable GetDrawableComponent(string componentName); + Drawable GetDrawableComponent(ISkinComponent component); Texture GetTexture(string componentName); diff --git a/osu.Game/Skinning/ISkinComponent.cs b/osu.Game/Skinning/ISkinComponent.cs new file mode 100644 index 0000000000..4bd9f21b6b --- /dev/null +++ b/osu.Game/Skinning/ISkinComponent.cs @@ -0,0 +1,10 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Skinning +{ + public interface ISkinComponent + { + string LookupName { get; } + } +} diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 1572c588e8..179b93d405 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -47,39 +47,24 @@ namespace osu.Game.Skinning Samples?.Dispose(); } - public override Drawable GetDrawableComponent(string componentName) + public override Drawable GetDrawableComponent(ISkinComponent component) { - bool animatable = false; - bool looping = true; - - switch (componentName) + switch (component.LookupName) { case "Play/Miss": - componentName = "hit0"; - animatable = true; - looping = false; - break; + return this.GetAnimation("hit0", true, false); case "Play/Meh": - componentName = "hit50"; - animatable = true; - looping = false; - break; + return this.GetAnimation("hit50", true, false); case "Play/Good": - componentName = "hit100"; - animatable = true; - looping = false; - break; + return this.GetAnimation("hit100", true, false); case "Play/Great": - componentName = "hit300"; - animatable = true; - looping = false; - break; + return this.GetAnimation("hit300", true, false); } - return this.GetAnimation(componentName, animatable, looping); + return this.GetAnimation(component.LookupName, false, false); } public override Texture GetTexture(string componentName) diff --git a/osu.Game/Skinning/PlaySkinComponent.cs b/osu.Game/Skinning/PlaySkinComponent.cs new file mode 100644 index 0000000000..f228d5cf9c --- /dev/null +++ b/osu.Game/Skinning/PlaySkinComponent.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; + +namespace osu.Game.Skinning +{ + public class PlaySkinComponent : ISkinComponent where T : struct + { + public readonly T Component; + + public PlaySkinComponent(T component) + { + this.Component = component; + } + + protected virtual string RulesetPrefix => string.Empty; + protected virtual string ComponentName => Component.ToString(); + + public string LookupName => + string.Join("/", new[] { "Play", RulesetPrefix, ComponentName }.Where(s => !string.IsNullOrEmpty(s))); + } +} diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 027d9df8b8..299f257e57 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -15,7 +15,7 @@ namespace osu.Game.Skinning public virtual SkinConfiguration Configuration { get; protected set; } - public abstract Drawable GetDrawableComponent(string componentName); + public abstract Drawable GetDrawableComponent(ISkinComponent componentName); public abstract SampleChannel GetSample(ISampleInfo sampleInfo); diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index a713933c6e..a55a128dff 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -125,7 +125,7 @@ namespace osu.Game.Skinning public event Action SourceChanged; - public Drawable GetDrawableComponent(string componentName) => CurrentSkin.Value.GetDrawableComponent(componentName); + public Drawable GetDrawableComponent(ISkinComponent component) => CurrentSkin.Value.GetDrawableComponent(component); public Texture GetTexture(string componentName) => CurrentSkin.Value.GetTexture(componentName); diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index 45b8baa0bb..85a80655ea 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Skinning private ISkinSource fallbackSource; - protected virtual bool AllowDrawableLookup(string componentName) => true; + protected virtual bool AllowDrawableLookup(ISkinComponent component) => true; protected virtual bool AllowTextureLookup(string componentName) => true; @@ -37,13 +37,13 @@ namespace osu.Game.Skinning RelativeSizeAxes = Axes.Both; } - public Drawable GetDrawableComponent(string componentName) + public Drawable GetDrawableComponent(ISkinComponent component) { Drawable sourceDrawable; - if (AllowDrawableLookup(componentName) && (sourceDrawable = skin?.GetDrawableComponent(componentName)) != null) + if (AllowDrawableLookup(component) && (sourceDrawable = skin?.GetDrawableComponent(component)) != null) return sourceDrawable; - return fallbackSource?.GetDrawableComponent(componentName); + return fallbackSource?.GetDrawableComponent(component); } public Texture GetTexture(string componentName) diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index 07f802944b..9ca5d60cb0 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -18,39 +18,39 @@ namespace osu.Game.Skinning /// public Drawable Drawable { get; private set; } - private readonly string componentName; + private readonly ISkinComponent component; private readonly ConfineMode confineMode; /// /// Create a new skinnable drawable. /// - /// The namespace-complete resource name for this skinnable element. + /// The namespace-complete resource name for this skinnable element. /// A function to create the default skin implementation of this element. /// A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present. /// How (if at all) the should be resize to fit within our own bounds. - public SkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) - : this(name, allowFallback, confineMode) + public SkinnableDrawable(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + : this(component, allowFallback, confineMode) { createDefault = defaultImplementation; } - protected SkinnableDrawable(string name, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + protected SkinnableDrawable(ISkinComponent component, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) : base(allowFallback) { - componentName = name; + this.component = component; this.confineMode = confineMode; RelativeSizeAxes = Axes.Both; } - private readonly Func createDefault; + private readonly Func createDefault; private readonly Cached scaling = new Cached(); private bool isDefault; - protected virtual Drawable CreateDefault(string name) => createDefault(name); + protected virtual Drawable CreateDefault(ISkinComponent component) => createDefault(component); /// /// Whether to apply size restrictions (specified via ) to the default implementation. @@ -59,13 +59,13 @@ namespace osu.Game.Skinning protected override void SkinChanged(ISkinSource skin, bool allowFallback) { - Drawable = skin.GetDrawableComponent(componentName); + Drawable = skin.GetDrawableComponent(component); isDefault = false; if (Drawable == null && allowFallback) { - Drawable = CreateDefault(componentName); + Drawable = CreateDefault(component); isDefault = true; } diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index 07ba48d6ae..0081aef520 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -19,11 +19,11 @@ namespace osu.Game.Skinning [Resolved] private TextureStore textures { get; set; } - public SkinnableSprite(string name, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) - : base(name, allowFallback, confineMode) + public SkinnableSprite(ISkinComponent component, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + : base(component, allowFallback, confineMode) { } - protected override Drawable CreateDefault(string name) => new Sprite { Texture = textures.Get(name) }; + protected override Drawable CreateDefault(ISkinComponent component) => new Sprite { Texture = textures.Get(component.LookupName) }; } } diff --git a/osu.Game/Skinning/SkinnableSpriteText.cs b/osu.Game/Skinning/SkinnableSpriteText.cs index 5af6df15e1..e72f9c9811 100644 --- a/osu.Game/Skinning/SkinnableSpriteText.cs +++ b/osu.Game/Skinning/SkinnableSpriteText.cs @@ -8,8 +8,8 @@ namespace osu.Game.Skinning { public class SkinnableSpriteText : SkinnableDrawable, IHasText { - public SkinnableSpriteText(string name, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) - : base(name, defaultImplementation, allowFallback, confineMode) + public SkinnableSpriteText(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + : base(component, defaultImplementation, allowFallback, confineMode) { } From a7c94c388307e02980a172ca357bb3da1fd78f63 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 15:12:03 +0900 Subject: [PATCH 2448/2854] Simplify hit result lookups --- osu.Game/Skinning/LegacySkin.cs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 179b93d405..e51bf8245c 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Game.Audio; +using osu.Game.Rulesets.Scoring; using osuTK.Graphics; namespace osu.Game.Skinning @@ -49,19 +50,25 @@ namespace osu.Game.Skinning public override Drawable GetDrawableComponent(ISkinComponent component) { - switch (component.LookupName) + switch (component) { - case "Play/Miss": - return this.GetAnimation("hit0", true, false); + case PlaySkinComponent resultComponent: + switch (resultComponent.Component) + { + case HitResult.Miss: + return this.GetAnimation("hit0", true, false); - case "Play/Meh": - return this.GetAnimation("hit50", true, false); + case HitResult.Meh: + return this.GetAnimation("hit50", true, false); - case "Play/Good": - return this.GetAnimation("hit100", true, false); + case HitResult.Good: + return this.GetAnimation("hit100", true, false); - case "Play/Great": - return this.GetAnimation("hit300", true, false); + case HitResult.Great: + return this.GetAnimation("hit300", true, false); + } + + break; } return this.GetAnimation(component.LookupName, false, false); From 70e417533f47009049075747a8bc87636b633b53 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 15:10:11 +0900 Subject: [PATCH 2449/2854] Update naming --- osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs | 4 ++-- osu.Game.Rulesets.Catch/CatchSkinComponent.cs | 2 +- osu.Game.Rulesets.Mania/ManiaSkinComponent.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 2 +- .../Objects/Drawables/Pieces/CirclePiece.cs | 2 +- osu.Game.Rulesets.Osu/OsuSkinComponent.cs | 2 +- osu.Game.Rulesets.Taiko/TaikoSkinComponent.cs | 2 +- osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 8 ++++---- osu.Game/Rulesets/Judgements/DrawableJudgement.cs | 2 +- .../{PlaySkinComponent.cs => GameplaySkinComponent.cs} | 6 +++--- osu.Game/Skinning/LegacySkin.cs | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) rename osu.Game/Skinning/{PlaySkinComponent.cs => GameplaySkinComponent.cs} (66%) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 13286f4524..c89cd95f36 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Catch.Tests { switch (component.LookupName) { - case "Play/Catch/fruit-catcher-idle": + case "Gameplay/Catch/fruit-catcher-idle": return new CatcherCustomSkin(); } diff --git a/osu.Game.Rulesets.Catch/CatchSkinComponent.cs b/osu.Game.Rulesets.Catch/CatchSkinComponent.cs index 620720310f..0a3e43dcfc 100644 --- a/osu.Game.Rulesets.Catch/CatchSkinComponent.cs +++ b/osu.Game.Rulesets.Catch/CatchSkinComponent.cs @@ -5,7 +5,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch { - public class CatchSkinComponent : PlaySkinComponent + public class CatchSkinComponent : GameplaySkinComponent { public CatchSkinComponent(CatchSkinComponents component) : base(component) diff --git a/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs b/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs index 72a3ce7ad5..69bd4b0ecf 100644 --- a/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs +++ b/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs @@ -5,7 +5,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Mania { - public class ManiaSkinComponent : PlaySkinComponent + public class ManiaSkinComponent : GameplaySkinComponent { public ManiaSkinComponent(ManiaSkinComponents component) : base(component) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index 445f81c6d4..1eb37f8119 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -188,7 +188,7 @@ namespace osu.Game.Rulesets.Osu.Mods [BackgroundDependencyLoader] private void load(TextureStore textures) { - Texture = textures.Get("Play/osu/blinds-panel"); + Texture = textures.Get("Gameplay/osu/blinds-panel"); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index a59cfc1123..210d5ff839 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Texture = textures.Get(@"Play/osu/disc"), + Texture = textures.Get(@"Gameplay/osu/disc"), }, new TrianglesPiece { diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponent.cs b/osu.Game.Rulesets.Osu/OsuSkinComponent.cs index ef0df6cbda..1d223f231b 100644 --- a/osu.Game.Rulesets.Osu/OsuSkinComponent.cs +++ b/osu.Game.Rulesets.Osu/OsuSkinComponent.cs @@ -5,7 +5,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu { - public class OsuSkinComponent : PlaySkinComponent + public class OsuSkinComponent : GameplaySkinComponent { public OsuSkinComponent(OsuSkinComponents component) : base(component) diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponent.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponent.cs index 474154279c..e6e4bc0dd7 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponent.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponent.cs @@ -5,7 +5,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko { - public class TaikoSkinComponent : PlaySkinComponent + public class TaikoSkinComponent : GameplaySkinComponent { public TaikoSkinComponent(TaikoSkinComponents component) : base(component) diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index d6866c7d25..9766da9a24 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -132,10 +132,10 @@ namespace osu.Game.Rulesets.Taiko.UI [BackgroundDependencyLoader] private void load(TextureStore textures, OsuColour colours) { - rim.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer"); - rimHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer-hit"); - centre.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner"); - centreHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner-hit"); + rim.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-outer"); + rimHit.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-outer-hit"); + centre.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-inner"); + centreHit.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-inner-hit"); rimHit.Colour = colours.Blue; centreHit.Colour = colours.Pink; diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index ecbdc53493..4f8cb7660b 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Judgements Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Child = new SkinnableDrawable(new PlaySkinComponent(Result.Type), _ => JudgementText = new OsuSpriteText + Child = new SkinnableDrawable(new GameplaySkinComponent(Result.Type), _ => JudgementText = new OsuSpriteText { Text = Result.Type.GetDescription().ToUpperInvariant(), Font = OsuFont.Numeric.With(size: 12), diff --git a/osu.Game/Skinning/PlaySkinComponent.cs b/osu.Game/Skinning/GameplaySkinComponent.cs similarity index 66% rename from osu.Game/Skinning/PlaySkinComponent.cs rename to osu.Game/Skinning/GameplaySkinComponent.cs index f228d5cf9c..da65084a1d 100644 --- a/osu.Game/Skinning/PlaySkinComponent.cs +++ b/osu.Game/Skinning/GameplaySkinComponent.cs @@ -5,11 +5,11 @@ using System.Linq; namespace osu.Game.Skinning { - public class PlaySkinComponent : ISkinComponent where T : struct + public class GameplaySkinComponent : ISkinComponent where T : struct { public readonly T Component; - public PlaySkinComponent(T component) + public GameplaySkinComponent(T component) { this.Component = component; } @@ -18,6 +18,6 @@ namespace osu.Game.Skinning protected virtual string ComponentName => Component.ToString(); public string LookupName => - string.Join("/", new[] { "Play", RulesetPrefix, ComponentName }.Where(s => !string.IsNullOrEmpty(s))); + string.Join("/", new[] { "Gameplay", RulesetPrefix, ComponentName }.Where(s => !string.IsNullOrEmpty(s))); } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index e51bf8245c..753b2ba41b 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -52,7 +52,7 @@ namespace osu.Game.Skinning { switch (component) { - case PlaySkinComponent resultComponent: + case GameplaySkinComponent resultComponent: switch (resultComponent.Component) { case HitResult.Miss: From 835ee0aa2fbd75ed38782a54750135131396e708 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 15:29:13 +0900 Subject: [PATCH 2450/2854] Code quality fixes --- .../Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs index fae1b68e4a..fbd6aedd45 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs @@ -18,10 +18,13 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private const int margin = 10; private readonly Bindable type = new Bindable(); + private readonly HitWindows hitWindows; + private readonly ScoreProcessor processor; private BarHitErrorDisplay leftDisplay; + private BarHitErrorDisplay rightDisplay; public HitErrorDisplayOverlay(ScoreProcessor processor) @@ -40,10 +43,10 @@ namespace osu.Game.Screens.Play.HitErrorDisplay protected override void LoadComplete() { base.LoadComplete(); - type.BindValueChanged(onTypeChanged, true); + type.BindValueChanged(typeChanged, true); } - private void onTypeChanged(ValueChangedEvent type) + private void typeChanged(ValueChangedEvent type) { switch (type.NewValue) { From fe90e194e37b977ebcd2232f0af0b5177efef571 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 15:29:44 +0900 Subject: [PATCH 2451/2854] Remove redundant qualifier --- osu.Game/Skinning/PlaySkinComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/PlaySkinComponent.cs b/osu.Game/Skinning/PlaySkinComponent.cs index f228d5cf9c..68b67ee0c5 100644 --- a/osu.Game/Skinning/PlaySkinComponent.cs +++ b/osu.Game/Skinning/PlaySkinComponent.cs @@ -11,7 +11,7 @@ namespace osu.Game.Skinning public PlaySkinComponent(T component) { - this.Component = component; + Component = component; } protected virtual string RulesetPrefix => string.Empty; From bdbfa7bd2f5a4afde5c21478144f76a49dc25b2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 15:32:47 +0900 Subject: [PATCH 2452/2854] Fix class naming --- ...isplay.cs => TestSceneBarHitErrorMeter.cs} | 12 +- osu.Game/Screens/Play/HUDOverlay.cs | 7 +- ...HitErrorDisplay.cs => BarHitErrorMeter.cs} | 4 +- .../Play/HitErrorDisplay/HitErrorDisplay.cs | 120 +++++++++++++++- .../HitErrorDisplay/HitErrorDisplayOverlay.cs | 129 ------------------ .../Play/HitErrorDisplay/HitErrorMeter.cs | 21 +++ 6 files changed, 146 insertions(+), 147 deletions(-) rename osu.Game.Tests/Visual/Gameplay/{TestSceneHitErrorDisplay.cs => TestSceneBarHitErrorMeter.cs} (94%) rename osu.Game/Screens/Play/HitErrorDisplay/{BarHitErrorDisplay.cs => BarHitErrorMeter.cs} (98%) delete mode 100644 osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs create mode 100644 osu.Game/Screens/Play/HitErrorDisplay/HitErrorMeter.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs similarity index 94% rename from osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs rename to osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs index a148bdad67..aac9e206c3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs @@ -19,16 +19,16 @@ using osu.Framework.Graphics.Sprites; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneHitErrorDisplay : OsuTestScene + public class TestSceneBarHitErrorMeter : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { - typeof(HitErrorDisplay), + typeof(HitErrorMeter), }; - private HitErrorDisplay display; + private HitErrorMeter meter; - public TestSceneHitErrorDisplay() + public TestSceneBarHitErrorMeter() { recreateDisplay(new OsuHitWindows(), 5); AddStep("New random judgement", () => newJudgement()); @@ -115,7 +115,7 @@ namespace osu.Game.Tests.Visual.Gameplay } }); - Add(display = new BarHitErrorDisplay(hitWindows) + Add(meter = new BarHitErrorMeter(hitWindows) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -124,7 +124,7 @@ namespace osu.Game.Tests.Visual.Gameplay private void newJudgement(float offset = 0) { - display?.OnNewJudgement(new JudgementResult(new Judgement()) + meter?.OnNewJudgement(new JudgementResult(new Judgement()) { TimeOffset = offset == 0 ? RNG.Next(-70, 70) : offset, Type = HitResult.Perfect, diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 02432cf64e..79392221e4 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -15,7 +15,6 @@ using osu.Game.Overlays.Notifications; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; -using osu.Game.Screens.Play.HitErrorDisplay; using osu.Game.Screens.Play.HUD; using osuTK; using osuTK.Input; @@ -34,7 +33,7 @@ namespace osu.Game.Screens.Play public readonly HealthDisplay HealthDisplay; public readonly SongProgress Progress; public readonly ModDisplay ModDisplay; - public readonly HitErrorDisplayOverlay HitErrorDisplayOverlay; + public readonly HitErrorDisplay.HitErrorDisplay HitErrorDisplay; public readonly HoldForMenuButton HoldToQuit; public readonly PlayerSettingsOverlay PlayerSettingsOverlay; @@ -86,7 +85,7 @@ namespace osu.Game.Screens.Play HealthDisplay = CreateHealthDisplay(), Progress = CreateProgress(), ModDisplay = CreateModsContainer(), - HitErrorDisplayOverlay = CreateHitErrorDisplayOverlay(), + HitErrorDisplay = CreateHitErrorDisplayOverlay(), } }, PlayerSettingsOverlay = CreatePlayerSettingsOverlay(), @@ -259,7 +258,7 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20, Right = 10 }, }; - protected virtual HitErrorDisplayOverlay CreateHitErrorDisplayOverlay() => new HitErrorDisplayOverlay(scoreProcessor) + protected virtual HitErrorDisplay.HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay.HitErrorDisplay(scoreProcessor) { RelativeSizeAxes = Axes.Both, }; diff --git a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs similarity index 98% rename from osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs rename to osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs index 85d017073a..3ec3740816 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs @@ -18,7 +18,7 @@ using System.Linq; namespace osu.Game.Screens.Play.HitErrorDisplay { - public class BarHitErrorDisplay : HitErrorDisplay + public class BarHitErrorMeter : HitErrorMeter { /// /// The amount of which will be stored to calculate arrow position. @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private readonly List judgementOffsets = new List(); private readonly double maxHitWindows; - public BarHitErrorDisplay(HitWindows hitWindows, bool reversed = false) + public BarHitErrorMeter(HitWindows hitWindows, bool reversed = false) : base(hitWindows) { maxHitWindows = HitWindows.Meh == 0 ? HitWindows.Good : HitWindows.Meh; diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs index 422e151d8a..5c884f3f53 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs @@ -2,20 +2,128 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Judgements; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Scoring; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Rulesets.Objects; namespace osu.Game.Screens.Play.HitErrorDisplay { - public abstract class HitErrorDisplay : CompositeDrawable + public class HitErrorDisplay : Container { - protected readonly HitWindows HitWindows; + private const int fade_duration = 200; + private const int margin = 10; - protected HitErrorDisplay(HitWindows hitWindows) + private readonly Bindable type = new Bindable(); + + private readonly HitWindows hitWindows; + + private readonly ScoreProcessor processor; + + private BarHitErrorMeter leftMeter; + + private BarHitErrorMeter rightMeter; + + public HitErrorDisplay(ScoreProcessor processor) { - HitWindows = hitWindows; + this.processor = processor; + hitWindows = processor.CreateHitWindows(); } - public abstract void OnNewJudgement(JudgementResult newJudgement); + [BackgroundDependencyLoader] + private void load(OsuConfigManager config, Bindable workingBeatmap) + { + config.BindWith(OsuSetting.ScoreMeter, type); + hitWindows.SetDifficulty(workingBeatmap.Value.BeatmapInfo.BaseDifficulty.OverallDifficulty); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + type.BindValueChanged(typeChanged, true); + } + + private void typeChanged(ValueChangedEvent type) + { + switch (type.NewValue) + { + case ScoreMeterType.None: + removeLeftDisplay(); + removeRightDisplay(); + break; + + case ScoreMeterType.HitErrorBoth: + addLeftDisplay(); + addRightDisplay(); + break; + + case ScoreMeterType.HitErrorLeft: + addLeftDisplay(); + removeRightDisplay(); + break; + + case ScoreMeterType.HitErrorRight: + addRightDisplay(); + removeLeftDisplay(); + break; + } + } + + private void addLeftDisplay() + { + if (leftMeter != null) + return; + + leftMeter = createNew(); + } + + private void addRightDisplay() + { + if (rightMeter != null) + return; + + rightMeter = createNew(true); + } + + private void removeRightDisplay() + { + if (rightMeter == null) + return; + + processor.NewJudgement -= rightMeter.OnNewJudgement; + + rightMeter.FadeOut(fade_duration, Easing.OutQuint).Expire(); + rightMeter = null; + } + + private void removeLeftDisplay() + { + if (leftMeter == null) + return; + + processor.NewJudgement -= leftMeter.OnNewJudgement; + + leftMeter.FadeOut(fade_duration, Easing.OutQuint).Expire(); + leftMeter = null; + } + + private BarHitErrorMeter createNew(bool reversed = false) + { + var display = new BarHitErrorMeter(hitWindows, reversed) + { + Margin = new MarginPadding(margin), + Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Alpha = 0, + }; + + processor.NewJudgement += display.OnNewJudgement; + Add(display); + display.FadeInFromZero(fade_duration, Easing.OutQuint); + return display; + } } } diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs deleted file mode 100644 index fbd6aedd45..0000000000 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplayOverlay.cs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Scoring; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Game.Beatmaps; -using osu.Game.Configuration; -using osu.Game.Rulesets.Objects; - -namespace osu.Game.Screens.Play.HitErrorDisplay -{ - public class HitErrorDisplayOverlay : Container - { - private const int fade_duration = 200; - private const int margin = 10; - - private readonly Bindable type = new Bindable(); - - private readonly HitWindows hitWindows; - - private readonly ScoreProcessor processor; - - private BarHitErrorDisplay leftDisplay; - - private BarHitErrorDisplay rightDisplay; - - public HitErrorDisplayOverlay(ScoreProcessor processor) - { - this.processor = processor; - hitWindows = processor.CreateHitWindows(); - } - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config, Bindable workingBeatmap) - { - config.BindWith(OsuSetting.ScoreMeter, type); - hitWindows.SetDifficulty(workingBeatmap.Value.BeatmapInfo.BaseDifficulty.OverallDifficulty); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - type.BindValueChanged(typeChanged, true); - } - - private void typeChanged(ValueChangedEvent type) - { - switch (type.NewValue) - { - case ScoreMeterType.None: - removeLeftDisplay(); - removeRightDisplay(); - break; - - case ScoreMeterType.HitErrorBoth: - addLeftDisplay(); - addRightDisplay(); - break; - - case ScoreMeterType.HitErrorLeft: - addLeftDisplay(); - removeRightDisplay(); - break; - - case ScoreMeterType.HitErrorRight: - addRightDisplay(); - removeLeftDisplay(); - break; - } - } - - private void addLeftDisplay() - { - if (leftDisplay != null) - return; - - leftDisplay = createNew(); - } - - private void addRightDisplay() - { - if (rightDisplay != null) - return; - - rightDisplay = createNew(true); - } - - private void removeRightDisplay() - { - if (rightDisplay == null) - return; - - processor.NewJudgement -= rightDisplay.OnNewJudgement; - - rightDisplay.FadeOut(fade_duration, Easing.OutQuint).Expire(); - rightDisplay = null; - } - - private void removeLeftDisplay() - { - if (leftDisplay == null) - return; - - processor.NewJudgement -= leftDisplay.OnNewJudgement; - - leftDisplay.FadeOut(fade_duration, Easing.OutQuint).Expire(); - leftDisplay = null; - } - - private BarHitErrorDisplay createNew(bool reversed = false) - { - var display = new BarHitErrorDisplay(hitWindows, reversed) - { - Margin = new MarginPadding(margin), - Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, - Alpha = 0, - }; - - processor.NewJudgement += display.OnNewJudgement; - Add(display); - display.FadeInFromZero(fade_duration, Easing.OutQuint); - return display; - } - } -} diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorMeter.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorMeter.cs new file mode 100644 index 0000000000..848e892eaa --- /dev/null +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorMeter.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Screens.Play.HitErrorDisplay +{ + public abstract class HitErrorMeter : CompositeDrawable + { + protected readonly HitWindows HitWindows; + + protected HitErrorMeter(HitWindows hitWindows) + { + HitWindows = hitWindows; + } + + public abstract void OnNewJudgement(JudgementResult newJudgement); + } +} From 6640161bc1fde5e88fff7e1084c83468670660ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 15:51:36 +0900 Subject: [PATCH 2453/2854] Simplify event propagation --- .../Play/HitErrorDisplay/BarHitErrorMeter.cs | 16 ++-- .../Play/HitErrorDisplay/HitErrorDisplay.cs | 90 ++++++------------- 2 files changed, 36 insertions(+), 70 deletions(-) diff --git a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs index 3ec3740816..b00f3f6f5f 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private readonly List judgementOffsets = new List(); private readonly double maxHitWindows; - public BarHitErrorMeter(HitWindows hitWindows, bool reversed = false) + public BarHitErrorMeter(HitWindows hitWindows, bool rightAligned = false) : base(hitWindows) { maxHitWindows = HitWindows.Meh == 0 ? HitWindows.Good : HitWindows.Meh; @@ -54,23 +54,23 @@ namespace osu.Game.Screens.Play.HitErrorDisplay { judgementsContainer = new Container { - Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Anchor = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft, Width = judgement_line_width, RelativeSizeAxes = Axes.Y, }, bar = new FillFlowContainer { - Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Anchor = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft, Width = bar_width, RelativeSizeAxes = Axes.Y, Direction = FillDirection.Vertical, }, new Container { - Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Anchor = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft, AutoSizeAxes = Axes.X, RelativeSizeAxes = Axes.Y, Child = arrow = new SpriteIcon @@ -78,7 +78,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativePositionAxes = Axes.Y, - Icon = reversed ? FontAwesome.Solid.ChevronRight : FontAwesome.Solid.ChevronLeft, + Icon = rightAligned ? FontAwesome.Solid.ChevronRight : FontAwesome.Solid.ChevronLeft, Size = new Vector2(8), } }, diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs index 5c884f3f53..eaaf8e810c 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs @@ -1,13 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using osu.Game.Rulesets.Scoring; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; namespace osu.Game.Screens.Play.HitErrorDisplay @@ -19,25 +22,33 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private readonly Bindable type = new Bindable(); - private readonly HitWindows hitWindows; + private HitWindows hitWindows; private readonly ScoreProcessor processor; - private BarHitErrorMeter leftMeter; - - private BarHitErrorMeter rightMeter; - public HitErrorDisplay(ScoreProcessor processor) { this.processor = processor; - hitWindows = processor.CreateHitWindows(); + processor.NewJudgement += onNewJudgement; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + processor.NewJudgement -= onNewJudgement; + } + + private void onNewJudgement(JudgementResult result) + { + foreach (var c in Children) + c.OnNewJudgement(result); } [BackgroundDependencyLoader] private void load(OsuConfigManager config, Bindable workingBeatmap) { config.BindWith(OsuSetting.ScoreMeter, type); - hitWindows.SetDifficulty(workingBeatmap.Value.BeatmapInfo.BaseDifficulty.OverallDifficulty); + hitWindows = workingBeatmap.Value.Beatmap.HitObjects.First().HitWindows; } protected override void LoadComplete() @@ -48,82 +59,37 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private void typeChanged(ValueChangedEvent type) { + Children.ForEach(c => c.FadeOut(fade_duration, Easing.OutQuint)); + switch (type.NewValue) { - case ScoreMeterType.None: - removeLeftDisplay(); - removeRightDisplay(); - break; - case ScoreMeterType.HitErrorBoth: - addLeftDisplay(); - addRightDisplay(); + createBar(false); + createBar(true); break; case ScoreMeterType.HitErrorLeft: - addLeftDisplay(); - removeRightDisplay(); + createBar(false); break; case ScoreMeterType.HitErrorRight: - addRightDisplay(); - removeLeftDisplay(); + createBar(true); break; } } - private void addLeftDisplay() + private void createBar(bool rightAligned) { - if (leftMeter != null) - return; - - leftMeter = createNew(); - } - - private void addRightDisplay() - { - if (rightMeter != null) - return; - - rightMeter = createNew(true); - } - - private void removeRightDisplay() - { - if (rightMeter == null) - return; - - processor.NewJudgement -= rightMeter.OnNewJudgement; - - rightMeter.FadeOut(fade_duration, Easing.OutQuint).Expire(); - rightMeter = null; - } - - private void removeLeftDisplay() - { - if (leftMeter == null) - return; - - processor.NewJudgement -= leftMeter.OnNewJudgement; - - leftMeter.FadeOut(fade_duration, Easing.OutQuint).Expire(); - leftMeter = null; - } - - private BarHitErrorMeter createNew(bool reversed = false) - { - var display = new BarHitErrorMeter(hitWindows, reversed) + var display = new BarHitErrorMeter(hitWindows, rightAligned) { Margin = new MarginPadding(margin), - Anchor = reversed ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = reversed ? Anchor.CentreRight : Anchor.CentreLeft, + Anchor = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft, + Origin = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft, Alpha = 0, }; - processor.NewJudgement += display.OnNewJudgement; Add(display); display.FadeInFromZero(fade_duration, Easing.OutQuint); - return display; } } } From b03b520818eb2e9474fa7f7ea7daf01c287263b5 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 30 Aug 2019 10:13:21 +0300 Subject: [PATCH 2454/2854] Move Absing from the APIKudosuHistory --- osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs | 6 ------ .../Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs | 3 ++- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs index 19ce11aa13..67ff20e6c2 100644 --- a/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs +++ b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs @@ -13,12 +13,6 @@ namespace osu.Game.Online.API.Requests.Responses public DateTimeOffset CreatedAt; [JsonProperty("amount")] - private int amount - { - //We can receive negative values. However "action" is enough to build needed items - set => Amount = Math.Abs(value); - } - public int Amount; [JsonProperty("post")] diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index fb7d597012..94733324ba 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -9,6 +9,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests; using osu.Game.Online.Chat; +using System; namespace osu.Game.Overlays.Profile.Sections.Kudosu { @@ -56,7 +57,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu if (!string.IsNullOrEmpty(prefix)) { linkFlowContainer.AddText(prefix); - linkFlowContainer.AddText($@" {historyItem.Amount} kudosu", t => + linkFlowContainer.AddText($@" {Math.Abs(historyItem.Amount)} kudosu", t => { t.Font = t.Font.With(italics: true); t.Colour = colours.Blue; From 71c844facdff1219fc5b0a6e1189b81e6058c8d5 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 30 Aug 2019 10:22:49 +0300 Subject: [PATCH 2455/2854] Remove unwanted spacings --- .../Kudosu/DrawableKudosuHistoryItem.cs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index 94733324ba..408468fa73 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -10,6 +10,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests; using osu.Game.Online.Chat; using System; +using osuTK; namespace osu.Game.Overlays.Profile.Sections.Kudosu { @@ -37,6 +38,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, + Spacing = new Vector2(3), }, date = new DrawableDate(historyItem.CreatedAt) { @@ -57,7 +59,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu if (!string.IsNullOrEmpty(prefix)) { linkFlowContainer.AddText(prefix); - linkFlowContainer.AddText($@" {Math.Abs(historyItem.Amount)} kudosu", t => + linkFlowContainer.AddText($@"{Math.Abs(historyItem.Amount)} kudosu", t => { t.Font = t.Font.With(italics: true); t.Colour = colours.Blue; @@ -75,40 +77,40 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu switch (historyItem.Action) { case KudosuAction.VoteGive: - return @" from obtaining votes in modding post of"; + return @"from obtaining votes in modding post of"; case KudosuAction.Give: - return $@" from {userLink()} for a post at"; + return $@"from {userLink()} for a post at"; case KudosuAction.Reset: return $@"Kudosu reset by {userLink()} for the post"; case KudosuAction.VoteReset: - return @" from losing votes in modding post of"; + return @"from losing votes in modding post of"; case KudosuAction.DenyKudosuReset: - return @" from modding post"; + return @"from modding post"; case KudosuAction.Revoke: return $@"Denied kudosu by {userLink()} for the post"; case KudosuAction.AllowKudosuGive: - return @" from kudosu deny repeal of modding post"; + return @"from kudosu deny repeal of modding post"; case KudosuAction.DeleteReset: - return @" from modding post deletion of"; + return @"from modding post deletion of"; case KudosuAction.RestoreGive: - return @" from modding post restoration of"; + return @"from modding post restoration of"; case KudosuAction.RecalculateGive: - return @" from votes recalculation in modding post of"; + return @"from votes recalculation in modding post of"; case KudosuAction.RecalculateReset: - return @" from votes recalculation in modding post of"; + return @"from votes recalculation in modding post of"; default: - return @" from unknown event "; + return @"from unknown event"; } } From 72dbeaec1632ce6a94c71bc82141d447a1f19d4f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 30 Aug 2019 10:26:11 +0300 Subject: [PATCH 2456/2854] Fix the comment --- osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs index 67ff20e6c2..f2297f7a10 100644 --- a/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs +++ b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs @@ -44,7 +44,7 @@ namespace osu.Game.Online.API.Requests.Responses { set { - //We will receive something like "foo.bar" or just "foo" + //We will receive something like "event.action" or just "action" string parsed = value.Contains(".") ? value.Split('.')[0].Pascalize() + value.Split('.')[1].Pascalize() : value.Pascalize(); Action = (KudosuAction)Enum.Parse(typeof(KudosuAction), parsed); From 5f3e638499c1d8fde4362239bdbfef91b9add024 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 16:40:24 +0900 Subject: [PATCH 2457/2854] Make test useful --- .../Visual/Gameplay/TestSceneBarHitErrorMeter.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs index aac9e206c3..28c5f0ae08 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs @@ -30,8 +30,14 @@ namespace osu.Game.Tests.Visual.Gameplay public TestSceneBarHitErrorMeter() { - recreateDisplay(new OsuHitWindows(), 5); - AddStep("New random judgement", () => newJudgement()); + var hitWindows = new OsuHitWindows(); + + recreateDisplay(hitWindows, 5); + + AddRepeatStep("New random judgement", () => newJudgement(), 40); + + AddRepeatStep("New max negative", () => newJudgement(-hitWindows.Meh), 20); + AddRepeatStep("New max positive", () => newJudgement(hitWindows.Meh), 20); AddStep("New fixed judgement (50ms)", () => newJudgement(50)); } @@ -122,11 +128,11 @@ namespace osu.Game.Tests.Visual.Gameplay }); } - private void newJudgement(float offset = 0) + private void newJudgement(double offset = 0) { meter?.OnNewJudgement(new JudgementResult(new Judgement()) { - TimeOffset = offset == 0 ? RNG.Next(-70, 70) : offset, + TimeOffset = offset == 0 ? RNG.Next(-150, 150) : offset, Type = HitResult.Perfect, }); } From a73d672c2f3c0397d80400d8c3e96d4b22b55d2c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 16:40:39 +0900 Subject: [PATCH 2458/2854] Tidy up judgement line logic (and fix it displaying at the wrong place) --- .../Play/HitErrorDisplay/BarHitErrorMeter.cs | 119 +++++++++--------- .../Play/HitErrorDisplay/HitErrorMeter.cs | 2 +- 2 files changed, 63 insertions(+), 58 deletions(-) diff --git a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs index b00f3f6f5f..ffaada6ff4 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -8,42 +9,46 @@ using osu.Game.Rulesets.Judgements; using osuTK.Graphics; using osuTK; using osu.Framework.Graphics.Sprites; -using System.Collections.Generic; using osu.Game.Rulesets.Objects; using osu.Framework.Allocation; using osu.Framework.Graphics.Colour; using osu.Game.Graphics; using osu.Framework.Extensions.Color4Extensions; -using System.Linq; namespace osu.Game.Screens.Play.HitErrorDisplay { public class BarHitErrorMeter : HitErrorMeter { - /// - /// The amount of which will be stored to calculate arrow position. - /// - private const int stored_judgements_amount = 5; + private readonly bool rightAligned; private const int judgement_fade_duration = 10000; - private const int arrow_move_duration = 500; + + private const int arrow_move_duration = 400; + private const int judgement_line_width = 8; + private const int bar_height = 200; + private const int bar_width = 3; + private const int spacing = 3; private readonly SpriteIcon arrow; + private readonly FillFlowContainer bar; + private readonly Container judgementsContainer; - private readonly List judgementOffsets = new List(); - private readonly double maxHitWindows; + + private readonly double maxHitWindow; public BarHitErrorMeter(HitWindows hitWindows, bool rightAligned = false) : base(hitWindows) { - maxHitWindows = HitWindows.Meh == 0 ? HitWindows.Good : HitWindows.Meh; + this.rightAligned = rightAligned; + maxHitWindow = Math.Max(Math.Max(HitWindows.Meh, HitWindows.Ok), HitWindows.Good); AutoSizeAxes = Axes.Both; + AddInternal(new FillFlowContainer { AutoSizeAxes = Axes.X, @@ -75,9 +80,10 @@ namespace osu.Game.Screens.Play.HitErrorDisplay RelativeSizeAxes = Axes.Y, Child = arrow = new SpriteIcon { - Anchor = Anchor.Centre, + Anchor = Anchor.TopCentre, Origin = Anchor.Centre, RelativePositionAxes = Axes.Y, + Y = 0.5f, Icon = rightAligned ? FontAwesome.Solid.ChevronRight : FontAwesome.Solid.ChevronLeft, Size = new Vector2(8), } @@ -93,24 +99,20 @@ namespace osu.Game.Screens.Play.HitErrorDisplay { bar.AddRange(new[] { - createColoredPiece(ColourInfo.GradientVertical(colours.Yellow.Opacity(0), colours.Yellow), - (maxHitWindows - HitWindows.Good) / (maxHitWindows * 2)), - createColoredPiece(colours.Green, (HitWindows.Good - HitWindows.Great) / (maxHitWindows * 2)), - createColoredPiece(colours.BlueLight, HitWindows.Great / maxHitWindows), - createColoredPiece(colours.Green, (HitWindows.Good - HitWindows.Great) / (maxHitWindows * 2)), - createColoredPiece(ColourInfo.GradientVertical(colours.Yellow, colours.Yellow.Opacity(0)), - (maxHitWindows - HitWindows.Good) / (maxHitWindows * 2)) + createColoredPiece(ColourInfo.GradientVertical(colours.Yellow.Opacity(0), colours.Yellow), (maxHitWindow - HitWindows.Good) / (maxHitWindow * 2)), + createColoredPiece(colours.Green, (HitWindows.Good - HitWindows.Great) / (maxHitWindow * 2)), + createColoredPiece(colours.BlueLight, HitWindows.Great / maxHitWindow), + createColoredPiece(colours.Green, (HitWindows.Good - HitWindows.Great) / (maxHitWindow * 2)), + createColoredPiece(ColourInfo.GradientVertical(colours.Yellow, colours.Yellow.Opacity(0)), (maxHitWindow - HitWindows.Good) / (maxHitWindow * 2)) }); } else { bar.AddRange(new[] { - createColoredPiece(ColourInfo.GradientVertical(colours.Green.Opacity(0), colours.Green), - (HitWindows.Good - HitWindows.Great) / (maxHitWindows * 2)), - createColoredPiece(colours.BlueLight, HitWindows.Great / maxHitWindows), - createColoredPiece(ColourInfo.GradientVertical(colours.Green, colours.Green.Opacity(0)), - (HitWindows.Good - HitWindows.Great) / (maxHitWindows * 2)), + createColoredPiece(ColourInfo.GradientVertical(colours.Green.Opacity(0), colours.Green), (HitWindows.Good - HitWindows.Great) / (maxHitWindow * 2)), + createColoredPiece(colours.BlueLight, HitWindows.Great / maxHitWindow), + createColoredPiece(ColourInfo.GradientVertical(colours.Green, colours.Green.Opacity(0)), (HitWindows.Good - HitWindows.Great) / (maxHitWindow * 2)), }); } } @@ -122,51 +124,54 @@ namespace osu.Game.Screens.Play.HitErrorDisplay Height = (float)height }; - public override void OnNewJudgement(JudgementResult newJudgement) + private double floatingAverage; + + public override void OnNewJudgement(JudgementResult judgement) { - if (!newJudgement.IsHit) + if (!judgement.IsHit) return; - var judgementLine = CreateJudgementLine(newJudgement); + judgementsContainer.Add(new JudgementLine + { + Y = getRelativeJudgementPosition(judgement.TimeOffset), + Anchor = rightAligned ? Anchor.TopLeft : Anchor.TopRight, + Origin = rightAligned ? Anchor.TopLeft : Anchor.TopRight, + }); - judgementsContainer.Add(judgementLine); - - judgementLine.FadeOut(judgement_fade_duration, Easing.OutQuint).Expire(); - - arrow.MoveToY(calculateArrowPosition(newJudgement), arrow_move_duration, Easing.OutQuint); + arrow.MoveToY(getRelativeJudgementPosition(floatingAverage = floatingAverage * 0.9 + judgement.TimeOffset * 0.1) + , arrow_move_duration, Easing.Out); } - protected virtual Container CreateJudgementLine(JudgementResult judgement) => new CircularContainer + private float getRelativeJudgementPosition(double value) => (float)((value / maxHitWindow) + 1) / 2; + + public class JudgementLine : CompositeDrawable { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Masking = true, - RelativeSizeAxes = Axes.X, - Height = 2, - RelativePositionAxes = Axes.Y, - Y = getRelativeJudgementPosition(judgement.TimeOffset), - Child = new Box + public JudgementLine() { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, + RelativeSizeAxes = Axes.X; + RelativePositionAxes = Axes.Y; + Height = 2; + + InternalChild = new CircularContainer + { + Masking = true, + RelativeSizeAxes = Axes.Both, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + } + }; } - }; - private float getRelativeJudgementPosition(double value) => (float)(value / maxHitWindows); + protected override void LoadComplete() + { + base.LoadComplete(); - private float calculateArrowPosition(JudgementResult newJudgement) - { - judgementOffsets.Add(newJudgement.TimeOffset); - - if (judgementOffsets.Count < stored_judgements_amount) - return getRelativeJudgementPosition(judgementOffsets.Average()); - - double sum = 0; - - for (int i = judgementOffsets.Count - stored_judgements_amount; i < judgementOffsets.Count; i++) - sum += judgementOffsets[i]; - - return getRelativeJudgementPosition(sum / stored_judgements_amount); + Width = 0; + this.ResizeWidthTo(1, 150, Easing.OutElasticHalf); + this.FadeTo(0.8f, 150).Then().FadeOut(judgement_fade_duration, Easing.OutQuint).Expire(); + } } } } diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorMeter.cs b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorMeter.cs index 848e892eaa..e4599eb2fc 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/HitErrorMeter.cs @@ -16,6 +16,6 @@ namespace osu.Game.Screens.Play.HitErrorDisplay HitWindows = hitWindows; } - public abstract void OnNewJudgement(JudgementResult newJudgement); + public abstract void OnNewJudgement(JudgementResult judgement); } } From 54696eef3990a1a93f2f707653d2b1fef5071ddc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 17:06:23 +0900 Subject: [PATCH 2459/2854] Reverse display, add animation and reduce width --- .../Gameplay/TestSceneBarHitErrorMeter.cs | 24 +++++--- .../Play/HitErrorDisplay/BarHitErrorMeter.cs | 59 +++++++++++-------- 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs index 28c5f0ae08..334e0d3b90 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs @@ -27,6 +27,7 @@ namespace osu.Game.Tests.Visual.Gameplay }; private HitErrorMeter meter; + private HitErrorMeter meter2; public TestSceneBarHitErrorMeter() { @@ -109,8 +110,8 @@ namespace osu.Game.Tests.Visual.Gameplay Add(new FillFlowContainer { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Direction = FillDirection.Vertical, AutoSizeAxes = Axes.Both, Children = new[] @@ -121,20 +122,29 @@ namespace osu.Game.Tests.Visual.Gameplay } }); - Add(meter = new BarHitErrorMeter(hitWindows) + Add(meter = new BarHitErrorMeter(hitWindows, true) { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + }); + + Add(meter2 = new BarHitErrorMeter(hitWindows, false) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, }); } private void newJudgement(double offset = 0) { - meter?.OnNewJudgement(new JudgementResult(new Judgement()) + var judgement = new JudgementResult(new Judgement()) { TimeOffset = offset == 0 ? RNG.Next(-150, 150) : offset, Type = HitResult.Perfect, - }); + }; + + meter.OnNewJudgement(judgement); + meter2.OnNewJudgement(judgement); } } } diff --git a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs index ffaada6ff4..7fb7b3cf99 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs @@ -25,19 +25,19 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private const int arrow_move_duration = 400; - private const int judgement_line_width = 8; + private const int judgement_line_width = 6; private const int bar_height = 200; - private const int bar_width = 3; + private const int bar_width = 2; - private const int spacing = 3; + private const int spacing = 2; - private readonly SpriteIcon arrow; + private SpriteIcon arrow; - private readonly FillFlowContainer bar; + private FillFlowContainer colourBarFlow; - private readonly Container judgementsContainer; + private Container judgementsContainer; private readonly double maxHitWindow; @@ -48,34 +48,39 @@ namespace osu.Game.Screens.Play.HitErrorDisplay maxHitWindow = Math.Max(Math.Max(HitWindows.Meh, HitWindows.Ok), HitWindows.Good); AutoSizeAxes = Axes.Both; + } - AddInternal(new FillFlowContainer + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChild = new FillFlowContainer { AutoSizeAxes = Axes.X, Height = bar_height, Direction = FillDirection.Horizontal, Spacing = new Vector2(spacing, 0), + Margin = new MarginPadding(2), Children = new Drawable[] { judgementsContainer = new Container { - Anchor = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft, + Anchor = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, Width = judgement_line_width, RelativeSizeAxes = Axes.Y, }, - bar = new FillFlowContainer + colourBarFlow = new FillFlowContainer { - Anchor = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft, + Anchor = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, Width = bar_width, RelativeSizeAxes = Axes.Y, Direction = FillDirection.Vertical, }, new Container { - Anchor = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft, - Origin = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft, + Anchor = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, AutoSizeAxes = Axes.X, RelativeSizeAxes = Axes.Y, Child = arrow = new SpriteIcon @@ -84,20 +89,16 @@ namespace osu.Game.Screens.Play.HitErrorDisplay Origin = Anchor.Centre, RelativePositionAxes = Axes.Y, Y = 0.5f, - Icon = rightAligned ? FontAwesome.Solid.ChevronRight : FontAwesome.Solid.ChevronLeft, + Icon = rightAligned ? FontAwesome.Solid.ChevronLeft : FontAwesome.Solid.ChevronRight, Size = new Vector2(8), } }, } - }); - } + }; - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { if (HitWindows.Meh != 0) { - bar.AddRange(new[] + colourBarFlow.AddRange(new[] { createColoredPiece(ColourInfo.GradientVertical(colours.Yellow.Opacity(0), colours.Yellow), (maxHitWindow - HitWindows.Good) / (maxHitWindow * 2)), createColoredPiece(colours.Green, (HitWindows.Good - HitWindows.Great) / (maxHitWindow * 2)), @@ -108,7 +109,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay } else { - bar.AddRange(new[] + colourBarFlow.AddRange(new[] { createColoredPiece(ColourInfo.GradientVertical(colours.Green.Opacity(0), colours.Green), (HitWindows.Good - HitWindows.Great) / (maxHitWindow * 2)), createColoredPiece(colours.BlueLight, HitWindows.Great / maxHitWindow), @@ -117,6 +118,16 @@ namespace osu.Game.Screens.Play.HitErrorDisplay } } + protected override void LoadComplete() + { + base.LoadComplete(); + + colourBarFlow.Height = 0; + colourBarFlow.ResizeHeightTo(1, 400, Easing.OutQuint); + + arrow.FadeInFromZero(400); + } + private Box createColoredPiece(ColourInfo colour, double height) => new Box { RelativeSizeAxes = Axes.Both, @@ -134,8 +145,8 @@ namespace osu.Game.Screens.Play.HitErrorDisplay judgementsContainer.Add(new JudgementLine { Y = getRelativeJudgementPosition(judgement.TimeOffset), - Anchor = rightAligned ? Anchor.TopLeft : Anchor.TopRight, - Origin = rightAligned ? Anchor.TopLeft : Anchor.TopRight, + Anchor = rightAligned ? Anchor.TopRight : Anchor.TopLeft, + Origin = rightAligned ? Anchor.TopRight : Anchor.TopLeft, }); arrow.MoveToY(getRelativeJudgementPosition(floatingAverage = floatingAverage * 0.9 + judgement.TimeOffset * 0.1) From 40729356fa1d19849f15120851653c323c372388 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 30 Aug 2019 17:34:02 +0900 Subject: [PATCH 2460/2854] Move beat divisor colour retrieval to BindableBeatDivisor --- osu.Game/Screens/Edit/BindableBeatDivisor.cs | 39 +++++++++++++++ .../Compose/Components/BeatDivisorControl.cs | 47 +++---------------- 2 files changed, 45 insertions(+), 41 deletions(-) diff --git a/osu.Game/Screens/Edit/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/BindableBeatDivisor.cs index ea3b68e3bd..055077cc4f 100644 --- a/osu.Game/Screens/Edit/BindableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/BindableBeatDivisor.cs @@ -4,6 +4,9 @@ using System; using System.Linq; using osu.Framework.Bindables; +using osu.Framework.Graphics.Colour; +using osu.Game.Graphics; +using osuTK.Graphics; namespace osu.Game.Screens.Edit { @@ -35,5 +38,41 @@ namespace osu.Game.Screens.Edit protected override int DefaultMinValue => VALID_DIVISORS.First(); protected override int DefaultMaxValue => VALID_DIVISORS.Last(); protected override int DefaultPrecision => 1; + + /// + /// Retrieves the appropriate colour for a beat divisor. + /// + /// The beat divisor. + /// The set of colours. + /// The applicable colour from for . + public ColourInfo GetColourFor(int beatDivisor, OsuColour colours) + { + switch (beatDivisor) + { + case 2: + return colours.BlueLight; + + case 4: + return colours.Blue; + + case 8: + return colours.BlueDarker; + + case 16: + return colours.PurpleDark; + + case 3: + return colours.YellowLight; + + case 6: + return colours.Yellow; + + case 12: + return colours.YellowDarker; + + default: + return Color4.White; + } + } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 0d16d8474b..ddcdfdaf80 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -188,6 +188,9 @@ namespace osu.Game.Screens.Edit.Compose.Components { private Marker marker; + [Resolved] + private OsuColour colours { get; set; } + private readonly BindableBeatDivisor beatDivisor; private readonly int[] availableDivisors; @@ -204,11 +207,12 @@ namespace osu.Game.Screens.Edit.Compose.Components { foreach (var t in availableDivisors) { - AddInternal(new Tick(t) + AddInternal(new Tick { Anchor = Anchor.TopLeft, Origin = Anchor.TopCentre, RelativePositionAxes = Axes.X, + Colour = beatDivisor.GetColourFor(t, colours), X = getMappedPosition(t) }); } @@ -284,11 +288,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private class Tick : CompositeDrawable { - private readonly int divisor; - - public Tick(int divisor) + public Tick() { - this.divisor = divisor; Size = new Vector2(2.5f, 10); InternalChild = new Box { RelativeSizeAxes = Axes.Both }; @@ -296,42 +297,6 @@ namespace osu.Game.Screens.Edit.Compose.Components CornerRadius = 0.5f; Masking = true; } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Colour = getColourForDivisor(divisor, colours); - } - - private ColourInfo getColourForDivisor(int divisor, OsuColour colours) - { - switch (divisor) - { - case 2: - return colours.BlueLight; - - case 4: - return colours.Blue; - - case 8: - return colours.BlueDarker; - - case 16: - return colours.PurpleDark; - - case 3: - return colours.YellowLight; - - case 6: - return colours.Yellow; - - case 12: - return colours.YellowDarker; - - default: - return Color4.White; - } - } } private class Marker : CompositeDrawable From 741bd0a5cffd4a972291054188cc139d62a7358d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 18:35:06 +0900 Subject: [PATCH 2461/2854] Fix incorrect colour sizes and simplify alignment specification --- .../Gameplay/TestSceneBarHitErrorMeter.cs | 4 +- osu.Game/Rulesets/Objects/HitWindows.cs | 13 ++ .../Play/HitErrorDisplay/BarHitErrorMeter.cs | 172 +++++++++++++----- 3 files changed, 140 insertions(+), 49 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs index 334e0d3b90..8852a27f50 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs @@ -37,8 +37,8 @@ namespace osu.Game.Tests.Visual.Gameplay AddRepeatStep("New random judgement", () => newJudgement(), 40); - AddRepeatStep("New max negative", () => newJudgement(-hitWindows.Meh), 20); - AddRepeatStep("New max positive", () => newJudgement(hitWindows.Meh), 20); + AddRepeatStep("New max negative", () => newJudgement(-hitWindows.HalfWindowFor(HitResult.Meh)), 20); + AddRepeatStep("New max positive", () => newJudgement(hitWindows.HalfWindowFor(HitResult.Meh)), 20); AddStep("New fixed judgement (50ms)", () => newJudgement(50)); } diff --git a/osu.Game/Rulesets/Objects/HitWindows.cs b/osu.Game/Rulesets/Objects/HitWindows.cs index fe099aaee7..e88af67c7c 100644 --- a/osu.Game/Rulesets/Objects/HitWindows.cs +++ b/osu.Game/Rulesets/Objects/HitWindows.cs @@ -65,6 +65,19 @@ namespace osu.Game.Rulesets.Objects return HitResult.None; } + /// + /// Retrieves a mapping of s to their half window timing for all allowed s. + /// + /// + public IEnumerable<(HitResult result, double length)> GetAllAvailableHalfWindows() + { + for (var result = HitResult.Meh; result <= HitResult.Perfect; ++result) + { + if (IsHitResultAllowed(result)) + yield return (result, HalfWindowFor(result)); + } + } + /// /// Check whether it is possible to achieve the provided . /// diff --git a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs index 7fb7b3cf99..5a2d892d7f 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; +using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -11,15 +11,16 @@ using osuTK; using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Objects; using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics.Colour; using osu.Game.Graphics; -using osu.Framework.Extensions.Color4Extensions; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play.HitErrorDisplay { public class BarHitErrorMeter : HitErrorMeter { - private readonly bool rightAligned; + private readonly Anchor alignment; private const int judgement_fade_duration = 10000; @@ -35,17 +36,17 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private SpriteIcon arrow; - private FillFlowContainer colourBarFlow; + private Container colourBarsEarly; + private Container colourBarsLate; private Container judgementsContainer; - private readonly double maxHitWindow; + private double maxHitWindow; public BarHitErrorMeter(HitWindows hitWindows, bool rightAligned = false) : base(hitWindows) { - this.rightAligned = rightAligned; - maxHitWindow = Math.Max(Math.Max(HitWindows.Meh, HitWindows.Ok), HitWindows.Good); + alignment = rightAligned ? Anchor.x0 : Anchor.x2; AutoSizeAxes = Axes.Both; } @@ -64,23 +65,40 @@ namespace osu.Game.Screens.Play.HitErrorDisplay { judgementsContainer = new Container { - Anchor = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, - Origin = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, + Anchor = Anchor.y1 | alignment, + Origin = Anchor.y1 | alignment, Width = judgement_line_width, RelativeSizeAxes = Axes.Y, }, - colourBarFlow = new FillFlowContainer + colourBars = new Container { - Anchor = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, - Origin = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, Width = bar_width, RelativeSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, + Anchor = Anchor.y1 | alignment, + Origin = Anchor.y1 | alignment, + Children = new Drawable[] + { + colourBarsEarly = new Container + { + Anchor = Anchor.y1 | alignment, + Origin = alignment, + RelativeSizeAxes = Axes.Both, + Height = 0.5f, + Scale = new Vector2(1, -1), + }, + colourBarsLate = new Container + { + Anchor = Anchor.y1 | alignment, + Origin = alignment, + RelativeSizeAxes = Axes.Both, + Height = 0.5f, + }, + } }, new Container { - Anchor = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, - Origin = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, + Anchor = Anchor.y1 | alignment, + Origin = Anchor.y1 | alignment, AutoSizeAxes = Axes.X, RelativeSizeAxes = Axes.Y, Child = arrow = new SpriteIcon @@ -89,53 +107,111 @@ namespace osu.Game.Screens.Play.HitErrorDisplay Origin = Anchor.Centre, RelativePositionAxes = Axes.Y, Y = 0.5f, - Icon = rightAligned ? FontAwesome.Solid.ChevronLeft : FontAwesome.Solid.ChevronRight, + Icon = alignment == Anchor.x2 ? FontAwesome.Solid.ChevronRight : FontAwesome.Solid.ChevronLeft, Size = new Vector2(8), } }, } }; - if (HitWindows.Meh != 0) - { - colourBarFlow.AddRange(new[] - { - createColoredPiece(ColourInfo.GradientVertical(colours.Yellow.Opacity(0), colours.Yellow), (maxHitWindow - HitWindows.Good) / (maxHitWindow * 2)), - createColoredPiece(colours.Green, (HitWindows.Good - HitWindows.Great) / (maxHitWindow * 2)), - createColoredPiece(colours.BlueLight, HitWindows.Great / maxHitWindow), - createColoredPiece(colours.Green, (HitWindows.Good - HitWindows.Great) / (maxHitWindow * 2)), - createColoredPiece(ColourInfo.GradientVertical(colours.Yellow, colours.Yellow.Opacity(0)), (maxHitWindow - HitWindows.Good) / (maxHitWindow * 2)) - }); - } - else - { - colourBarFlow.AddRange(new[] - { - createColoredPiece(ColourInfo.GradientVertical(colours.Green.Opacity(0), colours.Green), (HitWindows.Good - HitWindows.Great) / (maxHitWindow * 2)), - createColoredPiece(colours.BlueLight, HitWindows.Great / maxHitWindow), - createColoredPiece(ColourInfo.GradientVertical(colours.Green, colours.Green.Opacity(0)), (HitWindows.Good - HitWindows.Great) / (maxHitWindow * 2)), - }); - } + createColourBars(colours); } protected override void LoadComplete() { base.LoadComplete(); - colourBarFlow.Height = 0; - colourBarFlow.ResizeHeightTo(1, 400, Easing.OutQuint); + colourBars.Height = 0; + colourBars.ResizeHeightTo(1, 800, Easing.OutQuint); - arrow.FadeInFromZero(400); + arrow.Alpha = 0.01f; + arrow.Delay(200).FadeInFromZero(600); } - private Box createColoredPiece(ColourInfo colour, double height) => new Box + private void createColourBars(OsuColour colours) { - RelativeSizeAxes = Axes.Both, - Colour = colour, - Height = (float)height - }; + var windows = HitWindows.GetAllAvailableHalfWindows().ToArray(); + + maxHitWindow = windows.First().length; + + for (var i = 0; i < windows.Length; i++) + { + var (result, length) = windows[i]; + + colourBarsEarly.Add(createColourBar(result, (float)(length / maxHitWindow), i == 0)); + colourBarsLate.Add(createColourBar(result, (float)(length / maxHitWindow), i == 0)); + } + + // a little nub to mark the centre point. + var centre = createColourBar(windows.Last().result, 0.01f); + centre.Anchor = centre.Origin = Anchor.y1 | alignment; + centre.Width = 1.5f; + colourBars.Add(centre); + + Color4 getColour(HitResult result) + { + switch (result) + { + case HitResult.Meh: + return colours.Yellow; + + case HitResult.Ok: + return colours.Green; + + case HitResult.Good: + return colours.GreenLight; + + case HitResult.Great: + return colours.Blue; + + default: + return colours.BlueLight; + } + } + + Drawable createColourBar(HitResult result, float height, bool first = false) + { + var colour = getColour(result); + + if (first) + { + // the first bar needs gradient rendering. + const float gradient_start = 0.8f; + + return new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = getColour(result), + Height = height * gradient_start + }, + new Box + { + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(colour, colour.Opacity(0)), + Y = gradient_start, + Height = height * (1 - gradient_start) + }, + } + }; + } + + return new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colour, + Height = height + }; + } + } private double floatingAverage; + private Container colourBars; public override void OnNewJudgement(JudgementResult judgement) { @@ -145,11 +221,12 @@ namespace osu.Game.Screens.Play.HitErrorDisplay judgementsContainer.Add(new JudgementLine { Y = getRelativeJudgementPosition(judgement.TimeOffset), - Anchor = rightAligned ? Anchor.TopRight : Anchor.TopLeft, - Origin = rightAligned ? Anchor.TopRight : Anchor.TopLeft, + Anchor = alignment == Anchor.x2 ? Anchor.x0 : Anchor.x2, + Origin = alignment == Anchor.x2 ? Anchor.x0 : Anchor.x2, }); - arrow.MoveToY(getRelativeJudgementPosition(floatingAverage = floatingAverage * 0.9 + judgement.TimeOffset * 0.1) + arrow.MoveToY( + getRelativeJudgementPosition(floatingAverage = floatingAverage * 0.9 + judgement.TimeOffset * 0.1) , arrow_move_duration, Easing.Out); } @@ -180,6 +257,7 @@ namespace osu.Game.Screens.Play.HitErrorDisplay base.LoadComplete(); Width = 0; + this.ResizeWidthTo(1, 150, Easing.OutElasticHalf); this.FadeTo(0.8f, 150).Then().FadeOut(judgement_fade_duration, Easing.OutQuint).Expire(); } From 8fc177b743d8a1b3c5300379b597c5487650cb31 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 18:46:42 +0900 Subject: [PATCH 2462/2854] Fix namespacing and hitwindow source --- .../Gameplay/TestSceneBarHitErrorMeter.cs | 2 +- .../HitErrorDisplay.cs | 22 ++++++++++--------- .../HitErrorMeters}/BarHitErrorMeter.cs | 18 +++++++-------- .../HitErrorMeters}/HitErrorMeter.cs | 2 +- osu.Game/Screens/Play/HUDOverlay.cs | 8 +++---- 5 files changed, 26 insertions(+), 26 deletions(-) rename osu.Game/Screens/Play/{HitErrorDisplay => HUD}/HitErrorDisplay.cs (87%) rename osu.Game/Screens/Play/{HitErrorDisplay => HUD/HitErrorMeters}/BarHitErrorMeter.cs (99%) rename osu.Game/Screens/Play/{HitErrorDisplay => HUD/HitErrorMeters}/HitErrorMeter.cs (91%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs index 8852a27f50..98826331e1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs @@ -7,7 +7,6 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Screens.Play.HitErrorDisplay; using System; using System.Collections.Generic; using osu.Game.Rulesets.Judgements; @@ -16,6 +15,7 @@ using osu.Framework.MathUtils; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Game.Screens.Play.HUD.HitErrorMeters; namespace osu.Game.Tests.Visual.Gameplay { diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs similarity index 87% rename from osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs rename to osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index eaaf8e810c..0dcb1fee2b 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -1,19 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Scoring; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Game.Beatmaps; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Configuration; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Play.HUD.HitErrorMeters; -namespace osu.Game.Screens.Play.HitErrorDisplay +namespace osu.Game.Screens.Play.HUD { public class HitErrorDisplay : Container { @@ -22,13 +21,17 @@ namespace osu.Game.Screens.Play.HitErrorDisplay private readonly Bindable type = new Bindable(); - private HitWindows hitWindows; + private readonly HitWindows hitWindows; private readonly ScoreProcessor processor; - public HitErrorDisplay(ScoreProcessor processor) + public HitErrorDisplay(ScoreProcessor processor, HitWindows hitWindows) { this.processor = processor; + this.hitWindows = hitWindows; + + RelativeSizeAxes = Axes.Both; + processor.NewJudgement += onNewJudgement; } @@ -45,10 +48,9 @@ namespace osu.Game.Screens.Play.HitErrorDisplay } [BackgroundDependencyLoader] - private void load(OsuConfigManager config, Bindable workingBeatmap) + private void load(OsuConfigManager config) { config.BindWith(OsuSetting.ScoreMeter, type); - hitWindows = workingBeatmap.Value.Beatmap.HitObjects.First().HitWindows; } protected override void LoadComplete() diff --git a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs similarity index 99% rename from osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs rename to osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 5a2d892d7f..22cccf30d7 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -2,21 +2,21 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Judgements; -using osuTK.Graphics; -using osuTK; -using osu.Framework.Graphics.Sprites; -using osu.Game.Rulesets.Objects; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; +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.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; +using osuTK; +using osuTK.Graphics; -namespace osu.Game.Screens.Play.HitErrorDisplay +namespace osu.Game.Screens.Play.HUD.HitErrorMeters { public class BarHitErrorMeter : HitErrorMeter { diff --git a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs similarity index 91% rename from osu.Game/Screens/Play/HitErrorDisplay/HitErrorMeter.cs rename to osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index e4599eb2fc..da1d9fff0d 100644 --- a/osu.Game/Screens/Play/HitErrorDisplay/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -5,7 +5,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; -namespace osu.Game.Screens.Play.HitErrorDisplay +namespace osu.Game.Screens.Play.HUD.HitErrorMeters { public abstract class HitErrorMeter : CompositeDrawable { diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 79392221e4..21d5ae557f 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -33,7 +34,7 @@ namespace osu.Game.Screens.Play public readonly HealthDisplay HealthDisplay; public readonly SongProgress Progress; public readonly ModDisplay ModDisplay; - public readonly HitErrorDisplay.HitErrorDisplay HitErrorDisplay; + public readonly HitErrorDisplay HitErrorDisplay; public readonly HoldForMenuButton HoldToQuit; public readonly PlayerSettingsOverlay PlayerSettingsOverlay; @@ -258,10 +259,7 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20, Right = 10 }, }; - protected virtual HitErrorDisplay.HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay.HitErrorDisplay(scoreProcessor) - { - RelativeSizeAxes = Axes.Both, - }; + protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset.Objects.First().HitWindows); protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); From 171f88da409964efc0b56908fc30a0824ee20da9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2019 09:47:18 +0000 Subject: [PATCH 2463/2854] Bump ppy.osu.Framework from 2019.830.0 to 2019.830.1 Bumps [ppy.osu.Framework](https://github.com/ppy/osu-framework) from 2019.830.0 to 2019.830.1. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.830.0...2019.830.1) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8e6ce2d1ba..330018d5cb 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 47cc6ec97a..298fbc6704 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -118,7 +118,7 @@ - + From c3abf0ccb7112f654910ef867a658986e517fa54 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 18:50:38 +0900 Subject: [PATCH 2464/2854] Improve visuals --- .../Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 22cccf30d7..4e32be3cda 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -22,8 +22,6 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters { private readonly Anchor alignment; - private const int judgement_fade_duration = 10000; - private const int arrow_move_duration = 400; private const int judgement_line_width = 6; @@ -34,6 +32,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters private const int spacing = 2; + private const float chevron_size = 8; + private SpriteIcon arrow; private Container colourBarsEarly; @@ -99,7 +99,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters { Anchor = Anchor.y1 | alignment, Origin = Anchor.y1 | alignment, - AutoSizeAxes = Axes.X, + Width = chevron_size, RelativeSizeAxes = Axes.Y, Child = arrow = new SpriteIcon { @@ -108,7 +108,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters RelativePositionAxes = Axes.Y, Y = 0.5f, Icon = alignment == Anchor.x2 ? FontAwesome.Solid.ChevronRight : FontAwesome.Solid.ChevronLeft, - Size = new Vector2(8), + Size = new Vector2(chevron_size), } }, } @@ -124,7 +124,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters colourBars.Height = 0; colourBars.ResizeHeightTo(1, 800, Easing.OutQuint); - arrow.Alpha = 0.01f; + arrow.Alpha = 0; arrow.Delay(200).FadeInFromZero(600); } @@ -234,11 +234,13 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters public class JudgementLine : CompositeDrawable { + private const int judgement_fade_duration = 10000; + public JudgementLine() { RelativeSizeAxes = Axes.X; RelativePositionAxes = Axes.Y; - Height = 2; + Height = 3; InternalChild = new CircularContainer { @@ -258,7 +260,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters Width = 0; - this.ResizeWidthTo(1, 150, Easing.OutElasticHalf); + this.ResizeWidthTo(1, 200, Easing.OutElasticHalf); this.FadeTo(0.8f, 150).Then().FadeOut(judgement_fade_duration, Easing.OutQuint).Expire(); } } From b639ce8e5d237c845adec63f26f5993df4140be8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2019 10:01:16 +0000 Subject: [PATCH 2465/2854] Bump ppy.osu.Framework.iOS from 2019.830.0 to 2019.830.1 Bumps [ppy.osu.Framework.iOS](https://github.com/ppy/osu-framework) from 2019.830.0 to 2019.830.1. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.830.0...2019.830.1) Signed-off-by: dependabot-preview[bot] --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index 47cc6ec97a..afb0b9217e 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -119,7 +119,7 @@ - + From 815220de039ef25606591f5a0e0f7f1122952870 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2019 10:01:35 +0000 Subject: [PATCH 2466/2854] Bump ppy.osu.Framework.Android from 2019.830.0 to 2019.830.1 Bumps [ppy.osu.Framework.Android](https://github.com/ppy/osu-framework) from 2019.830.0 to 2019.830.1. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2019.830.0...2019.830.1) Signed-off-by: dependabot-preview[bot] --- osu.Android.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android.props b/osu.Android.props index 2c3c8bcaad..93a9a073a4 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -61,6 +61,6 @@ - + From 80671cefd7db5e83b1059fdccaa3bc5a475bd603 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 19:14:07 +0900 Subject: [PATCH 2467/2854] Final visual polish --- .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 4e32be3cda..51f9be4792 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -93,6 +93,22 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters RelativeSizeAxes = Axes.Both, Height = 0.5f, }, + new SpriteIcon + { + Y = -10, + Size = new Vector2(10), + Icon = FontAwesome.Solid.ShippingFast, + Anchor = Anchor.y0 | alignment, + Origin = Anchor.y0 | alignment, + }, + new SpriteIcon + { + Y = 10, + Size = new Vector2(10), + Icon = FontAwesome.Solid.Bicycle, + Anchor = Anchor.y2 | alignment, + Origin = Anchor.y2 | alignment, + } } }, new Container @@ -144,8 +160,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters // a little nub to mark the centre point. var centre = createColourBar(windows.Last().result, 0.01f); - centre.Anchor = centre.Origin = Anchor.y1 | alignment; - centre.Width = 1.5f; + centre.Anchor = centre.Origin = Anchor.y1 | (alignment == Anchor.x2 ? Anchor.x0 : Anchor.x2); + centre.Width = 2.5f; colourBars.Add(centre); Color4 getColour(HitResult result) From 665fc95d49c595b751d44ca5cf5bae91ae5bda58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 19:37:20 +0900 Subject: [PATCH 2468/2854] Handle no hitobjects / no hitwindows (osu!catch) --- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 3 +++ osu.Game/Screens/Play/HUDOverlay.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index 0dcb1fee2b..2e28d17b80 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -63,6 +63,9 @@ namespace osu.Game.Screens.Play.HUD { Children.ForEach(c => c.FadeOut(fade_duration, Easing.OutQuint)); + if (hitWindows == null) + return; + switch (type.NewValue) { case ScoreMeterType.HitErrorBoth: diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 21d5ae557f..285737f7a8 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -259,7 +259,7 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20, Right = 10 }, }; - protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset.Objects.First().HitWindows); + protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset.Objects.FirstOrDefault().HitWindows); protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); From fab12fa9cd00043a9b820d7511ca2fcddb729d8d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 30 Aug 2019 19:34:28 +0900 Subject: [PATCH 2469/2854] Centre align the icons Seems to look better this way. --- .../Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 51f9be4792..7d3b0ae141 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -98,16 +98,16 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters Y = -10, Size = new Vector2(10), Icon = FontAwesome.Solid.ShippingFast, - Anchor = Anchor.y0 | alignment, - Origin = Anchor.y0 | alignment, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, }, new SpriteIcon { Y = 10, Size = new Vector2(10), Icon = FontAwesome.Solid.Bicycle, - Anchor = Anchor.y2 | alignment, - Origin = Anchor.y2 | alignment, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, } } }, From 8b4976ad92399c19acffa4275710eaffe3d9ff02 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 30 Aug 2019 19:37:18 +0900 Subject: [PATCH 2470/2854] Remove unnecessary intermediate OD tests --- .../Gameplay/TestSceneBarHitErrorMeter.cs | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs index 98826331e1..d317c6551f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs @@ -46,14 +46,6 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestOsu() { AddStep("OD 1", () => recreateDisplay(new OsuHitWindows(), 1)); - AddStep("OD 2", () => recreateDisplay(new OsuHitWindows(), 2)); - AddStep("OD 3", () => recreateDisplay(new OsuHitWindows(), 3)); - AddStep("OD 4", () => recreateDisplay(new OsuHitWindows(), 4)); - AddStep("OD 5", () => recreateDisplay(new OsuHitWindows(), 5)); - AddStep("OD 6", () => recreateDisplay(new OsuHitWindows(), 6)); - AddStep("OD 7", () => recreateDisplay(new OsuHitWindows(), 7)); - AddStep("OD 8", () => recreateDisplay(new OsuHitWindows(), 8)); - AddStep("OD 9", () => recreateDisplay(new OsuHitWindows(), 9)); AddStep("OD 10", () => recreateDisplay(new OsuHitWindows(), 10)); } @@ -61,14 +53,6 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestTaiko() { AddStep("OD 1", () => recreateDisplay(new TaikoHitWindows(), 1)); - AddStep("OD 2", () => recreateDisplay(new TaikoHitWindows(), 2)); - AddStep("OD 3", () => recreateDisplay(new TaikoHitWindows(), 3)); - AddStep("OD 4", () => recreateDisplay(new TaikoHitWindows(), 4)); - AddStep("OD 5", () => recreateDisplay(new TaikoHitWindows(), 5)); - AddStep("OD 6", () => recreateDisplay(new TaikoHitWindows(), 6)); - AddStep("OD 7", () => recreateDisplay(new TaikoHitWindows(), 7)); - AddStep("OD 8", () => recreateDisplay(new TaikoHitWindows(), 8)); - AddStep("OD 9", () => recreateDisplay(new TaikoHitWindows(), 9)); AddStep("OD 10", () => recreateDisplay(new TaikoHitWindows(), 10)); } @@ -76,14 +60,6 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestMania() { AddStep("OD 1", () => recreateDisplay(new ManiaHitWindows(), 1)); - AddStep("OD 2", () => recreateDisplay(new ManiaHitWindows(), 2)); - AddStep("OD 3", () => recreateDisplay(new ManiaHitWindows(), 3)); - AddStep("OD 4", () => recreateDisplay(new ManiaHitWindows(), 4)); - AddStep("OD 5", () => recreateDisplay(new ManiaHitWindows(), 5)); - AddStep("OD 6", () => recreateDisplay(new ManiaHitWindows(), 6)); - AddStep("OD 7", () => recreateDisplay(new ManiaHitWindows(), 7)); - AddStep("OD 8", () => recreateDisplay(new ManiaHitWindows(), 8)); - AddStep("OD 9", () => recreateDisplay(new ManiaHitWindows(), 9)); AddStep("OD 10", () => recreateDisplay(new ManiaHitWindows(), 10)); } @@ -91,14 +67,6 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestCatch() { AddStep("OD 1", () => recreateDisplay(new CatchHitWindows(), 1)); - AddStep("OD 2", () => recreateDisplay(new CatchHitWindows(), 2)); - AddStep("OD 3", () => recreateDisplay(new CatchHitWindows(), 3)); - AddStep("OD 4", () => recreateDisplay(new CatchHitWindows(), 4)); - AddStep("OD 5", () => recreateDisplay(new CatchHitWindows(), 5)); - AddStep("OD 6", () => recreateDisplay(new CatchHitWindows(), 6)); - AddStep("OD 7", () => recreateDisplay(new CatchHitWindows(), 7)); - AddStep("OD 8", () => recreateDisplay(new CatchHitWindows(), 8)); - AddStep("OD 9", () => recreateDisplay(new CatchHitWindows(), 9)); AddStep("OD 10", () => recreateDisplay(new CatchHitWindows(), 10)); } From 6fb8a6cdbe894343c98a54e115c97a2451264717 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 30 Aug 2019 19:47:13 +0900 Subject: [PATCH 2471/2854] Fix testcases not working for OD10 --- .../Visual/Gameplay/TestSceneBarHitErrorMeter.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs index d317c6551f..f20440249b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs @@ -28,12 +28,11 @@ namespace osu.Game.Tests.Visual.Gameplay private HitErrorMeter meter; private HitErrorMeter meter2; + private HitWindows hitWindows; public TestSceneBarHitErrorMeter() { - var hitWindows = new OsuHitWindows(); - - recreateDisplay(hitWindows, 5); + recreateDisplay(new OsuHitWindows(), 5); AddRepeatStep("New random judgement", () => newJudgement(), 40); @@ -72,7 +71,9 @@ namespace osu.Game.Tests.Visual.Gameplay private void recreateDisplay(HitWindows hitWindows, float overallDifficulty) { - hitWindows.SetDifficulty(overallDifficulty); + this.hitWindows = hitWindows; + + hitWindows?.SetDifficulty(overallDifficulty); Clear(); @@ -84,9 +85,9 @@ namespace osu.Game.Tests.Visual.Gameplay AutoSizeAxes = Axes.Both, Children = new[] { - new SpriteText { Text = $@"Great: {hitWindows.Great}" }, - new SpriteText { Text = $@"Good: {hitWindows.Good}" }, - new SpriteText { Text = $@"Meh: {hitWindows.Meh}" }, + new SpriteText { Text = $@"Great: {hitWindows?.Great}" }, + new SpriteText { Text = $@"Good: {hitWindows?.Good}" }, + new SpriteText { Text = $@"Meh: {hitWindows?.Meh}" }, } }); From dfccc6036109226ca4be14cd92ca38f6fd3e6d5c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 30 Aug 2019 19:47:21 +0900 Subject: [PATCH 2472/2854] Reorder HitErrorDisplay --- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 24 ++++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index 2e28d17b80..cdfa0e993b 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -35,18 +35,6 @@ namespace osu.Game.Screens.Play.HUD processor.NewJudgement += onNewJudgement; } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - processor.NewJudgement -= onNewJudgement; - } - - private void onNewJudgement(JudgementResult result) - { - foreach (var c in Children) - c.OnNewJudgement(result); - } - [BackgroundDependencyLoader] private void load(OsuConfigManager config) { @@ -59,6 +47,12 @@ namespace osu.Game.Screens.Play.HUD type.BindValueChanged(typeChanged, true); } + private void onNewJudgement(JudgementResult result) + { + foreach (var c in Children) + c.OnNewJudgement(result); + } + private void typeChanged(ValueChangedEvent type) { Children.ForEach(c => c.FadeOut(fade_duration, Easing.OutQuint)); @@ -96,5 +90,11 @@ namespace osu.Game.Screens.Play.HUD Add(display); display.FadeInFromZero(fade_duration, Easing.OutQuint); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + processor.NewJudgement -= onNewJudgement; + } } } From fc813347ac522fad92e2b88b022ab2561957b6ee Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 30 Aug 2019 19:54:36 +0900 Subject: [PATCH 2473/2854] Make JudgementLine private --- osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 7d3b0ae141..d5d3cb528e 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -248,7 +248,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters private float getRelativeJudgementPosition(double value) => (float)((value / maxHitWindow) + 1) / 2; - public class JudgementLine : CompositeDrawable + private class JudgementLine : CompositeDrawable { private const int judgement_fade_duration = 10000; From f1db6c7039c1bbc037c36c0449a599f9a6694497 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Aug 2019 20:18:21 +0900 Subject: [PATCH 2474/2854] Fix likely nullref --- osu.Game/Screens/Play/HUDOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 285737f7a8..8e642ea552 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -259,7 +259,7 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20, Right = 10 }, }; - protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset.Objects.FirstOrDefault().HitWindows); + protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset.Objects.FirstOrDefault()?.HitWindows); protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); From 2876588193897bc9128bb62041c0e6adbc13941b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2019 13:36:31 +0000 Subject: [PATCH 2475/2854] Bump NUnit3TestAdapter from 3.15.0 to 3.15.1 Bumps [NUnit3TestAdapter](https://github.com/nunit/nunit3-vs-adapter) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/nunit/nunit3-vs-adapter/releases) - [Commits](https://github.com/nunit/nunit3-vs-adapter/compare/V3.15...V3.15.1) Signed-off-by: dependabot-preview[bot] --- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 7c282f449b..c527a81f51 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index 4dcfc1b81f..af10d5e06e 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index 197309c7c4..c331c811d2 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index a5db1625d9..d2a0a8fa6f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 4a9d88f3a6..84f67c9319 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index 2a8bd393da..bba3c92245 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -7,7 +7,7 @@ - + WinExe From 6e5cb8a318840fc1b1e156f8583641d4881cfd84 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 30 Aug 2019 23:19:34 +0300 Subject: [PATCH 2476/2854] implement video parsing --- osu.Game/Beatmaps/BeatmapManager.cs | 2 ++ .../Beatmaps/BeatmapManager_WorkingBeatmap.cs | 16 ++++++++++++++++ osu.Game/Beatmaps/BeatmapMetadata.cs | 4 +++- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 3 +++ .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 9 +++++++-- osu.Game/Beatmaps/IWorkingBeatmap.cs | 6 ++++++ osu.Game/Beatmaps/WorkingBeatmap.cs | 9 +++++++++ .../20171019041408_InitialCreate.Designer.cs | 2 ++ .../Migrations/20171019041408_InitialCreate.cs | 1 + ...171025071459_AddMissingIndexRules.Designer.cs | 2 ++ ...ddBeatmapOnlineIDUniqueConstraint.Designer.cs | 2 ++ ...209034410_AddRulesetInfoShortName.Designer.cs | 2 ++ .../20180125143340_Settings.Designer.cs | 2 ++ .../20180219060912_AddSkins.Designer.cs | 2 ++ ...55154_RemoveUniqueHashConstraints.Designer.cs | 2 ++ ...044111_UpdateTaikoDefaultBindings.Designer.cs | 2 ++ ...180628011956_RemoveNegativeSetIDs.Designer.cs | 2 ++ .../20180913080842_AddRankStatus.Designer.cs | 2 ++ .../20181007180454_StandardizePaths.Designer.cs | 2 ++ .../20181007180454_StandardizePaths.cs | 1 + .../20181128100659_AddSkinInfoHash.Designer.cs | 2 ++ ...20181130113755_AddScoreInfoTables.Designer.cs | 2 ++ .../20190225062029_AddUserIDColumn.Designer.cs | 2 ++ .../20190525060824_SkinSettings.Designer.cs | 2 ++ ...46_AddDateAddedColumnToBeatmapSet.Designer.cs | 2 ++ ...0708070844_AddBPMAndLengthColumns.Designer.cs | 2 ++ osu.Game/Migrations/OsuDbContextModelSnapshot.cs | 2 ++ osu.Game/Screens/Edit/EditorWorkingBeatmap.cs | 3 +++ osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 3 +++ osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 3 +++ 30 files changed, 93 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 166ba5111c..b9ed3664ef 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -13,6 +13,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Extensions; using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Video; using osu.Framework.Lists; using osu.Framework.Logging; using osu.Framework.Platform; @@ -340,6 +341,7 @@ namespace osu.Game.Beatmaps protected override IBeatmap GetBeatmap() => beatmap; protected override Texture GetBackground() => null; + protected override VideoSprite GetVideo() => null; protected override Track GetTrack() => null; } diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 5bbffc2f77..1d00c94ef2 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Video; using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Game.Beatmaps.Formats; @@ -64,6 +65,21 @@ namespace osu.Game.Beatmaps } } + protected override VideoSprite GetVideo() + { + if (Metadata?.VideoFile == null) + return null; + + try + { + return new VideoSprite(textureStore.GetStream(getPathForFile(Metadata.VideoFile))); + } + catch + { + return null; + } + } + protected override Track GetTrack() { try diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 001f319307..9267527d79 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -52,6 +52,7 @@ namespace osu.Game.Beatmaps public int PreviewTime { get; set; } public string AudioFile { get; set; } public string BackgroundFile { get; set; } + public string VideoFile { get; set; } public override string ToString() => $"{Artist} - {Title} ({Author})"; @@ -81,7 +82,8 @@ namespace osu.Game.Beatmaps && Tags == other.Tags && PreviewTime == other.PreviewTime && AudioFile == other.AudioFile - && BackgroundFile == other.BackgroundFile; + && BackgroundFile == other.BackgroundFile + && VideoFile == other.VideoFile; } } } diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 29ade24328..a3ab01c886 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -7,6 +7,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Video; using osu.Game.Rulesets; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; @@ -44,6 +45,8 @@ namespace osu.Game.Beatmaps protected override Texture GetBackground() => textures?.Get(@"Backgrounds/bg4"); + protected override VideoSprite GetVideo() => null; + protected override Track GetTrack() => GetVirtualTrack(); private class DummyRulesetInfo : RulesetInfo diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 02d969b571..0532790f0a 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -296,8 +296,13 @@ namespace osu.Game.Beatmaps.Formats switch (type) { case EventType.Background: - string filename = split[2].Trim('"'); - beatmap.BeatmapInfo.Metadata.BackgroundFile = FileSafety.PathStandardise(filename); + string bgFilename = split[2].Trim('"'); + beatmap.BeatmapInfo.Metadata.BackgroundFile = FileSafety.PathStandardise(bgFilename); + break; + + case EventType.Video: + string videoFilename = split[2].Trim('"'); + beatmap.BeatmapInfo.Metadata.VideoFile = FileSafety.PathStandardise(videoFilename); break; case EventType.Break: diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index 44071d9cc1..b932e67bae 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Video; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -25,6 +26,11 @@ namespace osu.Game.Beatmaps /// Texture Background { get; } + /// + /// Retrieves the video file for this . + /// + VideoSprite Video { get; } + /// /// Retrieves the audio track for this . /// diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index d8ab411beb..b489936556 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -19,6 +19,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.UI; using osu.Game.Skinning; +using osu.Framework.Graphics.Video; namespace osu.Game.Beatmaps { @@ -43,6 +44,7 @@ namespace osu.Game.Beatmaps track = new RecyclableLazy(() => GetTrack() ?? GetVirtualTrack()); background = new RecyclableLazy(GetBackground, BackgroundStillValid); + video = new RecyclableLazy(GetVideo); waveform = new RecyclableLazy(GetWaveform); storyboard = new RecyclableLazy(GetStoryboard); skin = new RecyclableLazy(GetSkin); @@ -183,9 +185,16 @@ namespace osu.Game.Beatmaps public bool BackgroundLoaded => background.IsResultAvailable; public Texture Background => background.Value; protected virtual bool BackgroundStillValid(Texture b) => b == null || b.Available; + protected abstract Texture GetBackground(); private readonly RecyclableLazy background; + public bool VideoLoaded => video.IsResultAvailable; + public VideoSprite Video => video.Value; + + protected abstract VideoSprite GetVideo(); + private readonly RecyclableLazy video; + public bool TrackLoaded => track.IsResultAvailable; public Track Track => track.Value; protected abstract Track GetTrack(); diff --git a/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs b/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs index c751530bf4..596d80557b 100644 --- a/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs +++ b/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs @@ -123,6 +123,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20171019041408_InitialCreate.cs b/osu.Game/Migrations/20171019041408_InitialCreate.cs index 9b6881f98c..3349998873 100644 --- a/osu.Game/Migrations/20171019041408_InitialCreate.cs +++ b/osu.Game/Migrations/20171019041408_InitialCreate.cs @@ -35,6 +35,7 @@ namespace osu.Game.Migrations AudioFile = table.Column(type: "TEXT", nullable: true), Author = table.Column(type: "TEXT", nullable: true), BackgroundFile = table.Column(type: "TEXT", nullable: true), + VideoFile = table.Column(type: "TEXT", nullable: true), PreviewTime = table.Column(type: "INTEGER", nullable: false), Source = table.Column(type: "TEXT", nullable: true), Tags = table.Column(type: "TEXT", nullable: true), diff --git a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs b/osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs index 4cd234f2ef..ab85aece9f 100644 --- a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs +++ b/osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs @@ -125,6 +125,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs b/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs index 006acf12cd..d565e1cbf2 100644 --- a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs +++ b/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs @@ -128,6 +128,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs b/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs index fc2496bc24..3c37c59595 100644 --- a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs +++ b/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs @@ -128,6 +128,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20180125143340_Settings.Designer.cs b/osu.Game/Migrations/20180125143340_Settings.Designer.cs index 4bb599eec1..4c41d223c5 100644 --- a/osu.Game/Migrations/20180125143340_Settings.Designer.cs +++ b/osu.Game/Migrations/20180125143340_Settings.Designer.cs @@ -128,6 +128,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs b/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs index cdc4ef2e66..124c61283f 100644 --- a/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs +++ b/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs @@ -128,6 +128,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.Designer.cs b/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.Designer.cs index f28408bfb3..9cbd75ce2c 100644 --- a/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.Designer.cs +++ b/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.Designer.cs @@ -126,6 +126,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs index aaa11e88b6..150bc2ecbc 100644 --- a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs +++ b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs @@ -125,6 +125,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs b/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs index 7eeacd56d7..0a1db37c7f 100644 --- a/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs +++ b/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs @@ -125,6 +125,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs b/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs index 5ab43da046..b04d36fac1 100644 --- a/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs +++ b/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs @@ -127,6 +127,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs index b387a45ecf..aed946e577 100644 --- a/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs +++ b/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs @@ -127,6 +127,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.cs index 274b8030a9..c106b839e2 100644 --- a/osu.Game/Migrations/20181007180454_StandardizePaths.cs +++ b/osu.Game/Migrations/20181007180454_StandardizePaths.cs @@ -15,6 +15,7 @@ namespace osu.Game.Migrations migrationBuilder.Sql($"UPDATE `BeatmapInfo` SET `Path` = REPLACE(`Path`, '{windowsStyle}', '{standardized}')"); migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `AudioFile` = REPLACE(`AudioFile`, '{windowsStyle}', '{standardized}')"); migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `BackgroundFile` = REPLACE(`BackgroundFile`, '{windowsStyle}', '{standardized}')"); + migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `VideoFile` = REPLACE(`VideoFile`, '{windowsStyle}', '{standardized}')"); migrationBuilder.Sql($"UPDATE `BeatmapSetFileInfo` SET `Filename` = REPLACE(`Filename`, '{windowsStyle}', '{standardized}')"); migrationBuilder.Sql($"UPDATE `SkinFileInfo` SET `Filename` = REPLACE(`Filename`, '{windowsStyle}', '{standardized}')"); } diff --git a/osu.Game/Migrations/20181128100659_AddSkinInfoHash.Designer.cs b/osu.Game/Migrations/20181128100659_AddSkinInfoHash.Designer.cs index 120674671a..fe0594e542 100644 --- a/osu.Game/Migrations/20181128100659_AddSkinInfoHash.Designer.cs +++ b/osu.Game/Migrations/20181128100659_AddSkinInfoHash.Designer.cs @@ -127,6 +127,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20181130113755_AddScoreInfoTables.Designer.cs b/osu.Game/Migrations/20181130113755_AddScoreInfoTables.Designer.cs index eee53182ce..efa64f014f 100644 --- a/osu.Game/Migrations/20181130113755_AddScoreInfoTables.Designer.cs +++ b/osu.Game/Migrations/20181130113755_AddScoreInfoTables.Designer.cs @@ -127,6 +127,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs b/osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs index 8e1e3a59f3..a950a54e39 100644 --- a/osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs +++ b/osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs @@ -127,6 +127,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs b/osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs index 348c42adb9..df2c434b4e 100644 --- a/osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs +++ b/osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs @@ -127,6 +127,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs b/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs index 9477369aa0..ea699edcc9 100644 --- a/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs +++ b/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs @@ -127,6 +127,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.Designer.cs b/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.Designer.cs index c5fcc16f84..fb678178a2 100644 --- a/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.Designer.cs +++ b/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.Designer.cs @@ -131,6 +131,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index 761dca2801..1725812d33 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -129,6 +129,8 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); + b.Property("VideoFile"); + b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs b/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs index 299059407c..4b8720fe1c 100644 --- a/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Video; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -33,6 +34,8 @@ namespace osu.Game.Screens.Edit public Texture Background => workingBeatmap.Background; + public VideoSprite Video => workingBeatmap.Video; + public Track Track => workingBeatmap.Track; public Waveform Waveform => workingBeatmap.Waveform; diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index a555a52e42..3fc9662b17 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -10,6 +10,7 @@ using Newtonsoft.Json; using NUnit.Framework; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Video; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.Rulesets; @@ -201,6 +202,8 @@ namespace osu.Game.Tests.Beatmaps protected override Texture GetBackground() => throw new NotImplementedException(); + protected override VideoSprite GetVideo() => throw new NotImplementedException(); + protected override Track GetTrack() => throw new NotImplementedException(); protected override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index 0ef35879e3..0d9f4f51be 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -3,6 +3,7 @@ using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Video; using osu.Game.Beatmaps; namespace osu.Game.Tests.Beatmaps @@ -25,6 +26,8 @@ namespace osu.Game.Tests.Beatmaps protected override Texture GetBackground() => null; + protected override VideoSprite GetVideo() => null; + protected override Track GetTrack() => null; } } From 58a0b4e19b2f5cfe29e9a98e3d33d314ffd9c277 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 30 Aug 2019 23:19:55 +0300 Subject: [PATCH 2477/2854] Add basic layout for player --- osu.Game/Screens/Play/Player.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b487f3e61b..9ff52d8444 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -10,6 +10,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Video; using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Framework.Screens; @@ -81,6 +82,8 @@ namespace osu.Game.Screens.Play protected DimmableStoryboard DimmableStoryboard { get; private set; } + protected VideoSprite Video { get; private set; } + [Cached] [Cached(Type = typeof(IBindable>))] protected new readonly Bindable> Mods = new Bindable>(Array.Empty()); @@ -143,6 +146,14 @@ namespace osu.Game.Screens.Play private void addUnderlayComponents(Container target) { target.Add(DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both }); + + var video = Beatmap.Value.Video; + + if (video != null) + { + target.Add(Video = video); + Video.RelativeSizeAxes = Axes.Both; + } } private void addGameplayComponents(Container target, WorkingBeatmap working) From d55be4d59cd645e3dcde3d60c28c266235127115 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 30 Aug 2019 23:48:38 +0300 Subject: [PATCH 2478/2854] Implement DimmableVideo component --- osu.Game/Screens/Play/DimmableVideo.cs | 64 ++++++++++++++++++++++++++ osu.Game/Screens/Play/Player.cs | 10 +--- 2 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Screens/Play/DimmableVideo.cs diff --git a/osu.Game/Screens/Play/DimmableVideo.cs b/osu.Game/Screens/Play/DimmableVideo.cs new file mode 100644 index 0000000000..68ce5fcd40 --- /dev/null +++ b/osu.Game/Screens/Play/DimmableVideo.cs @@ -0,0 +1,64 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Video; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Screens.Play +{ + public class DimmableVideo : UserDimContainer + { + private readonly VideoSprite video; + private DrawableVideo drawableVideo; + + public DimmableVideo(VideoSprite video) + { + this.video = video; + } + + [BackgroundDependencyLoader] + private void load() + { + initializeVideo(false); + } + + protected override void LoadComplete() + { + ShowStoryboard.BindValueChanged(_ => initializeVideo(true), true); + base.LoadComplete(); + } + + protected override bool ShowDimContent => ShowStoryboard.Value && DimLevel < 1; + + private void initializeVideo(bool async) + { + if (drawableVideo != null) + return; + + if (!ShowStoryboard.Value) + return; + + drawableVideo = new DrawableVideo(video); + + if (async) + LoadComponentAsync(drawableVideo, Add); + else + Add(drawableVideo); + } + + private class DrawableVideo : Container + { + public DrawableVideo(VideoSprite video) + { + RelativeSizeAxes = Axes.Both; + Masking = true; + + AddInternal(video); + video.RelativeSizeAxes = Axes.Both; + } + } + } +} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 9ff52d8444..968b78ad8e 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -81,6 +81,7 @@ namespace osu.Game.Screens.Play protected GameplayClockContainer GameplayClockContainer { get; private set; } protected DimmableStoryboard DimmableStoryboard { get; private set; } + protected DimmableVideo DimmableVideo { get; private set; } protected VideoSprite Video { get; private set; } @@ -146,14 +147,7 @@ namespace osu.Game.Screens.Play private void addUnderlayComponents(Container target) { target.Add(DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both }); - - var video = Beatmap.Value.Video; - - if (video != null) - { - target.Add(Video = video); - Video.RelativeSizeAxes = Axes.Both; - } + target.Add(DimmableVideo = new DimmableVideo(Beatmap.Value.Video) { RelativeSizeAxes = Axes.Both }); } private void addGameplayComponents(Container target, WorkingBeatmap working) From 5dd688a51b841f534070fe3066905817a9dec8c4 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 31 Aug 2019 00:09:23 +0300 Subject: [PATCH 2479/2854] Fix video doesn't use gameplay clock --- osu.Game/Screens/Play/DimmableVideo.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/Play/DimmableVideo.cs b/osu.Game/Screens/Play/DimmableVideo.cs index 68ce5fcd40..bc4105c3b9 100644 --- a/osu.Game/Screens/Play/DimmableVideo.cs +++ b/osu.Game/Screens/Play/DimmableVideo.cs @@ -59,6 +59,13 @@ namespace osu.Game.Screens.Play AddInternal(video); video.RelativeSizeAxes = Axes.Both; } + + [BackgroundDependencyLoader] + private void load(GameplayClock clock) + { + if (clock != null) + Clock = clock; + } } } } From fa3591e5ec35b80115b3ed3005067ef1ae05231a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 31 Aug 2019 00:42:20 +0300 Subject: [PATCH 2480/2854] Add setting to turn on/off the video --- osu.Game/Configuration/OsuConfigManager.cs | 2 ++ osu.Game/Graphics/Containers/UserDimContainer.cs | 4 ++++ .../Overlays/Settings/Sections/Graphics/DetailSettings.cs | 5 +++++ osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs | 2 +- osu.Game/Screens/Play/DimmableVideo.cs | 6 +++--- osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs | 3 +++ 6 files changed, 18 insertions(+), 4 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 0cecbb225f..357883da45 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -69,6 +69,7 @@ namespace osu.Game.Configuration Set(OsuSetting.ShowFpsDisplay, false); Set(OsuSetting.ShowStoryboard, true); + Set(OsuSetting.ShowVideo, true); Set(OsuSetting.BeatmapSkins, true); Set(OsuSetting.BeatmapHitsounds, true); @@ -136,6 +137,7 @@ namespace osu.Game.Configuration DimLevel, BlurLevel, ShowStoryboard, + ShowVideo, KeyOverlay, ScoreMeter, FloatingComments, diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index 2b7635cc88..d0e932fac0 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -35,6 +35,8 @@ namespace osu.Game.Graphics.Containers protected Bindable ShowStoryboard { get; private set; } + protected Bindable ShowVideo { get; private set; } + protected double DimLevel => EnableUserDim.Value ? UserDimLevel.Value : 0; protected override Container Content => dimContent; @@ -54,10 +56,12 @@ namespace osu.Game.Graphics.Containers { UserDimLevel = config.GetBindable(OsuSetting.DimLevel); ShowStoryboard = config.GetBindable(OsuSetting.ShowStoryboard); + ShowVideo = config.GetBindable(OsuSetting.ShowVideo); EnableUserDim.ValueChanged += _ => UpdateVisuals(); UserDimLevel.ValueChanged += _ => UpdateVisuals(); ShowStoryboard.ValueChanged += _ => UpdateVisuals(); + ShowVideo.ValueChanged += _ => UpdateVisuals(); StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals(); } diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs index 01cdc9aa32..6d9870598f 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs @@ -22,6 +22,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics Bindable = config.GetBindable(OsuSetting.ShowStoryboard) }, new SettingsCheckbox + { + LabelText = "Video", + Bindable = config.GetBindable(OsuSetting.ShowVideo) + }, + new SettingsCheckbox { LabelText = "Rotate cursor when dragging", Bindable = config.GetBindable(OsuSetting.CursorRotation) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 5225740d0b..2730b0b90d 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -178,7 +178,7 @@ namespace osu.Game.Screens.Backgrounds BlurAmount.ValueChanged += _ => UpdateVisuals(); } - protected override bool ShowDimContent => !ShowStoryboard.Value || !StoryboardReplacesBackground.Value; // The background needs to be hidden in the case of it being replaced by the storyboard + protected override bool ShowDimContent => !ShowStoryboard.Value || !StoryboardReplacesBackground.Value || !ShowVideo.Value; // The background needs to be hidden in the case of it being replaced by the storyboard protected override void UpdateVisuals() { diff --git a/osu.Game/Screens/Play/DimmableVideo.cs b/osu.Game/Screens/Play/DimmableVideo.cs index bc4105c3b9..0452af8419 100644 --- a/osu.Game/Screens/Play/DimmableVideo.cs +++ b/osu.Game/Screens/Play/DimmableVideo.cs @@ -27,18 +27,18 @@ namespace osu.Game.Screens.Play protected override void LoadComplete() { - ShowStoryboard.BindValueChanged(_ => initializeVideo(true), true); + ShowVideo.BindValueChanged(_ => initializeVideo(true), true); base.LoadComplete(); } - protected override bool ShowDimContent => ShowStoryboard.Value && DimLevel < 1; + protected override bool ShowDimContent => ShowVideo.Value && DimLevel < 1; private void initializeVideo(bool async) { if (drawableVideo != null) return; - if (!ShowStoryboard.Value) + if (!ShowVideo.Value) return; drawableVideo = new DrawableVideo(video); diff --git a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs index 1c8628f704..5e47b730ad 100644 --- a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs @@ -15,6 +15,7 @@ namespace osu.Game.Screens.Play.PlayerSettings private readonly PlayerSliderBar dimSliderBar; private readonly PlayerSliderBar blurSliderBar; private readonly PlayerCheckbox showStoryboardToggle; + private readonly PlayerCheckbox showVideoToggle; private readonly PlayerCheckbox beatmapSkinsToggle; private readonly PlayerCheckbox beatmapHitsoundsToggle; @@ -37,6 +38,7 @@ namespace osu.Game.Screens.Play.PlayerSettings Text = "Toggles:" }, showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" }, + showVideoToggle = new PlayerCheckbox { LabelText = "Video" }, beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" }, beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" } }; @@ -48,6 +50,7 @@ namespace osu.Game.Screens.Play.PlayerSettings dimSliderBar.Bindable = config.GetBindable(OsuSetting.DimLevel); blurSliderBar.Bindable = config.GetBindable(OsuSetting.BlurLevel); showStoryboardToggle.Current = config.GetBindable(OsuSetting.ShowStoryboard); + showVideoToggle.Current = config.GetBindable(OsuSetting.ShowVideo); beatmapSkinsToggle.Current = config.GetBindable(OsuSetting.BeatmapSkins); beatmapHitsoundsToggle.Current = config.GetBindable(OsuSetting.BeatmapHitsounds); } From 264441d90c0662edfdcf722e7806f9997551c0de Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 31 Aug 2019 02:42:26 +0300 Subject: [PATCH 2481/2854] Fix broken test --- osu.Game.Tests/WaveformTestBeatmap.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs index 3e0df8d45e..db9576b5fa 100644 --- a/osu.Game.Tests/WaveformTestBeatmap.cs +++ b/osu.Game.Tests/WaveformTestBeatmap.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Video; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.IO.Archives; @@ -42,6 +43,8 @@ namespace osu.Game.Tests protected override Texture GetBackground() => null; + protected override VideoSprite GetVideo() => null; + protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(firstAudioFile)); protected override Track GetTrack() => trackStore.Get(firstAudioFile); From fd958ec1abb31f182b673dc2b13223df9a854560 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 31 Aug 2019 02:56:41 +0300 Subject: [PATCH 2482/2854] Remove unused property accessor --- osu.Game/Screens/Play/Player.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 968b78ad8e..b5a378506a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -83,8 +83,6 @@ namespace osu.Game.Screens.Play protected DimmableStoryboard DimmableStoryboard { get; private set; } protected DimmableVideo DimmableVideo { get; private set; } - protected VideoSprite Video { get; private set; } - [Cached] [Cached(Type = typeof(IBindable>))] protected new readonly Bindable> Mods = new Bindable>(Array.Empty()); From d4291556eef6a67926b0dffd855c933abfc1ab82 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 31 Aug 2019 02:57:14 +0300 Subject: [PATCH 2483/2854] Remove unused using --- osu.Game/Screens/Play/Player.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b5a378506a..274107dfa5 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -10,7 +10,6 @@ using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Video; using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Framework.Screens; From 1ddf292ad687878c0acf8ef247a0060ba08a5d5c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Aug 2019 12:20:50 +0900 Subject: [PATCH 2484/2854] Fix vertical alignment of hit error display ticks Wasn't correctly centered before. --- osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index d5d3cb528e..594dd64e52 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -238,7 +238,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters { Y = getRelativeJudgementPosition(judgement.TimeOffset), Anchor = alignment == Anchor.x2 ? Anchor.x0 : Anchor.x2, - Origin = alignment == Anchor.x2 ? Anchor.x0 : Anchor.x2, + Origin = Anchor.y1 | (alignment == Anchor.x2 ? Anchor.x0 : Anchor.x2), }); arrow.MoveToY( From f89981e1a330c3105e7fe87481f7a0df6721fa32 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Aug 2019 12:23:15 +0900 Subject: [PATCH 2485/2854] Fix legacy skin text reading from the wrong source Regressed with ruleset legacy skin implementation. --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs index 3c508f34e0..d1bd0f0a88 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Skinning return !hasFont(font) ? null - : new LegacySpriteText(this, font) + : new LegacySpriteText(source, font) { // Spacing value was reverse-engineered from the ratio of the rendered sprite size in the visual inspector vs the actual texture size Scale = new Vector2(0.96f), @@ -117,6 +117,6 @@ namespace osu.Game.Rulesets.Osu.Skinning public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => configuration.Value is TConfiguration conf ? query.Invoke(conf) : default; - private bool hasFont(string fontName) => GetTexture($"{fontName}-0") != null; + private bool hasFont(string fontName) => source.GetTexture($"{fontName}-0") != null; } } From 12eeec36fca2dccda14043caab9802aa9cdc9754 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Aug 2019 12:33:29 +0900 Subject: [PATCH 2486/2854] Fix ruleset skins incorrectly providing configuration defaults --- osu.Game/Skinning/DefaultSkin.cs | 2 +- osu.Game/Skinning/DefaultSkinConfiguration.cs | 28 +++++++++++++++++++ osu.Game/Skinning/LegacySkin.cs | 2 +- osu.Game/Skinning/SkinConfiguration.cs | 15 ++++------ 4 files changed, 36 insertions(+), 11 deletions(-) create mode 100644 osu.Game/Skinning/DefaultSkinConfiguration.cs diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 6072bb64ed..ec957566cb 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -13,7 +13,7 @@ namespace osu.Game.Skinning public DefaultSkin() : base(SkinInfo.Default) { - Configuration = new SkinConfiguration(); + Configuration = new DefaultSkinConfiguration(); } public override Drawable GetDrawableComponent(string componentName) => null; diff --git a/osu.Game/Skinning/DefaultSkinConfiguration.cs b/osu.Game/Skinning/DefaultSkinConfiguration.cs new file mode 100644 index 0000000000..722b35f102 --- /dev/null +++ b/osu.Game/Skinning/DefaultSkinConfiguration.cs @@ -0,0 +1,28 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osuTK.Graphics; + +namespace osu.Game.Skinning +{ + /// + /// A skin configuration pre-populated with sane defaults. + /// + public class DefaultSkinConfiguration : SkinConfiguration + { + public DefaultSkinConfiguration() + { + HitCircleFont = "default"; + + ComboColours.AddRange(new[] + { + new Color4(17, 136, 170, 255), + new Color4(102, 136, 0, 255), + new Color4(204, 102, 0, 255), + new Color4(121, 9, 13, 255) + }); + + CursorExpand = true; + } + } +} diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 1572c588e8..56b8aa19c2 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -34,7 +34,7 @@ namespace osu.Game.Skinning using (StreamReader reader = new StreamReader(stream)) Configuration = new LegacySkinDecoder().Decode(reader); else - Configuration = new SkinConfiguration(); + Configuration = new DefaultSkinConfiguration(); Samples = audioManager.GetSampleStore(storage); Textures = new TextureStore(new TextureLoaderStore(storage)); diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs index 93b599f9f6..d585c58ef1 100644 --- a/osu.Game/Skinning/SkinConfiguration.cs +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -7,21 +7,18 @@ using osuTK.Graphics; namespace osu.Game.Skinning { + /// + /// An empty skin configuration. + /// public class SkinConfiguration : IHasComboColours, IHasCustomColours { public readonly SkinInfo SkinInfo = new SkinInfo(); - public List ComboColours { get; set; } = new List - { - new Color4(17, 136, 170, 255), - new Color4(102, 136, 0, 255), - new Color4(204, 102, 0, 255), - new Color4(121, 9, 13, 255) - }; + public List ComboColours { get; set; } = new List(); public Dictionary CustomColours { get; set; } = new Dictionary(); - public string HitCircleFont { get; set; } = "default"; + public string HitCircleFont { get; set; } public int HitCircleOverlap { get; set; } @@ -29,6 +26,6 @@ namespace osu.Game.Skinning public float? SliderPathRadius { get; set; } - public bool? CursorExpand { get; set; } = true; + public bool? CursorExpand { get; set; } } } From d2a3e0581b750c030d197573e6923815ca2e60f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Aug 2019 13:27:03 +0900 Subject: [PATCH 2487/2854] Fix legacy decoder using wrong configuration --- osu.Game/Skinning/LegacySkinDecoder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index ecb112955c..0160755eed 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -5,14 +5,14 @@ using osu.Game.Beatmaps.Formats; namespace osu.Game.Skinning { - public class LegacySkinDecoder : LegacyDecoder + public class LegacySkinDecoder : LegacyDecoder { public LegacySkinDecoder() : base(1) { } - protected override void ParseLine(SkinConfiguration skin, Section section, string line) + protected override void ParseLine(DefaultSkinConfiguration skin, Section section, string line) { line = StripComments(line); From 2988624f1fd66896c72cb286984a733ebd4bfb02 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Aug 2019 16:52:41 +0900 Subject: [PATCH 2488/2854] Add fallback for safety --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs index 3c508f34e0..97db2d7c8e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs @@ -115,7 +115,7 @@ namespace osu.Game.Rulesets.Osu.Skinning public SampleChannel GetSample(ISampleInfo sample) => null; public TValue GetValue(Func query) where TConfiguration : SkinConfiguration - => configuration.Value is TConfiguration conf ? query.Invoke(conf) : default; + => configuration.Value is TConfiguration conf ? query.Invoke(conf) : source.GetValue(query); private bool hasFont(string fontName) => GetTexture($"{fontName}-0") != null; } From 3da5eb6c8b3642980a8f2e0197d0a644122bae13 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Aug 2019 16:56:32 +0900 Subject: [PATCH 2489/2854] Add source lookups for safety --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs index d1bd0f0a88..f9fbdcd0e9 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs @@ -110,9 +110,9 @@ namespace osu.Game.Rulesets.Osu.Skinning return null; } - public Texture GetTexture(string componentName) => null; + public Texture GetTexture(string componentName) => source.GetTexture(componentName); - public SampleChannel GetSample(ISampleInfo sample) => null; + public SampleChannel GetSample(ISampleInfo sample) => source.GetSample(sample); public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => configuration.Value is TConfiguration conf ? query.Invoke(conf) : default; From cbbc6aad6eefdf4bcac3b1fba363dce0e66879d5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 31 Aug 2019 21:32:02 +0900 Subject: [PATCH 2490/2854] Make method static --- osu.Game/Screens/Edit/BindableBeatDivisor.cs | 2 +- osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/BindableBeatDivisor.cs index 055077cc4f..2aeb1ef04b 100644 --- a/osu.Game/Screens/Edit/BindableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/BindableBeatDivisor.cs @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Edit /// The beat divisor. /// The set of colours. /// The applicable colour from for . - public ColourInfo GetColourFor(int beatDivisor, OsuColour colours) + public static ColourInfo GetColourFor(int beatDivisor, OsuColour colours) { switch (beatDivisor) { diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index ddcdfdaf80..4d89e43ee5 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -212,7 +212,7 @@ namespace osu.Game.Screens.Edit.Compose.Components Anchor = Anchor.TopLeft, Origin = Anchor.TopCentre, RelativePositionAxes = Axes.X, - Colour = beatDivisor.GetColourFor(t, colours), + Colour = BindableBeatDivisor.GetColourFor(t, colours), X = getMappedPosition(t) }); } From c10d2302dc6d276a3f50601bd59db44a1b4ec443 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 31 Aug 2019 15:49:57 +0300 Subject: [PATCH 2491/2854] Remove video property from migrations --- osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs | 2 -- .../Migrations/20171025071459_AddMissingIndexRules.Designer.cs | 2 -- ...0171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs | 2 -- .../20171209034410_AddRulesetInfoShortName.Designer.cs | 2 -- osu.Game/Migrations/20180125143340_Settings.Designer.cs | 2 -- osu.Game/Migrations/20180219060912_AddSkins.Designer.cs | 2 -- .../20180529055154_RemoveUniqueHashConstraints.Designer.cs | 2 -- .../20180621044111_UpdateTaikoDefaultBindings.Designer.cs | 2 -- .../Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs | 2 -- osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs | 2 -- osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs | 2 -- osu.Game/Migrations/20181128100659_AddSkinInfoHash.Designer.cs | 2 -- .../Migrations/20181130113755_AddScoreInfoTables.Designer.cs | 2 -- osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs | 2 -- osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs | 2 -- .../20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs | 2 -- .../20190708070844_AddBPMAndLengthColumns.Designer.cs | 2 -- osu.Game/Migrations/OsuDbContextModelSnapshot.cs | 2 -- 18 files changed, 36 deletions(-) diff --git a/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs b/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs index 596d80557b..c751530bf4 100644 --- a/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs +++ b/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs @@ -123,8 +123,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs b/osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs index ab85aece9f..4cd234f2ef 100644 --- a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs +++ b/osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs @@ -125,8 +125,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs b/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs index d565e1cbf2..006acf12cd 100644 --- a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs +++ b/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs @@ -128,8 +128,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs b/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs index 3c37c59595..fc2496bc24 100644 --- a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs +++ b/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs @@ -128,8 +128,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20180125143340_Settings.Designer.cs b/osu.Game/Migrations/20180125143340_Settings.Designer.cs index 4c41d223c5..4bb599eec1 100644 --- a/osu.Game/Migrations/20180125143340_Settings.Designer.cs +++ b/osu.Game/Migrations/20180125143340_Settings.Designer.cs @@ -128,8 +128,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs b/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs index 124c61283f..cdc4ef2e66 100644 --- a/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs +++ b/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs @@ -128,8 +128,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.Designer.cs b/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.Designer.cs index 9cbd75ce2c..f28408bfb3 100644 --- a/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.Designer.cs +++ b/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.Designer.cs @@ -126,8 +126,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs index 150bc2ecbc..aaa11e88b6 100644 --- a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs +++ b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs @@ -125,8 +125,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs b/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs index 0a1db37c7f..7eeacd56d7 100644 --- a/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs +++ b/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs @@ -125,8 +125,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs b/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs index b04d36fac1..5ab43da046 100644 --- a/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs +++ b/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs @@ -127,8 +127,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs index aed946e577..b387a45ecf 100644 --- a/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs +++ b/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs @@ -127,8 +127,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20181128100659_AddSkinInfoHash.Designer.cs b/osu.Game/Migrations/20181128100659_AddSkinInfoHash.Designer.cs index fe0594e542..120674671a 100644 --- a/osu.Game/Migrations/20181128100659_AddSkinInfoHash.Designer.cs +++ b/osu.Game/Migrations/20181128100659_AddSkinInfoHash.Designer.cs @@ -127,8 +127,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20181130113755_AddScoreInfoTables.Designer.cs b/osu.Game/Migrations/20181130113755_AddScoreInfoTables.Designer.cs index efa64f014f..eee53182ce 100644 --- a/osu.Game/Migrations/20181130113755_AddScoreInfoTables.Designer.cs +++ b/osu.Game/Migrations/20181130113755_AddScoreInfoTables.Designer.cs @@ -127,8 +127,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs b/osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs index a950a54e39..8e1e3a59f3 100644 --- a/osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs +++ b/osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs @@ -127,8 +127,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs b/osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs index df2c434b4e..348c42adb9 100644 --- a/osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs +++ b/osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs @@ -127,8 +127,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs b/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs index ea699edcc9..9477369aa0 100644 --- a/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs +++ b/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs @@ -127,8 +127,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.Designer.cs b/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.Designer.cs index fb678178a2..c5fcc16f84 100644 --- a/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.Designer.cs +++ b/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.Designer.cs @@ -131,8 +131,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index 1725812d33..761dca2801 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -129,8 +129,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("VideoFile"); - b.Property("PreviewTime"); b.Property("Source"); From d2f7a653a8aecc198fb7cadaddbbe75e2888b855 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 31 Aug 2019 16:10:07 +0300 Subject: [PATCH 2492/2854] Fix nullref --- osu.Game/Screens/Play/DimmableVideo.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/DimmableVideo.cs b/osu.Game/Screens/Play/DimmableVideo.cs index 0452af8419..3e6b95d2cc 100644 --- a/osu.Game/Screens/Play/DimmableVideo.cs +++ b/osu.Game/Screens/Play/DimmableVideo.cs @@ -35,6 +35,9 @@ namespace osu.Game.Screens.Play private void initializeVideo(bool async) { + if (video == null) + return; + if (drawableVideo != null) return; From 94512fea8e82504f920a3e56a0a195029b831cdf Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 31 Aug 2019 16:20:33 +0300 Subject: [PATCH 2493/2854] Apply naming suggestions --- osu.Game/Beatmaps/IWorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 1 - osu.Game/Configuration/OsuConfigManager.cs | 4 ++-- osu.Game/Graphics/Containers/UserDimContainer.cs | 2 +- .../Overlays/Settings/Sections/Graphics/DetailSettings.cs | 2 +- osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index b932e67bae..a087a52ada 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -27,7 +27,7 @@ namespace osu.Game.Beatmaps Texture Background { get; } /// - /// Retrieves the video file for this . + /// Retrieves the video background file for this . /// VideoSprite Video { get; } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index b489936556..bf3fa90d7b 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -185,7 +185,6 @@ namespace osu.Game.Beatmaps public bool BackgroundLoaded => background.IsResultAvailable; public Texture Background => background.Value; protected virtual bool BackgroundStillValid(Texture b) => b == null || b.Available; - protected abstract Texture GetBackground(); private readonly RecyclableLazy background; diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 357883da45..71a74a5558 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -69,7 +69,7 @@ namespace osu.Game.Configuration Set(OsuSetting.ShowFpsDisplay, false); Set(OsuSetting.ShowStoryboard, true); - Set(OsuSetting.ShowVideo, true); + Set(OsuSetting.ShowVideoBackground, true); Set(OsuSetting.BeatmapSkins, true); Set(OsuSetting.BeatmapHitsounds, true); @@ -137,7 +137,7 @@ namespace osu.Game.Configuration DimLevel, BlurLevel, ShowStoryboard, - ShowVideo, + ShowVideoBackground, KeyOverlay, ScoreMeter, FloatingComments, diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index d0e932fac0..7683bbcd63 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -56,7 +56,7 @@ namespace osu.Game.Graphics.Containers { UserDimLevel = config.GetBindable(OsuSetting.DimLevel); ShowStoryboard = config.GetBindable(OsuSetting.ShowStoryboard); - ShowVideo = config.GetBindable(OsuSetting.ShowVideo); + ShowVideo = config.GetBindable(OsuSetting.ShowVideoBackground); EnableUserDim.ValueChanged += _ => UpdateVisuals(); UserDimLevel.ValueChanged += _ => UpdateVisuals(); diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs index 6d9870598f..56e56f6ca8 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics new SettingsCheckbox { LabelText = "Video", - Bindable = config.GetBindable(OsuSetting.ShowVideo) + Bindable = config.GetBindable(OsuSetting.ShowVideoBackground) }, new SettingsCheckbox { diff --git a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs index 5e47b730ad..ff64f35a18 100644 --- a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs @@ -50,7 +50,7 @@ namespace osu.Game.Screens.Play.PlayerSettings dimSliderBar.Bindable = config.GetBindable(OsuSetting.DimLevel); blurSliderBar.Bindable = config.GetBindable(OsuSetting.BlurLevel); showStoryboardToggle.Current = config.GetBindable(OsuSetting.ShowStoryboard); - showVideoToggle.Current = config.GetBindable(OsuSetting.ShowVideo); + showVideoToggle.Current = config.GetBindable(OsuSetting.ShowVideoBackground); beatmapSkinsToggle.Current = config.GetBindable(OsuSetting.BeatmapSkins); beatmapHitsoundsToggle.Current = config.GetBindable(OsuSetting.BeatmapHitsounds); } From 1b4ae5a4a495917257be26a88a0b48a57cc231e4 Mon Sep 17 00:00:00 2001 From: pi1024e Date: Thu, 22 Aug 2019 22:37:01 -0400 Subject: [PATCH 2494/2854] Spelling fixes --- osu.Game/Online/API/APIAccess.cs | 2 +- osu.Game/Screens/Menu/IntroCircles.cs | 2 +- osu.Game/Screens/Menu/LogoVisualisation.cs | 4 ++-- osu.Game/Screens/Menu/OsuLogo.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index d722c7a98a..0303293c41 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -165,7 +165,7 @@ namespace osu.Game.Online.API } // The Success callback event is fired on the main thread, so we should wait for that to run before proceeding. - // Without this, we will end up circulating this Connecting loop multiple times and queueing up many web requests + // Without this, we will end up circulating this Connecting loop multiple times and queuing up many web requests // before actually going online. while (State > APIState.Offline && State < APIState.Online) Thread.Sleep(500); diff --git a/osu.Game/Screens/Menu/IntroCircles.cs b/osu.Game/Screens/Menu/IntroCircles.cs index 4fa1a81123..c069f82134 100644 --- a/osu.Game/Screens/Menu/IntroCircles.cs +++ b/osu.Game/Screens/Menu/IntroCircles.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Menu Scheduler.AddDelayed(delegate { - // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu. + // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu. if (menuMusic.Value) { track.Restart(); diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 9d0a5cd05b..6984959e9c 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -47,7 +47,7 @@ namespace osu.Game.Screens.Menu private const float visualiser_rounds = 5; /// - /// How much should each bar go down each milisecond (based on a full bar). + /// How much should each bar go down each millisecond (based on a full bar). /// private const float decay_per_milisecond = 0.0024f; @@ -161,7 +161,7 @@ namespace osu.Game.Screens.Menu private IShader shader; private Texture texture; - //Asuming the logo is a circle, we don't need a second dimension. + //Assuming the logo is a circle, we don't need a second dimension. private float size; private Color4 colour; diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 0c5bf12bdb..d37cfe32db 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -229,7 +229,7 @@ namespace osu.Game.Screens.Menu } /// - /// Schedule a new extenral animation. Handled queueing and finishing previous animations in a sane way. + /// Schedule a new external animation. Handled queuing and finishing previous animations in a sane way. /// /// The animation to be performed /// If true, the new animation is delayed until all previous transforms finish. If false, existing transformed are cleared. From 5695bb670e62db544b536942bfd52db23554b461 Mon Sep 17 00:00:00 2001 From: pi1024e Date: Fri, 30 Aug 2019 13:48:45 -0400 Subject: [PATCH 2495/2854] change back to queuing --- osu.Game/Online/API/APIAccess.cs | 2 +- osu.Game/Screens/Menu/OsuLogo.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 0303293c41..d722c7a98a 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -165,7 +165,7 @@ namespace osu.Game.Online.API } // The Success callback event is fired on the main thread, so we should wait for that to run before proceeding. - // Without this, we will end up circulating this Connecting loop multiple times and queuing up many web requests + // Without this, we will end up circulating this Connecting loop multiple times and queueing up many web requests // before actually going online. while (State > APIState.Offline && State < APIState.Online) Thread.Sleep(500); diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index d37cfe32db..534400e720 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -229,7 +229,7 @@ namespace osu.Game.Screens.Menu } /// - /// Schedule a new external animation. Handled queuing and finishing previous animations in a sane way. + /// Schedule a new external animation. Handled queueing and finishing previous animations in a sane way. /// /// The animation to be performed /// If true, the new animation is delayed until all previous transforms finish. If false, existing transformed are cleared. From a1c72db5f62001aa62f994f5f24c463032a8ddce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 31 Aug 2019 17:01:12 +0200 Subject: [PATCH 2496/2854] Fix inconsistent sound effects on mod buttons Because HoverClickSounds.OnClick() does not fire upon right clicking on mod buttons, the sound effects that play on left and right click were inconsistent. Introduce HoverMouseUpSounds drawable that allows to play the click sound effect upon mouse up events for an arbitrary set of mouse buttons and use it on mod buttons. --- .../UserInterface/HoverMouseUpSounds.cs | 42 +++++++++++++++++++ osu.Game/Overlays/Mods/ModButton.cs | 3 +- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Graphics/UserInterface/HoverMouseUpSounds.cs diff --git a/osu.Game/Graphics/UserInterface/HoverMouseUpSounds.cs b/osu.Game/Graphics/UserInterface/HoverMouseUpSounds.cs new file mode 100644 index 0000000000..dcb443d5aa --- /dev/null +++ b/osu.Game/Graphics/UserInterface/HoverMouseUpSounds.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Extensions; +using osu.Framework.Input.Events; +using osuTK.Input; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// Adds hover sounds to a drawable, as well as click sounds upon MouseUp events for selected mouse buttons. + /// Intended to be used for controls that can respond to clicks of buttons other than the left mouse button in place of . + /// + public class HoverMouseUpSounds : HoverSounds + { + private SampleChannel sampleClick; + private readonly List buttons; + + public HoverMouseUpSounds(List buttons, HoverSampleSet sampleSet = HoverSampleSet.Normal) + : base(sampleSet) + { + this.buttons = buttons; + } + + protected override bool OnMouseUp(MouseUpEvent e) + { + if (Contains(e.ScreenSpaceMousePosition) && buttons.Contains(e.Button)) + sampleClick?.Play(); + return base.OnMouseUp(e); + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleClick = audio.Samples.Get($@"UI/generic-select{SampleSet.GetDescription()}"); + } + } +} diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 7b8745cf42..ba39360102 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -11,6 +11,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Events; @@ -283,7 +284,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.TopCentre, Font = OsuFont.GetFont(size: 18) }, - new HoverClickSounds() + new HoverMouseUpSounds(new List { MouseButton.Left, MouseButton.Right }) }; Mod = mod; From 658e0edc3e7df2510953bc9acc11aac35b081590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 31 Aug 2019 20:16:16 +0200 Subject: [PATCH 2497/2854] Handle other button clicks in HoverClickSounds As suggested in review, remove previously introduced HoverMouseUpSounds and instead change effect playing logic in HoverClickSounds by moving it out of OnClick() to OnMouseUp(). Users of the class can either use the existing constructor to play the effect only on left click or use the newly introduced constructor with the MouseButton[] parameter to specify which button clicks should trigger the sound. --- .../UserInterface/HoverClickSounds.cs | 32 ++++++++++++-- .../UserInterface/HoverMouseUpSounds.cs | 42 ------------------- osu.Game/Overlays/Mods/ModButton.cs | 3 +- 3 files changed, 29 insertions(+), 48 deletions(-) delete mode 100644 osu.Game/Graphics/UserInterface/HoverMouseUpSounds.cs diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs index 70d988f60e..8fe20e3566 100644 --- a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs @@ -1,11 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Extensions; using osu.Framework.Input.Events; +using osuTK.Input; namespace osu.Game.Graphics.UserInterface { @@ -16,16 +18,38 @@ namespace osu.Game.Graphics.UserInterface public class HoverClickSounds : HoverSounds { private SampleChannel sampleClick; + private readonly MouseButton[] buttons; + /// + /// Creates an instance that adds sounds on hover and left click only. + /// + /// Set of click samples to play. public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) - : base(sampleSet) + : this(new[] { MouseButton.Left }, sampleSet) { } - protected override bool OnClick(ClickEvent e) + /// + /// Creates an instance that adds sounds on hover and on click for any of the buttons specified. + /// + /// Array of button codes which should trigger the click sound. + /// Set of click samples to play. + public HoverClickSounds(MouseButton[] buttons, HoverSampleSet sampleSet = HoverSampleSet.Normal) + : base(sampleSet) { - sampleClick?.Play(); - return base.OnClick(e); + this.buttons = buttons; + } + + protected override bool OnMouseUp(MouseUpEvent e) + { + var index = Array.IndexOf(buttons, e.Button); + bool shouldPlayEffect = index > -1 && index < buttons.Length; + + // examine the button pressed first for short-circuiting + // in most usages it is more likely that another button was pressed than that the cursor left the drawable bounds + if (shouldPlayEffect && Contains(e.ScreenSpaceMousePosition)) + sampleClick?.Play(); + return base.OnMouseUp(e); } [BackgroundDependencyLoader] diff --git a/osu.Game/Graphics/UserInterface/HoverMouseUpSounds.cs b/osu.Game/Graphics/UserInterface/HoverMouseUpSounds.cs deleted file mode 100644 index dcb443d5aa..0000000000 --- a/osu.Game/Graphics/UserInterface/HoverMouseUpSounds.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Extensions; -using osu.Framework.Input.Events; -using osuTK.Input; - -namespace osu.Game.Graphics.UserInterface -{ - /// - /// Adds hover sounds to a drawable, as well as click sounds upon MouseUp events for selected mouse buttons. - /// Intended to be used for controls that can respond to clicks of buttons other than the left mouse button in place of . - /// - public class HoverMouseUpSounds : HoverSounds - { - private SampleChannel sampleClick; - private readonly List buttons; - - public HoverMouseUpSounds(List buttons, HoverSampleSet sampleSet = HoverSampleSet.Normal) - : base(sampleSet) - { - this.buttons = buttons; - } - - protected override bool OnMouseUp(MouseUpEvent e) - { - if (Contains(e.ScreenSpaceMousePosition) && buttons.Contains(e.Button)) - sampleClick?.Play(); - return base.OnMouseUp(e); - } - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - sampleClick = audio.Samples.Get($@"UI/generic-select{SampleSet.GetDescription()}"); - } - } -} diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index ba39360102..f46555dc4b 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -11,7 +11,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using System; -using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Events; @@ -284,7 +283,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.TopCentre, Font = OsuFont.GetFont(size: 18) }, - new HoverMouseUpSounds(new List { MouseButton.Left, MouseButton.Right }) + new HoverClickSounds(new[] { MouseButton.Left, MouseButton.Right }) }; Mod = mod; From aff4dab9aa342955a44e53335c6899fed8fbe3b3 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 1 Sep 2019 03:17:55 +0300 Subject: [PATCH 2498/2854] Ensure playing track of beatmap selected only if a track change occurred --- osu.Game/Screens/Select/SongSelect.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index edb0e6deb8..d0cb5986a8 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -414,7 +414,11 @@ namespace osu.Game.Screens.Select { Logger.Log($"beatmap changed from \"{Beatmap.Value.BeatmapInfo}\" to \"{beatmap}\""); - Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap.Value); + WorkingBeatmap previous = Beatmap.Value; + Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, previous); + + if (this.IsCurrentScreen() && Beatmap.Value?.Track != previous?.Track) + ensurePlayingSelected(); if (beatmap != null) { @@ -425,8 +429,6 @@ namespace osu.Game.Screens.Select } } - if (this.IsCurrentScreen()) - ensurePlayingSelected(); UpdateBeatmap(Beatmap.Value); } } From 7d955839be8fc02b466ab53000598e98c36afb9c Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 1 Sep 2019 04:22:24 +0300 Subject: [PATCH 2499/2854] Instantly move rank graph tooltip --- .../Overlays/Profile/Header/Components/RankGraph.cs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index 24ed0cc022..56405483af 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -283,18 +283,7 @@ namespace osu.Game.Overlays.Profile.Header.Components return true; } - private bool instantMove = true; - - public void Move(Vector2 pos) - { - if (instantMove) - { - Position = pos; - instantMove = false; - } - else - this.MoveTo(pos, 200, Easing.OutQuint); - } + public void Move(Vector2 pos) => Position = pos; protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); From a155814bc48b5547b63f50ea60a4ae67bc377bd8 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 1 Sep 2019 06:07:25 +0300 Subject: [PATCH 2500/2854] Implement instant movement properly --- .../Profile/Header/Components/RankGraph.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index 56405483af..c6d96c5917 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -283,9 +283,24 @@ namespace osu.Game.Overlays.Profile.Header.Components return true; } - public void Move(Vector2 pos) => Position = pos; + private bool instantMove = true; - protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); + public void Move(Vector2 pos) + { + if (instantMove) + { + Position = pos; + instantMove = false; + } + else + this.MoveTo(pos, 200, Easing.OutQuint); + } + + protected override void PopIn() + { + instantMove |= !IsPresent; + this.FadeIn(200, Easing.OutQuint); + } protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); } From d1eafafa51b6632c5facea6e9b952e29fdef3441 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 1 Sep 2019 19:57:12 +0900 Subject: [PATCH 2501/2854] Allow searching channels by topics Closes #5939 --- osu.Game/Overlays/Chat/Selection/ChannelListItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs b/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs index 4d77e5f93d..cb0639d85d 100644 --- a/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Chat.Selection private Color4 topicColour; private Color4 hoverColour; - public IEnumerable FilterTerms => new[] { channel.Name }; + public IEnumerable FilterTerms => new[] { channel.Name, channel.Topic }; public bool MatchingFilter { From c4dc34eefde8740e12bd2ed974f81841d862c878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 1 Sep 2019 13:10:11 +0200 Subject: [PATCH 2502/2854] Consolidate HoverClickSounds constructors As suggested in review, merge both HoverClickSounds constructors into one accepting optional arguments. Due to existing usages the parameter is added as second and supplied by name in ModButton. --- .../Graphics/UserInterface/HoverClickSounds.cs | 18 ++++++------------ osu.Game/Overlays/Mods/ModButton.cs | 2 +- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs index 8fe20e3566..7e6c0a0974 100644 --- a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs @@ -20,24 +20,18 @@ namespace osu.Game.Graphics.UserInterface private SampleChannel sampleClick; private readonly MouseButton[] buttons; - /// - /// Creates an instance that adds sounds on hover and left click only. - /// - /// Set of click samples to play. - public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) - : this(new[] { MouseButton.Left }, sampleSet) - { - } - /// /// Creates an instance that adds sounds on hover and on click for any of the buttons specified. /// - /// Array of button codes which should trigger the click sound. /// Set of click samples to play. - public HoverClickSounds(MouseButton[] buttons, HoverSampleSet sampleSet = HoverSampleSet.Normal) + /// + /// Array of button codes which should trigger the click sound. + /// If this optional parameter is omitted or set to null, the click sound will also be added on left click. + /// + public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal, MouseButton[] buttons = null) : base(sampleSet) { - this.buttons = buttons; + this.buttons = buttons ?? new[] { MouseButton.Left }; } protected override bool OnMouseUp(MouseUpEvent e) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index f46555dc4b..58892cd0dd 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -283,7 +283,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.TopCentre, Font = OsuFont.GetFont(size: 18) }, - new HoverClickSounds(new[] { MouseButton.Left, MouseButton.Right }) + new HoverClickSounds(buttons: new[] { MouseButton.Left, MouseButton.Right }) }; Mod = mod; From fc48b190fedb15f243e6087315567907cc80c166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 1 Sep 2019 13:32:53 +0200 Subject: [PATCH 2503/2854] Fix inaccurate xmldoc --- osu.Game/Graphics/UserInterface/HoverClickSounds.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs index 7e6c0a0974..050e5a2835 100644 --- a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs @@ -26,7 +26,7 @@ namespace osu.Game.Graphics.UserInterface /// Set of click samples to play. /// /// Array of button codes which should trigger the click sound. - /// If this optional parameter is omitted or set to null, the click sound will also be added on left click. + /// If this optional parameter is omitted or set to null, the click sound will only be added on left click. /// public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal, MouseButton[] buttons = null) : base(sampleSet) From 7ca51d3866657302a2c30f90b5e7a757bdb3fe93 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 11:20:50 +0900 Subject: [PATCH 2504/2854] Fix resume overlay being drawn below cursor Closes #5905. --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 11 +++++++---- osu.Game/Screens/Play/Player.cs | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 021bd515b5..0ee9196fb8 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -215,10 +215,6 @@ namespace osu.Game.Rulesets.UI continueResume(); } - public ResumeOverlay ResumeOverlay { get; private set; } - - protected virtual ResumeOverlay CreateResumeOverlay() => null; - /// /// Creates and adds the visual representation of a to this . /// @@ -389,6 +385,13 @@ namespace osu.Game.Rulesets.UI /// public abstract GameplayCursorContainer Cursor { get; } + /// + /// An optional overlay used when resuming gameplay from a paused state. + /// + public ResumeOverlay ResumeOverlay { get; protected set; } + + protected virtual ResumeOverlay CreateResumeOverlay() => null; + /// /// Sets a replay to be used, overriding local input. /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b487f3e61b..3f1603eabe 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -178,6 +178,7 @@ namespace osu.Game.Screens.Play }, // display the cursor above some HUD elements. DrawableRuleset.Cursor?.CreateProxy() ?? new Container(), + DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(), HUDOverlay = new HUDOverlay(ScoreProcessor, DrawableRuleset, Mods.Value) { HoldToQuit = From 2bd074883a834305efbf5a89610e2c442a08dc64 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 12:36:08 +0900 Subject: [PATCH 2505/2854] Fix OsuLegacySkin fallback logic being incorrect Fixes skin fonts not being applied. This is a temporary fix as configuration retrieval from skins will see a complete rewrite over the coming days. --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs index 904064e2f0..ea7257d258 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs @@ -115,7 +115,13 @@ namespace osu.Game.Rulesets.Osu.Skinning public SampleChannel GetSample(ISampleInfo sample) => source.GetSample(sample); public TValue GetValue(Func query) where TConfiguration : SkinConfiguration - => configuration.Value is TConfiguration conf ? query.Invoke(conf) : source.GetValue(query); + { + TValue val; + if (configuration.Value is TConfiguration conf && (val = query.Invoke(conf)) != null) + return val; + + return source.GetValue(query); + } private bool hasFont(string fontName) => source.GetTexture($"{fontName}-0") != null; } From cad68bb82fce1bfdaed3ca85b092b39f484cb222 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 12:54:59 +0900 Subject: [PATCH 2506/2854] Add autoplay helper property --- osu.Game/Tests/Visual/PlayerTestScene.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index 1ab20ecd48..599cb060b1 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -3,6 +3,7 @@ using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Testing; using osu.Game.Configuration; using osu.Game.Rulesets; @@ -40,6 +41,8 @@ namespace osu.Game.Tests.Visual protected virtual bool AllowFail => false; + protected virtual bool Autoplay => false; + private void loadPlayer() { var beatmap = CreateBeatmap(ruleset.RulesetInfo); @@ -53,6 +56,16 @@ namespace osu.Game.Tests.Visual LoadScreen(Player); } - protected virtual Player CreatePlayer(Ruleset ruleset) => new TestPlayer(false, false); + protected virtual Player CreatePlayer(Ruleset ruleset) + { + if (Autoplay) + { + var mod = ruleset.GetAutoplayMod(); + if (mod != null) + Mods.Value = Mods.Value.Concat(mod.Yield()).ToArray(); + } + + return new TestPlayer(false, false); + } } } From 2945fef62d5afbd53ad3022bd9304d34d108fe0d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 13:04:30 +0900 Subject: [PATCH 2507/2854] Expose HasCompleted from ScoreProcessor --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index e47df6b473..3b7e457990 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Scoring /// /// Whether all s have been processed. /// - protected virtual bool HasCompleted => false; + public virtual bool HasCompleted => false; /// /// Whether this ScoreProcessor has already triggered the failed state. @@ -205,7 +205,7 @@ namespace osu.Game.Rulesets.Scoring private const double combo_portion = 0.7; private const double max_score = 1000000; - protected sealed override bool HasCompleted => JudgedHits == MaxHits; + public sealed override bool HasCompleted => JudgedHits == MaxHits; protected int MaxHits { get; private set; } protected int JudgedHits { get; private set; } From fc668d8a74898586c8a774fd1bd50f36032c7746 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 13:24:39 +0900 Subject: [PATCH 2508/2854] Move autoplay mod to a less overridable location --- osu.Game/Tests/Visual/PlayerTestScene.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index 599cb060b1..ccd996098c 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -52,12 +52,6 @@ namespace osu.Game.Tests.Visual if (!AllowFail) Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) }; - Player = CreatePlayer(ruleset); - LoadScreen(Player); - } - - protected virtual Player CreatePlayer(Ruleset ruleset) - { if (Autoplay) { var mod = ruleset.GetAutoplayMod(); @@ -65,7 +59,10 @@ namespace osu.Game.Tests.Visual Mods.Value = Mods.Value.Concat(mod.Yield()).ToArray(); } - return new TestPlayer(false, false); + Player = CreatePlayer(ruleset); + LoadScreen(Player); } + + protected virtual Player CreatePlayer(Ruleset ruleset) => new TestPlayer(false, false); } } From 5b685c4cd2929f759be82a973f3f187f1642d50f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 13:25:39 +0900 Subject: [PATCH 2509/2854] Fix swell ticks having non-zero time offsets --- .../TestSceneSwellJudgements.cs | 74 +++++++++++++++++++ .../Objects/Drawables/DrawableSwellTick.cs | 6 +- 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs new file mode 100644 index 0000000000..f27e329e8e --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs @@ -0,0 +1,74 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Judgements; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Screens.Play; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + public class TestSceneSwellJudgements : PlayerTestScene + { + protected new TestPlayer Player => (TestPlayer)base.Player; + + public TestSceneSwellJudgements() + : base(new TaikoRuleset()) + { + } + + [Test] + public void TestZeroTickTimeOffsets() + { + AddUntilStep("gameplay finished", () => Player.ScoreProcessor.HasCompleted); + AddAssert("all tick offsets are 0", () => Player.Results.Where(r => r.Judgement is TaikoSwellTickJudgement).All(r => r.TimeOffset == 0)); + } + + protected override bool Autoplay => true; + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) + { + var beatmap = new Beatmap + { + BeatmapInfo = { Ruleset = new TaikoRuleset().RulesetInfo }, + HitObjects = + { + new Swell + { + StartTime = 1000, + Duration = 1000, + } + } + }; + + return beatmap; + } + + protected override Player CreatePlayer(Ruleset ruleset) => new TestPlayer(); + + protected class TestPlayer : Player + { + public readonly List Results = new List(); + + public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; + + public TestPlayer() + : base(false, false) + { + } + + [BackgroundDependencyLoader] + private void load() + { + ScoreProcessor.NewJudgement += r => Results.Add(r); + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs index 8b27d78101..4833d420f7 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs @@ -14,7 +14,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { } - public void TriggerResult(HitResult type) => ApplyResult(r => r.Type = type); + public void TriggerResult(HitResult type) + { + HitObject.StartTime = Time.Current; + ApplyResult(r => r.Type = type); + } protected override void CheckForResult(bool userTriggered, double timeOffset) { From 1df422e59135964b243ac168ba15bf8fd843a722 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 13:30:08 +0900 Subject: [PATCH 2510/2854] Hide taiko swell ticks --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs index 8b27d78101..c2e8c08e7e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.Objects.Drawables @@ -14,6 +15,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { } + protected override void UpdateInitialTransforms() => this.FadeOut(); + public void TriggerResult(HitResult type) => ApplyResult(r => r.Type = type); protected override void CheckForResult(bool userTriggered, double timeOffset) From 6603cbd74d2a5d4d0450af0953ea39452844ec89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 13:41:14 +0900 Subject: [PATCH 2511/2854] No language doesn't mean "Other" --- osu.Game/Overlays/BeatmapSet/Info.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index 72db03a5a6..df077a65a9 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -133,7 +133,7 @@ namespace osu.Game.Overlays.BeatmapSet source.Text = b.NewValue?.Metadata.Source ?? string.Empty; tags.Text = b.NewValue?.Metadata.Tags ?? string.Empty; genre.Text = b.NewValue?.OnlineInfo?.Genre?.Name ?? "Unspecified"; - language.Text = b.NewValue?.OnlineInfo?.Language?.Name ?? "Other"; + language.Text = b.NewValue?.OnlineInfo?.Language?.Name ?? "Unspecified"; }; } From d4c12881f5fd4417d6d108da0719001619fd0893 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 13:45:13 +0900 Subject: [PATCH 2512/2854] Remove unnecessary over-complication and fix transitions --- osu.Game/Overlays/BeatmapSet/Info.cs | 30 ++++++---------------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index df077a65a9..16d6236051 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -83,24 +83,12 @@ namespace osu.Game.Overlays.BeatmapSet { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - LayoutDuration = transition_duration, + Direction = FillDirection.Full, Children = new[] { source = new MetadataSection("Source"), - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Width = 0.5f, - FillMode = FillMode.Fit, - Children = new Drawable[] - { - genre = new MetadataSection("Genre"), - language = new MetadataSection("Language"), - } - }, + genre = new MetadataSection("Genre") { Width = 0.5f }, + language = new MetadataSection("Language") { Width = 0.5f }, tags = new MetadataSection("Tags"), }, }, @@ -132,8 +120,8 @@ namespace osu.Game.Overlays.BeatmapSet { source.Text = b.NewValue?.Metadata.Source ?? string.Empty; tags.Text = b.NewValue?.Metadata.Tags ?? string.Empty; - genre.Text = b.NewValue?.OnlineInfo?.Genre?.Name ?? "Unspecified"; - language.Text = b.NewValue?.OnlineInfo?.Language?.Name ?? "Unspecified"; + genre.Text = b.NewValue?.OnlineInfo?.Genre?.Name ?? string.Empty; + language.Text = b.NewValue?.OnlineInfo?.Language?.Name ?? string.Empty; }; } @@ -154,7 +142,7 @@ namespace osu.Game.Overlays.BeatmapSet { if (string.IsNullOrEmpty(value)) { - this.FadeOut(transition_duration); + Hide(); return; } @@ -164,12 +152,6 @@ namespace osu.Game.Overlays.BeatmapSet } } - public Color4 TextColour - { - get => textFlow.Colour; - set => textFlow.Colour = value; - } - public MetadataSection(string title) { RelativeSizeAxes = Axes.X; From 0f04357f1f0759c0560ed555f6e1071c1d4caef0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 14:41:14 +0900 Subject: [PATCH 2513/2854] Revert short name change for now --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index cd2f8d56af..5428b4eeb8 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Catch public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap); - public const string SHORT_NAME = "catch"; + public const string SHORT_NAME = "fruits"; public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] { From 8f8d35bd15f27f5613bfb9506c96266288081dee Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 15:02:16 +0900 Subject: [PATCH 2514/2854] Delay initial hitobject state computation --- .../Objects/Drawables/DrawableHitObject.cs | 9 ++++++ .../Scrolling/ScrollingHitObjectContainer.cs | 29 +++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 4a6f261905..a24476418c 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -9,6 +9,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; +using osu.Framework.Threading; using osu.Game.Audio; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Types; @@ -278,6 +279,14 @@ namespace osu.Game.Rulesets.Objects.Drawables UpdateResult(false); } + /// + /// Schedules an to this . + /// + /// + /// Only provided temporarily until hitobject pooling is implemented. + /// + protected internal new ScheduledDelegate Schedule(Action action) => base.Schedule(action); + private double? lifetimeStart; public override double LifetimeStart diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 1df8c8218f..107d55ff0d 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Caching; @@ -86,13 +87,34 @@ namespace osu.Game.Rulesets.UI.Scrolling scrollingInfo.Algorithm.Reset(); foreach (var obj in Objects) + { + computeLifetimeStartRecursive(obj); computeInitialStateRecursive(obj); + } + initialStateCache.Validate(); } } - private void computeInitialStateRecursive(DrawableHitObject hitObject) + private void computeLifetimeStartRecursive(DrawableHitObject hitObject) { + hitObject.LifetimeStart = scrollingInfo.Algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, timeRange.Value); + + foreach (var obj in hitObject.NestedHitObjects) + computeLifetimeStartRecursive(obj); + } + + private readonly Dictionary hitObjectInitialStateCache = new Dictionary(); + + // Cant use AddOnce() since the delegate is re-constructed every invocation + private void computeInitialStateRecursive(DrawableHitObject hitObject) => hitObject.Schedule(() => + { + if (!hitObjectInitialStateCache.TryGetValue(hitObject, out var cached)) + cached = hitObjectInitialStateCache[hitObject] = new Cached(); + + if (cached.IsValid) + return; + double endTime = hitObject.HitObject.StartTime; if (hitObject.HitObject is IHasEndTime e) @@ -113,7 +135,6 @@ namespace osu.Game.Rulesets.UI.Scrolling } } - hitObject.LifetimeStart = scrollingInfo.Algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, timeRange.Value); hitObject.LifetimeEnd = scrollingInfo.Algorithm.TimeAt(scrollLength * safe_lifetime_end_multiplier, endTime, timeRange.Value, scrollLength); foreach (var obj in hitObject.NestedHitObjects) @@ -123,7 +144,9 @@ namespace osu.Game.Rulesets.UI.Scrolling // Nested hitobjects don't need to scroll, but they do need accurate positions updatePosition(obj, hitObject.HitObject.StartTime); } - } + + cached.Validate(); + }); protected override void UpdateAfterChildrenLife() { From d74e1b9b6473613784be40adcf3202ae3f3908f1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 15:06:43 +0900 Subject: [PATCH 2515/2854] Remove from dictionary on Remove() --- .../Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 107d55ff0d..bd1f496dfa 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -51,8 +51,13 @@ namespace osu.Game.Rulesets.UI.Scrolling public override bool Remove(DrawableHitObject hitObject) { var result = base.Remove(hitObject); + if (result) + { initialStateCache.Invalidate(); + hitObjectInitialStateCache.Remove(hitObject); + } + return result; } From c06908adf7c653f98fddda7d08f55e50a290b642 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 15:37:38 +0900 Subject: [PATCH 2516/2854] Fix spacing specifications --- .../Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index 408468fa73..9b81e8c573 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, - Spacing = new Vector2(3), + Spacing = new Vector2(0, 3), }, date = new DrawableDate(historyItem.CreatedAt) { @@ -59,14 +59,15 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu if (!string.IsNullOrEmpty(prefix)) { linkFlowContainer.AddText(prefix); - linkFlowContainer.AddText($@"{Math.Abs(historyItem.Amount)} kudosu", t => + linkFlowContainer.AddText($@" {Math.Abs(historyItem.Amount)} kudosu ", t => { t.Font = t.Font.With(italics: true); t.Colour = colours.Blue; }); } - linkFlowContainer.AddLinks(formattedSource.Text + " ", formattedSource.Links); + linkFlowContainer.AddLinks(formattedSource.Text, formattedSource.Links); + linkFlowContainer.AddText(" "); linkFlowContainer.AddLink(historyItem.Post.Title, historyItem.Post.Url); } From da4507037333adcda31a9512f101b667cddb77ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 15:44:21 +0900 Subject: [PATCH 2517/2854] Group common prefixes together --- .../Kudosu/DrawableKudosuHistoryItem.cs | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index 9b81e8c573..ff64ea5648 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -120,29 +120,17 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu switch (historyItem.Action) { case KudosuAction.VoteGive: - return @"Received"; - case KudosuAction.Give: + case KudosuAction.AllowKudosuGive: + case KudosuAction.RestoreGive: + case KudosuAction.RecalculateGive: return @"Received"; - case KudosuAction.VoteReset: - return @"Lost"; - case KudosuAction.DenyKudosuReset: return @"Denied"; - case KudosuAction.AllowKudosuGive: - return @"Received"; - case KudosuAction.DeleteReset: - return @"Lost"; - - case KudosuAction.RestoreGive: - return @"Received"; - - case KudosuAction.RecalculateGive: - return @"Received"; - + case KudosuAction.VoteReset: case KudosuAction.RecalculateReset: return @"Lost"; From 3d551b08a96a1d9be3130a9b5bf27613fced44bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 15:57:23 +0900 Subject: [PATCH 2518/2854] Rename legacy actions --- .../Visual/Online/TestSceneKudosuHistory.cs | 6 +++--- .../API/Requests/GetUserKudosuHistoryRequest.cs | 16 ++++++++-------- .../Sections/Kudosu/DrawableKudosuHistoryItem.cs | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs b/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs index 8badfeaa23..a4f3bf65e6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs @@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual.Online { Amount = 5, CreatedAt = new DateTimeOffset(new DateTime(2012, 10, 11)), - Action = KudosuAction.Give, + Action = KudosuAction.ForumGive, Post = new APIKudosuHistory.ModdingPost { Title = @"Random post 2", @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.Online { Amount = 8, CreatedAt = new DateTimeOffset(new DateTime(2013, 9, 11)), - Action = KudosuAction.Reset, + Action = KudosuAction.ForumReset, Post = new APIKudosuHistory.ModdingPost { Title = @"Random post 3", @@ -107,7 +107,7 @@ namespace osu.Game.Tests.Visual.Online { Amount = 7, CreatedAt = new DateTimeOffset(new DateTime(2014, 8, 11)), - Action = KudosuAction.Revoke, + Action = KudosuAction.ForumRevoke, Post = new APIKudosuHistory.ModdingPost { Title = @"Random post 4", diff --git a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs index dd6f2ccf22..32aa0c15fa 100644 --- a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs @@ -21,16 +21,16 @@ namespace osu.Game.Online.API.Requests public enum KudosuAction { - Give, - VoteGive, - Reset, - VoteReset, - DenyKudosuReset, - Revoke, AllowKudosuGive, DeleteReset, - RestoreGive, + DenyKudosuReset, + ForumGive, + ForumReset, + ForumRevoke, RecalculateGive, - RecalculateReset + RecalculateReset, + RestoreGive, + VoteGive, + VoteReset, } } diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index ff64ea5648..5229269def 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -80,10 +80,10 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu case KudosuAction.VoteGive: return @"from obtaining votes in modding post of"; - case KudosuAction.Give: + case KudosuAction.ForumGive: return $@"from {userLink()} for a post at"; - case KudosuAction.Reset: + case KudosuAction.ForumReset: return $@"Kudosu reset by {userLink()} for the post"; case KudosuAction.VoteReset: @@ -92,7 +92,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu case KudosuAction.DenyKudosuReset: return @"from modding post"; - case KudosuAction.Revoke: + case KudosuAction.ForumRevoke: return $@"Denied kudosu by {userLink()} for the post"; case KudosuAction.AllowKudosuGive: @@ -120,7 +120,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu switch (historyItem.Action) { case KudosuAction.VoteGive: - case KudosuAction.Give: + case KudosuAction.ForumGive: case KudosuAction.AllowKudosuGive: case KudosuAction.RestoreGive: case KudosuAction.RecalculateGive: From 0985b1679f86de6a4a96d34edb0e84cb30a55dac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 15:57:55 +0900 Subject: [PATCH 2519/2854] Move enum to response class --- .../Visual/Online/TestSceneKudosuHistory.cs | 1 - .../API/Requests/GetUserKudosuHistoryRequest.cs | 15 --------------- .../API/Requests/Responses/APIKudosuHistory.cs | 15 +++++++++++++++ .../Sections/Kudosu/DrawableKudosuHistoryItem.cs | 1 - 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs b/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs index a4f3bf65e6..84152e40fa 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs @@ -10,7 +10,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Online.API.Requests; using osu.Framework.Extensions.IEnumerableExtensions; namespace osu.Game.Tests.Visual.Online diff --git a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs index 32aa0c15fa..e90e297672 100644 --- a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs @@ -18,19 +18,4 @@ namespace osu.Game.Online.API.Requests protected override string Target => $"users/{userId}/kudosu"; } - - public enum KudosuAction - { - AllowKudosuGive, - DeleteReset, - DenyKudosuReset, - ForumGive, - ForumReset, - ForumRevoke, - RecalculateGive, - RecalculateReset, - RestoreGive, - VoteGive, - VoteReset, - } } diff --git a/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs index f2297f7a10..25b11a6cf9 100644 --- a/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs +++ b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs @@ -53,4 +53,19 @@ namespace osu.Game.Online.API.Requests.Responses public KudosuAction Action; } + + public enum KudosuAction + { + AllowKudosuGive, + DeleteReset, + DenyKudosuReset, + ForumGive, + ForumReset, + ForumRevoke, + RecalculateGive, + RecalculateReset, + RestoreGive, + VoteGive, + VoteReset, + } } diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index 5229269def..6efe9825ec 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Online.API.Requests; using osu.Game.Online.Chat; using System; using osuTK; From 0a89603e79871ffa10c7858759ce231a6113f2b5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 16:07:16 +0900 Subject: [PATCH 2520/2854] Fix hit error potentially not displaying with null hitwindows --- osu.Game/Screens/Play/HUDOverlay.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 8e642ea552..eac45f9214 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -259,7 +259,9 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20, Right = 10 }, }; - protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset.Objects.FirstOrDefault()?.HitWindows); + protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay( + scoreProcessor, + drawableRuleset.Objects.Concat(drawableRuleset.Objects.SelectMany(h => h.NestedHitObjects)).FirstOrDefault(h => h.HitWindows != null)?.HitWindows); protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); From f3656475de102de8379409868d63aecc321ebe9f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 16:10:30 +0900 Subject: [PATCH 2521/2854] Return null hitwindows for non-time-based objects --- osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 3 +++ osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs | 3 +++ osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 ++ osu.Game.Rulesets.Osu/Objects/SliderTick.cs | 3 +++ osu.Game.Rulesets.Osu/Objects/Spinner.cs | 3 +++ osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 3 +++ osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs | 3 +++ osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs | 3 +++ osu.Game.Rulesets.Taiko/Objects/Swell.cs | 3 +++ osu.Game.Rulesets.Taiko/Objects/SwellTick.cs | 3 +++ 10 files changed, 29 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index 5e9f46d9c7..d28d04b3c1 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -5,6 +5,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Mania.Objects @@ -99,5 +100,7 @@ namespace osu.Game.Rulesets.Mania.Objects } public override Judgement CreateJudgement() => new HoldNoteJudgement(); + + protected override HitWindows CreateHitWindows() => null; } } diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs index c133ee73b1..6bb21633b6 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs @@ -3,6 +3,7 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Judgements; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mania.Objects { @@ -12,5 +13,7 @@ namespace osu.Game.Rulesets.Mania.Objects public class HoldNoteTick : ManiaHitObject { public override Judgement CreateJudgement() => new HoldNoteTickJudgement(); + + protected override HitWindows CreateHitWindows() => null; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index d3279652c7..93231844bb 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -229,5 +229,7 @@ namespace osu.Game.Rulesets.Osu.Objects nodeIndex < NodeSamples.Count ? NodeSamples[nodeIndex] : Samples; public override Judgement CreateJudgement() => new OsuJudgement(); + + protected override HitWindows CreateHitWindows() => null; } } diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs index 85439699dd..60e9084ed3 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs @@ -4,6 +4,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; namespace osu.Game.Rulesets.Osu.Objects @@ -30,5 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects } public override Judgement CreateJudgement() => new OsuJudgement(); + + protected override HitWindows CreateHitWindows() => null; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index 8a2fd3b7aa..69c779a182 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -6,6 +6,7 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; namespace osu.Game.Rulesets.Osu.Objects @@ -31,5 +32,7 @@ namespace osu.Game.Rulesets.Osu.Objects } public override Judgement CreateJudgement() => new OsuJudgement(); + + protected override HitWindows CreateHitWindows() => null; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 1d25735fe3..3ed52f21f0 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -6,6 +6,7 @@ using System; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects @@ -86,5 +87,7 @@ namespace osu.Game.Rulesets.Taiko.Objects } public override Judgement CreateJudgement() => new TaikoDrumRollJudgement(); + + protected override HitWindows CreateHitWindows() => null; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs index 8448036f76..39e2b45e24 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects @@ -25,5 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Objects public double HitWindow => TickSpacing / 2; public override Judgement CreateJudgement() => new TaikoDrumRollTickJudgement(); + + protected override HitWindows CreateHitWindows() => null; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs index 2a03c23934..830e640242 100644 --- a/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects @@ -9,5 +10,7 @@ namespace osu.Game.Rulesets.Taiko.Objects public class StrongHitObject : TaikoHitObject { public override Judgement CreateJudgement() => new TaikoStrongJudgement(); + + protected override HitWindows CreateHitWindows() => null; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs index befa728570..e7812841bf 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs @@ -4,6 +4,7 @@ using System; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects @@ -33,5 +34,7 @@ namespace osu.Game.Rulesets.Taiko.Objects } public override Judgement CreateJudgement() => new TaikoSwellJudgement(); + + protected override HitWindows CreateHitWindows() => null; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs index c2ae784b2a..049fa7de5f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects @@ -9,5 +10,7 @@ namespace osu.Game.Rulesets.Taiko.Objects public class SwellTick : TaikoHitObject { public override Judgement CreateJudgement() => new TaikoSwellTickJudgement(); + + protected override HitWindows CreateHitWindows() => null; } } From f2bdf94a1dda65e33a45ce3007b03c4d95f649ed Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 16:28:14 +0900 Subject: [PATCH 2522/2854] Add HitWindows to JudgementResult to indicate timing errors --- .../TestSceneDrawableJudgement.cs | 3 ++- .../Judgements/OsuJudgementResult.cs | 5 +++-- .../Objects/Drawables/DrawableOsuHitObject.cs | 3 ++- osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs | 2 +- .../TestSceneTaikoPlayfield.cs | 8 ++++---- .../Visual/Gameplay/TestSceneBarHitErrorMeter.cs | 2 +- osu.Game/Rulesets/Judgements/JudgementResult.cs | 14 +++++++++++++- .../Objects/Drawables/DrawableHitObject.cs | 6 ++++-- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 3 +++ 10 files changed, 34 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs index 82a8d0e5e6..6d240ee009 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Scoring; @@ -24,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Tests { foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType().Skip(1)) AddStep("Show " + result.GetDescription(), () => SetContents(() => - new DrawableOsuJudgement(new JudgementResult(null) { Type = result }, null) + new DrawableOsuJudgement(new JudgementResult(null, new HitWindows()) { Type = result }, null) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.cs b/osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.cs index c7661bddb1..367c2c8f14 100644 --- a/osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.cs +++ b/osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Osu.Judgements { @@ -9,8 +10,8 @@ namespace osu.Game.Rulesets.Osu.Judgements { public ComboResult ComboType; - public OsuJudgementResult(Judgement judgement) - : base(judgement) + public OsuJudgementResult(Judgement judgement, HitWindows hitWindows) + : base(judgement, hitWindows) { } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index b4f5642f45..02b6a932d3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -41,6 +42,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength); - protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement); + protected override JudgementResult CreateResult(Judgement judgement, HitWindows hitWindows) => new OsuJudgementResult(judgement, hitWindows); } } diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index cf0565c6da..08dc355bcf 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Osu.Scoring } } - protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement); + protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement, null); public override HitWindows CreateHitWindows() => new OsuHitWindows(); } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs index 3c84d900a6..2db1e3e70a 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs @@ -144,7 +144,7 @@ namespace osu.Game.Rulesets.Taiko.Tests var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) }; - ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new TaikoJudgement()) { Type = hitResult }); + ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new TaikoJudgement(), new TaikoHitWindows()) { Type = hitResult }); } private void addStrongHitJudgement(bool kiai) @@ -159,13 +159,13 @@ namespace osu.Game.Rulesets.Taiko.Tests var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) }; - ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new TaikoJudgement()) { Type = hitResult }); - ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new TaikoStrongJudgement()) { Type = HitResult.Great }); + ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new TaikoJudgement(), new TaikoHitWindows()) { Type = hitResult }); + ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new TaikoStrongJudgement(), new TaikoHitWindows()) { Type = HitResult.Great }); } private void addMissJudgement() { - ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new DrawableTestHit(new Hit()), new JudgementResult(new TaikoJudgement()) { Type = HitResult.Miss }); + ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new DrawableTestHit(new Hit()), new JudgementResult(new TaikoJudgement(), new TaikoHitWindows()) { Type = HitResult.Miss }); } private void addBarLine(bool major, double delay = scroll_time) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs index f20440249b..0376e775bc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs @@ -106,7 +106,7 @@ namespace osu.Game.Tests.Visual.Gameplay private void newJudgement(double offset = 0) { - var judgement = new JudgementResult(new Judgement()) + var judgement = new JudgementResult(new Judgement(), hitWindows) { TimeOffset = offset == 0 ? RNG.Next(-150, 150) : offset, Type = HitResult.Perfect, diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs index 195fe316ac..aea3c9085b 100644 --- a/osu.Game/Rulesets/Judgements/JudgementResult.cs +++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using JetBrains.Annotations; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; @@ -21,6 +23,13 @@ namespace osu.Game.Rulesets.Judgements /// public readonly Judgement Judgement; + /// + /// The which the was judged against. + /// May be null to indicate that the timing error should not be displayed to the user. + /// + [CanBeNull] + public readonly HitWindows HitWindows; + /// /// The offset from a perfect hit at which this occurred. /// Populated when this is applied via . @@ -56,9 +65,12 @@ namespace osu.Game.Rulesets.Judgements /// Creates a new . /// /// The to refer to for scoring information. - public JudgementResult(Judgement judgement) + /// The which the was judged against. + /// May be null to indicate that the timing error should not be displayed to the user. + public JudgementResult([NotNull] Judgement judgement, [CanBeNull] HitWindows hitWindows) { Judgement = judgement; + HitWindows = hitWindows; } public override string ToString() => $"{Type} (Score:{Judgement.NumericResultFor(this)} HP:{Judgement.HealthIncreaseFor(this)} {Judgement})"; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 4a6f261905..4106f8320f 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Objects.Drawables if (judgement != null) { - Result = CreateResult(judgement); + Result = CreateResult(judgement, HitObject.HitWindows); if (Result == null) throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}."); } @@ -401,7 +401,9 @@ namespace osu.Game.Rulesets.Objects.Drawables /// Creates the that represents the scoring result for this . /// /// The that provides the scoring information. - protected virtual JudgementResult CreateResult(Judgement judgement) => new JudgementResult(judgement); + /// The which the was judged against. + /// May be null to indicate that the timing error should not be displayed to the user. + protected virtual JudgementResult CreateResult(Judgement judgement, HitWindows hitWindows) => new JudgementResult(judgement, hitWindows); } public abstract class DrawableHitObject : DrawableHitObject diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 3b7e457990..3230551386 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -442,7 +442,7 @@ namespace osu.Game.Rulesets.Scoring /// Creates the that represents the scoring result for a . /// /// The that provides the scoring information. - protected virtual JudgementResult CreateResult(Judgement judgement) => new JudgementResult(judgement); + protected virtual JudgementResult CreateResult(Judgement judgement) => new JudgementResult(judgement, null); } public enum ScoringMode diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index cdfa0e993b..3e925a5a50 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -49,6 +49,9 @@ namespace osu.Game.Screens.Play.HUD private void onNewJudgement(JudgementResult result) { + if (result.HitWindows == null) + return; + foreach (var c in Children) c.OnNewJudgement(result); } From d21d68b36c4271d611db888c1917e09ee93b95ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 16:34:11 +0900 Subject: [PATCH 2523/2854] Refactor to match web implementation 1:1 --- .../Visual/Online/TestSceneKudosuHistory.cs | 33 +++-- .../Requests/Responses/APIKudosuHistory.cs | 44 ++++-- .../Kudosu/DrawableKudosuHistoryItem.cs | 138 +++++++++--------- 3 files changed, 122 insertions(+), 93 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs b/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs index 84152e40fa..325d657f0e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs @@ -58,7 +58,8 @@ namespace osu.Game.Tests.Visual.Online { Amount = 10, CreatedAt = new DateTimeOffset(new DateTime(2011, 11, 11)), - Action = KudosuAction.DenyKudosuReset, + Source = KudosuSource.DenyKudosu, + Action = KudosuAction.Reset, Post = new APIKudosuHistory.ModdingPost { Title = @"Random post 1", @@ -74,7 +75,8 @@ namespace osu.Game.Tests.Visual.Online { Amount = 5, CreatedAt = new DateTimeOffset(new DateTime(2012, 10, 11)), - Action = KudosuAction.ForumGive, + Source = KudosuSource.Forum, + Action = KudosuAction.Give, Post = new APIKudosuHistory.ModdingPost { Title = @"Random post 2", @@ -90,7 +92,8 @@ namespace osu.Game.Tests.Visual.Online { Amount = 8, CreatedAt = new DateTimeOffset(new DateTime(2013, 9, 11)), - Action = KudosuAction.ForumReset, + Source = KudosuSource.Forum, + Action = KudosuAction.Reset, Post = new APIKudosuHistory.ModdingPost { Title = @"Random post 3", @@ -106,7 +109,8 @@ namespace osu.Game.Tests.Visual.Online { Amount = 7, CreatedAt = new DateTimeOffset(new DateTime(2014, 8, 11)), - Action = KudosuAction.ForumRevoke, + Source = KudosuSource.Forum, + Action = KudosuAction.Revoke, Post = new APIKudosuHistory.ModdingPost { Title = @"Random post 4", @@ -122,7 +126,8 @@ namespace osu.Game.Tests.Visual.Online { Amount = 100, CreatedAt = new DateTimeOffset(new DateTime(2015, 7, 11)), - Action = KudosuAction.VoteGive, + Source = KudosuSource.Vote, + Action = KudosuAction.Give, Post = new APIKudosuHistory.ModdingPost { Title = @"Random post 5", @@ -138,7 +143,8 @@ namespace osu.Game.Tests.Visual.Online { Amount = 20, CreatedAt = new DateTimeOffset(new DateTime(2016, 6, 11)), - Action = KudosuAction.VoteReset, + Source = KudosuSource.Vote, + Action = KudosuAction.Reset, Post = new APIKudosuHistory.ModdingPost { Title = @"Random post 6", @@ -154,7 +160,8 @@ namespace osu.Game.Tests.Visual.Online { Amount = 11, CreatedAt = new DateTimeOffset(new DateTime(2016, 6, 11)), - Action = KudosuAction.AllowKudosuGive, + Source = KudosuSource.AllowKudosu, + Action = KudosuAction.Give, Post = new APIKudosuHistory.ModdingPost { Title = @"Random post 7", @@ -170,7 +177,8 @@ namespace osu.Game.Tests.Visual.Online { Amount = 24, CreatedAt = new DateTimeOffset(new DateTime(2014, 6, 11)), - Action = KudosuAction.DeleteReset, + Source = KudosuSource.Delete, + Action = KudosuAction.Reset, Post = new APIKudosuHistory.ModdingPost { Title = @"Random post 8", @@ -186,7 +194,8 @@ namespace osu.Game.Tests.Visual.Online { Amount = 12, CreatedAt = new DateTimeOffset(new DateTime(2016, 6, 11)), - Action = KudosuAction.RestoreGive, + Source = KudosuSource.Restore, + Action = KudosuAction.Give, Post = new APIKudosuHistory.ModdingPost { Title = @"Random post 9", @@ -202,7 +211,8 @@ namespace osu.Game.Tests.Visual.Online { Amount = 2, CreatedAt = new DateTimeOffset(new DateTime(2012, 6, 11)), - Action = KudosuAction.RecalculateGive, + Source = KudosuSource.Recalculate, + Action = KudosuAction.Give, Post = new APIKudosuHistory.ModdingPost { Title = @"Random post 10", @@ -218,7 +228,8 @@ namespace osu.Game.Tests.Visual.Online { Amount = 32, CreatedAt = new DateTimeOffset(new DateTime(2019, 8, 11)), - Action = KudosuAction.RecalculateReset, + Source = KudosuSource.Recalculate, + Action = KudosuAction.Reset, Post = new APIKudosuHistory.ModdingPost { Title = @"Random post 11", diff --git a/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs index 25b11a6cf9..d596ddc560 100644 --- a/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs +++ b/osu.Game/Online/API/Requests/Responses/APIKudosuHistory.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using Humanizer; +using System.Linq; using Newtonsoft.Json; namespace osu.Game.Online.API.Requests.Responses @@ -39,33 +39,45 @@ namespace osu.Game.Online.API.Requests.Responses public string Username; } + public KudosuSource Source; + + public KudosuAction Action; + [JsonProperty("action")] private string action { set { - //We will receive something like "event.action" or just "action" - string parsed = value.Contains(".") ? value.Split('.')[0].Pascalize() + value.Split('.')[1].Pascalize() : value.Pascalize(); + // incoming action may contain a prefix. if it doesn't, it's a legacy forum event. - Action = (KudosuAction)Enum.Parse(typeof(KudosuAction), parsed); + string[] split = value.Split('.'); + + if (split.Length > 1) + Enum.TryParse(split.First().Replace("_", ""), true, out Source); + else + Source = KudosuSource.Forum; + + Enum.TryParse(split.Last(), true, out Action); } } + } - public KudosuAction Action; + public enum KudosuSource + { + Unknown, + AllowKudosu, + Delete, + DenyKudosu, + Forum, + Recalculate, + Restore, + Vote } public enum KudosuAction { - AllowKudosuGive, - DeleteReset, - DenyKudosuReset, - ForumGive, - ForumReset, - ForumRevoke, - RecalculateGive, - RecalculateReset, - RestoreGive, - VoteGive, - VoteReset, + Give, + Reset, + Revoke, } } diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs index 6efe9825ec..d0cfe9fa54 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/DrawableKudosuHistoryItem.cs @@ -51,91 +51,97 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu private void load() { date.Colour = colours.GreySeafoamLighter; - - string prefix = getPrefix(historyItem); - var formattedSource = MessageFormatter.FormatText(getSource(historyItem)); - - if (!string.IsNullOrEmpty(prefix)) - { - linkFlowContainer.AddText(prefix); - linkFlowContainer.AddText($@" {Math.Abs(historyItem.Amount)} kudosu ", t => - { - t.Font = t.Font.With(italics: true); - t.Colour = colours.Blue; - }); - } - + var formattedSource = MessageFormatter.FormatText(getString(historyItem)); linkFlowContainer.AddLinks(formattedSource.Text, formattedSource.Links); - linkFlowContainer.AddText(" "); - linkFlowContainer.AddLink(historyItem.Post.Title, historyItem.Post.Url); } - private string getSource(APIKudosuHistory historyItem) + private string getString(APIKudosuHistory item) { - string userLink() => $"[{historyItem.Giver?.Url} {historyItem.Giver?.Username}]"; + string amount = $"{Math.Abs(item.Amount)} kudosu"; + string post = $"[{item.Post.Title}]({item.Post.Url})"; - switch (historyItem.Action) + switch (item.Source) { - case KudosuAction.VoteGive: - return @"from obtaining votes in modding post of"; + case KudosuSource.AllowKudosu: + switch (item.Action) + { + case KudosuAction.Give: + return $"Received {amount} from kudosu deny repeal of modding post {post}"; + } - case KudosuAction.ForumGive: - return $@"from {userLink()} for a post at"; + break; - case KudosuAction.ForumReset: - return $@"Kudosu reset by {userLink()} for the post"; + case KudosuSource.DenyKudosu: + switch (item.Action) + { + case KudosuAction.Reset: + return $"Denied {amount} from modding post {post}"; + } - case KudosuAction.VoteReset: - return @"from losing votes in modding post of"; + break; - case KudosuAction.DenyKudosuReset: - return @"from modding post"; + case KudosuSource.Delete: + switch (item.Action) + { + case KudosuAction.Reset: + return $"Lost {amount} from modding post deletion of {post}"; + } - case KudosuAction.ForumRevoke: - return $@"Denied kudosu by {userLink()} for the post"; + break; - case KudosuAction.AllowKudosuGive: - return @"from kudosu deny repeal of modding post"; + case KudosuSource.Restore: + switch (item.Action) + { + case KudosuAction.Give: + return $"Received {amount} from modding post restoration of {post}"; + } - case KudosuAction.DeleteReset: - return @"from modding post deletion of"; + break; - case KudosuAction.RestoreGive: - return @"from modding post restoration of"; + case KudosuSource.Vote: + switch (item.Action) + { + case KudosuAction.Give: + return $"Received {amount} from obtaining votes in modding post of {post}"; - case KudosuAction.RecalculateGive: - return @"from votes recalculation in modding post of"; + case KudosuAction.Reset: + return $"Lost {amount} from losing votes in modding post of {post}"; + } - case KudosuAction.RecalculateReset: - return @"from votes recalculation in modding post of"; + break; - default: - return @"from unknown event"; + case KudosuSource.Recalculate: + switch (item.Action) + { + case KudosuAction.Give: + return $"Received {amount} from votes recalculation in modding post of {post}"; + + case KudosuAction.Reset: + return $"Lost {amount} from votes recalculation in modding post of {post}"; + } + + break; + + case KudosuSource.Forum: + + string giver = $"[{item.Giver?.Username}]({item.Giver?.Url})"; + + switch (historyItem.Action) + { + case KudosuAction.Give: + return $"Received {amount} from {giver} for a post at {post}"; + + case KudosuAction.Reset: + return $"Kudosu reset by {giver} for the post {post}"; + + case KudosuAction.Revoke: + return $"Denied kudosu by {giver} for the post {post}"; + } + + break; } - } - private string getPrefix(APIKudosuHistory historyItem) - { - switch (historyItem.Action) - { - case KudosuAction.VoteGive: - case KudosuAction.ForumGive: - case KudosuAction.AllowKudosuGive: - case KudosuAction.RestoreGive: - case KudosuAction.RecalculateGive: - return @"Received"; - - case KudosuAction.DenyKudosuReset: - return @"Denied"; - - case KudosuAction.DeleteReset: - case KudosuAction.VoteReset: - case KudosuAction.RecalculateReset: - return @"Lost"; - - default: - return null; - } + return $"Unknown event ({amount} change)"; } } } From 841da7d69147ce0c1490eae4c500cbd802d1805b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 17:12:32 +0900 Subject: [PATCH 2524/2854] Fix potential null reference on DirectPanel unbind --- osu.Game/Overlays/Direct/DirectPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 641423f21f..a9b6633864 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Direct private BeatmapSetOverlay beatmapSetOverlay; public PreviewTrack Preview => PlayButton.Preview; - public Bindable PreviewPlaying => PlayButton.Playing; + public Bindable PreviewPlaying => PlayButton?.Playing; protected abstract PlayButton PlayButton { get; } protected abstract Box PreviewBar { get; } From 0c73c5acf3b104ebfe5b13832946be6752c5de1c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 17:14:40 +0900 Subject: [PATCH 2525/2854] Expose full hitobject rather than hit windows --- .../TestSceneDrawableJudgement.cs | 2 +- .../Judgements/OsuJudgementResult.cs | 4 ++-- .../Objects/Drawables/DrawableOsuHitObject.cs | 3 +-- .../Scoring/OsuScoreProcessor.cs | 2 +- .../TestSceneTaikoPlayfield.cs | 8 ++++---- .../Gameplay/TestSceneBarHitErrorMeter.cs | 2 +- .../Rulesets/Judgements/JudgementResult.cs | 19 +++++++++---------- .../Objects/Drawables/DrawableHitObject.cs | 6 ++---- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 5 +++-- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 2 +- 10 files changed, 25 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs index 6d240ee009..433ec6bd25 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Tests { foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType().Skip(1)) AddStep("Show " + result.GetDescription(), () => SetContents(() => - new DrawableOsuJudgement(new JudgementResult(null, new HitWindows()) { Type = result }, null) + new DrawableOsuJudgement(new JudgementResult(new HitObject(), new Judgement()) { Type = result }, null) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.cs b/osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.cs index 367c2c8f14..15444b847b 100644 --- a/osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.cs +++ b/osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.cs @@ -10,8 +10,8 @@ namespace osu.Game.Rulesets.Osu.Judgements { public ComboResult ComboType; - public OsuJudgementResult(Judgement judgement, HitWindows hitWindows) - : base(judgement, hitWindows) + public OsuJudgementResult(HitObject hitObject, Judgement judgement) + : base(hitObject, judgement) { } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 02b6a932d3..fcd42314fc 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Graphics.Containers; -using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -42,6 +41,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength); - protected override JudgementResult CreateResult(Judgement judgement, HitWindows hitWindows) => new OsuJudgementResult(judgement, hitWindows); + protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement); } } diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index 08dc355bcf..66ef020d09 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Osu.Scoring } } - protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement, null); + protected override JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new OsuJudgementResult(hitObject, judgement); public override HitWindows CreateHitWindows() => new OsuHitWindows(); } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs index 2db1e3e70a..6fd16c213b 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs @@ -144,7 +144,7 @@ namespace osu.Game.Rulesets.Taiko.Tests var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) }; - ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new TaikoJudgement(), new TaikoHitWindows()) { Type = hitResult }); + ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult }); } private void addStrongHitJudgement(bool kiai) @@ -159,13 +159,13 @@ namespace osu.Game.Rulesets.Taiko.Tests var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) }; - ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new TaikoJudgement(), new TaikoHitWindows()) { Type = hitResult }); - ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new TaikoStrongJudgement(), new TaikoHitWindows()) { Type = HitResult.Great }); + ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult }); + ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new HitObject(), new TaikoStrongJudgement()) { Type = HitResult.Great }); } private void addMissJudgement() { - ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new DrawableTestHit(new Hit()), new JudgementResult(new TaikoJudgement(), new TaikoHitWindows()) { Type = HitResult.Miss }); + ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new DrawableTestHit(new Hit()), new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = HitResult.Miss }); } private void addBarLine(bool major, double delay = scroll_time) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs index 0376e775bc..e9c15dab9b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs @@ -106,7 +106,7 @@ namespace osu.Game.Tests.Visual.Gameplay private void newJudgement(double offset = 0) { - var judgement = new JudgementResult(new Judgement(), hitWindows) + var judgement = new JudgementResult(new HitObject(), new Judgement()) { TimeOffset = offset == 0 ? RNG.Next(-150, 150) : offset, Type = HitResult.Perfect, diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs index aea3c9085b..56dc121b17 100644 --- a/osu.Game/Rulesets/Judgements/JudgementResult.cs +++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs @@ -19,16 +19,16 @@ namespace osu.Game.Rulesets.Judgements public HitResult Type; /// - /// The which this applies for. + /// The which was judged. /// - public readonly Judgement Judgement; + [NotNull] + public readonly HitObject HitObject; /// - /// The which the was judged against. - /// May be null to indicate that the timing error should not be displayed to the user. + /// The which this applies for. /// - [CanBeNull] - public readonly HitWindows HitWindows; + [NotNull] + public readonly Judgement Judgement; /// /// The offset from a perfect hit at which this occurred. @@ -64,13 +64,12 @@ namespace osu.Game.Rulesets.Judgements /// /// Creates a new . /// + /// The which was judged. /// The to refer to for scoring information. - /// The which the was judged against. - /// May be null to indicate that the timing error should not be displayed to the user. - public JudgementResult([NotNull] Judgement judgement, [CanBeNull] HitWindows hitWindows) + public JudgementResult([NotNull] HitObject hitObject, [NotNull] Judgement judgement) { + HitObject = hitObject; Judgement = judgement; - HitWindows = hitWindows; } public override string ToString() => $"{Type} (Score:{Judgement.NumericResultFor(this)} HP:{Judgement.HealthIncreaseFor(this)} {Judgement})"; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 4106f8320f..4073bd53ab 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Objects.Drawables if (judgement != null) { - Result = CreateResult(judgement, HitObject.HitWindows); + Result = CreateResult(judgement); if (Result == null) throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}."); } @@ -401,9 +401,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// Creates the that represents the scoring result for this . /// /// The that provides the scoring information. - /// The which the was judged against. - /// May be null to indicate that the timing error should not be displayed to the user. - protected virtual JudgementResult CreateResult(Judgement judgement, HitWindows hitWindows) => new JudgementResult(judgement, hitWindows); + protected virtual JudgementResult CreateResult(Judgement judgement) => new JudgementResult(HitObject, judgement); } public abstract class DrawableHitObject : DrawableHitObject diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 3230551386..86c2c07f2a 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -275,7 +275,7 @@ namespace osu.Game.Rulesets.Scoring if (judgement == null) return; - var result = CreateResult(judgement); + var result = CreateResult(obj, judgement); if (result == null) throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}."); @@ -441,8 +441,9 @@ namespace osu.Game.Rulesets.Scoring /// /// Creates the that represents the scoring result for a . /// + /// The which was judged. /// The that provides the scoring information. - protected virtual JudgementResult CreateResult(Judgement judgement) => new JudgementResult(judgement, null); + protected virtual JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new JudgementResult(hitObject, judgement); } public enum ScoringMode diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index 3e925a5a50..adda94d629 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Play.HUD private void onNewJudgement(JudgementResult result) { - if (result.HitWindows == null) + if (result.HitObject.HitWindows == null) return; foreach (var c in Children) From f6102b4d920ca15ade9af87434ceac6e856d7f9c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 17:15:36 +0900 Subject: [PATCH 2526/2854] Adjust xmldoc --- osu.Game/Rulesets/Objects/HitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index bf04963b76..f0547550e0 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Objects /// /// Creates the for this . - /// This can be null to indicate that the has no . + /// This can be null to indicate that the has no and timing errors should not be displayed to the user. /// /// This will only be invoked if hasn't been set externally (e.g. from a . /// From 4c150839c0de41487da978017ec4d496e1663417 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 17:38:52 +0900 Subject: [PATCH 2527/2854] Fix potential diffcalc hitwindow nullref --- .../Difficulty/ManiaDifficultyCalculator.cs | 7 ++++++- .../Difficulty/OsuDifficultyCalculator.cs | 6 +++++- .../Difficulty/TaikoDifficultyCalculator.cs | 6 +++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 4a9c22d339..d945abdb04 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -11,7 +11,9 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Difficulty.Preprocessing; using osu.Game.Rulesets.Mania.Difficulty.Skills; using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mania.Difficulty { @@ -32,12 +34,15 @@ namespace osu.Game.Rulesets.Mania.Difficulty if (beatmap.HitObjects.Count == 0) return new ManiaDifficultyAttributes { Mods = mods, Skills = skills }; + HitWindows hitWindows = new ManiaHitWindows(); + hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); + return new ManiaDifficultyAttributes { StarRating = difficultyValue(skills) * star_scaling_factor, Mods = mods, // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future - GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate, + GreatHitWindow = (int)(hitWindows.Great / 2) / clockRate, Skills = skills }; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index c197933233..61e9f60cdd 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -9,6 +9,7 @@ using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Difficulty.Skills; using osu.Game.Rulesets.Osu.Mods; @@ -34,8 +35,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; + HitWindows hitWindows = new OsuHitWindows(); + hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); + // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future - double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate; + double hitWindowGreat = (int)(hitWindows.Great / 2) / clockRate; double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; int maxCombo = beatmap.HitObjects.Count; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index c8f3e18911..fc93bccb94 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -8,6 +8,7 @@ using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Difficulty.Skills; using osu.Game.Rulesets.Taiko.Mods; @@ -29,12 +30,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty if (beatmap.HitObjects.Count == 0) return new TaikoDifficultyAttributes { Mods = mods, Skills = skills }; + HitWindows hitWindows = new TaikoHitWindows(); + hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); + return new TaikoDifficultyAttributes { StarRating = skills.Single().DifficultyValue() * star_scaling_factor, Mods = mods, // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future - GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate, + GreatHitWindow = (int)(hitWindows.Great / 2) / clockRate, MaxCombo = beatmap.HitObjects.Count(h => h is Hit), Skills = skills }; From f20e07136a842f2b23bd52f9a45039757fbcb084 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 17:48:41 +0900 Subject: [PATCH 2528/2854] Add attribute to catch potential future nullrefs --- osu.Game/Rulesets/Objects/HitObject.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index f0547550e0..5e029139d9 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using JetBrains.Annotations; using Newtonsoft.Json; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -56,6 +57,7 @@ namespace osu.Game.Rulesets.Objects /// /// The hit windows for this . /// + [CanBeNull] public HitWindows HitWindows { get; set; } private readonly List nestedHitObjects = new List(); @@ -116,6 +118,7 @@ namespace osu.Game.Rulesets.Objects /// This will only be invoked if hasn't been set externally (e.g. from a . /// /// + [CanBeNull] protected virtual HitWindows CreateHitWindows() => new HitWindows(); } } From 9c53430a0f7ac80f7d9a4786a7b58c0000f4803a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 18:18:59 +0900 Subject: [PATCH 2529/2854] Only initialise when required --- osu.Game/Skinning/SkinnableSound.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index bf647baeec..c402df1bde 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -16,7 +16,7 @@ namespace osu.Game.Skinning { private readonly ISampleInfo[] hitSamples; - private readonly List<(AdjustableProperty, BindableDouble)> adjustments = new List<(AdjustableProperty, BindableDouble)>(); + private List<(AdjustableProperty, BindableDouble)> adjustments; private SampleChannel[] channels; @@ -56,13 +56,15 @@ namespace osu.Game.Skinning public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) { + if (adjustments == null) adjustments = new List<(AdjustableProperty, BindableDouble)>(); + adjustments.Add((type, adjustBindable)); channels?.ForEach(c => c.AddAdjustment(type, adjustBindable)); } public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) { - adjustments.Remove((type, adjustBindable)); + adjustments?.Remove((type, adjustBindable)); channels?.ForEach(c => c.RemoveAdjustment(type, adjustBindable)); } @@ -84,8 +86,9 @@ namespace osu.Game.Skinning ch.Looping = looping; ch.Volume.Value = s.Volume / 100.0; - foreach (var adjustment in adjustments) - ch.AddAdjustment(adjustment.Item1, adjustment.Item2); + if (adjustments != null) + foreach (var adjustment in adjustments) + ch.AddAdjustment(adjustment.Item1, adjustment.Item2); } return ch; From f08b523abf9b9296f241dd32b841da15549eb3bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 18:20:30 +0900 Subject: [PATCH 2530/2854] Name tuple items --- osu.Game/Skinning/SkinnableSound.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index c402df1bde..07ffbb8a51 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -16,7 +16,7 @@ namespace osu.Game.Skinning { private readonly ISampleInfo[] hitSamples; - private List<(AdjustableProperty, BindableDouble)> adjustments; + private List<(AdjustableProperty property, BindableDouble bindable)> adjustments; private SampleChannel[] channels; @@ -88,7 +88,7 @@ namespace osu.Game.Skinning if (adjustments != null) foreach (var adjustment in adjustments) - ch.AddAdjustment(adjustment.Item1, adjustment.Item2); + ch.AddAdjustment(adjustment.property, adjustment.bindable); } return ch; From 8302658186b7ff64f63a9c72190d0fa1603d14ad Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 18:31:33 +0900 Subject: [PATCH 2531/2854] Fix other potential nullref cases that rider missed --- .../Objects/Drawables/DrawableHoldNote.cs | 3 +++ .../Objects/Drawables/DrawableNote.cs | 3 +++ .../TestSceneShaking.cs | 7 +++++-- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 8 +++++++- .../Objects/Drawables/DrawableHitCircle.cs | 5 +++++ .../Replays/OsuAutoGenerator.cs | 18 +++++++++--------- .../Replays/OsuAutoGeneratorBase.cs | 10 ++++++++++ .../Objects/Drawables/DrawableHit.cs | 5 +++++ 8 files changed, 47 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index fc3b6885d7..c5c157608f 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; @@ -209,6 +210,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected override void CheckForResult(bool userTriggered, double timeOffset) { + Debug.Assert(HitObject.HitWindows != null); + // Factor in the release lenience timeOffset /= release_window_lenience; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index dccff7f6ac..2cd81104a3 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -52,6 +53,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected override void CheckForResult(bool userTriggered, double timeOffset) { + Debug.Assert(HitObject.HitWindows != null); + if (!userTriggered) { if (!HitObject.HitWindows.CanBeHit(timeOffset)) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs index 84a73c7cfc..585fdb9cb4 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using osu.Framework.MathUtils; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; @@ -13,8 +14,10 @@ namespace osu.Game.Rulesets.Osu.Tests { var drawableHitObject = base.CreateDrawableHitCircle(circle, auto); - Scheduler.AddDelayed(() => drawableHitObject.TriggerJudgement(), - drawableHitObject.HitObject.StartTime - (drawableHitObject.HitObject.HitWindows.HalfWindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current); + Debug.Assert(drawableHitObject.HitObject.HitWindows != null); + + double delay = drawableHitObject.HitObject.StartTime - (drawableHitObject.HitObject.HitWindows.HalfWindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current; + Scheduler.AddDelayed(() => drawableHitObject.TriggerJudgement(), delay); return drawableHitObject; } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 5625028707..649b01c132 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Types; @@ -38,7 +39,12 @@ namespace osu.Game.Rulesets.Osu.Mods if ((osuHit.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime) || osuHit.IsHit) continue; - requiresHit |= osuHit is DrawableHitCircle && osuHit.IsHovered && osuHit.HitObject.HitWindows.CanBeHit(relativetime); + if (osuHit is DrawableHitCircle && osuHit.IsHovered) + { + Debug.Assert(osuHit.HitObject.HitWindows != null); + requiresHit |= osuHit.HitObject.HitWindows.CanBeHit(relativetime); + } + requiresHold |= (osuHit is DrawableSlider slider && (slider.Ball.IsHovered || osuHit.IsHovered)) || osuHit is DrawableSpinner; } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 0af278f6a4..1c40e37262 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -87,6 +88,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void CheckForResult(bool userTriggered, double timeOffset) { + Debug.Assert(HitObject.HitWindows != null); + if (!userTriggered) { if (!HitObject.HitWindows.CanBeHit(timeOffset)) @@ -119,6 +122,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void UpdateStateTransforms(ArmedState state) { + Debug.Assert(HitObject.HitWindows != null); + switch (state) { case ArmedState.Idle: diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 690263c6a0..e1614984de 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -92,20 +92,20 @@ namespace osu.Game.Rulesets.Osu.Replays double endTime = (prev as IHasEndTime)?.EndTime ?? prev.StartTime; // Make the cursor stay at a hitObject as long as possible (mainly for autopilot). - if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) + if (h.StartTime - HitWindows.HalfWindowFor(HitResult.Miss) > endTime + HitWindows.HalfWindowFor(HitResult.Meh) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); - if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - HitWindows.HalfWindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } - else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) + else if (h.StartTime - HitWindows.HalfWindowFor(HitResult.Meh) > endTime + HitWindows.HalfWindowFor(HitResult.Meh) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); - if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } - else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Good) > endTime + h.HitWindows.HalfWindowFor(HitResult.Good) + 50) + else if (h.StartTime - HitWindows.HalfWindowFor(HitResult.Good) > endTime + HitWindows.HalfWindowFor(HitResult.Good) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); - if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + HitWindows.HalfWindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - HitWindows.HalfWindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } } diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs index 9ab358ee12..7c94027c28 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs @@ -6,6 +6,8 @@ using osu.Game.Beatmaps; using System; using System.Collections.Generic; using osu.Game.Replays; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Replays; @@ -27,6 +29,11 @@ namespace osu.Game.Rulesets.Osu.Replays /// protected readonly double FrameDelay; + /// + /// The hit windows. + /// + protected readonly HitWindows HitWindows; + #endregion #region Construction / Initialisation @@ -41,6 +48,9 @@ namespace osu.Game.Rulesets.Osu.Replays // We are using ApplyModsToRate and not ApplyModsToTime to counteract the speed up / slow down from HalfTime / DoubleTime so that we remain at a constant framerate of 60 fps. FrameDelay = ApplyModsToRate(1000.0 / 60.0); + + HitWindows = new OsuHitWindows(); + HitWindows.SetDifficulty(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); } #endregion diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index fa45067210..0942b37f58 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Linq; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; @@ -34,6 +35,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void CheckForResult(bool userTriggered, double timeOffset) { + Debug.Assert(HitObject.HitWindows != null); + if (!userTriggered) { if (!HitObject.HitWindows.CanBeHit(timeOffset)) @@ -94,6 +97,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void UpdateStateTransforms(ArmedState state) { + Debug.Assert(HitObject.HitWindows != null); + switch (state) { case ArmedState.Idle: From 90671e061715b3c7458e3d2d1f8d32e2bb033ec5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Sep 2019 18:58:13 +0900 Subject: [PATCH 2532/2854] Attempt to not break per-hitobject hitwindows --- .../Replays/OsuAutoGenerator.cs | 53 +++++++++++++++---- .../Replays/OsuAutoGeneratorBase.cs | 8 --- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index e1614984de..e5fa571d4d 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -6,9 +6,11 @@ using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu.Objects; using System; +using System.Diagnostics; using System.Linq; using osu.Framework.Graphics; using osu.Game.Replays; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Scoring; @@ -36,6 +38,8 @@ namespace osu.Game.Rulesets.Osu.Replays /// private readonly double reactionTime; + private readonly HitWindows defaultHitWindows; + /// /// What easing to use when moving between hitobjects /// @@ -50,6 +54,9 @@ namespace osu.Game.Rulesets.Osu.Replays { // Already superhuman, but still somewhat realistic reactionTime = ApplyModsToRate(100); + + defaultHitWindows = new OsuHitWindows(); + defaultHitWindows.SetDifficulty(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); } #endregion @@ -91,21 +98,49 @@ namespace osu.Game.Rulesets.Osu.Replays { double endTime = (prev as IHasEndTime)?.EndTime ?? prev.StartTime; + HitWindows hitWindows = null; + + switch (h) + { + case HitCircle hitCircle: + hitWindows = hitCircle.HitWindows; + break; + + case Slider slider: + hitWindows = slider.TailCircle.HitWindows; + break; + + case Spinner _: + hitWindows = defaultHitWindows; + break; + } + + Debug.Assert(hitWindows != null); + // Make the cursor stay at a hitObject as long as possible (mainly for autopilot). - if (h.StartTime - HitWindows.HalfWindowFor(HitResult.Miss) > endTime + HitWindows.HalfWindowFor(HitResult.Meh) + 50) + if (h.StartTime - hitWindows.HalfWindowFor(HitResult.Miss) > endTime + hitWindows.HalfWindowFor(HitResult.Meh) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); - if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - HitWindows.HalfWindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) + AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + + if (!(h is Spinner)) + AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.HalfWindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } - else if (h.StartTime - HitWindows.HalfWindowFor(HitResult.Meh) > endTime + HitWindows.HalfWindowFor(HitResult.Meh) + 50) + else if (h.StartTime - hitWindows.HalfWindowFor(HitResult.Meh) > endTime + hitWindows.HalfWindowFor(HitResult.Meh) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); - if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) + AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + + if (!(h is Spinner)) + AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } - else if (h.StartTime - HitWindows.HalfWindowFor(HitResult.Good) > endTime + HitWindows.HalfWindowFor(HitResult.Good) + 50) + else if (h.StartTime - hitWindows.HalfWindowFor(HitResult.Good) > endTime + hitWindows.HalfWindowFor(HitResult.Good) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + HitWindows.HalfWindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); - if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - HitWindows.HalfWindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) + AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.HalfWindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + + if (!(h is Spinner)) + AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.HalfWindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } } diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs index 7c94027c28..3c889d1f52 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs @@ -29,11 +29,6 @@ namespace osu.Game.Rulesets.Osu.Replays ///
protected readonly double FrameDelay; - /// - /// The hit windows. - /// - protected readonly HitWindows HitWindows; - #endregion #region Construction / Initialisation @@ -48,9 +43,6 @@ namespace osu.Game.Rulesets.Osu.Replays // We are using ApplyModsToRate and not ApplyModsToTime to counteract the speed up / slow down from HalfTime / DoubleTime so that we remain at a constant framerate of 60 fps. FrameDelay = ApplyModsToRate(1000.0 / 60.0); - - HitWindows = new OsuHitWindows(); - HitWindows.SetDifficulty(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); } #endregion From 0bfe4650c3da9796c112de5abbca281817ddbccf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 19:01:17 +0900 Subject: [PATCH 2533/2854] Early return if no change occurred in looping value --- osu.Game/Skinning/SkinnableSound.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 07ffbb8a51..8012500280 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -45,6 +45,8 @@ namespace osu.Game.Skinning get => looping; set { + if (value == looping) return; + looping = value; channels?.ForEach(c => c.Looping = looping); From b460f76fa6ad54a9098babe7bef500caa938923e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 19:01:43 +0900 Subject: [PATCH 2534/2854] Adjust file spacing slightly --- osu.Game/Skinning/SkinnableSound.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 8012500280..3d0219ed93 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -54,6 +54,7 @@ namespace osu.Game.Skinning } public void Play() => channels?.ForEach(c => c.Play()); + public void Stop() => channels?.ForEach(c => c.Stop()); public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) From 0ee0184e01c5fd055d20744b998426e8f4d44ecb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2019 19:13:06 +0900 Subject: [PATCH 2535/2854] Remove unnecessary usings --- osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs index 3c889d1f52..9ab358ee12 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs @@ -6,8 +6,6 @@ using osu.Game.Beatmaps; using System; using System.Collections.Generic; using osu.Game.Replays; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Replays; From 53c254c6a5ca0b59f1471a670beaf81d083f0450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 2 Sep 2019 19:01:36 +0200 Subject: [PATCH 2536/2854] Replace Array.IndexOf() with Contains() --- osu.Game/Graphics/UserInterface/HoverClickSounds.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs index 050e5a2835..e64b9259f1 100644 --- a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -36,9 +36,7 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnMouseUp(MouseUpEvent e) { - var index = Array.IndexOf(buttons, e.Button); - bool shouldPlayEffect = index > -1 && index < buttons.Length; - + bool shouldPlayEffect = buttons.Contains(e.Button); // examine the button pressed first for short-circuiting // in most usages it is more likely that another button was pressed than that the cursor left the drawable bounds if (shouldPlayEffect && Contains(e.ScreenSpaceMousePosition)) From 6ca17bdfd5106815ae2bbdd544302bf821679fed Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 2 Sep 2019 10:42:21 -0700 Subject: [PATCH 2537/2854] Center icon and text using anchor and origin instead of margin --- osu.Game/Overlays/Chat/Selection/ChannelListItem.cs | 3 ++- osu.Game/Overlays/Direct/DirectPanel.cs | 3 ++- osu.Game/Overlays/Music/PlaylistItem.cs | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs b/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs index cb0639d85d..31c48deee0 100644 --- a/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs @@ -121,10 +121,11 @@ namespace osu.Game.Overlays.Chat.Selection { new SpriteIcon { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, Icon = FontAwesome.Solid.User, Size = new Vector2(text_size - 2), Shadow = false, - Margin = new MarginPadding { Top = 1 }, }, new OsuSpriteText { diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index a9b6633864..6074aa16a5 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -190,10 +190,11 @@ namespace osu.Game.Overlays.Direct text = new OsuSpriteText { Font = OsuFont.GetFont(weight: FontWeight.SemiBold, italics: true) }, new SpriteIcon { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, Icon = icon, Shadow = true, Size = new Vector2(14), - Margin = new MarginPadding { Top = 1 }, }, }; diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index df37a1b2c7..29b6ae00f3 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -161,12 +161,12 @@ namespace osu.Game.Overlays.Music { public PlaylistItemHandle() { - Anchor = Anchor.TopLeft; - Origin = Anchor.TopLeft; + Anchor = Anchor.CentreLeft; + Origin = Anchor.CentreLeft; Size = new Vector2(12); Icon = FontAwesome.Solid.Bars; Alpha = 0f; - Margin = new MarginPadding { Left = 5, Top = 2 }; + Margin = new MarginPadding { Left = 5 }; } public override bool HandlePositionalInput => IsPresent; From 5d3f3b7cc2376d925e4269de7f982ba7a58d8897 Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 2 Sep 2019 10:59:43 -0700 Subject: [PATCH 2538/2854] Add spacing to diff icons on direct panels --- osu.Game/Overlays/Direct/DirectGridPanel.cs | 1 + osu.Game/Overlays/Direct/DirectListPanel.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index 7bf94c1483..2528ccec41 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -151,6 +151,7 @@ namespace osu.Game.Overlays.Direct AutoSizeAxes = Axes.X, Height = 20, Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, + Spacing = new Vector2(3), Children = GetDifficultyIcons(colours), }, }, diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index 158ff648dd..b64142dfe7 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -129,6 +129,7 @@ namespace osu.Game.Overlays.Direct AutoSizeAxes = Axes.X, Height = 20, Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, + Spacing = new Vector2(3), Children = GetDifficultyIcons(colours), }, }, From ce446826e8f9c87e63b8c9483c2fc057114ab345 Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 2 Sep 2019 11:00:12 -0700 Subject: [PATCH 2539/2854] Match web's max diff icon number --- osu.Game/Overlays/Direct/DirectPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index a9b6633864..c911a521b9 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Direct public readonly BeatmapSetInfo SetInfo; private const double hover_transition_time = 400; - private const int maximum_difficulty_icons = 15; + private const int maximum_difficulty_icons = 10; private Container content; From 3d1f051437650be239c1af83f253ea942248fbd1 Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 2 Sep 2019 11:13:34 -0700 Subject: [PATCH 2540/2854] Move hover tests after key tests --- .../Gameplay/TestSceneGameplayMenuOverlay.cs | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs index 4727140d99..0d8a84fa51 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs @@ -56,17 +56,14 @@ namespace osu.Game.Tests.Visual.Gameplay pauseOverlay.Retries = failOverlay.Retries = retryCount; }); - AddToggleStep("Toggle pause overlay", t => pauseOverlay.ToggleVisibility()); - AddToggleStep("Toggle fail overlay", t => failOverlay.ToggleVisibility()); - - testHideResets(); - testEnterWithoutSelection(); testKeyUpFromInitial(); testKeyDownFromInitial(); testKeyUpWrapping(); testKeyDownWrapping(); + testHideResets(); + testMouseSelectionAfterKeySelection(); testKeySelectionAfterMouseSelection(); @@ -76,19 +73,6 @@ namespace osu.Game.Tests.Visual.Gameplay testEnterKeySelection(); } - /// - /// Test that hiding the overlay after hovering a button will reset the overlay to the initial state with no buttons selected. - /// - private void testHideResets() - { - AddStep("Show overlay", () => failOverlay.Show()); - - AddStep("Hover first button", () => InputManager.MoveMouseTo(failOverlay.Buttons.First())); - AddStep("Hide overlay", () => failOverlay.Hide()); - - AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected.Value)); - } - /// /// Tests that pressing enter after an overlay shows doesn't trigger an event because a selection hasn't occurred. /// @@ -162,6 +146,19 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Hide overlay", () => failOverlay.Hide()); } + /// + /// Test that hiding the overlay after hovering a button will reset the overlay to the initial state with no buttons selected. + /// + private void testHideResets() + { + AddStep("Show overlay", () => failOverlay.Show()); + + AddStep("Hover first button", () => InputManager.MoveMouseTo(failOverlay.Buttons.First())); + AddStep("Hide overlay", () => failOverlay.Hide()); + + AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected.Value)); + } + /// /// Tests that hovering a button that was previously selected with the keyboard correctly selects the new button and deselects the previous button. /// From cb55159b280d19c9376718093e34491c76852d02 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 3 Sep 2019 01:28:51 +0300 Subject: [PATCH 2541/2854] Use float types for cursor sizes --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 8 ++++---- osu.Game/Configuration/OsuConfigManager.cs | 4 ++-- osu.Game/Graphics/Cursor/MenuCursor.cs | 7 +++---- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 10 +++++----- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index eb1977a13d..f384b1150e 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private bool cursorExpand; - private Bindable cursorScale; + private Bindable cursorScale; private Bindable autoCursorScale; private readonly IBindable beatmap = new Bindable(); @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor this.beatmap.BindTo(beatmap); this.beatmap.ValueChanged += _ => calculateScale(); - cursorScale = config.GetBindable(OsuSetting.GameplayCursorSize); + cursorScale = config.GetBindable(OsuSetting.GameplayCursorSize); cursorScale.ValueChanged += _ => calculateScale(); autoCursorScale = config.GetBindable(OsuSetting.AutoCursorSize); @@ -70,12 +70,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private void calculateScale() { - float scale = (float)cursorScale.Value; + float scale = cursorScale.Value; if (autoCursorScale.Value && beatmap.Value != null) { // if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier. - scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY); + scale *= 1f - 0.7f * (1f + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY; } scaleTarget.Scale = new Vector2(scale); diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 0cecbb225f..c901a28a59 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -58,8 +58,8 @@ namespace osu.Game.Configuration Set(OsuSetting.AudioOffset, 0, -500.0, 500.0, 1); // Input - Set(OsuSetting.MenuCursorSize, 1.0, 0.5f, 2, 0.01); - Set(OsuSetting.GameplayCursorSize, 1.0, 0.1f, 2, 0.01); + Set(OsuSetting.MenuCursorSize, 1.0f, 0.5f, 2f, 0.01f); + Set(OsuSetting.GameplayCursorSize, 1.0f, 0.1f, 2f, 0.01f); Set(OsuSetting.AutoCursorSize, false); Set(OsuSetting.MouseDisableButtons, false); diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index e103798355..5a83d8e4ce 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -124,7 +124,7 @@ namespace osu.Game.Graphics.Cursor public class Cursor : Container { private Container cursorContainer; - private Bindable cursorScale; + private Bindable cursorScale; private const float base_scale = 0.15f; public Sprite AdditiveLayer; @@ -159,9 +159,8 @@ namespace osu.Game.Graphics.Cursor } }; - cursorScale = config.GetBindable(OsuSetting.MenuCursorSize); - cursorScale.ValueChanged += scale => cursorContainer.Scale = new Vector2((float)scale.NewValue * base_scale); - cursorScale.TriggerChange(); + cursorScale = config.GetBindable(OsuSetting.MenuCursorSize); + cursorScale.BindValueChanged(scale => cursorContainer.Scale = new Vector2(scale.NewValue * base_scale), true); } } diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 35be930a2e..d3029d8ab9 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -35,16 +35,16 @@ namespace osu.Game.Overlays.Settings.Sections Children = new Drawable[] { skinDropdown = new SkinSettingsDropdown(), - new SettingsSlider + new SettingsSlider { LabelText = "Menu cursor size", - Bindable = config.GetBindable(OsuSetting.MenuCursorSize), + Bindable = config.GetBindable(OsuSetting.MenuCursorSize), KeyboardStep = 0.01f }, - new SettingsSlider + new SettingsSlider { LabelText = "Gameplay cursor size", - Bindable = config.GetBindable(OsuSetting.GameplayCursorSize), + Bindable = config.GetBindable(OsuSetting.GameplayCursorSize), KeyboardStep = 0.01f }, new SettingsCheckbox @@ -95,7 +95,7 @@ namespace osu.Game.Overlays.Settings.Sections } } - private class SizeSlider : OsuSliderBar + private class SizeSlider : OsuSliderBar { public override string TooltipText => Current.Value.ToString(@"0.##x"); } From de6dba9716ba720f8756b38662ecc48aa32d148d Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 3 Sep 2019 01:50:52 +0300 Subject: [PATCH 2542/2854] Use float type for chat overlay height --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- osu.Game/Overlays/ChatOverlay.cs | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 0cecbb225f..9246acb00b 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -31,7 +31,7 @@ namespace osu.Game.Configuration Set(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation); - Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1); + Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f); // Online settings Set(OsuSetting.Username, string.Empty); diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 53a05656b1..6f848c7627 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -54,7 +54,7 @@ namespace osu.Game.Overlays private Box chatBackground; private Box tabBackground; - public Bindable ChatHeight { get; set; } + public Bindable ChatHeight { get; set; } private Container channelSelectionContainer; protected ChannelSelectionOverlay ChannelSelectionOverlay; @@ -190,14 +190,13 @@ namespace osu.Game.Overlays ChannelSelectionOverlay.OnRequestJoin = channel => channelManager.JoinChannel(channel); ChannelSelectionOverlay.OnRequestLeave = channelManager.LeaveChannel; - ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight); - ChatHeight.ValueChanged += height => + ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight); + ChatHeight.BindValueChanged(height => { - chatContainer.Height = (float)height.NewValue; - channelSelectionContainer.Height = 1f - (float)height.NewValue; - tabBackground.FadeTo(height.NewValue == 1 ? 1 : 0.8f, 200); - }; - ChatHeight.TriggerChange(); + chatContainer.Height = height.NewValue; + channelSelectionContainer.Height = 1f - height.NewValue; + tabBackground.FadeTo(height.NewValue == 1f ? 1f : 0.8f, 200); + }, true); chatBackground.Colour = colours.ChatBlue; @@ -273,7 +272,7 @@ namespace osu.Game.Overlays } } - private double startDragChatHeight; + private float startDragChatHeight; private bool isDragging; protected override bool OnDragStart(DragStartEvent e) @@ -291,7 +290,7 @@ namespace osu.Game.Overlays { if (isDragging) { - double targetChatHeight = startDragChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y; + float targetChatHeight = startDragChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y; // If the channel selection screen is shown, mind its minimum height if (ChannelSelectionOverlay.State.Value == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height) From 3b769128a82c8f9cb0e588208a7d5f7eee3938d6 Mon Sep 17 00:00:00 2001 From: jorolf Date: Tue, 3 Sep 2019 00:57:29 +0200 Subject: [PATCH 2543/2854] Add a 60bpm beat when no beatmap is playing --- .../TestSceneBeatSyncedContainer.cs | 10 ++- .../Containers/BeatSyncedContainer.cs | 64 +++++++++++++++---- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index 94228e22f0..d84ffa0d93 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio.Track; @@ -25,6 +26,11 @@ namespace osu.Game.Tests.Visual.UserInterface { private readonly NowPlayingOverlay np; + public override IReadOnlyList RequiredTypes => new[] + { + typeof(BeatSyncedContainer) + }; + [Cached] private MusicController musicController = new MusicController(); @@ -154,7 +160,9 @@ namespace osu.Game.Tests.Visual.UserInterface if (timingPoints[timingPoints.Count - 1] == current) return current; - return timingPoints[timingPoints.IndexOf(current) + 1]; + int index = timingPoints.IndexOf(current); // -1 means that this is a "default beat" + + return index == -1 ? current : timingPoints[index + 1]; } private int calculateBeatCount(TimingControlPoint current) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 621eeea2b7..f6d950c414 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -33,23 +33,46 @@ namespace osu.Game.Graphics.Containers ///
public double TimeSinceLastBeat { get; private set; } + /// + /// Default length of a beat in milliseconds. Used whenever there is no beatmap or track playing. + /// + private const double default_beat_length = 60000.0 / 60.0; + + private TimingControlPoint defaultTiming; + private EffectControlPoint defaultEffect; + private TrackAmplitudes defaultAmplitudes; + protected override void Update() { - if (!Beatmap.Value.TrackLoaded || !Beatmap.Value.BeatmapLoaded) return; + Track track = null; + IBeatmap beatmap = null; - var track = Beatmap.Value.Track; - var beatmap = Beatmap.Value.Beatmap; + double currentTrackTime; + TimingControlPoint timingPoint; + EffectControlPoint effectPoint; - if (track == null || beatmap == null) - return; + if (Beatmap.Value.TrackLoaded && Beatmap.Value.BeatmapLoaded) + { + track = Beatmap.Value.Track; + beatmap = Beatmap.Value.Beatmap; + } - double currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime; + if (track != null && beatmap != null && track.IsRunning) + { + currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime; - TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); - EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); + timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); + effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); - if (timingPoint.BeatLength == 0) - return; + if (timingPoint.BeatLength == 0) + return; + } + else + { + currentTrackTime = Clock.CurrentTime; + timingPoint = defaultTiming; + effectPoint = defaultEffect; + } int beatIndex = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength); @@ -67,7 +90,7 @@ namespace osu.Game.Graphics.Containers return; using (BeginDelayedSequence(-TimeSinceLastBeat, true)) - OnNewBeat(beatIndex, timingPoint, effectPoint, track.CurrentAmplitudes); + OnNewBeat(beatIndex, timingPoint, effectPoint, track?.CurrentAmplitudes ?? defaultAmplitudes); lastBeat = beatIndex; lastTimingPoint = timingPoint; @@ -77,6 +100,25 @@ namespace osu.Game.Graphics.Containers private void load(IBindable beatmap) { Beatmap.BindTo(beatmap); + defaultTiming = new TimingControlPoint + { + BeatLength = default_beat_length, + AutoGenerated = true, + Time = 0 + }; + defaultEffect = new EffectControlPoint + { + Time = 0, + AutoGenerated = true, + KiaiMode = false, + OmitFirstBarLine = false + }; + defaultAmplitudes = new TrackAmplitudes + { + FrequencyAmplitudes = new float[256], + LeftChannel = 0, + RightChannel = 0 + }; } protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) From eab06995d0378d8e984937acd34e3009c9dc0156 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 3 Sep 2019 11:37:56 +0900 Subject: [PATCH 2544/2854] Add some whitespace --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index f6d950c414..370d044ba4 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -100,12 +100,14 @@ namespace osu.Game.Graphics.Containers private void load(IBindable beatmap) { Beatmap.BindTo(beatmap); + defaultTiming = new TimingControlPoint { BeatLength = default_beat_length, AutoGenerated = true, Time = 0 }; + defaultEffect = new EffectControlPoint { Time = 0, @@ -113,6 +115,7 @@ namespace osu.Game.Graphics.Containers KiaiMode = false, OmitFirstBarLine = false }; + defaultAmplitudes = new TrackAmplitudes { FrequencyAmplitudes = new float[256], From 444419b2e6f99fcab6775245a346dd6f6db0c092 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 12:04:49 +0900 Subject: [PATCH 2545/2854] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 93a9a073a4..743508baf8 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -60,7 +60,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 330018d5cb..03207dfdf7 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,7 +14,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 9f8d82ad1e..ec76ceaf95 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -117,7 +117,7 @@ - + From 11dfdc33d519673df52de4a6e09434d0c83d9634 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2019 03:05:45 +0000 Subject: [PATCH 2546/2854] Bump ppy.osu.Game.Resources from 2019.830.0 to 2019.903.0 Bumps [ppy.osu.Game.Resources](https://github.com/ppy/osu-resources) from 2019.830.0 to 2019.903.0. - [Release notes](https://github.com/ppy/osu-resources/releases) - [Commits](https://github.com/ppy/osu-resources/compare/2019.830.0...2019.903.0) Signed-off-by: dependabot-preview[bot] --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 93a9a073a4..743508baf8 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -60,7 +60,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 330018d5cb..03207dfdf7 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,7 +14,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 9f8d82ad1e..ec76ceaf95 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -117,7 +117,7 @@ - + From 4f3511e8e9637fded836e3f0080b5ba28958ecd8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 12:32:10 +0900 Subject: [PATCH 2547/2854] Fix ring glow lookup being incorrect --- osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs index 00188689dd..30937313fd 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Texture = textures.Get("ring-glow"), + Texture = textures.Get("Gameplay/osu/ring-glow"), Blending = BlendingParameters.Additive, Alpha = 0.5f }; From 4cad55cee6a73bb010d0838afeecfa63d38b17a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 13:05:03 +0900 Subject: [PATCH 2548/2854] Move hit windows lookup to DrawableRuleset --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 23 +++++++++++++++++++++++ osu.Game/Screens/Play/HUDOverlay.cs | 5 +---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 0ee9196fb8..a32407d180 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -13,6 +13,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; +using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Graphics.Cursor; using osu.Framework.Input; @@ -390,6 +391,28 @@ namespace osu.Game.Rulesets.UI ///